[tomcat8] 02/13: New upstream version 8.5.8
Emmanuel Bourg
ebourg-guest at moszumanska.debian.org
Thu Nov 17 23:51:36 UTC 2016
This is an automated email from the git hooks/post-receive script.
ebourg-guest pushed a commit to branch experimental
in repository tomcat8.
commit d8d9950612b15787cdecbf35d510e63f6fdba088
Author: Emmanuel Bourg <ebourg at apache.org>
Date: Wed Nov 16 18:46:48 2016 +0100
New upstream version 8.5.8
---
BUILDING.txt | 98 +-
KEYS | 60 -
LICENSE | 4 +
NOTICE | 18 +-
RELEASE-NOTES | 11 +-
RUNNING.txt | 6 -
TOMCAT-NEXT.txt | 224 -
bin/catalina-tasks.xml | 2 +-
bin/catalina.bat | 14 +-
bin/catalina.sh | 33 +-
bin/daemon.sh | 3 -
bin/service.bat | 2 +-
bin/setclasspath.bat | 10 +-
bin/setclasspath.sh | 11 +-
bin/tool-wrapper.bat | 8 +-
bin/tool-wrapper.sh | 9 +-
build.properties.default | 55 +-
build.xml | 335 +-
conf/catalina.policy | 7 +-
conf/catalina.properties | 4 +-
conf/context.xml | 8 +-
conf/jaspic-providers.xml | 23 +
conf/jaspic-providers.xsd | 53 +
conf/logging.properties | 6 +
conf/server.xml | 36 +-
conf/tomcat-users.xml | 2 +-
conf/tomcat-users.xsd | 2 +-
conf/web.xml | 5 +-
java/javax/el/ArrayELResolver.java | 17 +-
java/javax/el/BeanELResolver.java | 21 +-
java/javax/el/BeanNameELResolver.java | 21 +-
java/javax/el/CompositeELResolver.java | 7 +-
java/javax/el/ELContext.java | 10 +-
java/javax/el/LambdaExpression.java | 5 +-
java/javax/el/ListELResolver.java | 17 +-
java/javax/el/MapELResolver.java | 17 +-
java/javax/el/ResourceBundleELResolver.java | 18 +-
java/javax/el/StaticFieldELResolver.java | 24 +-
.../javax/security/auth/message/AuthException.java | 30 +
java/javax/security/auth/message/AuthStatus.java | 37 +
java/javax/security/auth/message/ClientAuth.java | 30 +
java/javax/security/auth/message/MessageInfo.java | 33 +
.../javax/security/auth/message/MessagePolicy.java | 85 +
java/javax/security/auth/message/ServerAuth.java | 29 +
.../message/callback/CallerPrincipalCallback.java | 57 +
.../auth/message/callback/CertStoreCallback.java | 41 +
.../message/callback/GroupPrincipalCallback.java | 43 +
.../callback/PasswordValidationCallback.java | 65 +
.../auth/message/callback/PrivateKeyCallback.java | 123 +
.../auth/message/callback/SecretKeyCallback.java | 62 +
.../auth/message/callback/TrustStoreCallback.java | 38 +
.../security/auth/message/config/AuthConfig.java | 32 +
.../auth/message/config/AuthConfigFactory.java | 149 +
.../auth/message/config/AuthConfigProvider.java | 31 +
.../auth/message/config/ClientAuthConfig.java | 29 +
.../auth/message/config/ClientAuthContext.java | 22 +
.../auth/message/config/RegistrationListener.java | 22 +
.../auth/message/config/ServerAuthConfig.java | 29 +
.../auth/message/config/ServerAuthContext.java | 22 +
.../auth/message/module/ClientAuthModule.java | 34 +
.../auth/message/module/ServerAuthModule.java | 34 +
java/javax/servlet/http/Cookie.java | 27 +-
java/javax/servlet/http/HttpSessionListener.java | 1 -
.../servlet/jsp/el/ImplicitObjectELResolver.java | 27 +-
.../servlet/jsp/el/ScopedAttributeELResolver.java | 17 +-
java/javax/websocket/WebSocketContainer.java | 4 +-
java/org/apache/catalina/AccessLog.java | 1 +
java/org/apache/catalina/Context.java | 215 +-
java/org/apache/catalina/Engine.java | 14 +-
java/org/apache/catalina/Executor.java | 5 +-
java/org/apache/catalina/Globals.java | 43 -
java/org/apache/catalina/Group.java | 20 +-
java/org/apache/catalina/Host.java | 36 +-
java/org/apache/catalina/InstanceEvent.java | 397 --
java/org/apache/catalina/InstanceListener.java | 43 -
java/org/apache/catalina/JmxEnabled.java | 7 +-
java/org/apache/catalina/LifecycleEvent.java | 35 +-
java/org/apache/catalina/LifecycleState.java | 28 +-
java/org/apache/catalina/Manager.java | 132 +-
java/org/apache/catalina/Realm.java | 8 +
java/org/apache/catalina/Service.java | 19 +-
java/org/apache/catalina/Session.java | 42 +-
java/org/apache/catalina/SessionEvent.java | 9 +-
java/org/apache/catalina/SessionIdGenerator.java | 7 +-
java/org/apache/catalina/Store.java | 9 +-
java/org/apache/catalina/StoreManager.java | 2 +-
java/org/apache/catalina/TomcatPrincipal.java | 4 +-
java/org/apache/catalina/User.java | 14 +-
java/org/apache/catalina/UserDatabase.java | 17 +-
java/org/apache/catalina/Valve.java | 24 +-
java/org/apache/catalina/WebResource.java | 42 +-
java/org/apache/catalina/WebResourceRoot.java | 29 +-
java/org/apache/catalina/WebResourceSet.java | 2 +
java/org/apache/catalina/Wrapper.java | 94 +-
.../catalina/ant/AbstractCatalinaCommandTask.java | 2 +
.../catalina/ant/BaseRedirectorHelperTask.java | 9 +-
java/org/apache/catalina/ant/FindLeaksTask.java | 5 +
java/org/apache/catalina/ant/ValidatorTask.java | 2 +-
.../catalina/ant/jmx/JMXAccessorConditionBase.java | 4 +-
.../catalina/ant/jmx/JMXAccessorCreateTask.java | 25 +-
.../catalina/ant/jmx/JMXAccessorGetTask.java | 20 +-
.../catalina/ant/jmx/JMXAccessorInvokeTask.java | 16 +-
.../catalina/ant/jmx/JMXAccessorQueryTask.java | 94 +-
.../catalina/ant/jmx/JMXAccessorSetTask.java | 28 +-
.../apache/catalina/ant/jmx/JMXAccessorTask.java | 63 +-
.../ant/jmx/JMXAccessorUnregisterTask.java | 19 +-
.../catalina/authenticator/AuthenticatorBase.java | 746 ++-
.../catalina/authenticator/BasicAuthenticator.java | 19 +-
.../apache/catalina/authenticator/Constants.java | 13 +-
.../authenticator/DigestAuthenticator.java | 15 +-
.../catalina/authenticator/FormAuthenticator.java | 61 +-
.../catalina/authenticator/LocalStrings.properties | 3 +
.../authenticator/NonLoginAuthenticator.java | 2 +-
.../catalina/authenticator/SSLAuthenticator.java | 6 +-
.../catalina/authenticator/SingleSignOn.java | 2 +-
.../catalina/authenticator/SingleSignOnEntry.java | 13 +-
.../authenticator/SpnegoAuthenticator.java | 10 +-
.../jaspic/AuthConfigFactoryImpl.java | 296 +
.../authenticator/jaspic/CallbackHandlerImpl.java | 119 +
.../authenticator/jaspic/LocalStrings.properties | 28 +
.../authenticator/jaspic/MessageInfoImpl.java | 80 +
.../jaspic/PersistentProviderRegistrations.java | 257 +
.../jaspic/SimpleAuthConfigProvider.java | 89 +
.../jaspic/SimpleServerAuthConfig.java | 148 +
.../jaspic/SimpleServerAuthContext.java | 74 +
.../catalina/authenticator/mbeans-descriptors.xml | 2 +-
java/org/apache/catalina/comet/CometEvent.java | 146 -
java/org/apache/catalina/comet/CometFilter.java | 81 -
.../apache/catalina/comet/CometFilterChain.java | 45 -
java/org/apache/catalina/comet/CometProcessor.java | 45 -
.../apache/catalina/connector/CometEventImpl.java | 148 -
java/org/apache/catalina/connector/Connector.java | 425 +-
java/org/apache/catalina/connector/Constants.java | 26 -
.../apache/catalina/connector/CoyoteAdapter.java | 311 +-
.../catalina/connector/CoyoteInputStream.java | 164 +-
.../catalina/connector/CoyoteOutputStream.java | 22 +-
.../org/apache/catalina/connector/InputBuffer.java | 430 +-
.../catalina/connector/LocalStrings.properties | 7 +-
.../catalina/connector/LocalStrings_es.properties | 1 -
.../apache/catalina/connector/OutputBuffer.java | 523 +-
java/org/apache/catalina/connector/Request.java | 400 +-
.../apache/catalina/connector/RequestFacade.java | 43 +-
java/org/apache/catalina/connector/Response.java | 143 +-
.../apache/catalina/connector/ResponseFacade.java | 10 +-
.../catalina/connector/mbeans-descriptors.xml | 2 +-
.../org/apache/catalina/core/AccessLogAdapter.java | 9 +-
.../apache/catalina/core/ApplicationContext.java | 347 +-
.../catalina/core/ApplicationContextFacade.java | 27 +-
.../catalina/core/ApplicationDispatcher.java | 56 +-
.../catalina/core/ApplicationFilterChain.java | 314 +-
.../catalina/core/ApplicationFilterConfig.java | 6 +-
.../catalina/core/ApplicationFilterFactory.java | 117 +-
.../catalina/core/ApplicationHttpRequest.java | 91 +-
.../apache/catalina/core/ApplicationMapping.java | 115 +
.../catalina/core/ApplicationPushBuilder.java | 479 ++
.../apache/catalina/core/ApplicationRequest.java | 4 +-
.../core/ApplicationSessionCookieConfig.java | 1 +
.../apache/catalina/core/AprLifecycleListener.java | 33 +-
.../org/apache/catalina/core/AsyncContextImpl.java | 62 +-
java/org/apache/catalina/core/Constants.java | 6 +-
java/org/apache/catalina/core/ContainerBase.java | 32 +-
.../catalina/core/DefaultInstanceManager.java | 2 +
.../core/JreMemoryLeakPreventionListener.java | 12 +-
.../apache/catalina/core/LocalStrings.properties | 24 +-
.../catalina/core/NamingContextListener.java | 116 +-
java/org/apache/catalina/core/StandardContext.java | 487 +-
.../apache/catalina/core/StandardContextValve.java | 27 +-
.../apache/catalina/core/StandardEngineValve.java | 22 -
java/org/apache/catalina/core/StandardHost.java | 63 +-
.../apache/catalina/core/StandardHostValve.java | 51 +-
java/org/apache/catalina/core/StandardServer.java | 46 +-
java/org/apache/catalina/core/StandardService.java | 135 +-
java/org/apache/catalina/core/StandardWrapper.java | 148 +-
.../catalina/core/StandardWrapperFacade.java | 4 +-
.../apache/catalina/core/StandardWrapperValve.java | 191 +-
.../core/ThreadLocalLeakPreventionListener.java | 3 +-
.../apache/catalina/core/mbeans-descriptors.xml | 53 +-
java/org/apache/catalina/deploy/Constants.java | 26 -
.../catalina/deploy/NamingResourcesImpl.java | 41 +-
.../apache/catalina/deploy/mbeans-descriptors.xml | 2 +-
java/org/apache/catalina/filters/Constants.java | 2 -
java/org/apache/catalina/filters/CorsFilter.java | 173 +-
.../catalina/filters/CsrfPreventionFilterBase.java | 4 +-
.../org/apache/catalina/filters/ExpiresFilter.java | 31 +-
.../catalina/filters/FailedRequestFilter.java | 18 +-
java/org/apache/catalina/filters/FilterBase.java | 2 +-
.../catalina/filters/LocalStrings.properties | 2 +-
.../apache/catalina/filters/RemoteAddrFilter.java | 22 -
.../apache/catalina/filters/RemoteHostFilter.java | 22 -
.../apache/catalina/filters/RemoteIpFilter.java | 31 +-
.../org/apache/catalina/filters/RequestFilter.java | 37 +-
.../filters/SetCharacterEncodingFilter.java | 1 +
.../apache/catalina/filters/WebdavFixFilter.java | 5 +-
java/org/apache/catalina/ha/CatalinaCluster.java | 24 +-
java/org/apache/catalina/ha/ClusterDeployer.java | 2 +-
java/org/apache/catalina/ha/ClusterListener.java | 5 +-
java/org/apache/catalina/ha/ClusterManager.java | 2 +-
.../org/apache/catalina/ha/ClusterMessageBase.java | 15 +-
.../ha/authenticator/ClusterSingleSignOn.java | 3 +-
.../ha/authenticator/mbeans-descriptors.xml | 2 +-
java/org/apache/catalina/ha/backend/Sender.java | 10 +-
java/org/apache/catalina/ha/backend/TcpSender.java | 1 +
.../catalina/ha/context/ReplicatedContext.java | 4 +-
java/org/apache/catalina/ha/deploy/Constants.java | 27 -
.../apache/catalina/ha/deploy/FarmWarDeployer.java | 45 +-
.../catalina/ha/deploy/FileMessageFactory.java | 12 +-
java/org/apache/catalina/ha/deploy/WarWatcher.java | 5 +-
.../catalina/ha/deploy/mbeans-descriptors.xml | 2 +-
.../apache/catalina/ha/session/BackupManager.java | 4 +-
.../catalina/ha/session/ClusterManagerBase.java | 45 -
.../ha/session/ClusterSessionListener.java | 4 +-
java/org/apache/catalina/ha/session/Constants.java | 32 -
.../apache/catalina/ha/session/DeltaManager.java | 182 +-
.../apache/catalina/ha/session/DeltaRequest.java | 10 +-
.../apache/catalina/ha/session/DeltaSession.java | 20 +-
.../catalina/ha/session/JvmRouteBinderValve.java | 7 +-
.../catalina/ha/session/LocalStrings.properties | 2 +-
.../catalina/ha/session/mbeans-descriptors.xml | 24 -
.../apache/catalina/ha/tcp/LocalStrings.properties | 2 +-
.../apache/catalina/ha/tcp/ReplicationValve.java | 46 +-
.../apache/catalina/ha/tcp/SendMessageData.java | 12 +-
.../apache/catalina/ha/tcp/SimpleTcpCluster.java | 22 +-
.../apache/catalina/loader/JdbcLeakPrevention.java | 2 +-
.../catalina/loader/ParallelWebappClassLoader.java | 5 +-
java/org/apache/catalina/loader/ResourceEntry.java | 22 +-
.../catalina/loader/WebappClassLoaderBase.java | 662 +-
java/org/apache/catalina/loader/WebappLoader.java | 50 +-
.../apache/catalina/loader/mbeans-descriptors.xml | 2 +-
.../catalina/manager/HTMLManagerServlet.java | 60 +-
.../apache/catalina/manager/JMXProxyServlet.java | 117 +-
java/org/apache/catalina/manager/JspHelper.java | 6 +-
.../catalina/manager/LocalStrings.properties | 2 +-
.../apache/catalina/manager/ManagerServlet.java | 85 +-
.../apache/catalina/manager/StatusTransformer.java | 69 +-
.../apache/catalina/manager/host/Constants.java | 32 -
.../manager/host/HTMLHostManagerServlet.java | 69 +-
.../catalina/manager/host/HostManagerServlet.java | 68 +-
.../catalina/manager/host/LocalStrings.properties | 7 +
.../manager/util/BaseSessionComparator.java | 5 +-
.../apache/catalina/manager/util/SessionUtils.java | 20 +-
java/org/apache/catalina/mapper/Mapper.java | 167 +-
.../org/apache/catalina/mapper/MapperListener.java | 17 +-
java/org/apache/catalina/mapper/MappingData.java | 6 +-
.../apache/catalina/mapper/mbeans-descriptors.xml | 2 +-
java/org/apache/catalina/mbeans-descriptors.xml | 2 +-
java/org/apache/catalina/mbeans/Constants.java | 27 -
java/org/apache/catalina/mbeans/MBeanFactory.java | 21 +-
.../apache/catalina/mbeans/mbeans-descriptors.xml | 2 +-
java/org/apache/catalina/realm/Constants.java | 9 +-
.../realm/DigestCredentialHandlerBase.java | 3 +-
.../apache/catalina/realm/JAASCallbackHandler.java | 3 +-
.../catalina/realm/JAASMemoryLoginModule.java | 14 +-
java/org/apache/catalina/realm/JNDIRealm.java | 52 +-
.../apache/catalina/realm/LocalStrings.properties | 1 +
java/org/apache/catalina/realm/RealmBase.java | 319 +-
.../apache/catalina/realm/mbeans-descriptors.xml | 66 +-
.../catalina/security/SecurityClassLoad.java | 36 +-
.../apache/catalina/security/SecurityConfig.java | 5 +-
.../org/apache/catalina/security/SecurityUtil.java | 27 +-
.../catalina/servlet4preview/GenericFilter.java | 86 +
.../servlet4preview/RequestDispatcher.java | 47 +
.../catalina/servlet4preview/http/HttpFilter.java | 96 +
.../servlet4preview/http/HttpServletRequest.java | 48 +
.../http/HttpServletRequestWrapper.java | 81 +
.../catalina/servlet4preview/http/Mapping.java | 51 +
.../servlet4preview/http/MappingMatch.java | 32 +
.../catalina/servlet4preview/http/PushBuilder.java | 254 +
.../catalina/servlet4preview/package-info.java | 36 +
java/org/apache/catalina/servlets/CGIServlet.java | 23 -
.../apache/catalina/servlets/DefaultServlet.java | 303 +-
.../catalina/servlets/LocalStrings.properties | 2 -
.../catalina/servlets/LocalStrings_es.properties | 1 -
.../catalina/servlets/LocalStrings_fr.properties | 1 -
.../catalina/servlets/LocalStrings_ja.properties | 2 -
.../apache/catalina/servlets/WebdavServlet.java | 509 +-
.../catalina/session/LocalStrings.properties | 1 -
java/org/apache/catalina/session/ManagerBase.java | 196 +-
.../catalina/session/PersistentManagerBase.java | 14 +-
.../apache/catalina/session/StandardManager.java | 1 +
.../apache/catalina/session/StandardSession.java | 30 +-
.../apache/catalina/session/mbeans-descriptors.xml | 30 +-
.../apache/catalina/ssi/ExpressionParseTree.java | 15 +-
.../apache/catalina/ssi/ExpressionTokenizer.java | 9 +-
.../catalina/ssi/ResponseIncludeWrapper.java | 1 +
java/org/apache/catalina/ssi/SSIFilter.java | 7 -
java/org/apache/catalina/ssi/SSIMediator.java | 6 +-
java/org/apache/catalina/ssi/SSIProcessor.java | 8 +-
java/org/apache/catalina/ssi/SSIServlet.java | 1 +
.../catalina/ssi/SSIServletExternalResolver.java | 3 +-
.../apache/catalina/ssi/SSIServletRequestUtil.java | 1 +
.../catalina/startup/Authenticators.properties | 2 +-
java/org/apache/catalina/startup/Bootstrap.java | 14 +
java/org/apache/catalina/startup/Catalina.java | 39 +-
.../catalina/startup/CatalinaProperties.java | 17 +-
.../catalina/startup/CertificateCreateRule.java | 61 +
.../catalina/startup/ClassLoaderFactory.java | 11 +-
.../catalina/startup/ConnectorCreateRule.java | 23 +-
.../org/apache/catalina/startup/ContextConfig.java | 155 +-
.../apache/catalina/startup/ContextRuleSet.java | 6 +-
.../org/apache/catalina/startup/EngineRuleSet.java | 2 +-
java/org/apache/catalina/startup/ExpandWar.java | 5 +
.../org/apache/catalina/startup/FailedContext.java | 7 -
java/org/apache/catalina/startup/HostConfig.java | 79 +-
.../catalina/startup/LocalStrings.properties | 3 +
java/org/apache/catalina/startup/Tomcat.java | 157 +-
java/org/apache/catalina/startup/UserConfig.java | 26 +-
java/org/apache/catalina/startup/UserDatabase.java | 6 +-
.../apache/catalina/startup/WebAnnotationSet.java | 8 +
.../catalina/startup/WebappServiceLoader.java | 2 +
.../apache/catalina/startup/mbeans-descriptors.xml | 2 +-
.../apache/catalina/storeconfig/ConnectorSF.java | 31 +-
.../storeconfig/ConnectorStoreAppender.java | 54 +-
.../catalina/storeconfig/CredentialHandlerSF.java | 12 +-
.../apache/catalina/storeconfig/IStoreConfig.java | 6 +
.../catalina/storeconfig/InstanceListenerSF.java | 60 -
java/org/apache/catalina/storeconfig/LoaderSF.java | 7 +-
.../org/apache/catalina/storeconfig/ManagerSF.java | 7 +-
.../catalina/storeconfig/SSLHostConfigSF.java | 45 +
.../catalina/storeconfig/StandardContextSF.java | 64 +-
.../catalina/storeconfig/StandardServerSF.java | 12 +-
.../catalina/storeconfig/StandardServiceSF.java | 22 +-
.../apache/catalina/storeconfig/StoreAppender.java | 121 +-
.../apache/catalina/storeconfig/StoreConfig.java | 85 +-
.../storeconfig/StoreConfigLifecycleListener.java | 26 +-
.../catalina/storeconfig/StoreContextAppender.java | 23 +-
.../catalina/storeconfig/StoreDescription.java | 8 +-
.../catalina/storeconfig/StoreFactoryBase.java | 44 +-
.../catalina/storeconfig/StoreFactoryRule.java | 22 +-
.../catalina/storeconfig/StoreFileMover.java | 19 +-
.../apache/catalina/storeconfig/StoreLoader.java | 37 +-
.../apache/catalina/storeconfig/StoreRegistry.java | 31 +-
.../catalina/storeconfig/mbeans-descriptors.xml | 2 +-
.../catalina/storeconfig/server-registry.xml | 40 +-
java/org/apache/catalina/tribes/ByteMessage.java | 4 +-
java/org/apache/catalina/tribes/Channel.java | 14 +-
.../apache/catalina/tribes/ChannelException.java | 10 +-
.../apache/catalina/tribes/ChannelInterceptor.java | 14 +-
.../apache/catalina/tribes/ChannelReceiver.java | 14 +-
java/org/apache/catalina/tribes/ChannelSender.java | 13 +
java/org/apache/catalina/tribes/Member.java | 12 +-
.../apache/catalina/tribes/MembershipService.java | 58 +-
.../catalina/tribes/group/ChannelCoordinator.java | 18 +-
.../tribes/group/ChannelInterceptorBase.java | 4 +-
.../apache/catalina/tribes/group/GroupChannel.java | 20 +-
.../apache/catalina/tribes/group/RpcCallback.java | 9 +-
.../apache/catalina/tribes/group/RpcChannel.java | 5 +-
.../interceptors/DomainFilterInterceptor.java | 3 +-
.../interceptors/FragmentationInterceptor.java | 4 +-
.../tribes/group/interceptors/GzipInterceptor.java | 5 +-
.../group/interceptors/LocalStrings.properties | 1 +
.../interceptors/MessageDispatch15Interceptor.java | 28 -
.../interceptors/MessageDispatchInterceptor.java | 55 +-
.../group/interceptors/NonBlockingCoordinator.java | 37 +-
.../group/interceptors/OrderInterceptor.java | 3 +-
.../group/interceptors/SimpleCoordinator.java | 2 +-
.../interceptors/StaticMembershipInterceptor.java | 13 +-
.../group/interceptors/TcpFailureDetector.java | 9 +-
.../group/interceptors/TcpPingInterceptor.java | 9 +-
.../group/interceptors/ThroughputInterceptor.java | 3 +-
.../interceptors/TwoPhaseCommitInterceptor.java | 3 +-
java/org/apache/catalina/tribes/io/BufferPool.java | 3 +-
.../tribes/io/DirectByteArrayOutputStream.java | 1 -
.../apache/catalina/tribes/io/ObjectReader.java | 9 +-
.../catalina/tribes/io/ReplicationStream.java | 3 +-
.../org/apache/catalina/tribes/io/XByteBuffer.java | 47 +-
.../catalina/tribes/membership/McastService.java | 17 +-
.../tribes/membership/McastServiceImpl.java | 43 +-
.../catalina/tribes/membership/MemberImpl.java | 2 +
.../catalina/tribes/membership/Membership.java | 21 +-
.../tribes/tipis/AbstractReplicatedMap.java | 30 +-
.../catalina/tribes/tipis/LazyReplicatedMap.java | 14 +-
.../catalina/tribes/tipis/ReplicatedMap.java | 13 +-
.../catalina/tribes/tipis/ReplicatedMapEntry.java | 17 +-
.../catalina/tribes/transport/PooledSender.java | 3 +-
.../catalina/tribes/transport/ReceiverBase.java | 20 +-
.../tribes/transport/ReplicationTransmitter.java | 2 +
.../catalina/tribes/transport/RxTaskPool.java | 2 +
.../catalina/tribes/transport/SenderState.java | 3 +-
.../catalina/tribes/transport/bio/BioReceiver.java | 9 +-
.../tribes/transport/bio/BioReplicationTask.java | 10 +-
.../catalina/tribes/transport/bio/BioSender.java | 27 +-
.../tribes/transport/bio/PooledMultiSender.java | 11 +-
.../tribes/transport/bio/util/FastQueue.java | 300 -
.../tribes/transport/bio/util/LinkObject.java | 107 -
.../transport/bio/util/LocalStrings.properties | 21 -
.../bio/util/SingleRemoveSynchronizedAddLock.java | 256 -
.../catalina/tribes/transport/nio/NioReceiver.java | 27 +-
.../tribes/transport/nio/NioReplicationTask.java | 20 +-
.../catalina/tribes/transport/nio/NioSender.java | 12 +-
.../tribes/transport/nio/ParallelNioSender.java | 10 +-
.../tribes/transport/nio/PooledParallelSender.java | 3 +-
java/org/apache/catalina/tribes/util/Arrays.java | 2 +-
.../catalina/tribes/util/ExecutorFactory.java | 2 +-
.../apache/catalina/tribes/util/StringManager.java | 176 +-
.../apache/catalina/tribes/util/UUIDGenerator.java | 4 +-
java/org/apache/catalina/users/MemoryUser.java | 1 +
.../apache/catalina/users/MemoryUserDatabase.java | 13 +-
.../apache/catalina/users/mbeans-descriptors.xml | 2 +-
java/org/apache/catalina/util/CharsetMapper.java | 1 +
java/org/apache/catalina/util/Conversions.java | 42 -
.../catalina/util/CustomObjectInputStream.java | 15 +-
java/org/apache/catalina/util/DOMWriter.java | 52 +-
java/org/apache/catalina/util/Extension.java | 28 +-
.../apache/catalina/util/ExtensionValidator.java | 7 +-
java/org/apache/catalina/util/IOTools.java | 30 +-
java/org/apache/catalina/util/InstanceSupport.java | 342 -
java/org/apache/catalina/util/Introspection.java | 16 +-
java/org/apache/catalina/util/LifecycleBase.java | 29 +-
.../org/apache/catalina/util/LifecycleSupport.java | 108 -
.../apache/catalina/util/LocalStrings.properties | 2 -
java/org/apache/catalina/util/MIME2Java.java | 510 +-
java/org/apache/catalina/util/ParameterMap.java | 5 +-
java/org/apache/catalina/util/RequestUtil.java | 186 +-
java/org/apache/catalina/util/ResourceSet.java | 4 +-
java/org/apache/catalina/util/ServerInfo.java | 10 +-
java/org/apache/catalina/util/SessionConfig.java | 6 +-
.../catalina/util/SessionIdGeneratorBase.java | 87 +-
java/org/apache/catalina/util/Strftime.java | 1 +
java/org/apache/catalina/util/StringParser.java | 198 -
java/org/apache/catalina/util/UriUtil.java | 42 -
java/org/apache/catalina/util/XMLWriter.java | 14 +-
.../catalina/valves/AbstractAccessLogValve.java | 35 +-
.../org/apache/catalina/valves/AccessLogValve.java | 22 +-
.../valves/CometConnectionManagerValve.java | 343 -
.../apache/catalina/valves/ErrorReportValve.java | 6 +-
.../apache/catalina/valves/JDBCAccessLogValve.java | 27 +-
.../apache/catalina/valves/LocalStrings.properties | 2 -
.../catalina/valves/LocalStrings_es.properties | 2 -
.../apache/catalina/valves/PersistentValve.java | 3 +
.../apache/catalina/valves/RemoteAddrValve.java | 2 +
.../apache/catalina/valves/RemoteHostValve.java | 1 +
java/org/apache/catalina/valves/RemoteIpValve.java | 16 +-
.../apache/catalina/valves/RequestFilterValve.java | 14 +-
.../org/apache/catalina/valves/SemaphoreValve.java | 14 +-
java/org/apache/catalina/valves/ValveBase.java | 101 +-
.../apache/catalina/valves/mbeans-descriptors.xml | 2 +-
.../catalina/valves/rewrite/RewriteCond.java | 2 +
.../catalina/valves/rewrite/RewriteRule.java | 5 +-
.../catalina/valves/rewrite/RewriteValve.java | 31 +-
.../catalina/valves/rewrite/Substitution.java | 5 +-
.../catalina/valves/rewrite/mbeans-descriptors.xml | 2 +-
.../catalina/webresources/AbstractResource.java | 3 +-
.../catalina/webresources/AbstractResourceSet.java | 3 +-
java/org/apache/catalina/webresources/Cache.java | 3 +-
.../webresources/ClasspathURLStreamHandler.java | 2 +-
.../apache/catalina/webresources/Constants.java | 22 -
.../apache/catalina/webresources/FileResource.java | 55 +-
.../apache/catalina/webresources/StandardRoot.java | 12 +-
.../catalina/webresources/mbeans-descriptors.xml | 2 +-
java/org/apache/coyote/AbstractProcessor.java | 649 +-
java/org/apache/coyote/AbstractProcessorLight.java | 155 +
java/org/apache/coyote/AbstractProtocol.java | 564 +-
java/org/apache/coyote/ActionCode.java | 57 +-
java/org/apache/coyote/ActionHook.java | 3 -
java/org/apache/coyote/Adapter.java | 9 +-
java/org/apache/coyote/AsyncContextCallback.java | 2 +-
java/org/apache/coyote/AsyncStateMachine.java | 27 +-
java/org/apache/coyote/ByteBufferHolder.java | 64 -
java/org/apache/coyote/CloseNowException.java | 51 +
java/org/apache/coyote/Constants.java | 65 +-
java/org/apache/coyote/ErrorState.java | 36 +-
java/org/apache/coyote/InputBuffer.java | 52 +-
java/org/apache/coyote/LocalStrings.properties | 7 +
java/org/apache/coyote/LocalStrings_es.properties | 3 +
java/org/apache/coyote/OutputBuffer.java | 34 +-
java/org/apache/coyote/Processor.java | 89 +-
java/org/apache/coyote/ProtocolException.java | 42 +
java/org/apache/coyote/ProtocolHandler.java | 40 +-
java/org/apache/coyote/PushToken.java | 44 +
java/org/apache/coyote/Request.java | 134 +-
java/org/apache/coyote/RequestGroupInfo.java | 51 +-
java/org/apache/coyote/RequestInfo.java | 4 +-
java/org/apache/coyote/Response.java | 97 +-
java/org/apache/coyote/UpgradeProtocol.java | 93 +
.../apache/coyote/ajp/AbstractAjpProcessor.java | 1841 ------
.../org/apache/coyote/ajp/AbstractAjpProtocol.java | 124 +-
java/org/apache/coyote/ajp/AjpAprProcessor.java | 285 -
java/org/apache/coyote/ajp/AjpAprProtocol.java | 95 +-
java/org/apache/coyote/ajp/AjpMessage.java | 93 +-
java/org/apache/coyote/ajp/AjpNio2Processor.java | 265 -
java/org/apache/coyote/ajp/AjpNio2Protocol.java | 112 +-
java/org/apache/coyote/ajp/AjpNioProcessor.java | 216 -
java/org/apache/coyote/ajp/AjpNioProtocol.java | 139 +-
java/org/apache/coyote/ajp/AjpProcessor.java | 1466 ++++-
java/org/apache/coyote/ajp/AjpProtocol.java | 144 -
java/org/apache/coyote/ajp/Constants.java | 7 -
java/org/apache/coyote/ajp/LocalStrings.properties | 8 +-
.../coyote/http11/AbstractHttp11JsseProtocol.java | 106 +-
.../coyote/http11/AbstractHttp11Processor.java | 1865 ------
.../coyote/http11/AbstractHttp11Protocol.java | 451 +-
.../apache/coyote/http11/AbstractInputBuffer.java | 365 --
.../coyote/http11/AbstractNioInputBuffer.java | 667 --
.../apache/coyote/http11/AbstractOutputBuffer.java | 655 --
java/org/apache/coyote/http11/Constants.java | 60 +-
.../apache/coyote/http11/Http11AprProcessor.java | 520 --
.../apache/coyote/http11/Http11AprProtocol.java | 270 +-
.../apache/coyote/http11/Http11InputBuffer.java | 1093 ++++
.../apache/coyote/http11/Http11Nio2Processor.java | 589 --
.../apache/coyote/http11/Http11Nio2Protocol.java | 218 +-
.../apache/coyote/http11/Http11NioProcessor.java | 573 --
.../apache/coyote/http11/Http11NioProtocol.java | 227 +-
.../apache/coyote/http11/Http11OutputBuffer.java | 594 ++
java/org/apache/coyote/http11/Http11Processor.java | 1797 +++++-
java/org/apache/coyote/http11/Http11Protocol.java | 191 -
java/org/apache/coyote/http11/InputFilter.java | 31 +-
.../coyote/http11/InternalAprInputBuffer.java | 709 ---
.../coyote/http11/InternalAprOutputBuffer.java | 365 --
.../apache/coyote/http11/InternalInputBuffer.java | 592 --
.../coyote/http11/InternalNio2InputBuffer.java | 352 --
.../coyote/http11/InternalNio2OutputBuffer.java | 546 --
.../coyote/http11/InternalNioInputBuffer.java | 188 -
.../coyote/http11/InternalNioOutputBuffer.java | 336 -
.../apache/coyote/http11/InternalOutputBuffer.java | 243 -
.../apache/coyote/http11/LocalStrings.properties | 12 +-
.../coyote/http11/LocalStrings_es.properties | 1 -
java/org/apache/coyote/http11/OutputFilter.java | 22 +-
.../coyote/http11/filters/BufferedInputFilter.java | 75 +-
.../coyote/http11/filters/ChunkedInputFilter.java | 200 +-
.../coyote/http11/filters/ChunkedOutputFilter.java | 96 +-
.../coyote/http11/filters/GzipOutputFilter.java | 46 +-
.../coyote/http11/filters/IdentityInputFilter.java | 78 +-
.../http11/filters/IdentityOutputFilter.java | 52 +-
.../http11/filters/SavedRequestInputFilter.java | 20 +-
.../coyote/http11/filters/VoidInputFilter.java | 13 +-
.../coyote/http11/filters/VoidOutputFilter.java | 16 +-
.../coyote/http11/upgrade/AbstractProcessor.java | 190 -
.../http11/upgrade/AbstractServletInputStream.java | 250 -
.../upgrade/AbstractServletOutputStream.java | 268 -
.../apache/coyote/http11/upgrade/AprProcessor.java | 45 -
.../http11/upgrade/AprServletInputStream.java | 151 -
.../http11/upgrade/AprServletOutputStream.java | 168 -
.../apache/coyote/http11/upgrade/BioProcessor.java | 44 -
.../http11/upgrade/BioServletInputStream.java | 67 -
.../http11/upgrade/BioServletOutputStream.java | 51 -
.../apache/coyote/http11/upgrade/Constants.java | 22 -
.../http11/upgrade/InternalHttpUpgradeHandler.java | 40 +
.../coyote/http11/upgrade/LocalStrings.properties | 18 +-
.../coyote/http11/upgrade/Nio2Processor.java | 49 -
.../http11/upgrade/Nio2ServletInputStream.java | 235 -
.../http11/upgrade/Nio2ServletOutputStream.java | 199 -
.../apache/coyote/http11/upgrade/NioProcessor.java | 55 -
.../http11/upgrade/NioServletInputStream.java | 140 -
.../http11/upgrade/NioServletOutputStream.java | 140 -
.../http11/upgrade/UpgradeProcessorBase.java | 97 +
.../http11/upgrade/UpgradeProcessorExternal.java | 139 +
.../http11/upgrade/UpgradeProcessorInternal.java | 95 +
.../http11/upgrade/UpgradeServletInputStream.java | 253 +
.../http11/upgrade/UpgradeServletOutputStream.java | 282 +
java/org/apache/coyote/http2/AbstractStream.java | 149 +
java/org/apache/coyote/http2/ByteUtil.java | 94 +
.../apache/coyote/http2/ConnectionException.java | 29 +
.../coyote/http2/ConnectionSettingsBase.java | 218 +
.../coyote/http2/ConnectionSettingsLocal.java | 100 +
.../coyote/http2/ConnectionSettingsRemote.java | 34 +
java/org/apache/coyote/http2/Constants.java | 33 +
java/org/apache/coyote/http2/Flags.java | 49 +
java/org/apache/coyote/http2/FrameType.java | 138 +
java/org/apache/coyote/http2/HPackHuffman.java | 567 ++
java/org/apache/coyote/http2/HeaderSink.java | 37 +
java/org/apache/coyote/http2/Hpack.java | 216 +
java/org/apache/coyote/http2/HpackDecoder.java | 453 ++
java/org/apache/coyote/http2/HpackEncoder.java | 398 ++
java/org/apache/coyote/http2/HpackException.java | 36 +
java/org/apache/coyote/http2/Http2Error.java | 53 +
java/org/apache/coyote/http2/Http2Exception.java | 35 +
java/org/apache/coyote/http2/Http2Parser.java | 659 ++
java/org/apache/coyote/http2/Http2Protocol.java | 279 +
.../apache/coyote/http2/Http2UpgradeHandler.java | 1560 +++++
.../apache/coyote/http2/LocalStrings.properties | 137 +
java/org/apache/coyote/http2/Setting.java | 68 +
java/org/apache/coyote/http2/Stream.java | 918 +++
java/org/apache/coyote/http2/StreamException.java | 37 +
java/org/apache/coyote/http2/StreamProcessor.java | 266 +
.../apache/coyote/http2/StreamStateMachine.java | 257 +
java/org/apache/el/lang/ExpressionBuilder.java | 94 +-
java/org/apache/el/util/ReflectionUtil.java | 2 +-
java/org/apache/jasper/Constants.java | 13 -
java/org/apache/jasper/EmbeddedServletOptions.java | 15 +-
java/org/apache/jasper/JasperException.java | 11 +-
java/org/apache/jasper/JspC.java | 58 +-
java/org/apache/jasper/JspCompilationContext.java | 35 +-
java/org/apache/jasper/Options.java | 68 +-
java/org/apache/jasper/compiler/AntCompiler.java | 3 +
.../org/apache/jasper/compiler/BeanRepository.java | 2 +
java/org/apache/jasper/compiler/Compiler.java | 51 +-
.../apache/jasper/compiler/ELFunctionMapper.java | 1 +
java/org/apache/jasper/compiler/ELInterpreter.java | 3 +-
.../jasper/compiler/ELInterpreterFactory.java | 3 +
java/org/apache/jasper/compiler/ELNode.java | 9 +-
java/org/apache/jasper/compiler/ELParser.java | 2 +-
.../apache/jasper/compiler/ErrorDispatcher.java | 86 +-
java/org/apache/jasper/compiler/ErrorHandler.java | 4 +
java/org/apache/jasper/compiler/Generator.java | 40 +-
.../jasper/compiler/ImplicitTagLibraryInfo.java | 4 +-
java/org/apache/jasper/compiler/JDTCompiler.java | 3 -
.../apache/jasper/compiler/JarScannerFactory.java | 2 +
java/org/apache/jasper/compiler/JspConfig.java | 2 +
.../apache/jasper/compiler/JspRuntimeContext.java | 18 +-
java/org/apache/jasper/compiler/JspUtil.java | 36 +-
java/org/apache/jasper/compiler/Node.java | 40 +-
java/org/apache/jasper/compiler/PageDataImpl.java | 5 +-
java/org/apache/jasper/compiler/PageInfo.java | 7 +-
java/org/apache/jasper/compiler/Parser.java | 25 +-
.../apache/jasper/compiler/ParserController.java | 39 +-
java/org/apache/jasper/compiler/ServletWriter.java | 19 +-
java/org/apache/jasper/compiler/SmapGenerator.java | 1 +
java/org/apache/jasper/compiler/SmapStratum.java | 26 +-
java/org/apache/jasper/compiler/SmapUtil.java | 6 +-
.../apache/jasper/compiler/TagFileProcessor.java | 16 +-
.../apache/jasper/compiler/TagLibraryInfoImpl.java | 11 +-
.../compiler/tagplugin/TagPluginContext.java | 12 +-
java/org/apache/jasper/el/ELResolverImpl.java | 17 +-
.../jasper/resources/LocalStrings.properties | 6 +-
.../jasper/resources/LocalStrings_es.properties | 4 +-
.../jasper/resources/LocalStrings_fr.properties | 4 +-
.../jasper/resources/LocalStrings_ja.properties | 4 +-
.../org/apache/jasper/runtime/BodyContentImpl.java | 69 +-
.../apache/jasper/runtime/JspRuntimeLibrary.java | 10 +
.../apache/jasper/runtime/JspSourceDependent.java | 1 +
java/org/apache/jasper/runtime/JspWriterImpl.java | 7 +-
.../org/apache/jasper/runtime/PageContextImpl.java | 1 +
.../jasper/runtime/ProtectedFunctionMapper.java | 1 +
.../apache/jasper/security/SecurityClassLoad.java | 6 +-
java/org/apache/jasper/security/SecurityUtil.java | 2 +
java/org/apache/jasper/servlet/JasperLoader.java | 6 +-
.../apache/jasper/servlet/JspCServletContext.java | 2 +-
java/org/apache/jasper/servlet/JspServlet.java | 33 +-
.../apache/jasper/servlet/JspServletWrapper.java | 9 +-
java/org/apache/jasper/servlet/TldScanner.java | 10 +-
.../apache/jasper/servlet/mbeans-descriptors.xml | 2 +-
java/org/apache/jasper/tagplugins/jstl/Util.java | 20 +-
.../apache/jasper/tagplugins/jstl/tagPlugins.xml | 2 +-
java/org/apache/jasper/xmlparser/EncodingMap.java | 513 +-
java/org/apache/jasper/xmlparser/SymbolTable.java | 43 +-
java/org/apache/jasper/xmlparser/UCSReader.java | 6 +-
java/org/apache/jasper/xmlparser/UTF8Reader.java | 5 +-
java/org/apache/jasper/xmlparser/XMLChar.java | 12 +-
.../jasper/xmlparser/XMLEncodingDetector.java | 55 +-
java/org/apache/jasper/xmlparser/XMLString.java | 24 +-
.../apache/jasper/xmlparser/XMLStringBuffer.java | 35 +-
java/org/apache/juli/AsyncFileHandler.java | 23 +-
java/org/apache/juli/ClassLoaderLogManager.java | 9 +-
java/org/apache/juli/DateFormatCache.java | 7 +-
java/org/apache/juli/OneLineFormatter.java | 53 +-
java/org/apache/juli/logging/DirectJDKLog.java | 7 +-
java/org/apache/juli/logging/Log.java | 18 +
java/org/apache/juli/logging/LogFactory.java | 10 +
java/org/apache/naming/Constants.java | 6 +-
.../org/apache/naming/ContextAccessController.java | 25 +-
java/org/apache/naming/ContextBindings.java | 44 +-
java/org/apache/naming/EjbRef.java | 9 +-
java/org/apache/naming/NamingContext.java | 37 +-
java/org/apache/naming/ResourceLinkRef.java | 3 +
java/org/apache/naming/ResourceRef.java | 9 +
java/org/apache/naming/SelectorContext.java | 19 +-
java/org/apache/naming/ServiceRef.java | 2 +
java/org/apache/naming/factory/FactoryBase.java | 3 +-
.../org/apache/naming/factory/SendMailFactory.java | 1 -
java/org/apache/tomcat/ContextBind.java | 5 +-
.../org/apache/tomcat/InstanceManagerBindings.java | 36 +
java/org/apache/tomcat/JarScannerCallback.java | 26 +-
java/org/apache/tomcat/buildutil/SignCode.java | 3 +
.../apache/tomcat/dbcp/dbcp2/BasicDataSource.java | 57 +-
.../tomcat/dbcp/dbcp2/BasicDataSourceFactory.java | 4 +-
.../dbcp/dbcp2/DelegatingCallableStatement.java | 2 +-
.../tomcat/dbcp/dbcp2/DelegatingConnection.java | 10 +-
.../dbcp/dbcp2/DelegatingDatabaseMetaData.java | 1 +
.../dbcp/dbcp2/DelegatingPreparedStatement.java | 2 +-
.../tomcat/dbcp/dbcp2/DelegatingResultSet.java | 9 +-
.../tomcat/dbcp/dbcp2/DelegatingStatement.java | 6 +-
.../dbcp/dbcp2/LifetimeExceededException.java | 3 +-
.../tomcat/dbcp/dbcp2/PoolableConnection.java | 2 +-
.../dbcp/dbcp2/PoolableConnectionFactory.java | 4 +-
.../tomcat/dbcp/dbcp2/PoolingConnection.java | 20 +-
.../tomcat/dbcp/dbcp2/PoolingDataSource.java | 2 +-
.../apache/tomcat/dbcp/dbcp2/PoolingDriver.java | 16 +-
java/org/apache/tomcat/dbcp/dbcp2/Utils.java | 5 +
.../dbcp/dbcp2/cpdsadapter/DriverAdapterCPDS.java | 16 +-
.../dbcp2/datasources/CPDSConnectionFactory.java | 11 +-
.../dbcp2/datasources/InstanceKeyDataSource.java | 61 +-
.../datasources/InstanceKeyDataSourceFactory.java | 16 +-
.../datasources/KeyedCPDSConnectionFactory.java | 9 +-
.../dbcp2/datasources/PerUserPoolDataSource.java | 87 +-
.../dbcp2/datasources/SharedPoolDataSource.java | 12 +-
.../tomcat/dbcp/pool2/impl/GenericObjectPool.java | 1 -
.../dbcp/pool2/impl/SoftReferenceObjectPool.java | 2 +-
java/org/apache/tomcat/jni/Address.java | 7 +-
java/org/apache/tomcat/jni/Buffer.java | 2 +
.../org/apache/tomcat/jni/CertificateVerifier.java | 34 +
java/org/apache/tomcat/jni/Directory.java | 11 +-
java/org/apache/tomcat/jni/File.java | 33 +-
java/org/apache/tomcat/jni/Global.java | 6 +
java/org/apache/tomcat/jni/Local.java | 9 +-
java/org/apache/tomcat/jni/Lock.java | 10 +
java/org/apache/tomcat/jni/Mmap.java | 4 +
java/org/apache/tomcat/jni/Multicast.java | 5 +
java/org/apache/tomcat/jni/OS.java | 4 +
java/org/apache/tomcat/jni/Poll.java | 22 +
java/org/apache/tomcat/jni/Pool.java | 1 +
java/org/apache/tomcat/jni/Proc.java | 5 +
java/org/apache/tomcat/jni/Procattr.java | 14 +-
java/org/apache/tomcat/jni/Registry.java | 10 +
java/org/apache/tomcat/jni/SSL.java | 372 +-
java/org/apache/tomcat/jni/SSLContext.java | 284 +-
java/org/apache/tomcat/jni/SSLSocket.java | 21 +
java/org/apache/tomcat/jni/Shm.java | 7 +-
java/org/apache/tomcat/jni/Socket.java | 70 +-
java/org/apache/tomcat/jni/Status.java | 2 +-
java/org/apache/tomcat/jni/Stdlib.java | 6 +
java/org/apache/tomcat/jni/Thread.java | 2 +-
java/org/apache/tomcat/jni/Time.java | 12 +-
java/org/apache/tomcat/jni/User.java | 8 +
java/org/apache/tomcat/util/Diagnostics.java | 5 +-
.../org/apache/tomcat/util/IntrospectionUtils.java | 20 +-
java/org/apache/tomcat/util/buf/Ascii.java | 7 +-
java/org/apache/tomcat/util/buf/B2CConverter.java | 98 +-
.../apache/tomcat/util/buf/ByteBufferHolder.java | 55 +
.../apache/tomcat/util/buf/ByteBufferUtils.java | 112 +
java/org/apache/tomcat/util/buf/ByteChunk.java | 240 +-
java/org/apache/tomcat/util/buf/C2BConverter.java | 86 +-
java/org/apache/tomcat/util/buf/CharChunk.java | 131 +-
.../apache/tomcat/util/buf/LocalStrings.properties | 2 +
java/org/apache/tomcat/util/buf/MessageBytes.java | 58 +-
java/org/apache/tomcat/util/buf/StringCache.java | 26 +-
java/org/apache/tomcat/util/buf/UDecoder.java | 31 +-
java/org/apache/tomcat/util/buf/UEncoder.java | 30 +-
.../apache/tomcat/util/codec/binary/Base64.java | 37 +-
.../tomcat/util/codec/binary/BaseNCodec.java | 26 +-
.../util/collections/CaseInsensitiveKeyMap.java | 208 +
.../collections/ManagedConcurrentWeakHashMap.java | 20 +-
.../tomcat/util/collections/SynchronizedQueue.java | 2 +
.../tomcat/util/collections/SynchronizedStack.java | 8 +-
java/org/apache/tomcat/util/compat/Jre8Compat.java | 37 +-
java/org/apache/tomcat/util/compat/JreCompat.java | 11 +-
.../tomcat/util/descriptor/DigesterFactory.java | 1 +
.../tomcat/util/descriptor/LocalResolver.java | 2 +-
.../util/descriptor/tld/ImplicitTldRuleSet.java | 3 +-
.../util/descriptor/tld/TldResourcePath.java | 8 -
.../tomcat/util/descriptor/web/ContextHandler.java | 2 +
.../tomcat/util/descriptor/web/ContextService.java | 2 +-
.../util/descriptor/web/ContextTransaction.java | 9 +-
.../tomcat/util/descriptor/web/ErrorPage.java | 6 +-
.../tomcat/util/descriptor/web/FilterMap.java | 3 +-
.../descriptor/web/FragmentJarScannerCallback.java | 8 -
.../tomcat/util/descriptor/web/ResourceBase.java | 7 +-
.../util/descriptor/web/SecurityCollection.java | 29 +-
.../util/descriptor/web/SecurityConstraint.java | 136 +-
.../tomcat/util/descriptor/web/ServletDef.java | 1 +
.../tomcat/util/descriptor/web/WebRuleSet.java | 2 +
.../apache/tomcat/util/descriptor/web/WebXml.java | 30 +-
.../util/descriptor/web/mbeans-descriptors.xml | 2 +-
.../apache/tomcat/util/digester/ArrayStack.java | 2 +
.../tomcat/util/digester/CallMethodRule.java | 2 +
java/org/apache/tomcat/util/digester/Digester.java | 64 +-
.../util/digester/ObjectCreationFactory.java | 8 +-
java/org/apache/tomcat/util/digester/Rule.java | 151 +-
java/org/apache/tomcat/util/digester/RuleSet.java | 2 +-
java/org/apache/tomcat/util/digester/Rules.java | 6 +-
.../org/apache/tomcat/util/digester/RulesBase.java | 1 +
.../apache/tomcat/util/http/CookieProcessor.java | 17 +-
.../org/apache/tomcat/util/http/CookieSupport.java | 175 -
.../tomcat/util/http/FastHttpDateFormat.java | 26 +-
java/org/apache/tomcat/util/http/HttpMessages.java | 181 -
.../tomcat/util/http/LegacyCookieProcessor.java | 47 +-
java/org/apache/tomcat/util/http/MimeHeaders.java | 66 +-
java/org/apache/tomcat/util/http/Parameters.java | 5 +-
.../tomcat/util/http/Rfc6265CookieProcessor.java | 9 +-
.../org/apache/tomcat/util/http/ServerCookies.java | 1 +
.../apache/tomcat/util/http/SetCookieSupport.java | 49 -
.../util/http/fileupload/MultipartStream.java | 2 +-
.../tomcat/util/http/parser/Authorization.java | 7 +-
.../util/http/parser/LocalStrings.properties | 1 +
.../tomcat/util/http/res/LocalStrings.properties | 78 -
.../util/http/res/LocalStrings_es.properties | 61 -
.../util/http/res/LocalStrings_fr.properties | 61 -
.../util/http/res/LocalStrings_ja.properties | 19 -
.../apache/tomcat/util/log/SystemLogHandler.java | 9 +-
.../apache/tomcat/util/modeler/AttributeInfo.java | 12 +-
.../tomcat/util/modeler/BaseAttributeFilter.java | 9 +-
.../apache/tomcat/util/modeler/BaseModelMBean.java | 1 +
.../apache/tomcat/util/modeler/FeatureInfo.java | 8 +-
.../apache/tomcat/util/modeler/ManagedBean.java | 24 +-
.../tomcat/util/modeler/NotificationInfo.java | 3 +-
.../apache/tomcat/util/modeler/OperationInfo.java | 12 +-
.../apache/tomcat/util/modeler/ParameterInfo.java | 1 +
java/org/apache/tomcat/util/modeler/Registry.java | 120 +-
.../apache/tomcat/util/modeler/RegistryMBean.java | 26 +-
.../tomcat/util/modeler/mbeans-descriptors.dtd | 2 +-
.../MbeansDescriptorsIntrospectionSource.java | 5 +-
.../tomcat/util/modeler/modules/ModelerSource.java | 7 +-
.../apache/tomcat/util/net/AbstractEndpoint.java | 513 +-
.../tomcat/util/net/AbstractJsseEndpoint.java | 240 +
.../tomcat/util/net/ApplicationBufferHandler.java | 33 +
java/org/apache/tomcat/util/net/AprEndpoint.java | 1534 +++--
java/org/apache/tomcat/util/net/AprSSLSupport.java | 134 +
java/org/apache/tomcat/util/net/Constants.java | 8 +-
.../util/net/DefaultServerSocketFactory.java | 67 -
java/org/apache/tomcat/util/net/DispatchType.java | 10 +-
java/org/apache/tomcat/util/net/JIoEndpoint.java | 551 --
.../apache/tomcat/util/net/LocalStrings.properties | 133 +
.../tomcat/util/net/LocalStrings_es.properties | 39 +
.../util/net/{res => }/LocalStrings_fr.properties | 0
.../util/net/{res => }/LocalStrings_ja.properties | 0
java/org/apache/tomcat/util/net/Nio2Channel.java | 64 +-
java/org/apache/tomcat/util/net/Nio2Endpoint.java | 1829 ++++--
.../tomcat/util/net/NioBlockingSelector.java | 22 +-
java/org/apache/tomcat/util/net/NioChannel.java | 63 +-
java/org/apache/tomcat/util/net/NioEndpoint.java | 1152 ++--
.../apache/tomcat/util/net/NioSelectorPool.java | 28 +-
java/org/apache/tomcat/util/net/SSLContext.java | 50 +
java/org/apache/tomcat/util/net/SSLHostConfig.java | 787 +++
.../tomcat/util/net/SSLHostConfigCertificate.java | 269 +
.../apache/tomcat/util/net/SSLImplementation.java | 81 +-
java/org/apache/tomcat/util/net/SSLSupport.java | 32 +-
java/org/apache/tomcat/util/net/SSLUtil.java | 60 +-
java/org/apache/tomcat/util/net/SSLUtilBase.java | 177 +
.../apache/tomcat/util/net/SecureNio2Channel.java | 661 +-
.../apache/tomcat/util/net/SecureNioChannel.java | 337 +-
.../apache/tomcat/util/net/SendfileDataBase.java | 54 +
.../tomcat/util/net/ServerSocketFactory.java | 97 -
.../tomcat/util/net/SocketBufferHandler.java | 167 +
java/org/apache/tomcat/util/net/SocketEvent.java | 64 +
.../tomcat/util/net/SocketProcessorBase.java | 55 +
.../apache/tomcat/util/net/SocketProperties.java | 24 -
java/org/apache/tomcat/util/net/SocketStatus.java | 35 -
java/org/apache/tomcat/util/net/SocketWrapper.java | 182 -
.../apache/tomcat/util/net/SocketWrapperBase.java | 1071 ++++
.../tomcat/util/net/TLSClientHelloExtractor.java | 252 +
java/org/apache/tomcat/util/net/URL.java | 661 --
.../tomcat/util/net/jsse/JSSEImplementation.java | 26 +-
.../tomcat/util/net/jsse/JSSEKeyManager.java | 130 +-
.../tomcat/util/net/jsse/JSSESSLContext.java | 70 +
.../tomcat/util/net/jsse/JSSESocketFactory.java | 858 ---
.../apache/tomcat/util/net/jsse/JSSESupport.java | 131 +-
java/org/apache/tomcat/util/net/jsse/JSSEUtil.java | 374 ++
.../tomcat/util/net/jsse/LocalStrings.properties | 40 +
.../util/net/jsse/LocalStrings_es.properties | 17 +
.../net/jsse/{res => }/LocalStrings_fr.properties | 0
.../net/jsse/{res => }/LocalStrings_ja.properties | 0
.../tomcat/util/net/jsse/NioX509KeyManager.java | 92 -
java/org/apache/tomcat/util/net/jsse/PEMFile.java | 150 +
.../util/net/jsse/openssl/Authentication.java | 33 -
.../tomcat/util/net/jsse/openssl/Cipher.java | 4746 --------------
.../tomcat/util/net/jsse/openssl/Encryption.java | 41 -
.../util/net/jsse/openssl/EncryptionLevel.java | 28 -
.../tomcat/util/net/jsse/openssl/KeyExchange.java | 36 -
.../util/net/jsse/openssl/MessageDigest.java | 28 -
.../openssl/OpenSSLCipherConfigurationParser.java | 804 ---
.../tomcat/util/net/jsse/openssl/Protocol.java | 43 -
.../util/net/jsse/res/LocalStrings.properties | 37 -
.../util/net/jsse/res/LocalStrings_es.properties | 19 -
.../util/net/openssl/LocalStrings.properties | 48 +
.../tomcat/util/net/openssl/OpenSSLContext.java | 397 ++
.../tomcat/util/net/openssl/OpenSSLEngine.java | 1298 ++++
.../util/net/openssl/OpenSSLImplementation.java | 39 +
.../tomcat/util/net/openssl/OpenSSLKeyManager.java | 43 +
.../tomcat/util/net/openssl/OpenSSLProtocols.java | 45 +
.../util/net/openssl/OpenSSLSessionContext.java | 141 +
.../util/net/openssl/OpenSSLSessionStats.java | 126 +
.../tomcat/util/net/openssl/OpenSSLUtil.java | 105 +
.../util/net/openssl/OpenSSLX509Certificate.java | 190 +
.../util/net/openssl/ciphers/Authentication.java | 33 +
.../tomcat/util/net/openssl/ciphers/Cipher.java | 4747 ++++++++++++++
.../util/net/openssl/ciphers/Encryption.java | 41 +
.../util/net/openssl/ciphers/EncryptionLevel.java | 28 +
.../util/net/openssl/ciphers/KeyExchange.java | 36 +
.../net/openssl/ciphers/LocalStrings.properties | 16 +
.../util/net/openssl/ciphers/MessageDigest.java | 28 +
.../ciphers/OpenSSLCipherConfigurationParser.java | 827 +++
.../tomcat/util/net/openssl/ciphers/Protocol.java | 43 +
.../tomcat/util/net/res/LocalStrings.properties | 90 -
.../tomcat/util/net/res/LocalStrings_es.properties | 36 -
java/org/apache/tomcat/util/scan/Constants.java | 7 -
java/org/apache/tomcat/util/scan/Jar.java | 33 -
.../tomcat/util/scan/StandardJarScanner.java | 4 +
java/org/apache/tomcat/util/threads/Constants.java | 2 -
.../org/apache/tomcat/util/threads/LimitLatch.java | 5 +
.../tomcat/util/threads/ThreadPoolExecutor.java | 2 +
.../tomcat/websocket/AsyncChannelGroupUtil.java | 2 +-
.../websocket/AsyncChannelWrapperSecure.java | 14 +-
.../tomcat/websocket/BackgroundProcessManager.java | 2 +-
.../tomcat/websocket/CaseInsensitiveKeyMap.java | 208 -
java/org/apache/tomcat/websocket/Constants.java | 42 +-
.../tomcat/websocket/FutureToSendHandler.java | 14 +-
.../tomcat/websocket/LocalStrings.properties | 10 +-
java/org/apache/tomcat/websocket/MessagePart.java | 9 +-
.../apache/tomcat/websocket/PerMessageDeflate.java | 15 +-
.../websocket/SendHandlerToCompletionHandler.java | 42 -
.../tomcat/websocket/TransformationFactory.java | 2 +-
java/org/apache/tomcat/websocket/Util.java | 3 +-
java/org/apache/tomcat/websocket/WsFrameBase.java | 70 +-
.../org/apache/tomcat/websocket/WsFrameClient.java | 18 +-
.../tomcat/websocket/WsHandshakeResponse.java | 2 +
.../tomcat/websocket/WsRemoteEndpointImplBase.java | 274 +-
.../websocket/WsRemoteEndpointImplClient.java | 39 +-
java/org/apache/tomcat/websocket/WsSession.java | 30 +-
.../tomcat/websocket/WsWebSocketContainer.java | 64 +-
.../apache/tomcat/websocket/pojo/Constants.java | 6 +-
.../tomcat/websocket/pojo/PojoEndpointBase.java | 3 +-
.../tomcat/websocket/pojo/PojoEndpointServer.java | 12 +-
.../pojo/PojoMessageHandlerWholeBinary.java | 2 +-
.../pojo/PojoMessageHandlerWholeText.java | 2 +-
.../tomcat/websocket/pojo/PojoMethodMapping.java | 2 +-
.../apache/tomcat/websocket/server/Constants.java | 15 -
.../websocket/server/LocalStrings.properties | 3 +-
.../tomcat/websocket/server/UpgradeUtil.java | 2 +-
.../tomcat/websocket/server/UriTemplate.java | 3 +-
.../apache/tomcat/websocket/server/WsFilter.java | 2 -
.../tomcat/websocket/server/WsFrameServer.java | 77 +-
.../websocket/server/WsHandshakeRequest.java | 2 +-
.../websocket/server/WsHttpUpgradeHandler.java | 199 +-
.../server/WsRemoteEndpointImplServer.java | 128 +-
.../tomcat/websocket/server/WsServerContainer.java | 130 +-
modules/bayeux/.classpath | 9 -
modules/bayeux/.project | 17 -
modules/bayeux/build.xml | 228 -
.../java/org/apache/cometd/bayeux/Bayeux.java | 241 -
.../java/org/apache/cometd/bayeux/Channel.java | 102 -
.../java/org/apache/cometd/bayeux/Client.java | 90 -
.../java/org/apache/cometd/bayeux/DataFilter.java | 37 -
.../java/org/apache/cometd/bayeux/Listener.java | 44 -
.../java/org/apache/cometd/bayeux/Message.java | 67 -
.../org/apache/cometd/bayeux/SecurityPolicy.java | 28 -
.../org/apache/tomcat/bayeux/BayeuxException.java | 38 -
.../org/apache/tomcat/bayeux/BayeuxRequest.java | 53 -
.../org/apache/tomcat/bayeux/BayeuxServlet.java | 235 -
.../java/org/apache/tomcat/bayeux/ChannelImpl.java | 188 -
.../java/org/apache/tomcat/bayeux/ClientImpl.java | 279 -
.../java/org/apache/tomcat/bayeux/HttpError.java | 60 -
.../java/org/apache/tomcat/bayeux/MessageImpl.java | 80 -
.../java/org/apache/tomcat/bayeux/RequestBase.java | 258 -
.../org/apache/tomcat/bayeux/RequestFactory.java | 48 -
.../org/apache/tomcat/bayeux/TomcatBayeux.java | 175 -
.../tomcat/bayeux/request/MetaConnectRequest.java | 124 -
.../bayeux/request/MetaDisconnectRequest.java | 104 -
.../bayeux/request/MetaHandshakeRequest.java | 115 -
.../bayeux/request/MetaSubscribeRequest.java | 129 -
.../bayeux/request/MetaUnsubscribeRequest.java | 129 -
.../tomcat/bayeux/request/PublishRequest.java | 141 -
.../cometd/bayeux/samples/BayeuxStockTicker.java | 232 -
.../cometd/bayeux/samples/EchoChatClient.java | 118 -
modules/bayeux/webapps/cometd/WEB-INF/web.xml | 53 -
.../cometd/examples/simplechat/cometdchat.htm | 130 -
.../webapps/cometd/examples/simplechat/ticker.html | 143 -
modules/bayeux/webapps/cometd/index.html | 22 -
modules/jdbc-pool/build.xml | 2 +-
modules/jdbc-pool/doc/changelog.xml | 2 +-
modules/jdbc-pool/doc/jdbc-pool.xml | 4 +-
modules/jdbc-pool/doc/package.xsl | 2 +-
modules/jdbc-pool/doc/project.xml | 2 +-
modules/jdbc-pool/pom.xml | 6 +-
.../apache/tomcat/jdbc/pool/ConnectionPool.java | 37 +-
.../org/apache/tomcat/jdbc/pool/DataSource.java | 4 +-
.../apache/tomcat/jdbc/pool/DataSourceFactory.java | 11 +-
.../apache/tomcat/jdbc/pool/DataSourceProxy.java | 44 +-
.../apache/tomcat/jdbc/pool/FairBlockingQueue.java | 1 +
.../apache/tomcat/jdbc/pool/JdbcInterceptor.java | 16 +-
.../jdbc/pool/MultiLockFairBlockingQueue.java | 1 +
.../apache/tomcat/jdbc/pool/PoolConfiguration.java | 32 +-
.../apache/tomcat/jdbc/pool/PoolProperties.java | 2 +-
.../apache/tomcat/jdbc/pool/PooledConnection.java | 16 +-
.../org/apache/tomcat/jdbc/pool/XADataSource.java | 2 +-
.../AbstractCreateStatementInterceptor.java | 1 +
.../jdbc/pool/interceptor/AbstractQueryReport.java | 2 +-
.../jdbc/pool/interceptor/SlowQueryReport.java | 4 +-
.../jdbc/pool/interceptor/SlowQueryReportJmx.java | 4 +-
.../jdbc/pool/interceptor/StatementCache.java | 4 +
.../interceptor/StatementDecoratorInterceptor.java | 5 +-
.../jdbc/pool/interceptor/mbeans-descriptors.xml | 2 +-
.../tomcat/jdbc/pool/jmx/ConnectionPool.java | 4 +-
.../apache/tomcat/jdbc/pool/mbeans-descriptors.xml | 2 +-
.../jdbc/pool/interceptor/InduceSlowQuery.java | 2 +-
.../jdbc/pool/interceptor/TestInterceptor.java | 2 +-
.../apache/tomcat/jdbc/test/CreateTestTable.java | 2 +-
.../test/TestJdbcInterceptorConfigParsing.java | 4 +-
.../tomcat/jdbc/test/TestStatementCache.java | 1 -
modules/tomcat-lite/.classpath | 25 -
modules/tomcat-lite/.project | 17 -
modules/tomcat-lite/build.xml | 319 -
modules/tomcat-lite/ivy.xml | 32 -
.../apache/coyote/lite/LiteProtocolHandler.java | 426 --
.../org/apache/tomcat/lite/http/BaseMapper.java | 1112 ----
.../apache/tomcat/lite/http/CompressFilter.java | 225 -
.../org/apache/tomcat/lite/http/ContentType.java | 96 -
.../tomcat/lite/http/DefaultHttpConnector.java | 23 -
.../org/apache/tomcat/lite/http/Dispatcher.java | 199 -
.../apache/tomcat/lite/http/Http11Connection.java | 1459 -----
.../org/apache/tomcat/lite/http/HttpChannel.java | 830 ---
.../org/apache/tomcat/lite/http/HttpClient.java | 22 -
.../tomcat/lite/http/HttpConnectionPool.java | 399 --
.../org/apache/tomcat/lite/http/HttpConnector.java | 514 --
.../org/apache/tomcat/lite/http/HttpMessage.java | 508 --
.../org/apache/tomcat/lite/http/HttpRequest.java | 1019 ---
.../org/apache/tomcat/lite/http/HttpResponse.java | 581 --
.../org/apache/tomcat/lite/http/HttpServer.java | 35 -
.../org/apache/tomcat/lite/http/HttpWriter.java | 313 -
.../org/apache/tomcat/lite/http/MappingData.java | 69 -
.../java/org/apache/tomcat/lite/http/MultiMap.java | 357 --
.../org/apache/tomcat/lite/http/ServerCookie.java | 819 ---
.../apache/tomcat/lite/http/SpdyConnection.java | 820 ---
.../java/org/apache/tomcat/lite/http/package.html | 0
.../java/org/apache/tomcat/lite/io/BBucket.java | 41 -
.../java/org/apache/tomcat/lite/io/BBuffer.java | 1204 ----
.../apache/tomcat/lite/io/BufferedIOReader.java | 380 --
.../java/org/apache/tomcat/lite/io/CBucket.java | 508 --
.../java/org/apache/tomcat/lite/io/CBuffer.java | 387 --
.../org/apache/tomcat/lite/io/DumpChannel.java | 120 -
.../apache/tomcat/lite/io/FastHttpDateFormat.java | 231 -
.../org/apache/tomcat/lite/io/FileConnector.java | 27 -
.../apache/tomcat/lite/io/FileConnectorJavaIo.java | 49 -
.../org/apache/tomcat/lite/io/FutureCallbacks.java | 171 -
.../java/org/apache/tomcat/lite/io/Hex.java | 249 -
.../java/org/apache/tomcat/lite/io/IOBuffer.java | 698 --
.../java/org/apache/tomcat/lite/io/IOChannel.java | 371 --
.../org/apache/tomcat/lite/io/IOConnector.java | 66 -
.../org/apache/tomcat/lite/io/IOInputStream.java | 71 -
.../org/apache/tomcat/lite/io/IOOutputStream.java | 204 -
.../java/org/apache/tomcat/lite/io/IOReader.java | 236 -
.../java/org/apache/tomcat/lite/io/IOWriter.java | 212 -
.../apache/tomcat/lite/io/MemoryIOConnector.java | 88 -
.../java/org/apache/tomcat/lite/io/NioChannel.java | 198 -
.../java/org/apache/tomcat/lite/io/NioThread.java | 1154 ----
.../org/apache/tomcat/lite/io/SocketConnector.java | 132 -
.../org/apache/tomcat/lite/io/SocketIOChannel.java | 271 -
.../org/apache/tomcat/lite/io/SslProvider.java | 24 -
.../org/apache/tomcat/lite/io/UrlEncoding.java | 215 -
.../apache/tomcat/lite/io/WrappedException.java | 40 -
.../tomcat/lite/io/jsse/JsseSslProvider.java | 466 --
.../org/apache/tomcat/lite/io/jsse/SslChannel.java | 636 --
.../java/org/apache/tomcat/lite/io/package.html | 7 -
.../org/apache/tomcat/lite/proxy/CopyCallback.java | 57 -
.../apache/tomcat/lite/proxy/HttpProxyService.java | 368 --
.../tomcat/lite/proxy/ProxyFlushedCallback.java | 25 -
.../org/apache/tomcat/lite/proxy/SocksServer.java | 448 --
.../tomcat/lite/proxy/StaticContentService.java | 129 -
.../org/apache/tomcat/lite/service/IOStatus.java | 56 -
.../org/apache/tomcat/lite/service/LogConfig.java | 65 -
.../tomcat/lite/util/FastHttpDateFormat.java | 231 -
.../org/apache/tomcat/lite/util/LocaleParser.java | 394 --
.../java/org/apache/tomcat/lite/util/MimeMap.java | 195 -
.../java/org/apache/tomcat/lite/util/Range.java | 160 -
.../org/apache/tomcat/lite/util/URLEncoder.java | 227 -
.../java/org/apache/tomcat/lite/util/UrlUtils.java | 84 -
modules/tomcat-lite/pom.xml | 118 -
.../test/org/apache/coyote/lite/ServletTests.java | 98 -
.../test/org/apache/coyote/lite/Tomcat.java | 911 ---
.../apache/coyote/lite/TomcatLiteCoyoteTest.java | 42 -
.../apache/coyote/lite/TomcatStandaloneMain.java | 59 -
.../test/org/apache/tomcat/lite/TestMain.java | 313 -
.../org/apache/tomcat/lite/http/ClientTest.java | 60 -
.../tomcat/lite/http/CompressFilterTest.java | 82 -
.../apache/tomcat/lite/http/DispatcherTest.java | 46 -
.../tomcat/lite/http/HttpChannelInMemoryTest.java | 384 --
.../apache/tomcat/lite/http/HttpChannelTest.java | 128 -
.../org/apache/tomcat/lite/http/HttpsTest.java | 74 -
.../org/apache/tomcat/lite/http/LiveHttp1Test.java | 152 -
.../org/apache/tomcat/lite/http/MultiMapTest.java | 54 -
.../test/org/apache/tomcat/lite/http/SpdyTest.java | 165 -
.../org/apache/tomcat/lite/http/genrsa_512.cert | 15 -
.../org/apache/tomcat/lite/http/genrsa_512.der | Bin 345 -> 0 bytes
.../tomcat/lite/http/services/EchoCallback.java | 61 -
.../tomcat/lite/http/services/SleepCallback.java | 85 -
.../test/org/apache/tomcat/lite/http/spdyreq0 | Bin 425 -> 0 bytes
.../test/org/apache/tomcat/lite/http/spdyreq0.bin | Bin 425 -> 0 bytes
.../org/apache/tomcat/lite/http/spdyreqCompressed | Bin 277 -> 0 bytes
.../apache/tomcat/lite/http/spdyreqCompressed.bin | Bin 277 -> 0 bytes
.../org/apache/tomcat/lite/io/BBufferTest.java | 159 -
.../org/apache/tomcat/lite/io/CBufferTest.java | 32 -
.../test/org/apache/tomcat/lite/io/OneTest.java | 27 -
.../test/org/apache/tomcat/lite/io/SocksTest.java | 58 -
.../org/apache/tomcat/lite/io/UEncoderTest.java | 44 -
.../test/org/apache/tomcat/lite/io/test.properties | 33 -
.../org/apache/tomcat/lite/load/LiveHttp5Test.java | 38 -
.../tomcat/lite/load/LiveHttpThreadedTest.java | 292 -
.../org/apache/tomcat/lite/load/MicroTest.java | 50 -
.../org/apache/tomcat/lite/load/ThreadRunner.java | 64 -
.../tomcat/lite/proxy/LiveProxyHttp1Test.java | 33 -
.../org/apache/tomcat/lite/proxy/ProxyTest.java | 120 -
.../apache/tomcat/lite/proxy/SmallProxyTest.java | 109 -
.../org/apache/tomcat/lite/util/UEncoderTest.java | 51 -
.../apache/tomcat/test/watchdog/AntProperties.java | 75 -
.../tomcat/test/watchdog/CookieController.java | 636 --
.../apache/tomcat/test/watchdog/DynamicObject.java | 385 --
.../apache/tomcat/test/watchdog/HttpCookie.java | 290 -
.../apache/tomcat/test/watchdog/RfcDateParser.java | 103 -
.../tomcat/test/watchdog/WatchdogClient.java | 213 -
.../tomcat/test/watchdog/WatchdogHttpClient.java | 411 --
.../tomcat/test/watchdog/WatchdogTestCase.java | 93 -
.../tomcat/test/watchdog/WatchdogTestImpl.java | 1172 ----
res/INSTALLLICENSE | 4 +
.../javax.servlet.ServletContainerInitializer | 15 +
res/META-INF/jasper.jar/web-fragment.xml | 2 +-
res/META-INF/jaspic-api.jar.manifest | 11 +
.../javax.servlet.ServletContainerInitializer | 15 +
.../services/javax.websocket.ContainerProvider | 15 +
...socket.server.ServerEndpointConfig$Configurator | 15 +
res/META-INF/tomcat-websocket.jar/web-fragment.xml | 2 +-
res/checkstyle/checkstyle.xml | 2 +-
res/checkstyle/javax-checkstyle.xml | 2 +-
res/checkstyle/javax-import-control.xml | 23 +-
res/checkstyle/org-checkstyle.xml | 2 +-
res/checkstyle/org-import-control.xml | 4 +-
res/cobertura/logback.xml | 31 +
res/deployer/build.xml | 2 +-
res/findbugs/filter-false-positives.xml | 91 +-
res/ide-support/eclipse/eclipse.project | 2 +-
.../eclipse/java-compiler-errors-warnings.txt | 18 +
.../eclipse/org.eclipse.jdt.core.prefs.properties | 6 +-
res/ide-support/eclipse/start-tomcat.launch | 6 +-
res/ide-support/eclipse/stop-tomcat.launch | 6 +-
res/maven/mvn-pub.xml | 28 +-
res/maven/mvn.properties.default | 2 +-
res/maven/tomcat-embed-core.pom | 16 -
res/maven/tomcat-embed-logging-juli.pom | 32 -
res/maven/tomcat-embed-logging-log4j.pom | 32 -
res/maven/tomcat-extras-juli-adapters.pom | 47 -
res/maven/tomcat-extras-juli.pom | 32 -
res/maven/tomcat-jaspic-api.pom | 32 +
res/maven/tomcat-servlet-api.pom | 8 +-
res/rat/rat-excludes.txt | 82 +-
res/tomcat.nsi | 4 +-
.../javax.servlet.ServletContainerInitializer | 15 +
test/conf/jaspic-test-01.xml | 22 +
test/conf/jaspic-test-02.xml | 26 +
test/javax/el/TesterClass.java | 2 +-
test/javax/servlet/http/TestCookie.java | 4 +-
.../servlet/http/TestCookieNetscapeValidator.java | 43 -
test/javax/servlet/http/TestHttpServlet.java | 2 +-
.../catalina/authenticator/ResponseDescriptor.java | 59 +
.../authenticator/TestDigestAuthenticator.java | 6 +-
.../authenticator/TestFormAuthenticator.java | 15 +-
.../TestNonLoginAndBasicAuthenticator.java | 70 +-
.../TestSSOnonLoginAndBasicAuthenticator.java | 32 +-
.../TestSSOnonLoginAndDigestAuthenticator.java | 27 +-
.../TestPersistentProviderRegistrations.java | 87 +
.../jaspic/TestSimpleServerAuthConfig.java | 74 +
.../authenticator/jaspic/TesterMessageInfo.java | 55 +
.../jaspic/TesterServerAuthModuleA.java | 64 +
.../apache/catalina/comet/TestCometProcessor.java | 672 --
.../catalina/connector/TestCoyoteInputStream.java | 72 +
.../catalina/connector/TestCoyoteOutputStream.java | 43 +-
.../catalina/connector/TestMaxConnections.java | 3 +
.../apache/catalina/connector/TestResponse.java | 5 +-
.../connector/TestResponsePerformance.java | 4 +
.../org/apache/catalina/connector/test_content.txt | 19 +
.../catalina/core/TestApplicationContext.java | 82 +
.../catalina/core/TestApplicationMapping.java | 312 +
.../catalina/core/TestApplicationPushBuilder.java | 54 +
.../apache/catalina/core/TestAsyncContextImpl.java | 9 +-
.../apache/catalina/core/TestStandardContext.java | 1 -
.../catalina/core/TestStandardContextAliases.java | 3 +
.../core/TestStandardContextResources.java | 1 +
.../apache/catalina/core/TestStandardWrapper.java | 5 +-
.../catalina/core/TestSwallowAbortedUploads.java | 4 +-
.../apache/catalina/filters/TestCorsFilter.java | 22 +-
.../filters/TestRestCsrfPreventionFilter2.java | 72 +-
.../catalina/filters/TesterHttpServletRequest.java | 47 +-
.../catalina/loader/TestWebappClassLoader.java | 3 +-
.../loader/TestWebappClassLoaderWeaving.java | 15 +-
test/org/apache/catalina/mapper/TestMapper.java | 69 +-
.../catalina/mapper/TestMapperPerformance.java | 26 +-
.../apache/catalina/mbeans/TestRegistration.java | 11 +-
.../catalina/nonblocking/TestNonBlockingAPI.java | 50 +-
.../catalina/servlets/TestDefaultServlet.java | 234 +-
test/org/apache/catalina/session/Benchmarks.java | 23 +-
.../apache/catalina/startup/SimpleHttpClient.java | 32 +-
.../apache/catalina/startup/TestContextConfig.java | 4 +-
.../startup/TestContextConfigAnnotation.java | 42 +-
.../catalina/startup/TestTomcatClassLoader.java | 4 +-
.../apache/catalina/startup/TomcatBaseTest.java | 35 +-
.../catalina/startup/web-1lifecyclecallback.xml | 2 +-
test/org/apache/catalina/startup/web-1ordering.xml | 2 +-
.../catalina/startup/web-2lifecyclecallback.xml | 2 +-
test/org/apache/catalina/startup/web-2ordering.xml | 2 +-
.../apache/catalina/startup/web-fragment-1name.xml | 2 +-
.../catalina/startup/web-fragment-1ordering.xml | 2 +-
.../apache/catalina/startup/web-fragment-2name.xml | 2 +-
.../catalina/startup/web-fragment-2ordering.xml | 2 +-
.../apache/catalina/tribes/demos/EchoRpcTest.java | 4 -
.../catalina/tribes/demos/IntrospectionUtils.java | 6 +-
.../org/apache/catalina/tribes/demos/LoadTest.java | 4 +-
test/org/apache/catalina/tribes/demos/MapDemo.java | 2 +-
.../apache/catalina/tribes/test/NioSenderTest.java | 7 +-
.../tribes/test/channel/TestDataIntegrity.java | 2 +-
.../tribes/test/channel/TestMulticastPackages.java | 2 +-
.../test/channel/TestRemoteProcessException.java | 2 +-
.../tribes/test/channel/TestUdpPackages.java | 2 +-
.../tribes/test/transport/SocketNioReceive.java | 2 +-
.../tribes/test/transport/SocketNioSend.java | 8 +-
.../test/transport/SocketNioValidateSend.java | 8 +-
test/org/apache/catalina/util/TestConversions.java | 37 -
test/org/apache/catalina/valves/Benchmarks.java | 2 +-
.../webresources/war/TestHandlerIntegration.java | 1 +
test/org/apache/coyote/TestIoTimeouts.java | 241 +
test/org/apache/coyote/ajp/SimpleAjpClient.java | 5 +-
.../coyote/ajp/TestAbstractAjpProcessor.java | 28 +-
.../coyote/http11/TestAbstractHttp11Processor.java | 948 ---
.../apache/coyote/http11/TestGzipOutputFilter.java | 91 -
.../coyote/http11/TestHttp11InputBuffer.java | 604 ++
.../coyote/http11/TestHttp11OutputBuffer.java | 89 +
.../apache/coyote/http11/TestHttp11Processor.java | 975 +++
.../coyote/http11/TestInternalInputBuffer.java | 603 --
.../http11/filters/TestChunkedInputFilter.java | 4 +-
.../http11/filters/TestGzipOutputFilter.java | 87 +
.../coyote/http11/filters/TesterOutputBuffer.java | 126 +
.../apache/coyote/http11/upgrade/TestUpgrade.java | 77 +-
.../http11/upgrade/TestUpgradeInternalHandler.java | 267 +
test/org/apache/coyote/http2/Http2TestBase.java | 1178 ++++
.../apache/coyote/http2/TestAbstractStream.java | 249 +
test/org/apache/coyote/http2/TestByteUtil.java | 39 +
test/org/apache/coyote/http2/TestHpack.java | 90 +
test/org/apache/coyote/http2/TestHttp2Limits.java | 474 ++
.../apache/coyote/http2/TestHttp2Section_3_2.java | 180 +
.../apache/coyote/http2/TestHttp2Section_3_5.java | 39 +
.../apache/coyote/http2/TestHttp2Section_4_1.java | 72 +
.../apache/coyote/http2/TestHttp2Section_4_2.java | 143 +
.../apache/coyote/http2/TestHttp2Section_4_3.java | 100 +
.../apache/coyote/http2/TestHttp2Section_5_1.java | 309 +
.../apache/coyote/http2/TestHttp2Section_5_2.java | 116 +
.../apache/coyote/http2/TestHttp2Section_5_3.java | 249 +
.../apache/coyote/http2/TestHttp2Section_5_5.java | 101 +
.../apache/coyote/http2/TestHttp2Section_6_1.java | 168 +
.../apache/coyote/http2/TestHttp2Section_6_2.java | 118 +
.../apache/coyote/http2/TestHttp2Section_6_3.java | 94 +
.../apache/coyote/http2/TestHttp2Section_6_4.java | 87 +
.../apache/coyote/http2/TestHttp2Section_6_5.java | 151 +
.../apache/coyote/http2/TestHttp2Section_6_7.java | 92 +
.../apache/coyote/http2/TestHttp2Section_6_8.java | 104 +
.../apache/coyote/http2/TestHttp2Section_6_9.java | 290 +
.../apache/coyote/http2/TestHttp2Section_8_1.java | 215 +
test/org/apache/el/TestELInJsp.java | 4 +-
.../apache/el/parser/TestAstLambdaExpression.java | 10 +
test/org/apache/el/parser/TestELParser.java | 61 +
test/org/apache/jasper/compiler/TestCompiler.java | 5 +-
test/org/apache/jasper/compiler/TestJspConfig.java | 16 +-
.../jasper/servlet/TestJspCServletContext.java | 1 +
.../apache/naming/resources/TestWarDirContext.java | 1 +
test/org/apache/tomcat/unittest/TesterContext.java | 17 -
.../tomcat/unittest/TesterLeakingServlet1.java | 7 -
.../apache/tomcat/util/buf/TestB2CConverter.java | 12 +-
test/org/apache/tomcat/util/buf/TestUEncoder.java | 29 -
test/org/apache/tomcat/util/buf/TestUtf8.java | 112 +-
.../collections/TestCaseInsensitiveKeyMap.java | 215 +
.../apache/tomcat/util/http/CookiesBaseTest.java | 3 +-
.../util/http/TestCookiesDefaultSysProps.java | 2 +-
.../http/TestCookiesNoStrictNamingSysProps.java | 2 +-
.../util/http/TestMimeHeadersIntegration.java | 7 +-
.../util/http/TesterHttpMessagesPerformance.java | 44 -
test/org/apache/tomcat/util/net/TestCustomSsl.java | 3 +
.../apache/tomcat/util/net/TestSSLHostConfig.java | 68 +
.../util/net/TestSSLHostConfigIntegration.java | 62 +
test/org/apache/tomcat/util/net/TestSsl.java | 4 +-
.../apache/tomcat/util/net/TestXxxEndpoint.java | 4 -
test/org/apache/tomcat/util/net/TesterSupport.java | 33 +-
.../util/net/jsse/TesterBug50640SslImpl.java | 24 +-
.../tomcat/util/net/jsse/openssl/TestCipher.java | 1067 ----
.../TestOpenSSLCipherConfigurationParser.java | 594 --
.../TestOpenSSLCipherConfigurationParserOnly.java | 113 -
.../util/net/jsse/openssl/TesterOpenSSL.java | 439 --
.../util/net/openssl/ciphers/TestCipher.java | 1067 ++++
.../TestOpenSSLCipherConfigurationParser.java | 594 ++
.../TestOpenSSLCipherConfigurationParserOnly.java | 113 +
.../util/net/openssl/ciphers/TesterOpenSSL.java | 439 ++
.../tomcat/util/scan/TestStandardJarScanner.java | 13 +-
.../apache/tomcat/util/threads/TestLimitLatch.java | 2 +
.../tomcat/websocket/TestConnectionLimit.java | 109 +
.../websocket/TestWebSocketFrameClientSSL.java | 13 +-
.../tomcat/websocket/TestWsPingPongMessages.java | 2 -
.../tomcat/websocket/TestWsRemoteEndpoint.java | 103 +-
.../tomcat/websocket/TestWsSubprotocols.java | 23 +-
.../tomcat/websocket/TestWsWebSocketContainer.java | 16 +-
.../tomcat/websocket/TesterConnectionLimit.java | 108 -
.../apache/tomcat/websocket/TesterEchoServer.java | 23 +
.../tomcat/websocket/TesterFirehoseServer.java | 24 +-
.../websocket/pojo/TestEncodingDecoding.java | 165 +-
.../apache/tomcat/websocket/pojo/TesterUtil.java | 21 +-
.../tomcat/websocket/server/TestClassLoader.java | 151 +
.../apache/tomcat/websocket/server/TestClose.java | 45 +-
.../tomcat/websocket/server/TestShutdown.java | 111 +
.../server/TestWsRemoteEndpointImplServer.java | 26 +-
.../websocket/server/TestWsServerContainer.java | 21 +-
.../websocket/server/TesterEndpointConfig.java | 54 +
.../websocket/server/TesterWsCloseClient.java | 7 +-
test/webapp-2.2/WEB-INF/web.xml | 2 +-
test/webapp-2.3/WEB-INF/web.xml | 2 +-
test/webapp-2.4/WEB-INF/web.xml | 2 +-
test/webapp-2.5/WEB-INF/web.xml | 2 +-
test/webapp-3.0/WEB-INF/web.xml | 2 +-
test/webapp-3.1/WEB-INF/web.xml | 2 +-
.../WEB-INF/web.xml | 2 +-
test/webapp-fragments/WEB-INF/web.xml | 2 +-
test/webapp-servletsecurity/WEB-INF/web.xml | 2 +-
test/webapp-servletsecurity2/WEB-INF/web.xml | 2 +-
.../src/main/webapp/WEB-INF/web.xml | 2 +-
test/webapp/WEB-INF/bug53545.tld | 2 +-
.../META-INF/org.apache.jasper/tagPlugins.xml | 2 +-
.../classes/org/apache/tomcat/Bug58096.class | Bin 285 -> 208 bytes
.../classes/org/apache/tomcat/Bug58096.java | 2 +-
test/webapp/WEB-INF/tags/bug48668.tagx | 2 +-
test/webapp/WEB-INF/tags/bug55198.tagx | 2 +-
test/webapp/WEB-INF/tags/bug56265.tagx | 2 +-
test/webapp/WEB-INF/web.xml | 2 +-
test/webapp/bug5nnnn/bug54801a.jspx | 2 +-
test/webapp/bug5nnnn/bug54801b.jspx | 2 +-
test/webapp/bug5nnnn/bug54821a.jspx | 2 +-
test/webapp/bug5nnnn/bug54821b.jspx | 2 +-
test/webapp/bug5nnnn/bug56029.jspx | 2 +-
test/webapp/bug5nnnn/bug56334and56561.jspx | 2 +-
test/webapp/index.html | 16 +
test/webapp/index.html.br | Bin 0 -> 367 bytes
test/webapp/index.html.gz | Bin 135 -> 562 bytes
webapps/ROOT/WEB-INF/web.xml | 2 +-
webapps/docs/aio.xml | 279 +-
webapps/docs/appdev/web.xml.txt | 2 +-
webapps/docs/apr.xml | 21 +-
webapps/docs/building.xml | 2 +-
webapps/docs/changelog.xml | 6656 +++-----------------
webapps/docs/class-loader-howto.xml | 18 +-
webapps/docs/cluster-howto.xml | 10 +-
webapps/docs/config/ajp.xml | 44 +-
webapps/docs/config/cluster-interceptor.xml | 34 +-
webapps/docs/config/cluster-manager.xml | 20 -
webapps/docs/config/context.xml | 15 +-
webapps/docs/config/cookie-processor.xml | 101 +-
webapps/docs/config/filter.xml | 2 +-
webapps/docs/config/host.xml | 9 +-
webapps/docs/config/http.xml | 958 +--
webapps/docs/config/http2.xml | 187 +
webapps/docs/config/jaspic.xml | 186 +
webapps/docs/config/listeners.xml | 26 +-
webapps/docs/config/loader.xml | 12 +-
webapps/docs/config/manager.xml | 39 -
webapps/docs/config/project.xml | 4 +-
webapps/docs/config/realm.xml | 53 +-
webapps/docs/config/systemprops.xml | 151 +-
webapps/docs/config/valve.xml | 22 +-
webapps/docs/default-servlet.xml | 24 +-
webapps/docs/funcspecs/fs-admin-objects.xml | 3 -
webapps/docs/funcspecs/mbean-names.xml | 9 -
webapps/docs/jasper-howto.xml | 4 +-
webapps/docs/jndi-resources-howto.xml | 4 +-
webapps/docs/logging.xml | 219 +-
webapps/docs/manager-howto.xml | 2 +-
webapps/docs/realm-howto.xml | 18 +-
webapps/docs/security-howto.xml | 45 +-
webapps/docs/ssl-howto.xml | 62 +-
webapps/docs/tomcat-docs.xsl | 6 +-
webapps/docs/web-socket-howto.xml | 36 -
webapps/examples/WEB-INF/classes/async/Async0.java | 6 +-
webapps/examples/WEB-INF/classes/async/Async2.java | 10 +-
.../WEB-INF/classes/async/AsyncStockServlet.java | 7 +-
.../examples/WEB-INF/classes/chat/ChatServlet.java | 292 -
.../compressionFilters/CompressionFilter.java | 105 +-
.../CompressionResponseStream.java | 17 +-
.../CompressionServletResponseWrapper.java | 24 +-
.../WEB-INF/classes/filters/ExampleFilter.java | 7 +-
.../WEB-INF/classes/http2/SimpleImagePush.java | 52 +
.../examples/WEB-INF/classes/util/HTMLFilter.java | 2 +
.../classes/websocket/drawboard/Client.java | 3 +-
.../classes/websocket/drawboard/DrawMessage.java | 36 +-
.../WEB-INF/classes/websocket/drawboard/Room.java | 16 +-
.../examples/WEB-INF/jsp2/jsp2-example-taglib.tld | 2 +-
webapps/examples/WEB-INF/web.xml | 20 +-
webapps/examples/jsp/async/async1.jsp | 8 +-
webapps/examples/jsp/async/async3.jsp | 9 +-
webapps/examples/jsp/xml/xml.jsp | 2 +-
webapps/examples/servlets/chat/index.jsp | 32 -
webapps/examples/servlets/chat/login.jsp | 33 -
webapps/examples/servlets/chat/post.jsp | 55 -
webapps/examples/servlets/index.html | 25 +-
webapps/examples/websocket/drawboard.xhtml | 44 +-
webapps/host-manager/META-INF/context.xml | 6 -
webapps/host-manager/WEB-INF/jsp/403.jsp | 5 +
webapps/host-manager/WEB-INF/web.xml | 2 +-
webapps/manager/META-INF/context.xml | 6 -
webapps/manager/WEB-INF/jsp/403.jsp | 5 +
webapps/manager/WEB-INF/jsp/connectorCiphers.jsp | 2 +-
webapps/manager/WEB-INF/jsp/sessionDetail.jsp | 2 +-
webapps/manager/WEB-INF/jsp/sessionsList.jsp | 2 +-
webapps/manager/WEB-INF/web.xml | 2 +-
webapps/manager/xform.xsl | 2 +-
1381 files changed, 61629 insertions(+), 88126 deletions(-)
diff --git a/BUILDING.txt b/BUILDING.txt
index dfda8ff..5cab820 100644
--- a/BUILDING.txt
+++ b/BUILDING.txt
@@ -20,8 +20,8 @@
====================================================
This subproject contains the source code for Tomcat @VERSION_MAJOR_MINOR@, a container that
-implements the Servlet 3.1, JSP 2.3, EL 3.0 and WebSocket 1.1 specifications
-from the Java Community Process <http://www.jcp.org/>.
+implements the Servlet 3.1, JSP 2.3, EL 3.0, WebSocket 1.1 and JASPIC 1.1
+specifications from the Java Community Process <http://www.jcp.org/>.
Note: If you just need to run Apache Tomcat, it is not necessary to build
it. You may simply download a binary distribution. It is cross-platform.
@@ -35,7 +35,7 @@ source distribution, do the following:
1. If the JDK is already installed, skip to (2).
- 2. Download a version 7 of Java Development Kit (JDK) release (use the
+ 2. Download a version 8 of Java Development Kit (JDK) release (use the
latest update available for your chosen version) from one of:
http://www.oracle.com/technetwork/java/javase/downloads/index.html
@@ -66,8 +66,8 @@ source distribution, do the following:
(2) Install Apache Ant version 1.9.5 or later on your computer.
- 1. If Apache Ant version 1.9.5 or later is already installed on your computer,
- skip to (3).
+ 1. If Apache Ant version 1.9.5 or later is already installed on your
+ computer, skip to (3).
2. Download a binary distribution of Ant from:
@@ -318,15 +318,14 @@ directory:
output/build/logs
-By default the testsuite is run four times to test 4 different
-implementations of Tomcat connectors: BIO, NIO, NIO2 and APR. (If you are not
+By default the testsuite is run three times to test 3 different
+implementations of Tomcat connectors: NIO, NIO2 and APR. (If you are not
familiar with Tomcat connectors, see config/http.html in documentation for
details).
-The 4 runs are enabled and disabled individually by the following
+The 3 runs are enabled and disabled individually by the following
properties, which all are "true" by default:
- execute.test.bio=true
execute.test.nio=true
execute.test.nio2=true
execute.test.apr=true
@@ -423,57 +422,62 @@ For example:
junit.formatter.usefile=false
- 5. Optional support is provided for the Cobertura code coverage tool. It
- can be enabled using the following property:
+ 5. It is possible to speed up testing by letting JUnit to run several
+ tests in parallel.
+
+ 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.
+
+NOTE: Cobertura is licensed under GPL v2 with parts of it being under
+ Apache License v1.1. See http://cobertura.sf.net for details. Using it
+ during Tomcat build is optional and is off by default.
+
+ Cobertura can be enabled using the following properties:
test.cobertura=true
+ test.threads=1
+
+ Using Cobertura currently requires setting test.threads configuration
+ property to the value of 1. Setting that property to a different value
+ will disable code coverage.
The report files by default are written to
output/coverage
-* NOTE: Cobertura is licensed under GPL v2 with parts of it being under
- Apache License v1.1. See http://cobertura.sf.net for details. Using it
- during Tomcat build is optional and is off by default.
-
- 6. The performance tests are written to run reasonably powerful machines (such
+ 7. The performance tests are written to run reasonably powerful machines (such
as a developer may use day to day) assuming no other resource hungry
- processes are running. These assumptions are not always true (e.g. on CI
- systems running ina virtual machine) so the performance tests may be
- disabled by using the following property:
+ processes are running.
+
+ These assumptions are not always true (e.g. on CI systems running in a
+ virtual machine) so the performance tests may be disabled by using the
+ following property:
test.excludePerformance=true
- 7. Some tests include checks that the access log valve entries are as expected.
+ 8. Some tests include checks that the access log valve entries are as expected.
These checks include timings. On slower / loaded systems these checks will
often fail. The checks may be relaxed by using the following property:
test.relaxTiming=true
- 8. It is known that some platforms (e.g. OSX El Capitan) require IPv4 to
+ 9. It is known that some platforms (e.g. OSX El Capitan) require IPv4 to
be the default for the multicast tests to work. This is configured by
the following property:
java.net.preferIPv4Stack=true
- 9. Optional support is provided for FindBugs. It can be enabled using the
- following property:
-
- execute.findbugs=true
-
- The report file by default is written to
-
- output/findbugs
-
-* NOTE: Findbugs is licensed under LGPL. Using Findbugs during Tomcat build is
- optional and is off by default.
(8) Source code checks
(8.1) Checkstyle
-* NOTE: Checkstyle is licensed under LGPL. Using Checkstyle during Tomcat
- build is optional and is off by default.
+NOTE: Checkstyle is licensed under LGPL. Using Checkstyle during Tomcat
+ build is optional and is off by default.
+
+ See http://checkstyle.sourceforge.net/ for more information.
Tomcat comes with a Checkstyle configuration that tests its source code
for certain conventions, like presence of the license header.
@@ -490,14 +494,36 @@ is located in the following directory:
output/res/checkstyle
-It is possible to run the check separately by invoking the "validate"
+It is possible to run the check separately by calling the "validate"
target. The command is:
cd ${tomcat.source}
ant -Dexecute.validate=true validate
-(8.2) End-of-line conventions check
+(8.2) FindBugs
+
+NOTE: FindBugs is licensed under LGPL. Using Findbugs during Tomcat build is
+ optional and is off by default.
+
+ See http://findbugs.sourceforge.net/ for more information.
+
+To enable FindBugs, add the following property to build.properties file:
+
+ execute.findbugs=true
+
+To compile Tomcat classes and generate a FindBugs report, call the
+"findbugs" target. For example:
+
+ cd ${tomcat.source}
+ ant -Dexecute.findbugs=true findbugs
+
+The report file by default is written to
+
+ output/findbugs
+
+
+(8.3) End-of-line conventions check
You usually would not need to run this check. You can skip this section.
diff --git a/KEYS b/KEYS
index 53754bd..c0a9353 100644
--- a/KEYS
+++ b/KEYS
@@ -614,63 +614,3 @@ QCuUl/v3Zly66jThcNS/U6S9VXBtQRxDZtkL8pSzmJxaFvvnZgkQ/hlvJ8f9ccS7
2/KQU94=
=yvcP
-----END PGP PUBLIC KEY BLOCK-----
-
-pub 4096R/D63011C7 2013-09-19
- Key fingerprint = 713D A88B E509 1153 5FE7 16F5 208B 0AB1 D630 11C7
-uid Violeta Georgieva Georgieva (CODE SIGNING KEY) <violetagg at apache.org>
-sig 3 D63011C7 2013-09-19 Violeta Georgieva Georgieva (CODE SIGNING KEY) <violetagg at apache.org>
-sub 4096R/30480593 2013-09-19
-sig D63011C7 2013-09-19 Violeta Georgieva Georgieva (CODE SIGNING KEY) <violetagg at apache.org>
-
------BEGIN PGP PUBLIC KEY BLOCK-----
-Version: GnuPG v2.0.21 (MingW32)
-
-mQINBFI6WiwBEAD+kkswnsY8eaqvYkS+ZB5MJr7juWrv9Lw9OGsIXFlTvD1XK01c
-E8k4+uA2sOtaXQ5wTMdc5N3YzAXqFxplWuafQgEvhyTTq37M5YCxvtYEZy/EHQYT
-iok5H97lMRKbhLdZB+wkdsa0P/L1FveCUiEawKY/Rrfi+UeRAneSV+m7S+RrPphZ
-M9aNSczqYKfAqlpUAlUcrF/bt59vjhepoHcE4ev6SB+PCs0vbvX4iTvvZCTk1lZ9
-InS2wdK80Jz9pRB0Uf3LEnZxt9e3RkIFdQOCcEISmNlBKQQKFG+zCpIAbVoMLKEw
-rXWl8mLzGzBbhGmLpFroem3Ln1YiAxUqnPR/MoBquYnpTINwePgwKVWyQ1TXG2MF
-Z7DPayBMN+G51rfLS/8iy35pAnNeqbWQjavdUis6/0aRMv5EYMFMAerutQ5v99bA
-rGj6OL3R6repJLOGT4YWcD/Tw+eU1lMWxbq8BbbRU9Fd0iVFhFyKB/DQSxofvTCe
-PdWXRrptrE0/SmvuoTRVPmB21WyJenKdNmVOQ6U+W1Rs+5IKAdWWrGPcUt0qTrRC
-SL8vAQ7MejYLovFtRHslJRs7T3ratpRcQUNOx1jytJhmSUJktNWZWNHqBTe/eOAU
-Yr+QAkQVQXvRVWzHkDHQRTOFmNYIDZYRkzSP19sBWRnYdCs6CbIVPgMJVwARAQAB
-tEVWaW9sZXRhIEdlb3JnaWV2YSBHZW9yZ2lldmEgKENPREUgU0lHTklORyBLRVkp
-IDx2aW9sZXRhZ2dAYXBhY2hlLm9yZz6JAjcEEwEKACEFAlI6WiwCGwMFCwkIBwMF
-FQoJCAsFFgIDAQACHgECF4AACgkQIIsKsdYwEccMXA/+KMQKWfw2T2CXLhqvQLoh
-Irj1Vi9leAttKqKp2NCHLK1jf1qKzUx5U81VvizIGUsDXGlAvnnavrj+hmQqZdsO
-CoJAo7ViIR1ZhNca1tFK4Sy03wdpNyUkvxVuC+3peXmwhjPJoqU2ONCuDl/bCczl
-QAQpgZCMO93h45U9H6JkjqK01aDorQHxvXo+Ap2IViQvDkNtJ515vG2k5K+x2XHw
-Tv19wr5N2rz407TWKzS5hh7QHRgg+PZs/zPf1YHD7Tg5K6vvmZd+5EsDrse6tZXy
-mzz2+8Yg1SNa765Aq6p1uAQf5NKeej/25TbRYT7RyIlgDXPcPrKxy0cKzpqFqCFs
-jJEcN3NlQq+f1tOvUk8cQQS0G+Qws3EU7I74z8KaUfqmO/5ROrXLS50cKC9CODO0
-UFY8FbJDGzS5cFSBlqXYLeQvaOMg0LsV6wZLu6brxEsRYjSpwM8yBFO4bMcTxt4P
-VYtinNZ+6ude8mMz6BK/0/XbAL6rc5jwO2xj7GTCFNRTWOa8IGtwqg3qnAiHcg/V
-bTBQCOmzMujHBXLnZu6vg79BwzE7Ikq634D6HEwi1bC3XuVz+7NqdUQAGPSapwUo
-+0wC5DVwdjhe1zWcf2Zc45HWsx0HaGW28x/tBrw78fgwrSSyV2xunbxGpVaaysTy
-Oini8V70uLofn1SHtxvEQCm5Ag0EUjpaLAEQAKV7FnaAcxkzDa7zjrAgLRho44KM
-+lBt28+5KO3Jye1Lpf2+4aspu0PXkGW2Twv2tBQNZYs2CWF+vnHNUDuU8TkSpPt+
-2PRSZrQ0K+IpQF/qY3Wf+LYWFNXk5/wHJLGiQv/008svtupng6Ov39JwCNQ1iG3d
-nSWfnqHwQULyE7JcZf1It94G43+6NBvKakstOdK7d40dVhmRIKDdJkWhN3MKrGab
-FGFAF2Nb57IugQ9QO6Ve/BnjKZmJg7TyUZk27LVTC0aUQgGgDOvsF2Iw95IplCZ/
-jVbwdBGjeCStvI3c0DB+E2xwJ0g2Wf/CBLvmU9GKOW0toBfRUXFbfzNTJfW8kglt
-pDuELsVY5vHHxgujdDInHuTW1930zUw0cNA2+ai3sf+UGejh0e3nGfy1uOK3YQ6H
-2YWgqXlOkri9pMlE0NJo/3PW9QDu0YRplGl65k+GtHD2La1akq5V5Et0VNaOypBP
-OqnUM08LofAS126Kerm7uBSUQDDV6t1VTOBgPW5cJF9I8kdp04pzj4qb/3fuOuGc
-kBRfmO/Vkug8U81w/TnxX6EYGy5fyA4JFBJl++waPS/9dXhVnA0qXEivzw9gNQvC
-uXYcM3nm4yUrOouC/OlC1cS+6Wxjrx6qn3NnsVzMCtefNK93+TdheZ0cJrMhJKkW
-v+qttOzPIleqvDK9ABEBAAGJAh8EGAEKAAkFAlI6WiwCGwwACgkQIIsKsdYwEcdN
-tQ/+L8cw9Z9tfrqovO1fGFQwCSaomShsbjoUb5AR3Hj3OuPGwXd8J62mrw+RnGN+
-0w2RyTz52izYvcoB1jmMFQwqi7vM5KCbw6KA8oRX58WSqiWCIwpbUuTODvJrSXjX
-pz/J/d+PVZi8T1HAu5HxDqNC1XR+eUd1xA9Pgnmmw99+0rmzES7xexWADXo/RRPH
-mDCxGK3UKMHDYJLTx8D3MacMitzQulxVo9xWwH1C7ioL3o5zCv2mfIl32WNjqwpD
-h4gNpnAGRthizeYTgyJM9nCrSWgeE+izGZ9F0g5uXzhyk1f6jlUmXiwjMu/XOcJO
-5Rr1e42bWITuP49nB2QbdSqVvVscwCd5TEpOnQtVNZGsss/wQHXDmGSVrYYUQwO/
-cUrU+hTti1IJXgyFi7F1oxde+LCUxXmizKGoY96dVN+TYH5c17ub1/4/DYpOmcly
-tsQ2TOV0BqK4rgKLGfg2mA4zIFOdqXeGefLQVAF5fFzjFKKDi0ewp3sqy+ed6mKY
-1M/HmRX/YzIouFZ3ChFPIpeY23XxJC0BXkWR4pS7qxnelrWBZ+UbleNr9uHat5rC
-B77712dCT9zz85b380DnuMkrgz4HCnHuTcbHXIF1J604lars6ZrjtBvX+OsRHt7V
-f72qKJufP+n01xliW68LP4v93auM8nuE4kkEJ8ncHyuDq/Q=
-=fM0q
------END PGP PUBLIC KEY BLOCK-----
diff --git a/LICENSE b/LICENSE
index 112c0b4..e6a6baf 100644
--- a/LICENSE
+++ b/LICENSE
@@ -725,6 +725,10 @@ For the following XML Schemas for Java EE Deployment Descriptors:
- web-app_3_1.xsd
- web-common_3_1.xsd
- web-fragment_3_1.xsd
+ - javaee_8.xsd
+ - web-app_4_0.xsd
+ - web-common_4_0.xsd
+ - web-fragment_4_0.xsd
COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
diff --git a/NOTICE b/NOTICE
index adce2f4..1cc7541 100644
--- a/NOTICE
+++ b/NOTICE
@@ -4,6 +4,12 @@ Copyright 1999-2016 The Apache Software Foundation
This product includes software developed at
The Apache Software Foundation (http://www.apache.org/).
+This software contains code derived from netty-native
+developed by the Netty project
+(http://netty.io, https://github.com/netty/netty-tcnative/)
+and from finagle-native developed at Twitter
+(https://github.com/twitter/finagle).
+
The Windows Installer is built with the Nullsoft
Scriptable Install System (NSIS), which is
open source software. The original software and
@@ -15,12 +21,12 @@ JDT Core Batch Compiler component, which is open source software.
The original software and related information is available at
http://www.eclipse.org/jdt/core/.
-For the bayeux implementation
-The org.apache.cometd.bayeux API is derivative work originating at the Dojo Foundation
-* Copyright 2007-2008 Guy Molinari
-* Copyright 2007-2008 Filip Hanik
-* Copyright 2007 Dojo Foundation
-* Copyright 2007 Mort Bay Consulting Pty. Ltd.
+For portions of the Tomcat JNI OpenSSL API and the OpenSSL JSSE integration
+The org.apache.tomcat.jni and the org.apache.tomcat.net.openssl packages
+are derivative work originating from the Netty project and the finagle-native
+project developed at Twitter
+* Copyright 2014 The Netty Project
+* Copyright 2014 Twitter
The original XML Schemas for Java EE Deployment Descriptors:
- javaee_5.xsd
diff --git a/RELEASE-NOTES b/RELEASE-NOTES
index e53ee9d..008851c 100644
--- a/RELEASE-NOTES
+++ b/RELEASE-NOTES
@@ -57,6 +57,11 @@ removed or changed although it may be deprecated.
Note: As Tomcat @VERSION_MAJOR@ matures, the above list will be added to. The list is not
considered complete at this time.
+Note: A large number of deprecated methods, fields and configuration options
+ were removed in the transition from 8.0.x to 8.5.x. If any of those
+ removals triggers significant problems for the user community that the
+ deletion may be reverted in a later point release.
+
The remaining classes are considered part of the Tomcat internals and may change
without notice between point releases.
@@ -91,9 +96,9 @@ You can make additional APIs available to all of your web applications by
putting unpacked classes into a "classes" directory (not created by default),
or by placing them in JAR files in the "lib" directory.
-To override the XML parser implementation or interfaces, use the endorsed
-mechanism of the JVM. The default configuration defines JARs located in
-"endorsed" as endorsed.
+To override the XML parser implementation or interfaces, use the appropriate
+feature for your JVM. For Java <= 8 use the endorsed standards override
+feature. For Java 9+ use the upgradeable modules feature.
================================================================
diff --git a/RUNNING.txt b/RUNNING.txt
index 435c259..ec6f264 100644
--- a/RUNNING.txt
+++ b/RUNNING.txt
@@ -272,12 +272,6 @@ In CATALINA_HOME:
* lib - Libraries and classes, as explained below
- * endorsed - Libraries that override standard "Endorsed Standards"
- libraries provided by JRE. See Classloading documentation
- in the User Guide for details.
-
- By default this "endorsed" directory is absent.
-
In the default configuration the JAR libraries and classes both in
CATALINA_BASE/lib and in CATALINA_HOME/lib will be added to the common
classpath, but the ones in CATALINA_BASE will be added first and thus will
diff --git a/TOMCAT-NEXT.txt b/TOMCAT-NEXT.txt
deleted file mode 100644
index cafcbb3..0000000
--- a/TOMCAT-NEXT.txt
+++ /dev/null
@@ -1,224 +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.
-================================================================================
-
-Notes of things to consider for the next major Tomcat release (probably 8.0.x
-but possibly 7.1.x).
-
- 1. Refactor the TLD parsing. TLDs are currently parsed twice. Once by Catalina
- looking for listeners and once by Jasper.
- - Complete
-
- 2. Refactor the XML parsing (org.apache.tomcat.util.xml ?) to remove duplicate
- XML parsing code in Catalina and Jasper such as the entity resolvers used
- for validation.
- - Complete
-
- 3. TLDs may have a many to many relationship between URIs and TLD files. This
- can result in the same TLD file being parsed many times. Refactor the
- TldLocationCache to cache the parsed nodes (will need to check for changes
- to TLD files).
- - Complete
-
- 4. TLD files should be included in the dependencies for JSP and Tag files.
- - Complete
-
- 5. Run the unused code detector and remove everything that isn't currently used.
- Add deprecation markers for the removed code to Tomcat 7.0.x
- - Complete
-
- 6. Change the default URIEncoding on the connector to UTF-8.
- - Complete
-
- 7. Rip out all the JNDI code in resource handling and replace it with straight
- URLs (File or WAR).
- - Complete
-
- kkolinko: I think this proposal goes too far. There are several
- separate issues. There are:
-
- a) Internal API to define resources
- - BaseDirContext implementing aliases and resource jars,
- and there will be overlays in Servlet 3.1
- - StandardContext.setResources() allowing an arbitrary DirContext
- implementation via <Resources> element.
-
- Concerns:
- - Too many ways to configure it.
-
- b) Internal API to lookup resources
- - DirContext interface
-
- Concerns:
- - Unnecessary objects, e.g. NamingException instead of null.
-
- - Too many methods. Name vs. String. list() vs. listBindings().
-
- - Limited API. As a workaround, there are non-standard methods that
- are implemented on BaseDirContext instead, e.g. getRealPath(),
- doListBindings(..).
-
- - All caching (ProxyDirContext) and aliases handling is
- performed on the root level only.
-
- Once I do a lookup that returns a DirContext, further lookups on it
- will bypass the caching and aliases.
-
- c) WebappClassLoader and its interaction with resources
-
- WebappClassLoader uses DirContext API to access resources (classes,
- jars).
-
- Note that it has to construct a classpath for Java compiler called by
- Jasper. The compiler cannot operate on a DirContext and needs access
- to actual files and JARs.
-
- Concerns:
- - There are problems with access to classes and JAR files in
- non-unpacked WARs.
-
- It is resolved by unzipping the files into the working directory (in
- WebappLoader#setRepositories()).
-
- Note that DirContext is not notified of this copying.
- StandardJarScanner does not know of those copies either.
-
- - There are problems when the classes directory is served from
- multiple locations
-
- It seems to be worked around by adding the path of the alternative
- classes directory to virtualClasspath of VirtualWebappLoader (as shown
- by example in config/context.html#Virtual_webapp), but it is likely
- that I miss something.
-
- - antiJARLocking support in WebappClassLoader creates copies of
- resources, but does not notify the DirContext.
-
- - WebappClassLoader.jarFiles is used to track JAR files and keep them
- open. These might be useful when looking for resources in those files.
- These might be useful for StandardJarScanner.
-
- d) StandardJarScanner
-
- Concerns:
- - It essentially scans the same JARs as accessed by WebappClassLoader.
-
- It might be better to access them via WebappClassLoader rather that
- through Servlet API methods.
-
- The scanner is used by Jasper as well, so there are some concerns to
- keep the components independent.
-
- e) URL returned by ServletContext.getResource()
- jndi:/hostName/contextPath/resourcePath
-
- Goodies:
- - Uniform URL space. Both files and directories can be represented,
- hiding details of aliases, resource jars, etc.
-
- - It hides implementation details.
-
- - Permissions can be expressed as JndiPermission. They do not depend
- on context version number.
-
- - Accessing a resource through such URL leverages the cache
- implemented in ProxyDirContext class. We do not access the file system
- directly, nor need to open a JAR file.
-
- - It can be created from a String if necessary.
-
- Such use relies on DirContextURLStreamHandler.bindThread(..) being
- called earlier in the same thread.
-
- Concerns:
- - Some components would like to get "real" resource URL from this one.
-
- Maybe it can be exposed through some special header name,
- DirContextURLConnection.getHeaderField(str)?
-
- How such real URL can be prepared?
- DirContext.getNameInNamespace()?
- BaseDirContext.getRealPath()?
- ((FileResourceAttributes)DirContext.getAttributes()).getCanonicalPath()?
-
- - A resource lookup is performed twice. The first time in
- ServletContext.getResource() (implemented in ApplicationContext.getResource())
- to return null for non-existing paths.
- The second time in DirContextURLConnection.connect().
-
- It is good that there is a cache in ProxyDirContext that saves time
- for repeated lookups.
-
- - Using URLs involves encoding/decoding.
-
- If there were some other API to access resources in a web application,
- I would prefer some opaque object that allows access to resource
- properties, but is converted to string/url form only on demand.
-
- f) Connection, created from jndi: URL
- DirContextURLStreamHandler, DirContextURLConnection
-
- Goodies:
- - DirContextURLConnection provides information about resource via
- methods such as getContentLength(), getHeaderField(str).
-
- Concerns:
- - It exposes DirContext through some APIs, such as
- DirContextURLConnection.getContent().
-
- Is this feature going to be preserved for compatibility, or to be
- removed?
-
- - DirContextURLConnection.list(): a public method, that is not part of
- the usual URL API. So URL API is lacking. Maybe some other methods
- could be added.
-
- A possible candidate could be "isCollection()", instead of asking for
- getContentType() and checking its value against ResourceAttributes.COLLECTION_TYPE.
-
- Threads:
- http://markmail.org/thread/hqbmdn2qs6xcooko
-
- 8. Review the connector shutdown code for timing and threading issues
- particularly any that may result in a client socket being left open after a
- connector.stop().
- - Complete.
-
- 9. Remove the svn keywords from all the files. (Just Java files?)
- - Complete.
-
-10. Code to the interfaces in the o.a.catalina package and avoid coding directly
- to implementations in other packages. This is likely to require a lot of
- work. Maybe use Structure 101 (or similar) to help.
- - Partially complete - probably as far as is practical.
-
-11. Merge Service and Engine
- - Decided not to do this.
-
-12. Java 7 updates
- - Use of <> operator where possible
- - Complete
- - Use of try with resources
- - Complete
- - Catching multiple exceptions
- - Started
- - javax.[annotation to el] complete
- - remainder TODO
-
-13. Fix all FindBugs warnings
- - Complete
-
-14. Review date formatting with a view to reducing duplication.
diff --git a/bin/catalina-tasks.xml b/bin/catalina-tasks.xml
index 4e6a8ae..c7c9c28 100644
--- a/bin/catalina-tasks.xml
+++ b/bin/catalina-tasks.xml
@@ -1,4 +1,4 @@
-<?xml version="1.0"?>
+<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
diff --git a/bin/catalina.bat b/bin/catalina.bat
index 7208871..02627c4 100755
--- a/bin/catalina.bat
+++ b/bin/catalina.bat
@@ -63,12 +63,6 @@ rem should be used by Tomcat and also by the stop process,
rem the version command etc.
rem Most options should go into CATALINA_OPTS.
rem
-rem JAVA_ENDORSED_DIRS (Optional) Lists of of semi-colon separated directories
-rem containing some jars in order to allow replacement of APIs
-rem created outside of the JCP (i.e. DOM and SAX from W3C).
-rem It can also be used to update the XML parser implementation.
-rem Defaults to $CATALINA_HOME/endorsed.
-rem
rem JPDA_TRANSPORT (Optional) JPDA transport used when the "jpda start"
rem command is executed. The default is "dt_socket".
rem
@@ -339,17 +333,17 @@ goto setArgs
rem Execute Java with the applicable properties
if not "%JPDA%" == "" goto doJpda
if not "%SECURITY_POLICY_FILE%" == "" goto doSecurity
-%_EXECJAVA% %JAVA_OPTS% %CATALINA_OPTS% %DEBUG_OPTS% -Djava.endorsed.dirs="%JAVA_ENDORSED_DIRS%" -classpath "%CLASSPATH%" -Dcatalina.base="%CATALINA_BASE%" -Dcatalina.home="%CATALINA_HOME%" -Djava.io.tmpdir="%CATALINA_TMPDIR%" %MAINCLASS% %CMD_LINE_ARGS% %ACTION%
+%_EXECJAVA% %JAVA_OPTS% %CATALINA_OPTS% %DEBUG_OPTS% -classpath "%CLASSPATH%" -Dcatalina.base="%CATALINA_BASE%" -Dcatalina.home="%CATALINA_HOME%" -Djava.io.tmpdir="%CATALINA_TMPDIR%" %MAINCLASS% %CMD_LINE_ARGS% %ACTION%
goto end
:doSecurity
-%_EXECJAVA% %JAVA_OPTS% %CATALINA_OPTS% %DEBUG_OPTS% -Djava.endorsed.dirs="%JAVA_ENDORSED_DIRS%" -classpath "%CLASSPATH%" -Djava.security.manager -Djava.security.policy=="%SECURITY_POLICY_FILE%" -Dcatalina.base="%CATALINA_BASE%" -Dcatalina.home="%CATALINA_HOME%" -Djava.io.tmpdir="%CATALINA_TMPDIR%" %MAINCLASS% %CMD_LINE_ARGS% %ACTION%
+%_EXECJAVA% %JAVA_OPTS% %CATALINA_OPTS% %DEBUG_OPTS% -classpath "%CLASSPATH%" -Djava.security.manager -Djava.security.policy=="%SECURITY_POLICY_FILE%" -Dcatalina.base="%CATALINA_BASE%" -Dcatalina.home="%CATALINA_HOME%" -Djava.io.tmpdir="%CATALINA_TMPDIR%" %MAINCLASS% %CMD_LINE_ARGS% %ACTION%
goto end
:doJpda
if not "%SECURITY_POLICY_FILE%" == "" goto doSecurityJpda
-%_EXECJAVA% %JAVA_OPTS% %JPDA_OPTS% %CATALINA_OPTS% %DEBUG_OPTS% -Djava.endorsed.dirs="%JAVA_ENDORSED_DIRS%" -classpath "%CLASSPATH%" -Dcatalina.base="%CATALINA_BASE%" -Dcatalina.home="%CATALINA_HOME%" -Djava.io.tmpdir="%CATALINA_TMPDIR%" %MAINCLASS% %CMD_LINE_ARGS% %ACTION%
+%_EXECJAVA% %JAVA_OPTS% %JPDA_OPTS% %CATALINA_OPTS% %DEBUG_OPTS% -classpath "%CLASSPATH%" -Dcatalina.base="%CATALINA_BASE%" -Dcatalina.home="%CATALINA_HOME%" -Djava.io.tmpdir="%CATALINA_TMPDIR%" %MAINCLASS% %CMD_LINE_ARGS% %ACTION%
goto end
:doSecurityJpda
-%_EXECJAVA% %JAVA_OPTS% %JPDA_OPTS% %CATALINA_OPTS% %DEBUG_OPTS% -Djava.endorsed.dirs="%JAVA_ENDORSED_DIRS%" -classpath "%CLASSPATH%" -Djava.security.manager -Djava.security.policy=="%SECURITY_POLICY_FILE%" -Dcatalina.base="%CATALINA_BASE%" -Dcatalina.home="%CATALINA_HOME%" -Djava.io.tmpdir="%CATALINA_TMPDIR%" %MAINCLASS% %CMD_LINE_ARGS% %ACTION%
+%_EXECJAVA% %JAVA_OPTS% %JPDA_OPTS% %CATALINA_OPTS% %DEBUG_OPTS% -classpath "%CLASSPATH%" -Djava.security.manager -Djava.security.policy=="%SECURITY_POLICY_FILE%" -Dcatalina.base="%CATALINA_BASE%" -Dcatalina.home="%CATALINA_HOME%" -Djava.io.tmpdir="%CATALINA_TMPDIR%" %MAINCLASS% %CMD_LINE_ARGS% %ACTION%
goto end
:end
diff --git a/bin/catalina.sh b/bin/catalina.sh
index 51e68e8..e37a8b9 100755
--- a/bin/catalina.sh
+++ b/bin/catalina.sh
@@ -58,12 +58,6 @@
# the version command etc.
# Most options should go into CATALINA_OPTS.
#
-# JAVA_ENDORSED_DIRS (Optional) Lists of of colon separated directories
-# containing some jars in order to allow replacement of APIs
-# created outside of the JCP (i.e. DOM and SAX from W3C).
-# It can also be used to update the XML parser implementation.
-# Defaults to $CATALINA_HOME/endorsed.
-#
# JPDA_TRANSPORT (Optional) JPDA transport used when the "jpda start"
# command is executed. The default is "dt_socket".
#
@@ -98,6 +92,8 @@
# Example (all one line)
# LOGGING_MANAGER="-Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager"
#
+# UMASK (Optional) Override Tomcat's default UMASK of 0027
+#
# USE_NOHUP (Optional) If set to the string true the start command will
# use nohup so that the Tomcat process will ignore any hangup
# signals. Default is "false" unless running on HP-UX in which
@@ -237,7 +233,6 @@ if $cygwin; then
CATALINA_BASE=`cygpath --absolute --windows "$CATALINA_BASE"`
CATALINA_TMPDIR=`cygpath --absolute --windows "$CATALINA_TMPDIR"`
CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
- JAVA_ENDORSED_DIRS=`cygpath --path --windows "$JAVA_ENDORSED_DIRS"`
fi
if [ -z "$JSSE_OPTS" ] ; then
@@ -263,6 +258,12 @@ if [ -z "$LOGGING_MANAGER" ]; then
LOGGING_MANAGER="-Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager"
fi
+# Set UMASK unless it has been overridden
+if [ -z "$UMASK" ]; then
+ UMASK="0027"
+fi
+umask $UMASK
+
# Uncomment the following line to make the umask available when using the
# org.apache.catalina.security.SecurityListener
#JAVA_OPTS="$JAVA_OPTS -Dorg.apache.catalina.security.SecurityListener.UMASK=`umask`"
@@ -326,7 +327,7 @@ if [ "$1" = "debug" ] ; then
fi
shift
exec "$_RUNJDB" "$LOGGING_CONFIG" $LOGGING_MANAGER $JAVA_OPTS $CATALINA_OPTS \
- -Djava.endorsed.dirs="$JAVA_ENDORSED_DIRS" -classpath "$CLASSPATH" \
+ -classpath "$CLASSPATH" \
-sourcepath "$CATALINA_HOME"/../../java \
-Djava.security.manager \
-Djava.security.policy=="$CATALINA_BASE"/conf/catalina.policy \
@@ -336,7 +337,7 @@ if [ "$1" = "debug" ] ; then
org.apache.catalina.startup.Bootstrap "$@" start
else
exec "$_RUNJDB" "$LOGGING_CONFIG" $LOGGING_MANAGER $JAVA_OPTS $CATALINA_OPTS \
- -Djava.endorsed.dirs="$JAVA_ENDORSED_DIRS" -classpath "$CLASSPATH" \
+ -classpath "$CLASSPATH" \
-sourcepath "$CATALINA_HOME"/../../java \
-Dcatalina.base="$CATALINA_BASE" \
-Dcatalina.home="$CATALINA_HOME" \
@@ -354,7 +355,7 @@ elif [ "$1" = "run" ]; then
fi
shift
eval exec "\"$_RUNJAVA\"" "\"$LOGGING_CONFIG\"" $LOGGING_MANAGER $JAVA_OPTS $CATALINA_OPTS \
- -Djava.endorsed.dirs="\"$JAVA_ENDORSED_DIRS\"" -classpath "\"$CLASSPATH\"" \
+ -classpath "\"$CLASSPATH\"" \
-Djava.security.manager \
-Djava.security.policy=="\"$CATALINA_BASE/conf/catalina.policy\"" \
-Dcatalina.base="\"$CATALINA_BASE\"" \
@@ -363,7 +364,7 @@ elif [ "$1" = "run" ]; then
org.apache.catalina.startup.Bootstrap "$@" start
else
eval exec "\"$_RUNJAVA\"" "\"$LOGGING_CONFIG\"" $LOGGING_MANAGER $JAVA_OPTS $CATALINA_OPTS \
- -Djava.endorsed.dirs="\"$JAVA_ENDORSED_DIRS\"" -classpath "\"$CLASSPATH\"" \
+ -classpath "\"$CLASSPATH\"" \
-Dcatalina.base="\"$CATALINA_BASE\"" \
-Dcatalina.home="\"$CATALINA_HOME\"" \
-Djava.io.tmpdir="\"$CATALINA_TMPDIR\"" \
@@ -420,7 +421,7 @@ elif [ "$1" = "start" ] ; then
fi
shift
eval $_NOHUP "\"$_RUNJAVA\"" "\"$LOGGING_CONFIG\"" $LOGGING_MANAGER $JAVA_OPTS $CATALINA_OPTS \
- -Djava.endorsed.dirs="\"$JAVA_ENDORSED_DIRS\"" -classpath "\"$CLASSPATH\"" \
+ -classpath "\"$CLASSPATH\"" \
-Djava.security.manager \
-Djava.security.policy=="\"$CATALINA_BASE/conf/catalina.policy\"" \
-Dcatalina.base="\"$CATALINA_BASE\"" \
@@ -431,7 +432,7 @@ elif [ "$1" = "start" ] ; then
else
eval $_NOHUP "\"$_RUNJAVA\"" "\"$LOGGING_CONFIG\"" $LOGGING_MANAGER $JAVA_OPTS $CATALINA_OPTS \
- -Djava.endorsed.dirs="\"$JAVA_ENDORSED_DIRS\"" -classpath "\"$CLASSPATH\"" \
+ -classpath "\"$CLASSPATH\"" \
-Dcatalina.base="\"$CATALINA_BASE\"" \
-Dcatalina.home="\"$CATALINA_HOME\"" \
-Djava.io.tmpdir="\"$CATALINA_TMPDIR\"" \
@@ -482,8 +483,8 @@ elif [ "$1" = "stop" ] ; then
fi
fi
- eval "\"$_RUNJAVA\"" $LOGGING_MANAGER $JAVA_OPTS \
- -Djava.endorsed.dirs="\"$JAVA_ENDORSED_DIRS\"" -classpath "\"$CLASSPATH\"" \
+ eval "\"$_RUNJAVA\"" $JAVA_OPTS \
+ -classpath "\"$CLASSPATH\"" \
-Dcatalina.base="\"$CATALINA_BASE\"" \
-Dcatalina.home="\"$CATALINA_HOME\"" \
-Djava.io.tmpdir="\"$CATALINA_TMPDIR\"" \
@@ -569,7 +570,7 @@ elif [ "$1" = "stop" ] ; then
elif [ "$1" = "configtest" ] ; then
eval "\"$_RUNJAVA\"" $LOGGING_MANAGER $JAVA_OPTS \
- -Djava.endorsed.dirs="\"$JAVA_ENDORSED_DIRS\"" -classpath "\"$CLASSPATH\"" \
+ -classpath "\"$CLASSPATH\"" \
-Dcatalina.base="\"$CATALINA_BASE\"" \
-Dcatalina.home="\"$CATALINA_HOME\"" \
-Djava.io.tmpdir="\"$CATALINA_TMPDIR\"" \
diff --git a/bin/daemon.sh b/bin/daemon.sh
index dee6c77..5808892 100755
--- a/bin/daemon.sh
+++ b/bin/daemon.sh
@@ -186,7 +186,6 @@ case "$1" in
-errfile "&2" \
-classpath "$CLASSPATH" \
"$LOGGING_CONFIG" $JAVA_OPTS $CATALINA_OPTS \
- -Djava.endorsed.dirs="$JAVA_ENDORSED_DIRS" \
-Dcatalina.base="$CATALINA_BASE" \
-Dcatalina.home="$CATALINA_HOME" \
-Djava.io.tmpdir="$CATALINA_TMP" \
@@ -203,7 +202,6 @@ case "$1" in
-errfile "&1" \
-classpath "$CLASSPATH" \
"$LOGGING_CONFIG" $JAVA_OPTS $CATALINA_OPTS \
- -Djava.endorsed.dirs="$JAVA_ENDORSED_DIRS" \
-Dcatalina.base="$CATALINA_BASE" \
-Dcatalina.home="$CATALINA_HOME" \
-Djava.io.tmpdir="$CATALINA_TMP" \
@@ -215,7 +213,6 @@ case "$1" in
-stop \
-pidfile "$CATALINA_PID" \
-classpath "$CLASSPATH" \
- -Djava.endorsed.dirs="$JAVA_ENDORSED_DIRS" \
-Dcatalina.base="$CATALINA_BASE" \
-Dcatalina.home="$CATALINA_HOME" \
-Djava.io.tmpdir="$CATALINA_TMP" \
diff --git a/bin/service.bat b/bin/service.bat
index 6cc94bc..4bbc6c3 100755
--- a/bin/service.bat
+++ b/bin/service.bat
@@ -159,7 +159,7 @@ if not "%CATALINA_HOME%" == "%CATALINA_BASE%" set "CLASSPATH=%CLASSPATH%;%CATALI
--StopClass org.apache.catalina.startup.Bootstrap ^
--StartParams start ^
--StopParams stop ^
- --JvmOptions "-Dcatalina.home=%CATALINA_HOME%;-Dcatalina.base=%CATALINA_BASE%;-Djava.endorsed.dirs=%CATALINA_HOME%\endorsed;-Djava.io.tmpdir=%CATALINA_BASE%\temp;-Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager;-Djava.util.logging.config.file=%CATALINA_BASE%\conf\logging.properties" ^
+ --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" ^
--JvmMs 128 ^
--JvmMx 256
if not errorlevel 1 goto installed
diff --git a/bin/setclasspath.bat b/bin/setclasspath.bat
index 5e4d8d1..4cf6659 100755
--- a/bin/setclasspath.bat
+++ b/bin/setclasspath.bat
@@ -15,9 +15,8 @@ rem See the License for the specific language governing permissions and
rem limitations under the License.
rem ---------------------------------------------------------------------------
-rem Set JAVA_HOME or JRE_HOME if not already set, ensure any provided settings
-rem are valid and consistent with the selected start-up options and set up the
-rem endorsed directory.
+rem Set JAVA_HOME or JRE_HOME if not already set and ensure any provided
+rem settings are valid and consistent with the selected start-up options.
rem ---------------------------------------------------------------------------
rem Make sure prerequisite environment variables are set
@@ -65,11 +64,6 @@ echo This environment variable is needed to run this program
goto exit
:okJava
-rem Don't override the endorsed dir if the user has set it previously
-if not "%JAVA_ENDORSED_DIRS%" == "" goto gotEndorseddir
-rem Set the default -Djava.endorsed.dirs argument
-set "JAVA_ENDORSED_DIRS=%CATALINA_HOME%\endorsed"
-:gotEndorseddir
rem Don't override _RUNJAVA if the user has set it previously
if not "%_RUNJAVA%" == "" goto gotRunJava
diff --git a/bin/setclasspath.sh b/bin/setclasspath.sh
index 873fad3..ee8deb3 100755
--- a/bin/setclasspath.sh
+++ b/bin/setclasspath.sh
@@ -16,9 +16,8 @@
# limitations under the License.
# -----------------------------------------------------------------------------
-# Set JAVA_HOME or JRE_HOME if not already set, ensure any provided settings
-# are valid and consistent with the selected start-up options and set up the
-# endorsed directory.
+# Set JAVA_HOME or JRE_HOME if not already set and ensure any provided
+# settings are valid and consistent with the selected start-up options.
# -----------------------------------------------------------------------------
# Make sure prerequisite environment variables are set
@@ -77,12 +76,6 @@ if [ "$1" = "debug" ] ; then
fi
fi
-# Don't override the endorsed dir if the user has set it previously
-if [ -z "$JAVA_ENDORSED_DIRS" ]; then
- # Set the default -Djava.endorsed.dirs argument
- JAVA_ENDORSED_DIRS="$CATALINA_HOME"/endorsed
-fi
-
# Set standard commands for invoking Java, if not already set.
if [ -z "$_RUNJAVA" ]; then
_RUNJAVA="$JRE_HOME"/bin/java
diff --git a/bin/tool-wrapper.bat b/bin/tool-wrapper.bat
index fa52e32..64f38a0 100755
--- a/bin/tool-wrapper.bat
+++ b/bin/tool-wrapper.bat
@@ -31,12 +31,6 @@ rem Defaults to JAVA_HOME if empty. If JRE_HOME and JAVA_HOME
rem are both set, JRE_HOME is used.
rem
rem JAVA_OPTS (Optional) Java runtime options.
-rem
-rem JAVA_ENDORSED_DIRS (Optional) Lists of of semi-colon separated directories
-rem containing some jars in order to allow replacement of APIs
-rem created outside of the JCP (i.e. DOM and SAX from W3C).
-rem It can also be used to update the XML parser implementation.
-rem Defaults to $CATALINA_HOME/endorsed.
rem ---------------------------------------------------------------------------
setlocal
@@ -91,6 +85,6 @@ shift
goto setArgs
:doneSetArgs
-%_RUNJAVA% %JAVA_OPTS% %TOOL_OPTS% -Djava.endorsed.dirs="%JAVA_ENDORSED_DIRS%" -classpath "%CLASSPATH%" -Dcatalina.home="%CATALINA_HOME%" org.apache.catalina.startup.Tool %CMD_LINE_ARGS%
+%_RUNJAVA% %JAVA_OPTS% %TOOL_OPTS% -classpath "%CLASSPATH%" -Dcatalina.home="%CATALINA_HOME%" org.apache.catalina.startup.Tool %CMD_LINE_ARGS%
:end
diff --git a/bin/tool-wrapper.sh b/bin/tool-wrapper.sh
index c87fa9e..0262051 100755
--- a/bin/tool-wrapper.sh
+++ b/bin/tool-wrapper.sh
@@ -32,12 +32,6 @@
# are both set, JRE_HOME is used.
#
# JAVA_OPTS (Optional) Java runtime options.
-#
-# JAVA_ENDORSED_DIRS (Optional) Lists of of colon separated directories
-# containing some jars in order to allow replacement of APIs
-# created outside of the JCP (i.e. DOM and SAX from W3C).
-# It can also be used to update the XML parser implementation.
-# Defaults to $CATALINA_HOME/endorsed.
# -----------------------------------------------------------------------------
# OS specific support. $var _must_ be set to either true or false.
@@ -126,7 +120,6 @@ if $cygwin; then
JRE_HOME=`cygpath --absolute --windows "$JRE_HOME"`
CATALINA_HOME=`cygpath --absolute --windows "$CATALINA_HOME"`
CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
- JAVA_ENDORSED_DIRS=`cygpath --path --windows "$JAVA_ENDORSED_DIRS"`
fi
JAVA_OPTS="$JAVA_OPTS -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager"
@@ -134,6 +127,6 @@ JAVA_OPTS="$JAVA_OPTS -Djava.util.logging.manager=org.apache.juli.ClassLoaderLog
# ----- Execute The Requested Command -----------------------------------------
exec "$_RUNJAVA" $JAVA_OPTS $TOOL_OPTS \
- -Djava.endorsed.dirs="$JAVA_ENDORSED_DIRS" -classpath "$CLASSPATH" \
+ -classpath "$CLASSPATH" \
-Dcatalina.home="$CATALINA_HOME" \
org.apache.catalina.startup.Tool "$@"
diff --git a/build.properties.default b/build.properties.default
index 1654b91..5c2dee1 100644
--- a/build.properties.default
+++ b/build.properties.default
@@ -24,15 +24,14 @@
# ----- Version Control Flags -----
version.major=8
-version.minor=0
-version.build=39
+version.minor=5
+version.build=8
version.patch=0
version.suffix=
# ----- Build control flags -----
# Note enabling validation uses Checkstyle which is LGPL licensed
execute.validate=false
-execute.test.bio=true
execute.test.nio=true
execute.test.nio2=true
# Still requires APR/native library to be present
@@ -77,8 +76,6 @@ trydownload.httpusecaches=true
# contexts by the various build scripts.
base.path=${user.home}/tomcat-build-libs
-compile.source=1.7
-compile.target=1.7
compile.debug=true
# Do not pass -deprecation (-Xlint:deprecation) flag to javac
@@ -98,42 +95,6 @@ base-maven.loc=http://repo.maven.apache.org/maven2
# Mirror, was used when there were problems with the main SF downloads site
# base-sf.loc=http://sunet.dl.sourceforge.net
-# ----- Commons Logging, version 1.1 or later -----
-# If this version is updated, check the versions required for the dependencies below
-# - avalon-framework
-# - log4j
-# - logkit
-# - servletapi
-commons-logging.version=1.1.3
-commons-logging.home=${base.path}/commons-logging-${commons-logging.version}
-commons-logging-src.loc.1=${base-commons.loc.1}/logging/source/commons-logging-${commons-logging.version}-src.tar.gz
-commons-logging-src.loc.2=${base-commons.loc.2}/logging/source/commons-logging-${commons-logging.version}-src.tar.gz
-commons-logging-src.tar.gz=${commons-logging.home}/commons-logging-${commons-logging.version}-src.tar.gz
-
-# ----- Avalon Framework (required by commons logging) -----
-avalon-framework.version=4.1.5
-avalon-framework.home=${base.path}/avalon-framework-${avalon-framework.version}
-avalon-framework.loc=${base-maven.loc}/avalon-framework/avalon-framework/${avalon-framework.version}/avalon-framework-${avalon-framework.version}.jar
-avalon-framework.jar=${avalon-framework.home}/avalon-framework-${avalon-framework.version}.jar
-
-# ----- log4j (required by commons logging) -----
-log4j.version=1.2.17
-log4j.home=${base.path}/log4j-${log4j.version}
-log4j.loc=${base-maven.loc}/log4j/log4j/${log4j.version}/log4j-${log4j.version}.jar
-log4j.jar=${log4j.home}/log4j-${log4j.version}.jar
-
-# ----- logkit (required by commons logging) -----
-logkit.version=1.0.1
-logkit.home=${base.path}/logkit-${logkit.version}
-logkit.loc=${base-maven.loc}/logkit/logkit/${logkit.version}/logkit-${logkit.version}.jar
-logkit.jar=${logkit.home}/logkit-${logkit.version}.jar
-
-# ----- servletapi (required by commons logging) -----
-servletapi.version=2.3
-servletapi.home=${base.path}/servletapi-${servletapi.version}
-servletapi.loc=${base-maven.loc}/servletapi/servletapi/${servletapi.version}/servletapi-${servletapi.version}.jar
-servletapi.jar=${servletapi.home}/servletapi-${servletapi.version}.jar
-
# ----- Webservices - JAX RPC -----
jaxrpc-lib.version=1.1-rc4
jaxrpc-lib.home=${base.path}/jaxrpc-${jaxrpc-lib.version}
@@ -146,7 +107,7 @@ wsdl4j-lib.home=${base.path}/wsdl4j-${wsdl4j-lib.version}
wsdl4j-lib.loc=${base-maven.loc}/wsdl4j/wsdl4j/${wsdl4j-lib.version}/wsdl4j-${wsdl4j-lib.version}.jar
wsdl4j-lib.jar=${wsdl4j-lib.home}/wsdl4j-${wsdl4j-lib.version}.jar
-# ----- Eclipse JDT, version 4.5 or later -----#
+# ----- Eclipse JDT, version 4.5.1 or later -----#
# See https://wiki.apache.org/tomcat/JDTCoreBatchCompiler before updating
jdt.version=4.5.1
jdt.release=R-4.5.1-201509040015
@@ -225,16 +186,6 @@ checkstyle.home=${base.path}/checkstyle-${checkstyle.version}
checkstyle.loc=${base-sf.loc}/checkstyle/checkstyle/${checkstyle.version}/checkstyle-${checkstyle.version}-all.jar
checkstyle.jar=${checkstyle.home}/checkstyle-${checkstyle.version}-all.jar
-# ----- JSON Libraries (for bayeux module) -----
-json-lib.home=${base.path}/json-20080701
-json-lib.lib=http://repo1.maven.org/maven2/org/json/json/20080701/json-20080701.jar
-json-lib.jar=json.jar
-
-# ----- Dojo Toolkit (for bayeux module) -----
-dojo-js.home=${base.path}/dojo-release-1.1.1
-dojo-js.loc=http://download.dojotoolkit.org/release-1.1.1/dojo-release-1.1.1.tar.gz
-dojo-js.jar=${dojo-js.home}/dojo/dojo.js
-
# ----- Cobertura code coverage tool -----
cobertura.version=2.1.1
cobertura.home=${base.path}/cobertura-${cobertura.version}
diff --git a/build.xml b/build.xml
index 1d4b480..7fd18e4 100644
--- a/build.xml
+++ b/build.xml
@@ -1,4 +1,4 @@
-<?xml version="1.0"?>
+<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
@@ -15,7 +15,7 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<project name="Tomcat 8.0" default="deploy" basedir=".">
+<project name="Tomcat 8.5" default="deploy" basedir=".">
<!-- ===================== Initialize Property Values ==================== -->
@@ -48,6 +48,8 @@
<property name="jsp.revision" value="FR" />
<property name="el.revision" value="FR" />
<property name="websocket.revision" value="FR" />
+ <!-- MR B but this was first 1.1 release so use FR -->
+ <property name="jaspic.revision" value="FR" />
<!-- Release artifact base names -->
<property name="final.name" value="${project}-${version}" />
@@ -93,6 +95,7 @@
<property name="jsp-api.jar" value="${tomcat.build}/lib/jsp-api.jar"/>
<property name="el-api.jar" value="${tomcat.build}/lib/el-api.jar"/>
<property name="websocket-api.jar" value="${tomcat.build}/lib/websocket-api.jar"/>
+ <property name="jaspic-api.jar" value="${tomcat.build}/lib/jaspic-api.jar"/>
<property name="tomcat-websocket.jar" value="${tomcat.build}/lib/tomcat-websocket.jar"/>
<property name="catalina.jar" value="${tomcat.build}/lib/catalina.jar"/>
<property name="catalina-tribes.jar" value="${tomcat.build}/lib/catalina-tribes.jar"/>
@@ -118,6 +121,7 @@
<property name="jsp-api-src.jar" value="${tomcat.src.jars}/jsp-api-src.jar"/>
<property name="el-api-src.jar" value="${tomcat.src.jars}/el-api-src.jar"/>
<property name="websocket-api-src.jar" value="${tomcat.src.jars}/websocket-api-src.jar"/>
+ <property name="jaspic-api-src.jar" value="${tomcat.src.jars}/jaspic-api-src.jar"/>
<property name="tomcat-websocket-src.jar" value="${tomcat.src.jars}/tomcat-websocket-src.jar"/>
<property name="catalina-src.jar" value="${tomcat.src.jars}/catalina-src.jar"/>
<property name="catalina-tribes-src.jar" value="${tomcat.src.jars}/catalina-tribes-src.jar"/>
@@ -139,26 +143,18 @@
<property name="tomcat-embed-jasper.jar" value="${tomcat.embed}/tomcat-embed-jasper.jar"/>
<property name="tomcat-embed-el.jar" value="${tomcat.embed}/tomcat-embed-el.jar"/>
<property name="tomcat-embed-websocket.jar" value="${tomcat.embed}/tomcat-embed-websocket.jar"/>
- <property name="tomcat-embed-juli.jar" value="${tomcat.embed}/tomcat-embed-logging-juli.jar"/>
<property name="tomcat-embed-core-sources.jar" value="${tomcat.embed.sources}/tomcat-embed-core-src.jar"/>
<property name="tomcat-embed-jasper-sources.jar" value="${tomcat.embed.sources}/tomcat-embed-jasper-src.jar"/>
<property name="tomcat-embed-el-sources.jar" value="${tomcat.embed.sources}/tomcat-embed-el-src.jar"/>
<property name="tomcat-embed-websocket-sources.jar" value="${tomcat.embed.sources}/tomcat-embed-websocket-src.jar"/>
- <property name="tomcat-embed-juli-sources.jar" value="${tomcat.embed.sources}/tomcat-embed-logging-juli-src.jar"/>
<!-- Extras JARs & source JARs -->
- <property name="tomcat-juli-extras.jar" value="${tomcat.extras}/tomcat-juli.jar"/>
- <property name="tomcat-juli-adapters.jar" value="${tomcat.extras}/tomcat-juli-adapters.jar"/>
<property name="catalina-ws.jar" value="${tomcat.extras}/catalina-ws.jar"/>
<property name="catalina-jmx-remote.jar" value="${tomcat.extras}/catalina-jmx-remote.jar"/>
- <property name="tomcat-embed-log4j.jar" value="${tomcat.embed}/tomcat-embed-logging-log4j.jar"/>
- <property name="tomcat-juli-extras-src.jar" value="${tomcat.extras.sources}/tomcat-juli-src.jar"/>
- <property name="tomcat-juli-adapters-src.jar" value="${tomcat.extras.sources}/tomcat-juli-adapters-src.jar"/>
<property name="catalina-ws-src.jar" value="${tomcat.extras.sources}/catalina-ws-src.jar"/>
<property name="catalina-jmx-remote-src.jar" value="${tomcat.extras.sources}/catalina-jmx-remote-src.jar"/>
- <property name="tomcat-embed-log4j-src.jar" value="${tomcat.embed.sources}/tomcat-embed-logging-log4j-src.jar"/>
<!-- jdbc-pool JARs & source JARs -->
<property name="tomcat-jdbc.jar" value="${tomcat.pool}/tomcat-jdbc.jar"/>
@@ -321,6 +317,10 @@
<include name="javax/websocket/**" />
</patternset>
+ <patternset id="files.jaspic-api">
+ <include name="javax/security/auth/message/**" />
+ </patternset>
+
<patternset id="files.tomcat-websocket">
<include name="org/apache/tomcat/websocket/**" />
</patternset>
@@ -358,6 +358,7 @@
<patternset id="files.tomcat-util">
<include name="org/apache/tomcat/util/buf/**" />
<include name="org/apache/tomcat/util/codec/**" />
+ <include name="org/apache/tomcat/util/collections/**" />
<include name="org/apache/tomcat/util/compat/**" />
<include name="org/apache/tomcat/util/file/**" />
<include name="org/apache/tomcat/util/res/**" />
@@ -365,7 +366,6 @@
<include name="org/apache/tomcat/util/threads/**" />
<include name="org/apache/tomcat/util/*" />
<exclude name="org/apache/tomcat/util/bcel" />
- <exclude name="org/apache/tomcat/util/collections" />
<exclude name="org/apache/tomcat/util/descriptor" />
<exclude name="org/apache/tomcat/util/digester" />
<exclude name="org/apache/tomcat/util/http" />
@@ -417,7 +417,6 @@
<include name="org/apache/coyote/**" />
<!-- Remaining tomcat-util packages -->
<include name="org/apache/tomcat/util/bcel/**" />
- <include name="org/apache/tomcat/util/collections/**" />
<include name="org/apache/tomcat/util/http/**" />
<include name="org/apache/tomcat/util/log/**" />
<include name="org/apache/tomcat/util/modeler/**" />
@@ -442,7 +441,9 @@
<patternset refid="files.annotations-api" />
<patternset refid="files.catalina" />
<patternset refid="files.servlet-api" />
+ <patternset refid="files.jaspic-api" />
<patternset refid="files.tomcat-api" />
+ <patternset refid="files.tomcat-juli" />
<!-- These pattern sets conflict so include files directly
<patternset refid="files.tomcat-coyote" />
<patternset refid="files.tomcat-util" />
@@ -470,9 +471,6 @@
<patternset refid="files.tomcat-websocket" />
</patternset>
- <!-- Pattern sets used directly -->
- <!--<patternset refid="files.tomcat-juli" />-->
-
<!-- Pattern sets not included in embedded -->
<!-- Cluster support not included in embedded -->
<!--<patternset refid="files.catalina-tribes" />-->
@@ -489,15 +487,6 @@
<include name="org/apache/catalina/mbeans/JmxRemote*" />
</patternset>
- <patternset id="files.tomcat-extras-juli-adapters">
- <include name="org/apache/juli/logging/impl/**" />
- <exclude name="org/apache/juli/logging/impl/WeakHashtable*" />
- <exclude name="org/apache/juli/logging/impl/LogFactoryImpl*" />
- <!-- Javadoc and i18n exclusions -->
- <exclude name="**/package.html" />
- <exclude name="**/LocalStrings_*" />
- </patternset>
-
<!-- =========================== Build targets =========================== -->
<target name="build-prepare">
@@ -660,7 +649,7 @@
<compilerarg value="-Xlint:unchecked"/>
-->
<classpath refid="compile.classpath" />
- <exclude name="org/apache/naming/factory/webservices/**" />
+ <exclude name="org/apache/naming/factory/webservices/**" />
</javac>
<!-- Copy static resource files -->
<copy todir="${tomcat.classes}" encoding="ISO-8859-1">
@@ -687,6 +676,7 @@
<filter token="jsp.revision" value="${jsp.revision}"/>
<filter token="el.revision" value="${el.revision}"/>
<filter token="websocket.revision" value="${websocket.revision}"/>
+ <filter token="jaspic.revision" value="${jaspic.revision}"/>
<mkdir dir="${tomcat.manifests}" />
<copy todir="${tomcat.manifests}" overwrite="yes" filtering="yes"
@@ -730,6 +720,12 @@
filesId="files.websocket-api"
manifest="${tomcat.manifests}/websocket-api.jar.manifest" />
+ <!-- JASPIC 1.1 API JAR File -->
+ <jarIt jarfile="${jaspic-api.jar}"
+ filesDir="${tomcat.classes}"
+ filesId="files.jaspic-api"
+ manifest="${tomcat.manifests}/jaspic-api.jar.manifest" />
+
<!-- WebSocket 1.1 implementation JAR File -->
<jarIt jarfile="${tomcat-websocket.jar}"
filesDir="${tomcat.classes}"
@@ -1233,9 +1229,6 @@
filesDir="${tomcat.classes}"
filesId="files.tomcat-embed-websocket"
meta-inf="${tomcat.manifests}/tomcat-websocket.jar"/>
- <jarIt jarfile="${tomcat-embed-juli.jar}"
- filesDir="${tomcat.classes}"
- filesId="files.tomcat-juli"/>
</target>
@@ -1259,9 +1252,6 @@
<jarIt jarfile="${tomcat-embed-websocket-sources.jar}"
filesDir="java"
filesId="files.tomcat-embed-websocket"/>
- <jarIt jarfile="${tomcat-embed-juli-sources.jar}"
- filesDir="java"
- filesId="files.tomcat-juli"/>
<copy file="${tomcat-dbcp-src.jar}" todir="${tomcat.embed.sources}" />
@@ -1271,7 +1261,7 @@
<target name="embed"
description="Creates the experimental embedded release"
- depends="embed-jars,embed-sources,embed-extras" >
+ depends="embed-jars,embed-sources" >
<fixcrlf srcdir="${tomcat.embed}" eol="crlf"
encoding="ISO-8859-1" fixlast="false" >
@@ -1337,7 +1327,7 @@
<property name="junit.formatter.extension" value=".txt" />
<target name="test" description="Runs the JUnit test cases"
- depends="test-bio,test-nio,test-nio2,test-apr,cobertura-report" >
+ depends="test-nio,test-nio2,test-apr,cobertura-report" >
<fileset id="test.result.skippedtests" dir="${test.reports}" includes="*.txt">
<not>
<contains text="Skipped: 0" />
@@ -1371,12 +1361,6 @@
<fail if="test.result.failure" message='Some tests completed with a Failure. See ${tomcat.build}/logs for details, search for "FAILED".' />
</target>
- <target name="test-bio" description="Runs the JUnit test cases for BIO. Does not stop on errors."
- depends="test-compile,deploy,cobertura-instrument,test-openssl-exists" if="${execute.test.bio}">
- <runtests protocol="org.apache.coyote.http11.Http11Protocol"
- extension=".BIO" />
- </target>
-
<target name="test-nio" description="Runs the JUnit test cases for NIO. Does not stop on errors."
depends="test-compile,deploy,cobertura-instrument,test-openssl-exists" if="${execute.test.nio}">
<runtests protocol="org.apache.coyote.http11.Http11NioProtocol"
@@ -1450,6 +1434,7 @@
<sysproperty key="tomcat.test.reports" value="${test.reports}" />
<sysproperty key="tomcat.test.openssl.path" value="${test.openssl.path}" />
<sysproperty key="tomcat.test.relaxTiming" value="${test.relaxTiming}" />
+ <sysproperty key="tomcat.test.sslImplementation" value="${test.sslImplementation}" />
<!-- File for Cobertura to write coverage results to -->
<sysproperty key="net.sourceforge.cobertura.datafile" file="${cobertura.datafile}" />
@@ -1472,7 +1457,7 @@
<!-- Exclude the tests known to fail -->
<exclude name="org/apache/catalina/tribes/test/**" />
<!-- Exclude the OpenSSL tests unless OpenSSL is available -->
- <exclude name="org/apache/tomcat/util/net/jsse/openssl/**" unless="${test.openssl.exists}" />
+ <exclude name="org/apache/tomcat/util/net/openssl/ciphers/**" unless="${test.openssl.exists}" />
<!-- Exclude performance tests. E.g. on systems with slow/inconsistent timing -->
<exclude name="**/*Performance.java" if="${test.excludePerformance}" />
</fileset>
@@ -1523,6 +1508,7 @@
<exclude name="lib/**/jetty*.jar" />
<exclude name="lib/**/servlet-api*.jar" />
</fileset>
+ <pathelement path="res/cobertura"/>
</path>
<taskdef classpathref="cobertura.classpath" resource="tasks.properties" />
@@ -1554,7 +1540,7 @@
</target>
<target name="cobertura-report" if="${cobertura.enabled}"
- depends="test-bio,test-nio,test-nio2,test-apr"
+ depends="test-nio,test-nio2,test-apr"
description="Creates report from gathered Cobertura results">
<cobertura-report srcdir="${basedir}/java" destdir="${cobertura.out}"
@@ -1593,149 +1579,9 @@
<mkdir dir="${tomcat.extras.sources}"/>
<mkdir dir="${tomcat.embed}"/>
<mkdir dir="${tomcat.embed.sources}"/>
- <mkdir dir="${tomcat.extras}/logging"/>
<mkdir dir="${tomcat.extras}/webservices"/>
</target>
- <target name="extras-commons-logging-prepare"
- depends="extras-prepare"
- description="Prepare to build web services extras package">
-
- <antcall target="downloadfile-2">
- <param name="sourcefile.1" value="${commons-logging-src.loc.1}"/>
- <param name="sourcefile.2" value="${commons-logging-src.loc.2}"/>
- <param name="destfile" value="${commons-logging-src.tar.gz}"/>
- <param name="destdir" value="${commons-logging.home}"/>
- </antcall>
-
- <antcall target="downloadfile">
- <param name="sourcefile" value="${avalon-framework.loc}"/>
- <param name="destfile" value="${avalon-framework.jar}"/>
- <param name="destdir" value="${avalon-framework.home}"/>
- </antcall>
-
- <antcall target="downloadfile">
- <param name="sourcefile" value="${log4j.loc}"/>
- <param name="destfile" value="${log4j.jar}"/>
- <param name="destdir" value="${log4j.home}"/>
- </antcall>
-
- <antcall target="downloadfile">
- <param name="sourcefile" value="${logkit.loc}"/>
- <param name="destfile" value="${logkit.jar}"/>
- <param name="destdir" value="${logkit.home}"/>
- </antcall>
-
- <antcall target="downloadfile">
- <param name="sourcefile" value="${servletapi.loc}"/>
- <param name="destfile" value="${servletapi.jar}"/>
- <param name="destdir" value="${servletapi.home}"/>
- </antcall>
-
- </target>
-
- <target name="extras-commons-logging"
- depends="extras-commons-logging-prepare,compile,build-manifests"
- description="Build JULI for log4j extras package">
-
- <gunzip src="${commons-logging-src.tar.gz}"
- dest="${tomcat.extras}/logging/commons-logging-src.tar"/>
- <untar src="${tomcat.extras}/logging/commons-logging-src.tar"
- dest="${tomcat.extras}/logging/"/>
-
- <replace dir="${tomcat.extras}/logging/commons-logging-${commons-logging.version}-src/src/main/java/org/apache/commons"
- encoding="ISO-8859-1">
- <replacefilter token="org.apache.commons"
- value="org.apache.juli" />
- </replace>
- <mkdir dir="${tomcat.extras}/logging/commons-logging-${commons-logging.version}-src/src/main/java/org/apache/juli" />
- <move todir="${tomcat.extras}/logging/commons-logging-${commons-logging.version}-src/src/main/java/org/apache/juli">
- <fileset dir="${tomcat.extras}/logging/commons-logging-${commons-logging.version}-src/src/main/java/org/apache/commons" />
- </move>
-
- <replace dir="${tomcat.extras}/logging/commons-logging-${commons-logging.version}-src"
- encoding="ISO-8859-1">
- <replacefilter token="org.apache.commons"
- value="org.apache.juli" />
- <replacefilter token="org/apache/commons/"
- value="org/apache/juli/" />
- </replace>
- <copy tofile="${tomcat.extras}/logging/commons-logging-${commons-logging.version}-src/build2.xml"
- file="${tomcat.extras}/logging/commons-logging-${commons-logging.version}-src/build.xml" />
-
- <copy todir="${tomcat.extras}/logging/commons-logging-${commons-logging.version}-src">
- <fileset file="${avalon-framework.jar}" />
- <fileset file="${log4j.jar}" />
- <fileset file="${logkit.jar}" />
- <fileset file="${servletapi.jar}" />
- </copy>
-
- <ant antfile="${tomcat.extras}/logging/commons-logging-${commons-logging.version}-src/build2.xml"
- dir="${tomcat.extras}/logging/commons-logging-${commons-logging.version}-src"
- inheritAll="false" target="compile" />
-
- <jar jarfile="${tomcat-juli-extras.jar}"
- manifest="${tomcat.manifests}/default.manifest">
- <fileset dir="${tomcat.extras}/logging/commons-logging-${commons-logging.version}-src/target/classes">
- <include name="org/apache/juli/logging/*" />
- <include name="org/apache/juli/logging/impl/LogFactoryImpl*.class" />
- <include name="org/apache/juli/logging/impl/WeakHashtable*.class" />
- <include name="org/apache/juli/logging/impl/SimpleLog*.class" />
- <include name="org/apache/juli/logging/impl/NoOpLog*.class" />
- <include name="org/apache/juli/logging/impl/Jdk14Logger.class" />
- <!-- Javadoc and i18n exclusions -->
- <exclude name="**/package.html" />
- <exclude name="**/LocalStrings_*" />
- </fileset>
- <fileset dir="${tomcat.classes}">
- <include name="org/apache/juli/*" />
- <!-- Javadoc and i18n exclusions -->
- <exclude name="**/package.html" />
- <exclude name="**/LocalStrings_*" />
- </fileset>
- <zipfileset file="${tomcat.manifests}/default.notice"
- fullpath="META-INF/NOTICE" />
- <zipfileset file="${tomcat.manifests}/default.license"
- fullpath="META-INF/LICENSE" />
- </jar>
- <hashAndSign file="${tomcat-juli-extras.jar}" />
-
- <jarIt jarfile="${tomcat-juli-adapters.jar}"
- filesDir="${tomcat.extras}/logging/commons-logging-${commons-logging.version}-src/target/classes"
- filesId="files.tomcat-extras-juli-adapters" />
- <hashAndSign file="${tomcat-juli-adapters.jar}" />
-
- <!-- Source JARs -->
- <jar jarfile="${tomcat-juli-extras-src.jar}"
- manifest="${tomcat.manifests}/default.manifest">
- <fileset dir="${tomcat.extras}/logging/commons-logging-${commons-logging.version}-src/src/main/java">
- <include name="org/apache/juli/logging/*.java" />
- <include name="org/apache/juli/logging/impl/LogFactoryImpl*.java" />
- <include name="org/apache/juli/logging/impl/WeakHashtable*.java" />
- <include name="org/apache/juli/logging/impl/SimpleLog*.java" />
- <include name="org/apache/juli/logging/impl/NoOpLog*.java" />
- <include name="org/apache/juli/logging/impl/Jdk14Logger.java" />
- <!-- Javadoc and i18n exclusions -->
- <exclude name="**/package.html" />
- <exclude name="**/LocalStrings_*" />
- </fileset>
- <fileset dir="java">
- <include name="org/apache/juli/*" />
- <!-- Javadoc and i18n exclusions -->
- <exclude name="**/package.html" />
- <exclude name="**/LocalStrings_*" />
- </fileset>
- <zipfileset file="${tomcat.manifests}/default.notice"
- fullpath="META-INF/NOTICE" />
- <zipfileset file="${tomcat.manifests}/default.license"
- fullpath="META-INF/LICENSE" />
- </jar>
- <jarIt jarfile="${tomcat-juli-adapters-src.jar}"
- filesDir="${tomcat.extras}/logging/commons-logging-${commons-logging.version}-src/src/main/java"
- filesId="files.tomcat-extras-juli-adapters" />
-
- </target>
-
<target name="extras-webservices-prepare"
depends="extras-prepare"
description="Prepare to build web services extras package">
@@ -1802,57 +1648,10 @@
</target>
<target name="extras"
- depends="extras-commons-logging,extras-webservices,extras-jmx-remote"
+ depends="extras-webservices,extras-jmx-remote"
description="Build all extras packages">
</target>
- <target name="embed-extras" depends="extras"
- description="Embedded packaging for those extras that can use it">
-
- <jar jarfile="${tomcat-embed-log4j.jar}"
- manifest="${tomcat.manifests}/default.manifest">
- <fileset dir="${tomcat.extras}/logging/commons-logging-${commons-logging.version}-src/target/classes">
- <include name="org/apache/juli/logging/*" />
- <include name="org/apache/juli/logging/impl/*" />
- <!-- Javadoc and i18n exclusions -->
- <exclude name="**/package.html" />
- <exclude name="**/LocalStrings_*" />
- </fileset>
- <fileset dir="${tomcat.classes}">
- <include name="org/apache/juli/*" />
- <!-- Javadoc and i18n exclusions -->
- <exclude name="**/package.html" />
- <exclude name="**/LocalStrings_*" />
- </fileset>
- <zipfileset file="${tomcat.manifests}/default.notice"
- fullpath="META-INF/NOTICE" />
- <zipfileset file="${tomcat.manifests}/default.license"
- fullpath="META-INF/LICENSE" />
- </jar>
-
- <jar jarfile="${tomcat-embed-log4j-src.jar}"
- manifest="${tomcat.manifests}/default.manifest">
- <fileset dir="${tomcat.extras}/logging/commons-logging-${commons-logging.version}-src/src/main/java">
- <include name="org/apache/juli/logging/*.java" />
- <include name="org/apache/juli/logging/impl/*.java" />
- <!-- Javadoc and i18n exclusions -->
- <exclude name="**/package.html" />
- <exclude name="**/LocalStrings_*" />
- </fileset>
- <fileset dir="java">
- <include name="org/apache/juli/*" />
- <!-- Javadoc and i18n exclusions -->
- <exclude name="**/package.html" />
- <exclude name="**/LocalStrings_*" />
- </fileset>
- <zipfileset file="${tomcat.manifests}/default.notice"
- fullpath="META-INF/NOTICE" />
- <zipfileset file="${tomcat.manifests}/default.license"
- fullpath="META-INF/LICENSE" />
- </jar>
-
- </target>
-
<target name="dist-prepare" depends="download-dist">
<mkdir dir="${tomcat.dist}"/>
<mkdir dir="${tomcat.dist}/bin"/>
@@ -1947,7 +1746,8 @@ Apache Tomcat ${version} native binaries for Win64 AMD64/EMT64 platform.
charset="UTF-8"
additionalparam="-breakiterator -notimestamp"
maxmemory="512m"
- failonerror="true">
+ failonerror="true"
+ failonwarning="true">
<classpath>
<path refid="compile.classpath"/>
<path refid="tomcat.webservices.classpath"/>
@@ -1967,7 +1767,8 @@ Apache Tomcat ${version} native binaries for Win64 AMD64/EMT64 platform.
charset="UTF-8"
additionalparam="-breakiterator -notimestamp"
maxmemory="512m"
- failonerror="true">
+ failonerror="true"
+ failonwarning="true">
<classpath>
<path refid="compile.classpath"/>
<path refid="tomcat.webservices.classpath"/>
@@ -1987,7 +1788,8 @@ Apache Tomcat ${version} native binaries for Win64 AMD64/EMT64 platform.
charset="UTF-8"
additionalparam="-breakiterator -notimestamp"
maxmemory="512m"
- failonerror="true">
+ failonerror="true"
+ failonwarning="true">
<classpath>
<path refid="compile.classpath"/>
<path refid="tomcat.webservices.classpath"/>
@@ -2007,7 +1809,8 @@ Apache Tomcat ${version} native binaries for Win64 AMD64/EMT64 platform.
charset="UTF-8"
additionalparam="-breakiterator -notimestamp"
maxmemory="512m"
- failonerror="true">
+ failonerror="true"
+ failonwarning="true">
<classpath>
<path refid="compile.classpath"/>
<path refid="tomcat.webservices.classpath"/>
@@ -2026,7 +1829,8 @@ Apache Tomcat ${version} native binaries for Win64 AMD64/EMT64 platform.
charset="UTF-8"
additionalparam="-breakiterator -notimestamp"
maxmemory="512m"
- failonerror="true">
+ failonerror="true"
+ failonwarning="true">
<classpath>
<path refid="compile.classpath"/>
<path refid="tomcat.webservices.classpath"/>
@@ -2039,11 +1843,12 @@ Apache Tomcat ${version} native binaries for Win64 AMD64/EMT64 platform.
<link href="http://docs.oracle.com/javase/7/docs/api/"/>
<link href="http://commons.apache.org/proper/commons-io/javadocs/api-release/"/>
<link href="http://docs.oracle.com/javaee/7/api/"/>
- <sourcepath>
- <path location="${tomcat.dist}/src/java"/>
- <!--jdbc-pool src files for javadoc-->
- <path location="${tomcat.dist}/src/modules/jdbc-pool/src/main/java"/>
- </sourcepath>
+ <packageset dir="${tomcat.dist}/src/java/">
+ <include name="org/**"/>
+ <exclude name="org/apache/el/parser/**"/>
+ </packageset>
+ <!--jdbc-pool src files for javadoc-->
+ <packageset dir="${tomcat.dist}/src/modules/jdbc-pool/src/main/java"/>
</javadoc>
</target>
@@ -2058,6 +1863,7 @@ Apache Tomcat ${version} native binaries for Win64 AMD64/EMT64 platform.
<include name="jsp-api.jar"/>
<include name="jasper.jar"/>
<include name="jasper-el.jar"/>
+ <include name="jaspic-api.jar"/>
<include name="servlet-api.jar"/>
<include name="websocket-api.jar"/>
<include name="tomcat-websocket.jar"/>
@@ -2254,7 +2060,7 @@ skip.installer property in build.properties" />
</signcode>
</target>
- <target name="-installer" description="Builds Windows installer"
+ <target name="-installer" description="Builds the installer executable"
unless="skip.installer" depends="-installer-sign-uninstaller">
<exec dir="${tomcat.dist}" executable="${nsis.exe}" osfamily="windows">
<arg value="/DNSISDIR=${nsis.home}" />
@@ -2271,7 +2077,8 @@ skip.installer property in build.properties" />
<hashAndSign file="${tomcat.release}/v${version}/bin/${final.name}.exe" />
</target>
- <target name="installer-sign" depends="-installer" if="${do.codesigning}" >
+ <target name="installer-sign" description="Builds and signs the Windows installer"
+ depends="-installer" if="${do.codesigning}" >
<taskdef name="signcode"
classname="org.apache.tomcat.buildutil.SignCode"
classpath="${tomcat.classes}" />
@@ -2466,26 +2273,10 @@ skip.installer property in build.properties" />
<tar longfile="gnu" compression="gzip"
tarfile="${tomcat.release}/v${version}/bin/${final.name}.tar.gz">
- <tarfileset dir="${tomcat.dist}" mode="755" prefix="${final.name}">
- <include name="bin/catalina.sh" />
- <include name="bin/configtest.sh" />
- <include name="bin/daemon.sh" />
- <include name="bin/digest.sh" />
- <include name="bin/jasper.sh" />
- <include name="bin/jspc.sh" />
- <include name="bin/setclasspath.sh" />
- <include name="bin/startup.sh" />
- <include name="bin/shutdown.sh" />
- <include name="bin/tool-wrapper.sh" />
- <include name="bin/tool-wrapper-using-launcher.sh" />
- <include name="bin/shutdown-using-launcher.sh" />
- <include name="bin/startup-using-launcher.sh" />
- <include name="bin/version.sh" />
- </tarfileset>
- <tarfileset dir="${tomcat.dist}" mode="600" prefix="${final.name}">
+ <tarfileset dir="${tomcat.dist}" dirmode="700" filemode="600" prefix="${final.name}">
<include name="conf/**" />
</tarfileset>
- <tarfileset dir="${tomcat.dist}" prefix="${final.name}">
+ <tarfileset dir="${tomcat.dist}" dirmode="750" filemode="640" prefix="${final.name}">
<include name="bin/**" />
<include name="lib/**" />
<include name="logs/**" />
@@ -2519,6 +2310,24 @@ skip.installer property in build.properties" />
<exclude name="bin/*.exe"/>
<exclude name="bin/*.dll"/>
</tarfileset>
+ <!-- These need to be added after the bin directory is added else the -->
+ <!-- bin diretcory will pick up the wrong permissions. -->
+ <tarfileset dir="${tomcat.dist}" dirmode="750" filemode="750" prefix="${final.name}">
+ <include name="bin/catalina.sh" />
+ <include name="bin/configtest.sh" />
+ <include name="bin/daemon.sh" />
+ <include name="bin/digest.sh" />
+ <include name="bin/jasper.sh" />
+ <include name="bin/jspc.sh" />
+ <include name="bin/setclasspath.sh" />
+ <include name="bin/startup.sh" />
+ <include name="bin/shutdown.sh" />
+ <include name="bin/tool-wrapper.sh" />
+ <include name="bin/tool-wrapper-using-launcher.sh" />
+ <include name="bin/shutdown-using-launcher.sh" />
+ <include name="bin/startup-using-launcher.sh" />
+ <include name="bin/version.sh" />
+ </tarfileset>
</tar>
<hashAndSign file="${tomcat.release}/v${version}/bin/${final.name}.tar.gz" />
@@ -2650,6 +2459,14 @@ skip.installer property in build.properties" />
filesDir="java"
filesId="files.tomcat-websocket" />
+ <!-- JASPIC 1.1 API JAR File -->
+ <jarIt jarfile="${jaspic-api-src.jar}"
+ filesDir="java"
+ filesId="files.jaspic-api"
+ manifest="${tomcat.manifests}/jaspic-api.jar.manifest"
+ notice="${tomcat.manifests}/jaspic-api.jar.notice"
+ license="${tomcat.manifests}/jaspic-api.jar.license" />
+
<!-- Bootstrap JAR File -->
<jarIt jarfile="${bootstrap-src.jar}"
filesDir="java"
diff --git a/conf/catalina.policy b/conf/catalina.policy
index 10a33ca..a5b7c14 100644
--- a/conf/catalina.policy
+++ b/conf/catalina.policy
@@ -186,12 +186,13 @@ grant {
permission java.util.PropertyPermission
"org.apache.tomcat.util.http.ServerCookie.FWD_SLASH_IS_SEPARATOR", "read";
- // Applications using Comet need to be able to access this package
- permission java.lang.RuntimePermission "accessClassInPackage.org.apache.catalina.comet";
-
// Applications using WebSocket need to be able to access these packages
permission java.lang.RuntimePermission "accessClassInPackage.org.apache.tomcat.websocket";
permission java.lang.RuntimePermission "accessClassInPackage.org.apache.tomcat.websocket.server";
+
+ // Applications need to access these packages to use the Servlet 4.0 Preview
+ permission java.lang.RuntimePermission "accessClassInPackage.org.apache.catalina.servlet4preview";
+ permission java.lang.RuntimePermission "accessClassInPackage.org.apache.catalina.servlet4preview.http";
};
diff --git a/conf/catalina.properties b/conf/catalina.properties
index 8d06b3a..f1913e7 100644
--- a/conf/catalina.properties
+++ b/conf/catalina.properties
@@ -108,6 +108,7 @@ shared.loader=
tomcat.util.scan.StandardJarScanFilter.jarsToSkip=\
bootstrap.jar,commons-daemon.jar,tomcat-juli.jar,\
annotations-api.jar,el-api.jar,jsp-api.jar,servlet-api.jar,websocket-api.jar,\
+jaspic-api.jar,\
catalina.jar,catalina-ant.jar,catalina-ha.jar,catalina-storeconfig.jar,\
catalina-tribes.jar,\
jasper.jar,jasper-el.jar,ecj-*.jar,\
@@ -126,7 +127,8 @@ geronimo-spec-jaxrpc*.jar,wsdl4j*.jar,\
ant.jar,ant-junit*.jar,aspectj*.jar,jmx.jar,h2*.jar,hibernate*.jar,httpclient*.jar,\
jmx-tools.jar,jta*.jar,log4j*.jar,mail*.jar,slf4j*.jar,\
xercesImpl.jar,xmlParserAPIs.jar,xml-apis.jar,\
-junit.jar,junit-*.jar,ant-launcher.jar,\
+junit.jar,junit-*.jar,hamcrest-*.jar,easymock-*.jar,cglib-*.jar,\
+objenesis-*.jar,ant-launcher.jar,\
cobertura-*.jar,asm-*.jar,dom4j-*.jar,icu4j-*.jar,jaxen-*.jar,jdom-*.jar,\
jetty-*.jar,oro-*.jar,servlet-api-*.jar,tagsoup-*.jar,xmlParserAPIs-*.jar,\
xom-*.jar
diff --git a/conf/context.xml b/conf/context.xml
index 98727cb..64de61f 100644
--- a/conf/context.xml
+++ b/conf/context.xml
@@ -1,4 +1,4 @@
-<?xml version='1.0' encoding='utf-8'?>
+<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
@@ -27,10 +27,4 @@
<!--
<Manager pathname="" />
-->
-
- <!-- Uncomment this to enable Comet connection tacking (provides events
- on session expiration as well as webapp lifecycle) -->
- <!--
- <Valve className="org.apache.catalina.valves.CometConnectionManagerValve" />
- -->
</Context>
diff --git a/conf/jaspic-providers.xml b/conf/jaspic-providers.xml
new file mode 100644
index 0000000..cdebf87
--- /dev/null
+++ b/conf/jaspic-providers.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+-->
+<jaspic-providers xmlns="http://tomcat.apache.org/xml"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://tomcat.apache.org/xml jaspic-providers.xsd"
+ version="1.0">
+ <!-- No JASPIC providers configured by default -->
+</jaspic-providers>
diff --git a/conf/jaspic-providers.xsd b/conf/jaspic-providers.xsd
new file mode 100644
index 0000000..73a87aa
--- /dev/null
+++ b/conf/jaspic-providers.xsd
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+-->
+<xs:schema xmlns="http://www.w3.org/2001/XMLSchema"
+ targetNamespace="http://tomcat.apache.org/xml"
+ xmlns:jaspic="http://tomcat.apache.org/xml"
+ xmlns:xs="http://www.w3.org/2001/XMLSchema"
+ elementFormDefault="qualified"
+ attributeFormDefault="unqualified"
+ version="1.0">
+ <xs:element name="jaspic-providers">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="provider" minOccurs="0" maxOccurs="unbounded">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="property" minOccurs="0" maxOccurs="unbounded">
+ <xs:complexType>
+ <xs:attribute name="name" use="required" type="jaspic:propertyname" />
+ <xs:attribute name="value" use="required" type="xs:string" />
+ </xs:complexType>
+ </xs:element>
+ </xs:sequence>
+ <xs:attribute name="className" use="required" type="xs:string" />
+ <xs:attribute name="layer" use="required" type="xs:string" />
+ <xs:attribute name="appContext" use="required" type="xs:string" />
+ <xs:attribute name="description" type="xs:string" />
+ </xs:complexType>
+ </xs:element>
+ </xs:sequence>
+ <xs:attribute name="version" type="xs:string" />
+ </xs:complexType>
+ </xs:element>
+ <xs:simpleType name="propertyname">
+ <xs:restriction base="xs:string">
+ <xs:minLength value="1"/>
+ </xs:restriction>
+ </xs:simpleType>
+</xs:schema>
\ No newline at end of file
diff --git a/conf/logging.properties b/conf/logging.properties
index cb5ed66..a2ad9c2 100644
--- a/conf/logging.properties
+++ b/conf/logging.properties
@@ -62,3 +62,9 @@ org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/host-manager].ha
# To see debug messages in TldLocationsCache, uncomment the following line:
#org.apache.jasper.compiler.TldLocationsCache.level = FINE
+
+# To see debug messages for HTTP/2 handling, uncomment the following line:
+#org.apache.coyote.http2.level = FINE
+
+# To see debug messages for WebSocket handling, uncomment the following line:
+#org.apache.tomcat.websocket.level = FINE
diff --git a/conf/server.xml b/conf/server.xml
index eb7dad7..3cf407e 100644
--- a/conf/server.xml
+++ b/conf/server.xml
@@ -1,4 +1,4 @@
-<?xml version='1.0' encoding='utf-8'?>
+<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
@@ -61,7 +61,7 @@
<!-- A "Connector" represents an endpoint by which requests are received
and responses are returned. Documentation at :
- Java HTTP Connector: /docs/config/http.html (blocking & non-blocking)
+ Java HTTP Connector: /docs/config/http.html
Java AJP Connector: /docs/config/ajp.html
APR (HTTP/AJP) Connector: /docs/apr.html
Define a non-SSL/TLS HTTP/1.1 Connector on port 8080
@@ -77,14 +77,34 @@
redirectPort="8443" />
-->
<!-- Define a SSL/TLS HTTP/1.1 Connector on port 8443
- This connector uses the NIO implementation that requires the JSSE
- style configuration. When using the APR/native implementation, the
- OpenSSL style configuration is required as described in the APR/native
- documentation -->
+ This connector uses the NIO implementation with the JSSE engine. When
+ using the JSSE engine, the JSSE configuration attributes must be used.
+ -->
<!--
<Connector port="8443" protocol="org.apache.coyote.http11.Http11NioProtocol"
- maxThreads="150" SSLEnabled="true" scheme="https" secure="true"
- clientAuth="false" sslProtocol="TLS" />
+ maxThreads="150" SSLEnabled="true">
+ <SSLHostConfig>
+ <Certificate certificateKeystoreFile="conf/localhost-rsa.jks"
+ type="RSA" />
+ </SSLHostConfig>
+ </Connector>
+ -->
+ <!-- Define a SSL/TLS HTTP/1.1 Connector on port 8443 with HTTP/2
+ This connector uses the APR/native implementation. When using the
+ APR/native implementation or the OpenSSL engine with NIO or NIO2 then
+ the OpenSSL configuration attributes must be used.
+ -->
+ <!--
+ <Connector port="8443" protocol="org.apache.coyote.http11.Http11AprProtocol"
+ maxThreads="150" SSLEnabled="true" >
+ <UpgradeProtocol className="org.apache.coyote.http2.Http2Protocol" />
+ <SSLHostConfig>
+ <Certificate certificateKeyFile="conf/localhost-rsa-key.pem"
+ certificateFile="conf/localhost-rsa-cert.pem"
+ certificateChainFile="conf/localhost-rsa-chain.pem"
+ type="RSA" />
+ </SSLHostConfig>
+ </Connector>
-->
<!-- Define an AJP 1.3 Connector on port 8009 -->
diff --git a/conf/tomcat-users.xml b/conf/tomcat-users.xml
index fcac27d..aef66d0 100644
--- a/conf/tomcat-users.xml
+++ b/conf/tomcat-users.xml
@@ -1,4 +1,4 @@
-<?xml version='1.0' encoding='utf-8'?>
+<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
diff --git a/conf/tomcat-users.xsd b/conf/tomcat-users.xsd
index 67a1d5f..948bd01 100644
--- a/conf/tomcat-users.xsd
+++ b/conf/tomcat-users.xsd
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="UTF-8" ?>
+<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
diff --git a/conf/web.xml b/conf/web.xml
index cf08cfe..acaeaa5 100644
--- a/conf/web.xml
+++ b/conf/web.xml
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="ISO-8859-1"?>
+<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
@@ -242,9 +242,6 @@
<!-- attribute values, should the rules in JSP.1.6 -->
<!-- for the escaping of quote characters be -->
<!-- strictly applied? [true] -->
- <!-- The default can be changed with the -->
- <!-- org.apache.jasper.compiler.Parser. -->
- <!-- STRICT_QUOTE_ESCAPING system property. -->
<!-- -->
<!-- quoteAttributeEL When EL is used in an attribute value on a -->
<!-- JSP page should the rules for quoting of -->
diff --git a/java/javax/el/ArrayELResolver.java b/java/javax/el/ArrayELResolver.java
index 1fb4dc8..5bbb2a1 100644
--- a/java/javax/el/ArrayELResolver.java
+++ b/java/javax/el/ArrayELResolver.java
@@ -20,6 +20,7 @@ package javax.el;
import java.beans.FeatureDescriptor;
import java.lang.reflect.Array;
import java.util.Iterator;
+import java.util.Objects;
public class ArrayELResolver extends ELResolver {
@@ -35,9 +36,7 @@ public class ArrayELResolver extends ELResolver {
@Override
public Class<?> getType(ELContext context, Object base, Object property) {
- if (context == null) {
- throw new NullPointerException();
- }
+ Objects.requireNonNull(context);
if (base != null && base.getClass().isArray()) {
context.setPropertyResolved(base, property);
@@ -55,9 +54,7 @@ public class ArrayELResolver extends ELResolver {
@Override
public Object getValue(ELContext context, Object base, Object property) {
- if (context == null) {
- throw new NullPointerException();
- }
+ Objects.requireNonNull(context);
if (base != null && base.getClass().isArray()) {
context.setPropertyResolved(base, property);
@@ -74,9 +71,7 @@ public class ArrayELResolver extends ELResolver {
@Override
public void setValue(ELContext context, Object base, Object property,
Object value) {
- if (context == null) {
- throw new NullPointerException();
- }
+ Objects.requireNonNull(context);
if (base != null && base.getClass().isArray()) {
context.setPropertyResolved(base, property);
@@ -100,9 +95,7 @@ public class ArrayELResolver extends ELResolver {
@Override
public boolean isReadOnly(ELContext context, Object base, Object property) {
- if (context == null) {
- throw new NullPointerException();
- }
+ Objects.requireNonNull(context);
if (base != null && base.getClass().isArray()) {
context.setPropertyResolved(base, property);
diff --git a/java/javax/el/BeanELResolver.java b/java/javax/el/BeanELResolver.java
index 39eb6ce..27f3d58 100644
--- a/java/javax/el/BeanELResolver.java
+++ b/java/javax/el/BeanELResolver.java
@@ -30,6 +30,7 @@ import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
+import java.util.Objects;
import java.util.WeakHashMap;
import java.util.concurrent.ConcurrentHashMap;
@@ -71,9 +72,7 @@ public class BeanELResolver extends ELResolver {
@Override
public Class<?> getType(ELContext context, Object base, Object property) {
- if (context == null) {
- throw new NullPointerException();
- }
+ Objects.requireNonNull(context);
if (base == null || property == null) {
return null;
}
@@ -84,9 +83,7 @@ public class BeanELResolver extends ELResolver {
@Override
public Object getValue(ELContext context, Object base, Object property) {
- if (context == null) {
- throw new NullPointerException();
- }
+ Objects.requireNonNull(context);
if (base == null || property == null) {
return null;
}
@@ -108,9 +105,7 @@ public class BeanELResolver extends ELResolver {
@Override
public void setValue(ELContext context, Object base, Object property,
Object value) {
- if (context == null) {
- throw new NullPointerException();
- }
+ Objects.requireNonNull(context);
if (base == null || property == null) {
return;
}
@@ -141,9 +136,7 @@ public class BeanELResolver extends ELResolver {
@Override
public Object invoke(ELContext context, Object base, Object method,
Class<?>[] paramTypes, Object[] params) {
- if (context == null) {
- throw new NullPointerException();
- }
+ Objects.requireNonNull(context);
if (base == null || method == null) {
return null;
}
@@ -177,9 +170,7 @@ public class BeanELResolver extends ELResolver {
@Override
public boolean isReadOnly(ELContext context, Object base, Object property) {
- if (context == null) {
- throw new NullPointerException();
- }
+ Objects.requireNonNull(context);
if (base == null || property == null) {
return false;
}
diff --git a/java/javax/el/BeanNameELResolver.java b/java/javax/el/BeanNameELResolver.java
index a847f32..0e7b48d 100644
--- a/java/javax/el/BeanNameELResolver.java
+++ b/java/javax/el/BeanNameELResolver.java
@@ -18,6 +18,7 @@ package javax.el;
import java.beans.FeatureDescriptor;
import java.util.Iterator;
+import java.util.Objects;
/**
* @since EL 3.0
@@ -32,10 +33,7 @@ public class BeanNameELResolver extends ELResolver {
@Override
public Object getValue(ELContext context, Object base, Object property) {
-
- if (context == null) {
- throw new NullPointerException();
- }
+ Objects.requireNonNull(context);
if (base != null || !(property instanceof String)) {
return null;
}
@@ -59,10 +57,7 @@ public class BeanNameELResolver extends ELResolver {
@Override
public void setValue(ELContext context, Object base, Object property,
Object value) {
-
- if (context == null) {
- throw new NullPointerException();
- }
+ Objects.requireNonNull(context);
if (base != null || !(property instanceof String)) {
return;
}
@@ -100,10 +95,7 @@ public class BeanNameELResolver extends ELResolver {
@Override
public Class<?> getType(ELContext context, Object base, Object property) {
-
- if (context == null) {
- throw new NullPointerException();
- }
+ Objects.requireNonNull(context);
if (base != null || !(property instanceof String)) {
return null;
}
@@ -126,10 +118,7 @@ public class BeanNameELResolver extends ELResolver {
@Override
public boolean isReadOnly(ELContext context, Object base, Object property) {
-
- if (context == null) {
- throw new NullPointerException();
- }
+ Objects.requireNonNull(context);
if (base != null || !(property instanceof String)) {
// Return value undefined
return false;
diff --git a/java/javax/el/CompositeELResolver.java b/java/javax/el/CompositeELResolver.java
index 5868a12..f28f528 100644
--- a/java/javax/el/CompositeELResolver.java
+++ b/java/javax/el/CompositeELResolver.java
@@ -19,6 +19,7 @@ package javax.el;
import java.beans.FeatureDescriptor;
import java.util.Iterator;
import java.util.NoSuchElementException;
+import java.util.Objects;
public class CompositeELResolver extends ELResolver {
@@ -43,9 +44,7 @@ public class CompositeELResolver extends ELResolver {
}
public void add(ELResolver elResolver) {
- if (elResolver == null) {
- throw new NullPointerException();
- }
+ Objects.requireNonNull(elResolver);
if (this.size >= this.resolvers.length) {
ELResolver[] nr = new ELResolver[this.size * 2];
@@ -201,7 +200,7 @@ public class CompositeELResolver extends ELResolver {
public boolean hasNext() {
if (this.next != null)
return true;
- if (this.itr != null){
+ if (this.itr != null) {
while (this.next == null && itr.hasNext()) {
this.next = itr.next();
}
diff --git a/java/javax/el/ELContext.java b/java/javax/el/ELContext.java
index d260fd7..f294192 100644
--- a/java/javax/el/ELContext.java
+++ b/java/javax/el/ELContext.java
@@ -23,6 +23,7 @@ import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
+import java.util.Objects;
public abstract class ELContext {
@@ -75,9 +76,8 @@ public abstract class ELContext {
*/
public void putContext(@SuppressWarnings("rawtypes") Class key,
Object contextObject) {
- if (key == null || contextObject == null) {
- throw new NullPointerException();
- }
+ Objects.requireNonNull(key);
+ Objects.requireNonNull(contextObject);
if (this.map == null) {
this.map = new HashMap<>();
@@ -98,9 +98,7 @@ public abstract class ELContext {
* If the supplied key is <code>null</code>
*/
public Object getContext(@SuppressWarnings("rawtypes") Class key) {
- if (key == null) {
- throw new NullPointerException();
- }
+ Objects.requireNonNull(key);
if (this.map == null) {
return null;
}
diff --git a/java/javax/el/LambdaExpression.java b/java/javax/el/LambdaExpression.java
index c303ac2..7e9ca06 100644
--- a/java/javax/el/LambdaExpression.java
+++ b/java/javax/el/LambdaExpression.java
@@ -19,6 +19,7 @@ package javax.el;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
public class LambdaExpression {
@@ -42,9 +43,7 @@ public class LambdaExpression {
public Object invoke(ELContext context, Object... args)
throws ELException {
- if (context == null) {
- throw new NullPointerException();
- }
+ Objects.requireNonNull(context);
int formalParamCount = 0;
if (formalParameters != null) {
diff --git a/java/javax/el/ListELResolver.java b/java/javax/el/ListELResolver.java
index 031b02c..749fdae 100644
--- a/java/javax/el/ListELResolver.java
+++ b/java/javax/el/ListELResolver.java
@@ -22,6 +22,7 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
+import java.util.Objects;
public class ListELResolver extends ELResolver {
@@ -40,9 +41,7 @@ public class ListELResolver extends ELResolver {
@Override
public Class<?> getType(ELContext context, Object base, Object property) {
- if (context == null) {
- throw new NullPointerException();
- }
+ Objects.requireNonNull(context);
if (base instanceof List<?>) {
context.setPropertyResolved(base, property);
@@ -60,9 +59,7 @@ public class ListELResolver extends ELResolver {
@Override
public Object getValue(ELContext context, Object base, Object property) {
- if (context == null) {
- throw new NullPointerException();
- }
+ Objects.requireNonNull(context);
if (base instanceof List<?>) {
context.setPropertyResolved(base, property);
@@ -80,9 +77,7 @@ public class ListELResolver extends ELResolver {
@Override
public void setValue(ELContext context, Object base, Object property,
Object value) {
- if (context == null) {
- throw new NullPointerException();
- }
+ Objects.requireNonNull(context);
if (base instanceof List<?>) {
context.setPropertyResolved(base, property);
@@ -107,9 +102,7 @@ public class ListELResolver extends ELResolver {
@Override
public boolean isReadOnly(ELContext context, Object base, Object property) {
- if (context == null) {
- throw new NullPointerException();
- }
+ Objects.requireNonNull(context);
if (base instanceof List<?>) {
context.setPropertyResolved(base, property);
diff --git a/java/javax/el/MapELResolver.java b/java/javax/el/MapELResolver.java
index 26e6cce..abc0e06 100644
--- a/java/javax/el/MapELResolver.java
+++ b/java/javax/el/MapELResolver.java
@@ -24,6 +24,7 @@ import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
public class MapELResolver extends ELResolver {
@@ -42,9 +43,7 @@ public class MapELResolver extends ELResolver {
@Override
public Class<?> getType(ELContext context, Object base, Object property) {
- if (context == null) {
- throw new NullPointerException();
- }
+ Objects.requireNonNull(context);
if (base instanceof Map<?,?>) {
context.setPropertyResolved(base, property);
@@ -56,9 +55,7 @@ public class MapELResolver extends ELResolver {
@Override
public Object getValue(ELContext context, Object base, Object property) {
- if (context == null) {
- throw new NullPointerException();
- }
+ Objects.requireNonNull(context);
if (base instanceof Map<?,?>) {
context.setPropertyResolved(base, property);
@@ -71,9 +68,7 @@ public class MapELResolver extends ELResolver {
@Override
public void setValue(ELContext context, Object base, Object property,
Object value) {
- if (context == null) {
- throw new NullPointerException();
- }
+ Objects.requireNonNull(context);
if (base instanceof Map<?, ?>) {
context.setPropertyResolved(base, property);
@@ -95,9 +90,7 @@ public class MapELResolver extends ELResolver {
@Override
public boolean isReadOnly(ELContext context, Object base, Object property) {
- if (context == null) {
- throw new NullPointerException();
- }
+ Objects.requireNonNull(context);
if (base instanceof Map<?, ?>) {
context.setPropertyResolved(base, property);
diff --git a/java/javax/el/ResourceBundleELResolver.java b/java/javax/el/ResourceBundleELResolver.java
index a123f28..f9b9723 100644
--- a/java/javax/el/ResourceBundleELResolver.java
+++ b/java/javax/el/ResourceBundleELResolver.java
@@ -23,6 +23,7 @@ import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.MissingResourceException;
+import java.util.Objects;
import java.util.ResourceBundle;
public class ResourceBundleELResolver extends ELResolver {
@@ -33,10 +34,7 @@ public class ResourceBundleELResolver extends ELResolver {
@Override
public Object getValue(ELContext context, Object base, Object property) {
-
- if (context == null) {
- throw new NullPointerException();
- }
+ Objects.requireNonNull(context);
if (base instanceof ResourceBundle) {
context.setPropertyResolved(base, property);
@@ -56,9 +54,7 @@ public class ResourceBundleELResolver extends ELResolver {
@Override
public Class<?> getType(ELContext context, Object base, Object property) {
- if (context == null) {
- throw new NullPointerException();
- }
+ Objects.requireNonNull(context);
if (base instanceof ResourceBundle) {
context.setPropertyResolved(base, property);
@@ -70,9 +66,7 @@ public class ResourceBundleELResolver extends ELResolver {
@Override
public void setValue(ELContext context, Object base, Object property,
Object value) {
- if (context == null) {
- throw new NullPointerException();
- }
+ Objects.requireNonNull(context);
if (base instanceof ResourceBundle) {
context.setPropertyResolved(base, property);
@@ -83,9 +77,7 @@ public class ResourceBundleELResolver extends ELResolver {
@Override
public boolean isReadOnly(ELContext context, Object base, Object property) {
- if (context == null) {
- throw new NullPointerException();
- }
+ Objects.requireNonNull(context);
if (base instanceof ResourceBundle) {
context.setPropertyResolved(base, property);
diff --git a/java/javax/el/StaticFieldELResolver.java b/java/javax/el/StaticFieldELResolver.java
index bf761b1..19911dc 100644
--- a/java/javax/el/StaticFieldELResolver.java
+++ b/java/javax/el/StaticFieldELResolver.java
@@ -23,6 +23,7 @@ import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Iterator;
+import java.util.Objects;
/**
* @since EL 3.0
@@ -31,10 +32,7 @@ public class StaticFieldELResolver extends ELResolver {
@Override
public Object getValue(ELContext context, Object base, Object property) {
-
- if (context == null) {
- throw new NullPointerException();
- }
+ Objects.requireNonNull(context);
if (base instanceof ELClass && property instanceof String) {
context.setPropertyResolved(base, property);
@@ -68,10 +66,7 @@ public class StaticFieldELResolver extends ELResolver {
@Override
public void setValue(ELContext context, Object base, Object property,
Object value) {
-
- if (context == null) {
- throw new NullPointerException();
- }
+ Objects.requireNonNull(context);
if (base instanceof ELClass && property instanceof String) {
Class<?> clazz = ((ELClass) base).getKlass();
@@ -87,10 +82,7 @@ public class StaticFieldELResolver extends ELResolver {
@Override
public Object invoke(ELContext context, Object base, Object method,
Class<?>[] paramTypes, Object[] params) {
-
- if (context == null) {
- throw new NullPointerException();
- }
+ Objects.requireNonNull(context);
if (base instanceof ELClass && method instanceof String) {
context.setPropertyResolved(base, method);
@@ -151,9 +143,7 @@ public class StaticFieldELResolver extends ELResolver {
@Override
public Class<?> getType(ELContext context, Object base, Object property) {
- if (context == null) {
- throw new NullPointerException();
- }
+ Objects.requireNonNull(context);
if (base instanceof ELClass && property instanceof String) {
context.setPropertyResolved(base, property);
@@ -186,9 +176,7 @@ public class StaticFieldELResolver extends ELResolver {
@Override
public boolean isReadOnly(ELContext context, Object base, Object property) {
- if (context == null) {
- throw new NullPointerException();
- }
+ Objects.requireNonNull(context);
if (base instanceof ELClass && property instanceof String) {
context.setPropertyResolved(base, property);
diff --git a/java/javax/security/auth/message/AuthException.java b/java/javax/security/auth/message/AuthException.java
new file mode 100644
index 0000000..a043772
--- /dev/null
+++ b/java/javax/security/auth/message/AuthException.java
@@ -0,0 +1,30 @@
+/**
+ * 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 javax.security.auth.message;
+
+import javax.security.auth.login.LoginException;
+
+public class AuthException extends LoginException {
+ private static final long serialVersionUID = -1156951780670243758L;
+
+ public AuthException() {
+ }
+
+ public AuthException(String msg) {
+ super(msg);
+ }
+}
diff --git a/java/javax/security/auth/message/AuthStatus.java b/java/javax/security/auth/message/AuthStatus.java
new file mode 100644
index 0000000..b9ca385
--- /dev/null
+++ b/java/javax/security/auth/message/AuthStatus.java
@@ -0,0 +1,37 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package javax.security.auth.message;
+
+public class AuthStatus {
+
+ public static final AuthStatus SUCCESS = new AuthStatus("SUCCESS");
+ public static final AuthStatus FAILURE = new AuthStatus("FAILURE");
+ public static final AuthStatus SEND_SUCCESS = new AuthStatus("SEND_SUCCESS");
+ public static final AuthStatus SEND_FAILURE = new AuthStatus("SEND_FAILURE");
+ public static final AuthStatus SEND_CONTINUE = new AuthStatus("SEND_CONTINUE");
+
+ private final String name;
+
+ private AuthStatus(String name) {
+ this.name = name;
+ }
+
+ @Override
+ public String toString() {
+ return name;
+ }
+}
diff --git a/java/javax/security/auth/message/ClientAuth.java b/java/javax/security/auth/message/ClientAuth.java
new file mode 100644
index 0000000..a537ef2
--- /dev/null
+++ b/java/javax/security/auth/message/ClientAuth.java
@@ -0,0 +1,30 @@
+/**
+ * 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 javax.security.auth.message;
+
+import javax.security.auth.Subject;
+
+public interface ClientAuth {
+
+ AuthStatus secureRequest(MessageInfo messageInfo, Subject clientSubject) throws AuthException;
+
+ AuthStatus validateResponse(MessageInfo messageInfo, Subject clientSubject,
+ Subject serviceSubject) throws AuthException;
+
+ void cleanSubject(MessageInfo messageInfo, Subject subject) throws AuthException;
+}
+
diff --git a/java/javax/security/auth/message/MessageInfo.java b/java/javax/security/auth/message/MessageInfo.java
new file mode 100644
index 0000000..dc3d263
--- /dev/null
+++ b/java/javax/security/auth/message/MessageInfo.java
@@ -0,0 +1,33 @@
+/**
+ * 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 javax.security.auth.message;
+
+import java.util.Map;
+
+public interface MessageInfo {
+
+ Object getRequestMessage();
+
+ Object getResponseMessage();
+
+ void setRequestMessage(Object request);
+
+ void setResponseMessage(Object response);
+
+ @SuppressWarnings("rawtypes") // JASPIC API uses raw types
+ Map getMap();
+}
diff --git a/java/javax/security/auth/message/MessagePolicy.java b/java/javax/security/auth/message/MessagePolicy.java
new file mode 100644
index 0000000..39673b2
--- /dev/null
+++ b/java/javax/security/auth/message/MessagePolicy.java
@@ -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 javax.security.auth.message;
+
+public class MessagePolicy {
+
+ private final TargetPolicy[] targetPolicies;
+ private final boolean mandatory;
+
+ public MessagePolicy(TargetPolicy[] targetPolicies, boolean mandatory) {
+ if (targetPolicies == null) {
+ throw new IllegalArgumentException("targetPolicies is null");
+ }
+ this.targetPolicies = targetPolicies;
+ this.mandatory = mandatory;
+ }
+
+ public boolean isMandatory() {
+ return mandatory;
+ }
+
+ public TargetPolicy[] getTargetPolicies() {
+ if (targetPolicies.length == 0) {
+ return null;
+ }
+ return targetPolicies;
+ }
+
+ public static interface ProtectionPolicy {
+
+ static String AUTHENTICATE_SENDER = "#authenticateSender";
+ static String AUTHENTICATE_CONTENT = "#authenticateContent";
+ static String AUTHENTICATE_RECIPIENT = "#authenticateRecipient";
+
+ String getID();
+ }
+
+ public static interface Target {
+
+ Object get(MessageInfo messageInfo);
+
+ void remove(MessageInfo messageInfo);
+
+ void put(MessageInfo messageInfo, Object data);
+ }
+
+ public static class TargetPolicy {
+
+ private final Target[] targets;
+ private final ProtectionPolicy protectionPolicy;
+
+ public TargetPolicy(Target[] targets, ProtectionPolicy protectionPolicy) {
+ if (protectionPolicy == null) {
+ throw new IllegalArgumentException("protectionPolicy is null");
+ }
+ this.targets = targets;
+ this.protectionPolicy = protectionPolicy;
+ }
+
+ public Target[] getTargets() {
+ if (targets == null || targets.length == 0) {
+ return null;
+ }
+ return targets;
+ }
+
+ public ProtectionPolicy getProtectionPolicy() {
+ return protectionPolicy;
+ }
+ }
+}
diff --git a/java/javax/security/auth/message/ServerAuth.java b/java/javax/security/auth/message/ServerAuth.java
new file mode 100644
index 0000000..bb404f5
--- /dev/null
+++ b/java/javax/security/auth/message/ServerAuth.java
@@ -0,0 +1,29 @@
+/**
+ * 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 javax.security.auth.message;
+
+import javax.security.auth.Subject;
+
+public interface ServerAuth {
+
+ AuthStatus validateRequest(MessageInfo messageInfo, Subject clientSubject,
+ Subject serviceSubject) throws AuthException;
+
+ AuthStatus secureResponse(MessageInfo messageInfo, Subject serviceSubject) throws AuthException;
+
+ void cleanSubject(MessageInfo messageInfo, Subject subject) throws AuthException;
+}
diff --git a/java/javax/security/auth/message/callback/CallerPrincipalCallback.java b/java/javax/security/auth/message/callback/CallerPrincipalCallback.java
new file mode 100644
index 0000000..2c7010f
--- /dev/null
+++ b/java/javax/security/auth/message/callback/CallerPrincipalCallback.java
@@ -0,0 +1,57 @@
+/**
+ * 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 javax.security.auth.message.callback;
+
+import java.security.Principal;
+
+import javax.security.auth.Subject;
+import javax.security.auth.callback.Callback;
+
+/**
+ * Callback that enables an authentication module to inform the runtime of the
+ * call principal or name of the caller principal.
+ */
+public class CallerPrincipalCallback implements Callback {
+
+ private final Subject subject;
+ private final Principal principal;
+ private final String name;
+
+ public CallerPrincipalCallback(Subject subject, Principal principal) {
+ this.subject = subject;
+ this.principal = principal;
+ this.name = null;
+ }
+
+ public CallerPrincipalCallback(Subject subject, String name) {
+ this.subject = subject;
+ this.principal = null;
+ this.name = name;
+ }
+
+ public Subject getSubject() {
+ return subject;
+ }
+
+ public Principal getPrincipal() {
+ return principal;
+ }
+
+ public String getName() {
+ return name;
+ }
+}
diff --git a/java/javax/security/auth/message/callback/CertStoreCallback.java b/java/javax/security/auth/message/callback/CertStoreCallback.java
new file mode 100644
index 0000000..13734e8
--- /dev/null
+++ b/java/javax/security/auth/message/callback/CertStoreCallback.java
@@ -0,0 +1,41 @@
+/**
+ * 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 javax.security.auth.message.callback;
+
+import java.security.cert.CertStore;
+
+import javax.security.auth.callback.Callback;
+
+/**
+ * Callback that enables a runtime to inform authentication modules of the
+ * CertStore to use.
+ */
+public class CertStoreCallback implements Callback {
+
+ private CertStore certStore;
+
+ public CertStoreCallback() {
+ }
+
+ public void setCertStore(CertStore certStore) {
+ this.certStore = certStore;
+ }
+
+ public CertStore getCertStore() {
+ return certStore;
+ }
+}
diff --git a/java/javax/security/auth/message/callback/GroupPrincipalCallback.java b/java/javax/security/auth/message/callback/GroupPrincipalCallback.java
new file mode 100644
index 0000000..f44c046
--- /dev/null
+++ b/java/javax/security/auth/message/callback/GroupPrincipalCallback.java
@@ -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 javax.security.auth.message.callback;
+
+import javax.security.auth.Subject;
+import javax.security.auth.callback.Callback;
+
+/**
+ * Callback that enables an authentication module to inform the runtime of the
+ * groups a user is in.
+ */
+public class GroupPrincipalCallback implements Callback {
+
+ private final Subject subject;
+ private final String[] groups;
+
+ public GroupPrincipalCallback(Subject subject, String[] groups) {
+ this.subject = subject;
+ this.groups = groups;
+ }
+
+ public Subject getSubject() {
+ return subject;
+ }
+
+ public String[] getGroups() {
+ return groups;
+ }
+}
diff --git a/java/javax/security/auth/message/callback/PasswordValidationCallback.java b/java/javax/security/auth/message/callback/PasswordValidationCallback.java
new file mode 100644
index 0000000..2eaad2d
--- /dev/null
+++ b/java/javax/security/auth/message/callback/PasswordValidationCallback.java
@@ -0,0 +1,65 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package javax.security.auth.message.callback;
+
+import java.util.Arrays;
+
+import javax.security.auth.Subject;
+import javax.security.auth.callback.Callback;
+
+/**
+ * Callback that enables an authentication module to supply a user name and
+ * password (to a runtime?) and determine if the result of validation.
+ */
+public class PasswordValidationCallback implements Callback {
+
+ private final Subject subject;
+ private final String username;
+ private char[] password;
+ private boolean result;
+
+ public PasswordValidationCallback(Subject subject, String username, char[] password) {
+ this.subject = subject;
+ this.username = username;
+ this.password = password;
+ }
+
+ public Subject getSubject() {
+ return subject;
+ }
+
+ public String getUsername() {
+ return username;
+ }
+
+ public char[] getPassword() {
+ return password;
+ }
+
+ public void clearPassword() {
+ Arrays.fill(password, (char) 0);
+ password = new char[0];
+ }
+
+ public void setResult(boolean result) {
+ this.result = result;
+ }
+
+ public boolean getResult() {
+ return result;
+ }
+}
diff --git a/java/javax/security/auth/message/callback/PrivateKeyCallback.java b/java/javax/security/auth/message/callback/PrivateKeyCallback.java
new file mode 100644
index 0000000..76feefe
--- /dev/null
+++ b/java/javax/security/auth/message/callback/PrivateKeyCallback.java
@@ -0,0 +1,123 @@
+/**
+ * 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 javax.security.auth.message.callback;
+
+import java.math.BigInteger;
+import java.security.PrivateKey;
+import java.security.cert.Certificate;
+
+import javax.security.auth.callback.Callback;
+import javax.security.auth.x500.X500Principal;
+
+/**
+ * Callback that enables an authentication module to request a certificate chain
+ * and private key from the runtime. The information specifying the chain and
+ * key may be an alias, a digest, a subject key, or an issuer ID. Other request
+ * types may be supported.
+ */
+public class PrivateKeyCallback implements Callback {
+
+ private final Request request;
+ private Certificate[] chain;
+ private PrivateKey key;
+
+ public PrivateKeyCallback(Request request) {
+ this.request = request;
+ }
+
+ public Request getRequest() {
+ return request;
+ }
+
+ public void setKey(PrivateKey key, Certificate[] chain) {
+ this.key = key;
+ this.chain = chain;
+ }
+
+ public PrivateKey getKey() {
+ return key;
+ }
+
+ public Certificate[] getChain() {
+ return chain;
+ }
+
+ public static interface Request {
+ }
+
+ public static class AliasRequest implements Request {
+
+ private final String alias;
+
+ public AliasRequest(String alias) {
+ this.alias = alias;
+ }
+
+ public String getAlias() {
+ return alias;
+ }
+ }
+
+ public static class DigestRequest implements Request {
+ private final byte[] digest;
+ private final String algorithm;
+
+ public DigestRequest(byte[] digest, String algorithm) {
+ this.digest = digest;
+ this.algorithm = algorithm;
+ }
+
+ public byte[] getDigest() {
+ return digest;
+ }
+
+ public String getAlgorithm() {
+ return algorithm;
+ }
+ }
+
+ public static class SubjectKeyIDRequest implements Request {
+
+ private final byte[] subjectKeyID;
+
+ public SubjectKeyIDRequest(byte[] subjectKeyID) {
+ this.subjectKeyID = subjectKeyID;
+ }
+
+ public byte[] getSubjectKeyID() {
+ return subjectKeyID;
+ }
+ }
+
+ public static class IssuerSerialNumRequest implements Request {
+ private final X500Principal issuer;
+ private final BigInteger serialNum;
+
+ public IssuerSerialNumRequest(X500Principal issuer, BigInteger serialNum) {
+ this.issuer = issuer;
+ this.serialNum = serialNum;
+ }
+
+ public X500Principal getIssuer() {
+ return issuer;
+ }
+
+ public BigInteger getSerialNum() {
+ return serialNum;
+ }
+ }
+}
diff --git a/java/javax/security/auth/message/callback/SecretKeyCallback.java b/java/javax/security/auth/message/callback/SecretKeyCallback.java
new file mode 100644
index 0000000..ff248fc
--- /dev/null
+++ b/java/javax/security/auth/message/callback/SecretKeyCallback.java
@@ -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 javax.security.auth.message.callback;
+
+import javax.crypto.SecretKey;
+import javax.security.auth.callback.Callback;
+
+/**
+ * A callback enabling an authentication module to request a secret key from the
+ * runtime, by supplying an alias. Other request types may also be supported.
+ */
+public class SecretKeyCallback implements Callback {
+
+ private final Request request;
+ private SecretKey key;
+
+ public SecretKeyCallback(Request request) {
+ this.request = request;
+ }
+
+ public Request getRequest() {
+ return request;
+ }
+
+ public void setKey(SecretKey key) {
+ this.key = key;
+ }
+
+ public SecretKey getKey() {
+ return key;
+ }
+
+ public static interface Request {
+ }
+
+ public static class AliasRequest implements Request {
+
+ private final String alias;
+
+ public AliasRequest(String alias) {
+ this.alias = alias;
+ }
+
+ public String getAlias() {
+ return alias;
+ }
+ }
+}
diff --git a/java/javax/security/auth/message/callback/TrustStoreCallback.java b/java/javax/security/auth/message/callback/TrustStoreCallback.java
new file mode 100644
index 0000000..8312e44
--- /dev/null
+++ b/java/javax/security/auth/message/callback/TrustStoreCallback.java
@@ -0,0 +1,38 @@
+/**
+ * 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 javax.security.auth.message.callback;
+
+import java.security.KeyStore;
+
+import javax.security.auth.callback.Callback;
+
+/**
+ * A Callback enabling an authentication module to request a truststore from the
+ * runtime.
+ */
+public class TrustStoreCallback implements Callback {
+
+ private KeyStore trustStore;
+
+ public void setTrustStore(KeyStore trustStore) {
+ this.trustStore = trustStore;
+ }
+
+ public KeyStore getTrustStore() {
+ return trustStore;
+ }
+}
diff --git a/java/javax/security/auth/message/config/AuthConfig.java b/java/javax/security/auth/message/config/AuthConfig.java
new file mode 100644
index 0000000..701a3eb
--- /dev/null
+++ b/java/javax/security/auth/message/config/AuthConfig.java
@@ -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 javax.security.auth.message.config;
+
+import javax.security.auth.message.MessageInfo;
+
+public interface AuthConfig {
+
+ String getMessageLayer();
+
+ String getAppContext();
+
+ String getAuthContextID(MessageInfo messageInfo);
+
+ void refresh();
+
+ boolean isProtected();
+}
diff --git a/java/javax/security/auth/message/config/AuthConfigFactory.java b/java/javax/security/auth/message/config/AuthConfigFactory.java
new file mode 100644
index 0000000..021aec1
--- /dev/null
+++ b/java/javax/security/auth/message/config/AuthConfigFactory.java
@@ -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 javax.security.auth.message.config;
+
+import java.security.AccessController;
+import java.security.Permission;
+import java.security.PrivilegedAction;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.security.Security;
+import java.security.SecurityPermission;
+import java.util.Map;
+
+public abstract class AuthConfigFactory {
+
+ public static final String DEFAULT_FACTORY_SECURITY_PROPERTY =
+ "authconfigprovider.factory";
+ public static final String GET_FACTORY_PERMISSION_NAME =
+ "getProperty.authconfigprovider.factory";
+ public static final String SET_FACTORY_PERMISSION_NAME =
+ "setProperty.authconfigprovider.factory";
+ public static final String PROVIDER_REGISTRATION_PERMISSION_NAME =
+ "setProperty.authconfigfactory.provider";
+
+ public static final SecurityPermission getFactorySecurityPermission =
+ new SecurityPermission(GET_FACTORY_PERMISSION_NAME);
+
+ public static final SecurityPermission setFactorySecurityPermission =
+ new SecurityPermission(SET_FACTORY_PERMISSION_NAME);
+
+ public static final SecurityPermission providerRegistrationSecurityPermission =
+ new SecurityPermission(PROVIDER_REGISTRATION_PERMISSION_NAME);
+
+ private static final String DEFAULT_JASPI_AUTHCONFIGFACTORYIMPL =
+ "org.apache.catalina.authenticator.jaspic.AuthConfigFactoryImpl";
+
+ private static AuthConfigFactory factory;
+
+ public AuthConfigFactory() {
+ }
+
+ public static synchronized AuthConfigFactory getFactory() {
+ checkPermission(getFactorySecurityPermission);
+ if (factory != null) {
+ return factory;
+ }
+
+ final String className = getFactoryClassName();
+ try {
+ factory = AccessController.doPrivileged(
+ new PrivilegedExceptionAction<AuthConfigFactory>() {
+ @Override
+ public AuthConfigFactory run() throws ClassNotFoundException,
+ InstantiationException, IllegalAccessException {
+ // Load this class with the same class loader as used for
+ // this class. Note that the Thread context class loader
+ // should not be used since that would trigger a memory leak
+ // in container environments.
+ Class<?> clazz = Class.forName(className);
+ return (AuthConfigFactory) clazz.newInstance();
+ }
+ });
+ } catch (PrivilegedActionException e) {
+ Exception inner = e.getException();
+ if (inner instanceof InstantiationException) {
+ throw (SecurityException) new SecurityException("AuthConfigFactory error:" +
+ inner.getCause().getMessage()).initCause(inner.getCause());
+ } else {
+ throw (SecurityException) new SecurityException(
+ "AuthConfigFactory error: " + inner).initCause(inner);
+ }
+ }
+
+ return factory;
+ }
+
+ public static synchronized void setFactory(AuthConfigFactory factory) {
+ checkPermission(setFactorySecurityPermission);
+ AuthConfigFactory.factory = factory;
+ }
+
+ public abstract AuthConfigProvider getConfigProvider(String layer, String appContext,
+ RegistrationListener listener);
+
+ @SuppressWarnings("rawtypes") // JASPIC API uses raw types
+ public abstract String registerConfigProvider(String className, Map properties, String layer,
+ String appContext, String description);
+
+ public abstract String registerConfigProvider(AuthConfigProvider provider, String layer,
+ String appContext, String description);
+
+ public abstract boolean removeRegistration(String registrationID);
+
+ public abstract String[] detachListener(RegistrationListener listener, String layer,
+ String appContext);
+
+ public abstract String[] getRegistrationIDs(AuthConfigProvider provider);
+
+ public abstract RegistrationContext getRegistrationContext(String registrationID);
+
+ public abstract void refresh();
+
+ private static void checkPermission(Permission permission) {
+ SecurityManager securityManager = System.getSecurityManager();
+ if (securityManager != null) {
+ securityManager.checkPermission(permission);
+ }
+ }
+
+ private static String getFactoryClassName() {
+ String className = AccessController.doPrivileged(new PrivilegedAction<String>() {
+ @Override
+ public String run() {
+ return Security.getProperty(DEFAULT_FACTORY_SECURITY_PROPERTY);
+ }
+ });
+
+ if (className != null) {
+ return className;
+ }
+
+ return DEFAULT_JASPI_AUTHCONFIGFACTORYIMPL;
+ }
+
+ public static interface RegistrationContext {
+
+ String getMessageLayer();
+
+ String getAppContext();
+
+ String getDescription();
+
+ boolean isPersistent();
+ }
+}
diff --git a/java/javax/security/auth/message/config/AuthConfigProvider.java b/java/javax/security/auth/message/config/AuthConfigProvider.java
new file mode 100644
index 0000000..ddde485
--- /dev/null
+++ b/java/javax/security/auth/message/config/AuthConfigProvider.java
@@ -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 javax.security.auth.message.config;
+
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.message.AuthException;
+
+public interface AuthConfigProvider {
+
+ ClientAuthConfig getClientAuthConfig(String layer, String appContext, CallbackHandler handler)
+ throws AuthException;
+
+ ServerAuthConfig getServerAuthConfig(String layer, String appContext, CallbackHandler handler)
+ throws AuthException;
+
+ void refresh();
+}
diff --git a/java/javax/security/auth/message/config/ClientAuthConfig.java b/java/javax/security/auth/message/config/ClientAuthConfig.java
new file mode 100644
index 0000000..2c13584
--- /dev/null
+++ b/java/javax/security/auth/message/config/ClientAuthConfig.java
@@ -0,0 +1,29 @@
+/**
+ * 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 javax.security.auth.message.config;
+
+import java.util.Map;
+
+import javax.security.auth.Subject;
+import javax.security.auth.message.AuthException;
+
+public interface ClientAuthConfig extends AuthConfig {
+
+ @SuppressWarnings("rawtypes") // JASPIC API uses raw types
+ ClientAuthContext getAuthContext(String authContextID, Subject clientSubject, Map properties)
+ throws AuthException;
+}
diff --git a/java/javax/security/auth/message/config/ClientAuthContext.java b/java/javax/security/auth/message/config/ClientAuthContext.java
new file mode 100644
index 0000000..dfc58a6
--- /dev/null
+++ b/java/javax/security/auth/message/config/ClientAuthContext.java
@@ -0,0 +1,22 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package javax.security.auth.message.config;
+
+import javax.security.auth.message.ClientAuth;
+
+public interface ClientAuthContext extends ClientAuth {
+}
diff --git a/java/javax/security/auth/message/config/RegistrationListener.java b/java/javax/security/auth/message/config/RegistrationListener.java
new file mode 100644
index 0000000..74496fb
--- /dev/null
+++ b/java/javax/security/auth/message/config/RegistrationListener.java
@@ -0,0 +1,22 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package javax.security.auth.message.config;
+
+public interface RegistrationListener {
+
+ void notify(String layer, String appContext);
+}
diff --git a/java/javax/security/auth/message/config/ServerAuthConfig.java b/java/javax/security/auth/message/config/ServerAuthConfig.java
new file mode 100644
index 0000000..de22a51
--- /dev/null
+++ b/java/javax/security/auth/message/config/ServerAuthConfig.java
@@ -0,0 +1,29 @@
+/**
+ * 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 javax.security.auth.message.config;
+
+import java.util.Map;
+
+import javax.security.auth.Subject;
+import javax.security.auth.message.AuthException;
+
+public interface ServerAuthConfig extends AuthConfig {
+
+ @SuppressWarnings("rawtypes") // JASPIC API uses raw types
+ ServerAuthContext getAuthContext(String authContextID, Subject serviceSubject, Map properties)
+ throws AuthException;
+}
diff --git a/java/javax/security/auth/message/config/ServerAuthContext.java b/java/javax/security/auth/message/config/ServerAuthContext.java
new file mode 100644
index 0000000..4c627ab
--- /dev/null
+++ b/java/javax/security/auth/message/config/ServerAuthContext.java
@@ -0,0 +1,22 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package javax.security.auth.message.config;
+
+import javax.security.auth.message.ServerAuth;
+
+public interface ServerAuthContext extends ServerAuth {
+}
diff --git a/java/javax/security/auth/message/module/ClientAuthModule.java b/java/javax/security/auth/message/module/ClientAuthModule.java
new file mode 100644
index 0000000..6cb5c4e
--- /dev/null
+++ b/java/javax/security/auth/message/module/ClientAuthModule.java
@@ -0,0 +1,34 @@
+/**
+ * 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 javax.security.auth.message.module;
+
+import java.util.Map;
+
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.message.AuthException;
+import javax.security.auth.message.ClientAuth;
+import javax.security.auth.message.MessagePolicy;
+
+public interface ClientAuthModule extends ClientAuth {
+
+ @SuppressWarnings("rawtypes") // JASPIC API uses raw types
+ void initialize(MessagePolicy requestPolicy, MessagePolicy responsePolicy,
+ CallbackHandler handler, Map options) throws AuthException;
+
+ @SuppressWarnings("rawtypes") // JASPIC API uses raw types
+ Class[] getSupportedMessageTypes();
+}
diff --git a/java/javax/security/auth/message/module/ServerAuthModule.java b/java/javax/security/auth/message/module/ServerAuthModule.java
new file mode 100644
index 0000000..4b3a6fb
--- /dev/null
+++ b/java/javax/security/auth/message/module/ServerAuthModule.java
@@ -0,0 +1,34 @@
+/**
+ * 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 javax.security.auth.message.module;
+
+import java.util.Map;
+
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.message.AuthException;
+import javax.security.auth.message.MessagePolicy;
+import javax.security.auth.message.ServerAuth;
+
+public interface ServerAuthModule extends ServerAuth {
+
+ @SuppressWarnings("rawtypes") // JASPIC API uses raw types
+ void initialize(MessagePolicy requestPolicy, MessagePolicy responsePolicy,
+ CallbackHandler handler, Map options) throws AuthException;
+
+ @SuppressWarnings("rawtypes") // JASPIC API uses raw types
+ Class[] getSupportedMessageTypes();
+}
diff --git a/java/javax/servlet/http/Cookie.java b/java/javax/servlet/http/Cookie.java
index cd8a330..fc2cb89 100644
--- a/java/javax/servlet/http/Cookie.java
+++ b/java/javax/servlet/http/Cookie.java
@@ -48,9 +48,8 @@ import java.util.ResourceBundle;
* cache pages that use cookies created with this class. This class does not
* support the cache control defined with HTTP 1.1.
* <p>
- * This class supports both the Version 0 (by Netscape) and Version 1 (by RFC
- * 2109) cookie specifications. By default, cookies are created using Version 0
- * to ensure the best interoperability.
+ * This class supports both the RFC 2109 and the RFC 6265 specifications.
+ * By default, cookies are created using RFC 6265.
*/
public class Cookie implements Cloneable, Serializable {
@@ -68,7 +67,7 @@ public class Cookie implements Cloneable, Serializable {
validation = new RFC2109Validator();
}
else {
- validation = new NetscapeValidator();
+ validation = new RFC6265Validator();
}
}
@@ -420,23 +419,16 @@ class CookieNameValidator {
}
}
-class NetscapeValidator extends CookieNameValidator {
- // the Netscape specification describes NAME=VALUE as
- // "a sequence of characters excluding semi-colon, comma and white space"
- // we also exclude the '=' character that separates NAME from VALUE
- private static final String NETSCAPE_SEPARATORS = ",; " + "=";
-
- NetscapeValidator() {
- super(NETSCAPE_SEPARATORS);
- }
-}
-
class RFC6265Validator extends CookieNameValidator {
private static final String RFC2616_SEPARATORS = "()<>@,;:\\\"/[]?={} \t";
RFC6265Validator() {
super(RFC2616_SEPARATORS);
+ }
+}
+class RFC2109Validator extends RFC6265Validator {
+ RFC2109Validator() {
// special treatment to allow for FWD_SLASH_IS_SEPARATOR property
boolean allowSlash;
String prop = System.getProperty("org.apache.tomcat.util.http.ServerCookie.FWD_SLASH_IS_SEPARATOR");
@@ -449,11 +441,6 @@ class RFC6265Validator extends CookieNameValidator {
allowed.set('/');
}
}
-}
-
-class RFC2109Validator extends RFC6265Validator {
- RFC2109Validator() {
- }
@Override
void validate(String name) {
diff --git a/java/javax/servlet/http/HttpSessionListener.java b/java/javax/servlet/http/HttpSessionListener.java
index a0f1009..6245728 100644
--- a/java/javax/servlet/http/HttpSessionListener.java
+++ b/java/javax/servlet/http/HttpSessionListener.java
@@ -44,5 +44,4 @@ public interface HttpSessionListener extends EventListener {
* the notification event
*/
public void sessionDestroyed(HttpSessionEvent se);
-
}
diff --git a/java/javax/servlet/jsp/el/ImplicitObjectELResolver.java b/java/javax/servlet/jsp/el/ImplicitObjectELResolver.java
index 80be339..43ecded 100644
--- a/java/javax/servlet/jsp/el/ImplicitObjectELResolver.java
+++ b/java/javax/servlet/jsp/el/ImplicitObjectELResolver.java
@@ -26,6 +26,7 @@ import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.Set;
import java.util.Vector;
@@ -77,9 +78,7 @@ public class ImplicitObjectELResolver extends ELResolver {
@Override
public Object getValue(ELContext context, Object base, Object property) {
- if (context == null) {
- throw new NullPointerException();
- }
+ Objects.requireNonNull(context);
if (base == null && property != null) {
int idx = Arrays.binarySearch(SCOPE_NAMES, property.toString());
@@ -120,9 +119,7 @@ public class ImplicitObjectELResolver extends ELResolver {
@Override
@SuppressWarnings({ "unchecked", "rawtypes" }) // TCK signature test fails with generics
public Class getType(ELContext context, Object base, Object property) {
- if (context == null) {
- throw new NullPointerException();
- }
+ Objects.requireNonNull(context);
if (base == null && property != null) {
int idx = Arrays.binarySearch(SCOPE_NAMES, property.toString());
@@ -136,9 +133,7 @@ public class ImplicitObjectELResolver extends ELResolver {
@Override
public void setValue(ELContext context, Object base, Object property,
Object value) {
- if (context == null) {
- throw new NullPointerException();
- }
+ Objects.requireNonNull(context);
if (base == null && property != null) {
int idx = Arrays.binarySearch(SCOPE_NAMES, property.toString());
@@ -151,9 +146,7 @@ public class ImplicitObjectELResolver extends ELResolver {
@Override
public boolean isReadOnly(ELContext context, Object base, Object property) {
- if (context == null) {
- throw new NullPointerException();
- }
+ Objects.requireNonNull(context);
if (base == null && property != null) {
int idx = Arrays.binarySearch(SCOPE_NAMES, property.toString());
@@ -597,9 +590,7 @@ public class ImplicitObjectELResolver extends ELResolver {
@Override
public final V put(String key, V value) {
- if (key == null) {
- throw new NullPointerException();
- }
+ Objects.requireNonNull(key);
if (value == null) {
this.removeAttribute(key);
} else {
@@ -610,13 +601,9 @@ public class ImplicitObjectELResolver extends ELResolver {
@Override
public final V remove(Object key) {
- if (key == null) {
- throw new NullPointerException();
- }
+ Objects.requireNonNull(key);
this.removeAttribute((String) key);
return null;
}
-
}
-
}
diff --git a/java/javax/servlet/jsp/el/ScopedAttributeELResolver.java b/java/javax/servlet/jsp/el/ScopedAttributeELResolver.java
index 4b7aefa..5316475 100644
--- a/java/javax/servlet/jsp/el/ScopedAttributeELResolver.java
+++ b/java/javax/servlet/jsp/el/ScopedAttributeELResolver.java
@@ -21,6 +21,7 @@ import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
+import java.util.Objects;
import javax.el.ELClass;
import javax.el.ELContext;
@@ -51,9 +52,7 @@ public class ScopedAttributeELResolver extends ELResolver {
@Override
public Object getValue(ELContext context, Object base, Object property) {
- if (context == null) {
- throw new NullPointerException();
- }
+ Objects.requireNonNull(context);
Object result = null;
@@ -112,9 +111,7 @@ public class ScopedAttributeELResolver extends ELResolver {
@Override
public Class<Object> getType(ELContext context, Object base, Object property) {
- if (context == null) {
- throw new NullPointerException();
- }
+ Objects.requireNonNull(context);
if (base == null) {
context.setPropertyResolved(base, property);
@@ -126,9 +123,7 @@ public class ScopedAttributeELResolver extends ELResolver {
@Override
public void setValue(ELContext context, Object base, Object property, Object value) {
- if (context == null) {
- throw new NullPointerException();
- }
+ Objects.requireNonNull(context);
if (base == null) {
context.setPropertyResolved(base, property);
@@ -147,9 +142,7 @@ public class ScopedAttributeELResolver extends ELResolver {
@Override
public boolean isReadOnly(ELContext context, Object base, Object property) {
- if (context == null) {
- throw new NullPointerException();
- }
+ Objects.requireNonNull(context);
if (base == null) {
context.setPropertyResolved(base, property);
diff --git a/java/javax/websocket/WebSocketContainer.java b/java/javax/websocket/WebSocketContainer.java
index 729619a..f2da3e4 100644
--- a/java/javax/websocket/WebSocketContainer.java
+++ b/java/javax/websocket/WebSocketContainer.java
@@ -55,7 +55,7 @@ public interface WebSocketContainer {
*
* @return The WebSocket session for the connection
*
- * @throws DeploymentException If the connection can not be established
+ * @throws DeploymentException If the connection cannot be established
* @throws IOException If an I/O occurred while trying to establish the
* connection
*/
@@ -76,7 +76,7 @@ public interface WebSocketContainer {
*
* @return The WebSocket session for the connection
*
- * @throws DeploymentException If the connection can not be established
+ * @throws DeploymentException If the connection cannot be established
* @throws IOException If an I/O occurred while trying to establish the
* connection
*/
diff --git a/java/org/apache/catalina/AccessLog.java b/java/org/apache/catalina/AccessLog.java
index 388815e..b862269 100644
--- a/java/org/apache/catalina/AccessLog.java
+++ b/java/org/apache/catalina/AccessLog.java
@@ -14,6 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package org.apache.catalina;
import org.apache.catalina.connector.Request;
diff --git a/java/org/apache/catalina/Context.java b/java/org/apache/catalina/Context.java
index bffa0b3..2b10be1 100644
--- a/java/org/apache/catalina/Context.java
+++ b/java/org/apache/catalina/Context.java
@@ -325,12 +325,16 @@ public interface Context extends Container, ContextBind {
/**
* Return the alternate Deployment Descriptor name.
+ *
+ * @return the name
*/
public String getAltDDName();
/**
* Set an alternate Deployment Descriptor name.
+ *
+ * @param altDDName The new name
*/
public void setAltDDName(String altDDName) ;
@@ -345,6 +349,8 @@ public interface Context extends Container, ContextBind {
/**
* Return the deny-uncovered-http-methods flag for this web application.
+ *
+ * @return The current value of the flag
*/
public boolean getDenyUncoveredHttpMethods();
@@ -359,6 +365,8 @@ public interface Context extends Container, ContextBind {
/**
* Return the display name of this web application.
+ *
+ * @return The display name
*/
public String getDisplayName();
@@ -372,7 +380,9 @@ public interface Context extends Container, ContextBind {
/**
- * Return the distributable flag for this web application.
+ * Get the distributable flag for this web application.
+ *
+ * @return The value of the distributable flag for this web application.
*/
public boolean getDistributable();
@@ -386,8 +396,9 @@ public interface Context extends Container, ContextBind {
/**
- * Return the document root for this Context. This can be an absolute
- * pathname, a relative pathname, or a URL.
+ * Obtain the document root for this Context.
+ *
+ * @return An absolute pathname, a relative pathname, or a URL.
*/
public String getDocBase();
@@ -402,13 +413,18 @@ public interface Context extends Container, ContextBind {
/**
- * Return the URL encoded context path, using UTF-8.
+ * Return the URL encoded context path
+ *
+ * @return The URL encoded (with UTF-8) context path
*/
public String getEncodedPath();
/**
- * Return the boolean on the annotations parsing.
+ * Determine if annotations parsing is currently disabled
+ *
+ * @return {@code true} if annotation parsing is disabled for this web
+ * application
*/
public boolean getIgnoreAnnotations();
@@ -423,7 +439,7 @@ public interface Context extends Container, ContextBind {
/**
- * Return the login configuration descriptor for this web application.
+ * @return the login configuration descriptor for this web application.
*/
public LoginConfig getLoginConfig();
@@ -437,7 +453,7 @@ public interface Context extends Container, ContextBind {
/**
- * Return the naming resources associated with this web application.
+ * @return the naming resources associated with this web application.
*/
public NamingResourcesImpl getNamingResources();
@@ -451,7 +467,7 @@ public interface Context extends Container, ContextBind {
/**
- * Return the context path for this web application.
+ * @return the context path for this web application.
*/
public String getPath();
@@ -465,7 +481,7 @@ public interface Context extends Container, ContextBind {
/**
- * Return the public identifier of the deployment descriptor DTD that is
+ * @return the public identifier of the deployment descriptor DTD that is
* currently being parsed.
*/
public String getPublicId();
@@ -481,7 +497,7 @@ public interface Context extends Container, ContextBind {
/**
- * Return the reloadable flag for this web application.
+ * @return the reloadable flag for this web application.
*/
public boolean getReloadable();
@@ -495,7 +511,7 @@ public interface Context extends Container, ContextBind {
/**
- * Return the override flag for this web application.
+ * @return the override flag for this web application.
*/
public boolean getOverride();
@@ -509,7 +525,7 @@ public interface Context extends Container, ContextBind {
/**
- * Return the privileged flag for this web application.
+ * @return the privileged flag for this web application.
*/
public boolean getPrivileged();
@@ -523,13 +539,13 @@ public interface Context extends Container, ContextBind {
/**
- * Return the servlet context for which this Context is a facade.
+ * @return the Servlet context for which this Context is a facade.
*/
public ServletContext getServletContext();
/**
- * Return the default session timeout (in minutes) for this
+ * @return the default session timeout (in minutes) for this
* web application.
*/
public int getSessionTimeout();
@@ -564,7 +580,7 @@ public interface Context extends Container, ContextBind {
public void setSwallowAbortedUploads(boolean swallowAbortedUploads);
/**
- * Return the value of the swallowOutput flag.
+ * @return the value of the swallowOutput flag.
*/
public boolean getSwallowOutput();
@@ -580,7 +596,7 @@ public interface Context extends Container, ContextBind {
/**
- * Return the Java class name of the Wrapper implementation used
+ * @return the Java class name of the Wrapper implementation used
* for servlets registered in this Context.
*/
public String getWrapperClass();
@@ -683,7 +699,7 @@ public interface Context extends Container, ContextBind {
public void setJarScanner(JarScanner jarScanner);
/**
- * Obtain the {@link Authenticator} that is used by this context or
+ * @return the {@link Authenticator} that is used by this context or
* <code>null</code> if none is used.
*/
public Authenticator getAuthenticator();
@@ -691,21 +707,29 @@ public interface Context extends Container, ContextBind {
/**
* Set whether or not the effective web.xml for this context should be
* logged on context start.
+ *
+ * @param logEffectiveWebXml set to <code>true</code> to log the complete
+ * web.xml that will be used for the webapp
*/
public void setLogEffectiveWebXml(boolean logEffectiveWebXml);
/**
* Should the effective web.xml for this context be logged on context start?
+ *
+ * @return true if the reconstructed web.xml that will be used for the
+ * webapp should be logged
*/
public boolean getLogEffectiveWebXml();
/**
- * Get the instance manager associated with this context.
+ * @return the instance manager associated with this context.
*/
public InstanceManager getInstanceManager();
/**
* Set the instance manager associated with this context.
+ *
+ * @param instanceManager the new instance manager instance
*/
public void setInstanceManager(InstanceManager instanceManager);
@@ -756,6 +780,8 @@ public interface Context extends Container, ContextBind {
/**
* Add a security constraint to the set for this web application.
+ *
+ * @param constraint The security constraint that should be added
*/
public void addConstraint(SecurityConstraint constraint);
@@ -795,17 +821,6 @@ public interface Context extends Container, ContextBind {
*/
public void addFilterMapBefore(FilterMap filterMap);
- /**
- * Add the classname of an InstanceListener to be added to each
- * Wrapper appended to this Context.
- *
- * @param listener Java class name of an InstanceListener class
- *
- * @deprecated Will be removed in 8.5.x onwards
- */
- @Deprecated
- public void addInstanceListener(String listener);
-
/**
* Add a Locale Encoding Mapping (see Sec 5.4 of Servlet spec 2.4)
@@ -948,32 +963,34 @@ public interface Context extends Container, ContextBind {
* the Java implementation class appropriate for this Context
* implementation. The constructor of the instantiated Wrapper
* will have been called, but no properties will have been set.
+ *
+ * @return a newly created wrapper instance that is used to wrap a Servlet
*/
public Wrapper createWrapper();
/**
- * Return the set of application listener class names configured
+ * @return the set of application listener class names configured
* for this application.
*/
public String[] findApplicationListeners();
/**
- * Return the set of application parameters for this application.
+ * @return the set of application parameters for this application.
*/
public ApplicationParameter[] findApplicationParameters();
/**
- * Return the set of security constraints for this web application.
+ * @return the set of security constraints for this web application.
* If there are none, a zero-length array is returned.
*/
public SecurityConstraint[] findConstraints();
/**
- * Return the error page entry for the specified HTTP error code,
+ * @return the error page entry for the specified HTTP error code,
* if any; otherwise return <code>null</code>.
*
* @param errorCode Error code to look up
@@ -982,7 +999,7 @@ public interface Context extends Container, ContextBind {
/**
- * Return the error page entry for the specified Java exception type,
+ * @return the error page entry for the specified Java exception type,
* if any; otherwise return <code>null</code>.
*
* @param exceptionType Exception type to look up
@@ -992,14 +1009,14 @@ public interface Context extends Container, ContextBind {
/**
- * Return the set of defined error pages for all specified error codes
+ * @return the set of defined error pages for all specified error codes
* and exception types.
*/
public ErrorPage[] findErrorPages();
/**
- * Return the filter definition for the specified filter name, if any;
+ * @return the filter definition for the specified filter name, if any;
* otherwise return <code>null</code>.
*
* @param filterName Filter name to look up
@@ -1008,29 +1025,19 @@ public interface Context extends Container, ContextBind {
/**
- * Return the set of defined filters for this Context.
+ * @return the set of defined filters for this Context.
*/
public FilterDef[] findFilterDefs();
/**
- * Return the set of filter mappings for this Context.
+ * @return the set of filter mappings for this Context.
*/
public FilterMap[] findFilterMaps();
/**
- * Return the set of InstanceListener classes that will be added to
- * newly created Wrappers automatically.
- *
- * @deprecated Will be removed in 8.5.x onwards
- */
- @Deprecated
- public String[] findInstanceListeners();
-
-
- /**
- * Return the MIME type to which the specified extension is mapped,
+ * @return the MIME type to which the specified extension is mapped,
* if any; otherwise return <code>null</code>.
*
* @param extension Extension to map to a MIME type
@@ -1039,14 +1046,14 @@ public interface Context extends Container, ContextBind {
/**
- * Return the extensions for which MIME mappings are defined. If there
+ * @return the extensions for which MIME mappings are defined. If there
* are none, a zero-length array is returned.
*/
public String[] findMimeMappings();
/**
- * Return the value for the specified context initialization
+ * @return the value for the specified context initialization
* parameter name, if any; otherwise return <code>null</code>.
*
* @param name Name of the parameter to return
@@ -1055,7 +1062,7 @@ public interface Context extends Container, ContextBind {
/**
- * Return the names of all defined context initialization parameters
+ * @return the names of all defined context initialization parameters
* for this Context. If no parameters are defined, a zero-length
* array is returned.
*/
@@ -1068,12 +1075,13 @@ public interface Context extends Container, ContextBind {
* is one. Otherwise, return the specified role unchanged.
*
* @param role Security role to map
+ * @return The role name that was mapped to the specified role
*/
public String findRoleMapping(String role);
/**
- * Return <code>true</code> if the specified security role is defined
+ * @return <code>true</code> if the specified security role is defined
* for this application; otherwise return <code>false</code>.
*
* @param role Security role to verify
@@ -1082,14 +1090,14 @@ public interface Context extends Container, ContextBind {
/**
- * Return the security roles defined for this application. If none
+ * @return the security roles defined for this application. If none
* have been defined, a zero-length array is returned.
*/
public String[] findSecurityRoles();
/**
- * Return the servlet name mapped by the specified pattern (if any);
+ * @return the servlet name mapped by the specified pattern (if any);
* otherwise return <code>null</code>.
*
* @param pattern Pattern for which a mapping is requested
@@ -1098,14 +1106,14 @@ public interface Context extends Container, ContextBind {
/**
- * Return the patterns of all defined servlet mappings for this
+ * @return the patterns of all defined servlet mappings for this
* Context. If no mappings are defined, a zero-length array is returned.
*/
public String[] findServletMappings();
/**
- * Return the context-relative URI of the error page for the specified
+ * @return the context-relative URI of the error page for the specified
* HTTP status code, if any; otherwise return <code>null</code>.
*
* @param status HTTP status code to look up
@@ -1114,7 +1122,7 @@ public interface Context extends Container, ContextBind {
/**
- * Return the set of HTTP status codes for which error pages have
+ * @return the set of HTTP status codes for which error pages have
* been specified. If none are specified, a zero-length array
* is returned.
*/
@@ -1122,26 +1130,29 @@ public interface Context extends Container, ContextBind {
/**
- * Get the associated ThreadBindingListener.
+ * @return the associated ThreadBindingListener.
*/
public ThreadBindingListener getThreadBindingListener();
/**
* Get the associated ThreadBindingListener.
+ *
+ * @param threadBindingListener Set the listener that will receive
+ * notifications when entering and exiting the application scope
*/
public void setThreadBindingListener(ThreadBindingListener threadBindingListener);
/**
- * Return the set of watched resources for this Context. If none are
+ * @return the set of watched resources for this Context. If none are
* defined, a zero length array will be returned.
*/
public String[] findWatchedResources();
/**
- * Return <code>true</code> if the specified welcome file is defined
+ * @return <code>true</code> if the specified welcome file is defined
* for this Context; otherwise return <code>false</code>.
*
* @param name Welcome file to verify
@@ -1150,21 +1161,21 @@ public interface Context extends Container, ContextBind {
/**
- * Return the set of welcome files defined for this Context. If none are
+ * @return the set of welcome files defined for this Context. If none are
* defined, a zero-length array is returned.
*/
public String[] findWelcomeFiles();
/**
- * Return the set of LifecycleListener classes that will be added to
+ * @return the set of LifecycleListener classes that will be added to
* newly created Wrappers automatically.
*/
public String[] findWrapperLifecycles();
/**
- * Return the set of ContainerListener classes that will be added to
+ * @return the set of ContainerListener classes that will be added to
* newly created Wrappers automatically.
*/
public String[] findWrapperListeners();
@@ -1173,6 +1184,8 @@ public interface Context extends Container, ContextBind {
/**
* Notify all {@link javax.servlet.ServletRequestListener}s that a request
* has started.
+ *
+ * @param request The request object that will be passed to the listener
* @return <code>true</code> if the listeners fire successfully, else
* <code>false</code>
*/
@@ -1181,6 +1194,8 @@ public interface Context extends Container, ContextBind {
/**
* Notify all {@link javax.servlet.ServletRequestListener}s that a request
* has ended.
+ *
+ * @param request The request object that will be passed to the listener
* @return <code>true</code> if the listeners fire successfully, else
* <code>false</code>
*/
@@ -1248,18 +1263,6 @@ public interface Context extends Container, ContextBind {
/**
- * Remove a class name from the set of InstanceListener classes that
- * will be added to newly created Wrappers.
- *
- * @param listener Class name of an InstanceListener class to be removed
- *
- * @deprecated Will be removed in 8.5.x onwards
- */
- @Deprecated
- public void removeInstanceListener(String listener);
-
-
- /**
* Remove the MIME mapping for the specified extension, if it exists;
* otherwise, no action is taken.
*
@@ -1339,7 +1342,7 @@ public interface Context extends Container, ContextBind {
/**
- * Return the real path for a given virtual path, if possible; otherwise
+ * @return the real path for a given virtual path, if possible; otherwise
* return <code>null</code>.
*
* @param path The path to the desired resource
@@ -1348,7 +1351,7 @@ public interface Context extends Container, ContextBind {
/**
- * Return the effective major version of the Servlet spec used by this
+ * @return the effective major version of the Servlet spec used by this
* context.
*/
public int getEffectiveMajorVersion();
@@ -1357,12 +1360,14 @@ public interface Context extends Container, ContextBind {
/**
* Set the effective major version of the Servlet spec used by this
* context.
+ *
+ * @param major Set the version number
*/
public void setEffectiveMajorVersion(int major);
/**
- * Return the effective minor version of the Servlet spec used by this
+ * @return the effective minor version of the Servlet spec used by this
* context.
*/
public int getEffectiveMinorVersion();
@@ -1371,12 +1376,14 @@ public interface Context extends Container, ContextBind {
/**
* Set the effective minor version of the Servlet spec used by this
* context.
+ *
+ * @param minor Set the version number
*/
public void setEffectiveMinorVersion(int minor);
/**
- * Obtain the JSP configuration for this context.
+ * @return the JSP configuration for this context.
* Will be null if there is no JSP configuration.
*/
public JspConfigDescriptor getJspConfigDescriptor();
@@ -1384,6 +1391,8 @@ public interface Context extends Container, ContextBind {
/**
* Set the JspConfigDescriptor for this context.
* A null value indicates there is not JSP configuration.
+ *
+ * @param descriptor the new JSP configuration
*/
public void setJspConfigDescriptor(JspConfigDescriptor descriptor);
@@ -1399,20 +1408,24 @@ public interface Context extends Container, ContextBind {
/**
* Is this Context paused whilst it is reloaded?
+ *
+ * @return <code>true</code> if the context has been paused
*/
public boolean getPaused();
/**
* Is this context using version 2.2 of the Servlet spec?
+ *
+ * @return <code>true</code> for a legacy Servlet 2.2 webapp
*/
boolean isServlet22();
/**
- * Notification that servlet security has been dynamically set in a
+ * Notification that Servlet security has been dynamically set in a
* {@link javax.servlet.ServletRegistration.Dynamic}
- * @param registration servlet security was modified for
- * @param servletSecurityElement new security constraints for this servlet
+ * @param registration Servlet security was modified for
+ * @param servletSecurityElement new security constraints for this Servlet
* @return urls currently mapped to this registration that are already
* present in web.xml
*/
@@ -1423,6 +1436,8 @@ public interface Context extends Container, ContextBind {
* Sets the (comma separated) list of Servlets that expect a resource to be
* present. Used to ensure that welcome files associated with Servlets that
* expect a resource to be present are not mapped when there is no resource.
+ *
+ * @param resourceOnlyServlets The Servlet names comma separated list
*/
public void setResourceOnlyServlets(String resourceOnlyServlets);
@@ -1443,7 +1458,7 @@ public interface Context extends Container, ContextBind {
public boolean isResourceOnlyServlet(String servletName);
/**
- * Return the base name to use for WARs, directories or context.xml files
+ * @return the base name to use for WARs, directories or context.xml files
* for this context.
*/
public String getBaseName();
@@ -1452,24 +1467,29 @@ public interface Context extends Container, ContextBind {
* Set the version of this web application - used to differentiate
* different versions of the same web application when using parallel
* deployment.
+ *
+ * @param webappVersion The webapp version associated with the context,
+ * which should be unique
*/
public void setWebappVersion(String webappVersion);
/**
- * Set the version of this web application - used to differentiate
+ * @return The version of this web application, used to differentiate
* different versions of the same web application when using parallel
- * deployment. If not specified, defaults to the empty string.
+ * deployment.
*/
public String getWebappVersion();
/**
* Configure whether or not requests listeners will be fired on forwards for
* this Context.
+ *
+ * @param enable <code>true</code> to fire request listeners when forwarding
*/
public void setFireRequestListenersOnForwards(boolean enable);
/**
- * Determine whether or not requests listeners will be fired on forwards for
+ * @return whether or not requests listeners will be fired on forwards for
* this Context.
*/
public boolean getFireRequestListenersOnForwards();
@@ -1478,11 +1498,14 @@ public interface Context extends Container, ContextBind {
* Configures if a user presents authentication credentials, whether the
* context will process them when the request is for a non-protected
* resource.
+ *
+ * @param enable <code>true</code> to perform authentication even outside
+ * security constraints
*/
public void setPreemptiveAuthentication(boolean enable);
/**
- * Determines if a user presents authentication credentials, will the
+ * @return if a user presents authentication credentials, will the
* context will process them when the request is for a non-protected
* resource.
*/
@@ -1491,17 +1514,19 @@ public interface Context extends Container, ContextBind {
/**
* Configures if a response body is included when a redirect response is
* sent to the client.
+ *
+ * @param enable <code>true</code> to send a response body for redirects
*/
public void setSendRedirectBody(boolean enable);
/**
- * Determines if the context is configured to include a response body as
+ * @return if the context is configured to include a response body as
* part of a redirect response.
*/
public boolean getSendRedirectBody();
/**
- * Return the Loader with which this Context is associated.
+ * @return the Loader with which this Context is associated.
*/
public Loader getLoader();
@@ -1513,7 +1538,7 @@ public interface Context extends Container, ContextBind {
public void setLoader(Loader loader);
/**
- * Return the Resources with which this Context is associated.
+ * @return the Resources with which this Context is associated.
*/
public WebResourceRoot getResources();
@@ -1525,7 +1550,7 @@ public interface Context extends Container, ContextBind {
public void setResources(WebResourceRoot resources);
/**
- * Return the Manager with which this Context is associated. If there is
+ * @return the Manager with which this Context is associated. If there is
* no associated Manager, return <code>null</code>.
*/
public Manager getManager();
@@ -1548,7 +1573,7 @@ public interface Context extends Container, ContextBind {
public void setAddWebinfClassesResources(boolean addWebinfClassesResources);
/**
- * Gets the flag that indicates if /WEB-INF/classes should be treated like
+ * @return the flag that indicates if /WEB-INF/classes should be treated like
* an exploded JAR and JAR resources made available as if they were in a
* JAR.
*/
@@ -1652,7 +1677,7 @@ public interface Context extends Container, ContextBind {
public Map<String, String> findPreDestroyMethods();
/**
- * Obtain the token necessary for operations on the associated JNDI naming
+ * @return the token necessary for operations on the associated JNDI naming
* context.
*/
public Object getNamingToken();
@@ -1669,7 +1694,7 @@ public interface Context extends Container, ContextBind {
public void setCookieProcessor(CookieProcessor cookieProcessor);
/**
- * Obtains the {@link CookieProcessor} that will be used to process cookies
+ * @return the {@link CookieProcessor} that will be used to process cookies
* for this Context.
*/
public CookieProcessor getCookieProcessor();
diff --git a/java/org/apache/catalina/Engine.java b/java/org/apache/catalina/Engine.java
index 9fe4096..66b4411 100644
--- a/java/org/apache/catalina/Engine.java
+++ b/java/org/apache/catalina/Engine.java
@@ -14,8 +14,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
-
package org.apache.catalina;
/**
@@ -44,12 +42,8 @@ package org.apache.catalina;
*/
public interface Engine extends Container {
-
- // ------------------------------------------------------------- Properties
-
-
/**
- * Return the default hostname for this Engine.
+ * @return the default host name for this Engine.
*/
public String getDefaultHost();
@@ -63,7 +57,7 @@ public interface Engine extends Container {
/**
- * Retrieve the JvmRouteId for this engine.
+ * @return the JvmRouteId for this engine.
*/
public String getJvmRoute();
@@ -78,7 +72,7 @@ public interface Engine extends Container {
/**
- * Return the <code>Service</code> with which we are associated (if any).
+ * @return the <code>Service</code> with which we are associated (if any).
*/
public Service getService();
@@ -89,6 +83,4 @@ public interface Engine extends Container {
* @param service The service that owns this Engine
*/
public void setService(Service service);
-
-
}
diff --git a/java/org/apache/catalina/Executor.java b/java/org/apache/catalina/Executor.java
index 5f323c7..16b3bbb 100644
--- a/java/org/apache/catalina/Executor.java
+++ b/java/org/apache/catalina/Executor.java
@@ -18,8 +18,8 @@ package org.apache.catalina;
import java.util.concurrent.TimeUnit;
-
public interface Executor extends java.util.concurrent.Executor, Lifecycle {
+
public String getName();
/**
@@ -31,6 +31,9 @@ public interface Executor extends java.util.concurrent.Executor, Lifecycle {
* time until it throws a RejectedExecutionException
*
* @param command the runnable task
+ * @param timeout the length of time to wait for the task to complete
+ * @param unit the units in which timeout is expressed
+ *
* @throws java.util.concurrent.RejectedExecutionException if this task
* cannot be accepted for execution - the queue is full
* @throws NullPointerException if command or unit is null
diff --git a/java/org/apache/catalina/Globals.java b/java/org/apache/catalina/Globals.java
index 568a657..994902b 100644
--- a/java/org/apache/catalina/Globals.java
+++ b/java/org/apache/catalina/Globals.java
@@ -16,8 +16,6 @@
*/
package org.apache.catalina;
-import org.apache.tomcat.util.compat.JreVendor;
-
/**
* Global constants that are applicable to multiple packages within Catalina.
*
@@ -138,41 +136,6 @@ public final class Globals {
/**
* The request attribute that is set to the value of {@code Boolean.TRUE}
- * if connector processing this request supports Comet API.
- * Duplicated here for neater code in the catalina packages.
- */
- public static final String COMET_SUPPORTED_ATTR =
- org.apache.coyote.Constants.COMET_SUPPORTED_ATTR;
-
-
- /**
- * The request attribute that is set to the value of {@code Boolean.TRUE}
- * if connector processing this request supports setting
- * per-connection request timeout through Comet API.
- *
- * @see org.apache.catalina.comet.CometEvent#setTimeout(int)
- *
- * Duplicated here for neater code in the catalina packages.
- */
- public static final String COMET_TIMEOUT_SUPPORTED_ATTR =
- org.apache.coyote.Constants.COMET_TIMEOUT_SUPPORTED_ATTR;
-
-
- /**
- * The request attribute that can be set to a value of type
- * {@code java.lang.Integer} to specify per-connection request
- * timeout for Comet API. The value is in milliseconds.
- *
- * @see org.apache.catalina.comet.CometEvent#setTimeout(int)
- *
- * Duplicated here for neater code in the catalina packages.
- */
- public static final String COMET_TIMEOUT_ATTR =
- org.apache.coyote.Constants.COMET_TIMEOUT_ATTR;
-
-
- /**
- * The request attribute that is set to the value of {@code Boolean.TRUE}
* if connector processing this request supports use of sendfile.
*
* Duplicated here for neater code in the catalina packages.
@@ -309,12 +272,6 @@ public final class Globals {
public static final String JASPER_XML_BLOCK_EXTERNAL_INIT_PARAM =
"org.apache.jasper.XML_BLOCK_EXTERNAL";
- @Deprecated // Will be removed in Tomcat 8.5.x
- public static final boolean IS_ORACLE_JVM = JreVendor.IS_ORACLE_JVM;
-
- @Deprecated // Will be removed in Tomcat 8.5.x
- public static final boolean IS_IBM_JVM = JreVendor.IS_IBM_JVM;
-
/**
* Name of the ServletContext attribute under which we store the context
* Realm's CredentialHandler (if both the Realm and the CredentialHandler
diff --git a/java/org/apache/catalina/Group.java b/java/org/apache/catalina/Group.java
index d5a935a..05ece8b 100644
--- a/java/org/apache/catalina/Group.java
+++ b/java/org/apache/catalina/Group.java
@@ -14,15 +14,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
-
package org.apache.catalina;
-
import java.security.Principal;
import java.util.Iterator;
-
/**
* <p>Abstract representation of a group of {@link User}s in a
* {@link UserDatabase}. Each user that is a member of this group
@@ -33,12 +29,10 @@ import java.util.Iterator;
*/
public interface Group extends Principal {
-
// ------------------------------------------------------------- Properties
-
/**
- * Return the description of this group.
+ * @return the description of this group.
*/
public String getDescription();
@@ -52,7 +46,7 @@ public interface Group extends Principal {
/**
- * Return the group name of this group, which must be unique
+ * @return the group name of this group, which must be unique
* within the scope of a {@link UserDatabase}.
*/
public String getGroupname();
@@ -68,26 +62,25 @@ public interface Group extends Principal {
/**
- * Return the set of {@link Role}s assigned specifically to this group.
+ * @return the set of {@link Role}s assigned specifically to this group.
*/
public Iterator<Role> getRoles();
/**
- * Return the {@link UserDatabase} within which this Group is defined.
+ * @return the {@link UserDatabase} within which this Group is defined.
*/
public UserDatabase getUserDatabase();
/**
- * Return the set of {@link User}s that are members of this group.
+ * @return the set of {@link User}s that are members of this group.
*/
public Iterator<User> getUsers();
// --------------------------------------------------------- Public Methods
-
/**
* Add a new {@link Role} to those assigned specifically to this group.
*
@@ -100,6 +93,9 @@ public interface Group extends Principal {
* Is this group specifically assigned the specified {@link Role}?
*
* @param role The role to check
+ *
+ * @return <code>true</code> if the group is assigned to the specified role
+ * otherwise <code>false</code>
*/
public boolean isInRole(Role role);
diff --git a/java/org/apache/catalina/Host.java b/java/org/apache/catalina/Host.java
index 329bacc..e0f53bb 100644
--- a/java/org/apache/catalina/Host.java
+++ b/java/org/apache/catalina/Host.java
@@ -67,7 +67,7 @@ public interface Host extends Container {
/**
- * Return the XML root for this Host. This can be an absolute
+ * @return the XML root for this Host. This can be an absolute
* pathname, a relative pathname, or a URL.
* If null, defaults to
* ${catalina.base}/conf/<engine name>/<host name> directory
@@ -84,20 +84,20 @@ public interface Host extends Container {
public void setXmlBase(String xmlBase);
/**
- * Return a default configuration path of this Host. The file will be
+ * @return a default configuration path of this Host. The file will be
* canonical if possible.
*/
public File getConfigBaseFile();
/**
- * Return the application root for this Host. This can be an absolute
+ * @return the application root for this Host. This can be an absolute
* pathname, a relative pathname, or a URL.
*/
public String getAppBase();
/**
- * Return an absolute {@link File} for the appBase of this Host. The file
+ * @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.
*/
@@ -114,7 +114,7 @@ public interface Host extends Container {
/**
- * Return the value of the auto deploy flag. If true, it indicates that
+ * @return the value of the auto deploy flag. If true, it indicates that
* this host's child webapps should be discovered and automatically
* deployed dynamically.
*/
@@ -130,7 +130,7 @@ public interface Host extends Container {
/**
- * Return the Java class name of the context configuration class
+ * @return the Java class name of the context configuration class
* for new web applications.
*/
public String getConfigClass();
@@ -146,7 +146,7 @@ public interface Host extends Container {
/**
- * Return the value of the deploy on startup flag. If true, it indicates
+ * @return the value of the deploy on startup flag. If true, it indicates
* that this host's child webapps should be discovered and automatically
* deployed.
*/
@@ -162,7 +162,7 @@ public interface Host extends Container {
/**
- * Return the regular expression that defines the files and directories in
+ * @return the regular expression that defines the files and directories in
* the host's appBase that will be ignored by the automatic deployment
* process.
*/
@@ -170,7 +170,7 @@ public interface Host extends Container {
/**
- * Return the compiled regular expression that defines the files and
+ * @return the compiled regular expression that defines the files and
* directories in the host's appBase that will be ignored by the automatic
* deployment process.
*/
@@ -181,12 +181,14 @@ public interface Host extends Container {
* Set the regular expression that defines the files and directories in
* the host's appBase that will be ignored by the automatic deployment
* process.
+ *
+ * @param deployIgnore A regular expression matching file names
*/
public void setDeployIgnore(String deployIgnore);
/**
- * Return the executor that is used for starting and stopping contexts. This
+ * @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.
*/
@@ -194,7 +196,7 @@ public interface Host extends Container {
/**
- * Returns true if the Host will attempt to create directories for appBase and xmlBase
+ * Returns <code>true</code> if the Host will attempt to create directories for appBase and xmlBase
* unless they already exist.
* @return true if the Host will attempt to create directories
*/
@@ -211,17 +213,19 @@ public interface Host extends Container {
/**
- * Returns true of the Host is configured to automatically undeploy old
+ * @return <code>true</code> of the Host is configured to automatically undeploy old
* versions of applications deployed using parallel deployment. This only
- * takes effect is {@link #getAutoDeploy()} also returns true.
+ * takes effect is {@link #getAutoDeploy()} also returns <code>true</code>.
*/
public boolean getUndeployOldVersions();
/**
- * Set to true if the Host should automatically undeploy old versions of
+ * Set to <code>true</code> if the Host should automatically undeploy old versions of
* applications deployed using parallel deployment. This only takes effect
- * if {@link #getAutoDeploy()} returns true.
+ * if {@link #getAutoDeploy()} returns <code>true</code>.
+ *
+ * @param undeployOldVersions The new value for this flag
*/
public void setUndeployOldVersions(boolean undeployOldVersions);
@@ -237,7 +241,7 @@ public interface Host extends Container {
/**
- * Return the set of alias names for this Host. If none are defined,
+ * @return the set of alias names for this Host. If none are defined,
* a zero length array is returned.
*/
public String[] findAliases();
diff --git a/java/org/apache/catalina/InstanceEvent.java b/java/org/apache/catalina/InstanceEvent.java
deleted file mode 100644
index 433b02b..0000000
--- a/java/org/apache/catalina/InstanceEvent.java
+++ /dev/null
@@ -1,397 +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;
-
-
-import java.util.EventObject;
-
-import javax.servlet.Filter;
-import javax.servlet.Servlet;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
-
-
-/**
- * General event for notifying listeners of significant events related to
- * a specific instance of a Servlet, or a specific instance of a Filter,
- * as opposed to the Wrapper component that manages it.
- *
- * @author Craig R. McClanahan
- *
- * @deprecated Will be removed in 8.5.x onwards
- */
- at Deprecated
-public final class InstanceEvent extends EventObject {
-
- private static final long serialVersionUID = 1L;
-
-
- /**
- * The event indicating that the <code>init()</code> method is about
- * to be called for this instance.
- */
- public static final String BEFORE_INIT_EVENT = "beforeInit";
-
-
- /**
- * The event indicating that the <code>init()</code> method has returned.
- */
- public static final String AFTER_INIT_EVENT = "afterInit";
-
-
- /**
- * The event indicating that the <code>service()</code> method is about
- * to be called on a servlet. The <code>servlet</code> property contains
- * the servlet being called, and the <code>request</code> and
- * <code>response</code> properties contain the current request and
- * response being processed.
- */
- public static final String BEFORE_SERVICE_EVENT = "beforeService";
-
-
- /**
- * The event indicating that the <code>service()</code> method has
- * returned. The <code>servlet</code> property contains the servlet
- * that was called, and the <code>request</code> and
- * <code>response</code> properties contain the current request and
- * response being processed.
- */
- public static final String AFTER_SERVICE_EVENT = "afterService";
-
-
- /**
- * The event indicating that the <code>destroy</code> method is about
- * to be called for this instance.
- */
- public static final String BEFORE_DESTROY_EVENT = "beforeDestroy";
-
-
- /**
- * The event indicating that the <code>destroy()</code> method has
- * returned.
- */
- public static final String AFTER_DESTROY_EVENT = "afterDestroy";
-
-
- /**
- * The event indicating that the <code>service()</code> method of a
- * servlet accessed via a request dispatcher is about to be called.
- * The <code>servlet</code> property contains a reference to the
- * dispatched-to servlet instance, and the <code>request</code> and
- * <code>response</code> properties contain the current request and
- * response being processed. The <code>wrapper</code> property will
- * contain a reference to the dispatched-to Wrapper.
- */
- public static final String BEFORE_DISPATCH_EVENT = "beforeDispatch";
-
-
- /**
- * The event indicating that the <code>service()</code> method of a
- * servlet accessed via a request dispatcher has returned. The
- * <code>servlet</code> property contains a reference to the
- * dispatched-to servlet instance, and the <code>request</code> and
- * <code>response</code> properties contain the current request and
- * response being processed. The <code>wrapper</code> property will
- * contain a reference to the dispatched-to Wrapper.
- */
- public static final String AFTER_DISPATCH_EVENT = "afterDispatch";
-
-
- /**
- * The event indicating that the <code>doFilter()</code> method of a
- * Filter is about to be called. The <code>filter</code> property
- * contains a reference to the relevant filter instance, and the
- * <code>request</code> and <code>response</code> properties contain
- * the current request and response being processed.
- */
- public static final String BEFORE_FILTER_EVENT = "beforeFilter";
-
-
- /**
- * The event indicating that the <code>doFilter()</code> method of a
- * Filter has returned. The <code>filter</code> property contains
- * a reference to the relevant filter instance, and the
- * <code>request</code> and <code>response</code> properties contain
- * the current request and response being processed.
- */
- public static final String AFTER_FILTER_EVENT = "afterFilter";
-
-
- // ----------------------------------------------------------- Constructors
-
-
- /**
- * Construct a new InstanceEvent with the specified parameters. This
- * constructor is used for filter lifecycle events.
- *
- * @param wrapper Wrapper managing this servlet instance
- * @param filter Filter instance for which this event occurred
- * @param type Event type (required)
- */
- public InstanceEvent(Wrapper wrapper, Filter filter, String type) {
- this(wrapper, filter, type, null, null, null);
- }
-
-
- /**
- * Construct a new InstanceEvent with the specified parameters. This
- * constructor is used for filter lifecycle events.
- *
- * @param wrapper Wrapper managing this servlet instance
- * @param filter Filter instance for which this event occurred
- * @param type Event type (required)
- * @param exception Exception that occurred
- */
- public InstanceEvent(Wrapper wrapper, Filter filter, String type,
- Throwable exception) {
- this(wrapper, filter, type, null, null, exception);
- }
-
-
- /**
- * Construct a new InstanceEvent with the specified parameters. This
- * constructor is used for filter processing events.
- *
- * @param wrapper Wrapper managing this servlet instance
- * @param filter Filter instance for which this event occurred
- * @param type Event type (required)
- * @param request Servlet request we are processing
- * @param response Servlet response we are processing
- */
- public InstanceEvent(Wrapper wrapper, Filter filter, String type,
- ServletRequest request, ServletResponse response) {
- this(wrapper, filter, type, request, response, null);
- }
-
-
- /**
- * Construct a new InstanceEvent with the specified parameters. This
- * constructor is used for filter processing events.
- *
- * @param wrapper Wrapper managing this servlet instance
- * @param filter Filter instance for which this event occurred
- * @param type Event type (required)
- * @param request Servlet request we are processing
- * @param response Servlet response we are processing
- * @param exception Exception that occurred
- */
- public InstanceEvent(Wrapper wrapper, Filter filter, String type,
- ServletRequest request, ServletResponse response,
- Throwable exception) {
-
- super(wrapper);
- this.filter = filter;
- this.servlet = null;
- this.type = type;
- this.request = request;
- this.response = response;
- this.exception = exception;
-
- }
-
-
- /**
- * Construct a new InstanceEvent with the specified parameters. This
- * constructor is used for processing servlet lifecycle events.
- *
- * @param wrapper Wrapper managing this servlet instance
- * @param servlet Servlet instance for which this event occurred
- * @param type Event type (required)
- */
- public InstanceEvent(Wrapper wrapper, Servlet servlet, String type) {
- this(wrapper, servlet, type, null, null, null);
- }
-
-
- /**
- * Construct a new InstanceEvent with the specified parameters. This
- * constructor is used for processing servlet lifecycle events.
- *
- * @param wrapper Wrapper managing this servlet instance
- * @param servlet Servlet instance for which this event occurred
- * @param type Event type (required)
- * @param exception Exception that occurred
- */
- public InstanceEvent(Wrapper wrapper, Servlet servlet, String type,
- Throwable exception) {
- this(wrapper, servlet, type, null, null, exception);
- }
-
-
- /**
- * Construct a new InstanceEvent with the specified parameters. This
- * constructor is used for processing servlet processing events.
- *
- * @param wrapper Wrapper managing this servlet instance
- * @param servlet Servlet instance for which this event occurred
- * @param type Event type (required)
- * @param request Servlet request we are processing
- * @param response Servlet response we are processing
- */
- public InstanceEvent(Wrapper wrapper, Servlet servlet, String type,
- ServletRequest request, ServletResponse response) {
- this(wrapper, servlet, type, request, response, null);
- }
-
-
- /**
- * Construct a new InstanceEvent with the specified parameters. This
- * constructor is used for processing servlet processing events.
- *
- * @param wrapper Wrapper managing this servlet instance
- * @param servlet Servlet instance for which this event occurred
- * @param type Event type (required)
- * @param request Servlet request we are processing
- * @param response Servlet response we are processing
- * @param exception Exception that occurred
- */
- public InstanceEvent(Wrapper wrapper, Servlet servlet, String type,
- ServletRequest request, ServletResponse response,
- Throwable exception) {
-
- super(wrapper);
- this.filter = null;
- this.servlet = servlet;
- this.type = type;
- this.request = request;
- this.response = response;
- this.exception = exception;
-
- }
-
-
- // ----------------------------------------------------- Instance Variables
-
-
- /**
- * The exception that was thrown during the processing being reported
- * by this event (AFTER_INIT_EVENT, AFTER_SERVICE_EVENT,
- * AFTER_DESTROY_EVENT, AFTER_DISPATCH_EVENT, and AFTER_FILTER_EVENT only).
- */
- private final Throwable exception;
-
-
- /**
- * The Filter instance for which this event occurred (BEFORE_FILTER_EVENT
- * and AFTER_FILTER_EVENT only).
- */
- private final transient Filter filter;
-
-
- /**
- * The servlet request being processed (BEFORE_FILTER_EVENT,
- * AFTER_FILTER_EVENT, BEFORE_SERVICE_EVENT, and AFTER_SERVICE_EVENT).
- */
- private final transient ServletRequest request;
-
-
- /**
- * The servlet response being processed (BEFORE_FILTER_EVENT,
- * AFTER_FILTER_EVENT, BEFORE_SERVICE_EVENT, and AFTER_SERVICE_EVENT).
- */
- private final transient ServletResponse response;
-
-
- /**
- * The Servlet instance for which this event occurred (not present on
- * BEFORE_FILTER_EVENT or AFTER_FILTER_EVENT events).
- */
- private final transient Servlet servlet;
-
-
- /**
- * The event type this instance represents.
- */
- private final String type;
-
-
- // ------------------------------------------------------------- Properties
-
-
- /**
- * Return the exception that occurred during the processing
- * that was reported by this event.
- */
- public Throwable getException() {
-
- return (this.exception);
-
- }
-
-
- /**
- * Return the filter instance for which this event occurred.
- */
- public Filter getFilter() {
-
- return (this.filter);
-
- }
-
-
- /**
- * Return the servlet request for which this event occurred.
- */
- public ServletRequest getRequest() {
-
- return (this.request);
-
- }
-
-
- /**
- * Return the servlet response for which this event occurred.
- */
- public ServletResponse getResponse() {
-
- return (this.response);
-
- }
-
-
- /**
- * Return the servlet instance for which this event occurred.
- */
- public Servlet getServlet() {
-
- return (this.servlet);
-
- }
-
-
- /**
- * Return the event type of this event.
- */
- public String getType() {
-
- return (this.type);
-
- }
-
-
- /**
- * Return the Wrapper managing the servlet instance for which this
- * event occurred.
- */
- public Wrapper getWrapper() {
-
- return (Wrapper) getSource();
-
- }
-}
diff --git a/java/org/apache/catalina/InstanceListener.java b/java/org/apache/catalina/InstanceListener.java
deleted file mode 100644
index 0146f6c..0000000
--- a/java/org/apache/catalina/InstanceListener.java
+++ /dev/null
@@ -1,43 +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;
-
-
-/**
- * Interface defining a listener for significant events related to a
- * specific servlet instance, rather than to the Wrapper component that
- * is managing that instance.
- *
- * @author Craig R. McClanahan
- *
- * @deprecated Will be removed in 8.5.x onwards
- */
- at Deprecated
-public interface InstanceListener {
-
-
- /**
- * Acknowledge the occurrence of the specified event.
- *
- * @param event InstanceEvent that has occurred
- */
- public void instanceEvent(InstanceEvent event);
-
-
-}
diff --git a/java/org/apache/catalina/JmxEnabled.java b/java/org/apache/catalina/JmxEnabled.java
index cdf0b86..2d514f4 100644
--- a/java/org/apache/catalina/JmxEnabled.java
+++ b/java/org/apache/catalina/JmxEnabled.java
@@ -28,7 +28,7 @@ import javax.management.ObjectName;
public interface JmxEnabled extends MBeanRegistration {
/**
- * Obtain the domain under which this component will be / has been
+ * @return the domain under which this component will be / has been
* registered.
*/
String getDomain();
@@ -38,12 +38,15 @@ public interface JmxEnabled extends MBeanRegistration {
* Specify the domain under which this component should be registered. Used
* with components that cannot (easily) navigate the component hierarchy to
* determine the correct domain to use.
+ *
+ * @param domain The name of the domain under which this component should be
+ * registered
*/
void setDomain(String domain);
/**
- * Obtain the name under which this component has been registered with JMX.
+ * @return the name under which this component has been registered with JMX.
*/
ObjectName getObjectName();
}
diff --git a/java/org/apache/catalina/LifecycleEvent.java b/java/org/apache/catalina/LifecycleEvent.java
index 3a53e97..c681586 100644
--- a/java/org/apache/catalina/LifecycleEvent.java
+++ b/java/org/apache/catalina/LifecycleEvent.java
@@ -14,19 +14,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
-
package org.apache.catalina;
-
import java.util.EventObject;
-
/**
* General event for notifying listeners of significant changes on a component
- * that implements the Lifecycle interface. In particular, this will be useful
- * on Containers, where these events replace the ContextInterceptor concept in
- * Tomcat 3.x.
+ * that implements the Lifecycle interface.
*
* @author Craig R. McClanahan
*/
@@ -35,8 +29,6 @@ public final class LifecycleEvent extends EventObject {
private static final long serialVersionUID = 1L;
- // ----------------------------------------------------------- Constructors
-
/**
* Construct a new LifecycleEvent with the specified parameters.
*
@@ -45,16 +37,12 @@ public final class LifecycleEvent extends EventObject {
* @param data Event data (if any)
*/
public LifecycleEvent(Lifecycle lifecycle, String type, Object data) {
-
super(lifecycle);
this.type = type;
this.data = data;
}
- // ----------------------------------------------------- Instance Variables
-
-
/**
* The event data associated with this event.
*/
@@ -67,37 +55,26 @@ public final class LifecycleEvent extends EventObject {
private final String type;
- // ------------------------------------------------------------- Properties
-
-
/**
- * Return the event data of this event.
+ * @return the event data of this event.
*/
public Object getData() {
-
- return (this.data);
-
+ return data;
}
/**
- * Return the Lifecycle on which this event occurred.
+ * @return the Lifecycle on which this event occurred.
*/
public Lifecycle getLifecycle() {
-
return (Lifecycle) getSource();
-
}
/**
- * Return the event type of this event.
+ * @return the event type of this event.
*/
public String getType() {
-
- return (this.type);
-
+ return this.type;
}
-
-
}
diff --git a/java/org/apache/catalina/LifecycleState.java b/java/org/apache/catalina/LifecycleState.java
index f469e51..dccb253 100644
--- a/java/org/apache/catalina/LifecycleState.java
+++ b/java/org/apache/catalina/LifecycleState.java
@@ -14,7 +14,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
package org.apache.catalina;
/**
@@ -33,25 +32,7 @@ public enum LifecycleState {
STOPPED(false, Lifecycle.AFTER_STOP_EVENT),
DESTROYING(false, Lifecycle.BEFORE_DESTROY_EVENT),
DESTROYED(false, Lifecycle.AFTER_DESTROY_EVENT),
- FAILED(false, null),
- /**
- * @deprecated Unused. Will be removed in Tomcat 8.5.x. The state transition
- * checking in {@link org.apache.catalina.util.LifecycleBase}
- * makes it impossible to use this state. The intended behaviour
- * can be obtained by setting the state to
- * {@link LifecycleState#FAILED} in
- * <code>LifecycleBase.startInternal()</code>
- */
- @Deprecated
- MUST_STOP(true, null),
- /**
- * @deprecated Unused. Will be removed in Tomcat 8.5.x. The state transition
- * checking in {@link org.apache.catalina.util.LifecycleBase}
- * makes it impossible to use this state. The intended behaviour
- * can be obtained by implementing {@link Lifecycle.SingleUse}.
- */
- @Deprecated
- MUST_DESTROY(false, null);
+ FAILED(false, null);
private final boolean available;
private final String lifecycleEvent;
@@ -69,16 +50,15 @@ public enum LifecycleState {
* <li>{@link #STARTING}</li>
* <li>{@link #STARTED}</li>
* <li>{@link #STOPPING_PREP}</li>
- * <li>{@link #MUST_STOP}</li>
* </ul>
+ *
+ * @return <code>true</code> if the component is available for use,
+ * otherwise <code>false</code>
*/
public boolean isAvailable() {
return available;
}
- /**
- *
- */
public String getLifecycleEvent() {
return lifecycleEvent;
}
diff --git a/java/org/apache/catalina/Manager.java b/java/org/apache/catalina/Manager.java
index 5566d85..4c8275f 100644
--- a/java/org/apache/catalina/Manager.java
+++ b/java/org/apache/catalina/Manager.java
@@ -14,15 +14,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
-
package org.apache.catalina;
-
import java.beans.PropertyChangeListener;
import java.io.IOException;
-
/**
* A <b>Manager</b> manages the pool of Sessions that are associated with a
* particular Context. Different Manager implementations may support
@@ -43,34 +39,12 @@ import java.io.IOException;
*/
public interface Manager {
-
// ------------------------------------------------------------- Properties
-
/**
- * Return the Container with which this Manager is associated.
+ * Get the Context with which this Manager is associated.
*
- * @deprecated Use {@link #getContext()}. This method will be removed in
- * Tomcat 9 onwards.
- */
- @Deprecated
- public Container getContainer();
-
-
- /**
- * Set the Container with which this Manager is associated.
- *
- * @param container The newly associated Container
- *
- * @deprecated Use {@link #setContext(Context)}. This method will be removed in
- * Tomcat 9 onwards.
- */
- @Deprecated
- public void setContainer(Container container);
-
-
- /**
- * Return the Context with which this Manager is associated.
+ * @return The associated Context
*/
public Context getContext();
@@ -88,56 +62,7 @@ public interface Manager {
/**
- * Return the distributable flag for the sessions supported by
- * this Manager.
- *
- * @deprecated Ignored. {@link Context#getDistributable()} always takes
- * precedence. Will be removed in Tomcat 8.5.x.
- */
- @Deprecated
- public boolean getDistributable();
-
-
- /**
- * Set the distributable flag for the sessions supported by this
- * Manager. If this flag is set, all user data objects added to
- * sessions associated with this manager must implement Serializable.
- *
- * @param distributable The new distributable flag
- *
- * @deprecated Ignored. {@link Context#getDistributable()} always takes
- * precedence. Will be removed in Tomcat 8.5.x.
- */
- @Deprecated
- public void setDistributable(boolean distributable);
-
-
- /**
- * Return the default maximum inactive interval (in seconds)
- * for Sessions created by this Manager.
- *
- * @deprecated Ignored. {@link Context#getSessionTimeout()} always takes
- * precedence. Will be removed in Tomcat 8.5.x.
- */
- @Deprecated
- public int getMaxInactiveInterval();
-
-
- /**
- * Set the default maximum inactive interval (in seconds)
- * for Sessions created by this Manager.
- *
- * @param interval The new default value
- *
- * @deprecated Ignored. {@link Context#getSessionTimeout()} always takes
- * precedence. Will be removed in Tomcat 8.5.x.
- */
- @Deprecated
- public void setMaxInactiveInterval(int interval);
-
-
- /**
- * return the session id generator
+ * @return the session id generator
*/
public SessionIdGenerator getSessionIdGenerator();
@@ -151,32 +76,6 @@ public interface Manager {
/**
- * Gets the session id length (in bytes) of Sessions created by
- * this Manager.
- *
- * @deprecated Use {@link SessionIdGenerator#getSessionIdLength()}.
- * This method will be removed in Tomcat 9 onwards.
- *
- * @return The session id length
- */
- @Deprecated
- public int getSessionIdLength();
-
-
- /**
- * Sets the session id length (in bytes) for Sessions created by this
- * Manager.
- *
- * @deprecated Use {@link SessionIdGenerator#setSessionIdLength(int)}.
- * This method will be removed in Tomcat 9 onwards.
- *
- * @param idLength The session id length
- */
- @Deprecated
- public void setSessionIdLength(int idLength);
-
-
- /**
* Returns the total number of sessions created by this manager.
*
* @return Total number of sessions created by this manager.
@@ -291,9 +190,10 @@ public interface Manager {
* @return The current rate (in sessions per minute) of session expiration
*/
public int getSessionExpireRate();
- // --------------------------------------------------------- Public Methods
+ // --------------------------------------------------------- Public Methods
+
/**
* Add this Session to the set of active Sessions for this Manager.
*
@@ -332,6 +232,8 @@ public interface Manager {
* Get a session from the recycled ones or create a new empty one.
* The PersistentManager manager does not need to create session data
* because it reads it from the Store.
+ *
+ * @return An empty Session object
*/
public Session createEmptySession();
@@ -349,6 +251,9 @@ public interface Manager {
* method of the returned session.
* @exception IllegalStateException if a new session cannot be
* instantiated for any reason
+ *
+ * @return An empty Session object with the given ID or a newly created
+ * session ID if none was specified
*/
public Session createSession(String sessionId);
@@ -363,6 +268,9 @@ public interface Manager {
* instantiated for any reason
* @exception IOException if an input/output error occurs while
* processing this request
+ *
+ * @return the request session or {@code null} if a session with the
+ * requested ID could not be found
*/
public Session findSession(String id) throws IOException;
@@ -370,6 +278,8 @@ public interface Manager {
/**
* Return the set of active Sessions associated with this Manager.
* If this Manager has no active Sessions, a zero-length array is returned.
+ *
+ * @return All the currently active sessions managed by this manager
*/
public Session[] findSessions();
@@ -420,13 +330,13 @@ public interface Manager {
*/
public void unload() throws IOException;
- /**
- * This method will be invoked by the context/container on a periodic
- * basis and allows the manager to implement
- * a method that executes periodic tasks, such as expiring sessions etc.
- */
- public void backgroundProcess();
+ /**
+ * This method will be invoked by the context/container on a periodic
+ * basis and allows the manager to implement
+ * a method that executes periodic tasks, such as expiring sessions etc.
+ */
+ public void backgroundProcess();
/**
diff --git a/java/org/apache/catalina/Realm.java b/java/org/apache/catalina/Realm.java
index 6fb9b7c..0960028 100644
--- a/java/org/apache/catalina/Realm.java
+++ b/java/org/apache/catalina/Realm.java
@@ -223,4 +223,12 @@ public interface Realm {
* @param listener The listener to remove
*/
public void removePropertyChangeListener(PropertyChangeListener listener);
+
+
+ /**
+ * Return roles associated with given principal
+ * @param principal the {@link Principal} to get the roles for.
+ * @return principal roles
+ */
+ public String[] getRoles(Principal principal);
}
diff --git a/java/org/apache/catalina/Service.java b/java/org/apache/catalina/Service.java
index 3e50d2a..dcb87f6 100644
--- a/java/org/apache/catalina/Service.java
+++ b/java/org/apache/catalina/Service.java
@@ -38,25 +38,10 @@ public interface Service extends Lifecycle {
// ------------------------------------------------------------- Properties
/**
- * @return the <code>Container</code> that handles requests for all
+ * @return the <code>Engine</code> that handles requests for all
* <code>Connectors</code> associated with this Service.
- *
- * @deprecated Return value will be narrowed to Engine in Tomcat 9.
- */
- @Deprecated
- public Container getContainer();
-
- /**
- * Set the <code>Container</code> that handles requests for all
- * <code>Connectors</code> associated with this Service.
- *
- * @param container The new Container
- *
- * @deprecated Use {@link #setContainer(Engine)} Will be removed in Tomcat
- * 9.
*/
- @Deprecated
- public void setContainer(Container container);
+ public Engine getContainer();
/**
* Set the <code>Engine</code> that handles requests for all
diff --git a/java/org/apache/catalina/Session.java b/java/org/apache/catalina/Session.java
index 59af20b..f551ada 100644
--- a/java/org/apache/catalina/Session.java
+++ b/java/org/apache/catalina/Session.java
@@ -66,7 +66,7 @@ public interface Session {
/**
- * Return the authentication type used to authenticate our cached
+ * @return the authentication type used to authenticate our cached
* Principal, if any.
*/
public String getAuthType();
@@ -82,13 +82,13 @@ public interface Session {
/**
- * Return the creation time for this session.
+ * @return the creation time for this session.
*/
public long getCreationTime();
/**
- * Return the creation time for this session, bypassing the session validity
+ * @return the creation time for this session, bypassing the session validity
* checks.
*/
public long getCreationTimeInternal();
@@ -104,13 +104,13 @@ public interface Session {
/**
- * Return the session identifier for this session.
+ * @return the session identifier for this session.
*/
public String getId();
/**
- * Return the session identifier for this session.
+ * @return the session identifier for this session.
*/
public String getIdInternal();
@@ -136,7 +136,7 @@ public interface Session {
/**
- * Return the last time the client sent a request associated with this
+ * @return the last time the client sent a request associated with this
* session, as the number of milliseconds since midnight, January 1, 1970
* GMT. Actions that your application takes, such as getting or setting
* a value associated with the session, do not affect the access time.
@@ -145,13 +145,13 @@ public interface Session {
public long getThisAccessedTime();
/**
- * Return the last client access time without invalidation check
+ * @return the last client access time without invalidation check
* @see #getThisAccessedTime()
*/
public long getThisAccessedTimeInternal();
/**
- * Return the last time the client sent a request associated with this
+ * @return the last time the client sent a request associated with this
* session, as the number of milliseconds since midnight, January 1, 1970
* GMT. Actions that your application takes, such as getting or setting
* a value associated with the session, do not affect the access time.
@@ -160,24 +160,24 @@ public interface Session {
public long getLastAccessedTime();
/**
- * Return the last client access time without invalidation check
+ * @return the last client access time without invalidation check
* @see #getLastAccessedTime()
*/
public long getLastAccessedTimeInternal();
/**
- * Return the idle time (in milliseconds) from last client access time.
+ * @return the idle time (in milliseconds) from last client access time.
*/
public long getIdleTime();
/**
- * Return the idle time from last client access time without invalidation check
+ * @return the idle time from last client access time without invalidation check
* @see #getIdleTime()
*/
public long getIdleTimeInternal();
/**
- * Return the Manager within which this Session is valid.
+ * @return the Manager within which this Session is valid.
*/
public Manager getManager();
@@ -191,7 +191,7 @@ public interface Session {
/**
- * Return the maximum time interval, in seconds, between client requests
+ * @return the maximum time interval, in seconds, between client requests
* before the servlet container will invalidate the session. A negative
* time indicates that the session should never time out.
*/
@@ -217,7 +217,7 @@ public interface Session {
/**
- * Return the authenticated Principal that is associated with this Session.
+ * @return the authenticated Principal that is associated with this Session.
* This provides an <code>Authenticator</code> with a means to cache a
* previously authenticated Principal, and avoid potentially expensive
* <code>Realm.authenticate()</code> calls on every request. If there
@@ -238,7 +238,7 @@ public interface Session {
/**
- * Return the <code>HttpSession</code> for which this object
+ * @return the <code>HttpSession</code> for which this object
* is the facade.
*/
public HttpSession getSession();
@@ -253,7 +253,7 @@ public interface Session {
/**
- * Return the <code>isValid</code> flag for this session.
+ * @return <code>true</code> if the session is still valid
*/
public boolean isValid();
@@ -271,6 +271,9 @@ public interface Session {
/**
* Add a session event listener to this component.
+ *
+ * @param listener the SessionListener instance that should be notified
+ * for session events
*/
public void addSessionListener(SessionListener listener);
@@ -289,7 +292,7 @@ public interface Session {
/**
- * Return the object bound with the specified name to the internal notes
+ * @return the object bound with the specified name to the internal notes
* for this session, or <code>null</code> if no such binding exists.
*
* @param name Name of the note to be returned
@@ -298,7 +301,7 @@ public interface Session {
/**
- * Return an Iterator containing the String names of all notes bindings
+ * @return an Iterator containing the String names of all notes bindings
* that exist for this session.
*/
public Iterator<String> getNoteNames();
@@ -322,6 +325,9 @@ public interface Session {
/**
* Remove a session event listener from this component.
+ *
+ * @param listener remove the sesion listener, which will no longer be
+ * notified
*/
public void removeSessionListener(SessionListener listener);
diff --git a/java/org/apache/catalina/SessionEvent.java b/java/org/apache/catalina/SessionEvent.java
index 432a761..7f77a86 100644
--- a/java/org/apache/catalina/SessionEvent.java
+++ b/java/org/apache/catalina/SessionEvent.java
@@ -68,7 +68,7 @@ public final class SessionEvent extends EventObject {
/**
- * Return the event data of this event.
+ * @return the event data of this event.
*/
public Object getData() {
@@ -78,7 +78,7 @@ public final class SessionEvent extends EventObject {
/**
- * Return the Session on which this event occurred.
+ * @return the Session on which this event occurred.
*/
public Session getSession() {
@@ -88,7 +88,7 @@ public final class SessionEvent extends EventObject {
/**
- * Return the event type of this event.
+ * @return the event type of this event.
*/
public String getType() {
@@ -97,9 +97,6 @@ public final class SessionEvent extends EventObject {
}
- /**
- * Return a string representation of this event.
- */
@Override
public String toString() {
diff --git a/java/org/apache/catalina/SessionIdGenerator.java b/java/org/apache/catalina/SessionIdGenerator.java
index b980d76..b11a4c3 100644
--- a/java/org/apache/catalina/SessionIdGenerator.java
+++ b/java/org/apache/catalina/SessionIdGenerator.java
@@ -19,7 +19,7 @@ package org.apache.catalina;
public interface SessionIdGenerator {
/**
- * Return the node identifier associated with this node which will be
+ * @return the node identifier associated with this node which will be
* included in the generated session ID.
*/
public String getJvmRoute();
@@ -33,7 +33,7 @@ public interface SessionIdGenerator {
public void setJvmRoute(String jvmRoute);
/**
- * Return the number of bytes for a session ID
+ * @return the number of bytes for a session ID
*/
public int getSessionIdLength();
@@ -46,6 +46,8 @@ public interface SessionIdGenerator {
/**
* Generate and return a new session identifier.
+ *
+ * @return the newly generated session id
*/
public String generateSessionId();
@@ -53,6 +55,7 @@ public interface SessionIdGenerator {
* Generate and return a new session identifier.
*
* @param route node identifier to include in generated id
+ * @return the newly generated session id
*/
public String generateSessionId(String route);
}
diff --git a/java/org/apache/catalina/Store.java b/java/org/apache/catalina/Store.java
index e9f7f03..63d57b6 100644
--- a/java/org/apache/catalina/Store.java
+++ b/java/org/apache/catalina/Store.java
@@ -37,7 +37,7 @@ public interface Store {
// ------------------------------------------------------------- Properties
/**
- * Return the Manager instance associated with this Store.
+ * @return the Manager instance associated with this Store.
*/
public Manager getManager();
@@ -51,7 +51,7 @@ public interface Store {
/**
- * Return the number of Sessions present in this Store.
+ * @return the number of Sessions present in this Store.
*
* @exception IOException if an input/output error occurs
*/
@@ -70,7 +70,7 @@ public interface Store {
/**
- * Return an array containing the session identifiers of all Sessions
+ * @return an array containing the session identifiers of all Sessions
* currently saved in this Store. If there are no such Sessions, a
* zero-length array is returned.
*
@@ -88,6 +88,7 @@ public interface Store {
*
* @exception ClassNotFoundException if a deserialization error occurs
* @exception IOException if an input/output error occurs
+ * @return the loaded Session instance
*/
public Session load(String id)
throws ClassNotFoundException, IOException;
@@ -107,6 +108,8 @@ public interface Store {
/**
* Remove all Sessions from this Store.
+ *
+ * @exception IOException if an input/output error occurs
*/
public void clear() throws IOException;
diff --git a/java/org/apache/catalina/StoreManager.java b/java/org/apache/catalina/StoreManager.java
index 83ceb69..fbc05b8 100644
--- a/java/org/apache/catalina/StoreManager.java
+++ b/java/org/apache/catalina/StoreManager.java
@@ -23,7 +23,7 @@ package org.apache.catalina;
public interface StoreManager extends DistributedManager {
/**
- * Return the Store object which manages persistent Session
+ * @return the Store object which manages persistent Session
* storage for this Manager.
*/
Store getStore();
diff --git a/java/org/apache/catalina/TomcatPrincipal.java b/java/org/apache/catalina/TomcatPrincipal.java
index 8d0c07c..4d5dad6 100644
--- a/java/org/apache/catalina/TomcatPrincipal.java
+++ b/java/org/apache/catalina/TomcatPrincipal.java
@@ -27,12 +27,12 @@ import org.ietf.jgss.GSSCredential;
public interface TomcatPrincipal extends Principal {
/**
- * The authenticated Principal to be exposed to applications.
+ * @return The authenticated Principal to be exposed to applications.
*/
Principal getUserPrincipal();
/**
- * The user's delegated credentials.
+ * @return The user's delegated credentials.
*/
GSSCredential getGssCredential();
diff --git a/java/org/apache/catalina/User.java b/java/org/apache/catalina/User.java
index 67868e8..e50fd8f 100644
--- a/java/org/apache/catalina/User.java
+++ b/java/org/apache/catalina/User.java
@@ -39,7 +39,7 @@ public interface User extends Principal {
/**
- * Return the full name of this user.
+ * @return the full name of this user.
*/
public String getFullName();
@@ -53,13 +53,13 @@ public interface User extends Principal {
/**
- * Return the set of {@link Group}s to which this user belongs.
+ * @return the set of {@link Group}s to which this user belongs.
*/
public Iterator<Group> getGroups();
/**
- * Return the logon password of this user, optionally prefixed with the
+ * @return the logon password of this user, optionally prefixed with the
* identifier of an encoding scheme surrounded by curly braces, such as
* <code>{md5}xxxxx</code>.
*/
@@ -77,19 +77,19 @@ public interface User extends Principal {
/**
- * Return the set of {@link Role}s assigned specifically to this user.
+ * @return the set of {@link Role}s assigned specifically to this user.
*/
public Iterator<Role> getRoles();
/**
- * Return the {@link UserDatabase} within which this User is defined.
+ * @return the {@link UserDatabase} within which this User is defined.
*/
public UserDatabase getUserDatabase();
/**
- * Return the logon username of this user, which must be unique
+ * @return the logon username of this user, which must be unique
* within the scope of a {@link UserDatabase}.
*/
public String getUsername();
@@ -127,6 +127,7 @@ public interface User extends Principal {
* Is this user in the specified {@link Group}?
*
* @param group The group to check
+ * @return <code>true</code> if the user is in the specified group
*/
public boolean isInGroup(Group group);
@@ -137,6 +138,7 @@ public interface User extends Principal {
* {@link Group} membership.
*
* @param role The role to check
+ * @return <code>true</code> if the user has the specified role
*/
public boolean isInRole(Role role);
diff --git a/java/org/apache/catalina/UserDatabase.java b/java/org/apache/catalina/UserDatabase.java
index 33f1b77..9a1a932 100644
--- a/java/org/apache/catalina/UserDatabase.java
+++ b/java/org/apache/catalina/UserDatabase.java
@@ -38,25 +38,25 @@ public interface UserDatabase {
/**
- * Return the set of {@link Group}s defined in this user database.
+ * @return the set of {@link Group}s defined in this user database.
*/
public Iterator<Group> getGroups();
/**
- * Return the unique global identifier of this user database.
+ * @return the unique global identifier of this user database.
*/
public String getId();
/**
- * Return the set of {@link Role}s defined in this user database.
+ * @return the set of {@link Role}s defined in this user database.
*/
public Iterator<Role> getRoles();
/**
- * Return the set of {@link User}s defined in this user database.
+ * @return the set of {@link User}s defined in this user database.
*/
public Iterator<User> getUsers();
@@ -77,6 +77,7 @@ public interface UserDatabase {
*
* @param groupname The group name of the new group (must be unique)
* @param description The description of this group
+ * @return The new group
*/
public Group createGroup(String groupname, String description);
@@ -86,6 +87,7 @@ public interface UserDatabase {
*
* @param rolename The role name of the new role (must be unique)
* @param description The description of this role
+ * @return The new role
*/
public Role createRole(String rolename, String description);
@@ -96,13 +98,14 @@ public interface UserDatabase {
* @param username The logon username of the new user (must be unique)
* @param password The logon password of the new user
* @param fullName The full name of the new user
+ * @return The new user
*/
public User createUser(String username, String password,
String fullName);
/**
- * Return the {@link Group} with the specified group name, if any;
+ * @return the {@link Group} with the specified group name, if any;
* otherwise return <code>null</code>.
*
* @param groupname Name of the group to return
@@ -111,7 +114,7 @@ public interface UserDatabase {
/**
- * Return the {@link Role} with the specified role name, if any;
+ * @return the {@link Role} with the specified role name, if any;
* otherwise return <code>null</code>.
*
* @param rolename Name of the role to return
@@ -120,7 +123,7 @@ public interface UserDatabase {
/**
- * Return the {@link User} with the specified user name, if any;
+ * @return the {@link User} with the specified user name, if any;
* otherwise return <code>null</code>.
*
* @param username Name of the user to return
diff --git a/java/org/apache/catalina/Valve.java b/java/org/apache/catalina/Valve.java
index ac0af85..32ccade 100644
--- a/java/org/apache/catalina/Valve.java
+++ b/java/org/apache/catalina/Valve.java
@@ -14,20 +14,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
-
package org.apache.catalina;
-
import java.io.IOException;
import javax.servlet.ServletException;
-import org.apache.catalina.comet.CometEvent;
import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.Response;
-
/**
* <p>A <b>Valve</b> is a request processing component associated with a
* particular Container. A series of Valves are generally associated with
@@ -48,7 +43,7 @@ public interface Valve {
//-------------------------------------------------------------- Properties
/**
- * Return the next Valve in the pipeline containing this Valve, if any.
+ * @return the next Valve in the pipeline containing this Valve, if any.
*/
public Valve getNext();
@@ -123,22 +118,5 @@ public interface Valve {
throws IOException, ServletException;
- /**
- * Process a Comet event.
- *
- * @param request The servlet request to be processed
- * @param response The servlet response to be created
- *
- * @exception IOException if an input/output error occurs, or is thrown
- * by a subsequently invoked Valve, Filter, or Servlet
- * @exception ServletException if a servlet error occurs, or is thrown
- * by a subsequently invoked Valve, Filter, or Servlet
- */
- public void event(Request request, Response response, CometEvent event)
- throws IOException, ServletException;
-
-
public boolean isAsyncSupported();
-
-
}
diff --git a/java/org/apache/catalina/WebResource.java b/java/org/apache/catalina/WebResource.java
index 7f5465c..2852a1b 100644
--- a/java/org/apache/catalina/WebResource.java
+++ b/java/org/apache/catalina/WebResource.java
@@ -27,18 +27,18 @@ import java.util.jar.Manifest;
*/
public interface WebResource {
/**
- * See {@link java.io.File#lastModified()}.
+ * @return {@link java.io.File#lastModified()}.
*/
long getLastModified();
/**
- * Return the last modified time of this resource in the correct format for
+ * @return the last modified time of this resource in the correct format for
* the HTTP Last-Modified header as specified by RFC 2616.
*/
String getLastModifiedHttp();
/**
- * See {@link java.io.File#exists()}.
+ * @return {@link java.io.File#exists()}.
*/
boolean exists();
@@ -48,46 +48,48 @@ public interface WebResource {
* additional {@link WebResourceSet}. For example, if an external
* directory is mapped to /WEB-INF/lib in an otherwise empty web
* application, /WEB-INF will be represented as a virtual resource.
+ *
+ * @return <code>true</code> for a virtual resource
*/
boolean isVirtual();
/**
- * See {@link java.io.File#isDirectory()}.
+ * @return {@link java.io.File#isDirectory()}.
*/
boolean isDirectory();
/**
- * See {@link java.io.File#isFile()}.
+ * @return {@link java.io.File#isFile()}.
*/
boolean isFile();
/**
- * See {@link java.io.File#delete()}.
+ * @return {@link java.io.File#delete()}.
*/
boolean delete();
/**
- * See {@link java.io.File#getName()}.
+ * @return {@link java.io.File#getName()}.
*/
String getName();
/**
- * See {@link java.io.File#length()}.
+ * @return {@link java.io.File#length()}.
*/
long getContentLength();
/**
- * See {@link java.io.File#getCanonicalPath()}.
+ * @return {@link java.io.File#getCanonicalPath()}.
*/
String getCanonicalPath();
/**
- * See {@link java.io.File#canRead()}.
+ * @return {@link java.io.File#canRead()}.
*/
boolean canRead();
/**
- * The path of this resource relative to the web application root. If the
+ * @return The path of this resource relative to the web application root. If the
* resource is a directory, the return value will end in '/'.
*/
String getWebappPath();
@@ -102,11 +104,13 @@ public interface WebResource {
/**
* Set the MIME type for this Resource.
+ *
+ * @param mimeType The mime type that will be associated with the resource
*/
void setMimeType(String mimeType);
/**
- * Get the MIME type for this Resource.
+ * @return the MIME type for this Resource.
*/
String getMimeType();
@@ -120,37 +124,37 @@ public interface WebResource {
InputStream getInputStream();
/**
- * Obtain the cached binary content of this resource.
+ * @return the cached binary content of this resource.
*/
byte[] getContent();
/**
- * The time the file was created. If not available, the result of
+ * @return The time the file was created. If not available, the result of
* {@link #getLastModified()} will be returned.
*/
long getCreation();
/**
- * Obtain a URL to access the resource or <code>null</code> if no such URL
+ * @return a URL to access the resource or <code>null</code> if no such URL
* is available or if the resource does not exist.
*/
URL getURL();
/**
- * Get code base for this resource that will be used when looking up the
+ * @return the code base for this resource that will be used when looking up the
* assigned permissions for the code base in the security policy file when
* running under a security manager.
*/
URL getCodeBase();
/**
- * Obtain a reference to the WebResourceRoot of which this WebResource is a
+ * @return a reference to the WebResourceRoot of which this WebResource is a
* part.
*/
WebResourceRoot getWebResourceRoot();
/**
- * Obtain the certificates that were used to sign this resource to verify
+ * @return the certificates that were used to sign this resource to verify
* it or @null if none.
*
* @see java.util.jar.JarEntry#getCertificates()
@@ -158,7 +162,7 @@ public interface WebResource {
Certificate[] getCertificates();
/**
- * Obtain the manifest associated with this resource or @null if none.
+ * @return the manifest associated with this resource or @null if none.
*
* @see java.util.jar.JarFile#getManifest()
*/
diff --git a/java/org/apache/catalina/WebResourceRoot.java b/java/org/apache/catalina/WebResourceRoot.java
index 2873b80..5a023c7 100644
--- a/java/org/apache/catalina/WebResourceRoot.java
+++ b/java/org/apache/catalina/WebResourceRoot.java
@@ -247,11 +247,13 @@ public interface WebResourceRoot extends Lifecycle {
/**
* Adds the provided WebResourceSet to this web application as a 'Pre'
* resource.
+ *
+ * @param webResourceSet the resource set to use
*/
void addPreResources(WebResourceSet webResourceSet);
/**
- * Get the list of WebResourceSet configured to this web application
+ * @return the list of WebResourceSet configured to this web application
* as a 'Pre' resource.
*/
WebResourceSet[] getPreResources();
@@ -259,11 +261,13 @@ public interface WebResourceRoot extends Lifecycle {
/**
* Adds the provided WebResourceSet to this web application as a 'Jar'
* resource.
+ *
+ * @param webResourceSet the resource set to use
*/
void addJarResources(WebResourceSet webResourceSet);
/**
- * Get the list of WebResourceSet configured to this web application
+ * @return the list of WebResourceSet configured to this web application
* as a 'Jar' resource.
*/
WebResourceSet[] getJarResources();
@@ -271,22 +275,26 @@ public interface WebResourceRoot extends Lifecycle {
/**
* Adds the provided WebResourceSet to this web application as a 'Post'
* resource.
+ *
+ * @param webResourceSet the resource set to use
*/
void addPostResources(WebResourceSet webResourceSet);
/**
- * Get the list of WebResourceSet configured to this web application
+ * @return the list of WebResourceSet configured to this web application
* as a 'Post' resource.
*/
WebResourceSet[] getPostResources();
/**
- * Obtain the web application this WebResourceRoot is associated with.
+ * @return the web application this WebResourceRoot is associated with.
*/
Context getContext();
/**
* Set the web application this WebResourceRoot is associated with.
+ *
+ * @param context the associated context
*/
void setContext(Context context);
@@ -313,7 +321,7 @@ public interface WebResourceRoot extends Lifecycle {
void setCachingAllowed(boolean cachingAllowed);
/**
- * Get whether or not caching is permitted for this web application.
+ * @return <code>true</code> if caching is permitted for this web application.
*/
boolean isCachingAllowed();
@@ -397,12 +405,21 @@ public interface WebResourceRoot extends Lifecycle {
*/
void backgroundProcess();
+ /**
+ * Add a specified resource to track to be able to later release
+ * resources on stop.
+ * @param trackedResource the resource that will be tracked
+ */
void registerTrackedResource(TrackedWebResource trackedResource);
+ /**
+ * Stop tracking specified resource, once it no longer needs to free resources.
+ * @param trackedResource the resource that was tracked
+ */
void deregisterTrackedResource(TrackedWebResource trackedResource);
/**
- * Obtain the set of {@link WebResourceSet#getBaseUrl()} for all
+ * @return the set of {@link WebResourceSet#getBaseUrl()} for all
* {@link WebResourceSet}s used by this root.
*/
List<URL> getBaseUrls();
diff --git a/java/org/apache/catalina/WebResourceSet.java b/java/org/apache/catalina/WebResourceSet.java
index 7e9d2f4..9d71cd0 100644
--- a/java/org/apache/catalina/WebResourceSet.java
+++ b/java/org/apache/catalina/WebResourceSet.java
@@ -122,6 +122,8 @@ public interface WebResourceSet extends Lifecycle {
* Obtain the base URL for this set of resources. One of the uses of this is
* to grant read permissions to the resources when running under a security
* manager.
+ *
+ * @return The base URL for this set of resources
*/
URL getBaseUrl();
diff --git a/java/org/apache/catalina/Wrapper.java b/java/org/apache/catalina/Wrapper.java
index e7ff924..4f366dd 100644
--- a/java/org/apache/catalina/Wrapper.java
+++ b/java/org/apache/catalina/Wrapper.java
@@ -62,7 +62,7 @@ public interface Wrapper extends Container {
/**
- * Return the available date/time for this servlet, in milliseconds since
+ * @return the available date/time for this servlet, in milliseconds since
* the epoch. If this date/time is in the future, any request for this
* servlet will return an SC_SERVICE_UNAVAILABLE error. If it is zero,
* the servlet is currently available. A value equal to Long.MAX_VALUE
@@ -83,7 +83,7 @@ public interface Wrapper extends Container {
/**
- * Return the load-on-startup order value (negative value means
+ * @return the load-on-startup order value (negative value means
* load on first call).
*/
public int getLoadOnStartup();
@@ -99,7 +99,7 @@ public interface Wrapper extends Container {
/**
- * Return the run-as identity for this servlet.
+ * @return the run-as identity for this servlet.
*/
public String getRunAs();
@@ -113,7 +113,7 @@ public interface Wrapper extends Container {
/**
- * Return the fully qualified servlet class name for this servlet.
+ * @return the fully qualified servlet class name for this servlet.
*/
public String getServletClass();
@@ -136,25 +136,27 @@ public interface Wrapper extends Container {
* @return Array of names of the methods supported by the underlying
* servlet
*
- * @throws ServletException If the target servlet can not be loaded
+ * @throws ServletException If the target servlet cannot be loaded
*/
public String[] getServletMethods() throws ServletException;
/**
- * Is this servlet currently unavailable?
+ * @return <code>true</code> if this Servlet is currently unavailable.
*/
public boolean isUnavailable();
/**
- * Return the associated servlet instance.
+ * @return the associated Servlet instance.
*/
public Servlet getServlet();
/**
- * Set the associated servlet instance
+ * Set the associated Servlet instance
+ *
+ * @param servlet The associated Servlet
*/
public void setServlet(Servlet servlet);
@@ -171,17 +173,6 @@ public interface Wrapper extends Container {
/**
- * Add a new listener interested in InstanceEvents.
- *
- * @param listener The new listener
- *
- * @deprecated Will be removed in 8.5.x onwards
- */
- @Deprecated
- public void addInstanceListener(InstanceListener listener);
-
-
- /**
* Add a mapping associated with the Wrapper.
*
* @param mapping The new wrapper mapping
@@ -201,16 +192,17 @@ public interface Wrapper extends Container {
/**
* Allocate an initialized instance of this Servlet that is ready to have
- * its <code>service()</code> method called. If the servlet class does
+ * its <code>service()</code> method called. If the Servlet class does
* not implement <code>SingleThreadModel</code>, the (only) initialized
- * instance may be returned immediately. If the servlet class implements
+ * instance may be returned immediately. If the Servlet class implements
* <code>SingleThreadModel</code>, the Wrapper implementation must ensure
* that this instance is not allocated again until it is deallocated by a
* call to <code>deallocate()</code>.
*
- * @exception ServletException if the servlet init() method threw
+ * @exception ServletException if the Servlet init() method threw
* an exception
* @exception ServletException if a loading error occurs
+ * @return a new Servlet instance
*/
public Servlet allocate() throws ServletException;
@@ -228,7 +220,7 @@ public interface Wrapper extends Container {
/**
- * Return the value for the specified initialization parameter name,
+ * @return the value for the specified initialization parameter name,
* if any; otherwise return <code>null</code>.
*
* @param name Name of the requested initialization parameter
@@ -237,20 +229,20 @@ public interface Wrapper extends Container {
/**
- * Return the names of all defined initialization parameters for this
+ * @return the names of all defined initialization parameters for this
* servlet.
*/
public String[] findInitParameters();
/**
- * Return the mappings associated with this wrapper.
+ * @return the mappings associated with this wrapper.
*/
public String[] findMappings();
/**
- * Return the security role link for the specified security role
+ * @return the security role link for the specified security role
* reference name, if any; otherwise return <code>null</code>.
*
* @param name Security role reference used within this servlet
@@ -259,7 +251,7 @@ public interface Wrapper extends Container {
/**
- * Return the set of security role reference names associated with
+ * @return the set of security role reference names associated with
* this servlet, if any; otherwise return a zero-length array.
*/
public String[] findSecurityReferences();
@@ -272,20 +264,19 @@ public interface Wrapper extends Container {
/**
- * Load and initialize an instance of this servlet, if there is not already
+ * Load and initialize an instance of this Servlet, if there is not already
* at least one initialized instance. This can be used, for example, to
- * load servlets that are marked in the deployment descriptor to be loaded
+ * load Servlets that are marked in the deployment descriptor to be loaded
* at server startup time.
*
- * @exception ServletException if the servlet init() method threw
- * an exception
- * @exception ServletException if some other loading problem occurs
+ * @exception ServletException if the Servlet init() method threw
+ * an exception or if some other loading problem occurs
*/
public void load() throws ServletException;
/**
- * Remove the specified initialization parameter from this servlet.
+ * Remove the specified initialization parameter from this Servlet.
*
* @param name Name of the initialization parameter to remove
*/
@@ -293,17 +284,6 @@ public interface Wrapper extends Container {
/**
- * Remove a listener no longer interested in InstanceEvents.
- *
- * @param listener The listener to remove
- *
- * @deprecated Will be removed in 8.5.x onwards
- */
- @Deprecated
- public void removeInstanceListener(InstanceListener listener);
-
-
- /**
* Remove a mapping associated with the wrapper.
*
* @param mapping The pattern to remove
@@ -320,11 +300,11 @@ public interface Wrapper extends Container {
/**
- * Process an UnavailableException, marking this servlet as unavailable
+ * Process an UnavailableException, marking this Servlet as unavailable
* for the specified amount of time.
*
* @param unavailable The exception that occurred, or <code>null</code>
- * to mark this servlet as permanently unavailable
+ * to mark this Servlet as permanently unavailable
*/
public void unavailable(UnavailableException unavailable);
@@ -342,7 +322,7 @@ public interface Wrapper extends Container {
/**
- * Get the multi-part configuration for the associated servlet. If no
+ * @return the multi-part configuration for the associated Servlet. If no
* multi-part configuration has been defined, then <code>null</code> will be
* returned.
*/
@@ -350,8 +330,10 @@ public interface Wrapper extends Container {
/**
- * Set the multi-part configuration for the associated servlet. To clear the
+ * Set the multi-part configuration for the associated Servlet. To clear the
* multi-part configuration specify <code>null</code> as the new value.
+ *
+ * @param multipartConfig The configuration associated with the Servlet
*/
public void setMultipartConfigElement(
MultipartConfigElement multipartConfig);
@@ -359,21 +341,29 @@ public interface Wrapper extends Container {
/**
* Does the associated Servlet support async processing? Defaults to
* <code>false</code>.
+ *
+ * @return <code>true</code> if the Servlet supports async
*/
public boolean isAsyncSupported();
/**
- * Set the async support for the associated servlet.
+ * Set the async support for the associated Servlet.
+ *
+ * @param asyncSupport the new value
*/
public void setAsyncSupported(boolean asyncSupport);
/**
* Is the associated Servlet enabled? Defaults to <code>true</code>.
+ *
+ * @return <code>true</code> if the Servlet is enabled
*/
public boolean isEnabled();
/**
* Sets the enabled attribute for the associated servlet.
+ *
+ * @param enabled the new value
*/
public void setEnabled(boolean enabled);
@@ -390,16 +380,22 @@ public interface Wrapper extends Container {
* Scan for (if necessary) and process (if found) the
* {@link javax.servlet.annotation.ServletSecurity} annotations for the
* Servlet associated with this wrapper.
+ *
+ * @throws ServletException if an annotation scanning error occurs
*/
public void servletSecurityAnnotationScan() throws ServletException;
/**
* Is the Servlet overridable by a ServletContainerInitializer?
+ *
+ * @return <code>true</code> if the Servlet can be overridden in a ServletContainerInitializer
*/
public boolean isOverridable();
/**
* Sets the overridable attribute for this Servlet.
+ *
+ * @param overridable the new value
*/
public void setOverridable(boolean overridable);
}
diff --git a/java/org/apache/catalina/ant/AbstractCatalinaCommandTask.java b/java/org/apache/catalina/ant/AbstractCatalinaCommandTask.java
index bc02d47..51b3449 100644
--- a/java/org/apache/catalina/ant/AbstractCatalinaCommandTask.java
+++ b/java/org/apache/catalina/ant/AbstractCatalinaCommandTask.java
@@ -57,6 +57,8 @@ public abstract class AbstractCatalinaCommandTask extends
*
* @param command Command to be executed
*
+ * @return The generated query string
+ *
* @exception BuildException if an error occurs
*/
public StringBuilder createQueryString(String command) throws BuildException {
diff --git a/java/org/apache/catalina/ant/BaseRedirectorHelperTask.java b/java/org/apache/catalina/ant/BaseRedirectorHelperTask.java
index a54a06b..76030ef 100644
--- a/java/org/apache/catalina/ant/BaseRedirectorHelperTask.java
+++ b/java/org/apache/catalina/ant/BaseRedirectorHelperTask.java
@@ -102,14 +102,18 @@ public abstract class BaseRedirectorHelperTask extends Task {
* Whether to fail (with a BuildException) if
* ManagerServlet returns an error. The default behavior is
* to do so.
+ *
+ * @param fail The new value of failonerror
*/
public void setFailonerror(boolean fail) {
failOnError = fail;
}
/**
- * Returns the value of the failOnError
- * property.
+ * Returns the value of the failOnError property.
+ *
+ * @return <code>true</code> if the task should will if an error occurs,
+ * otherwise <code>false</code>
*/
public boolean isFailOnError() {
return failOnError;
@@ -349,6 +353,7 @@ public abstract class BaseRedirectorHelperTask extends Task {
* priorities to output stream.
*
* @param output The output to log. Should not be <code>null</code>.
+ * @param priority The priority level that should be used
*/
protected void handleOutput(String output, int priority) {
if (priority == Project.MSG_ERR) {
diff --git a/java/org/apache/catalina/ant/FindLeaksTask.java b/java/org/apache/catalina/ant/FindLeaksTask.java
index 2db6aad..cbd394b 100644
--- a/java/org/apache/catalina/ant/FindLeaksTask.java
+++ b/java/org/apache/catalina/ant/FindLeaksTask.java
@@ -29,6 +29,8 @@ public class FindLeaksTask extends AbstractCatalinaTask {
/**
* Sets the statusLine parameter that controls if the response includes a
* status line or not.
+ *
+ * @param statusLine <code>true</code> if the status line should be included
*/
public void setStatusLine(boolean statusLine) {
this.statusLine = statusLine;
@@ -37,6 +39,9 @@ public class FindLeaksTask extends AbstractCatalinaTask {
/**
* Returns the statusLine parameter that controls if the response includes a
* status line or not.
+ *
+ * @return <code>true</code> if the status line should be included,
+ * otherwise <code>false</code>
*/
public boolean getStatusLine() {
return statusLine;
diff --git a/java/org/apache/catalina/ant/ValidatorTask.java b/java/org/apache/catalina/ant/ValidatorTask.java
index 97b3fc3..3fa75cc 100644
--- a/java/org/apache/catalina/ant/ValidatorTask.java
+++ b/java/org/apache/catalina/ant/ValidatorTask.java
@@ -80,7 +80,7 @@ public class ValidatorTask extends BaseRedirectorHelperTask {
}
File file = new File(path, Constants.ApplicationWebXml);
- if ((!file.exists()) || (!file.canRead())) {
+ if (!file.canRead()) {
throw new BuildException("Cannot find web.xml");
}
diff --git a/java/org/apache/catalina/ant/jmx/JMXAccessorConditionBase.java b/java/org/apache/catalina/ant/jmx/JMXAccessorConditionBase.java
index 8348213..a13fe90 100644
--- a/java/org/apache/catalina/ant/jmx/JMXAccessorConditionBase.java
+++ b/java/org/apache/catalina/ant/jmx/JMXAccessorConditionBase.java
@@ -150,8 +150,8 @@ public abstract class JMXAccessorConditionBase extends ProjectComponent implemen
* from jmxOpen Task).
*
* @return active JMXConnection
- * @throws MalformedURLException
- * @throws IOException
+ * @throws MalformedURLException Invalid URL for JMX server
+ * @throws IOException Connection error
*/
protected MBeanServerConnection getJMXConnection()
throws MalformedURLException, IOException {
diff --git a/java/org/apache/catalina/ant/jmx/JMXAccessorCreateTask.java b/java/org/apache/catalina/ant/jmx/JMXAccessorCreateTask.java
index af1c157..28aab6c 100644
--- a/java/org/apache/catalina/ant/jmx/JMXAccessorCreateTask.java
+++ b/java/org/apache/catalina/ant/jmx/JMXAccessorCreateTask.java
@@ -113,14 +113,6 @@ public class JMXAccessorCreateTask extends JMXAccessorTask {
// ------------------------------------------------------ protected Methods
- /**
- * Execute the specified command, based on the configured properties. The
- * input stream will be closed upon completion of this task, whether it was
- * executed successfully or not.
- *
- * @exception Exception
- * if an error occurs
- */
@Override
public String jmxExecute(MBeanServerConnection jmxServerConnection)
throws Exception {
@@ -132,19 +124,19 @@ public class JMXAccessorCreateTask extends JMXAccessorTask {
throw new BuildException(
"Must specify a 'className' for get");
}
- return jmxCreate(jmxServerConnection, getName());
+ jmxCreate(jmxServerConnection, getName());
+ return null;
}
/**
- * create new Mbean and when set from ClassLoader Objectname
- * @param jmxServerConnection
- * @param name
- * @return The value of the given named attribute
- * @throws Exception
+ * Create new MBean from ClassLoader identified by an ObjectName.
+ *
+ * @param jmxServerConnection Connection to the JMX server
+ * @param name MBean name
+ * @throws Exception Error creating MBean
*/
- protected String jmxCreate(MBeanServerConnection jmxServerConnection,
+ protected void jmxCreate(MBeanServerConnection jmxServerConnection,
String name) throws Exception {
- String error = null;
Object argsA[] = null;
String sigA[] = null;
if (args != null) {
@@ -182,7 +174,6 @@ public class JMXAccessorCreateTask extends JMXAccessorTask {
else
jmxServerConnection.createMBean(className, new ObjectName(name),argsA,sigA);
}
- return error;
}
}
diff --git a/java/org/apache/catalina/ant/jmx/JMXAccessorGetTask.java b/java/org/apache/catalina/ant/jmx/JMXAccessorGetTask.java
index 304b45c..a538ea6 100644
--- a/java/org/apache/catalina/ant/jmx/JMXAccessorGetTask.java
+++ b/java/org/apache/catalina/ant/jmx/JMXAccessorGetTask.java
@@ -80,14 +80,6 @@ public class JMXAccessorGetTask extends JMXAccessorTask {
// ------------------------------------------------------ protected Methods
- /**
- * Execute the specified command, based on the configured properties. The
- * input stream will be closed upon completion of this task, whether it was
- * executed successfully or not.
- *
- * @exception BuildException
- * if an error occurs
- */
@Override
public String jmxExecute(MBeanServerConnection jmxServerConnection)
throws Exception {
@@ -104,12 +96,14 @@ public class JMXAccessorGetTask extends JMXAccessorTask {
/**
- * @param jmxServerConnection
- * @param name
- * @return The value of the given named attribute
- * @throws Exception
+ * Get property value.
+ *
+ * @param jmxServerConnection Connection to the JMX server
+ * @param name The MBean name
+ * @return The error message if any
+ * @throws Exception An error occurred
*/
- protected String jmxGet(MBeanServerConnection jmxServerConnection,String name) throws Exception {
+ protected String jmxGet(MBeanServerConnection jmxServerConnection, String name) throws Exception {
String error = null;
if(isEcho()) {
handleOutput("MBean " + name + " get attribute " + attribute );
diff --git a/java/org/apache/catalina/ant/jmx/JMXAccessorInvokeTask.java b/java/org/apache/catalina/ant/jmx/JMXAccessorInvokeTask.java
index 3ac3dff..3891965 100644
--- a/java/org/apache/catalina/ant/jmx/JMXAccessorInvokeTask.java
+++ b/java/org/apache/catalina/ant/jmx/JMXAccessorInvokeTask.java
@@ -131,14 +131,6 @@ public class JMXAccessorInvokeTask extends JMXAccessorTask {
// ------------------------------------------------------ protected Methods
- /**
- * Execute the specified command, based on the configured properties. The
- * input stream will be closed upon completion of this task, whether it was
- * executed successfully or not.
- *
- * @exception BuildException
- * if an error occurs
- */
@Override
public String jmxExecute(MBeanServerConnection jmxServerConnection)
throws Exception {
@@ -154,8 +146,12 @@ public class JMXAccessorInvokeTask extends JMXAccessorTask {
}
/**
- * @param jmxServerConnection
- * @throws Exception
+ * Invoke specified operation.
+ *
+ * @param jmxServerConnection Connection to the JMX server
+ * @param name The MBean name
+ * @return null (no error message to report other than exception)
+ * @throws Exception An error occurred
*/
protected String jmxInvoke(MBeanServerConnection jmxServerConnection, String name) throws Exception {
Object result ;
diff --git a/java/org/apache/catalina/ant/jmx/JMXAccessorQueryTask.java b/java/org/apache/catalina/ant/jmx/JMXAccessorQueryTask.java
index 0893618..68807a1 100644
--- a/java/org/apache/catalina/ant/jmx/JMXAccessorQueryTask.java
+++ b/java/org/apache/catalina/ant/jmx/JMXAccessorQueryTask.java
@@ -82,14 +82,6 @@ public class JMXAccessorQueryTask extends JMXAccessorTask {
// ------------------------------------------------------ protected Methods
- /**
- * Execute the specified command, based on the configured properties. The
- * input stream will be closed upon completion of this task, whether it was
- * executed successfully or not.
- *
- * @exception Exception
- * if an error occurs
- */
@Override
public String jmxExecute(MBeanServerConnection jmxServerConnection)
throws Exception {
@@ -105,10 +97,10 @@ public class JMXAccessorQueryTask extends JMXAccessorTask {
/**
* Call Mbean server for some mbeans with same domain, attributes.
* with <em>attributebinding=true</em> you can save all attributes from all found objects
- * as your ant properties
- * @param jmxServerConnection
- * @param qry
- * @return The query result
+ *
+ * @param jmxServerConnection Connection to the JMX server
+ * @param qry The query
+ * @return null (no error message to report other than exception)
*/
protected String jmxQuery(MBeanServerConnection jmxServerConnection,
String qry) {
@@ -143,57 +135,39 @@ public class JMXAccessorQueryTask extends JMXAccessorTask {
return isError;
}
+ protected void bindAttributes(MBeanServerConnection jmxServerConnection, String pname, ObjectName oname) {
+ try {
+ MBeanInfo minfo = jmxServerConnection.getMBeanInfo(oname);
+ MBeanAttributeInfo attrs[] = minfo.getAttributes();
+ Object value = null;
+
+ for (int i = 0; i < attrs.length; i++) {
+ if (!attrs[i].isReadable())
+ continue;
+ String attName = attrs[i].getName();
+ if (attName.indexOf('=') >= 0 || attName.indexOf(':') >= 0
+ || attName.indexOf(' ') >= 0) {
+ continue;
+ }
- /**
- * @param jmxServerConnection
- * @param resultproperty
- * @param pname
- * @param oname
- *
- * @deprecated Unused. Will be removed in Tomcat 8.5.x
- */
- @Deprecated
- protected void bindAttributes(MBeanServerConnection jmxServerConnection,
- String resultproperty, String pname, ObjectName oname) {
- bindAttributes(jmxServerConnection, pname, oname);
- }
-
- protected void bindAttributes(MBeanServerConnection jmxServerConnection,
- String pname, ObjectName oname) {
- if (jmxServerConnection != null && pname != null && oname != null ) {
- try {
- MBeanInfo minfo = jmxServerConnection.getMBeanInfo(oname);
- MBeanAttributeInfo attrs[] = minfo.getAttributes();
- Object value = null;
-
- for (int i = 0; i < attrs.length; i++) {
- if (!attrs[i].isReadable())
- continue;
- String attName = attrs[i].getName();
- if (attName.indexOf('=') >= 0 || attName.indexOf(':') >= 0
- || attName.indexOf(' ') >= 0) {
- continue;
- }
-
- try {
- value = jmxServerConnection
- .getAttribute(oname, attName);
- } catch (Exception e) {
- if (isEcho())
- handleErrorOutput("Error getting attribute "
- + oname + " " + pname + attName + " "
- + e.toString());
- continue;
- }
- if (value == null)
- continue;
- if ("modelerType".equals(attName))
- continue;
- createProperty(pname + attName, value);
+ try {
+ value = jmxServerConnection
+ .getAttribute(oname, attName);
+ } catch (Exception e) {
+ if (isEcho())
+ handleErrorOutput("Error getting attribute "
+ + oname + " " + pname + attName + " "
+ + e.toString());
+ continue;
}
- } catch (Exception e) {
- // Ignore
+ if (value == null)
+ continue;
+ if ("modelerType".equals(attName))
+ continue;
+ createProperty(pname + attName, value);
}
+ } catch (Exception e) {
+ // Ignore
}
}
}
diff --git a/java/org/apache/catalina/ant/jmx/JMXAccessorSetTask.java b/java/org/apache/catalina/ant/jmx/JMXAccessorSetTask.java
index 53082fb..c81e9d0 100644
--- a/java/org/apache/catalina/ant/jmx/JMXAccessorSetTask.java
+++ b/java/org/apache/catalina/ant/jmx/JMXAccessorSetTask.java
@@ -128,14 +128,6 @@ public class JMXAccessorSetTask extends JMXAccessorTask {
}
// ------------------------------------------------------ protected Methods
- /**
- * Execute the specified command, based on the configured properties. The
- * input stream will be closed upon completion of this task, whether it was
- * executed successfully or not.
- *
- * @exception Exception
- * if an error occurs
- */
@Override
public String jmxExecute(MBeanServerConnection jmxServerConnection)
throws Exception {
@@ -151,9 +143,12 @@ public class JMXAccessorSetTask extends JMXAccessorTask {
}
/**
- * @param jmxServerConnection
- * @param name
- * @throws Exception
+ * Set property value.
+ *
+ * @param jmxServerConnection Connection to the JMX server
+ * @param name The MBean name
+ * @return null (no error message to report other than exception)
+ * @throws Exception An error occurred
*/
protected String jmxSet(MBeanServerConnection jmxServerConnection,
String name) throws Exception {
@@ -176,11 +171,12 @@ public class JMXAccessorSetTask extends JMXAccessorTask {
/**
* Get MBean Attribute from Mbean Server
- * @param jmxServerConnection
- * @param name
- * @param attribute
- * @return The type
- * @throws Exception
+ *
+ * @param jmxServerConnection The JMX connection name
+ * @param name The MBean name
+ * @param attribute The attribute name
+ * @return The type of the attribute
+ * @throws Exception An error occurred
*/
protected String getMBeanAttributeType(
MBeanServerConnection jmxServerConnection,
diff --git a/java/org/apache/catalina/ant/jmx/JMXAccessorTask.java b/java/org/apache/catalina/ant/jmx/JMXAccessorTask.java
index 1cd99ad..c17656c 100644
--- a/java/org/apache/catalina/ant/jmx/JMXAccessorTask.java
+++ b/java/org/apache/catalina/ant/jmx/JMXAccessorTask.java
@@ -126,9 +126,10 @@ public class JMXAccessorTask extends BaseRedirectorHelperTask {
// ------------------------------------------------------------- Properties
/**
- * The name used at remote MbeanServer
+ * Get the name used at remote MbeanServer.
+ *
+ * @return the name used at remote MbeanServer
*/
-
public String getName() {
return (this.name);
}
@@ -196,7 +197,7 @@ public class JMXAccessorTask extends BaseRedirectorHelperTask {
}
/**
- * The login password for the <code>Manager</code> application.
+ * @return The login password for the <code>Manager</code> application.
*/
public String getPassword() {
return (this.password);
@@ -207,7 +208,7 @@ public class JMXAccessorTask extends BaseRedirectorHelperTask {
}
/**
- * The login username for the <code>JMX</code> MBeanServer.
+ * @return The login username for the <code>JMX</code> MBeanServer.
*/
public String getUsername() {
return (this.username);
@@ -218,9 +219,8 @@ public class JMXAccessorTask extends BaseRedirectorHelperTask {
}
/**
- * The URL of the <code>JMX JSR 160</code> MBeanServer to be used.
+ * @return The URL of the <code>JMX JSR 160</code> MBeanServer to be used.
*/
-
public String getUrl() {
return (this.url);
}
@@ -230,9 +230,8 @@ public class JMXAccessorTask extends BaseRedirectorHelperTask {
}
/**
- * The Host of the <code>JMX JSR 160</code> MBeanServer to be used.
+ * @return The Host of the <code>JMX JSR 160</code> MBeanServer to be used.
*/
-
public String getHost() {
return (this.host);
}
@@ -242,9 +241,8 @@ public class JMXAccessorTask extends BaseRedirectorHelperTask {
}
/**
- * The Port of the <code>JMX JSR 160</code> MBeanServer to be used.
+ * @return The Port of the <code>JMX JSR 160</code> MBeanServer to be used.
*/
-
public String getPort() {
return (this.port);
}
@@ -344,7 +342,18 @@ public class JMXAccessorTask extends BaseRedirectorHelperTask {
}
/**
- * create a new JMX Connection with auth when username and password is set.
+ * Create a new JMX Connection with auth when username and password is set.
+ *
+ * @param url URL to be used for the JMX connection
+ * (if specified, it is a complete URL so host and port will not
+ * be used)
+ * @param host Host name of the JMX server
+ * @param port Port number for the JMX server
+ * @param username User name for the connection
+ * @param password Credentials corresponding to the specified user
+ * @throws MalformedURLException Invalid URL specified
+ * @throws IOException Other connection error
+ * @return the JMX connection
*/
public static MBeanServerConnection createJMXConnection(String url,
String host, String port, String username, String password)
@@ -396,9 +405,18 @@ public class JMXAccessorTask extends BaseRedirectorHelperTask {
/**
* Get Current Connection from <em>ref</em> parameter or create a new one!
*
- * @return The server connection
- * @throws MalformedURLException
- * @throws IOException
+ * @param project The Ant project
+ * @param url URL to be used for the JMX connection
+ * (if specified, it is a complete URL so host and port will not
+ * be used)
+ * @param host Host name of the JMX server
+ * @param port Port number for the JMX server
+ * @param username User name for the connection
+ * @param password Credentials corresponding to the specified user
+ * @param refId The Id of the reference to retrieve in the project
+ * @throws MalformedURLException Invalid URL specified
+ * @throws IOException Other connection error
+ * @return the JMX connection
*/
@SuppressWarnings("null")
public static MBeanServerConnection accessJMXConnection(Project project,
@@ -432,9 +450,9 @@ public class JMXAccessorTask extends BaseRedirectorHelperTask {
/**
* get JMXConnection
*
- * @return The connection
- * @throws MalformedURLException
- * @throws IOException
+ * @throws MalformedURLException Invalid URL specified
+ * @throws IOException Other connection error
+ * @return the JMX connection
*/
protected MBeanServerConnection getJMXConnection()
throws MalformedURLException, IOException {
@@ -472,8 +490,9 @@ public class JMXAccessorTask extends BaseRedirectorHelperTask {
* input stream will be closed upon completion of this task, whether it was
* executed successfully or not.
*
- * @exception Exception
- * if an error occurs
+ * @param jmxServerConnection The JMX connection that should be used
+ * @return An error message string in some situations
+ * @exception Exception if an error occurs
*/
public String jmxExecute(MBeanServerConnection jmxServerConnection)
throws Exception {
@@ -555,7 +574,7 @@ public class JMXAccessorTask extends BaseRedirectorHelperTask {
/**
* @param name context of result
- * @param result
+ * @param result The result
*/
protected void echoResult(String name, Object result) {
if (isEcho()) {
@@ -588,8 +607,8 @@ public class JMXAccessorTask extends BaseRedirectorHelperTask {
* option is that you delimit your result with a delimiter
* (java.util.StringTokenizer is used).
*
- * @param propertyPrefix
- * @param result
+ * @param propertyPrefix Prefix for the property
+ * @param result The result
*/
protected void createProperty(String propertyPrefix, Object result) {
if (propertyPrefix == null)
diff --git a/java/org/apache/catalina/ant/jmx/JMXAccessorUnregisterTask.java b/java/org/apache/catalina/ant/jmx/JMXAccessorUnregisterTask.java
index 293de30..62f1728 100644
--- a/java/org/apache/catalina/ant/jmx/JMXAccessorUnregisterTask.java
+++ b/java/org/apache/catalina/ant/jmx/JMXAccessorUnregisterTask.java
@@ -53,14 +53,6 @@ public class JMXAccessorUnregisterTask extends JMXAccessorTask {
// ------------------------------------------------------ protected Methods
- /**
- * Execute the specified command, based on the configured properties. The
- * input stream will be closed upon completion of this task, whether it was
- * executed successfully or not.
- *
- * @exception Exception
- * if an error occurs
- */
@Override
public String jmxExecute(MBeanServerConnection jmxServerConnection)
throws Exception {
@@ -73,11 +65,12 @@ public class JMXAccessorUnregisterTask extends JMXAccessorTask {
/**
- * Unregister Mbean
- * @param jmxServerConnection
- * @param name
- * @return The value of the given named attribute
- * @throws Exception
+ * Unregister MBean.
+ *
+ * @param jmxServerConnection Connection to the JMX server
+ * @param name The MBean name
+ * @return null (no error message to report other than exception)
+ * @throws Exception An error occurred
*/
protected String jmxUuregister(MBeanServerConnection jmxServerConnection,String name) throws Exception {
String error = null;
diff --git a/java/org/apache/catalina/authenticator/AuthenticatorBase.java b/java/org/apache/catalina/authenticator/AuthenticatorBase.java
index 1c801f4..5111971 100644
--- a/java/org/apache/catalina/authenticator/AuthenticatorBase.java
+++ b/java/org/apache/catalina/authenticator/AuthenticatorBase.java
@@ -22,7 +22,19 @@ import java.security.cert.X509Certificate;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
-
+import java.util.Map;
+import java.util.Set;
+
+import javax.security.auth.Subject;
+import javax.security.auth.message.AuthException;
+import javax.security.auth.message.AuthStatus;
+import javax.security.auth.message.MessageInfo;
+import javax.security.auth.message.config.AuthConfigFactory;
+import javax.security.auth.message.config.AuthConfigProvider;
+import javax.security.auth.message.config.RegistrationListener;
+import javax.security.auth.message.config.ServerAuthConfig;
+import javax.security.auth.message.config.ServerAuthContext;
+import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
@@ -38,6 +50,8 @@ import org.apache.catalina.Realm;
import org.apache.catalina.Session;
import org.apache.catalina.Valve;
import org.apache.catalina.Wrapper;
+import org.apache.catalina.authenticator.jaspic.CallbackHandlerImpl;
+import org.apache.catalina.authenticator.jaspic.MessageInfoImpl;
import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.Response;
import org.apache.catalina.realm.GenericPrincipal;
@@ -52,38 +66,39 @@ import org.apache.tomcat.util.descriptor.web.SecurityConstraint;
import org.apache.tomcat.util.http.FastHttpDateFormat;
import org.apache.tomcat.util.res.StringManager;
-
/**
* Basic implementation of the <b>Valve</b> interface that enforces the
* <code><security-constraint></code> elements in the web application
- * deployment descriptor. This functionality is implemented as a Valve
- * so that it can be omitted in environments that do not require these
- * features. Individual implementations of each supported authentication
- * method can subclass this base class as required.
+ * deployment descriptor. This functionality is implemented as a Valve so that
+ * it can be omitted in environments that do not require these features.
+ * Individual implementations of each supported authentication method can
+ * subclass this base class as required.
* <p>
- * <b>USAGE CONSTRAINT</b>: When this class is utilized, the Context to
- * which it is attached (or a parent Container in a hierarchy) must have an
- * associated Realm that can be used for authenticating users and enumerating
- * the roles to which they have been assigned.
+ * <b>USAGE CONSTRAINT</b>: When this class is utilized, the Context to which it
+ * is attached (or a parent Container in a hierarchy) must have an associated
+ * Realm that can be used for authenticating users and enumerating the roles to
+ * which they have been assigned.
* <p>
- * <b>USAGE CONSTRAINT</b>: This Valve is only useful when processing HTTP
- * requests. Requests of any other type will simply be passed through.
+ * <b>USAGE CONSTRAINT</b>: 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 {
+ implements Authenticator, RegistrationListener {
private static final Log log = LogFactory.getLog(AuthenticatorBase.class);
+ /**
+ * "Expires" header always set to Date(1), so generate once only
+ */
+ private static final String DATE_ONE =
+ (new SimpleDateFormat(FastHttpDateFormat.RFC1123_DATE, Locale.US)).format(new Date(1));
- //------------------------------------------------------ Constructor
- public AuthenticatorBase() {
- super(true);
- }
-
- // ----------------------------------------------------- Instance Variables
-
+ /**
+ * The string manager for this package.
+ */
+ protected static final StringManager sm = StringManager.getManager(AuthenticatorBase.class);
/**
* Authentication header
@@ -95,6 +110,33 @@ public abstract class AuthenticatorBase extends ValveBase
*/
protected static final String REALM_NAME = "Authentication required";
+ protected static String getRealmName(Context context) {
+ if (context == null) {
+ // Very unlikely
+ return REALM_NAME;
+ }
+
+ LoginConfig config = context.getLoginConfig();
+ if (config == null) {
+ return REALM_NAME;
+ }
+
+ String result = config.getRealmName();
+ if (result == null) {
+ return REALM_NAME;
+ }
+
+ return result;
+ }
+
+ // ------------------------------------------------------ Constructor
+
+ public AuthenticatorBase() {
+ super(true);
+ }
+
+ // ----------------------------------------------------- Instance Variables
+
/**
* 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
@@ -107,14 +149,12 @@ public abstract class AuthenticatorBase extends ValveBase
*/
protected boolean alwaysUseSession = false;
-
/**
- * Should we cache authenticated Principals if the request is part of
- * an HTTP session?
+ * Should we cache authenticated Principals if the request is part of an
+ * HTTP session?
*/
protected boolean cache = true;
-
/**
* Should the session ID, if any, be changed upon a successful
* authentication to prevent a session fixation attack?
@@ -126,10 +166,9 @@ public abstract class AuthenticatorBase extends ValveBase
*/
protected Context context = null;
-
/**
- * Flag to determine if we disable proxy caching, or leave the issue
- * up to the webapp developer.
+ * Flag to determine if we disable proxy caching, or leave the issue up to
+ * the webapp developer.
*/
protected boolean disableProxyCaching = true;
@@ -172,104 +211,64 @@ public abstract class AuthenticatorBase extends ValveBase
protected SessionIdGeneratorBase sessionIdGenerator = null;
/**
- * The string manager for this package.
- */
- protected static final StringManager sm =
- StringManager.getManager(Constants.Package);
-
-
- /**
- * The SingleSignOn implementation in our request processing chain,
- * if there is one.
+ * The SingleSignOn implementation in our request processing chain, if there
+ * is one.
*/
protected SingleSignOn sso = null;
-
- /**
- * "Expires" header always set to Date(1), so generate once only
- */
- private static final String DATE_ONE =
- (new SimpleDateFormat(FastHttpDateFormat.RFC1123_DATE,
- Locale.US)).format(new Date(1));
-
-
- protected static String getRealmName(Context context) {
- if (context == null) {
- // Very unlikely
- return REALM_NAME;
- }
-
- LoginConfig config = context.getLoginConfig();
- if (config == null) {
- return REALM_NAME;
- }
-
- String result = config.getRealmName();
- if (result == null) {
- return REALM_NAME;
- }
-
- return result;
- }
+ private volatile String jaspicAppContextID = null;
+ private volatile AuthConfigProvider jaspicProvider = null;
// ------------------------------------------------------------- Properties
-
public boolean getAlwaysUseSession() {
return alwaysUseSession;
}
-
public void setAlwaysUseSession(boolean alwaysUseSession) {
this.alwaysUseSession = alwaysUseSession;
}
-
/**
* Return the cache authenticated Principals flag.
+ *
+ * @return <code>true</code> if authenticated Principals will be cached,
+ * otherwise <code>false</code>
*/
public boolean getCache() {
-
- return (this.cache);
-
+ return this.cache;
}
-
/**
* Set the cache authenticated Principals flag.
*
- * @param cache The new cache flag
+ * @param cache
+ * The new cache flag
*/
public void setCache(boolean cache) {
-
this.cache = cache;
-
}
-
/**
* Return the Container to which this Valve is attached.
*/
@Override
public Container getContainer() {
-
- return (this.context);
-
+ return this.context;
}
-
/**
* Set the Container to which this Valve is attached.
*
- * @param container The container to which we are attached
+ * @param container
+ * The container to which we are attached
*/
@Override
public void setContainer(Container container) {
if (container != null && !(container instanceof Context)) {
- throw new IllegalArgumentException
- (sm.getString("authenticator.notContext"));
+ throw new IllegalArgumentException(sm.getString("authenticator.notContext"));
}
super.setContainer(container);
@@ -277,10 +276,12 @@ public abstract class AuthenticatorBase extends ValveBase
}
-
/**
* Return the flag that states if we add headers to disable caching by
* proxies.
+ *
+ * @return <code>true</code> if the headers will be added, otherwise
+ * <code>false</code>
*/
public boolean getDisableProxyCaching() {
return disableProxyCaching;
@@ -289,8 +290,10 @@ public abstract class AuthenticatorBase extends ValveBase
/**
* Set the value of the flag that states if we add headers to disable
* caching by proxies.
- * @param nocache <code>true</code> if we add headers to disable proxy
- * caching, <code>false</code> if we leave the headers alone.
+ *
+ * @param nocache
+ * <code>true</code> if we add headers to disable proxy caching,
+ * <code>false</code> if we leave the headers alone.
*/
public void setDisableProxyCaching(boolean nocache) {
disableProxyCaching = nocache;
@@ -299,6 +302,9 @@ public abstract class AuthenticatorBase extends ValveBase
/**
* Return the flag that states, if proxy caching is disabled, what headers
* we add to disable the caching.
+ *
+ * @return <code>true</code> if a Pragma header should be used, otherwise
+ * <code>false</code>
*/
public boolean getSecurePagesWithPragma() {
return securePagesWithPragma;
@@ -307,9 +313,11 @@ public abstract class AuthenticatorBase extends ValveBase
/**
* Set the value of the flag that states what headers we add to disable
* proxy caching.
- * @param securePagesWithPragma <code>true</code> if we add headers which
- * are incompatible with downloading office documents in IE under SSL but
- * which fix a caching problem in Mozilla.
+ *
+ * @param securePagesWithPragma
+ * <code>true</code> if we add headers which are incompatible
+ * with downloading office documents in IE under SSL but which
+ * fix a caching problem in Mozilla.
*/
public void setSecurePagesWithPragma(boolean securePagesWithPragma) {
this.securePagesWithPragma = securePagesWithPragma;
@@ -330,96 +338,94 @@ public abstract class AuthenticatorBase extends ValveBase
* Set the value of the flag that states if we should change the session ID
* of an existing session upon successful authentication.
*
- * @param changeSessionIdOnAuthentication
- * <code>true</code> to change session ID upon successful
- * authentication, <code>false</code> to do not perform the
- * change.
+ * @param changeSessionIdOnAuthentication <code>true</code> to change
+ * session ID upon successful authentication, <code>false</code>
+ * to do not perform the change.
*/
- public void setChangeSessionIdOnAuthentication(
- boolean changeSessionIdOnAuthentication) {
+ public void setChangeSessionIdOnAuthentication(boolean changeSessionIdOnAuthentication) {
this.changeSessionIdOnAuthentication = changeSessionIdOnAuthentication;
}
/**
* Return the secure random number generator class name.
+ *
+ * @return The fully qualified name of the SecureRandom implementation to
+ * use
*/
public String getSecureRandomClass() {
-
- return (this.secureRandomClass);
-
+ return this.secureRandomClass;
}
-
/**
* Set the secure random number generator class name.
*
- * @param secureRandomClass The new secure random number generator class
- * name
+ * @param secureRandomClass
+ * The new secure random number generator class name
*/
public void setSecureRandomClass(String secureRandomClass) {
this.secureRandomClass = secureRandomClass;
}
-
/**
* Return the secure random number generator algorithm name.
+ *
+ * @return The name of the SecureRandom algorithm used
*/
public String getSecureRandomAlgorithm() {
return secureRandomAlgorithm;
}
-
/**
* Set the secure random number generator algorithm name.
*
- * @param secureRandomAlgorithm The new secure random number generator
- * algorithm name
+ * @param secureRandomAlgorithm
+ * The new secure random number generator algorithm name
*/
public void setSecureRandomAlgorithm(String secureRandomAlgorithm) {
this.secureRandomAlgorithm = secureRandomAlgorithm;
}
-
/**
* Return the secure random number generator provider name.
+ *
+ * @return The name of the SecureRandom provider
*/
public String getSecureRandomProvider() {
return secureRandomProvider;
}
-
/**
* Set the secure random number generator provider name.
*
- * @param secureRandomProvider The new secure random number generator
- * provider name
+ * @param secureRandomProvider
+ * The new secure random number generator provider name
*/
public void setSecureRandomProvider(String secureRandomProvider) {
this.secureRandomProvider = secureRandomProvider;
}
-
-
// --------------------------------------------------------- Public Methods
-
/**
* Enforce the security restrictions in the web application deployment
* descriptor of our associated Context.
*
- * @param request Request to be processed
- * @param response Response to be processed
+ * @param request
+ * Request to be processed
+ * @param response
+ * Response to be processed
*
- * @exception IOException if an input/output error occurs
- * @exception ServletException if thrown by a processing element
+ * @exception IOException
+ * if an input/output error occurs
+ * @exception ServletException
+ * if thrown by a processing element
*/
@Override
- public void invoke(Request request, Response response)
- throws IOException, ServletException {
+ public void invoke(Request request, Response response) throws IOException, ServletException {
if (log.isDebugEnabled()) {
- log.debug("Security checking request " +
- request.getMethod() + " " + request.getRequestURI());
+ log.debug("Security checking request " + request.getMethod() + " " +
+ request.getRequestURI());
}
// Have we got a cached authenticated Principal to record?
@@ -431,10 +437,8 @@ public abstract class AuthenticatorBase extends ValveBase
principal = session.getPrincipal();
if (principal != null) {
if (log.isDebugEnabled()) {
- log.debug("We have cached auth type " +
- session.getAuthType() +
- " for principal " +
- session.getPrincipal());
+ log.debug("We have cached auth type " + session.getAuthType() +
+ " for principal " + principal);
}
request.setAuthType(session.getAuthType());
request.setUserPrincipal(principal);
@@ -443,45 +447,7 @@ public abstract class AuthenticatorBase extends ValveBase
}
}
- // Special handling for form-based logins to deal with the case
- // where the login form (and therefore the "j_security_check" URI
- // to which it submits) might be outside the secured area
- String contextPath = this.context.getPath();
- String decodedRequestURI = request.getDecodedRequestURI();
- if (decodedRequestURI.startsWith(contextPath) &&
- decodedRequestURI.endsWith(Constants.FORM_ACTION)) {
- if (!authenticate(request, response)) {
- if (log.isDebugEnabled()) {
- log.debug(" Failed authenticate() test ??" + decodedRequestURI );
- }
- return;
- }
- }
-
- // Special handling for form-based logins to deal with the case where
- // 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()) &&
- !authenticate(request, response)) {
- if (log.isDebugEnabled()) {
- log.debug(" Failed authenticate() test");
- }
- /*
- * ASSERT: Authenticator already set the appropriate
- * HTTP status code, so we do not have to do anything
- * special
- */
- return;
- }
- }
+ boolean authRequired = isContinuationRequired(request);
// The Servlet may specify security constraints through annotations.
// Ensure that they have been processed before constraints are checked
@@ -492,10 +458,14 @@ public abstract class AuthenticatorBase extends ValveBase
Realm realm = this.context.getRealm();
// Is this request URI subject to a security constraint?
- SecurityConstraint [] constraints
- = realm.findSecurityConstraints(request, this.context);
+ SecurityConstraint[] constraints = realm.findSecurityConstraints(request, this.context);
+
+ AuthConfigProvider jaspicProvider = getJaspicProvider();
+ if (jaspicProvider != null) {
+ authRequired = true;
+ }
- if (constraints == null && !context.getPreemptiveAuthentication()) {
+ if (constraints == null && !context.getPreemptiveAuthentication() && !authRequired) {
if (log.isDebugEnabled()) {
log.debug(" Not subject to any constraint");
}
@@ -506,7 +476,7 @@ public abstract class AuthenticatorBase extends ValveBase
// 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())) {
+ !"POST".equalsIgnoreCase(request.getMethod())) {
if (securePagesWithPragma) {
// Note: These can cause problems with downloading files with IE
response.setHeader("Pragma", "No-cache");
@@ -517,20 +487,18 @@ public abstract class AuthenticatorBase extends ValveBase
response.setHeader("Expires", DATE_ONE);
}
- int i;
if (constraints != null) {
// Enforce any user data constraint for this security constraint
if (log.isDebugEnabled()) {
log.debug(" Calling hasUserDataPermission()");
}
- if (!realm.hasUserDataPermission(request, response,
- constraints)) {
+ if (!realm.hasUserDataPermission(request, response, constraints)) {
if (log.isDebugEnabled()) {
log.debug(" Failed hasUserDataPermission() test");
}
/*
- * ASSERT: Authenticator already set the appropriate
- * HTTP status code, so we do not have to do anything special
+ * ASSERT: Authenticator already set the appropriate HTTP status
+ * code, so we do not have to do anything special
*/
return;
}
@@ -538,50 +506,60 @@ public abstract class AuthenticatorBase extends ValveBase
// Since authenticate modifies the response on failure,
// we have to check for allow-from-all first.
- boolean authRequired;
- if (constraints == null) {
- authRequired = false;
- } else {
- authRequired = true;
- for(i=0; i < constraints.length && authRequired; i++) {
- if(!constraints[i].getAuthConstraint()) {
- authRequired = false;
- break;
- } else if(!constraints[i].getAllRoles() &&
+ boolean hasAuthConstraint = false;
+ if (constraints != null) {
+ hasAuthConstraint = true;
+ for (int i = 0; i < constraints.length && hasAuthConstraint; i++) {
+ if (!constraints[i].getAuthConstraint()) {
+ hasAuthConstraint = false;
+ } else if (!constraints[i].getAllRoles() &&
!constraints[i].getAuthenticatedUsers()) {
- String [] roles = constraints[i].findAuthRoles();
- if(roles == null || roles.length == 0) {
- authRequired = false;
- break;
+ String[] roles = constraints[i].findAuthRoles();
+ if (roles == null || roles.length == 0) {
+ hasAuthConstraint = false;
}
}
}
}
+ if (!authRequired && hasAuthConstraint) {
+ authRequired = true;
+ }
+
if (!authRequired && context.getPreemptiveAuthentication()) {
authRequired =
- request.getCoyoteRequest().getMimeHeaders().getValue(
- "authorization") != null;
+ request.getCoyoteRequest().getMimeHeaders().getValue("authorization") != null;
}
- if (!authRequired && context.getPreemptiveAuthentication() &&
- HttpServletRequest.CLIENT_CERT_AUTH.equals(getAuthMethod())) {
+ if (!authRequired && context.getPreemptiveAuthentication()
+ && HttpServletRequest.CLIENT_CERT_AUTH.equals(getAuthMethod())) {
X509Certificate[] certs = getRequestCertificates(request);
authRequired = certs != null && certs.length > 0;
}
- if(authRequired) {
+ JaspicState jaspicState = null;
+
+ if (authRequired) {
if (log.isDebugEnabled()) {
log.debug(" Calling authenticate()");
}
- if (!authenticate(request, response)) {
+
+ if (jaspicProvider != null) {
+ jaspicState = getJaspicState(jaspicProvider, request, response, hasAuthConstraint);
+ if (jaspicState == null) {
+ return;
+ }
+ }
+
+ if (jaspicProvider == null && !doAuthenticate(request, response) ||
+ jaspicProvider != null &&
+ !authenticateJaspic(request, response, jaspicState, false)) {
if (log.isDebugEnabled()) {
log.debug(" Failed authenticate() test");
}
/*
- * ASSERT: Authenticator already set the appropriate
- * HTTP status code, so we do not have to do anything
- * special
+ * ASSERT: Authenticator already set the appropriate HTTP status
+ * code, so we do not have to do anything special
*/
return;
}
@@ -592,16 +570,13 @@ public abstract class AuthenticatorBase extends ValveBase
if (log.isDebugEnabled()) {
log.debug(" Calling accessControl()");
}
- if (!realm.hasResourcePermission(request, response,
- constraints,
- this.context)) {
+ if (!realm.hasResourcePermission(request, response, constraints, this.context)) {
if (log.isDebugEnabled()) {
log.debug(" Failed accessControl() test");
}
/*
- * ASSERT: AccessControl method has already set the
- * appropriate HTTP status code, so we do not have to do
- * anything special
+ * ASSERT: AccessControl method has already set the appropriate
+ * HTTP status code, so we do not have to do anything special
*/
return;
}
@@ -613,20 +588,113 @@ public abstract class AuthenticatorBase extends ValveBase
}
getNext().invoke(request, response);
+ if (jaspicProvider != null) {
+ secureResponseJspic(request, response, jaspicState);
+ }
+ }
+
+
+ @Override
+ public boolean authenticate(Request request, HttpServletResponse httpResponse)
+ throws IOException {
+
+ AuthConfigProvider jaspicProvider = getJaspicProvider();
+
+ if (jaspicProvider == null) {
+ return doAuthenticate(request, httpResponse);
+ } else {
+ Response response = request.getResponse();
+ JaspicState jaspicState = getJaspicState(jaspicProvider, request, response, true);
+ if (jaspicState == null) {
+ return false;
+ }
+
+ boolean result = authenticateJaspic(request, response, jaspicState, true);
+
+ secureResponseJspic(request, response, jaspicState);
+
+ return result;
+ }
+ }
+
+
+ private void secureResponseJspic(Request request, Response response, JaspicState state) {
+ try {
+ state.serverAuthContext.secureResponse(state.messageInfo, null);
+ request.setRequest((HttpServletRequest) state.messageInfo.getRequestMessage());
+ response.setResponse((HttpServletResponse) state.messageInfo.getResponseMessage());
+ } catch (AuthException e) {
+ log.warn(sm.getString("authenticator.jaspicSecureResponseFail"), e);
+ }
+ }
+
+
+ private JaspicState getJaspicState(AuthConfigProvider jaspicProvider, Request request,
+ Response response, boolean authMandatory) throws IOException {
+ JaspicState jaspicState = new JaspicState();
+
+ jaspicState.messageInfo =
+ new MessageInfoImpl(request.getRequest(), response.getResponse(), authMandatory);
+
+ try {
+ ServerAuthConfig serverAuthConfig = jaspicProvider.getServerAuthConfig(
+ "HttpServlet", jaspicAppContextID, CallbackHandlerImpl.getInstance());
+ String authContextID = serverAuthConfig.getAuthContextID(jaspicState.messageInfo);
+ jaspicState.serverAuthContext = serverAuthConfig.getAuthContext(authContextID, null, null);
+ } catch (AuthException e) {
+ log.warn(sm.getString("authenticator.jaspicServerAuthContextFail"), e);
+ response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+ return null;
+ }
+
+ return jaspicState;
}
// ------------------------------------------------------ Protected Methods
/**
+ * Provided for sub-classes 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
+ * 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
+ */
+ protected abstract boolean doAuthenticate(Request request, HttpServletResponse response)
+ throws IOException;
+
+
+ /**
+ * Does this authenticator require that {@link #authenticate(Request,
+ * HttpServletResponse)} is called to continue an authentication process
+ * that started in a previous request?
+ *
+ * @param request The request currently being processed
+ *
+ * @return {@code true} if authenticate() must be called, otherwise
+ * {@code false}
+ */
+ protected boolean isContinuationRequired(Request request) {
+ return false;
+ }
+
+
+ /**
* Look for the X509 certificate chain in the Request under the key
* <code>javax.servlet.request.X509Certificate</code>. If not found, trigger
* extracting the certificate chain from the Coyote request.
*
- * @param request Request to be processed
+ * @param request
+ * Request to be processed
*
- * @return The X509 certificate chain if found, <code>null</code>
- * otherwise.
+ * @return The X509 certificate chain if found, <code>null</code> otherwise.
*/
protected X509Certificate[] getRequestCertificates(final Request request)
throws IllegalStateException {
@@ -647,13 +715,14 @@ public abstract class AuthenticatorBase extends ValveBase
return certs;
}
-
/**
- * Associate the specified single sign on identifier with the
- * specified Session.
+ * Associate the specified single sign on identifier with the specified
+ * Session.
*
- * @param ssoId Single sign on identifier
- * @param session Session to be associated
+ * @param ssoId
+ * Single sign on identifier
+ * @param session
+ * Session to be associated
*/
protected void associate(String ssoId, Session session) {
@@ -665,21 +734,64 @@ public abstract class AuthenticatorBase extends ValveBase
}
- /**
- * Authenticate the user making this request, based on the login
- * configuration of the {@link Context} with which this Authenticator is
- * associated. Return <code>true</code> if any specified constraint has
- * been satisfied, or <code>false</code> if we have created a response
- * challenge already.
- *
- * @param request Request we are processing
- * @param response Response we are populating
- *
- * @exception IOException if an input/output error occurs
- */
- @Override
- public abstract boolean authenticate(Request request,
- HttpServletResponse response) throws IOException;
+ private boolean authenticateJaspic(Request request, Response response, JaspicState state,
+ boolean requirePrincipal) {
+
+ boolean cachedAuth = checkForCachedAuthentication(request, response, false);
+ Subject client = new Subject();
+ AuthStatus authStatus;
+ try {
+ authStatus = state.serverAuthContext.validateRequest(state.messageInfo, client, null);
+ } catch (AuthException e) {
+ log.debug(sm.getString("authenticator.loginFail"), e);
+ return false;
+ }
+
+ request.setRequest((HttpServletRequest) state.messageInfo.getRequestMessage());
+ response.setResponse((HttpServletResponse) state.messageInfo.getResponseMessage());
+
+ if (authStatus == AuthStatus.SUCCESS) {
+ GenericPrincipal principal = getPrincipal(client);
+ if (log.isDebugEnabled()) {
+ log.debug("Authenticated user: " + principal);
+ }
+ if (principal == null) {
+ request.setUserPrincipal(null);
+ request.setAuthType(null);
+ if (requirePrincipal) {
+ return false;
+ }
+ } else if (cachedAuth == false ||
+ !principal.getUserPrincipal().equals(request.getUserPrincipal())) {
+ // Skip registration if authentication credentials were
+ // cached and the Principal did not change.
+ request.setNote(Constants.REQ_JASPIC_SUBJECT_NOTE, client);
+ @SuppressWarnings("rawtypes")// JASPIC API uses raw types
+ Map map = state.messageInfo.getMap();
+ if (map != null && map.containsKey("javax.servlet.http.registerSession")) {
+ register(request, response, principal, "JASPIC", null, null, true, true);
+ } else {
+ register(request, response, principal, "JASPIC", null, null);
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+
+
+ private GenericPrincipal getPrincipal(Subject subject) {
+ if (subject == null) {
+ return null;
+ }
+
+ Set<GenericPrincipal> principals = subject.getPrivateCredentials(GenericPrincipal.class);
+ if (principals.isEmpty()) {
+ return null;
+ }
+
+ return principals.iterator().next();
+ }
/**
@@ -687,16 +799,18 @@ public abstract class AuthenticatorBase extends ValveBase
* 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 useSSO
+ * Should information available from SSO be used to attempt to
+ * authenticate the current user?
*
* @return <code>true</code> if the user was authenticated via the cache,
* otherwise <code>false</code>
*/
- protected boolean checkForCachedAuthentication(Request request,
- HttpServletResponse response, boolean useSSO) {
+ protected boolean checkForCachedAuthentication(Request request, HttpServletResponse response, boolean useSSO) {
// Has the user already been authenticated?
Principal principal = request.getUserPrincipal();
@@ -719,12 +833,14 @@ public abstract class AuthenticatorBase extends ValveBase
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,
+ * 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
+ */
if (reauthenticateFromSSO(ssoId, request)) {
return true;
}
@@ -745,7 +861,7 @@ public abstract class AuthenticatorBase extends ValveBase
if (log.isDebugEnabled()) {
log.debug(sm.getString("authenticator.check.authorizeFail", username));
}
- authorized = new GenericPrincipal(username, null, null);
+ authorized = new GenericPrincipal(username, null, null);
}
String authType = request.getAuthType();
if (authType == null || authType.length() == 0) {
@@ -758,14 +874,16 @@ public abstract class AuthenticatorBase extends ValveBase
return false;
}
-
/**
- * Attempts reauthentication to the <code>Realm</code> using
- * the credentials included in argument <code>entry</code>.
+ * Attempts reauthentication to the <code>Realm</code> using the credentials
+ * included in argument <code>entry</code>.
*
- * @param ssoId identifier of SingleSignOn session with which the
- * caller is associated
- * @param request the request that needs to be authenticated
+ * @param ssoId
+ * identifier of SingleSignOn session with which the caller is
+ * associated
+ * @param request
+ * the request that needs to be authenticated
+ * @return <code>true</code> if the reauthentication from SSL occurred
*/
protected boolean reauthenticateFromSSO(String ssoId, Request request) {
@@ -788,36 +906,46 @@ public abstract class AuthenticatorBase extends ValveBase
if (log.isDebugEnabled()) {
log.debug(" Reauthenticated cached principal '" +
- request.getUserPrincipal().getName() +
- "' with auth type '" + request.getAuthType() + "'");
+ request.getUserPrincipal().getName() +
+ "' with auth type '" + request.getAuthType() + "'");
}
}
return reauthenticated;
}
-
/**
* Register an authenticated Principal and authentication type in our
* request, in the current session (if there is one), and with our
- * SingleSignOn valve, if there is one. Set the appropriate cookie
- * to be returned.
+ * SingleSignOn valve, if there is one. Set the appropriate cookie to be
+ * returned.
*
- * @param request The servlet request we are processing
- * @param response The servlet response we are generating
- * @param principal The authenticated Principal to be registered
- * @param authType The authentication type to be registered
- * @param username Username used to authenticate (if any)
- * @param password Password used to authenticate (if any)
+ * @param request
+ * The servlet request we are processing
+ * @param response
+ * The servlet response we are generating
+ * @param principal
+ * The authenticated Principal to be registered
+ * @param authType
+ * The authentication type to be registered
+ * @param username
+ * Username used to authenticate (if any)
+ * @param password
+ * Password used to authenticate (if any)
*/
- public void register(Request request, HttpServletResponse response,
- Principal principal, String authType,
- String username, String password) {
+ public void register(Request request, HttpServletResponse response, Principal principal,
+ String authType, String username, String password) {
+ register(request, response, principal, authType, username, password, alwaysUseSession, cache);
+ }
+
+
+ private void register(Request request, HttpServletResponse response, Principal principal,
+ String authType, String username, String password, boolean alwaysUseSession,
+ boolean cache) {
if (log.isDebugEnabled()) {
String name = (principal == null) ? "none" : principal.getName();
- log.debug("Authenticated '" + name + "' with type '" + authType +
- "'");
+ log.debug("Authenticated '" + name + "' with type '" + authType + "'");
}
// Cache the authentication information in our request
@@ -885,13 +1013,14 @@ public abstract class AuthenticatorBase extends ValveBase
// Bugzilla 34724
String ssoDomain = sso.getCookieDomain();
- if(ssoDomain != null) {
+ if (ssoDomain != null) {
cookie.setDomain(ssoDomain);
}
- // Configure httpOnly on SSO cookie using same rules as session cookies
- if (request.getServletContext().getSessionCookieConfig().isHttpOnly() ||
- request.getContext().getUseHttpOnly()) {
+ // Configure httpOnly on SSO cookie using same rules as session
+ // cookies
+ if (request.getServletContext().getSessionCookieConfig().isHttpOnly()
+ || request.getContext().getUseHttpOnly()) {
cookie.setHttpOnly(true);
}
@@ -927,11 +1056,9 @@ public abstract class AuthenticatorBase extends ValveBase
}
@Override
- public void login(String username, String password, Request request)
- throws ServletException {
+ public void login(String username, String password, Request request) throws ServletException {
Principal principal = doLogin(request, username, password);
- register(request, request.getResponse(), principal,
- getAuthMethod(), username, password);
+ register(request, request.getResponse(), principal, getAuthMethod(), username, password);
}
protected abstract String getAuthMethod();
@@ -939,14 +1066,18 @@ public abstract class AuthenticatorBase extends ValveBase
/**
* Process the login request.
*
- * @param request Associated request
- * @param username The user
- * @param password The password
- * @return The authenticated Principal
+ * @param request
+ * Associated request
+ * @param username
+ * The user
+ * @param password
+ * The password
+ * @return The authenticated Principal
* @throws ServletException
+ * No principal was authenticated with the specified credentials
*/
- protected Principal doLogin(Request request, String username,
- String password) throws ServletException {
+ protected Principal doLogin(Request request, String username, String password)
+ throws ServletException {
Principal p = context.getRealm().authenticate(username, password);
if (p == null) {
throw new ServletException(sm.getString("authenticator.loginFail"));
@@ -956,20 +1087,43 @@ public abstract class AuthenticatorBase extends ValveBase
@Override
public void logout(Request request) {
- register(request, request.getResponse(), null,
- null, null, null);
+ AuthConfigProvider provider = getJaspicProvider();
+ if (provider != null) {
+ MessageInfo messageInfo = new MessageInfoImpl(request, request.getResponse(), true);
+ Subject client = (Subject) request.getNote(Constants.REQ_JASPIC_SUBJECT_NOTE);
+ if (client == null) {
+ return;
+ }
+
+ ServerAuthContext serverAuthContext;
+ try {
+ ServerAuthConfig serverAuthConfig = provider.getServerAuthConfig("HttpServlet",
+ jaspicAppContextID, CallbackHandlerImpl.getInstance());
+ String authContextID = serverAuthConfig.getAuthContextID(messageInfo);
+ serverAuthContext = serverAuthConfig.getAuthContext(authContextID, null, null);
+ serverAuthContext.cleanSubject(messageInfo, client);
+ } catch (AuthException e) {
+ log.debug(sm.getString("authenticator.jaspicCleanSubjectFail"), e);
+ }
+ }
+ register(request, request.getResponse(), null, null, null, null);
}
+
/**
- * Start this component and implement the requirements
- * of {@link org.apache.catalina.util.LifecycleBase#startInternal()}.
+ * Start this component and implement the requirements of
+ * {@link org.apache.catalina.util.LifecycleBase#startInternal()}.
*
- * @exception LifecycleException if this component detects a fatal error
- * that prevents this component from being used
+ * @exception LifecycleException
+ * if this component detects a fatal error that prevents this
+ * component from being used
*/
@Override
protected synchronized void startInternal() throws LifecycleException {
+ ServletContext servletContext = context.getServletContext();
+ jaspicAppContextID = servletContext.getVirtualServerName() + " " +
+ servletContext.getContextPath();
// Look up the SingleSignOn implementation in our request processing
// path, if there is one
@@ -1002,13 +1156,13 @@ public abstract class AuthenticatorBase extends ValveBase
super.startInternal();
}
-
/**
- * Stop this component and implement the requirements
- * of {@link org.apache.catalina.util.LifecycleBase#stopInternal()}.
+ * Stop this component and implement the requirements of
+ * {@link org.apache.catalina.util.LifecycleBase#stopInternal()}.
*
- * @exception LifecycleException if this component detects a fatal error
- * that prevents this component from being used
+ * @exception LifecycleException
+ * if this component detects a fatal error that prevents this
+ * component from being used
*/
@Override
protected synchronized void stopInternal() throws LifecycleException {
@@ -1017,4 +1171,32 @@ public abstract class AuthenticatorBase extends ValveBase
sso = null;
}
+
+
+ private AuthConfigProvider getJaspicProvider() {
+ AuthConfigProvider provider = jaspicProvider;
+ if (provider == null) {
+ AuthConfigFactory factory = AuthConfigFactory.getFactory();
+ provider = factory.getConfigProvider("HttpServlet", jaspicAppContextID, this);
+ if (provider != null) {
+ jaspicProvider = provider;
+ }
+ }
+ return provider;
+ }
+
+
+ @Override
+ public void notify(String layer, String appContext) {
+ AuthConfigFactory factory = AuthConfigFactory.getFactory();
+ AuthConfigProvider provider = factory.getConfigProvider("HttpServlet", jaspicAppContextID,
+ this);
+ jaspicProvider = provider;
+ }
+
+
+ private static class JaspicState {
+ public MessageInfo messageInfo = null;
+ public ServerAuthContext serverAuthContext = null;
+ }
}
diff --git a/java/org/apache/catalina/authenticator/BasicAuthenticator.java b/java/org/apache/catalina/authenticator/BasicAuthenticator.java
index 32dab86..1a5742a 100644
--- a/java/org/apache/catalina/authenticator/BasicAuthenticator.java
+++ b/java/org/apache/catalina/authenticator/BasicAuthenticator.java
@@ -48,19 +48,8 @@ public class BasicAuthenticator extends AuthenticatorBase {
// --------------------------------------------------------- Public Methods
- /**
- * Authenticate the user making this request, based on the specified
- * login configuration. Return <code>true</code> if any specified
- * constraint has been satisfied, or <code>false</code> if we have
- * created a response challenge already.
- *
- * @param request Request we are processing
- * @param response Response we are creating
- *
- * @exception IOException if an input/output error occurs
- */
@Override
- public boolean authenticate(Request request, HttpServletResponse response)
+ protected boolean doAuthenticate(Request request, HttpServletResponse response)
throws IOException {
if (checkForCachedAuthentication(request, response, true)) {
@@ -85,7 +74,7 @@ public class BasicAuthenticator extends AuthenticatorBase {
if (principal != null) {
register(request, response, principal,
HttpServletRequest.BASIC_AUTH, username, password);
- return (true);
+ return true;
}
}
catch (IllegalArgumentException iae) {
@@ -102,7 +91,7 @@ public class BasicAuthenticator extends AuthenticatorBase {
value.append('\"');
response.setHeader(AUTH_HEADER_NAME, value.toString());
response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
- return (false);
+ return false;
}
@@ -117,7 +106,7 @@ public class BasicAuthenticator extends AuthenticatorBase {
* as per RFC 2617 section 2, and the Base64 encoded credentials as
* per RFC 2045 section 6.8.
*/
- protected static class BasicCredentials {
+ public static class BasicCredentials {
// the only authentication method supported by this parser
// note: we include single white space as its delimiter
diff --git a/java/org/apache/catalina/authenticator/Constants.java b/java/org/apache/catalina/authenticator/Constants.java
index 1f35b52..5e75c2c 100644
--- a/java/org/apache/catalina/authenticator/Constants.java
+++ b/java/org/apache/catalina/authenticator/Constants.java
@@ -20,9 +20,6 @@ package org.apache.catalina.authenticator;
public class Constants {
-
- public static final String Package = "org.apache.catalina.authenticator";
-
// Authentication methods for login configuration
// Servlet spec schemes are defined in HttpServletRequest
// Vendor specific schemes
@@ -41,6 +38,10 @@ public class Constants {
public static final String DEFAULT_JAAS_CONF = "conf/jaas.conf";
public static final String DEFAULT_LOGIN_MODULE_NAME =
"com.sun.security.jgss.krb5.accept";
+ /**
+ * @deprecated Unused. Will be removed in Tomcat 9.
+ */
+ @Deprecated
public static final String USE_SUBJECT_CREDS_ONLY_PROPERTY =
"javax.security.auth.useSubjectCredsOnly";
@@ -58,7 +59,11 @@ public class Constants {
* request is associated.
*/
public static final String REQ_SSOID_NOTE =
- "org.apache.catalina.request.SSOID";
+ "org.apache.catalina.request.SSOID";
+
+
+ public static final String REQ_JASPIC_SUBJECT_NOTE =
+ "org.apache.catalina.authenticator.jaspic.SUBJECT";
// ---------------------------------------------------------- Session Notes
diff --git a/java/org/apache/catalina/authenticator/DigestAuthenticator.java b/java/org/apache/catalina/authenticator/DigestAuthenticator.java
index bd8b4e1..8a1e8b3 100644
--- a/java/org/apache/catalina/authenticator/DigestAuthenticator.java
+++ b/java/org/apache/catalina/authenticator/DigestAuthenticator.java
@@ -194,7 +194,7 @@ public class DigestAuthenticator extends AuthenticatorBase {
* @exception IOException if an input/output error occurs
*/
@Override
- public boolean authenticate(Request request, HttpServletResponse response)
+ protected boolean doAuthenticate(Request request, HttpServletResponse response)
throws IOException {
// NOTE: We don't try to reauthenticate using any existing SSO session,
@@ -254,6 +254,10 @@ public class DigestAuthenticator extends AuthenticatorBase {
/**
* Removes the quotes on a string. RFC2617 states quotes are optional for
* all parameters except realm.
+ *
+ * @param quotedString The quoted string
+ * @param quotesRequired <code>true</code> if quotes were required
+ * @return The unquoted string
*/
protected static String removeQuotes(String quotedString,
boolean quotesRequired) {
@@ -270,6 +274,9 @@ public class DigestAuthenticator extends AuthenticatorBase {
/**
* Removes the quotes on a string.
+ *
+ * @param quotedString The quoted string
+ * @return The unquoted string
*/
protected static String removeQuotes(String quotedString) {
return removeQuotes(quotedString, false);
@@ -281,6 +288,7 @@ public class DigestAuthenticator extends AuthenticatorBase {
* time-stamp ":" private-key ) ).
*
* @param request HTTP Servlet request
+ * @return The generated nonce
*/
protected String generateNonce(Request request) {
@@ -334,6 +342,7 @@ public class DigestAuthenticator extends AuthenticatorBase {
* @param request HTTP Servlet request
* @param response HTTP Servlet response
* @param nonce nonce token
+ * @param isNonceStale <code>true</code> to add a stale parameter
*/
protected void setAuthenticateHeader(HttpServletRequest request,
HttpServletResponse response,
@@ -402,7 +411,7 @@ public class DigestAuthenticator extends AuthenticatorBase {
};
}
- private static class DigestInfo {
+ public static class DigestInfo {
private final String opaque;
private final long nonceValidity;
@@ -608,7 +617,7 @@ public class DigestAuthenticator extends AuthenticatorBase {
}
- private static class NonceInfo {
+ public static class NonceInfo {
private final long timestamp;
private final boolean seen[];
private final int offset;
diff --git a/java/org/apache/catalina/authenticator/FormAuthenticator.java b/java/org/apache/catalina/authenticator/FormAuthenticator.java
index 5f5c180..40d9f3c 100644
--- a/java/org/apache/catalina/authenticator/FormAuthenticator.java
+++ b/java/org/apache/catalina/authenticator/FormAuthenticator.java
@@ -38,7 +38,6 @@ import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.ExceptionUtils;
import org.apache.tomcat.util.buf.ByteChunk;
-import org.apache.tomcat.util.buf.CharChunk;
import org.apache.tomcat.util.buf.MessageBytes;
import org.apache.tomcat.util.descriptor.web.LoginConfig;
import org.apache.tomcat.util.http.MimeHeaders;
@@ -76,7 +75,9 @@ public class FormAuthenticator
// ------------------------------------------------------------- Properties
/**
- * Return the character encoding to use to read the username and password.
+ * Return the character encoding to use to read the user name and password.
+ *
+ * @return The name of the character encoding
*/
public String getCharacterEncoding() {
return characterEncoding;
@@ -84,7 +85,9 @@ public class FormAuthenticator
/**
- * Set the character encoding to be used to read the username and password.
+ * Set the character encoding to be used to read the user name and password.
+ *
+ * @param encoding The name of the encoding to use
*/
public void setCharacterEncoding(String encoding) {
characterEncoding = encoding;
@@ -93,6 +96,8 @@ public class FormAuthenticator
/**
* Return the landing page to use when FORM auth is mis-used.
+ *
+ * @return The path to the landing page relative to the web application root
*/
public String getLandingPage() {
return landingPage;
@@ -101,13 +106,16 @@ public class FormAuthenticator
/**
* Set the landing page to use when the FORM auth is mis-used.
+ *
+ * @param landingPage The path to the landing page relative to the web
+ * application root
*/
public void setLandingPage(String landingPage) {
this.landingPage = landingPage;
}
- // --------------------------------------------------------- Public Methods
+ // ------------------------------------------------------ Protected Methods
/**
@@ -122,7 +130,7 @@ public class FormAuthenticator
* @exception IOException if an input/output error occurs
*/
@Override
- public boolean authenticate(Request request, HttpServletResponse response)
+ protected boolean doAuthenticate(Request request, HttpServletResponse response)
throws IOException {
if (checkForCachedAuthentication(request, response, true)) {
@@ -199,9 +207,6 @@ public class FormAuthenticator
}
// Acquire references to objects we will need to evaluate
- MessageBytes uriMB = MessageBytes.newInstance();
- CharChunk uriCC = uriMB.getCharChunk();
- uriCC.setLimit(-1);
String contextPath = request.getContextPath();
String requestURI = request.getDecodedRequestURI();
@@ -339,12 +344,40 @@ public class FormAuthenticator
@Override
- protected String getAuthMethod() {
- return HttpServletRequest.FORM_AUTH;
+ protected boolean isContinuationRequired(Request request) {
+ // Special handling for form-based logins to deal with the case
+ // where the login form (and therefore the "j_security_check" URI
+ // to which it submits) might be outside the secured area
+ String contextPath = this.context.getPath();
+ String decodedRequestURI = request.getDecodedRequestURI();
+ if (decodedRequestURI.startsWith(contextPath) &&
+ decodedRequestURI.endsWith(Constants.FORM_ACTION)) {
+ return true;
+ }
+
+ // Special handling for form-based logins to deal with the case where
+ // 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;
}
- // ------------------------------------------------------ Protected Methods
+ @Override
+ protected String getAuthMethod() {
+ return HttpServletRequest.FORM_AUTH;
+ }
/**
@@ -461,6 +494,7 @@ public class FormAuthenticator
* we signaled after successful authentication?
*
* @param request The request to be verified
+ * @return <code>true</code> if the requests matched the saved one
*/
protected boolean matchRequest(Request request) {
// Has a session been created?
@@ -498,6 +532,8 @@ public class FormAuthenticator
*
* @param request The request to be restored
* @param session The session containing the saved information
+ * @return <code>true</code> if the request was successfully restored
+ * @throws IOException if an IO error occurred during the process
*/
protected boolean restoreRequest(Request request, Session session)
throws IOException {
@@ -588,7 +624,7 @@ public class FormAuthenticator
*
* @param request The request to be saved
* @param session The session to contain the saved information
- * @throws IOException
+ * @throws IOException if an IO error occurred during the process
*/
protected void saveRequest(Request request, Session session)
throws IOException {
@@ -651,6 +687,7 @@ public class FormAuthenticator
* from the saved request so that we can redirect to it.
*
* @param session Our current session
+ * @return the original request URL
*/
protected String savedRequestURL(Session session) {
diff --git a/java/org/apache/catalina/authenticator/LocalStrings.properties b/java/org/apache/catalina/authenticator/LocalStrings.properties
index ff87d02..50eb703 100644
--- a/java/org/apache/catalina/authenticator/LocalStrings.properties
+++ b/java/org/apache/catalina/authenticator/LocalStrings.properties
@@ -19,6 +19,9 @@ authenticator.check.authorize=Authorizing connector provided user [{0}] via Tomc
authenticator.check.authorizeFail=Realm did not recognise user [{0}]. Creating a Principal with that name and no roles.
authenticator.check.found=Already authenticated [{0}]
authenticator.check.sso=Not authenticated but SSO session ID [{0}] found. Attempting re-authentication.
+authenticator.jaspicCleanSubjectFail=Failed to clean JASPIC subject
+authenticator.jaspicSecureResponseFail=Failed to secure response during JASPIC processing
+authenticator.jaspicServerAuthContextFail=Failed to obtain a JASPIC ServerAuthContext instance
authenticator.formlogin=Invalid direct reference to form login page
authenticator.loginFail=Login failed
authenticator.manager=Exception initializing trust managers
diff --git a/java/org/apache/catalina/authenticator/NonLoginAuthenticator.java b/java/org/apache/catalina/authenticator/NonLoginAuthenticator.java
index 3018fff..dfdcee0 100644
--- a/java/org/apache/catalina/authenticator/NonLoginAuthenticator.java
+++ b/java/org/apache/catalina/authenticator/NonLoginAuthenticator.java
@@ -74,7 +74,7 @@ public final class NonLoginAuthenticator extends AuthenticatorBase {
* @exception IOException if an input/output error occurs
*/
@Override
- public boolean authenticate(Request request, HttpServletResponse response)
+ protected boolean doAuthenticate(Request request, HttpServletResponse response)
throws IOException {
// Don't try and use SSO to authenticate since there is no auth
diff --git a/java/org/apache/catalina/authenticator/SSLAuthenticator.java b/java/org/apache/catalina/authenticator/SSLAuthenticator.java
index 02d1ee8..10a03cf 100644
--- a/java/org/apache/catalina/authenticator/SSLAuthenticator.java
+++ b/java/org/apache/catalina/authenticator/SSLAuthenticator.java
@@ -46,7 +46,7 @@ public class SSLAuthenticator extends AuthenticatorBase {
* @exception IOException if an input/output error occurs
*/
@Override
- public boolean authenticate(Request request, HttpServletResponse response)
+ protected boolean doAuthenticate(Request request, HttpServletResponse response)
throws IOException {
// NOTE: We don't try to reauthenticate using any existing SSO session,
@@ -85,13 +85,13 @@ public class SSLAuthenticator extends AuthenticatorBase {
}
response.sendError(HttpServletResponse.SC_UNAUTHORIZED,
sm.getString("authenticator.unauthorized"));
- return (false);
+ return false;
}
// Cache the principal (if requested) and record this authentication
register(request, response, principal,
HttpServletRequest.CLIENT_CERT_AUTH, null, null);
- return (true);
+ return true;
}
diff --git a/java/org/apache/catalina/authenticator/SingleSignOn.java b/java/org/apache/catalina/authenticator/SingleSignOn.java
index 0b29253..8ff9af1 100644
--- a/java/org/apache/catalina/authenticator/SingleSignOn.java
+++ b/java/org/apache/catalina/authenticator/SingleSignOn.java
@@ -59,7 +59,7 @@ import org.apache.tomcat.util.res.StringManager;
*/
public class SingleSignOn extends ValveBase {
- private static final StringManager sm = StringManager.getManager(Constants.Package);
+ private static final StringManager sm = StringManager.getManager(SingleSignOn.class);
/* The engine at the top of the container hierarchy in which this SSO Valve
* has been placed. It is used to get back to a session object from a
diff --git a/java/org/apache/catalina/authenticator/SingleSignOnEntry.java b/java/org/apache/catalina/authenticator/SingleSignOnEntry.java
index d09ca6c..a427875 100644
--- a/java/org/apache/catalina/authenticator/SingleSignOnEntry.java
+++ b/java/org/apache/catalina/authenticator/SingleSignOnEntry.java
@@ -46,19 +46,19 @@ public class SingleSignOnEntry implements Serializable {
// ------------------------------------------------------ Instance Fields
- protected String authType = null;
+ private String authType = null;
- protected String password = null;
+ private String password = null;
// Marked as transient so special handling can be applied to serialization
- protected transient Principal principal = null;
+ private transient Principal principal = null;
- protected ConcurrentMap<SingleSignOnSessionKey,SingleSignOnSessionKey> sessionKeys =
+ private final ConcurrentMap<SingleSignOnSessionKey,SingleSignOnSessionKey> sessionKeys =
new ConcurrentHashMap<>();
- protected String username = null;
+ private String username = null;
- protected boolean canReauthenticate = false;
+ private boolean canReauthenticate = false;
// --------------------------------------------------------- Constructors
@@ -86,6 +86,7 @@ public class SingleSignOnEntry implements Serializable {
*
* @param sso The <code>SingleSignOn</code> valve that is managing
* the SSO session.
+ * @param ssoId The ID of the SSO session.
* @param session The <code>Session</code> being associated with the SSO.
*/
public void addSession(SingleSignOn sso, String ssoId, Session session) {
diff --git a/java/org/apache/catalina/authenticator/SpnegoAuthenticator.java b/java/org/apache/catalina/authenticator/SpnegoAuthenticator.java
index d1d9ff0..cb57812 100644
--- a/java/org/apache/catalina/authenticator/SpnegoAuthenticator.java
+++ b/java/org/apache/catalina/authenticator/SpnegoAuthenticator.java
@@ -134,7 +134,7 @@ public class SpnegoAuthenticator extends AuthenticatorBase {
@Override
- public boolean authenticate(Request request, HttpServletResponse response)
+ protected boolean doAuthenticate(Request request, HttpServletResponse response)
throws IOException {
if (checkForCachedAuthentication(request, response, true)) {
@@ -307,13 +307,13 @@ public class SpnegoAuthenticator extends AuthenticatorBase {
/**
* This class gets a gss credential via a privileged action.
*/
- private static class AcceptAction implements PrivilegedExceptionAction<byte[]> {
+ public static class AcceptAction implements PrivilegedExceptionAction<byte[]> {
GSSContext gssContext;
byte[] decoded;
- AcceptAction(GSSContext context, byte[] decodedToken) {
+ public AcceptAction(GSSContext context, byte[] decodedToken) {
this.gssContext = context;
this.decoded = decodedToken;
}
@@ -326,7 +326,7 @@ public class SpnegoAuthenticator extends AuthenticatorBase {
}
- private static class AuthenticateAction implements PrivilegedAction<Principal> {
+ public static class AuthenticateAction implements PrivilegedAction<Principal> {
private final Realm realm;
private final GSSContext gssContext;
@@ -359,7 +359,7 @@ public class SpnegoAuthenticator extends AuthenticatorBase {
* This hack works by re-ordering the list of mechTypes in the NegTokenInit
* token.
*/
- private static class SpnegoTokenFixer {
+ public static class SpnegoTokenFixer {
public static void fix(byte[] token) {
SpnegoTokenFixer fixer = new SpnegoTokenFixer(token);
diff --git a/java/org/apache/catalina/authenticator/jaspic/AuthConfigFactoryImpl.java b/java/org/apache/catalina/authenticator/jaspic/AuthConfigFactoryImpl.java
new file mode 100644
index 0000000..ba999dc
--- /dev/null
+++ b/java/org/apache/catalina/authenticator/jaspic/AuthConfigFactoryImpl.java
@@ -0,0 +1,296 @@
+/**
+ * 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;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import javax.security.auth.message.config.AuthConfigFactory;
+import javax.security.auth.message.config.AuthConfigProvider;
+import javax.security.auth.message.config.RegistrationListener;
+
+import org.apache.catalina.Globals;
+import org.apache.catalina.authenticator.jaspic.PersistentProviderRegistrations.Provider;
+import org.apache.catalina.authenticator.jaspic.PersistentProviderRegistrations.Providers;
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
+import org.apache.tomcat.util.res.StringManager;
+
+public class AuthConfigFactoryImpl extends AuthConfigFactory {
+
+ private static final Log log = LogFactory.getLog(AuthConfigFactoryImpl.class);
+ private static final StringManager sm = StringManager.getManager(AuthConfigFactoryImpl.class);
+
+ private static final String CONFIG_PATH = "conf/jaspic-providers.xml";
+ private static final File CONFIG_FILE =
+ new File(System.getProperty(Globals.CATALINA_BASE_PROP), CONFIG_PATH);
+ private static final Object CONFIG_FILE_LOCK = new Object();
+
+ private static final String[] EMPTY_STRING_ARRAY = new String[0];
+
+ private final Map<String,RegistrationContextImpl> registrations = new ConcurrentHashMap<>();
+
+
+ public AuthConfigFactoryImpl() {
+ loadPersistentRegistrations();
+ }
+
+
+ @Override
+ public AuthConfigProvider getConfigProvider(String layer, String appContext,
+ RegistrationListener listener) {
+ String registrationID = getRegistrarionID(layer, appContext);
+ RegistrationContextImpl registrationContext = registrations.get(registrationID);
+ if (registrationContext != null) {
+ registrationContext.addListener(null);
+ return registrationContext.getProvider();
+ }
+ return null;
+ }
+
+
+ @Override
+ public String registerConfigProvider(String className,
+ @SuppressWarnings("rawtypes") Map properties, String layer, String appContext,
+ String description) {
+ String registrationID =
+ doRegisterConfigProvider(className, properties, layer, appContext, description);
+ savePersistentRegistrations();
+ return registrationID;
+ }
+
+
+ @SuppressWarnings("unchecked")
+ private String doRegisterConfigProvider(String className,
+ @SuppressWarnings("rawtypes") Map properties, String layer, String appContext,
+ String description) {
+ if (log.isDebugEnabled()) {
+ log.debug(sm.getString("authConfigFactoryImpl.registerClass",
+ className, layer, appContext));
+ }
+ Class<?> clazz;
+ AuthConfigProvider provider = null;
+ try {
+ clazz = Class.forName(className, true, Thread.currentThread().getContextClassLoader());
+ } catch (ClassNotFoundException e) {
+ // Ignore so the re-try below can proceed
+ }
+ try {
+ clazz = Class.forName(className);
+ Constructor<?> constructor = clazz.getConstructor(Map.class, AuthConfigFactory.class);
+ provider = (AuthConfigProvider) constructor.newInstance(properties, null);
+ } catch (ClassNotFoundException | NoSuchMethodException | InstantiationException |
+ IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
+ throw new SecurityException(e);
+ }
+
+ String registrationID = getRegistrarionID(layer, appContext);
+ registrations.put(registrationID,
+ new RegistrationContextImpl(layer, appContext, description, true, provider, properties));
+ return registrationID;
+ }
+
+
+ @Override
+ public String registerConfigProvider(AuthConfigProvider provider, String layer,
+ String appContext, String description) {
+ if (log.isDebugEnabled()) {
+ log.debug(sm.getString("authConfigFactoryImpl.registerInstance",
+ provider.getClass().getName(), layer, appContext));
+ }
+ String registrationID = getRegistrarionID(layer, appContext);
+ registrations.put(registrationID,
+ new RegistrationContextImpl(layer, appContext, description, false, provider, null));
+ return registrationID;
+ }
+
+
+ @Override
+ public boolean removeRegistration(String registrationID) {
+ return registrations.remove(registrationID) != null;
+ }
+
+
+ @Override
+ public String[] detachListener(RegistrationListener listener, String layer, String appContext) {
+ String registrationID = getRegistrarionID(layer, appContext);
+ RegistrationContextImpl registrationContext = registrations.get(registrationID);
+ if (registrationContext.removeListener(listener)) {
+ return new String[] { registrationID };
+ }
+ return EMPTY_STRING_ARRAY;
+ }
+
+
+ @Override
+ public String[] getRegistrationIDs(AuthConfigProvider provider) {
+ if (provider == null) {
+ return registrations.keySet().toArray(EMPTY_STRING_ARRAY);
+ } else {
+ List<String> results = new ArrayList<>();
+ for (Entry<String,RegistrationContextImpl> entry : registrations.entrySet()) {
+ if (provider.equals(entry.getValue().getProvider())) {
+ results.add(entry.getKey());
+ }
+ }
+ return results.toArray(EMPTY_STRING_ARRAY);
+ }
+ }
+
+
+ @Override
+ public RegistrationContext getRegistrationContext(String registrationID) {
+ return registrations.get(registrationID);
+ }
+
+
+ @Override
+ public void refresh() {
+ loadPersistentRegistrations();
+ }
+
+
+ private String getRegistrarionID(String layer, String appContext) {
+ return layer + ":" + appContext;
+ }
+
+
+ private void loadPersistentRegistrations() {
+ synchronized (CONFIG_FILE_LOCK) {
+ if (log.isDebugEnabled()) {
+ log.debug(sm.getString("authConfigFactoryImpl.load",
+ CONFIG_FILE.getAbsolutePath()));
+ }
+ if (!CONFIG_FILE.isFile()) {
+ return;
+ }
+ Providers providers = PersistentProviderRegistrations.loadProviders(CONFIG_FILE);
+ for (Provider provider : providers.getProviders()) {
+ doRegisterConfigProvider(provider.getClassName(), provider.getProperties(),
+ provider.getLayer(), provider.getAppContext(), provider.getDescription());
+ }
+ }
+ }
+
+
+ private void savePersistentRegistrations() {
+ synchronized (CONFIG_FILE_LOCK) {
+ Providers providers = new Providers();
+ for (Entry<String,RegistrationContextImpl> entry : registrations.entrySet()) {
+ if (entry.getValue().isPersistent()) {
+ Provider provider = new Provider();
+ provider.setAppContext(entry.getValue().getAppContext());
+ provider.setClassName(entry.getValue().getProvider().getClass().getName());
+ provider.setDescription(entry.getValue().getDescription());
+ provider.setLayer(entry.getValue().getMessageLayer());
+ for (Entry<String,String> property : entry.getValue().getProperties().entrySet()) {
+ provider.addProperty(property.getKey(), property.getValue());
+ }
+ providers.addProvider(provider);
+ }
+ }
+ PersistentProviderRegistrations.writeProviders(providers, CONFIG_FILE);
+ }
+ }
+
+
+ private static class RegistrationContextImpl implements RegistrationContext {
+
+ private RegistrationContextImpl(String messageLayer, String appContext, String description,
+ boolean persistent, AuthConfigProvider provider, Map<String,String> properties) {
+ this.messageLayer = messageLayer;
+ this.appContext = appContext;
+ this.description = description;
+ this.persistent = persistent;
+ this.provider = provider;
+ Map<String,String> propertiesCopy = new HashMap<>();
+ if (properties != null) {
+ propertiesCopy.putAll(properties);
+ }
+ this.properties = Collections.unmodifiableMap(propertiesCopy);
+ }
+
+ private final String messageLayer;
+ private final String appContext;
+ private final String description;
+ private final boolean persistent;
+ private final AuthConfigProvider provider;
+ private final Map<String,String> properties;
+ private final List<RegistrationListener> listeners = new CopyOnWriteArrayList<>();
+
+ @Override
+ public String getMessageLayer() {
+ return messageLayer;
+ }
+
+
+ @Override
+ public String getAppContext() {
+ return appContext;
+ }
+
+ @Override
+ public String getDescription() {
+ return description;
+ }
+
+
+ @Override
+ public boolean isPersistent() {
+ return persistent;
+ }
+
+
+ private AuthConfigProvider getProvider() {
+ return provider;
+ }
+
+
+ private void addListener(RegistrationListener listener) {
+ if (listener != null) {
+ listeners.add(listener);
+ }
+ }
+
+
+ private Map<String,String> getProperties() {
+ return properties;
+ }
+
+
+ private boolean removeListener(RegistrationListener listener) {
+ boolean result = false;
+ Iterator<RegistrationListener> iter = listeners.iterator();
+ while (iter.hasNext()) {
+ if (iter.next().equals(listener)) {
+ iter.remove();
+ }
+ }
+ return result;
+ }
+ }
+}
diff --git a/java/org/apache/catalina/authenticator/jaspic/CallbackHandlerImpl.java b/java/org/apache/catalina/authenticator/jaspic/CallbackHandlerImpl.java
new file mode 100644
index 0000000..91895a6
--- /dev/null
+++ b/java/org/apache/catalina/authenticator/jaspic/CallbackHandlerImpl.java
@@ -0,0 +1,119 @@
+/*
+ * 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.IOException;
+import java.security.Principal;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import javax.security.auth.Subject;
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.UnsupportedCallbackException;
+import javax.security.auth.message.callback.CallerPrincipalCallback;
+import javax.security.auth.message.callback.GroupPrincipalCallback;
+
+import org.apache.catalina.realm.GenericPrincipal;
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
+import org.apache.tomcat.util.res.StringManager;
+
+/**
+ * Implemented as a singleton since the class is stateless.
+ */
+public class CallbackHandlerImpl implements CallbackHandler {
+
+ private static final Log log = LogFactory.getLog(CallbackHandlerImpl.class);
+ private static final StringManager sm = StringManager.getManager(CallbackHandlerImpl.class);
+
+ private static CallbackHandler instance;
+
+
+ static {
+ instance = new CallbackHandlerImpl();
+ }
+
+
+ public static CallbackHandler getInstance() {
+ return instance;
+ }
+
+
+ private CallbackHandlerImpl() {
+ // Hide default constructor
+ }
+
+
+ @Override
+ public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
+
+ String name = null;
+ Principal principal = null;
+ Subject subject = null;
+ String[] groups = null;
+
+ if (callbacks != null) {
+ // Need to combine data from multiple callbacks so use this to hold
+ // the data
+ // Process the callbacks
+ for (Callback callback : callbacks) {
+ if (callback instanceof CallerPrincipalCallback) {
+ CallerPrincipalCallback cpc = (CallerPrincipalCallback) callback;
+ name = cpc.getName();
+ principal = cpc.getPrincipal();
+ subject = cpc.getSubject();
+ } else if (callback instanceof GroupPrincipalCallback) {
+ GroupPrincipalCallback gpc = (GroupPrincipalCallback) callback;
+ groups = gpc.getGroups();
+ } else {
+ log.error(sm.getString("callbackHandlerImpl.jaspicCallbackMissing",
+ callback.getClass().getName()));
+ }
+ }
+
+ // Create the GenericPrincipal
+ Principal gp = getPrincipal(principal, name, groups);
+ if (subject != null && gp != null) {
+ subject.getPrivateCredentials().add(gp);
+ }
+ }
+ }
+
+
+ private Principal getPrincipal(Principal principal, String name, String[] groups) {
+ // If the Principal is cached in the session JASPIC may simply return it
+ if (principal instanceof GenericPrincipal) {
+ return principal;
+ }
+ if (name == null && principal != null) {
+ name = principal.getName();
+ }
+ if (name == null) {
+ return null;
+ }
+ List<String> roles;
+ if (groups == null || groups.length == 0) {
+ roles = Collections.emptyList();
+ } else {
+ roles = Arrays.asList(groups);
+ }
+
+ return new GenericPrincipal(name, null, roles, principal);
+ }
+}
diff --git a/java/org/apache/catalina/authenticator/jaspic/LocalStrings.properties b/java/org/apache/catalina/authenticator/jaspic/LocalStrings.properties
new file mode 100644
index 0000000..ac4b956
--- /dev/null
+++ b/java/org/apache/catalina/authenticator/jaspic/LocalStrings.properties
@@ -0,0 +1,28 @@
+# 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.
+
+authConfigFactoryImpl.load=Loading persistent provider registrations from [{0}]
+authConfigFactoryImpl.registerClass=Registering class [{0}] for layer [{1}] and application context [{2}]
+authConfigFactoryImpl.registerInstance=Registering instance of type[{0}] for layer [{1}] and application context [{2}]
+
+callbackHandlerImpl.jaspicCallbackMissing=Unsupported JASPIC callback of type [{0}] received which was ignored
+
+jaspicAuthenticator.authenticate=Authenticating request for [{0}] via JASPIC
+
+persistentProviderRegistrations.deleteFail=The temporary file [{0}] cannot be deleted
+persistentProviderRegistrations.existsDeleteFail=The temporary file [{0}] already exists and cannot be deleted
+persistentProviderRegistrations.moveFail=Failed to move [{0}] to [{1}]
+
+simpleServerAuthConfig.noModules="No ServerAuthModules configured"
\ No newline at end of file
diff --git a/java/org/apache/catalina/authenticator/jaspic/MessageInfoImpl.java b/java/org/apache/catalina/authenticator/jaspic/MessageInfoImpl.java
new file mode 100644
index 0000000..4d0ba64
--- /dev/null
+++ b/java/org/apache/catalina/authenticator/jaspic/MessageInfoImpl.java
@@ -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.authenticator.jaspic;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.security.auth.message.MessageInfo;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.tomcat.util.res.StringManager;
+
+public class MessageInfoImpl implements MessageInfo {
+ protected static final StringManager sm = StringManager.getManager(MessageInfoImpl.class);
+
+ public static final String IS_MANDATORY = "javax.security.auth.message.MessagePolicy.isMandatory";
+
+ private final Map<String, Object> map = new HashMap<>();
+ private HttpServletRequest request;
+ private HttpServletResponse response;
+
+ public MessageInfoImpl() {
+ }
+
+ public MessageInfoImpl(HttpServletRequest request, HttpServletResponse response, boolean authMandatory) {
+ this.request = request;
+ this.response = response;
+ map.put(IS_MANDATORY, Boolean.toString(authMandatory));
+ }
+
+ @Override
+ @SuppressWarnings("rawtypes")
+ // JASPIC uses raw types
+ public Map getMap() {
+ return map;
+ }
+
+ @Override
+ public Object getRequestMessage() {
+ return request;
+ }
+
+ @Override
+ public Object getResponseMessage() {
+ return response;
+ }
+
+ @Override
+ public void setRequestMessage(Object request) {
+ if (!(request instanceof HttpServletRequest)) {
+ throw new IllegalArgumentException(sm.getString("authenticator.jaspic.badRequestType",
+ request.getClass().getName()));
+ }
+ this.request = (HttpServletRequest) request;
+ }
+
+ @Override
+ public void setResponseMessage(Object response) {
+ if (!(response instanceof HttpServletResponse)) {
+ throw new IllegalArgumentException(sm.getString("authenticator.jaspic.badResponseType",
+ response.getClass().getName()));
+ }
+ this.response = (HttpServletResponse) response;
+ }
+}
\ No newline at end of file
diff --git a/java/org/apache/catalina/authenticator/jaspic/PersistentProviderRegistrations.java b/java/org/apache/catalina/authenticator/jaspic/PersistentProviderRegistrations.java
new file mode 100644
index 0000000..05eb121
--- /dev/null
+++ b/java/org/apache/catalina/authenticator/jaspic/PersistentProviderRegistrations.java
@@ -0,0 +1,257 @@
+/**
+ * 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;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
+import org.apache.tomcat.util.digester.Digester;
+import org.apache.tomcat.util.res.StringManager;
+import org.xml.sax.SAXException;
+
+/**
+ * Utility class for the loading and saving of JASPIC persistent provider
+ * registrations.
+ */
+final class PersistentProviderRegistrations {
+
+ private static final Log log = LogFactory.getLog(PersistentProviderRegistrations.class);
+ private static final StringManager sm =
+ StringManager.getManager(PersistentProviderRegistrations.class);
+
+
+ private PersistentProviderRegistrations() {
+ // Utility class. Hide default constructor
+ }
+
+
+ static Providers loadProviders(File configFile) {
+ try (InputStream is = new FileInputStream(configFile)) {
+ // Construct a digester to read the XML input file
+ Digester digester = new Digester();
+
+ try {
+ digester.setFeature("http://apache.org/xml/features/allow-java-encodings", true);
+ digester.setValidating(true);
+ digester.setNamespaceAware(true);
+ } catch (Exception e) {
+ throw new SecurityException(e);
+ }
+
+ // Create an object to hold the parse results and put it on the top
+ // of the digester's stack
+ Providers result = new Providers();
+ digester.push(result);
+
+ // Configure the digester
+ digester.addObjectCreate("jaspic-providers/provider", Provider.class.getName());
+ digester.addSetProperties("jaspic-providers/provider");
+ digester.addSetNext("jaspic-providers/provider", "addProvider", Provider.class.getName());
+
+ digester.addObjectCreate("jaspic-providers/provider/property", Property.class.getName());
+ digester.addSetProperties("jaspic-providers/provider/property");
+ digester.addSetNext("jaspic-providers/provider/property", "addProperty", Property.class.getName());
+
+ // Parse the input
+ digester.parse(is);
+
+ return result;
+ } catch (IOException | SAXException e) {
+ throw new SecurityException(e);
+ }
+ }
+
+
+ static void writeProviders(Providers providers, File configFile) {
+ File configFileOld = new File(configFile.getAbsolutePath() + ".old");
+ File configFileNew = new File(configFile.getAbsolutePath() + ".new");
+
+ // Remove left over temporary files if present
+ if (configFileOld.exists()) {
+ if (configFileOld.delete()) {
+ throw new SecurityException(sm.getString(
+ "persistentProviderRegistrations.existsDeleteFail",
+ configFileOld.getAbsolutePath()));
+ }
+ }
+ if (configFileNew.exists()) {
+ if (configFileNew.delete()) {
+ throw new SecurityException(sm.getString(
+ "persistentProviderRegistrations.existsDeleteFail",
+ configFileNew.getAbsolutePath()));
+ }
+ }
+
+ // Write out the providers to the temporary new file
+ try (OutputStream fos = new FileOutputStream(configFileNew);
+ Writer writer = new OutputStreamWriter(fos, StandardCharsets.UTF_8)) {
+ writer.write(
+ "<?xml version='1.0' encoding='utf-8'?>\n" +
+ "<jaspic-providers\n" +
+ " xmlns=\"http://tomcat.apache.org/xml\"\n" +
+ " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n" +
+ " xsi:schemaLocation=\"http://tomcat.apache.org/xml jaspic-providers.xsd\"\n" +
+ " version=\"1.0\">\n");
+ for (Provider provider : providers.providers) {
+ writer.write(" <provider className=\"");
+ writer.write(provider.getClassName());
+ writer.write("\" layer=\"");
+ writer.write(provider.getLayer());
+ writer.write("\" appContext=\"");
+ writer.write(provider.getAppContext());
+ if (provider.getDescription() != null) {
+ writer.write("\" description=\"");
+ writer.write(provider.getDescription());
+ }
+ writer.write("\">\n");
+ for (Entry<String,String> entry : provider.getProperties().entrySet()) {
+ writer.write(" <property name=\"");
+ writer.write(entry.getKey());
+ writer.write("\" value=\"");
+ writer.write(entry.getValue());
+ writer.write("\"/>\n");
+ }
+ writer.write(" </provider>\n");
+ }
+ writer.write("</jaspic-providers>\n");
+ } catch (IOException e) {
+ configFileNew.delete();
+ throw new SecurityException(e);
+ }
+
+ // Move the current file out of the way
+ if (configFile.isFile()) {
+ if (!configFile.renameTo(configFileOld)) {
+ throw new SecurityException(sm.getString("persistentProviderRegistrations.moveFail",
+ configFile.getAbsolutePath(), configFileOld.getAbsolutePath()));
+ }
+ }
+
+ // Move the new file into place
+ if (!configFileNew.renameTo(configFile)) {
+ throw new SecurityException(sm.getString("persistentProviderRegistrations.moveFail",
+ configFileNew.getAbsolutePath(), configFile.getAbsolutePath()));
+ }
+
+ // Remove the old file
+ if (configFileOld.exists() && !configFileOld.delete()) {
+ log.warn(sm.getString("persistentProviderRegistrations.deleteFail",
+ configFileOld.getAbsolutePath()));
+ }
+ }
+
+
+ public static class Providers {
+ private final List<Provider> providers = new ArrayList<>();
+
+ public void addProvider(Provider provider) {
+ providers.add(provider);
+ }
+
+ public List<Provider> getProviders() {
+ return providers;
+ }
+ }
+
+
+ public static class Provider {
+ private String className;
+ private String layer;
+ private String appContext;
+ private String description;
+ private final Map<String,String> properties = new HashMap<>();
+
+
+ public String getClassName() {
+ return className;
+ }
+ public void setClassName(String className) {
+ this.className = className;
+ }
+
+
+ public String getLayer() {
+ return layer;
+ }
+ public void setLayer(String layer) {
+ this.layer = layer;
+ }
+
+
+ public String getAppContext() {
+ return appContext;
+ }
+ public void setAppContext(String appContext) {
+ this.appContext = appContext;
+ }
+
+
+ public String getDescription() {
+ return description;
+ }
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+
+ public void addProperty(Property property) {
+ properties.put(property.getName(), property.getValue());
+ }
+ void addProperty(String name, String value) {
+ properties.put(name, value);
+ }
+ public Map<String,String> getProperties() {
+ return properties;
+ }
+ }
+
+
+ public static class Property {
+ private String name;
+ private String value;
+
+
+ public String getName() {
+ return name;
+ }
+ public void setName(String name) {
+ this.name = name;
+ }
+
+
+ public String getValue() {
+ return value;
+ }
+ public void setValue(String value) {
+ this.value = value;
+ }
+ }
+}
diff --git a/java/org/apache/catalina/authenticator/jaspic/SimpleAuthConfigProvider.java b/java/org/apache/catalina/authenticator/jaspic/SimpleAuthConfigProvider.java
new file mode 100644
index 0000000..476e978
--- /dev/null
+++ b/java/org/apache/catalina/authenticator/jaspic/SimpleAuthConfigProvider.java
@@ -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.authenticator.jaspic;
+
+import java.util.Map;
+
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.message.AuthException;
+import javax.security.auth.message.config.AuthConfigFactory;
+import javax.security.auth.message.config.AuthConfigProvider;
+import javax.security.auth.message.config.ClientAuthConfig;
+import javax.security.auth.message.config.ServerAuthConfig;
+
+/**
+ * Basic implementation primarily intended for use when using third-party
+ * {@link javax.security.auth.message.module.ServerAuthModule} implementations
+ * that only provide the module.
+ */
+public class SimpleAuthConfigProvider implements AuthConfigProvider {
+
+ private final Map<String,String> properties;
+
+ private volatile ServerAuthConfig serverAuthConfig;
+
+ public SimpleAuthConfigProvider(Map<String,String> properties, AuthConfigFactory factory) {
+ this.properties = properties;
+ if (factory != null) {
+ factory.registerConfigProvider(this, null, null, "Automatic registration");
+ }
+ }
+
+
+ /**
+ * {@inheritDoc}
+ * <p>
+ * This implementation does not support client-side authentication and
+ * therefore always returns {@code null}.
+ */
+ @Override
+ public ClientAuthConfig getClientAuthConfig(String layer, String appContext,
+ CallbackHandler handler) throws AuthException {
+ return null;
+ }
+
+
+ @Override
+ public ServerAuthConfig getServerAuthConfig(String layer, String appContext,
+ CallbackHandler handler) throws AuthException {
+ ServerAuthConfig serverAuthConfig = this.serverAuthConfig;
+ if (serverAuthConfig == null) {
+ synchronized (this) {
+ if (this.serverAuthConfig == null) {
+ this.serverAuthConfig = createServerAuthConfig(layer, appContext, handler, properties);
+ }
+ serverAuthConfig = this.serverAuthConfig;
+ }
+ }
+ return serverAuthConfig;
+ }
+
+
+ protected ServerAuthConfig createServerAuthConfig(String layer, String appContext,
+ CallbackHandler handler, Map<String,String> properties) {
+ return new SimpleServerAuthConfig(layer, appContext, handler, properties);
+ }
+
+
+ @Override
+ public void refresh() {
+ ServerAuthConfig serverAuthConfig = this.serverAuthConfig;
+ if (serverAuthConfig != null) {
+ serverAuthConfig.refresh();
+ }
+ }
+}
diff --git a/java/org/apache/catalina/authenticator/jaspic/SimpleServerAuthConfig.java b/java/org/apache/catalina/authenticator/jaspic/SimpleServerAuthConfig.java
new file mode 100644
index 0000000..7fa5b32
--- /dev/null
+++ b/java/org/apache/catalina/authenticator/jaspic/SimpleServerAuthConfig.java
@@ -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.catalina.authenticator.jaspic;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.security.auth.Subject;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.message.AuthException;
+import javax.security.auth.message.MessageInfo;
+import javax.security.auth.message.config.ServerAuthConfig;
+import javax.security.auth.message.config.ServerAuthContext;
+import javax.security.auth.message.module.ServerAuthModule;
+
+import org.apache.tomcat.util.res.StringManager;
+
+/**
+ * Basic implementation primarily intended for use when using third-party
+ * {@link ServerAuthModule} implementations that only provide the module. This
+ * implementation supports configuring the {@link ServerAuthContext} with
+ * multiple modules.
+ */
+public class SimpleServerAuthConfig implements ServerAuthConfig {
+
+ private static StringManager sm = StringManager.getManager(SimpleServerAuthConfig.class);
+
+ private static final String SERVER_AUTH_MODULE_KEY_PREFIX =
+ "org.apache.catalina.authenticator.jaspic.ServerAuthModule.";
+
+ private final String layer;
+ private final String appContext;
+ private final CallbackHandler handler;
+ private final Map<String,String> properties;
+
+ private volatile ServerAuthContext serverAuthContext;
+
+ public SimpleServerAuthConfig(String layer, String appContext, CallbackHandler handler,
+ Map<String,String> properties) {
+ this.layer = layer;
+ this.appContext = appContext;
+ this.handler = handler;
+ this.properties = properties;
+ }
+
+
+ @Override
+ public String getMessageLayer() {
+ return layer;
+ }
+
+
+ @Override
+ public String getAppContext() {
+ return appContext;
+ }
+
+
+ @Override
+ public String getAuthContextID(MessageInfo messageInfo) {
+ return messageInfo.toString();
+ }
+
+
+ @Override
+ public void refresh() {
+ serverAuthContext = null;
+ }
+
+
+ @Override
+ public boolean isProtected() {
+ return false;
+ }
+
+
+ @SuppressWarnings({"rawtypes", "unchecked"}) // JASPIC API uses raw types
+ @Override
+ public ServerAuthContext getAuthContext(String authContextID, Subject serviceSubject,
+ Map properties) throws AuthException {
+ ServerAuthContext serverAuthContext = this.serverAuthContext;
+ if (serverAuthContext == null) {
+ synchronized (this) {
+ if (this.serverAuthContext == null) {
+ Map<String,String> mergedProperties = new HashMap<>();
+ if (this.properties != null) {
+ mergedProperties.putAll(this.properties);
+ }
+ if (properties != null) {
+ mergedProperties.putAll(properties);
+ }
+
+ List<ServerAuthModule> modules = new ArrayList<>();
+ int moduleIndex = 1;
+ String key = SERVER_AUTH_MODULE_KEY_PREFIX + moduleIndex;
+ String moduleClassName = mergedProperties.get(key);
+ while (moduleClassName != null) {
+ try {
+ Class<?> clazz = Class.forName(moduleClassName);
+ ServerAuthModule module = (ServerAuthModule) clazz.newInstance();
+ module.initialize(null, null, handler, mergedProperties);
+ modules.add(module);
+ } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
+ AuthException ae = new AuthException();
+ ae.initCause(e);
+ throw ae;
+ }
+
+ // Look for the next module
+ moduleIndex++;
+ key = SERVER_AUTH_MODULE_KEY_PREFIX + moduleIndex;
+ moduleClassName = mergedProperties.get(key);
+ }
+
+ if (modules.size() == 0) {
+ throw new AuthException(sm.getString("simpleServerAuthConfig.noModules"));
+ }
+
+ this.serverAuthContext = createServerAuthContext(modules);
+ }
+ serverAuthContext = this.serverAuthContext;
+ }
+ }
+
+ return serverAuthContext;
+ }
+
+
+ protected ServerAuthContext createServerAuthContext(List<ServerAuthModule> modules) {
+ return new SimpleServerAuthContext(modules);
+ }
+}
diff --git a/java/org/apache/catalina/authenticator/jaspic/SimpleServerAuthContext.java b/java/org/apache/catalina/authenticator/jaspic/SimpleServerAuthContext.java
new file mode 100644
index 0000000..d36cb0f
--- /dev/null
+++ b/java/org/apache/catalina/authenticator/jaspic/SimpleServerAuthContext.java
@@ -0,0 +1,74 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.catalina.authenticator.jaspic;
+
+import java.util.List;
+
+import javax.security.auth.Subject;
+import javax.security.auth.message.AuthException;
+import javax.security.auth.message.AuthStatus;
+import javax.security.auth.message.MessageInfo;
+import javax.security.auth.message.config.ServerAuthContext;
+import javax.security.auth.message.module.ServerAuthModule;
+
+/**
+ * Basic implementation primarily intended for use when using third-party
+ * {@link ServerAuthModule} implementations that only provide the module. This
+ * implementation supports multiple modules and will treat the user as
+ * authenticated if any one module is able to authenticate the user.
+ */
+public class SimpleServerAuthContext implements ServerAuthContext {
+
+ private final List<ServerAuthModule> modules;
+
+
+ public SimpleServerAuthContext(List<ServerAuthModule> modules) {
+ this.modules = modules;
+ }
+
+
+ @SuppressWarnings("unchecked") // JASPIC API uses raw types
+ @Override
+ public AuthStatus validateRequest(MessageInfo messageInfo, Subject clientSubject,
+ Subject serviceSubject) throws AuthException {
+ for (int moduleIndex = 0; moduleIndex < modules.size(); moduleIndex++) {
+ ServerAuthModule module = modules.get(moduleIndex);
+ AuthStatus result = module.validateRequest(messageInfo, clientSubject, serviceSubject);
+ if (result != AuthStatus.SEND_FAILURE) {
+ messageInfo.getMap().put("moduleIndex", Integer.valueOf(moduleIndex));
+ return result;
+ }
+ }
+ return AuthStatus.SEND_FAILURE;
+ }
+
+
+ @Override
+ public AuthStatus secureResponse(MessageInfo messageInfo, Subject serviceSubject)
+ throws AuthException {
+ ServerAuthModule module = modules.get(((Integer) messageInfo.getMap().get("moduleIndex")).intValue());
+ return module.secureResponse(messageInfo, serviceSubject);
+ }
+
+
+ @Override
+ public void cleanSubject(MessageInfo messageInfo, Subject subject) throws AuthException {
+ for (ServerAuthModule module : modules) {
+ module.cleanSubject(messageInfo, subject);
+ }
+ }
+}
diff --git a/java/org/apache/catalina/authenticator/mbeans-descriptors.xml b/java/org/apache/catalina/authenticator/mbeans-descriptors.xml
index c07c26e..de95ae4 100644
--- a/java/org/apache/catalina/authenticator/mbeans-descriptors.xml
+++ b/java/org/apache/catalina/authenticator/mbeans-descriptors.xml
@@ -1,4 +1,4 @@
-<?xml version="1.0"?>
+<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
diff --git a/java/org/apache/catalina/comet/CometEvent.java b/java/org/apache/catalina/comet/CometEvent.java
deleted file mode 100644
index 4cadc6c..0000000
--- a/java/org/apache/catalina/comet/CometEvent.java
+++ /dev/null
@@ -1,146 +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.comet;
-
-import java.io.IOException;
-
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-/**
- * The CometEvent interface.
- *
- * @author Remy Maucherat
- */
-public interface CometEvent {
-
- /**
- * Enumeration describing the major events that the container can invoke
- * the CometProcessors event() method with.<br>
- * BEGIN - will be called at the beginning
- * of the processing of the connection. It can be used to initialize any relevant
- * fields using the request and response objects. Between the end of the processing
- * of this event, and the beginning of the processing of the end or error events,
- * it is possible to use the response object to write data on the open connection.
- * Note that the response object and dependent OutputStream and Writer are still
- * not synchronized, so when they are accessed by multiple threads,
- * synchronization is mandatory. After processing the initial event, the request
- * is considered to be committed.<br>
- * READ - This indicates that input data is available, and that one read can be made
- * without blocking. The available and ready methods of the InputStream or
- * Reader may be used to determine if there is a risk of blocking: the servlet
- * should read while data is reported available. When encountering a read error,
- * the servlet should report it by propagating the exception properly. Throwing
- * an exception will cause the error event to be invoked, and the connection
- * will be closed.
- * Alternately, it is also possible to catch any exception, perform clean up
- * on any data structure the servlet may be using, and using the close method
- * of the event. It is not allowed to attempt reading data from the request
- * object outside of the execution of this method.<br>
- * END - End may be called to end the processing of the request. Fields that have
- * been initialized in the begin method should be reset. After this event has
- * been processed, the request and response objects, as well as all their dependent
- * objects will be recycled and used to process other requests. End will also be
- * called when data is available and the end of file is reached on the request input
- * (this usually indicates the client has pipelined a request).<br>
- * ERROR - Error will be called by the container in the case where an IO exception
- * or a similar unrecoverable error occurs on the connection. Fields that have
- * been initialized in the begin method should be reset. After this event has
- * been processed, the request and response objects, as well as all their dependent
- * objects will be recycled and used to process other requests.
- */
- public enum EventType {BEGIN, READ, END, ERROR}
-
-
- /**
- * Event details.<br>
- * TIMEOUT - the connection timed out (sub type of ERROR); note that this ERROR type is not fatal, and
- * the connection will not be closed unless the servlet uses the close method of the event<br>
- * CLIENT_DISCONNECT - the client connection was closed (sub type of ERROR)<br>
- * IOEXCEPTION - an IO exception occurred, such as invalid content, for example, an invalid chunk block (sub type of ERROR)<br>
- * WEBAPP_RELOAD - the webapplication is being reloaded (sub type of END)<br>
- * SERVER_SHUTDOWN - the server is shutting down (sub type of END)<br>
- * SESSION_END - the servlet ended the session (sub type of END)
- */
- public enum EventSubType { TIMEOUT, CLIENT_DISCONNECT, IOEXCEPTION, WEBAPP_RELOAD, SERVER_SHUTDOWN, SESSION_END }
-
-
- /**
- * Returns the HttpServletRequest.
- *
- * @return HttpServletRequest
- */
- public HttpServletRequest getHttpServletRequest();
-
- /**
- * Returns the HttpServletResponse.
- *
- * @return HttpServletResponse
- */
- public HttpServletResponse getHttpServletResponse();
-
- /**
- * Returns the event type.
- *
- * @return EventType
- */
- public EventType getEventType();
-
- /**
- * Returns the sub type of this event.
- *
- * @return EventSubType
- */
- public EventSubType getEventSubType();
-
- /**
- * Ends the Comet session. This signals to the container that
- * the container wants to end the comet session. This will send back to the
- * client a notice that the server has no more data to send as part of this
- * request. The servlet should perform any needed cleanup as if it had received
- * an END or ERROR event.
- *
- * @throws IOException if an IO exception occurs
- */
- public void close() throws IOException;
-
- /**
- * Sets the timeout for this Comet connection. Please NOTE, that the implementation
- * of a per connection timeout is OPTIONAL and MAY NOT be implemented.<br>
- * This method sets the timeout in milliseconds of idle time on the connection.
- * The timeout is reset every time data is received from the connection or data is flushed
- * using <code>response.flushBuffer()</code>. If a timeout occurs, the
- * <code>error(HttpServletRequest, HttpServletResponse)</code> method is invoked. The
- * web application SHOULD NOT attempt to reuse the request and response objects after a timeout
- * as the <code>error(HttpServletRequest, HttpServletResponse)</code> method indicates.<br>
- * This method should not be called asynchronously, as that will have no effect.
- *
- * @param timeout The timeout in milliseconds for this connection, must be a positive value, larger than 0
- * @throws IOException An IOException may be thrown to indicate an IO error,
- * or that the EOF has been reached on the connection
- * @throws ServletException An exception has occurred, as specified by the root
- * cause
- * @throws UnsupportedOperationException if per connection timeout is not supported, either at all or at this phase
- * of the invocation.
- */
- public void setTimeout(int timeout)
- throws IOException, ServletException, UnsupportedOperationException;
-
-}
diff --git a/java/org/apache/catalina/comet/CometFilter.java b/java/org/apache/catalina/comet/CometFilter.java
deleted file mode 100644
index ffb0d1c..0000000
--- a/java/org/apache/catalina/comet/CometFilter.java
+++ /dev/null
@@ -1,81 +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.comet;
-
-import java.io.IOException;
-
-import javax.servlet.Filter;
-import javax.servlet.ServletException;
-
-/**
- * A Comet filter, similar to regular filters, performs filtering tasks on either
- * the request to a resource (a Comet servlet), or on the response from a resource, or both.
- * <br><br>
- * Filters perform filtering in the <code>doFilterEvent</code> method. Every Filter has access to
- * a FilterConfig object from which it can obtain its initialization parameters, a
- * reference to the ServletContext which it can use, for example, to load resources
- * needed for filtering tasks.
- * <p>
- * Filters are configured in the deployment descriptor of a web application
- * <p>
- * Examples that have been identified for this design are<br>
- * 1) Authentication Filters <br>
- * 2) Logging and Auditing Filters <br>
- * 3) Image conversion Filters <br>
- * 4) Data compression Filters <br>
- * 5) Encryption Filters <br>
- * 6) Tokenizing Filters <br>
- * 7) Filters that trigger resource access events <br>
- * 8) XSL/T filters <br>
- * 9) Mime-type chain Filter <br>
- * <br>
- *
- * @author Remy Maucherat
- */
-public interface CometFilter extends Filter {
-
-
- /**
- * The <code>doFilterEvent</code> method of the CometFilter is called by the container
- * each time a request/response pair is passed through the chain due
- * to a client event for a resource at the end of the chain. The CometFilterChain passed in to this
- * method allows the Filter to pass on the event to the next entity in the
- * chain.<p>
- * A typical implementation of this method would follow the following pattern:- <br>
- * 1. Examine the request<br>
- * 2. Optionally wrap the request object contained in the event with a custom implementation to
- * filter content or headers for input filtering and pass a CometEvent instance containing
- * the wrapped request to the next filter<br>
- * 3. Optionally wrap the response object contained in the event with a custom implementation to
- * filter content or headers for output filtering and pass a CometEvent instance containing
- * the wrapped request to the next filter<br>
- * 4. a) <strong>Either</strong> invoke the next entity in the chain using the CometFilterChain object (<code>chain.doFilterEvent()</code>), <br>
- * 4. b) <strong>or</strong> not pass on the request/response pair to the next entity in the filter chain to block the event processing<br>
- * 5. Directly set fields on the response after invocation of the next entity in the filter chain.
- *
- * @param event the event that is being processed. Another event may be passed along the chain.
- * @param chain
- * @throws IOException
- * @throws ServletException
- */
- public void doFilterEvent(CometEvent event, CometFilterChain chain)
- throws IOException, ServletException;
-
-
-}
diff --git a/java/org/apache/catalina/comet/CometFilterChain.java b/java/org/apache/catalina/comet/CometFilterChain.java
deleted file mode 100644
index 68ff5f1..0000000
--- a/java/org/apache/catalina/comet/CometFilterChain.java
+++ /dev/null
@@ -1,45 +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.comet;
-
-import java.io.IOException;
-
-import javax.servlet.ServletException;
-
-/**
- * A CometFilterChain is an object provided by the servlet container to the developer
- * giving a view into the invocation chain of a filtered event for a resource. Filters
- * use the CometFilterChain to invoke the next filter in the chain, or if the calling filter
- * is the last filter in the chain, to invoke the resource at the end of the chain.
- *
- * @author Remy Maucherat
- */
-public interface CometFilterChain {
-
-
- /**
- * Causes the next filter in the chain to be invoked, or if the calling filter is the last filter
- * in the chain, causes the resource at the end of the chain to be invoked.
- *
- * @param event the event to pass along the chain.
- */
- public void doFilterEvent(CometEvent event) throws IOException, ServletException;
-
-
-}
diff --git a/java/org/apache/catalina/comet/CometProcessor.java b/java/org/apache/catalina/comet/CometProcessor.java
deleted file mode 100644
index 4e22f8d..0000000
--- a/java/org/apache/catalina/comet/CometProcessor.java
+++ /dev/null
@@ -1,45 +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.comet;
-
-import java.io.IOException;
-
-import javax.servlet.Servlet;
-import javax.servlet.ServletException;
-
-/**
- * This interface should be implemented by servlets which would like to handle
- * asynchronous IO, receiving events when data is available for reading, and
- * being able to output data without the need for being invoked by the container.
- * Note: When this interface is implemented, the service method of the servlet will
- * never be called, and will be replaced with a begin event.
- */
-public interface CometProcessor extends Servlet{
-
- /**
- * Process the given Comet event.
- *
- * @param event The Comet event that will be processed
- * @throws IOException
- * @throws ServletException
- */
- public void event(CometEvent event)
- throws IOException, ServletException;
-
-}
diff --git a/java/org/apache/catalina/connector/CometEventImpl.java b/java/org/apache/catalina/connector/CometEventImpl.java
deleted file mode 100644
index 9b45d91..0000000
--- a/java/org/apache/catalina/connector/CometEventImpl.java
+++ /dev/null
@@ -1,148 +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.connector;
-
-import java.io.IOException;
-
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.apache.catalina.Globals;
-import org.apache.catalina.comet.CometEvent;
-import org.apache.tomcat.util.res.StringManager;
-
-public class CometEventImpl implements CometEvent {
-
-
- /**
- * The string manager for this package.
- */
- protected static final StringManager sm =
- StringManager.getManager(Constants.Package);
-
-
- public CometEventImpl(Request request, Response response) {
- this.request = request;
- this.response = response;
- }
-
-
- // ----------------------------------------------------- Instance Variables
-
-
- /**
- * Associated request.
- */
- protected Request request = null;
-
-
- /**
- * Associated response.
- */
- protected Response response = null;
-
-
- /**
- * Event type.
- */
- protected EventType eventType = EventType.BEGIN;
-
-
- /**
- * Event sub type.
- */
- protected EventSubType eventSubType = null;
-
-
- // --------------------------------------------------------- Public Methods
-
- /**
- * Clear the event.
- */
- public void clear() {
- request = null;
- response = null;
- }
-
- public void setEventType(EventType eventType) {
- this.eventType = eventType;
- }
-
- public void setEventSubType(EventSubType eventSubType) {
- this.eventSubType = eventSubType;
- }
-
- @Override
- public void close() throws IOException {
- if (request == null) {
- throw new IllegalStateException(sm.getString("cometEvent.nullRequest"));
- }
- request.finishRequest();
- response.finishResponse();
- if (request.isComet()) {
- request.cometClose();
- }
- }
-
- @Override
- public EventSubType getEventSubType() {
- return eventSubType;
- }
-
- @Override
- public EventType getEventType() {
- return eventType;
- }
-
- @Override
- public HttpServletRequest getHttpServletRequest() {
- return request.getRequest();
- }
-
- @Override
- public HttpServletResponse getHttpServletResponse() {
- return response.getResponse();
- }
-
- @Override
- public void setTimeout(int timeout) throws IOException, ServletException,
- UnsupportedOperationException {
- if (Boolean.TRUE.equals(request.getAttribute(Globals.COMET_TIMEOUT_SUPPORTED_ATTR))) {
- request.setAttribute(Globals.COMET_TIMEOUT_ATTR,
- Integer.valueOf(timeout));
- if (request.isComet()) {
- request.setCometTimeout(timeout);
- }
- } else {
- throw new UnsupportedOperationException();
- }
- }
-
- @Override
- public String toString() {
- StringBuilder buf = new StringBuilder();
- buf.append(super.toString());
- buf.append("[EventType:");
- buf.append(eventType);
- buf.append(", EventSubType:");
- buf.append(eventSubType);
- buf.append("]");
- return buf.toString();
- }
-
-}
diff --git a/java/org/apache/catalina/connector/Connector.java b/java/org/apache/catalina/connector/Connector.java
index 4adfc48..bf01d7d 100644
--- a/java/org/apache/catalina/connector/Connector.java
+++ b/java/org/apache/catalina/connector/Connector.java
@@ -32,9 +32,13 @@ import org.apache.catalina.core.AprLifecycleListener;
import org.apache.catalina.util.LifecycleMBeanBase;
import org.apache.coyote.Adapter;
import org.apache.coyote.ProtocolHandler;
+import org.apache.coyote.UpgradeProtocol;
+import org.apache.coyote.http11.AbstractHttp11JsseProtocol;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.IntrospectionUtils;
+import org.apache.tomcat.util.net.SSLHostConfig;
+import org.apache.tomcat.util.net.openssl.OpenSSLImplementation;
import org.apache.tomcat.util.res.StringManager;
@@ -163,11 +167,16 @@ public class Connector extends LifecycleMBeanBase {
/**
* The string manager for this package.
*/
- protected static final StringManager sm =
- StringManager.getManager(Constants.Package);
+ protected static final StringManager sm = StringManager.getManager(Connector.class);
/**
+ * The maximum number of cookies permitted for a request. Use a value less
+ * than zero for no limit. Defaults to 200.
+ */
+ private int maxCookieCount = 200;
+
+ /**
* The maximum number of parameters (GET plus POST) which will be
* automatically parsed by the container. 10000 by default. A value of less
* than 0 means no limit.
@@ -225,33 +234,35 @@ public class Connector extends LifecycleMBeanBase {
protected Adapter adapter = null;
- /**
- * URI encoding.
- */
- protected String URIEncoding = null;
- protected String URIEncodingLower = null;
+ /**
+ * URI encoding.
+ */
+ protected String URIEncoding = null;
+ protected String URIEncodingLower = null;
- /**
- * URI encoding as body.
- */
- protected boolean useBodyEncodingForURI = false;
+ /**
+ * URI encoding as body.
+ */
+ protected boolean useBodyEncodingForURI = false;
- protected static final HashMap<String,String> replacements =
- new HashMap<>();
- static {
- replacements.put("acceptCount", "backlog");
- replacements.put("connectionLinger", "soLinger");
- replacements.put("connectionTimeout", "soTimeout");
- replacements.put("rootFile", "rootfile");
- }
+ protected static final HashMap<String,String> replacements = new HashMap<>();
+ static {
+ replacements.put("acceptCount", "backlog");
+ replacements.put("connectionLinger", "soLinger");
+ replacements.put("connectionTimeout", "soTimeout");
+ replacements.put("rootFile", "rootfile");
+ }
// ------------------------------------------------------------- Properties
/**
- * Return a configured property.
+ * Return a property from the protocol handler.
+ *
+ * @param name the property name
+ * @return the property value
*/
public Object getProperty(String name) {
String repl = name;
@@ -263,7 +274,11 @@ public class Connector extends LifecycleMBeanBase {
/**
- * Set a configured property.
+ * Set a property on the protocol handler.
+ *
+ * @param name the property name
+ * @param value the property value
+ * @return <code>true</code> if the property was successfully set
*/
public boolean setProperty(String name, String value) {
String repl = name;
@@ -273,8 +288,12 @@ public class Connector extends LifecycleMBeanBase {
return IntrospectionUtils.setProperty(protocolHandler, repl, value);
}
+
/**
- * Return a configured property.
+ * Return a property from the protocol handler.
+ *
+ * @param name the property name
+ * @return the property value
*/
public Object getAttribute(String name) {
return getProperty(name);
@@ -282,7 +301,10 @@ public class Connector extends LifecycleMBeanBase {
/**
- * Set a configured property.
+ * Set a property on the protocol handler.
+ *
+ * @param name the property name
+ * @param value the property value
*/
public void setAttribute(String name, Object value) {
setProperty(name, String.valueOf(value));
@@ -290,12 +312,10 @@ public class Connector extends LifecycleMBeanBase {
/**
- * Return the <code>Service</code> with which we are associated (if any).
+ * @return the <code>Service</code> with which we are associated (if any).
*/
public Service getService() {
-
- return (this.service);
-
+ return this.service;
}
@@ -305,19 +325,16 @@ public class Connector extends LifecycleMBeanBase {
* @param service The service that owns this Engine
*/
public void setService(Service service) {
-
this.service = service;
-
}
/**
- * True if the TRACE method is allowed. Default value is "false".
+ * @return <code>true</code> if the TRACE method is allowed. Default value
+ * is <code>false</code>.
*/
public boolean getAllowTrace() {
-
- return (this.allowTrace);
-
+ return this.allowTrace;
}
@@ -327,20 +344,16 @@ public class Connector extends LifecycleMBeanBase {
* @param allowTrace The new allowTrace flag
*/
public void setAllowTrace(boolean allowTrace) {
-
this.allowTrace = allowTrace;
setProperty("allowTrace", String.valueOf(allowTrace));
-
}
/**
- * Return the default timeout for async requests in ms.
+ * @return the default timeout for async requests in ms.
*/
public long getAsyncTimeout() {
-
return asyncTimeout;
-
}
@@ -350,20 +363,16 @@ public class Connector extends LifecycleMBeanBase {
* @param asyncTimeout The new timeout in ms.
*/
public void setAsyncTimeout(long asyncTimeout) {
-
this.asyncTimeout= asyncTimeout;
setProperty("asyncTimeout", String.valueOf(asyncTimeout));
-
}
/**
- * Return the "enable DNS lookups" flag.
+ * @return the "enable DNS lookups" flag.
*/
public boolean getEnableLookups() {
-
- return (this.enableLookups);
-
+ return this.enableLookups;
}
@@ -373,33 +382,23 @@ public class Connector extends LifecycleMBeanBase {
* @param enableLookups The new "enable DNS lookups" flag value
*/
public void setEnableLookups(boolean enableLookups) {
-
this.enableLookups = enableLookups;
setProperty("enableLookups", String.valueOf(enableLookups));
-
}
- /**
- * Return the maximum number of headers that are allowed by the container. A
- * value of less than 0 means no limit.
- */
- public int getMaxHeaderCount() {
- return ((Integer) getProperty("maxHeaderCount")).intValue();
+ public int getMaxCookieCount() {
+ return maxCookieCount;
}
- /**
- * Set the maximum number of headers in a request that are allowed by the
- * container. A value of less than 0 means no limit.
- *
- * @param maxHeaderCount The new setting
- */
- public void setMaxHeaderCount(int maxHeaderCount) {
- setProperty("maxHeaderCount", String.valueOf(maxHeaderCount));
+
+ public void setMaxCookieCount(int maxCookieCount) {
+ this.maxCookieCount = maxCookieCount;
}
+
/**
- * Return the maximum number of parameters (GET plus POST) that will be
+ * @return the maximum number of parameters (GET plus POST) that will be
* automatically parsed by the container. A value of less than 0 means no
* limit.
*/
@@ -417,17 +416,16 @@ public class Connector extends LifecycleMBeanBase {
*/
public void setMaxParameterCount(int maxParameterCount) {
this.maxParameterCount = maxParameterCount;
+ setProperty("maxParameterCount", String.valueOf(maxParameterCount));
}
/**
- * Return the maximum size of a POST which will be automatically
+ * @return the maximum size of a POST which will be automatically
* parsed by the container.
*/
public int getMaxPostSize() {
-
- return (maxPostSize);
-
+ return maxPostSize;
}
@@ -439,19 +437,17 @@ public class Connector extends LifecycleMBeanBase {
* be automatically parsed by the container
*/
public void setMaxPostSize(int maxPostSize) {
-
this.maxPostSize = maxPostSize;
+ setProperty("maxPostSize", String.valueOf(maxPostSize));
}
/**
- * Return the maximum size of a POST which will be saved by the container
+ * @return the maximum size of a POST which will be saved by the container
* during authentication.
*/
public int getMaxSavePostSize() {
-
- return (maxSavePostSize);
-
+ return maxSavePostSize;
}
@@ -463,50 +459,55 @@ public class Connector extends LifecycleMBeanBase {
* be saved by the container during authentication.
*/
public void setMaxSavePostSize(int maxSavePostSize) {
-
this.maxSavePostSize = maxSavePostSize;
setProperty("maxSavePostSize", String.valueOf(maxSavePostSize));
}
+ /**
+ * @return the HTTP methods which will support body parameters parsing
+ */
public String getParseBodyMethods() {
-
return this.parseBodyMethods;
-
}
+
+ /**
+ * Set list of HTTP methods which should allow body parameter
+ * parsing. This defaults to <code>POST</code>.
+ *
+ * @param methods Comma separated list of HTTP method names
+ */
public void setParseBodyMethods(String methods) {
HashSet<String> methodSet = new HashSet<>();
- if( null != methods ) {
+ if (null != methods) {
methodSet.addAll(Arrays.asList(methods.split("\\s*,\\s*")));
}
- if( methodSet.contains("TRACE") ) {
+ if (methodSet.contains("TRACE")) {
throw new IllegalArgumentException(sm.getString("coyoteConnector.parseBodyMethodNoTrace"));
}
this.parseBodyMethods = methods;
this.parseBodyMethodsSet = methodSet;
-
+ setProperty("parseBodyMethods", methods);
}
- protected boolean isParseBodyMethod(String method) {
+ protected boolean isParseBodyMethod(String method) {
return parseBodyMethodsSet.contains(method);
-
}
+
/**
- * Return the port number on which this connector is configured to listen
+ * @return the port number on which this connector is configured to listen
* for requests. The special value of 0 means select a random free port
* when the socket is bound.
*/
public int getPort() {
-
- return (this.port);
-
+ return this.port;
}
@@ -516,15 +517,13 @@ public class Connector extends LifecycleMBeanBase {
* @param port The new port number
*/
public void setPort(int port) {
-
this.port = port;
setProperty("port", String.valueOf(port));
-
}
/**
- * Return the port number on which this connector is listening to requests.
+ * @return the port number on which this connector is listening to requests.
* If the special value for {@link #getPort} of zero is used then this method
* will report the actual port bound.
*/
@@ -534,23 +533,21 @@ public class Connector extends LifecycleMBeanBase {
/**
- * Return the Coyote protocol handler in use.
+ * @return the Coyote protocol handler in use.
*/
public String getProtocol() {
-
- if ("org.apache.coyote.http11.Http11NioProtocol".equals
- (getProtocolHandlerClassName())
- || "org.apache.coyote.http11.Http11AprProtocol".equals
- (getProtocolHandlerClassName())) {
+ if (("org.apache.coyote.http11.Http11NioProtocol".equals(getProtocolHandlerClassName()) &&
+ (!AprLifecycleListener.isAprAvailable() || !AprLifecycleListener.getUseAprConnector())) ||
+ "org.apache.coyote.http11.Http11AprProtocol".equals(getProtocolHandlerClassName()) &&
+ AprLifecycleListener.getUseAprConnector()) {
return "HTTP/1.1";
- } else if ("org.apache.coyote.ajp.AjpNioProtocol".equals
- (getProtocolHandlerClassName())
- || "org.apache.coyote.ajp.AjpAprProtocol".equals
- (getProtocolHandlerClassName())) {
+ } else if (("org.apache.coyote.ajp.AjpNioProtocol".equals(getProtocolHandlerClassName()) &&
+ (!AprLifecycleListener.isAprAvailable() || !AprLifecycleListener.getUseAprConnector())) ||
+ "org.apache.coyote.ajp.AjpAprProtocol".equals(getProtocolHandlerClassName()) &&
+ AprLifecycleListener.getUseAprConnector()) {
return "AJP/1.3";
}
return getProtocolHandlerClassName();
-
}
@@ -558,44 +555,39 @@ public class Connector extends LifecycleMBeanBase {
* Set the Coyote protocol which will be used by the connector.
*
* @param protocol The Coyote protocol name
+ *
+ * @deprecated Will be removed in Tomcat 9. Protocol must be configured via
+ * the constructor
*/
+ @Deprecated
public void setProtocol(String protocol) {
- if (AprLifecycleListener.isAprAvailable()) {
- if ("HTTP/1.1".equals(protocol)) {
- setProtocolHandlerClassName
- ("org.apache.coyote.http11.Http11AprProtocol");
- } else if ("AJP/1.3".equals(protocol)) {
- setProtocolHandlerClassName
- ("org.apache.coyote.ajp.AjpAprProtocol");
- } else if (protocol != null) {
- setProtocolHandlerClassName(protocol);
+ boolean aprConnector = AprLifecycleListener.isAprAvailable() &&
+ AprLifecycleListener.getUseAprConnector();
+
+ if ("HTTP/1.1".equals(protocol) || protocol == null) {
+ if (aprConnector) {
+ setProtocolHandlerClassName("org.apache.coyote.http11.Http11AprProtocol");
} else {
- setProtocolHandlerClassName
- ("org.apache.coyote.http11.Http11AprProtocol");
+ setProtocolHandlerClassName("org.apache.coyote.http11.Http11NioProtocol");
}
- } else {
- if ("HTTP/1.1".equals(protocol)) {
- setProtocolHandlerClassName
- ("org.apache.coyote.http11.Http11NioProtocol");
- } else if ("AJP/1.3".equals(protocol)) {
- setProtocolHandlerClassName
- ("org.apache.coyote.ajp.AjpNioProtocol");
- } else if (protocol != null) {
- setProtocolHandlerClassName(protocol);
+ } else if ("AJP/1.3".equals(protocol)) {
+ if (aprConnector) {
+ setProtocolHandlerClassName("org.apache.coyote.ajp.AjpAprProtocol");
+ } else {
+ setProtocolHandlerClassName("org.apache.coyote.ajp.AjpNioProtocol");
}
+ } else {
+ setProtocolHandlerClassName(protocol);
}
-
}
/**
- * Return the class name of the Coyote protocol handler in use.
+ * @return the class name of the Coyote protocol handler in use.
*/
public String getProtocolHandlerClassName() {
-
- return (this.protocolHandlerClassName);
-
+ return this.protocolHandlerClassName;
}
@@ -604,31 +596,29 @@ public class Connector extends LifecycleMBeanBase {
* by the connector.
*
* @param protocolHandlerClassName The new class name
+ *
+ * @deprecated Will be removed in Tomcat 9. Protocol must be configured via
+ * the constructor
*/
+ @Deprecated
public void setProtocolHandlerClassName(String protocolHandlerClassName) {
-
this.protocolHandlerClassName = protocolHandlerClassName;
-
}
/**
- * Return the protocol handler associated with the connector.
+ * @return the protocol handler associated with the connector.
*/
public ProtocolHandler getProtocolHandler() {
-
- return (this.protocolHandler);
-
+ return this.protocolHandler;
}
/**
- * Return the proxy server name for this Connector.
+ * @return the proxy server name for this Connector.
*/
public String getProxyName() {
-
- return (this.proxyName);
-
+ return this.proxyName;
}
@@ -641,21 +631,18 @@ public class Connector extends LifecycleMBeanBase {
if(proxyName != null && proxyName.length() > 0) {
this.proxyName = proxyName;
- setProperty("proxyName", proxyName);
} else {
this.proxyName = null;
}
-
+ setProperty("proxyName", this.proxyName);
}
/**
- * Return the proxy server port for this Connector.
+ * @return the proxy server port for this Connector.
*/
public int getProxyPort() {
-
- return (this.proxyPort);
-
+ return this.proxyPort;
}
@@ -665,22 +652,18 @@ public class Connector extends LifecycleMBeanBase {
* @param proxyPort The new proxy server port
*/
public void setProxyPort(int proxyPort) {
-
this.proxyPort = proxyPort;
setProperty("proxyPort", String.valueOf(proxyPort));
-
}
/**
- * Return the port number to which a request should be redirected if
+ * @return the port number to which a request should be redirected if
* it comes in on a non-SSL port and is subject to a security constraint
* with a transport guarantee that requires SSL.
*/
public int getRedirectPort() {
-
- return (this.redirectPort);
-
+ return this.redirectPort;
}
@@ -690,21 +673,17 @@ public class Connector extends LifecycleMBeanBase {
* @param redirectPort The redirect port number (non-SSL to SSL)
*/
public void setRedirectPort(int redirectPort) {
-
this.redirectPort = redirectPort;
setProperty("redirectPort", String.valueOf(redirectPort));
-
}
/**
- * Return the scheme that will be assigned to requests received
+ * @return the scheme that will be assigned to requests received
* through this connector. Default value is "http".
*/
public String getScheme() {
-
- return (this.scheme);
-
+ return this.scheme;
}
@@ -715,20 +694,16 @@ public class Connector extends LifecycleMBeanBase {
* @param scheme The new scheme
*/
public void setScheme(String scheme) {
-
this.scheme = scheme;
-
}
/**
- * Return the secure connection flag that will be assigned to requests
+ * @return the secure connection flag that will be assigned to requests
* received through this connector. Default value is "false".
*/
public boolean getSecure() {
-
- return (this.secure);
-
+ return this.secure;
}
@@ -739,72 +714,67 @@ public class Connector extends LifecycleMBeanBase {
* @param secure The new secure connection flag
*/
public void setSecure(boolean secure) {
-
this.secure = secure;
setProperty("secure", Boolean.toString(secure));
}
- /**
- * Return the character encoding to be used for the URI using the original
- * case.
- */
- public String getURIEncoding() {
- return this.URIEncoding;
- }
-
- /**
- * Return the character encoding to be used for the URI using lower case.
- */
- public String getURIEncodingLower() {
- return this.URIEncodingLower;
- }
-
-
- /**
- * Set the URI encoding to be used for the URI.
- *
- * @param URIEncoding The new URI character encoding.
- */
- public void setURIEncoding(String URIEncoding) {
- this.URIEncoding = URIEncoding;
- if (URIEncoding == null) {
- URIEncodingLower = null;
- } else {
- this.URIEncodingLower = URIEncoding.toLowerCase(Locale.ENGLISH);
- }
- setProperty("uRIEncoding", URIEncoding);
- }
+ /**
+ * @return the character encoding to be used for the URI using the original
+ * case.
+ */
+ public String getURIEncoding() {
+ return this.URIEncoding;
+ }
- /**
- * Return the true if the entity body encoding should be used for the URI.
- */
- public boolean getUseBodyEncodingForURI() {
+ /**
+ * @return the character encoding to be used for the URI using lower case.
+ */
+ public String getURIEncodingLower() {
+ return this.URIEncodingLower;
+ }
- return (this.useBodyEncodingForURI);
- }
+ /**
+ * Set the URI encoding to be used for the URI.
+ *
+ * @param URIEncoding The new URI character encoding.
+ */
+ public void setURIEncoding(String URIEncoding) {
+ this.URIEncoding = URIEncoding;
+ if (URIEncoding == null) {
+ URIEncodingLower = null;
+ } else {
+ this.URIEncodingLower = URIEncoding.toLowerCase(Locale.ENGLISH);
+ }
+ setProperty("uRIEncoding", URIEncoding);
+ }
- /**
- * Set if the entity body encoding should be used for the URI.
- *
- * @param useBodyEncodingForURI The new value for the flag.
- */
- public void setUseBodyEncodingForURI(boolean useBodyEncodingForURI) {
+ /**
+ * @return the true if the entity body encoding should be used for the URI.
+ */
+ public boolean getUseBodyEncodingForURI() {
+ return this.useBodyEncodingForURI;
+ }
- this.useBodyEncodingForURI = useBodyEncodingForURI;
- setProperty
- ("useBodyEncodingForURI", String.valueOf(useBodyEncodingForURI));
- }
+ /**
+ * Set if the entity body encoding should be used for the URI.
+ *
+ * @param useBodyEncodingForURI The new value for the flag.
+ */
+ public void setUseBodyEncodingForURI(boolean useBodyEncodingForURI) {
+ this.useBodyEncodingForURI = useBodyEncodingForURI;
+ setProperty("useBodyEncodingForURI", String.valueOf(useBodyEncodingForURI));
+ }
/**
* Indicates whether the generation of an X-Powered-By response header for
- * servlet-generated responses is enabled or disabled for this Connector.
+ * Servlet-generated responses is enabled or disabled for this Connector.
*
- * @return true if generation of X-Powered-By response header is enabled,
+ * @return <code>true</code> if generation of X-Powered-By response header is enabled,
* false otherwise
*/
public boolean getXpoweredBy() {
@@ -825,6 +795,7 @@ public class Connector extends LifecycleMBeanBase {
setProperty("xpoweredBy", String.valueOf(xpoweredBy));
}
+
/**
* Enable the use of IP-based virtual hosting.
*
@@ -836,8 +807,11 @@ public class Connector extends LifecycleMBeanBase {
setProperty("useIPVHosts", String.valueOf(useIPVHosts));
}
+
/**
* Test if IP-based virtual hosting is enabled.
+ *
+ * @return <code>true</code> if IP vhosts are enabled
*/
public boolean getUseIPVHosts() {
return useIPVHosts;
@@ -852,12 +826,34 @@ public class Connector extends LifecycleMBeanBase {
return "Internal";
}
- // --------------------------------------------------------- Public Methods
+ public void addSslHostConfig(SSLHostConfig sslHostConfig) {
+ protocolHandler.addSslHostConfig(sslHostConfig);
+ }
+
+
+ public SSLHostConfig[] findSslHostConfigs() {
+ return protocolHandler.findSslHostConfigs();
+ }
+
+
+ public void addUpgradeProtocol(UpgradeProtocol upgradeProtocol) {
+ protocolHandler.addUpgradeProtocol(upgradeProtocol);
+ }
+
+
+ public UpgradeProtocol[] findUpgradeProtocols() {
+ return protocolHandler.findUpgradeProtocols();
+ }
+
+
+ // --------------------------------------------------------- Public Methods
/**
* Create (or allocate) and return a Request object suitable for
* specifying the contents of a Request to the responsible Container.
+ *
+ * @return a new Servlet request object
*/
public Request createRequest() {
@@ -871,6 +867,8 @@ public class Connector extends LifecycleMBeanBase {
/**
* Create (or allocate) and return a Response object suitable for
* receiving the contents of a Response from the responsible Container.
+ *
+ * @return a new Servlet response object
*/
public Response createResponse() {
@@ -923,7 +921,7 @@ public class Connector extends LifecycleMBeanBase {
/**
- * Pause the connector.
+ * Resume the connector.
*/
public void resume() {
try {
@@ -955,13 +953,22 @@ public class Connector extends LifecycleMBeanBase {
sm.getString("coyoteConnector.protocolHandlerNoApr",
getProtocolHandlerClassName()));
}
+ if (AprLifecycleListener.isAprAvailable() &&
+ AprLifecycleListener.getUseOpenSSL() &&
+ 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());
+ }
+ }
try {
protocolHandler.init();
} catch (Exception e) {
- throw new LifecycleException
- (sm.getString
- ("coyoteConnector.protocolHandlerInitializationFailed"), e);
+ throw new LifecycleException(
+ sm.getString("coyoteConnector.protocolHandlerInitializationFailed"), e);
}
}
diff --git a/java/org/apache/catalina/connector/Constants.java b/java/org/apache/catalina/connector/Constants.java
deleted file mode 100644
index 3130be4..0000000
--- a/java/org/apache/catalina/connector/Constants.java
+++ /dev/null
@@ -1,26 +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.connector;
-
-/**
- * Static constants for this package.
- */
-public final class Constants {
-
- public static final String Package = "org.apache.catalina.connector";
-
-}
diff --git a/java/org/apache/catalina/connector/CoyoteAdapter.java b/java/org/apache/catalina/connector/CoyoteAdapter.java
index fdf8993..27cd958 100644
--- a/java/org/apache/catalina/connector/CoyoteAdapter.java
+++ b/java/org/apache/catalina/connector/CoyoteAdapter.java
@@ -34,8 +34,6 @@ import org.apache.catalina.Context;
import org.apache.catalina.Host;
import org.apache.catalina.Wrapper;
import org.apache.catalina.authenticator.AuthenticatorBase;
-import org.apache.catalina.comet.CometEvent;
-import org.apache.catalina.comet.CometEvent.EventType;
import org.apache.catalina.core.AsyncContextImpl;
import org.apache.catalina.util.ServerInfo;
import org.apache.catalina.util.SessionConfig;
@@ -52,7 +50,7 @@ import org.apache.tomcat.util.buf.MessageBytes;
import org.apache.tomcat.util.http.ServerCookie;
import org.apache.tomcat.util.http.ServerCookies;
import org.apache.tomcat.util.net.SSLSupport;
-import org.apache.tomcat.util.net.SocketStatus;
+import org.apache.tomcat.util.net.SocketEvent;
import org.apache.tomcat.util.res.StringManager;
@@ -69,7 +67,7 @@ public class CoyoteAdapter implements Adapter {
// -------------------------------------------------------------- Constants
- private static final String POWERED_BY = "Servlet/3.1 JSP/2.3 " +
+ private static final String POWERED_BY = "Servlet/4.0 JSP/2.3 " +
"(" + ServerInfo.getServerInfo() + " Java/" +
System.getProperty("java.vm.vendor") + "/" +
System.getProperty("java.runtime.version") + ")";
@@ -122,146 +120,14 @@ public class CoyoteAdapter implements Adapter {
/**
* The string manager for this package.
*/
- protected static final StringManager sm =
- StringManager.getManager(Constants.Package);
+ protected static final StringManager sm = StringManager.getManager(CoyoteAdapter.class);
// -------------------------------------------------------- Adapter Methods
-
- /**
- * Event method.
- *
- * @return false to indicate an error, expected or not
- */
- @SuppressWarnings("deprecation")
- @Override
- public boolean event(org.apache.coyote.Request req,
- org.apache.coyote.Response res, SocketStatus status) {
-
- Request request = (Request) req.getNote(ADAPTER_NOTES);
- Response response = (Response) res.getNote(ADAPTER_NOTES);
-
- if (request.getWrapper() == null) {
- return false;
- }
-
- boolean error = false;
- boolean read = false;
- try {
- if (status == SocketStatus.OPEN_READ) {
- if (response.isClosed()) {
- // The event has been closed asynchronously, so call end instead of
- // read to cleanup the pipeline
- request.getEvent().setEventType(CometEvent.EventType.END);
- request.getEvent().setEventSubType(null);
- } else {
- try {
- // Fill the read buffer of the servlet layer
- if (request.read()) {
- read = true;
- }
- } catch (IOException e) {
- error = true;
- }
- if (read) {
- request.getEvent().setEventType(CometEvent.EventType.READ);
- request.getEvent().setEventSubType(null);
- } else if (error) {
- request.getEvent().setEventType(CometEvent.EventType.ERROR);
- request.getEvent().setEventSubType(CometEvent.EventSubType.CLIENT_DISCONNECT);
- } else {
- request.getEvent().setEventType(CometEvent.EventType.END);
- request.getEvent().setEventSubType(null);
- }
- }
- } else if (status == SocketStatus.DISCONNECT) {
- request.getEvent().setEventType(CometEvent.EventType.ERROR);
- request.getEvent().setEventSubType(CometEvent.EventSubType.CLIENT_DISCONNECT);
- error = true;
- } else if (status == SocketStatus.ERROR) {
- request.getEvent().setEventType(CometEvent.EventType.ERROR);
- request.getEvent().setEventSubType(CometEvent.EventSubType.IOEXCEPTION);
- error = true;
- } else if (status == SocketStatus.STOP) {
- request.getEvent().setEventType(CometEvent.EventType.END);
- request.getEvent().setEventSubType(CometEvent.EventSubType.SERVER_SHUTDOWN);
- } else if (status == SocketStatus.TIMEOUT) {
- if (response.isClosed()) {
- // The event has been closed asynchronously, so call end instead of
- // read to cleanup the pipeline
- request.getEvent().setEventType(CometEvent.EventType.END);
- request.getEvent().setEventSubType(null);
- } else {
- request.getEvent().setEventType(CometEvent.EventType.ERROR);
- request.getEvent().setEventSubType(CometEvent.EventSubType.TIMEOUT);
- }
- }
-
- req.getRequestProcessor().setWorkerThreadName(Thread.currentThread().getName());
-
- // Calling the container
- connector.getService().getContainer().getPipeline().getFirst().event(request, response, request.getEvent());
-
- if (!error && !response.isClosed() && (request.getAttribute(
- RequestDispatcher.ERROR_EXCEPTION) != null)) {
- // An unexpected exception occurred while processing the event, so
- // error should be called
- request.getEvent().setEventType(CometEvent.EventType.ERROR);
- request.getEvent().setEventSubType(null);
- error = true;
- connector.getService().getContainer().getPipeline().getFirst().event(request, response, request.getEvent());
- }
- if (response.isClosed() || !request.isComet()) {
- if (status==SocketStatus.OPEN_READ &&
- request.getEvent().getEventType() != EventType.END) {
- //CometEvent.close was called during an event other than END
- request.getEvent().setEventType(CometEvent.EventType.END);
- request.getEvent().setEventSubType(null);
- error = true;
- connector.getService().getContainer().getPipeline().getFirst().event(request, response, request.getEvent());
- }
- res.action(ActionCode.COMET_END, null);
- } else if (!error && read && request.getAvailable()) {
- // If this was a read and not all bytes have been read, or if no data
- // was read from the connector, then it is an error
- request.getEvent().setEventType(CometEvent.EventType.ERROR);
- request.getEvent().setEventSubType(CometEvent.EventSubType.IOEXCEPTION);
- error = true;
- connector.getService().getContainer().getPipeline().getFirst().event(request, response, request.getEvent());
- }
- return (!error);
- } catch (Throwable t) {
- ExceptionUtils.handleThrowable(t);
- if (!(t instanceof IOException)) {
- log.error(sm.getString("coyoteAdapter.service"), t);
- }
- error = true;
- return false;
- } finally {
- req.getRequestProcessor().setWorkerThreadName(null);
- // Recycle the wrapper request and response
- if (error || response.isClosed() || !request.isComet()) {
- if (request.getMappingData().context != null) {
- request.getMappingData().context.logAccess(
- request, response,
- System.currentTimeMillis() - req.getStartTime(),
- false);
- } else {
- // Should normally not happen
- log(req, res, System.currentTimeMillis() - req.getStartTime());
- }
- request.recycle();
- request.setFilterChain(null);
- response.recycle();
- }
- }
- }
-
- @SuppressWarnings("deprecation")
@Override
public boolean asyncDispatch(org.apache.coyote.Request req,
- org.apache.coyote.Response res, SocketStatus status) throws Exception {
+ org.apache.coyote.Response res, SocketEvent status) throws Exception {
Request request = (Request) req.getNote(ADAPTER_NOTES);
Response response = (Response) res.getNote(ADAPTER_NOTES);
@@ -269,12 +135,11 @@ public class CoyoteAdapter implements Adapter {
throw new IllegalStateException(
"Dispatch may only happen on an existing request.");
}
- boolean comet = false;
boolean success = true;
AsyncContextImpl asyncConImpl = request.getAsyncContextInternal();
req.getRequestProcessor().setWorkerThreadName(Thread.currentThread().getName());
try {
- if (!request.isAsync() && !comet) {
+ if (!request.isAsync()) {
// Error or timeout - need to tell listeners the request is over
// Have to test this first since state may change while in this
// method and this is only required if entering this method in
@@ -288,11 +153,11 @@ public class CoyoteAdapter implements Adapter {
response.setSuspended(false);
}
- if (status==SocketStatus.TIMEOUT) {
+ if (status==SocketEvent.TIMEOUT) {
if (!asyncConImpl.timeout()) {
asyncConImpl.setErrorState(null, false);
}
- } else if (status==SocketStatus.ERROR) {
+ } 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
// trigger a close
@@ -320,7 +185,7 @@ public class CoyoteAdapter implements Adapter {
if (!request.isAsyncDispatching() && request.isAsync()) {
WriteListener writeListener = res.getWriteListener();
ReadListener readListener = req.getReadListener();
- if (writeListener != null && status == SocketStatus.OPEN_WRITE) {
+ if (writeListener != null && status == SocketEvent.OPEN_WRITE) {
ClassLoader oldCL = null;
try {
oldCL = request.getContext().bind(false, null);
@@ -336,7 +201,7 @@ public class CoyoteAdapter implements Adapter {
} finally {
request.getContext().unbind(false, oldCL);
}
- } else if (readListener != null && status == SocketStatus.OPEN_READ) {
+ } else if (readListener != null && status == SocketEvent.OPEN_READ) {
ClassLoader oldCL = null;
try {
oldCL = request.getContext().bind(false, null);
@@ -378,25 +243,7 @@ public class CoyoteAdapter implements Adapter {
}
}
- if (request.isComet()) {
- if (!response.isClosed() && !response.isError()) {
- if (request.getAvailable() || (request.getContentLength() > 0 && (!request.isParametersParsed()))) {
- // Invoke a read event right away if there are available bytes
- if (event(req, res, SocketStatus.OPEN_READ)) {
- comet = true;
- res.action(ActionCode.COMET_BEGIN, null);
- }
- } else {
- comet = true;
- res.action(ActionCode.COMET_BEGIN, null);
- }
- } else {
- // Clear the filter chain, as otherwise it will not be reset elsewhere
- // since this is a Comet request
- request.setFilterChain(null);
- }
- }
- if (!request.isAsync() && !comet) {
+ if (!request.isAsync()) {
request.finishRequest();
response.finishResponse();
}
@@ -427,7 +274,7 @@ public class CoyoteAdapter implements Adapter {
}
// Access logging
- if (!success || !request.isAsync() && !comet) {
+ if (!success || !request.isAsync()) {
long time = 0;
if (req.getStartTime() != -1) {
time = System.currentTimeMillis() - req.getStartTime();
@@ -441,14 +288,9 @@ public class CoyoteAdapter implements Adapter {
req.getRequestProcessor().setWorkerThreadName(null);
// Recycle the wrapper request and response
- if (!success || (!comet && !request.isAsync())) {
+ if (!success || !request.isAsync()) {
request.recycle();
response.recycle();
- } else {
- // Clear converters so that the minimum amount of memory
- // is used by this processor
- request.clearEncoders();
- response.clearEncoders();
}
}
return success;
@@ -458,7 +300,6 @@ public class CoyoteAdapter implements Adapter {
/**
* Service method.
*/
- @SuppressWarnings("deprecation")
@Override
public void service(org.apache.coyote.Request req,
org.apache.coyote.Response res)
@@ -493,7 +334,6 @@ public class CoyoteAdapter implements Adapter {
response.addHeader("X-Powered-By", POWERED_BY);
}
- boolean comet = false;
boolean async = false;
boolean postParseSuccess = false;
@@ -507,23 +347,7 @@ public class CoyoteAdapter implements Adapter {
request.setAsyncSupported(connector.getService().getContainer().getPipeline().isAsyncSupported());
// Calling the container
connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);
-
- if (request.isComet()) {
- if (!response.isClosed() && !response.isError()) {
- comet = true;
- res.action(ActionCode.COMET_BEGIN, null);
- if (request.getAvailable() || (request.getContentLength() > 0 && (!request.isParametersParsed()))) {
- // Invoke a read event right away if there are available bytes
- event(req, res, SocketStatus.OPEN_READ);
- }
- } else {
- // Clear the filter chain, as otherwise it will not be reset elsewhere
- // since this is a Comet request
- request.setFilterChain(null);
- }
- }
}
-
if (request.isAsync()) {
async = true;
ReadListener readListener = req.getReadListener();
@@ -540,6 +364,7 @@ public class CoyoteAdapter implements Adapter {
request.getContext().unbind(false, oldCL);
}
}
+
Throwable throwable =
(Throwable) request.getAttribute(RequestDispatcher.ERROR_EXCEPTION);
@@ -549,25 +374,20 @@ public class CoyoteAdapter implements Adapter {
if (!request.isAsyncCompleting() && throwable != null) {
request.getAsyncContextInternal().setErrorState(throwable, true);
}
- } else if (!comet) {
+ } else {
request.finishRequest();
response.finishResponse();
}
+
} catch (IOException e) {
// Ignore
} finally {
// Access log
- if (!async && !comet) {
- if (postParseSuccess) {
- // Log only if processing was invoked.
- // If postParseRequest() failed, it has already logged it.
- // If context is null this was the start of a comet request
- // that failed and has already been logged.
- request.getMappingData().context.logAccess(
- request, response,
- System.currentTimeMillis() - req.getStartTime(),
- false);
- }
+ if (!async && postParseSuccess) {
+ // Log only if processing was invoked.
+ // If postParseRequest() failed, it has already logged it.
+ request.getMappingData().context.logAccess(request, response,
+ System.currentTimeMillis() - req.getStartTime(), false);
}
req.getRequestProcessor().setWorkerThreadName(null);
@@ -575,17 +395,11 @@ public class CoyoteAdapter implements Adapter {
res.action(ActionCode.IS_ERROR, error);
// Recycle the wrapper request and response
- if (!comet && !async || error.get()) {
+ if (!async || error.get()) {
request.recycle();
response.recycle();
- } else {
- // Clear converters so that the minimum amount of memory
- // is used by this processor
- request.clearEncoders();
- response.clearEncoders();
}
}
-
}
@@ -600,35 +414,6 @@ public class CoyoteAdapter implements Adapter {
@Override
- public void errorDispatch(org.apache.coyote.Request req,
- org.apache.coyote.Response res) {
- Request request = (Request) req.getNote(ADAPTER_NOTES);
- Response response = (Response) res.getNote(ADAPTER_NOTES);
-
- if (request != null && request.getMappingData().context != null) {
- request.getMappingData().context.logAccess(
- request, response,
- System.currentTimeMillis() - req.getStartTime(),
- false);
- } else {
- log(req, res, System.currentTimeMillis() - req.getStartTime());
- }
-
- if (request != null) {
- request.recycle();
- }
-
- if (response != null) {
- response.recycle();
- }
-
- req.recycle();
- res.recycle();
- }
-
-
- @SuppressWarnings("deprecation")
- @Override
public void log(org.apache.coyote.Request req,
org.apache.coyote.Response res, long time) {
@@ -746,23 +531,22 @@ public class CoyoteAdapter implements Adapter {
* @throws IOException If there is insufficient space in a buffer while
* processing headers
* @throws ServletException If the supported methods of the target servlet
- * can not be determined
+ * cannot be determined
*/
- @SuppressWarnings("deprecation")
protected boolean postParseRequest(org.apache.coyote.Request req, Request request,
org.apache.coyote.Response res, Response response) throws IOException, ServletException {
- // If the processor has set the scheme (AJP will do this) use this to
- // set the secure flag as well. If the processor hasn't set it, use the
- // settings from the connector
- if (! req.scheme().isNull()) {
- // use processor specified scheme to determine secure state
- request.setSecure(req.scheme().equals("https"));
- } else {
- // use connector scheme and secure configuration, (defaults to
+ // If the processor has set the scheme (AJP does this, HTTP does this if
+ // SSL is enabled) use this to set the secure flag as well. If the
+ // processor hasn't set it, use the settings from the connector
+ if (req.scheme().isNull()) {
+ // Use connector scheme and secure configuration, (defaults to
// "http" and false respectively)
req.scheme().setString(connector.getScheme());
request.setSecure(connector.getSecure());
+ } else {
+ // Use processor specified scheme to determine secure state
+ request.setSecure(req.scheme().equals("https"));
}
// At this point the Host header has been processed.
@@ -771,6 +555,13 @@ public class CoyoteAdapter implements Adapter {
int proxyPort = connector.getProxyPort();
if (proxyPort != 0) {
req.setServerPort(proxyPort);
+ } else if (req.getServerPort() == -1) {
+ // Not explicitly set. Use default ports based on the scheme
+ if (req.scheme().equals("https")) {
+ req.setServerPort(443);
+ } else {
+ req.setServerPort(80);
+ }
}
if (proxyName != null) {
req.serverName().setString(proxyName);
@@ -1074,8 +865,8 @@ public class CoyoteAdapter implements Adapter {
* interested in the session ID that will be in this form. Other parameters
* can safely be ignored.
*
- * @param req
- * @param request
+ * @param req The Coyote request object
+ * @param request The Servlet request object
*/
protected void parsePathParameters(org.apache.coyote.Request req,
Request request) {
@@ -1085,6 +876,11 @@ public class CoyoteAdapter implements Adapter {
ByteChunk uriBC = req.decodedURI().getByteChunk();
int semicolon = uriBC.indexOf(';', 0);
+ // Performance optimisation. Return as soon as it is known there are no
+ // path parameters;
+ if (semicolon == -1) {
+ return;
+ }
// What encoding to use? Some platforms, eg z/os, use a default
// encoding that doesn't give the expected result so be explicit
@@ -1174,6 +970,8 @@ public class CoyoteAdapter implements Adapter {
/**
* Look for SSL session ID if required. Only look for SSL Session ID if it
* is the only tracking method enabled.
+ *
+ * @param request The Servlet request obejct
*/
protected void parseSessionSslId(Request request) {
if (request.getRequestedSessionId() == null &&
@@ -1189,6 +987,8 @@ public class CoyoteAdapter implements Adapter {
/**
* Parse session id in URL.
+ *
+ * @param request The Servlet request obejct
*/
protected void parseSessionCookiesId(Request request) {
@@ -1243,6 +1043,10 @@ public class CoyoteAdapter implements Adapter {
/**
* Character conversion of the URI.
+ *
+ * @param uri MessageBytes object containing the URI
+ * @param request The Servlet request obejct
+ * @throws IOException if a IO exception occurs sending an error to the client
*/
protected void convertURI(MessageBytes uri, Request request) throws IOException {
@@ -1256,13 +1060,13 @@ public class CoyoteAdapter implements Adapter {
B2CConverter conv = request.getURIConverter();
try {
if (conv == null) {
- conv = new B2CConverter(enc, true);
+ conv = new B2CConverter(B2CConverter.getCharset(enc), true);
request.setURIConverter(conv);
} else {
conv.recycle();
}
} catch (IOException e) {
- log.error("Invalid URI encoding; using HTTP default");
+ log.error(sm.getString("coyoteAdapter.invalidEncoding"));
connector.setURIEncoding(null);
}
if (conv != null) {
@@ -1292,6 +1096,8 @@ public class CoyoteAdapter implements Adapter {
/**
* Character conversion of the a US-ASCII MessageBytes.
+ *
+ * @param mb The MessageBytes instance contaning the bytes that should be converted to chars
*/
protected void convertMB(MessageBytes mb) {
@@ -1502,6 +1308,11 @@ public class CoyoteAdapter implements Adapter {
/**
* Copy an array of bytes to a different position. Used during
* normalization.
+ *
+ * @param b The bytes that should be copied
+ * @param dest Destination offset
+ * @param src Source offset
+ * @param len Length
*/
protected static void copyBytes(byte[] b, int dest, int src, int len) {
for (int pos = 0; pos < len; pos++) {
diff --git a/java/org/apache/catalina/connector/CoyoteInputStream.java b/java/org/apache/catalina/connector/CoyoteInputStream.java
index d7af1d0..acec26d 100644
--- a/java/org/apache/catalina/connector/CoyoteInputStream.java
+++ b/java/org/apache/catalina/connector/CoyoteInputStream.java
@@ -17,6 +17,7 @@
package org.apache.catalina.connector;
import java.io.IOException;
+import java.nio.ByteBuffer;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
@@ -34,8 +35,7 @@ import org.apache.tomcat.util.res.StringManager;
*/
public class CoyoteInputStream extends ServletInputStream {
- protected static final StringManager sm =
- StringManager.getManager(Constants.Package);
+ protected static final StringManager sm = StringManager.getManager(CoyoteInputStream.class);
protected InputBuffer ib;
@@ -67,25 +67,24 @@ public class CoyoteInputStream extends ServletInputStream {
public int read() throws IOException {
checkNonBlockingRead();
- if (SecurityUtil.isPackageProtectionEnabled()){
+ if (SecurityUtil.isPackageProtectionEnabled()) {
- try{
- Integer result =
- AccessController.doPrivileged(
- new PrivilegedExceptionAction<Integer>(){
+ try {
+ Integer result = AccessController
+ .doPrivileged(new PrivilegedExceptionAction<Integer>() {
@Override
- public Integer run() throws IOException{
+ public Integer run() throws IOException {
Integer integer = Integer.valueOf(ib.readByte());
return integer;
}
- });
+ });
return result.intValue();
- } catch(PrivilegedActionException pae){
+ } catch (PrivilegedActionException pae) {
Exception e = pae.getException();
- if (e instanceof IOException){
- throw (IOException)e;
+ if (e instanceof IOException) {
+ throw (IOException) e;
} else {
throw new RuntimeException(e.getMessage(), e);
}
@@ -98,30 +97,29 @@ public class CoyoteInputStream extends ServletInputStream {
@Override
public int available() throws IOException {
- if (SecurityUtil.isPackageProtectionEnabled()){
- try{
- Integer result =
- AccessController.doPrivileged(
- new PrivilegedExceptionAction<Integer>(){
+ if (SecurityUtil.isPackageProtectionEnabled()) {
+ try {
+ Integer result = AccessController
+ .doPrivileged(new PrivilegedExceptionAction<Integer>() {
@Override
- public Integer run() throws IOException{
+ public Integer run() throws IOException {
Integer integer = Integer.valueOf(ib.available());
return integer;
}
- });
+ });
return result.intValue();
- } catch(PrivilegedActionException pae){
+ } catch (PrivilegedActionException pae) {
Exception e = pae.getException();
- if (e instanceof IOException){
- throw (IOException)e;
+ if (e instanceof IOException) {
+ throw (IOException) e;
} else {
throw new RuntimeException(e.getMessage(), e);
}
}
} else {
- return ib.available();
+ return ib.available();
}
}
@@ -129,59 +127,54 @@ public class CoyoteInputStream extends ServletInputStream {
public int read(final byte[] b) throws IOException {
checkNonBlockingRead();
- if (SecurityUtil.isPackageProtectionEnabled()){
- try{
- Integer result =
- AccessController.doPrivileged(
- new PrivilegedExceptionAction<Integer>(){
+ if (SecurityUtil.isPackageProtectionEnabled()) {
+ try {
+ Integer result = AccessController
+ .doPrivileged(new PrivilegedExceptionAction<Integer>() {
@Override
- public Integer run() throws IOException{
- Integer integer =
- Integer.valueOf(ib.read(b, 0, b.length));
+ public Integer run() throws IOException {
+ Integer integer = Integer.valueOf(ib.read(b, 0, b.length));
return integer;
}
- });
+ });
return result.intValue();
- } catch(PrivilegedActionException pae){
+ } catch (PrivilegedActionException pae) {
Exception e = pae.getException();
- if (e instanceof IOException){
- throw (IOException)e;
+ if (e instanceof IOException) {
+ throw (IOException) e;
} else {
- throw new RuntimeException(e.getMessage() ,e);
+ throw new RuntimeException(e.getMessage(), e);
}
}
} else {
return ib.read(b, 0, b.length);
- }
+ }
}
@Override
- public int read(final byte[] b, final int off, final int len)
- throws IOException {
+ public int read(final byte[] b, final int off, final int len) throws IOException {
checkNonBlockingRead();
- if (SecurityUtil.isPackageProtectionEnabled()){
- try{
- Integer result =
- AccessController.doPrivileged(
- new PrivilegedExceptionAction<Integer>(){
+ if (SecurityUtil.isPackageProtectionEnabled()) {
+ try {
+ Integer result = AccessController
+ .doPrivileged(new PrivilegedExceptionAction<Integer>() {
@Override
- public Integer run() throws IOException{
- Integer integer =
- Integer.valueOf(ib.read(b, off, len));
+ public Integer run() throws IOException {
+ Integer integer = Integer.valueOf(ib.read(b, off, len));
return integer;
}
- });
+ });
return result.intValue();
- } catch(PrivilegedActionException pae){
+ } catch (PrivilegedActionException pae) {
Exception e = pae.getException();
- if (e instanceof IOException){
- throw (IOException)e;
+ if (e instanceof IOException) {
+ throw (IOException) e;
} else {
throw new RuntimeException(e.getMessage(), e);
}
@@ -192,6 +185,47 @@ public class CoyoteInputStream extends ServletInputStream {
}
+ /**
+ * Transfers bytes from the buffer to the specified ByteBuffer. After the
+ * operation the position of the ByteBuffer will be returned to the one
+ * before the operation, the limit will be the position incremented by
+ * the number of the transfered bytes.
+ *
+ * @param b the ByteBuffer into which bytes are to be written.
+ * @return an integer specifying the actual number of bytes read, or -1 if
+ * the end of the stream is reached
+ * @throws IOException if an input or output exception has occurred
+ */
+ public int read(final ByteBuffer b) throws IOException {
+ checkNonBlockingRead();
+
+ if (SecurityUtil.isPackageProtectionEnabled()) {
+ try {
+ Integer result = AccessController
+ .doPrivileged(new PrivilegedExceptionAction<Integer>() {
+
+ @Override
+ public Integer run() throws IOException {
+ Integer integer = Integer.valueOf(ib.read(b));
+ return integer;
+ }
+
+ });
+ return result.intValue();
+ } catch (PrivilegedActionException pae) {
+ Exception e = pae.getException();
+ if (e instanceof IOException) {
+ throw (IOException) e;
+ } else {
+ throw new RuntimeException(e.getMessage(), e);
+ }
+ }
+ } else {
+ return ib.read(b);
+ }
+ }
+
+
@Override
public int readLine(byte[] b, int off, int len) throws IOException {
return super.readLine(b, off, len);
@@ -206,28 +240,27 @@ public class CoyoteInputStream extends ServletInputStream {
@Override
public void close() throws IOException {
- if (SecurityUtil.isPackageProtectionEnabled()){
- try{
- AccessController.doPrivileged(
- new PrivilegedExceptionAction<Void>(){
+ if (SecurityUtil.isPackageProtectionEnabled()) {
+ try {
+ AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() {
- @Override
- public Void run() throws IOException{
- ib.close();
- return null;
- }
+ @Override
+ public Void run() throws IOException {
+ ib.close();
+ return null;
+ }
});
- } catch(PrivilegedActionException pae){
+ } catch (PrivilegedActionException pae) {
Exception e = pae.getException();
- if (e instanceof IOException){
- throw (IOException)e;
+ if (e instanceof IOException) {
+ throw (IOException) e;
} else {
throw new RuntimeException(e.getMessage(), e);
}
}
} else {
- ib.close();
+ ib.close();
}
}
@@ -251,8 +284,7 @@ public class CoyoteInputStream extends ServletInputStream {
private void checkNonBlockingRead() {
if (!ib.isBlocking() && !ib.isReady()) {
- throw new IllegalStateException(
- sm.getString("coyoteInputStream.nbNotready"));
+ throw new IllegalStateException(sm.getString("coyoteInputStream.nbNotready"));
}
}
}
diff --git a/java/org/apache/catalina/connector/CoyoteOutputStream.java b/java/org/apache/catalina/connector/CoyoteOutputStream.java
index 4d5949c..53b6dae 100644
--- a/java/org/apache/catalina/connector/CoyoteOutputStream.java
+++ b/java/org/apache/catalina/connector/CoyoteOutputStream.java
@@ -17,6 +17,7 @@
package org.apache.catalina.connector;
import java.io.IOException;
+import java.nio.ByteBuffer;
import javax.servlet.ServletOutputStream;
import javax.servlet.WriteListener;
@@ -31,8 +32,7 @@ import org.apache.tomcat.util.res.StringManager;
*/
public class CoyoteOutputStream extends ServletOutputStream {
- protected static final StringManager sm =
- StringManager.getManager(Constants.Package);
+ protected static final StringManager sm = StringManager.getManager(CoyoteOutputStream.class);
// ----------------------------------------------------- Instance Variables
@@ -55,8 +55,7 @@ public class CoyoteOutputStream extends ServletOutputStream {
* Prevent cloning the facade.
*/
@Override
- protected Object clone()
- throws CloneNotSupportedException {
+ protected Object clone() throws CloneNotSupportedException {
throw new CloneNotSupportedException();
}
@@ -101,6 +100,15 @@ public class CoyoteOutputStream extends ServletOutputStream {
}
+ public void write(ByteBuffer from) throws IOException {
+ boolean nonBlocking = checkNonBlockingWrite();
+ ob.write(from);
+ if (nonBlocking) {
+ checkRegisterForWrite();
+ }
+ }
+
+
/**
* Will send the buffer to the client.
*/
@@ -125,8 +133,7 @@ public class CoyoteOutputStream extends ServletOutputStream {
private boolean checkNonBlockingWrite() {
boolean nonBlocking = !ob.isBlocking();
if (nonBlocking && !ob.isReady()) {
- throw new IllegalStateException(
- sm.getString("coyoteOutputStream.nbNotready"));
+ throw new IllegalStateException(sm.getString("coyoteOutputStream.nbNotready"));
}
return nonBlocking;
}
@@ -146,8 +153,7 @@ public class CoyoteOutputStream extends ServletOutputStream {
@Override
- public void close()
- throws IOException {
+ public void close() throws IOException {
ob.close();
}
diff --git a/java/org/apache/catalina/connector/InputBuffer.java b/java/org/apache/catalina/connector/InputBuffer.java
index 743ac32..8f79620 100644
--- a/java/org/apache/catalina/connector/InputBuffer.java
+++ b/java/org/apache/catalina/connector/InputBuffer.java
@@ -18,11 +18,15 @@ package org.apache.catalina.connector;
import java.io.IOException;
import java.io.Reader;
+import java.nio.Buffer;
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.charset.Charset;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.ConcurrentMap;
import javax.servlet.ReadListener;
@@ -32,7 +36,8 @@ import org.apache.coyote.ContainerThreadMarker;
import org.apache.coyote.Request;
import org.apache.tomcat.util.buf.B2CConverter;
import org.apache.tomcat.util.buf.ByteChunk;
-import org.apache.tomcat.util.buf.CharChunk;
+import org.apache.tomcat.util.collections.SynchronizedStack;
+import org.apache.tomcat.util.net.ApplicationBufferHandler;
import org.apache.tomcat.util.res.StringManager;
/**
@@ -44,22 +49,14 @@ import org.apache.tomcat.util.res.StringManager;
* @author Remy Maucherat
*/
public class InputBuffer extends Reader
- implements ByteChunk.ByteInputChannel, CharChunk.CharInputChannel,
- CharChunk.CharOutputChannel {
+ implements ByteChunk.ByteInputChannel, ApplicationBufferHandler {
/**
* The string manager for this package.
*/
- protected static final StringManager sm =
- StringManager.getManager(Constants.Package);
+ protected static final StringManager sm = StringManager.getManager(InputBuffer.class);
-
- // -------------------------------------------------------------- Constants
-
-
- public static final String DEFAULT_ENCODING =
- org.apache.coyote.Constants.DEFAULT_CHARACTER_ENCODING;
- public static final int DEFAULT_BUFFER_SIZE = 8*1024;
+ public static final int DEFAULT_BUFFER_SIZE = 8 * 1024;
// The buffer can be used for byte[] and char[] reading
// ( this is needed to support ServletInputStream and BufferedReader )
@@ -67,19 +64,24 @@ public class InputBuffer extends Reader
public final int CHAR_STATE = 1;
public final int BYTE_STATE = 2;
- // ----------------------------------------------------- Instance Variables
+ /**
+ * Encoder cache.
+ */
+ private static final ConcurrentMap<Charset, SynchronizedStack<B2CConverter>> encoders = new ConcurrentHashMap<>();
+
+ // ----------------------------------------------------- Instance Variables
/**
* The byte buffer.
*/
- private final ByteChunk bb;
+ private ByteBuffer bb;
/**
- * The chunk buffer.
+ * The char buffer.
*/
- private CharChunk cb;
+ private CharBuffer cb;
/**
@@ -101,18 +103,6 @@ public class InputBuffer extends Reader
/**
- * Encoder is set.
- */
- private boolean gotEnc = false;
-
-
- /**
- * List of encoders.
- */
- protected final ConcurrentHashMap<String,B2CConverter> encoders = new ConcurrentHashMap<>();
-
-
- /**
* Current byte to char converter.
*/
protected B2CConverter conv;
@@ -131,6 +121,12 @@ public class InputBuffer extends Reader
/**
+ * Char buffer limit.
+ */
+ private int readLimit;
+
+
+ /**
* Buffer size.
*/
private final int size;
@@ -157,14 +153,11 @@ public class InputBuffer extends Reader
public InputBuffer(int size) {
this.size = size;
- bb = new ByteChunk(size);
- bb.setLimit(size);
- bb.setByteInputChannel(this);
- cb = new CharChunk(size);
- cb.setLimit(size);
- cb.setOptimizedWrite(false);
- cb.setCharInputChannel(this);
- cb.setCharOutputChannel(this);
+ bb = ByteBuffer.allocate(size);
+ clear(bb);
+ cb = CharBuffer.allocate(size);
+ clear(cb);
+ readLimit = size;
}
@@ -192,44 +185,34 @@ public class InputBuffer extends Reader
state = INITIAL_STATE;
// If usage of mark made the buffer too big, reallocate it
- if (cb.getChars().length > size) {
- cb = new CharChunk(size);
- cb.setLimit(size);
- cb.setOptimizedWrite(false);
- cb.setCharInputChannel(this);
- cb.setCharOutputChannel(this);
+ if (cb.capacity() > size) {
+ cb = CharBuffer.allocate(size);
+ clear(cb);
} else {
- cb.recycle();
+ clear(cb);
}
+ readLimit = size;
markPos = -1;
- bb.recycle();
+ clear(bb);
closed = false;
if (conv != null) {
conv.recycle();
+ encoders.get(conv.getCharset()).push(conv);
+ conv = null;
}
- gotEnc = false;
enc = null;
}
/**
- * Clear cached encoders (to save memory for Comet requests).
- */
- public void clearEncoders() {
- encoders.clear();
- }
-
-
- /**
* Close the input buffer.
*
* @throws IOException An underlying IOException occurred
*/
@Override
- public void close()
- throws IOException {
+ public void close() throws IOException {
closed = true;
}
@@ -237,19 +220,13 @@ public class InputBuffer extends Reader
public int available() {
int available = 0;
if (state == BYTE_STATE) {
- available = bb.getLength();
+ available = bb.remaining();
} else if (state == CHAR_STATE) {
- available = cb.getLength();
+ available = cb.remaining();
}
if (available == 0) {
- // Written this way to avoid use of IS_COMET action where possible
- boolean readForAvailable = coyoteRequest.getReadListener() != null;
- if (!readForAvailable) {
- AtomicBoolean isComet = new AtomicBoolean();
- coyoteRequest.action(ActionCode.IS_COMET, isComet);
- readForAvailable = isComet.get();
- }
- coyoteRequest.action(ActionCode.AVAILABLE, Boolean.valueOf(readForAvailable));
+ coyoteRequest.action(ActionCode.AVAILABLE,
+ Boolean.valueOf(coyoteRequest.getReadListener() != null));
available = (coyoteRequest.getAvailable() > 0) ? 1 : 0;
}
return available;
@@ -280,9 +257,9 @@ public class InputBuffer extends Reader
public boolean isFinished() {
int available = 0;
if (state == BYTE_STATE) {
- available = bb.getLength();
+ available = bb.remaining();
} else if (state == CHAR_STATE) {
- available = cb.getLength();
+ available = cb.remaining();
}
if (available > 0) {
return false;
@@ -296,8 +273,6 @@ public class InputBuffer extends Reader
if (coyoteRequest.getReadListener() == null) {
throw new IllegalStateException(sm.getString("inputBuffer.requiresNonBlocking"));
}
- // Need to check is finished before we check available() as BIO always
- // returns 1 for isAvailable()
if (isFinished()) {
// If this is a non-container thread, need to trigger a read
// which will eventually lead to a call to onAllDataRead() via a
@@ -326,16 +301,10 @@ public class InputBuffer extends Reader
/**
* Reads new bytes in the byte chunk.
*
- * @param cbuf Byte buffer to be written to the response
- * @param off Offset
- * @param len Length
- *
* @throws IOException An underlying IOException occurred
*/
@Override
- public int realReadBytes(byte cbuf[], int off, int len)
- throws IOException {
-
+ public int realReadBytes() throws IOException {
if (closed) {
return -1;
}
@@ -343,119 +312,130 @@ public class InputBuffer extends Reader
return -1;
}
- if(state == INITIAL_STATE) {
+ if (state == INITIAL_STATE) {
state = BYTE_STATE;
}
- int result = coyoteRequest.doRead(bb);
+ int result = coyoteRequest.doRead(this);
return result;
-
}
- public int readByte()
- throws IOException {
-
+ public int readByte() throws IOException {
if (closed) {
throw new IOException(sm.getString("inputBuffer.streamClosed"));
}
- return bb.substract();
+ if (checkByteBufferEof()) {
+ return -1;
+ }
+ return bb.get() & 0xFF;
}
- public int read(byte[] b, int off, int len)
- throws IOException {
-
+ public int read(byte[] b, int off, int len) throws IOException {
if (closed) {
throw new IOException(sm.getString("inputBuffer.streamClosed"));
}
- return bb.substract(b, off, len);
+ if (checkByteBufferEof()) {
+ return -1;
+ }
+ int n = Math.min(len, bb.remaining());
+ bb.get(b, off, n);
+ return n;
}
- // ------------------------------------------------- Chars Handling Methods
-
-
/**
- * Since the converter will use append, it is possible to get chars to
- * be removed from the buffer for "writing". Since the chars have already
- * been read before, they are ignored. If a mark was set, then the
- * mark is lost.
+ * Transfers bytes from the buffer to the specified ByteBuffer. After the
+ * operation the position of the ByteBuffer will be returned to the one
+ * before the operation, the limit will be the position incremented by
+ * the number of the transfered bytes.
+ *
+ * @param to the ByteBuffer into which bytes are to be written.
+ * @return an integer specifying the actual number of bytes read, or -1 if
+ * the end of the stream is reached
+ * @throws IOException if an input or output exception has occurred
*/
- @Override
- public void realWriteChars(char c[], int off, int len)
- throws IOException {
- markPos = -1;
- cb.setOffset(0);
- cb.setEnd(0);
+ public int read(ByteBuffer to) throws IOException {
+ if (closed) {
+ throw new IOException(sm.getString("inputBuffer.streamClosed"));
+ }
+
+ if (checkByteBufferEof()) {
+ return -1;
+ }
+ int n = Math.min(to.remaining(), bb.remaining());
+ int orgLimit = bb.limit();
+ bb.limit(bb.position() + n);
+ to.put(bb);
+ bb.limit(orgLimit);
+ to.limit(to.position()).position(to.position() - n);
+ return n;
}
+ // ------------------------------------------------- Chars Handling Methods
+
+
public void setEncoding(String s) {
enc = s;
}
- @Override
- public int realReadChars(char cbuf[], int off, int len)
- throws IOException {
-
- if (!gotEnc) {
- setConverter();
- }
+ public int realReadChars() throws IOException {
+ checkConverter();
boolean eof = false;
- if (bb.getLength() <= 0) {
- int nRead = realReadBytes(bb.getBytes(), 0, bb.getBytes().length);
+ if (bb.remaining() <= 0) {
+ int nRead = realReadBytes();
if (nRead < 0) {
eof = true;
}
}
if (markPos == -1) {
- cb.setOffset(0);
- cb.setEnd(0);
+ clear(cb);
} else {
// Make sure there's enough space in the worst case
- cb.makeSpace(bb.getLength());
- if ((cb.getBuffer().length - cb.getEnd()) == 0 && bb.getLength() != 0) {
+ makeSpace(bb.remaining());
+ if ((cb.capacity() - cb.limit()) == 0 && bb.remaining() != 0) {
// We went over the limit
- cb.setOffset(0);
- cb.setEnd(0);
+ clear(cb);
markPos = -1;
}
}
state = CHAR_STATE;
- conv.convert(bb, cb, eof);
+ conv.convert(bb, cb, this, eof);
- if (cb.getLength() == 0 && eof) {
+ if (cb.remaining() == 0 && eof) {
return -1;
} else {
- return cb.getLength();
+ return cb.remaining();
}
}
@Override
- public int read()
- throws IOException {
+ public int read() throws IOException {
if (closed) {
throw new IOException(sm.getString("inputBuffer.streamClosed"));
}
- return cb.substract();
+ if (checkCharBufferEof()) {
+ return -1;
+ }
+ return cb.get();
}
@Override
- public int read(char[] cbuf)
- throws IOException {
+ public int read(char[] cbuf) throws IOException {
if (closed) {
throw new IOException(sm.getString("inputBuffer.streamClosed"));
@@ -466,22 +446,23 @@ public class InputBuffer extends Reader
@Override
- public int read(char[] cbuf, int off, int len)
- throws IOException {
+ public int read(char[] cbuf, int off, int len) throws IOException {
if (closed) {
throw new IOException(sm.getString("inputBuffer.streamClosed"));
}
- return cb.substract(cbuf, off, len);
+ if (checkCharBufferEof()) {
+ return -1;
+ }
+ int n = Math.min(len, cb.remaining());
+ cb.get(cbuf, off, n);
+ return n;
}
@Override
- public long skip(long n)
- throws IOException {
-
-
+ public long skip(long n) throws IOException {
if (closed) {
throw new IOException(sm.getString("inputBuffer.streamClosed"));
}
@@ -492,34 +473,24 @@ public class InputBuffer extends Reader
long nRead = 0;
while (nRead < n) {
- if (cb.getLength() >= n) {
- cb.setOffset(cb.getStart() + (int) n);
+ if (cb.remaining() >= n) {
+ cb.position(cb.position() + (int) n);
nRead = n;
} else {
- nRead += cb.getLength();
- cb.setOffset(cb.getEnd());
- int toRead = 0;
- if (cb.getChars().length < (n - nRead)) {
- toRead = cb.getChars().length;
- } else {
- toRead = (int) (n - nRead);
- }
- int nb = realReadChars(cb.getChars(), 0, toRead);
+ nRead += cb.remaining();
+ cb.position(cb.limit());
+ int nb = realReadChars();
if (nb < 0) {
break;
}
}
}
-
return nRead;
-
}
@Override
- public boolean ready()
- throws IOException {
-
+ public boolean ready() throws IOException {
if (closed) {
throw new IOException(sm.getString("inputBuffer.streamClosed"));
}
@@ -537,33 +508,27 @@ public class InputBuffer extends Reader
@Override
- public void mark(int readAheadLimit)
- throws IOException {
+ public void mark(int readAheadLimit) throws IOException {
if (closed) {
throw new IOException(sm.getString("inputBuffer.streamClosed"));
}
- if (cb.getLength() <= 0) {
- cb.setOffset(0);
- cb.setEnd(0);
+ if (cb.remaining() <= 0) {
+ clear(cb);
} else {
- if ((cb.getBuffer().length > (2 * size))
- && (cb.getLength()) < (cb.getStart())) {
- System.arraycopy(cb.getBuffer(), cb.getStart(),
- cb.getBuffer(), 0, cb.getLength());
- cb.setEnd(cb.getLength());
- cb.setOffset(0);
+ if ((cb.capacity() > (2 * size)) && (cb.remaining()) < (cb.position())) {
+ cb.compact();
+ cb.flip();
}
}
- cb.setLimit(cb.getStart() + readAheadLimit + size);
- markPos = cb.getStart();
+ readLimit = cb.position() + readAheadLimit + size;
+ markPos = cb.position();
}
@Override
- public void reset()
- throws IOException {
+ public void reset() throws IOException {
if (closed) {
throw new IOException(sm.getString("inputBuffer.streamClosed"));
@@ -571,65 +536,140 @@ public class InputBuffer extends Reader
if (state == CHAR_STATE) {
if (markPos < 0) {
- cb.recycle();
+ clear(cb);
markPos = -1;
throw new IOException();
} else {
- cb.setOffset(markPos);
+ cb.position(markPos);
}
} else {
- bb.recycle();
+ clear(bb);
}
}
- public void checkConverter()
- throws IOException {
-
- if (!gotEnc) {
+ public void checkConverter() throws IOException {
+ if (conv == null) {
setConverter();
}
-
}
- protected void setConverter()
- throws IOException {
-
+ private void setConverter() throws IOException {
if (coyoteRequest != null) {
enc = coyoteRequest.getCharacterEncoding();
}
- gotEnc = true;
if (enc == null) {
- enc = DEFAULT_ENCODING;
+ enc = org.apache.coyote.Constants.DEFAULT_CHARACTER_ENCODING;
+ }
+
+ Charset charset = B2CConverter.getCharset(enc);
+ SynchronizedStack<B2CConverter> stack = encoders.get(charset);
+ if (stack == null) {
+ stack = new SynchronizedStack<>();
+ encoders.putIfAbsent(charset, stack);
+ stack = encoders.get(charset);
}
- conv = encoders.get(enc);
+ conv = stack.pop();
+
if (conv == null) {
- if (SecurityUtil.isPackageProtectionEnabled()){
- try{
- conv = AccessController.doPrivileged(
- new PrivilegedExceptionAction<B2CConverter>(){
-
- @Override
- public B2CConverter run() throws IOException {
- return new B2CConverter(enc);
- }
-
- }
- );
- }catch(PrivilegedActionException ex){
- Exception e = ex.getException();
- if (e instanceof IOException) {
- throw (IOException)e;
+ conv = createConverter(charset);
+ }
+ }
+
+
+ private static B2CConverter createConverter(final Charset charset) throws IOException {
+ if (SecurityUtil.isPackageProtectionEnabled()) {
+ try {
+ return AccessController.doPrivileged(new PrivilegedExceptionAction<B2CConverter>() {
+
+ @Override
+ public B2CConverter run() throws IOException {
+ return new B2CConverter(charset);
}
+ });
+ } catch (PrivilegedActionException ex) {
+ Exception e = ex.getException();
+ if (e instanceof IOException) {
+ throw (IOException) e;
+ } else {
+ throw new IOException(e);
}
- } else {
- conv = new B2CConverter(enc);
}
- encoders.put(enc, conv);
+ } else {
+ return new B2CConverter(charset);
+ }
+
+ }
+
+
+ @Override
+ public void setByteBuffer(ByteBuffer buffer) {
+ bb = buffer;
+ }
+
+
+ @Override
+ public ByteBuffer getByteBuffer() {
+ return bb;
+ }
+
+
+ @Override
+ public void expand(int size) {
+ // no-op
+ }
+
+
+ private boolean checkByteBufferEof() throws IOException {
+ if (bb.remaining() == 0) {
+ int n = realReadBytes();
+ if (n < 0) {
+ return true;
+ }
}
+ return false;
+ }
+ private boolean checkCharBufferEof() throws IOException {
+ if (cb.remaining() == 0) {
+ int n = realReadChars();
+ if (n < 0) {
+ return true;
+ }
+ }
+ return false;
}
+ private void clear(Buffer buffer) {
+ buffer.rewind().limit(0);
+ }
+
+ private void makeSpace(int count) {
+ int desiredSize = cb.limit() + count;
+ if(desiredSize > readLimit) {
+ desiredSize = readLimit;
+ }
+
+ if(desiredSize <= cb.capacity()) {
+ return;
+ }
+
+ int newSize = 2 * cb.capacity();
+ if(desiredSize >= newSize) {
+ newSize= 2 * cb.capacity() + count;
+ }
+
+ if (newSize > readLimit) {
+ newSize = readLimit;
+ }
+
+ CharBuffer tmp = CharBuffer.allocate(newSize);
+ cb.position(0);
+ tmp.put(cb);
+ tmp.flip();
+ cb = tmp;
+ tmp = null;
+ }
}
diff --git a/java/org/apache/catalina/connector/LocalStrings.properties b/java/org/apache/catalina/connector/LocalStrings.properties
index fc5cd00..b0a2363 100644
--- a/java/org/apache/catalina/connector/LocalStrings.properties
+++ b/java/org/apache/catalina/connector/LocalStrings.properties
@@ -12,9 +12,6 @@
# 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.
-
-cometEvent.nullRequest=The event object has been recycled and is no longer associated with a request
-
coyoteAdapter.accesslogFail=Exception while attempting to add an entry to the access log
coyoteAdapter.asyncDispatch=Exception while processing an asynchronous request
coyoteAdapter.authenticate=Authenticated user [{0}] provided by connector
@@ -22,8 +19,8 @@ coyoteAdapter.authorize=Authorizing user [{0}] using Tomcat's Realm
coyoteAdapter.checkRecycled.request=Encountered a non-recycled request and recycled it forcedly.
coyoteAdapter.checkRecycled.response=Encountered a non-recycled response and recycled it forcedly.
coyoteAdapter.debug=The variable [{0}] has value [{1}]
+coyoteAdapter.invalidEncoding=Invalid URI encoding, using HTTP default
coyoteAdapter.parsePathParam=Unable to parse the path parameters using encoding [{0}]. The path parameters in the URL will be ignored.
-coyoteAdapter.service=An exception or error occurred in the container during the request processing
coyoteConnector.invalidPort=The connector cannot start since the specified port value of [{0}] is invalid
coyoteConnector.protocolHandlerDestroyFailed=Protocol handler destroy failed
@@ -73,10 +70,12 @@ inputBuffer.requiresNonBlocking=Not available in non blocking mode
outputBuffer.writeNull=The String argument to write(String,int,int) may not be null
request.asyncNotSupported=A filter or servlet of the current chain does not support asynchronous operations.
+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)
requestFacade.nullRequest=The request object has been recycled and is no longer associated with this facade
+response.illegalWrap=The response wrapper must wrap the response obtained from getResponse()
response.sendRedirectFail=Failed to redirect to [{0}]
responseFacade.nullResponse=The response object has been recycled and is no longer associated with this facade
diff --git a/java/org/apache/catalina/connector/LocalStrings_es.properties b/java/org/apache/catalina/connector/LocalStrings_es.properties
index 229984c..0b49c9e 100644
--- a/java/org/apache/catalina/connector/LocalStrings_es.properties
+++ b/java/org/apache/catalina/connector/LocalStrings_es.properties
@@ -51,7 +51,6 @@ coyoteRequest.uploadLocationInvalid = No es v\u00E1lida la localizaci\u00F3n [{0
coyoteRequest.sessionEndAccessFail = Excepci\u00F3n disparada acabando acceso a sesi\u00F3n mientras se reciclaba el requerimiento
requestFacade.nullRequest = El objeto de requerimiento ha sido reciclado y ya no est\u00E1 asociado con esta fachada
responseFacade.nullResponse = El objeto de respuesta ha sido reciclado y ya no est\u00E1 asociado con esta fachada
-cometEvent.nullRequest = El objeto de evento ha sido reciclado y ya no est\u00E1 asociado con este requerimiento
mapperListener.unknownDefaultHost = M\u00E1quina por defecto desconocida\: {0} para el conector [{1}]
mapperListener.registerHost = Registrar m\u00E1quina {0} en dominio {1} para el conector [{2}]
mapperListener.unregisterHost = Desregistrar m\u00E1quina {0} en dominio {1} para el conector [{2}]
diff --git a/java/org/apache/catalina/connector/OutputBuffer.java b/java/org/apache/catalina/connector/OutputBuffer.java
index e9bb42e..42782c4 100644
--- a/java/org/apache/catalina/connector/OutputBuffer.java
+++ b/java/org/apache/catalina/connector/OutputBuffer.java
@@ -18,10 +18,15 @@ package org.apache.catalina.connector;
import java.io.IOException;
import java.io.Writer;
+import java.nio.Buffer;
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.charset.Charset;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
-import java.util.concurrent.ConcurrentHashMap;
+import java.util.HashMap;
+import java.util.Map;
import javax.servlet.WriteListener;
import javax.servlet.http.HttpServletResponse;
@@ -29,9 +34,8 @@ import javax.servlet.http.HttpServletResponse;
import org.apache.catalina.Globals;
import org.apache.coyote.ActionCode;
import org.apache.coyote.Response;
-import org.apache.tomcat.util.buf.ByteChunk;
+import org.apache.tomcat.util.buf.B2CConverter;
import org.apache.tomcat.util.buf.C2BConverter;
-import org.apache.tomcat.util.buf.CharChunk;
import org.apache.tomcat.util.res.StringManager;
/**
@@ -42,17 +46,16 @@ import org.apache.tomcat.util.res.StringManager;
* @author Costin Manolache
* @author Remy Maucherat
*/
-public class OutputBuffer extends Writer
- implements ByteChunk.ByteOutputChannel, CharChunk.CharOutputChannel {
+public class OutputBuffer extends Writer {
- private static final StringManager sm =
- StringManager.getManager(Constants.Package);
+ private static final StringManager sm = StringManager.getManager(OutputBuffer.class);
- // -------------------------------------------------------------- Constants
+ public static final int DEFAULT_BUFFER_SIZE = 8 * 1024;
- public static final String DEFAULT_ENCODING =
- org.apache.coyote.Constants.DEFAULT_CHARACTER_ENCODING;
- public static final int DEFAULT_BUFFER_SIZE = 8*1024;
+ /**
+ * Encoder cache.
+ */
+ private final Map<Charset, C2BConverter> encoders = new HashMap<>();
// ----------------------------------------------------- Instance Variables
@@ -60,13 +63,13 @@ public class OutputBuffer extends Writer
/**
* The byte buffer.
*/
- private final ByteChunk bb;
+ private ByteBuffer bb;
/**
- * The chunk buffer.
+ * The char buffer.
*/
- private final CharChunk cb;
+ private final CharBuffer cb;
/**
@@ -100,36 +103,12 @@ public class OutputBuffer extends Writer
/**
- * Byte chunk used to output bytes.
- */
- private final ByteChunk outputChunk = new ByteChunk();
-
-
- /**
- * Char chunk used to output chars.
- */
- private CharChunk outputCharChunk = new CharChunk();
-
-
- /**
* Encoding to use.
*/
private String enc;
/**
- * Encoder is set.
- */
- private boolean gotEnc = false;
-
-
- /**
- * List of encoders.
- */
- protected final ConcurrentHashMap<String, C2BConverter> encoders = new ConcurrentHashMap<>();
-
-
- /**
* Current char to byte converter.
*/
protected C2BConverter conv;
@@ -167,13 +146,10 @@ public class OutputBuffer extends Writer
*/
public OutputBuffer(int size) {
- bb = new ByteChunk(size);
- bb.setLimit(size);
- bb.setByteOutputChannel(this);
- cb = new CharChunk(size);
- cb.setLimit(size);
- cb.setOptimizedWrite(false);
- cb.setCharOutputChannel(this);
+ bb = ByteBuffer.allocate(size);
+ clear(bb);
+ cb = CharBuffer.allocate(size);
+ clear(cb);
}
@@ -232,39 +208,29 @@ public class OutputBuffer extends Writer
bytesWritten = 0;
charsWritten = 0;
- bb.recycle();
- cb.recycle();
- outputCharChunk.setChars(null, 0, 0);
+ clear(bb);
+ clear(cb);
closed = false;
suspended = false;
doFlush = false;
- if (conv!= null) {
+ if (conv != null) {
conv.recycle();
+ conv = null;
}
- gotEnc = false;
enc = null;
}
/**
- * Clear cached encoders (to save memory for Comet requests).
- */
- public void clearEncoders() {
- encoders.clear();
- }
-
-
- /**
* Close the output buffer. This tries to calculate the response size if
* the response has not been committed yet.
*
* @throws IOException An underlying IOException occurred
*/
@Override
- public void close()
- throws IOException {
+ public void close() throws IOException {
if (closed) {
return;
@@ -275,24 +241,23 @@ public class OutputBuffer extends Writer
// If there are chars, flush all of them to the byte buffer now as bytes are used to
// calculate the content-length (if everything fits into the byte buffer, of course).
- if (cb.getLength() > 0) {
- cb.flushBuffer();
+ if (cb.remaining() > 0) {
+ flushCharBuffer();
}
- if ((!coyoteResponse.isCommitted()) && (coyoteResponse.getContentLengthLong() == -1) &&
- !coyoteResponse.getRequest().method().equals("HEAD")) {
+ if ((!coyoteResponse.isCommitted()) && (coyoteResponse.getContentLengthLong() == -1)
+ && !coyoteResponse.getRequest().method().equals("HEAD")) {
// If this didn't cause a commit of the response, the final content
// length can be calculated. Only do this if this is not a HEAD
// request since in that case no body should have been written and
// setting a value of zero here will result in an explicit content
// length of zero being set on the response.
if (!coyoteResponse.isCommitted()) {
- coyoteResponse.setContentLength(bb.getLength());
+ coyoteResponse.setContentLength(bb.remaining());
}
}
- if (coyoteResponse.getStatus() ==
- HttpServletResponse.SC_SWITCHING_PROTOCOLS) {
+ if (coyoteResponse.getStatus() == HttpServletResponse.SC_SWITCHING_PROTOCOLS) {
doFlush(true);
} else {
doFlush(false);
@@ -302,8 +267,7 @@ public class OutputBuffer extends Writer
// The request should have been completely read by the time the response
// is closed. Further reads of the input a) are pointless and b) really
// confuse AJP (bug 50189) so close the input buffer to prevent them.
- Request req = (Request) coyoteResponse.getRequest().getNote(
- CoyoteAdapter.ADAPTER_NOTES);
+ Request req = (Request) coyoteResponse.getRequest().getNote(CoyoteAdapter.ADAPTER_NOTES);
req.inputBuffer.close();
coyoteResponse.action(ActionCode.CLOSE, null);
@@ -324,6 +288,7 @@ public class OutputBuffer extends Writer
/**
* Flush bytes or chars contained in the buffer.
*
+ * @param realFlush <code>true</code> if this should also cause a real network flush
* @throws IOException An underlying IOException occurred
*/
protected void doFlush(boolean realFlush) throws IOException {
@@ -338,11 +303,11 @@ public class OutputBuffer extends Writer
coyoteResponse.sendHeaders();
initial = false;
}
- if (cb.getLength() > 0) {
- cb.flushBuffer();
+ if (cb.remaining() > 0) {
+ flushCharBuffer();
}
- if (bb.getLength() > 0) {
- bb.flushBuffer();
+ if (bb.remaining() > 0) {
+ flushByteBuffer();
}
} finally {
doFlush = false;
@@ -366,15 +331,11 @@ public class OutputBuffer extends Writer
* Sends the buffer data to the client output, checking the
* state of Response and calling the right interceptors.
*
- * @param buf Byte buffer to be written to the response
- * @param off Offset
- * @param cnt Length
+ * @param buf the ByteBuffer to be written to the response
*
* @throws IOException An underlying IOException occurred
*/
- @Override
- public void realWriteBytes(byte buf[], int off, int cnt)
- throws IOException {
+ public void realWriteBytes(ByteBuffer buf) throws IOException {
if (closed) {
return;
@@ -384,14 +345,13 @@ public class OutputBuffer extends Writer
}
// If we really have something to write
- if (cnt > 0) {
+ if (buf.remaining() > 0) {
// real write to the adapter
- outputChunk.setBytes(buf, off, cnt);
try {
- coyoteResponse.doWrite(outputChunk);
+ coyoteResponse.doWrite(buf);
} catch (IOException e) {
// An IOException on a write is almost always due to
- // the remote client aborting the request. Wrap this
+ // the remote client aborting the request. Wrap this
// so that it can be handled better by the error dispatcher.
throw new ClientAbortException(e);
}
@@ -411,33 +371,64 @@ public class OutputBuffer extends Writer
}
- private void writeBytes(byte b[], int off, int len)
- throws IOException {
+ public void write(ByteBuffer from) throws IOException {
+
+ if (suspended) {
+ return;
+ }
+
+ writeBytes(from);
+
+ }
+
+
+ private void writeBytes(byte b[], int off, int len) throws IOException {
if (closed) {
return;
}
- bb.append(b, off, len);
+ append(b, off, len);
bytesWritten += len;
// if called from within flush(), then immediately flush
// remaining bytes
if (doFlush) {
- bb.flushBuffer();
+ flushByteBuffer();
}
}
- public void writeByte(int b)
- throws IOException {
+ private void writeBytes(ByteBuffer from) throws IOException {
+
+ if (closed) {
+ return;
+ }
+
+ append(from);
+ bytesWritten += from.remaining();
+
+ // if called from within flush(), then immediately flush
+ // remaining bytes
+ if (doFlush) {
+ flushByteBuffer();
+ }
+
+ }
+
+
+ public void writeByte(int b) throws IOException {
if (suspended) {
return;
}
- bb.append((byte) b);
+ if (isFull(bb)) {
+ flushByteBuffer();
+ }
+
+ transfer((byte) b, bb);
bytesWritten++;
}
@@ -449,52 +440,44 @@ public class OutputBuffer extends Writer
/**
* Convert the chars to bytes, then send the data to the client.
*
- * @param buf Char buffer to be written to the response
- * @param off Offset
- * @param len Length
+ * @param from Char buffer to be written to the response
*
* @throws IOException An underlying IOException occurred
*/
- @Override
- public void realWriteChars(char buf[], int off, int len)
- throws IOException {
+ public void realWriteChars(CharBuffer from) throws IOException {
- outputCharChunk.setChars(buf, off, len);
- while (outputCharChunk.getLength() > 0) {
- conv.convert(outputCharChunk, bb);
- if (bb.getLength() == 0) {
+ while (from.remaining() > 0) {
+ conv.convert(from, bb);
+ if (bb.remaining() == 0) {
// Break out of the loop if more chars are needed to produce any output
break;
}
- if (outputCharChunk.getLength() > 0) {
- if (bb.getBuffer().length == bb.getEnd() && bb.getLength() < bb.getLimit()) {
- // Need to expand output buffer
- bb.makeSpace(outputCharChunk.getLength());
- } else {
- bb.flushBuffer();
- }
+ if (from.remaining() > 0) {
+ flushByteBuffer();
}
}
}
@Override
- public void write(int c)
- throws IOException {
+ public void write(int c) throws IOException {
if (suspended) {
return;
}
- cb.append((char) c);
+ if (isFull(cb)) {
+ flushCharBuffer();
+ }
+
+ transfer((char) c, cb);
charsWritten++;
}
@Override
- public void write(char c[])
- throws IOException {
+ public void write(char c[]) throws IOException {
if (suspended) {
return;
@@ -506,14 +489,13 @@ public class OutputBuffer extends Writer
@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;
}
- cb.append(c, off, len);
+ append(c, off, len);
charsWritten += len;
}
@@ -523,8 +505,7 @@ public class OutputBuffer extends Writer
* Append a string to the buffer
*/
@Override
- public void write(String s, int off, int len)
- throws IOException {
+ public void write(String s, int off, int len) throws IOException {
if (suspended) {
return;
@@ -533,14 +514,23 @@ public class OutputBuffer extends Writer
if (s == null) {
throw new NullPointerException(sm.getString("outputBuffer.writeNull"));
}
- cb.append(s, off, len);
+
+ int sOff = off;
+ int sEnd = off + len;
+ while (sOff < sEnd) {
+ int n = transfer(s, sOff, sEnd - sOff, cb);
+ sOff += n;
+ if (isFull(cb)) {
+ flushCharBuffer();
+ }
+ }
+
charsWritten += len;
}
@Override
- public void write(String s)
- throws IOException {
+ public void write(String s) throws IOException {
if (suspended) {
return;
@@ -549,8 +539,7 @@ public class OutputBuffer extends Writer
if (s == null) {
s = "null";
}
- cb.append(s);
- charsWritten += s.length();
+ write(s, 0, s.length());
}
@@ -559,60 +548,81 @@ public class OutputBuffer extends Writer
}
- public void checkConverter()
- throws IOException {
-
- if (!gotEnc) {
+ public void checkConverter() throws IOException {
+ if (conv == null) {
setConverter();
}
-
}
- protected void setConverter()
- throws IOException {
+ private void setConverter() throws IOException {
if (coyoteResponse != null) {
enc = coyoteResponse.getCharacterEncoding();
}
- gotEnc = true;
if (enc == null) {
- enc = DEFAULT_ENCODING;
+ enc = org.apache.coyote.Constants.DEFAULT_CHARACTER_ENCODING;
}
- conv = encoders.get(enc);
+
+ final Charset charset = getCharset(enc);
+ conv = encoders.get(charset);
+
if (conv == null) {
- if (Globals.IS_SECURITY_ENABLED){
- try{
- conv = AccessController.doPrivileged(
- new PrivilegedExceptionAction<C2BConverter>(){
-
- @Override
- public C2BConverter run() throws IOException{
- return new C2BConverter(enc);
- }
-
- }
- );
- }catch(PrivilegedActionException ex){
- Exception e = ex.getException();
- if (e instanceof IOException) {
- throw (IOException)e;
+ conv = createConverter(charset);
+ encoders.put(charset, conv);
+ }
+ }
+
+
+ private static Charset getCharset(final String encoding) throws IOException {
+ if (Globals.IS_SECURITY_ENABLED) {
+ try {
+ return AccessController.doPrivileged(new PrivilegedExceptionAction<Charset>() {
+ @Override
+ public Charset run() throws IOException {
+ return B2CConverter.getCharset(encoding);
}
+ });
+ } catch (PrivilegedActionException ex) {
+ Exception e = ex.getException();
+ if (e instanceof IOException) {
+ throw (IOException) e;
+ } else {
+ throw new IOException(ex);
}
- } else {
- conv = new C2BConverter(enc);
}
+ } else {
+ return B2CConverter.getCharset(encoding);
+ }
+ }
- encoders.put(enc, conv);
+ private static C2BConverter createConverter(final Charset charset) throws IOException {
+ if (Globals.IS_SECURITY_ENABLED) {
+ try {
+ return AccessController.doPrivileged(new PrivilegedExceptionAction<C2BConverter>() {
+ @Override
+ public C2BConverter run() throws IOException {
+ return new C2BConverter(charset);
+ }
+ });
+ } catch (PrivilegedActionException ex) {
+ Exception e = ex.getException();
+ if (e instanceof IOException) {
+ throw (IOException) e;
+ } else {
+ throw new IOException(ex);
+ }
+ }
+ } else {
+ return new C2BConverter(charset);
}
}
// -------------------- BufferedOutputStream compatibility
-
public long getContentWritten() {
return bytesWritten + charsWritten;
}
@@ -629,8 +639,9 @@ public class OutputBuffer extends Writer
public void setBufferSize(int size) {
- if (size > bb.getLimit()) {// ??????
- bb.setLimit(size);
+ if (size > bb.capacity()) {
+ bb = ByteBuffer.allocate(size);
+ clear(bb);
}
}
@@ -640,12 +651,15 @@ public class OutputBuffer extends Writer
}
public void reset(boolean resetWriterStreamFlags) {
- bb.recycle();
- cb.recycle();
+ clear(bb);
+ clear(cb);
bytesWritten = 0;
charsWritten = 0;
if (resetWriterStreamFlags) {
- gotEnc = false;
+ if (conv != null) {
+ conv.recycle();
+ }
+ conv = null;
enc = null;
}
initial = true;
@@ -653,7 +667,7 @@ public class OutputBuffer extends Writer
public int getBufferSize() {
- return bb.getLimit();
+ return bb.capacity();
}
@@ -679,4 +693,197 @@ public class OutputBuffer extends Writer
public void checkRegisterForWrite() {
coyoteResponse.checkRegisterForWrite();
}
+
+ /**
+ * Add data to the buffer.
+ *
+ * @param src Bytes array
+ * @param off Offset
+ * @param len Length
+ * @throws IOException Writing overflow data to the output channel failed
+ */
+ public void append(byte src[], int off, int len) throws IOException {
+ if (bb.remaining() == 0) {
+ appendByteArray(src, off, len);
+ } else {
+ int n = transfer(src, off, len, bb);
+ len = len - n;
+ off = off + n;
+ if (isFull(bb)) {
+ flushByteBuffer();
+ appendByteArray(src, off, len);
+ }
+ }
+ }
+
+ /**
+ * Add data to the buffer.
+ * @param src Char array
+ * @param off Offset
+ * @param len Length
+ * @throws IOException Writing overflow data to the output channel failed
+ */
+ 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);
+ return;
+ }
+
+ // Optimization:
+ // If len-avail < length ( i.e. after we fill the buffer with
+ // what we can, the remaining will fit in the buffer ) we'll just
+ // copy the first part, flush, then copy the second part - 1 write
+ // and still have some space for more. We'll still have 2 writes, but
+ // we write more on the first.
+ if(len + cb.limit() < 2 * cb.capacity()) {
+ /* If the request length exceeds the size of the output buffer,
+ flush the output buffer and then write the data directly.
+ We can't avoid 2 writes, but we can write more on the second
+ */
+ int n = transfer(src, off, len, cb);
+
+ flushCharBuffer();
+
+ transfer(src, off + n, len - n, cb);
+ } else {
+ // long write - flush the buffer and write the rest
+ // directly from source
+ flushCharBuffer();
+
+ realWriteChars(CharBuffer.wrap(src, off, len));
+ }
+ }
+
+
+ public void append(ByteBuffer from) throws IOException {
+ if (bb.remaining() == 0) {
+ appendByteBuffer(from);
+ } else {
+ transfer(from, bb);
+ if (isFull(bb)) {
+ flushByteBuffer();
+ appendByteBuffer(from);
+ }
+ }
+ }
+
+ private void appendByteArray(byte src[], int off, int len) throws IOException {
+ if (len == 0) {
+ return;
+ }
+
+ int limit = bb.capacity();
+ while (len >= limit) {
+ realWriteBytes(ByteBuffer.wrap(src, off, limit));
+ len = len - limit;
+ off = off + limit;
+ }
+
+ if (len > 0) {
+ transfer(src, off, len, bb);
+ }
+ }
+
+ private void appendByteBuffer(ByteBuffer from) throws IOException {
+ if (from.remaining() == 0) {
+ return;
+ }
+
+ int limit = bb.capacity();
+ int fromLimit = from.limit();
+ while (from.remaining() >= limit) {
+ from.limit(from.position() + limit);
+ realWriteBytes(from.slice());
+ from.position(from.limit());
+ from.limit(fromLimit);
+ }
+
+ if (from.remaining() > 0) {
+ transfer(from, bb);
+ }
+ }
+
+ private void flushByteBuffer() throws IOException {
+ realWriteBytes(bb.slice());
+ clear(bb);
+ }
+
+ private void flushCharBuffer() throws IOException {
+ realWriteChars(cb.slice());
+ clear(cb);
+ }
+
+ private void transfer(byte b, ByteBuffer to) {
+ toWriteMode(to);
+ to.put(b);
+ toReadMode(to);
+ }
+
+ private void transfer(char b, CharBuffer to) {
+ toWriteMode(to);
+ to.put(b);
+ toReadMode(to);
+ }
+
+ private int transfer(byte[] buf, int off, int len, ByteBuffer to) {
+ toWriteMode(to);
+ int max = Math.min(len, to.remaining());
+ if (max > 0) {
+ to.put(buf, off, max);
+ }
+ toReadMode(to);
+ return max;
+ }
+
+ private int transfer(char[] buf, int off, int len, CharBuffer to) {
+ toWriteMode(to);
+ int max = Math.min(len, to.remaining());
+ if (max > 0) {
+ to.put(buf, off, max);
+ }
+ toReadMode(to);
+ return max;
+ }
+
+ private int transfer(String s, int off, int len, CharBuffer to) {
+ toWriteMode(to);
+ int max = Math.min(len, to.remaining());
+ if (max > 0) {
+ to.put(s, off, off + max);
+ }
+ toReadMode(to);
+ return max;
+ }
+
+ private void transfer(ByteBuffer from, ByteBuffer to) {
+ toWriteMode(to);
+ int max = Math.min(from.remaining(), to.remaining());
+ if (max > 0) {
+ int fromLimit = from.limit();
+ from.limit(from.position() + max);
+ to.put(from);
+ from.limit(fromLimit);
+ }
+ toReadMode(to);
+ }
+
+ private void clear(Buffer buffer) {
+ buffer.rewind().limit(0);
+ }
+
+ private boolean isFull(Buffer buffer) {
+ return buffer.limit() == buffer.capacity();
+ }
+
+ private void toReadMode(Buffer buffer) {
+ buffer.limit(buffer.position())
+ .reset();
+ }
+
+ private void toWriteMode(Buffer buffer) {
+ buffer.mark()
+ .position(buffer.limit())
+ .limit(buffer.capacity());
+ }
}
diff --git a/java/org/apache/catalina/connector/Request.java b/java/org/apache/catalina/connector/Request.java
index 97a196f..211d214 100644
--- a/java/org/apache/catalina/connector/Request.java
+++ b/java/org/apache/catalina/connector/Request.java
@@ -16,7 +16,6 @@
*/
package org.apache.catalina.connector;
-
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
@@ -60,6 +59,7 @@ import javax.servlet.ServletResponse;
import javax.servlet.SessionTrackingMode;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpUpgradeHandler;
@@ -74,14 +74,19 @@ import org.apache.catalina.Realm;
import org.apache.catalina.Session;
import org.apache.catalina.TomcatPrincipal;
import org.apache.catalina.Wrapper;
+import org.apache.catalina.core.ApplicationMapping;
import org.apache.catalina.core.ApplicationPart;
+import org.apache.catalina.core.ApplicationPushBuilder;
import org.apache.catalina.core.ApplicationSessionCookieConfig;
import org.apache.catalina.core.AsyncContextImpl;
import org.apache.catalina.mapper.MappingData;
+import org.apache.catalina.servlet4preview.http.Mapping;
+import org.apache.catalina.servlet4preview.http.PushBuilder;
import org.apache.catalina.util.ParameterMap;
import org.apache.catalina.util.URLEncoder;
import org.apache.coyote.ActionCode;
import org.apache.coyote.UpgradeToken;
+import org.apache.coyote.http11.upgrade.InternalHttpUpgradeHandler;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.InstanceManager;
@@ -109,15 +114,13 @@ import org.apache.tomcat.util.res.StringManager;
import org.ietf.jgss.GSSCredential;
import org.ietf.jgss.GSSException;
-
/**
* Wrapper object for the Coyote request.
*
* @author Remy Maucherat
* @author Craig R. McClanahan
*/
-public class Request
- implements HttpServletRequest {
+public class Request implements org.apache.catalina.servlet4preview.http.HttpServletRequest {
private static final Log log = LogFactory.getLog(Request.class);
@@ -125,11 +128,10 @@ public class Request
public Request() {
-
- formats[0].setTimeZone(GMT_ZONE);
- formats[1].setTimeZone(GMT_ZONE);
- formats[2].setTimeZone(GMT_ZONE);
-
+ formats = new SimpleDateFormat[formatsTemplate.length];
+ for(int i = 0; i < formats.length; i++) {
+ formats[i] = (SimpleDateFormat) formatsTemplate[i].clone();
+ }
}
@@ -153,6 +155,8 @@ public class Request
/**
* Get the Coyote request.
+ *
+ * @return the Coyote request object
*/
public org.apache.coyote.Request getCoyoteRequest() {
return (this.coyoteRequest);
@@ -168,8 +172,7 @@ public class Request
/**
* The string manager for this package.
*/
- protected static final StringManager sm =
- StringManager.getManager(Constants.Package);
+ protected static final StringManager sm = StringManager.getManager(Request.class);
/**
@@ -184,7 +187,9 @@ public class Request
* Notice that because SimpleDateFormat is not thread-safe, we can't
* declare formats[] as a static variable.
*/
- protected final SimpleDateFormat formats[] = {
+ protected final SimpleDateFormat formats[];
+
+ private static final SimpleDateFormat formatsTemplate[] = {
new SimpleDateFormat(FastHttpDateFormat.RFC1123_DATE, Locale.US),
new SimpleDateFormat("EEEEEE, dd-MMM-yy HH:mm:ss zzz", Locale.US),
new SimpleDateFormat("EEE MMMM d HH:mm:ss yyyy", Locale.US)
@@ -200,7 +205,7 @@ public class Request
/**
* The attributes associated with this Request, keyed by attribute name.
*/
- protected final ConcurrentHashMap<String, Object> attributes = new ConcurrentHashMap<>();
+ private final Map<String, Object> attributes = new ConcurrentHashMap<>();
/**
@@ -231,18 +236,6 @@ public class Request
/**
- * Associated event.
- */
- protected CometEventImpl event = null;
-
-
- /**
- * Comet state (can be accessed from multiple threads concurrently).
- */
- protected volatile boolean comet = false;
-
-
- /**
* The current dispatcher type.
*/
protected DispatcherType internalDispatcherType = null;
@@ -424,21 +417,17 @@ public class Request
protected Boolean asyncSupported = null;
+ private HttpServletRequest applicationRequest = null;
- /**
- * Path parameters
- */
- protected final Map<String,String> pathParameters = new HashMap<>();
// --------------------------------------------------------- Public Methods
-
protected void addPathParameter(String name, String value) {
- pathParameters.put(name, value);
+ coyoteRequest.addPathParameter(name, value);
}
protected String getPathParameter(String name) {
- return pathParameters.get(name);
+ return coyoteRequest.getPathParameter(name);
}
public void setAsyncSupported(boolean asyncSupported) {
@@ -454,12 +443,6 @@ public class Request
internalDispatcherType = null;
requestDispatcherPath = null;
- comet = false;
- if (event != null) {
- event.clear();
- event = null;
- }
-
authType = null;
inputBuffer.recycle();
usingInputStream = false;
@@ -503,7 +486,9 @@ public class Request
}
mappingData.recycle();
+ applicationMapping.recycle();
+ applicationRequest = null;
if (Globals.IS_SECURITY_ENABLED || Connector.RECYCLE_FACADES) {
if (facade != null) {
facade.clear();
@@ -524,15 +509,6 @@ public class Request
asyncContext.recycle();
}
asyncContext = null;
-
- pathParameters.clear();
- }
-
- /**
- * Clear cached encoders (to save memory for Comet requests).
- */
- public void clearEncoders() {
- inputBuffer.clearEncoders();
}
@@ -563,21 +539,15 @@ public class Request
}
- public boolean read() throws IOException {
- return (inputBuffer.realReadBytes(null, 0, 0) > 0);
- }
-
-
// -------------------------------------------------------- Request Methods
-
/**
* Associated Catalina connector.
*/
protected Connector connector;
/**
- * Return the Connector through which this Request was received.
+ * @return the Connector through which this Request was received.
*/
public Connector getConnector() {
return this.connector;
@@ -599,6 +569,8 @@ public class Request
* This is available as soon as the appropriate Context is identified.
* Note that availability of a Context allows <code>getContextPath()</code>
* to return a value, and thus enables parsing of the request URI.
+ *
+ * @return the Context mapped with the request
*/
public Context getContext() {
return mappingData.context;
@@ -624,6 +596,8 @@ public class Request
/**
* Get filter chain associated with the request.
+ *
+ * @return the associated filter chain
*/
public FilterChain getFilterChain() {
return this.filterChain;
@@ -640,7 +614,7 @@ public class Request
/**
- * Return the Host within which this Request is being processed.
+ * @return the Host within which this Request is being processed.
*/
public Host getHost() {
return mappingData.host;
@@ -651,9 +625,10 @@ public class Request
* Mapping data.
*/
protected final MappingData mappingData = new MappingData();
+ private final ApplicationMapping applicationMapping = new ApplicationMapping(mappingData);
/**
- * Return mapping data.
+ * @return mapping data.
*/
public MappingData getMappingData() {
return mappingData;
@@ -665,15 +640,40 @@ public class Request
*/
protected RequestFacade facade = null;
+
/**
- * Return the <code>ServletRequest</code> for which this object
+ * @return the <code>ServletRequest</code> for which this object
* is the facade. This method must be implemented by a subclass.
*/
public HttpServletRequest getRequest() {
if (facade == null) {
facade = new RequestFacade(this);
}
- return facade;
+ if (applicationRequest == null) {
+ applicationRequest = facade;
+ }
+ return applicationRequest;
+ }
+
+
+ /**
+ * Set a wrapped HttpServletRequest to pass to the application. Components
+ * wishing to wrap the request should obtain the request via
+ * {@link #getRequest()}, wrap it and then call this method with the
+ * wrapped request.
+ *
+ * @param applicationRequest The wrapped request to pass to the application
+ */
+ public void setRequest(HttpServletRequest applicationRequest) {
+ // Check the wrapper wraps this request
+ ServletRequest r = applicationRequest;
+ while (r instanceof HttpServletRequestWrapper) {
+ r = ((HttpServletRequestWrapper) r).getRequest();
+ }
+ if (r != facade) {
+ throw new IllegalArgumentException(sm.getString("request.illegalWrap"));
+ }
+ this.applicationRequest = applicationRequest;
}
@@ -683,7 +683,7 @@ public class Request
protected org.apache.catalina.connector.Response response = null;
/**
- * Return the Response with which this Request is associated.
+ * @return the Response with which this Request is associated.
*/
public org.apache.catalina.connector.Response getResponse() {
return this.response;
@@ -699,7 +699,7 @@ public class Request
}
/**
- * Return the input stream associated with this Request.
+ * @return the input stream associated with this Request.
*/
public InputStream getStream() {
if (inputStream == null) {
@@ -714,7 +714,7 @@ public class Request
protected B2CConverter URIConverter = null;
/**
- * Return the URI converter.
+ * @return the URI converter.
*/
protected B2CConverter getURIConverter() {
return URIConverter;
@@ -731,7 +731,7 @@ public class Request
/**
- * Return the Wrapper within which this Request is being processed.
+ * @return the Wrapper within which this Request is being processed.
*/
public Wrapper getWrapper() {
return mappingData.wrapper;
@@ -757,6 +757,7 @@ public class Request
* Create and return a ServletInputStream to read the content
* associated with this Request.
*
+ * @return the created input stream
* @exception IOException if an input/output error occurs
*/
public ServletInputStream createInputStream()
@@ -775,18 +776,14 @@ public class Request
* @exception IOException if an input/output error occurs
*/
public void finishRequest() throws IOException {
- // Optionally disable swallowing of additional request data.
- Context context = getContext();
- if (context != null &&
- response.getStatus() == HttpServletResponse.SC_REQUEST_ENTITY_TOO_LARGE &&
- !context.getSwallowAbortedUploads()) {
- coyoteRequest.action(ActionCode.DISABLE_SWALLOW_INPUT, null);
+ if (response.getStatus() == HttpServletResponse.SC_REQUEST_ENTITY_TOO_LARGE) {
+ checkSwallowInput();
}
}
/**
- * Return the object bound with the specified name to the internal notes
+ * @return the object bound with the specified name to the internal notes
* for this request, or <code>null</code> if no such binding exists.
*
* @param name Name of the note to be returned
@@ -875,7 +872,7 @@ public class Request
/**
- * Return the specified request attribute if it exists; otherwise, return
+ * @return the specified request attribute if it exists; otherwise, return
* <code>null</code>.
*
* @param name Name of the request attribute to return
@@ -940,6 +937,8 @@ public class Request
/**
* Test if a given name is one of the special Servlet-spec SSL attributes.
+ *
+ * @return <code>true</code> if this is a special SSL attribute
*/
static boolean isSSLAttribute(String name) {
return Globals.CERTIFICATES_ATTR.equals(name) ||
@@ -971,11 +970,11 @@ public class Request
* have names starting with "org.apache.tomcat" and include:
* <ul>
* <li>{@link Globals#SENDFILE_SUPPORTED_ATTR}</li>
- * <li>{@link Globals#COMET_SUPPORTED_ATTR}</li>
- * <li>{@link Globals#COMET_TIMEOUT_SUPPORTED_ATTR}</li>
* </ul>
* Connector implementations may return some, all or none of these
* attributes and may also support additional attributes.
+ *
+ * @return the attribute names enumeration
*/
@Override
public Enumeration<String> getAttributeNames() {
@@ -991,7 +990,7 @@ public class Request
/**
- * Return the character encoding for this Request.
+ * @return the character encoding for this Request.
*/
@Override
public String getCharacterEncoding() {
@@ -1000,7 +999,7 @@ public class Request
/**
- * Return the content length for this Request.
+ * @return the content length for this Request.
*/
@Override
public int getContentLength() {
@@ -1009,7 +1008,7 @@ public class Request
/**
- * Return the content type for this Request.
+ * @return the content type for this Request.
*/
@Override
public String getContentType() {
@@ -1019,6 +1018,8 @@ public class Request
/**
* Set the content type for this Request.
+ *
+ * @param contentType The content type
*/
public void setContentType(String contentType) {
coyoteRequest.setContentType(contentType);
@@ -1026,7 +1027,7 @@ public class Request
/**
- * Return the servlet input stream for this Request. The default
+ * @return the servlet input stream for this Request. The default
* implementation returns a servlet input stream created by
* <code>createInputStream()</code>.
*
@@ -1052,7 +1053,7 @@ public class Request
/**
- * Return the preferred Locale that the client will accept content in,
+ * @return the preferred Locale that the client will accept content in,
* based on the value for the first <code>Accept-Language</code> header
* that was encountered. If the request did not specify a preferred
* language, the server's default Locale is returned.
@@ -1073,7 +1074,7 @@ public class Request
/**
- * Return the set of preferred Locales that the client will accept
+ * @return the set of preferred Locales that the client will accept
* content in, based on the values for any <code>Accept-Language</code>
* headers that were encountered. If the request did not specify a
* preferred language, the server's default Locale is returned.
@@ -1096,7 +1097,7 @@ public class Request
/**
- * Return the value of the specified request parameter, if any; otherwise,
+ * @return the value of the specified request parameter, if any; otherwise,
* return <code>null</code>. If there is more than one value defined,
* return only the first one.
*
@@ -1146,7 +1147,7 @@ public class Request
/**
- * Return the names of all defined request parameters for this request.
+ * @return the names of all defined request parameters for this request.
*/
@Override
public Enumeration<String> getParameterNames() {
@@ -1161,7 +1162,7 @@ public class Request
/**
- * Return the defined values for the specified request parameter, if any;
+ * @return the defined values for the specified request parameter, if any;
* otherwise, return <code>null</code>.
*
* @param name Name of the desired request parameter
@@ -1179,7 +1180,7 @@ public class Request
/**
- * Return the protocol and version used to make this Request.
+ * @return the protocol and version used to make this Request.
*/
@Override
public String getProtocol() {
@@ -1192,6 +1193,7 @@ public class Request
* default implementation wraps a <code>BufferedReader</code> around the
* servlet input stream returned by <code>createInputStream()</code>.
*
+ * @return a buffered reader for the request
* @exception IllegalStateException if <code>getInputStream()</code>
* has already been called for this request
* @exception IOException if an input/output error occurs
@@ -1215,7 +1217,7 @@ public class Request
/**
- * Return the real path of the specified virtual path.
+ * @return the real path of the specified virtual path.
*
* @param path Path to be translated
*
@@ -1244,7 +1246,7 @@ public class Request
/**
- * Return the remote IP address making this Request.
+ * @return the remote IP address making this Request.
*/
@Override
public String getRemoteAddr() {
@@ -1258,7 +1260,7 @@ public class Request
/**
- * Return the remote host name making this Request.
+ * @return the remote host name making this Request.
*/
@Override
public String getRemoteHost() {
@@ -1275,7 +1277,7 @@ public class Request
}
/**
- * Returns the Internet Protocol (IP) source port of the client
+ * @return the Internet Protocol (IP) source port of the client
* or last proxy that sent the request.
*/
@Override
@@ -1289,7 +1291,7 @@ public class Request
}
/**
- * Returns the host name of the Internet Protocol (IP) interface on
+ * @return the host name of the Internet Protocol (IP) interface on
* which the request was received.
*/
@Override
@@ -1303,7 +1305,7 @@ public class Request
}
/**
- * Returns the Internet Protocol (IP) address of the interface on
+ * @return the Internet Protocol (IP) address of the interface on
* which the request was received.
*/
@Override
@@ -1318,7 +1320,7 @@ public class Request
/**
- * Returns the Internet Protocol (IP) port number of the interface
+ * @return the Internet Protocol (IP) port number of the interface
* on which the request was received.
*/
@Override
@@ -1332,7 +1334,7 @@ public class Request
}
/**
- * Return a RequestDispatcher that wraps the resource at the specified
+ * @return a RequestDispatcher that wraps the resource at the specified
* path, which may be interpreted as relative to the current request path.
*
* @param path Path of the resource to be wrapped
@@ -1391,7 +1393,7 @@ public class Request
/**
- * Return the scheme used to make this Request.
+ * @return the scheme used to make this Request.
*/
@Override
public String getScheme() {
@@ -1400,7 +1402,7 @@ public class Request
/**
- * Return the server name responding to this Request.
+ * @return the server name responding to this Request.
*/
@Override
public String getServerName() {
@@ -1409,7 +1411,7 @@ public class Request
/**
- * Return the server port responding to this Request.
+ * @return the server port responding to this Request.
*/
@Override
public int getServerPort() {
@@ -1418,7 +1420,7 @@ public class Request
/**
- * Was this request received on a secure connection?
+ * @return <code>true</code> if this request was received on a secure connection.
*/
@Override
public boolean isSecure() {
@@ -1515,6 +1517,10 @@ public class Request
/**
* Notify interested listeners that attribute has been assigned a value.
+ *
+ * @param name Attribute name
+ * @param value New attribute value
+ * @param oldValue Old attribute value
*/
private void notifyAttributeAssigned(String name, Object value,
Object oldValue) {
@@ -1557,6 +1563,9 @@ public class Request
/**
* Notify interested listeners that attribute has been removed.
+ *
+ * @param name Attribute name
+ * @param value Attribute value
*/
private void notifyAttributeRemoved(String name, Object value) {
Context context = getContext();
@@ -1899,6 +1908,31 @@ public class Request
// --------------------------------------------- HttpServletRequest Methods
/**
+ * Pulled forward from Servlet 4.0. The method signature may be modified,
+ * removed or replaced at any time until Servlet 4.0 becomes final.
+ *
+ * @return {@code true} If this request supports server push
+ */
+ @Override
+ public boolean isPushSupported() {
+ AtomicBoolean result = new AtomicBoolean();
+ coyoteRequest.action(ActionCode.IS_PUSH_SUPPORTED, result);
+ return result.get();
+ }
+
+
+ /**
+ * Pulled forward from Servlet 4.0. The method signature may be modified,
+ * removed or replaced at any time until Servlet 4.0 becomes final.
+ *
+ * @return A builder to use to construct the push request
+ */
+ @Override
+ public PushBuilder getPushBuilder() {
+ return new ApplicationPushBuilder(this);
+ }
+
+ /**
* {@inheritDoc}
*
* @since Servlet 3.1
@@ -1910,8 +1944,13 @@ public class Request
T handler;
InstanceManager instanceManager = null;
try {
- instanceManager = getContext().getInstanceManager();
- handler = (T) instanceManager.newInstance(httpUpgradeHandlerClass);
+ // Do not go through the instance manager for internal Tomcat classes since they don't need injection
+ if (InternalHttpUpgradeHandler.class.isAssignableFrom(httpUpgradeHandlerClass)) {
+ handler = httpUpgradeHandlerClass.newInstance();
+ } else {
+ instanceManager = getContext().getInstanceManager();
+ handler = (T) instanceManager.newInstance(httpUpgradeHandlerClass);
+ }
} catch (InstantiationException | IllegalAccessException | InvocationTargetException | NamingException e) {
throw new ServletException(e);
}
@@ -2045,6 +2084,8 @@ public class Request
* Return the set of Cookies received with this Request. Triggers parsing of
* the Cookie HTTP headers followed by conversion to Cookie objects if this
* has not already been performed.
+ *
+ * @return the array of cookies
*/
@Override
public Cookie[] getCookies() {
@@ -2059,6 +2100,8 @@ public class Request
* Return the server representation of the cookies associated with this
* request. Triggers parsing of the Cookie HTTP headers (but not conversion
* to Cookie objects) if the headers have not yet been parsed.
+ *
+ * @return the server cookies
*/
public ServerCookies getServerCookies() {
parseCookies();
@@ -2071,6 +2114,7 @@ public class Request
* return -1.
*
* @param name Name of the requested date header
+ * @return the date as a long
*
* @exception IllegalArgumentException if the specified header value
* cannot be converted to a date
@@ -2098,6 +2142,7 @@ public class Request
* return <code>null</code>
*
* @param name Name of the requested header
+ * @return the header value
*/
@Override
public String getHeader(String name) {
@@ -2110,6 +2155,7 @@ public class Request
* return an empty enumeration.
*
* @param name Name of the requested header
+ * @return the enumeration with the header values
*/
@Override
public Enumeration<String> getHeaders(String name) {
@@ -2118,7 +2164,7 @@ public class Request
/**
- * Return the names of all headers received with this request.
+ * @return the names of all headers received with this request.
*/
@Override
public Enumeration<String> getHeaderNames() {
@@ -2131,6 +2177,7 @@ public class Request
* is no such header for this request.
*
* @param name Name of the requested header
+ * @return the header value as an int
*
* @exception IllegalArgumentException if the specified header value
* cannot be converted to an integer
@@ -2147,8 +2194,14 @@ public class Request
}
+ @Override
+ public Mapping getMapping() {
+ return applicationMapping.getMapping();
+ }
+
+
/**
- * Return the HTTP request method used in this Request.
+ * @return the HTTP request method used in this Request.
*/
@Override
public String getMethod() {
@@ -2157,7 +2210,7 @@ public class Request
/**
- * Return the path information associated with this Request.
+ * @return the path information associated with this Request.
*/
@Override
public String getPathInfo() {
@@ -2166,7 +2219,7 @@ public class Request
/**
- * Return the extra path information for this request, translated
+ * @return the extra path information for this request, translated
* to a real path.
*/
@Override
@@ -2186,7 +2239,7 @@ public class Request
/**
- * Return the query string associated with this request.
+ * @return the query string associated with this request.
*/
@Override
public String getQueryString() {
@@ -2195,7 +2248,7 @@ public class Request
/**
- * Return the name of the remote user that has been authenticated
+ * @return the name of the remote user that has been authenticated
* for this Request.
*/
@Override
@@ -2220,7 +2273,7 @@ public class Request
/**
- * Return the session identifier included in this request, if any.
+ * @return the session identifier included in this request, if any.
*/
@Override
public String getRequestedSessionId() {
@@ -2229,7 +2282,7 @@ public class Request
/**
- * Return the request URI for this request.
+ * @return the request URI for this request.
*/
@Override
public String getRequestURI() {
@@ -2279,7 +2332,7 @@ public class Request
/**
- * Return the portion of the request URI used to select the servlet
+ * @return the portion of the request URI used to select the servlet
* that will process this request.
*/
@Override
@@ -2289,7 +2342,7 @@ public class Request
/**
- * Return the session associated with this Request, creating one
+ * @return the session associated with this Request, creating one
* if necessary.
*/
@Override
@@ -2304,7 +2357,7 @@ public class Request
/**
- * Return the session associated with this Request, creating one
+ * @return the session associated with this Request, creating one
* if necessary and requested.
*
* @param create Create a new session if one does not exist
@@ -2321,7 +2374,7 @@ public class Request
/**
- * Return <code>true</code> if the session identifier included in this
+ * @return <code>true</code> if the session identifier included in this
* request came from a cookie.
*/
@Override
@@ -2336,7 +2389,7 @@ public class Request
/**
- * Return <code>true</code> if the session identifier included in this
+ * @return <code>true</code> if the session identifier included in this
* request came from the request URI.
*/
@Override
@@ -2351,7 +2404,7 @@ public class Request
/**
- * Return <code>true</code> if the session identifier included in this
+ * @return <code>true</code> if the session identifier included in this
* request came from the request URI.
*
* @deprecated As of Version 2.1 of the Java Servlet API, use
@@ -2365,7 +2418,7 @@ public class Request
/**
- * Return <code>true</code> if the session identifier included in this
+ * @return <code>true</code> if the session identifier included in this
* request identifies a valid session.
*/
@Override
@@ -2417,7 +2470,7 @@ public class Request
/**
- * Return <code>true</code> if the authenticated user principal
+ * @return <code>true</code> if the authenticated user principal
* possesses the specified role name.
*
* @param role Role name to be validated
@@ -2459,7 +2512,7 @@ public class Request
/**
- * Return the principal that has been authenticated for this Request.
+ * @return the principal that has been authenticated for this Request.
*/
public Principal getPrincipal() {
return userPrincipal;
@@ -2467,7 +2520,7 @@ public class Request
/**
- * Return the principal that has been authenticated for this Request.
+ * @return the principal that has been authenticated for this Request.
*/
@Override
public Principal getUserPrincipal() {
@@ -2501,7 +2554,7 @@ public class Request
/**
- * Return the session associated with this Request, creating one
+ * @return the session associated with this Request, creating one
* if necessary.
*/
public Session getSessionInternal() {
@@ -2566,7 +2619,7 @@ public class Request
}
/**
- * Return the session associated with this Request, creating one
+ * @return the session associated with this Request, creating one
* if necessary and requested.
*
* @param create Create a new session if one does not exist
@@ -2577,50 +2630,16 @@ public class Request
/**
- * Get the event associated with the request.
- * @return the event
- */
- public CometEventImpl getEvent() {
- if (event == null) {
- event = new CometEventImpl(this, response);
- }
- return event;
- }
-
-
- /**
- * Return true if the current request is handling Comet traffic.
- */
- public boolean isComet() {
- return comet;
- }
-
-
- /**
- * Set comet state.
- */
- public void setComet(boolean comet) {
- this.comet = comet;
- }
-
- /**
- * return true if we have parsed parameters
+ * @return <code>true</code> if we have parsed parameters
*/
public boolean isParametersParsed() {
return parametersParsed;
}
- /**
- * Return true if bytes are available.
- */
- public boolean getAvailable() {
- return (inputBuffer.available() > 0);
- }
-
/**
- * Return true if an attempt has been made to read the request body and all
- * of the request body has been read
+ * @return <code>true</code> if an attempt has been made to read the request
+ * body and all of the request body has been read.
*/
public boolean isFinished() {
return coyoteRequest.isFinished();
@@ -2628,7 +2647,9 @@ public class Request
/**
- * Disable swallowing of remaining input if configured
+ * Check the configuration for aborted uploads and if configured to do so,
+ * disable the swallowing of any remaining input and close the connection
+ * once the response has been written.
*/
protected void checkSwallowInput() {
Context context = getContext();
@@ -2637,20 +2658,8 @@ public class Request
}
}
- public void cometClose() {
- coyoteRequest.action(ActionCode.COMET_CLOSE,getEvent());
- setComet(false);
- }
-
- public void setCometTimeout(long timeout) {
- coyoteRequest.action(ActionCode.COMET_SETTIMEOUT, Long.valueOf(timeout));
- }
-
/**
- * @throws IOException If an I/O error occurs
- * @throws IllegalStateException If the response has been committed
- * @throws ServletException If the caller is responsible for handling the
- * error and the container has NOT set the HTTP response code etc.
+ * {@inheritDoc}
*/
@Override
public boolean authenticate(HttpServletResponse response)
@@ -3044,6 +3053,7 @@ public class Request
cookiesParsed = true;
ServerCookies serverCookies = coyoteRequest.getCookies();
+ serverCookies.setLimit(connector.getMaxCookieCount());
CookieProcessor cookieProcessor = getContext().getCookieProcessor();
cookieProcessor.parseCookieHeader(coyoteRequest.getMimeHeaders(), serverCookies);
}
@@ -3256,8 +3266,13 @@ public class Request
/**
* Read post body in an array.
+ *
+ * @param body The bytes array in which the body will be read
+ * @param len The body length
+ * @return the bytes count that has been read
+ * @throws IOException if an IO exception occurred
*/
- protected int readPostBody(byte body[], int len)
+ protected int readPostBody(byte[] body, int len)
throws IOException {
int offset = 0;
@@ -3275,6 +3290,9 @@ public class Request
/**
* Read chunked post body.
+ *
+ * @return the post body as a bytes array
+ * @throws IOException if an IO exception occurred
*/
protected byte[] readChunkedPostBody() throws IOException {
ByteChunk body = new ByteChunk();
@@ -3341,6 +3359,9 @@ public class Request
/**
* Parse accept-language header value.
+ *
+ * @param value the header value
+ * @param locales the map that will hold the result
*/
protected void parseLocalesHeader(String value, TreeMap<Double, ArrayList<Locale>> locales) {
@@ -3366,17 +3387,6 @@ public class Request
}
- protected static final boolean isAlpha(String value) {
- for (int i = 0; i < value.length(); i++) {
- char c = value.charAt(i);
- if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))) {
- return false;
- }
- }
- return true;
- }
-
-
// ----------------------------------------------------- Special attributes handling
private static interface SpecialAttributeAdapter {
@@ -3477,44 +3487,22 @@ public class Request
// NO-OP
}
});
- specialAttributes.put(Globals.COMET_SUPPORTED_ATTR,
- new SpecialAttributeAdapter() {
- @Override
- public Object get(Request request, String name) {
- return Boolean.valueOf(
- request.getConnector().getProtocolHandler(
- ).isCometSupported());
- }
- @Override
- public void set(Request request, String name, Object value) {
- // NO-OP
- }
- });
- specialAttributes.put(Globals.COMET_TIMEOUT_SUPPORTED_ATTR,
- new SpecialAttributeAdapter() {
- @Override
- public Object get(Request request, String name) {
- return Boolean.valueOf(
- request.getConnector().getProtocolHandler(
- ).isCometTimeoutSupported());
- }
- @Override
- public void set(Request request, String name, Object value) {
- // NO-OP
- }
- });
specialAttributes.put(Globals.SENDFILE_SUPPORTED_ATTR,
new SpecialAttributeAdapter() {
@Override
public Object get(Request request, String name) {
return Boolean.valueOf(
request.getConnector().getProtocolHandler(
- ).isSendfileSupported());
+ ).isSendfileSupported() && request.getCoyoteRequest().getSendfile());
}
@Override
public void set(Request request, String name, Object value) {
// NO-OP
}
});
+
+ for (SimpleDateFormat sdf : formatsTemplate) {
+ sdf.setTimeZone(GMT_ZONE);
+ }
}
}
diff --git a/java/org/apache/catalina/connector/RequestFacade.java b/java/org/apache/catalina/connector/RequestFacade.java
index 89b6516..73975c3 100644
--- a/java/org/apache/catalina/connector/RequestFacade.java
+++ b/java/org/apache/catalina/connector/RequestFacade.java
@@ -34,7 +34,6 @@ import javax.servlet.ServletInputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.Cookie;
-import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpUpgradeHandler;
@@ -42,6 +41,9 @@ import javax.servlet.http.Part;
import org.apache.catalina.Globals;
import org.apache.catalina.security.SecurityUtil;
+import org.apache.catalina.servlet4preview.http.HttpServletRequest;
+import org.apache.catalina.servlet4preview.http.Mapping;
+import org.apache.catalina.servlet4preview.http.PushBuilder;
import org.apache.tomcat.util.res.StringManager;
/**
@@ -242,8 +244,7 @@ public class RequestFacade implements HttpServletRequest {
/**
* The string manager for this package.
*/
- protected static final StringManager sm =
- StringManager.getManager(Constants.Package);
+ protected static final StringManager sm = StringManager.getManager(RequestFacade.class);
// --------------------------------------------------------- Public Methods
@@ -1113,4 +1114,40 @@ public class RequestFacade implements HttpServletRequest {
Class<T> httpUpgradeHandlerClass) throws java.io.IOException, ServletException {
return request.upgrade(httpUpgradeHandlerClass);
}
+
+
+ /**
+ * {@inheritDoc}
+ * <p>
+ * Pulled forward from Servlet 4.0. The method signature may be modified,
+ * removed or replaced at any time until Servlet 4.0 becomes final.
+ */
+ @Override
+ public Mapping getMapping() {
+ return request.getMapping();
+ }
+
+
+ /**
+ * {@inheritDoc}
+ * <p>
+ * Pulled forward from Servlet 4.0. The method signature may be modified,
+ * removed or replaced at any time until Servlet 4.0 becomes final.
+ */
+ @Override
+ public boolean isPushSupported() {
+ return request.isPushSupported();
+ }
+
+
+ /**
+ * {@inheritDoc}
+ * <p>
+ * Pulled forward from Servlet 4.0. The method signature may be modified,
+ * removed or replaced at any time until Servlet 4.0 becomes final.
+ */
+ @Override
+ public PushBuilder getPushBuilder() {
+ return request.getPushBuilder();
+ }
}
diff --git a/java/org/apache/catalina/connector/Response.java b/java/org/apache/catalina/connector/Response.java
index 6d33f4a..d00fd34 100644
--- a/java/org/apache/catalina/connector/Response.java
+++ b/java/org/apache/catalina/connector/Response.java
@@ -37,9 +37,11 @@ import java.util.Vector;
import java.util.concurrent.atomic.AtomicInteger;
import javax.servlet.ServletOutputStream;
+import javax.servlet.ServletResponse;
import javax.servlet.SessionTrackingMode;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpServletResponseWrapper;
import org.apache.catalina.Context;
import org.apache.catalina.Globals;
@@ -131,7 +133,7 @@ public class Response implements HttpServletResponse {
}
/**
- * Get the Coyote response.
+ * @return the Coyote response.
*/
public org.apache.coyote.Response getCoyoteResponse() {
return this.coyoteResponse;
@@ -139,7 +141,7 @@ public class Response implements HttpServletResponse {
/**
- * Return the Context within which this Request is being processed.
+ * @return the Context within which this Request is being processed.
*/
public Context getContext() {
return (request.getContext());
@@ -237,8 +239,16 @@ public class Response implements HttpServletResponse {
protected final CharChunk redirectURLCC = new CharChunk();
- // --------------------------------------------------------- Public Methods
+ /*
+ * Not strictly required but it makes generating HTTP/2 push requests a lot
+ * easier if these are retained until the response is recycled.
+ */
+ private final List<Cookie> cookies = new ArrayList<>();
+ private HttpServletResponse applicationResponse = null;
+
+
+ // --------------------------------------------------------- Public Methods
/**
* Release all object references, and initialize instance variables, in
@@ -246,6 +256,7 @@ public class Response implements HttpServletResponse {
*/
public void recycle() {
+ cookies.clear();
outputBuffer.recycle();
usingOutputStream = false;
usingWriter = false;
@@ -254,6 +265,7 @@ public class Response implements HttpServletResponse {
errorState.set(0);
isCharacterEncodingSet = false;
+ applicationResponse = null;
if (Globals.IS_SECURITY_ENABLED || Connector.RECYCLE_FACADES) {
if (facade != null) {
facade.clear();
@@ -274,19 +286,15 @@ public class Response implements HttpServletResponse {
}
- /**
- * Clear cached encoders (to save memory for Comet requests).
- */
- public void clearEncoders() {
- outputBuffer.clearEncoders();
+ public List<Cookie> getCookies() {
+ return cookies;
}
// ------------------------------------------------------- Response Methods
-
/**
- * Return the number of bytes the application has actually written to the
+ * @return the number of bytes the application has actually written to the
* output stream. This excludes chunking, compression, etc. as well as
* headers.
*/
@@ -296,8 +304,9 @@ public class Response implements HttpServletResponse {
/**
- * Return the number of bytes the actually written to the socket. This
+ * @return the number of bytes the actually written to the socket. This
* includes chunking, compression, etc. but excludes headers.
+ * @param flush if <code>true</code> will perform a buffer flush first
*/
public long getBytesWritten(boolean flush) {
if (flush) {
@@ -322,6 +331,8 @@ public class Response implements HttpServletResponse {
/**
* Application commit flag accessor.
+ *
+ * @return <code>true</code> if the application has committed the response
*/
public boolean isAppCommitted() {
return (this.appCommitted || isCommitted() || isSuspended()
@@ -336,7 +347,7 @@ public class Response implements HttpServletResponse {
protected Request request = null;
/**
- * Return the Request with which this Response is associated.
+ * @return the Request with which this Response is associated.
*/
public org.apache.catalina.connector.Request getRequest() {
return (this.request);
@@ -357,15 +368,41 @@ public class Response implements HttpServletResponse {
*/
protected ResponseFacade facade = null;
+
/**
- * Return the <code>ServletResponse</code> for which this object
+ * @return the <code>ServletResponse</code> for which this object
* is the facade.
*/
public HttpServletResponse getResponse() {
if (facade == null) {
facade = new ResponseFacade(this);
}
- return (facade);
+ if (applicationResponse == null) {
+ applicationResponse = facade;
+ }
+ return applicationResponse;
+ }
+
+
+ /**
+ * Set a wrapped HttpServletResponse to pass to the application. Components
+ * wishing to wrap the response should obtain the response via
+ * {@link #getResponse()}, wrap it and then call this method with the
+ * wrapped response.
+ *
+ * @param applicationResponse The wrapped response to pass to the
+ * application
+ */
+ public void setResponse(HttpServletResponse applicationResponse) {
+ // Check the wrapper wraps this request
+ ServletResponse r = applicationResponse;
+ while (r instanceof HttpServletResponseWrapper) {
+ r = ((HttpServletResponseWrapper) r).getResponse();
+ }
+ if (r != facade) {
+ throw new IllegalArgumentException(sm.getString("response.illegalWrap"));
+ }
+ this.applicationResponse = applicationResponse;
}
@@ -381,6 +418,8 @@ public class Response implements HttpServletResponse {
/**
* Suspended flag accessor.
+ *
+ * @return <code>true</code> if the response is suspended
*/
public boolean isSuspended() {
return outputBuffer.isSuspended();
@@ -389,6 +428,8 @@ public class Response implements HttpServletResponse {
/**
* Closed flag accessor.
+ *
+ * @return <code>true</code> if the response has been closed
*/
public boolean isClosed() {
return outputBuffer.isClosed();
@@ -397,6 +438,8 @@ public class Response implements HttpServletResponse {
/**
* Set the error flag.
+ *
+ * @return <code>false</code> if the error flag was already set
*/
public boolean setError() {
boolean result = errorState.compareAndSet(0, 1);
@@ -412,6 +455,8 @@ public class Response implements HttpServletResponse {
/**
* Error flag accessor.
+ *
+ * @return <code>true</code> if the response has encountered an error
*/
public boolean isError() {
return errorState.get() > 0;
@@ -441,7 +486,7 @@ public class Response implements HttpServletResponse {
/**
- * Return the content length that was set or calculated for this Response.
+ * @return the content length that was set or calculated for this Response.
*/
public int getContentLength() {
return getCoyoteResponse().getContentLength();
@@ -449,7 +494,7 @@ public class Response implements HttpServletResponse {
/**
- * Return the content type that was set or calculated for this response,
+ * @return the content type that was set or calculated for this response,
* or <code>null</code> if no content type was set.
*/
@Override
@@ -498,7 +543,7 @@ public class Response implements HttpServletResponse {
/**
- * Return the actual buffer size used for this Response.
+ * @return the actual buffer size used for this Response.
*/
@Override
public int getBufferSize() {
@@ -507,7 +552,7 @@ public class Response implements HttpServletResponse {
/**
- * Return the character encoding used for this Response.
+ * @return the character encoding used for this Response.
*/
@Override
public String getCharacterEncoding() {
@@ -516,7 +561,7 @@ public class Response implements HttpServletResponse {
/**
- * Return the servlet output stream associated with this Response.
+ * @return the servlet output stream associated with this Response.
*
* @exception IllegalStateException if <code>getWriter</code> has
* already been called for this response
@@ -541,7 +586,7 @@ public class Response implements HttpServletResponse {
/**
- * Return the Locale assigned to this response.
+ * @return the Locale assigned to this response.
*/
@Override
public Locale getLocale() {
@@ -550,7 +595,7 @@ public class Response implements HttpServletResponse {
/**
- * Return the writer associated with this Response.
+ * @return the writer associated with this Response.
*
* @exception IllegalStateException if <code>getOutputStream</code> has
* already been called for this response
@@ -592,6 +637,8 @@ public class Response implements HttpServletResponse {
/**
* Has the output of this response already been committed?
+ *
+ * @return <code>true</code> if the response has been committed
*/
@Override
public boolean isCommitted() {
@@ -751,7 +798,7 @@ public class Response implements HttpServletResponse {
}
- /*
+ /**
* Overrides the name of the character encoding used in the body
* of the request. This method must be called prior to reading
* request parameters or reading input using getReader().
@@ -855,7 +902,7 @@ public class Response implements HttpServletResponse {
/**
- * Return the error message that was set with <code>sendError()</code>
+ * @return the error message that was set with <code>sendError()</code>
* for this Response.
*/
public String getMessage() {
@@ -885,6 +932,8 @@ public class Response implements HttpServletResponse {
return;
}
+ cookies.add(cookie);
+
String header = generateCookieString(cookie);
//if we reached here, no exception, cookie is valid
// the header name is Set-Cookie for both "old" and v.1 ( RFC2109 )
@@ -1019,7 +1068,7 @@ public class Response implements HttpServletResponse {
* visible to {@link org.apache.coyote.Response}
*
* Called from set/addHeader.
- * Return true if the header is special, no need to set the header.
+ * @return <code>true</code> if the header is special, no need to set the header.
*/
private boolean checkSpecialHeader(String name, String value) {
if (name.equalsIgnoreCase("Content-Type")) {
@@ -1061,6 +1110,7 @@ public class Response implements HttpServletResponse {
* Has the specified header been set already in this response?
*
* @param name Name of the header to check
+ * @return <code>true</code> if the header has been set
*/
@Override
public boolean containsHeader(String name) {
@@ -1087,6 +1137,7 @@ public class Response implements HttpServletResponse {
* into the specified redirect URL, if necessary.
*
* @param url URL to be encoded
+ * @return <code>true</code> if the URL was encoded
*/
@Override
public String encodeRedirectURL(String url) {
@@ -1105,6 +1156,7 @@ public class Response implements HttpServletResponse {
* into the specified redirect URL, if necessary.
*
* @param url URL to be encoded
+ * @return <code>true</code> if the URL was encoded
*
* @deprecated As of Version 2.1 of the Java Servlet API, use
* <code>encodeRedirectURL()</code> instead.
@@ -1121,6 +1173,7 @@ public class Response implements HttpServletResponse {
* into the specified URL, if necessary.
*
* @param url URL to be encoded
+ * @return <code>true</code> if the URL was encoded
*/
@Override
public String encodeURL(String url) {
@@ -1153,6 +1206,7 @@ public class Response implements HttpServletResponse {
* into the specified URL, if necessary.
*
* @param url URL to be encoded
+ * @return <code>true</code> if the URL was encoded
*
* @deprecated As of Version 2.1 of the Java Servlet API, use
* <code>encodeURL()</code> instead.
@@ -1256,6 +1310,10 @@ public class Response implements HttpServletResponse {
* Internal method that allows a redirect to be sent with a status other
* than {@link HttpServletResponse#SC_FOUND} (302). No attempt is made to
* validate the status code.
+ *
+ * @param location Location URL to redirect to
+ * @param status HTTP status code that will be sent
+ * @throws IOException an IO exception occurred
*/
public void sendRedirect(String location, int status) throws IOException {
if (isCommitted()) {
@@ -1355,7 +1413,7 @@ public class Response implements HttpServletResponse {
char cc=name.charAt(0);
if (cc=='C' || cc=='c') {
if (checkSpecialHeader(name, value))
- return;
+ return;
}
getCoyoteResponse().setHeader(name, value);
@@ -1444,26 +1502,27 @@ public class Response implements HttpServletResponse {
* </ul>
*
* @param location Absolute URL to be validated
+ * @return <code>true</code> if the URL should be encoded
*/
protected boolean isEncodeable(final String location) {
if (location == null) {
- return (false);
+ return false;
}
// Is this an intra-document reference?
if (location.startsWith("#")) {
- return (false);
+ return false;
}
// Are we in a valid session that is not using cookies?
final Request hreq = request;
final Session session = hreq.getSessionInternal(false);
if (session == null) {
- return (false);
+ return false;
}
if (hreq.isRequestedSessionIdFromCookie()) {
- return (false);
+ return false;
}
// Is URL encoding permitted
@@ -1493,15 +1552,15 @@ public class Response implements HttpServletResponse {
try {
url = new URL(location);
} catch (MalformedURLException e) {
- return (false);
+ return false;
}
// Does this URL match down to (and including) the context path?
if (!hreq.getScheme().equalsIgnoreCase(url.getProtocol())) {
- return (false);
+ return false;
}
if (!hreq.getServerName().equalsIgnoreCase(url.getHost())) {
- return (false);
+ return false;
}
int serverPort = hreq.getServerPort();
if (serverPort == -1) {
@@ -1520,25 +1579,25 @@ public class Response implements HttpServletResponse {
}
}
if (serverPort != urlPort) {
- return (false);
+ return false;
}
String contextPath = getContext().getPath();
if (contextPath != null) {
String file = url.getFile();
if (!file.startsWith(contextPath)) {
- return (false);
+ return false;
}
String tok = ";" +
SessionConfig.getSessionUriParamName(request.getContext()) +
"=" + session.getIdInternal();
if( file.indexOf(tok, contextPath.length()) >= 0 ) {
- return (false);
+ return false;
}
}
// This URL belongs to our web application, so it is encodeable
- return (true);
+ return true;
}
@@ -1549,6 +1608,7 @@ public class Response implements HttpServletResponse {
* already absolute, return it unchanged.
*
* @param location URL to be (possibly) converted and then returned
+ * @return the encoded URL
*
* @exception IllegalArgumentException if a MalformedURLException is
* thrown when converting the relative URL to an absolute one
@@ -1644,9 +1704,11 @@ public class Response implements HttpServletResponse {
}
- /*
+ /**
* Removes /./ and /../ sequences from absolute URLs.
* Code borrowed heavily from CoyoteAdapter.normalize()
+ *
+ * @param cc the char chunk containing the chars to normalize
*/
private void normalize(CharChunk cc) {
// Strip query string and/or fragment first as doing it this way makes
@@ -1739,7 +1801,10 @@ public class Response implements HttpServletResponse {
/**
- * Determine if an absolute URL has a path component
+ * Determine if an absolute URL has a path component.
+ *
+ * @param uri the URL that will be checked
+ * @return <code>true</code> if the URL has a path
*/
private boolean hasPath(String uri) {
int pos = uri.indexOf("://");
@@ -1753,13 +1818,13 @@ public class Response implements HttpServletResponse {
return true;
}
-
/**
* Return the specified URL with the specified session identifier
* suitably encoded.
*
* @param url URL to be encoded with the session id
* @param sessionId Session id to be included in the encoded URL
+ * @return the encoded URL
*/
protected String toEncoded(String url, String sessionId) {
diff --git a/java/org/apache/catalina/connector/ResponseFacade.java b/java/org/apache/catalina/connector/ResponseFacade.java
index 60c1d29..0bc5106 100644
--- a/java/org/apache/catalina/connector/ResponseFacade.java
+++ b/java/org/apache/catalina/connector/ResponseFacade.java
@@ -106,8 +106,7 @@ public class ResponseFacade
/**
* The string manager for this package.
*/
- protected static final StringManager sm =
- StringManager.getManager(Constants.Package);
+ protected static final StringManager sm = StringManager.getManager(ResponseFacade.class);
/**
@@ -226,23 +225,18 @@ public class ResponseFacade
}
response.setContentLength(len);
-
}
- /**
- * TODO SERVLET 3.1
- */
@Override
public void setContentLengthLong(long length) {
if (isCommitted()) {
return;
}
-
response.setContentLengthLong(length);
-
}
+
@Override
public void setContentType(String type) {
diff --git a/java/org/apache/catalina/connector/mbeans-descriptors.xml b/java/org/apache/catalina/connector/mbeans-descriptors.xml
index 78cf03f..9c02405 100644
--- a/java/org/apache/catalina/connector/mbeans-descriptors.xml
+++ b/java/org/apache/catalina/connector/mbeans-descriptors.xml
@@ -1,4 +1,4 @@
-<?xml version="1.0"?>
+<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
diff --git a/java/org/apache/catalina/core/AccessLogAdapter.java b/java/org/apache/catalina/core/AccessLogAdapter.java
index a4c6889..590878b 100644
--- a/java/org/apache/catalina/core/AccessLogAdapter.java
+++ b/java/org/apache/catalina/core/AccessLogAdapter.java
@@ -17,6 +17,7 @@
package org.apache.catalina.core;
import java.util.Arrays;
+import java.util.Objects;
import org.apache.catalina.AccessLog;
import org.apache.catalina.connector.Request;
@@ -30,16 +31,12 @@ public class AccessLogAdapter implements AccessLog {
private AccessLog[] logs;
public AccessLogAdapter(AccessLog log) {
- if (log == null) {
- throw new NullPointerException();
- }
+ Objects.requireNonNull(log);
logs = new AccessLog[] { log };
}
public void add(AccessLog log) {
- if (log == null) {
- throw new NullPointerException();
- }
+ Objects.requireNonNull(log);
AccessLog newArray[] = Arrays.copyOf(logs, logs.length + 1);
newArray[newArray.length - 1] = log;
logs = newArray;
diff --git a/java/org/apache/catalina/core/ApplicationContext.java b/java/org/apache/catalina/core/ApplicationContext.java
index 24d539d..70d7ec4 100644
--- a/java/org/apache/catalina/core/ApplicationContext.java
+++ b/java/org/apache/catalina/core/ApplicationContext.java
@@ -60,13 +60,13 @@ import org.apache.catalina.Container;
import org.apache.catalina.Context;
import org.apache.catalina.Engine;
import org.apache.catalina.Globals;
-import org.apache.catalina.Host;
import org.apache.catalina.LifecycleState;
import org.apache.catalina.Service;
import org.apache.catalina.WebResourceRoot;
import org.apache.catalina.Wrapper;
import org.apache.catalina.connector.Connector;
import org.apache.catalina.mapper.MappingData;
+import org.apache.catalina.servlet4preview.http.Mapping;
import org.apache.catalina.util.ServerInfo;
import org.apache.catalina.util.URLEncoder;
import org.apache.tomcat.util.ExceptionUtils;
@@ -210,22 +210,12 @@ public class ApplicationContext implements ServletContext {
// ------------------------------------------------- ServletContext Methods
- /**
- * Return the value of the specified context attribute, if any;
- * otherwise return <code>null</code>.
- *
- * @param name Name of the context attribute to return
- */
@Override
public Object getAttribute(String name) {
return (attributes.get(name));
}
- /**
- * Return an enumeration of the names of the context attributes
- * associated with this context.
- */
@Override
public Enumeration<String> getAttributeNames() {
Set<String> names = new HashSet<>();
@@ -234,16 +224,6 @@ public class ApplicationContext implements ServletContext {
}
- /**
- * Return a <code>ServletContext</code> object that corresponds to a
- * specified URI on the server. This method allows servlets to gain
- * access to the context for various parts of the server, and as needed
- * obtain <code>RequestDispatcher</code> objects or resources from the
- * context. The given path must be absolute (beginning with a "/"),
- * and is interpreted based on our virtual host's document root.
- *
- * @param uri Absolute URI of a resource on the server
- */
@Override
public ServletContext getContext(String uri) {
@@ -305,21 +285,12 @@ public class ApplicationContext implements ServletContext {
}
- /**
- * Return the main path associated with this context.
- */
@Override
public String getContextPath() {
return context.getPath();
}
- /**
- * Return the value of the specified initialization parameter, or
- * <code>null</code> if this parameter does not exist.
- *
- * @param name Name of the initialization parameter to retrieve
- */
@Override
public String getInitParameter(final String name) {
// Special handling for XML settings as the context setting must
@@ -338,10 +309,6 @@ public class ApplicationContext implements ServletContext {
}
- /**
- * Return the names of the context's initialization parameters, or an
- * empty enumeration if the context has no initialization parameters.
- */
@Override
public Enumeration<String> getInitParameterNames() {
Set<String> names = new HashSet<>();
@@ -358,25 +325,15 @@ public class ApplicationContext implements ServletContext {
}
- /**
- * Return the major version of the Java Servlet API that we implement.
- */
@Override
public int getMajorVersion() {
-
- return (Constants.MAJOR_VERSION);
-
+ return Constants.MAJOR_VERSION;
}
- /**
- * Return the minor version of the Java Servlet API that we implement.
- */
@Override
public int getMinorVersion() {
-
- return (Constants.MINOR_VERSION);
-
+ return Constants.MINOR_VERSION;
}
@@ -420,17 +377,11 @@ public class ApplicationContext implements ServletContext {
if (wrapper == null)
return (null);
- return new ApplicationDispatcher(wrapper, null, null, null, null, name);
+ return new ApplicationDispatcher(wrapper, null, null, null, null, null, name);
}
- /**
- * Return the real path for a given virtual path, if possible; otherwise
- * return <code>null</code>.
- *
- * @param path The path to the desired resource
- */
@Override
public String getRealPath(String path) {
String validatedPath = validateResourcePath(path, true);
@@ -438,13 +389,6 @@ public class ApplicationContext implements ServletContext {
}
- /**
- * Return a <code>RequestDispatcher</code> instance that acts as a
- * wrapper for the resource at the given path. The path must begin
- * with a "/" and is interpreted as relative to the current context root.
- *
- * @param path The path to the desired resource.
- */
@Override
public RequestDispatcher getRequestDispatcher(String path) {
@@ -538,6 +482,7 @@ public class ApplicationContext implements ServletContext {
Wrapper wrapper = mappingData.wrapper;
String wrapperPath = mappingData.wrapperPath.toString();
String pathInfo = mappingData.pathInfo.toString();
+ Mapping mapping = (new ApplicationMapping(mappingData)).getMapping();
mappingData.recycle();
@@ -545,21 +490,10 @@ public class ApplicationContext implements ServletContext {
// Construct a RequestDispatcher to process this request
return new ApplicationDispatcher(wrapper, encodedUri, wrapperPath, pathInfo,
- queryString, null);
+ queryString, mapping, null);
}
-
- /**
- * Return the URL to the resource that is mapped to a specified path.
- * The path must begin with a "/" and is interpreted as relative to the
- * current context root.
- *
- * @param path The path to the desired resource
- *
- * @exception MalformedURLException if the path is not given
- * in the correct form
- */
@Override
public URL getResource(String path) throws MalformedURLException {
@@ -579,14 +513,6 @@ public class ApplicationContext implements ServletContext {
}
- /**
- * Return the requested resource as an <code>InputStream</code>. The
- * path must be specified according to the rules described under
- * <code>getResource</code>. If no such resource can be identified,
- * return <code>null</code>.
- *
- * @param path The path to the desired resource.
- */
@Override
public InputStream getResourceAsStream(String path) {
@@ -630,14 +556,6 @@ public class ApplicationContext implements ServletContext {
}
- /**
- * Return a Set containing the resource paths of resources member of the
- * specified collection. Each path will be a String starting with
- * a "/" character. Paths representing directories will end with a "/"
- * character.
- *
- * @param path Collection path
- */
@Override
public Set<String> getResourcePaths(String path) {
@@ -659,43 +577,25 @@ public class ApplicationContext implements ServletContext {
}
- /**
- * Return the name and version of the servlet container.
- */
@Override
public String getServerInfo() {
-
- return (ServerInfo.getServerInfo());
-
+ return ServerInfo.getServerInfo();
}
- /**
- * @deprecated As of Java Servlet API 2.1, with no direct replacement.
- */
@Override
@Deprecated
public Servlet getServlet(String name) {
-
- return (null);
-
+ return null;
}
- /**
- * Return the display name of this web application.
- */
@Override
public String getServletContextName() {
-
- return (context.getDisplayName());
-
+ return context.getDisplayName();
}
- /**
- * @deprecated As of Java Servlet API 2.1, with no direct replacement.
- */
@Override
@Deprecated
public Enumeration<String> getServletNames() {
@@ -703,9 +603,6 @@ public class ApplicationContext implements ServletContext {
}
- /**
- * @deprecated As of Java Servlet API 2.1, with no direct replacement.
- */
@Override
@Deprecated
public Enumeration<Servlet> getServlets() {
@@ -713,56 +610,25 @@ public class ApplicationContext implements ServletContext {
}
- /**
- * Writes the specified message to a servlet log file.
- *
- * @param message Message to be written
- */
@Override
public void log(String message) {
-
context.getLogger().info(message);
-
}
- /**
- * Writes the specified exception and message to a servlet log file.
- *
- * @param exception Exception to be reported
- * @param message Message to be written
- *
- * @deprecated As of Java Servlet API 2.1, use
- * <code>log(String, Throwable)</code> instead
- */
@Override
@Deprecated
public void log(Exception exception, String message) {
-
context.getLogger().error(message, exception);
-
}
- /**
- * Writes the specified message and exception to a servlet log file.
- *
- * @param message Message to be written
- * @param throwable Exception to be reported
- */
@Override
public void log(String message, Throwable throwable) {
-
context.getLogger().error(message, throwable);
-
}
- /**
- * Remove the context attribute with the specified name, if any.
- *
- * @param name Name of the context attribute to be removed
- */
@Override
public void removeAttribute(String name) {
@@ -804,20 +670,11 @@ public class ApplicationContext implements ServletContext {
log(sm.getString("applicationContext.attributeEvent"), t);
}
}
-
}
- /**
- * Bind the specified value with the specified context attribute name,
- * replacing any existing value for that name.
- *
- * @param name Attribute name to be bound
- * @param value New attribute value to be bound
- */
@Override
public void setAttribute(String name, Object value) {
-
// Name cannot be null
if (name == null)
throw new IllegalArgumentException
@@ -882,75 +739,28 @@ public class ApplicationContext implements ServletContext {
log(sm.getString("applicationContext.attributeEvent"), t);
}
}
-
}
- /**
- * Add filter to context.
- * @param filterName Name of filter to add
- * @param filterClass Name of filter class
- * @return <code>null</code> if the filter has already been fully defined,
- * else a {@link javax.servlet.FilterRegistration.Dynamic} object
- * that can be used to further configure the filter
- * @throws IllegalStateException if the context has already been initialised
- * @throws UnsupportedOperationException - if this context was passed to the
- * {@link ServletContextListener#contextInitialized(javax.servlet.ServletContextEvent)}
- * method of a {@link ServletContextListener} that was not declared
- * in web.xml, a web-fragment or annotated with
- * {@link javax.servlet.annotation.WebListener}.
- */
@Override
- public FilterRegistration.Dynamic addFilter(String filterName,
- String filterClass) throws IllegalStateException {
-
- return addFilter(filterName, filterClass, null);
+ public FilterRegistration.Dynamic addFilter(String filterName, String className) {
+ return addFilter(filterName, className, null);
}
- /**
- * Add filter to context.
- * @param filterName Name of filter to add
- * @param filter Filter to add
- * @return <code>null</code> if the filter has already been fully defined,
- * else a {@link javax.servlet.FilterRegistration.Dynamic} object
- * that can be used to further configure the filter
- * @throws IllegalStateException if the context has already been initialised
- * @throws UnsupportedOperationException - if this context was passed to the
- * {@link ServletContextListener#contextInitialized(javax.servlet.ServletContextEvent)}
- * method of a {@link ServletContextListener} that was not declared
- * in web.xml, a web-fragment or annotated with
- * {@link javax.servlet.annotation.WebListener}.
- */
@Override
- public FilterRegistration.Dynamic addFilter(String filterName,
- Filter filter) throws IllegalStateException {
-
+ public FilterRegistration.Dynamic addFilter(String filterName, Filter filter) {
return addFilter(filterName, null, filter);
}
- /**
- * Add filter to context.
- * @param filterName Name of filter to add
- * @param filterClass Class of filter to add
- * @return <code>null</code> if the filter has already been fully defined,
- * else a {@link javax.servlet.FilterRegistration.Dynamic} object
- * that can be used to further configure the filter
- * @throws IllegalStateException if the context has already been initialised
- * @throws UnsupportedOperationException - if this context was passed to the
- * {@link ServletContextListener#contextInitialized(javax.servlet.ServletContextEvent)}
- * method of a {@link ServletContextListener} that was not declared
- * in web.xml, a web-fragment or annotated with
- * {@link javax.servlet.annotation.WebListener}.
- */
@Override
public FilterRegistration.Dynamic addFilter(String filterName,
- Class<? extends Filter> filterClass) throws IllegalStateException {
-
+ Class<? extends Filter> filterClass) {
return addFilter(filterName, filterClass.getName(), null);
}
+
private FilterRegistration.Dynamic addFilter(String filterName,
String filterClass, Filter filter) throws IllegalStateException {
@@ -991,23 +801,18 @@ public class ApplicationContext implements ServletContext {
return new ApplicationFilterRegistration(filterDef, context);
}
+
@Override
- public <T extends Filter> T createFilter(Class<T> c)
- throws ServletException {
+ public <T extends Filter> T createFilter(Class<T> c) throws ServletException {
try {
@SuppressWarnings("unchecked")
T filter = (T) context.getInstanceManager().newInstance(c.getName());
return filter;
- } catch (IllegalAccessException e) {
- throw new ServletException(e);
} catch (InvocationTargetException e) {
ExceptionUtils.handleThrowable(e.getCause());
throw new ServletException(e);
- } catch (NamingException e) {
- throw new ServletException(e);
- } catch (InstantiationException e) {
- throw new ServletException(e);
- } catch (ClassNotFoundException e) {
+ } catch (IllegalAccessException | NamingException | InstantiationException |
+ ClassNotFoundException e) {
throw new ServletException(e);
}
}
@@ -1023,72 +828,25 @@ public class ApplicationContext implements ServletContext {
}
- /**
- * Add servlet to context.
- * @param servletName Name of servlet to add
- * @param servletClass Name of servlet class
- * @return <code>null</code> if the servlet has already been fully defined,
- * else a {@link javax.servlet.ServletRegistration.Dynamic} object
- * that can be used to further configure the servlet
- * @throws IllegalStateException if the context has already been initialised
- * @throws UnsupportedOperationException - if this context was passed to the
- * {@link ServletContextListener#contextInitialized(javax.servlet.ServletContextEvent)}
- * method of a {@link ServletContextListener} that was not declared
- * in web.xml, a web-fragment or annotated with
- * {@link javax.servlet.annotation.WebListener}.
- */
@Override
- public ServletRegistration.Dynamic addServlet(String servletName,
- String servletClass) throws IllegalStateException {
-
- return addServlet(servletName, servletClass, null);
+ public ServletRegistration.Dynamic addServlet(String servletName, String className) {
+ return addServlet(servletName, className, null);
}
- /**
- * Add servlet to context.
- * @param servletName Name of servlet to add
- * @param servlet Servlet instance to add
- * @return <code>null</code> if the servlet has already been fully defined,
- * else a {@link javax.servlet.ServletRegistration.Dynamic} object
- * that can be used to further configure the servlet
- * @throws IllegalStateException if the context has already been initialised
- * @throws UnsupportedOperationException - if this context was passed to the
- * {@link ServletContextListener#contextInitialized(javax.servlet.ServletContextEvent)}
- * method of a {@link ServletContextListener} that was not declared
- * in web.xml, a web-fragment or annotated with
- * {@link javax.servlet.annotation.WebListener}.
- */
@Override
- public ServletRegistration.Dynamic addServlet(String servletName,
- Servlet servlet) throws IllegalStateException {
-
+ public ServletRegistration.Dynamic addServlet(String servletName, Servlet servlet) {
return addServlet(servletName, null, servlet);
}
- /**
- * Add servlet to context.
- * @param servletName Name of servlet to add
- * @param servletClass Class of servlet to add
- * @return <code>null</code> if the servlet has already been fully defined,
- * else a {@link javax.servlet.ServletRegistration.Dynamic} object
- * that can be used to further configure the servlet
- * @throws IllegalStateException if the context has already been initialised
- * @throws UnsupportedOperationException - if this context was passed to the
- * {@link ServletContextListener#contextInitialized(javax.servlet.ServletContextEvent)}
- * method of a {@link ServletContextListener} that was not declared
- * in web.xml, a web-fragment or annotated with
- * {@link javax.servlet.annotation.WebListener}.
- */
@Override
public ServletRegistration.Dynamic addServlet(String servletName,
- Class<? extends Servlet> servletClass)
- throws IllegalStateException {
-
+ Class<? extends Servlet> servletClass) {
return addServlet(servletName, servletClass.getName(), null);
}
+
private ServletRegistration.Dynamic addServlet(String servletName,
String servletClass, Servlet servlet) throws IllegalStateException {
@@ -1142,16 +900,11 @@ public class ApplicationContext implements ServletContext {
T servlet = (T) context.getInstanceManager().newInstance(c.getName());
context.dynamicServletCreated(servlet);
return servlet;
- } catch (IllegalAccessException e) {
- throw new ServletException(e);
} catch (InvocationTargetException e) {
ExceptionUtils.handleThrowable(e.getCause());
throw new ServletException(e);
- } catch (NamingException e) {
- throw new ServletException(e);
- } catch (InstantiationException e) {
- throw new ServletException(e);
- } catch (ClassNotFoundException e) {
+ } catch (IllegalAccessException | NamingException | InstantiationException |
+ ClassNotFoundException e) {
throw new ServletException(e);
}
}
@@ -1168,19 +921,12 @@ public class ApplicationContext implements ServletContext {
}
- /**
- * By default {@link SessionTrackingMode#URL} is always supported, {@link
- * SessionTrackingMode#COOKIE} is supported unless the <code>cookies</code>
- * attribute has been set to <code>false</code> for the context and {@link
- * SessionTrackingMode#SSL} is supported if at least one of the connectors
- * used by this context has the attribute <code>secure</code> set to
- * <code>true</code>.
- */
@Override
public Set<SessionTrackingMode> getDefaultSessionTrackingModes() {
return defaultSessionTrackingModes;
}
+
private void populateSessionTrackingModes() {
// URL re-writing is always enabled by default
defaultSessionTrackingModes = EnumSet.of(SessionTrackingMode.URL);
@@ -1204,10 +950,7 @@ public class ApplicationContext implements ServletContext {
}
}
- /**
- * Return the supplied value if one was previously set, else return the
- * defaults.
- */
+
@Override
public Set<SessionTrackingMode> getEffectiveSessionTrackingModes() {
if (sessionTrackingModes != null) {
@@ -1223,15 +966,8 @@ public class ApplicationContext implements ServletContext {
}
- /**
- * @throws IllegalStateException if the context has already been initialised
- * @throws IllegalArgumentException If SSL is requested in combination with
- * anything else or if an unsupported
- * tracking mode is requested
- */
@Override
- public void setSessionTrackingModes(
- Set<SessionTrackingMode> sessionTrackingModes) {
+ public void setSessionTrackingModes(Set<SessionTrackingMode> sessionTrackingModes) {
if (!context.getState().equals(LifecycleState.STARTING_PREP)) {
throw new IllegalStateException(
@@ -1303,24 +1039,13 @@ public class ApplicationContext implements ServletContext {
EventListener listener = (EventListener) obj;
addListener(listener);
}
- } catch (IllegalAccessException e) {
- throw new IllegalArgumentException(sm.getString(
- "applicationContext.addListener.iae.cnfe", className),
- e);
} catch (InvocationTargetException e) {
ExceptionUtils.handleThrowable(e.getCause());
throw new IllegalArgumentException(sm.getString(
"applicationContext.addListener.iae.cnfe", className),
e);
- } catch (NamingException e) {
- throw new IllegalArgumentException(sm.getString(
- "applicationContext.addListener.iae.cnfe", className),
- e);
- } catch (InstantiationException e) {
- throw new IllegalArgumentException(sm.getString(
- "applicationContext.addListener.iae.cnfe", className),
- e);
- } catch (ClassNotFoundException e) {
+ } catch (IllegalAccessException | NamingException | InstantiationException |
+ ClassNotFoundException e) {
throw new IllegalArgumentException(sm.getString(
"applicationContext.addListener.iae.cnfe", className),
e);
@@ -1389,14 +1114,10 @@ public class ApplicationContext implements ServletContext {
throw new IllegalArgumentException(sm.getString(
"applicationContext.addListener.iae.wrongType",
listener.getClass().getName()));
- } catch (IllegalAccessException e) {
- throw new ServletException(e);
} catch (InvocationTargetException e) {
ExceptionUtils.handleThrowable(e.getCause());
throw new ServletException(e);
- } catch (NamingException e) {
- throw new ServletException(e);
- } catch (InstantiationException e) {
+ } catch (IllegalAccessException | NamingException | InstantiationException e) {
throw new ServletException(e);
}
}
@@ -1501,7 +1222,9 @@ public class ApplicationContext implements ServletContext {
@Override
public String getVirtualServerName() {
// Constructor will fail if context or its parent is null
- return ((Host) context.getParent()).getName();
+ Container host = context.getParent();
+ Container engine = host.getParent();
+ return engine.getName() + "/" + host.getName();
}
@@ -1534,7 +1257,7 @@ public class ApplicationContext implements ServletContext {
/**
- * Return the facade associated with this ApplicationContext.
+ * @return the facade associated with this ApplicationContext.
*/
protected ServletContext getFacade() {
diff --git a/java/org/apache/catalina/core/ApplicationContextFacade.java b/java/org/apache/catalina/core/ApplicationContextFacade.java
index cc469ad..f1ced89 100644
--- a/java/org/apache/catalina/core/ApplicationContextFacade.java
+++ b/java/org/apache/catalina/core/ApplicationContextFacade.java
@@ -858,12 +858,8 @@ public class ApplicationContextFacade implements ServletContext {
InvocationTargetException {
if (SecurityUtil.isPackageProtectionEnabled()){
- return AccessController.doPrivileged(new PrivilegedExceptionAction<Object>(){
- @Override
- public Object run() throws IllegalAccessException, InvocationTargetException{
- return method.invoke(context, params);
- }
- });
+ return AccessController.doPrivileged(
+ new PrivilegedExecuteMethod(method, context, params));
} else {
return method.invoke(context, params);
}
@@ -895,4 +891,23 @@ public class ApplicationContextFacade implements ServletContext {
throw realException;
}
+
+
+ private static class PrivilegedExecuteMethod implements PrivilegedExceptionAction<Object> {
+
+ private final Method method;
+ private final ApplicationContext context;
+ private final Object[] params;
+
+ public PrivilegedExecuteMethod(Method method, ApplicationContext context, Object[] params) {
+ this.method = method;
+ this.context = context;
+ this.params = params;
+ }
+
+ @Override
+ public Object run() throws Exception {
+ return method.invoke(context, params);
+ }
+ }
}
diff --git a/java/org/apache/catalina/core/ApplicationDispatcher.java b/java/org/apache/catalina/core/ApplicationDispatcher.java
index bc94048..88a5994 100644
--- a/java/org/apache/catalina/core/ApplicationDispatcher.java
+++ b/java/org/apache/catalina/core/ApplicationDispatcher.java
@@ -38,14 +38,13 @@ import javax.servlet.http.HttpServletResponse;
import org.apache.catalina.AsyncDispatcher;
import org.apache.catalina.Context;
import org.apache.catalina.Globals;
-import org.apache.catalina.InstanceEvent;
import org.apache.catalina.Wrapper;
import org.apache.catalina.connector.ClientAbortException;
import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.RequestFacade;
import org.apache.catalina.connector.Response;
import org.apache.catalina.connector.ResponseFacade;
-import org.apache.catalina.util.InstanceSupport;
+import org.apache.catalina.servlet4preview.http.Mapping;
import org.apache.tomcat.util.ExceptionUtils;
import org.apache.tomcat.util.res.StringManager;
@@ -61,7 +60,6 @@ import org.apache.tomcat.util.res.StringManager;
*
* @author Craig R. McClanahan
*/
- at SuppressWarnings("deprecation")
final class ApplicationDispatcher implements AsyncDispatcher, RequestDispatcher {
static final boolean STRICT_SERVLET_COMPLIANCE;
@@ -202,12 +200,13 @@ final class ApplicationDispatcher implements AsyncDispatcher, RequestDispatcher
* (if any)
* @param queryString Query string parameters included with this request
* (if any)
+ * @param mapping The mapping for this resource (if any)
* @param name Servlet name (if a named dispatcher was created)
* else <code>null</code>
*/
public ApplicationDispatcher
(Wrapper wrapper, String requestURI, String servletPath,
- String pathInfo, String queryString, String name) {
+ String pathInfo, String queryString, Mapping mapping, String name) {
super();
@@ -218,12 +217,8 @@ final class ApplicationDispatcher implements AsyncDispatcher, RequestDispatcher
this.servletPath = servletPath;
this.pathInfo = pathInfo;
this.queryString = queryString;
+ this.mapping = mapping;
this.name = name;
- if (wrapper instanceof StandardWrapper)
- this.support = ((StandardWrapper) wrapper).getInstanceSupport();
- else
- this.support = new InstanceSupport(wrapper);
-
}
@@ -266,17 +261,15 @@ final class ApplicationDispatcher implements AsyncDispatcher, RequestDispatcher
/**
- * The StringManager for this package.
+ * The mapping for this RequestDispatcher.
*/
- private static final StringManager sm =
- StringManager.getManager(Constants.Package);
+ private final Mapping mapping;
/**
- * The InstanceSupport instance associated with our Wrapper (used to
- * send "before dispatch" and "after dispatch" events.
+ * The StringManager for this package.
*/
- private final InstanceSupport support;
+ private static final StringManager sm = StringManager.getManager(Constants.Package);
/**
@@ -365,8 +358,7 @@ final class ApplicationDispatcher implements AsyncDispatcher, RequestDispatcher
(ApplicationHttpRequest) wrapRequest(state);
String contextPath = context.getPath();
HttpServletRequest hrequest = state.hrequest;
- if (hrequest.getAttribute(
- RequestDispatcher.FORWARD_REQUEST_URI) == null) {
+ if (hrequest.getAttribute(RequestDispatcher.FORWARD_REQUEST_URI) == null) {
wrequest.setAttribute(RequestDispatcher.FORWARD_REQUEST_URI,
hrequest.getRequestURI());
wrequest.setAttribute(RequestDispatcher.FORWARD_CONTEXT_PATH,
@@ -377,6 +369,16 @@ final class ApplicationDispatcher implements AsyncDispatcher, RequestDispatcher
hrequest.getPathInfo());
wrequest.setAttribute(RequestDispatcher.FORWARD_QUERY_STRING,
hrequest.getQueryString());
+ Mapping mapping;
+ if (hrequest instanceof org.apache.catalina.servlet4preview.http.HttpServletRequest) {
+ mapping = ((org.apache.catalina.servlet4preview.http.HttpServletRequest)
+ hrequest).getMapping();
+ } else {
+ mapping = (new ApplicationMapping(null)).getMapping();
+ }
+ wrequest.setAttribute(
+ org.apache.catalina.servlet4preview.RequestDispatcher.FORWARD_MAPPING,
+ mapping);
}
wrequest.setContextPath(contextPath);
@@ -387,6 +389,7 @@ final class ApplicationDispatcher implements AsyncDispatcher, RequestDispatcher
wrequest.setQueryString(queryString);
wrequest.setQueryParams(queryString);
}
+ wrequest.setMapping(mapping);
processRequest(request,response,state);
}
@@ -576,6 +579,11 @@ final class ApplicationDispatcher implements AsyncDispatcher, RequestDispatcher
queryString);
wrequest.setQueryParams(queryString);
}
+ if (mapping != null) {
+ wrequest.setAttribute(
+ org.apache.catalina.servlet4preview.RequestDispatcher.INCLUDE_MAPPING,
+ mapping);
+ }
wrequest.setAttribute(Globals.DISPATCHER_TYPE_ATTR,
DispatcherType.INCLUDE);
@@ -713,35 +721,23 @@ final class ApplicationDispatcher implements AsyncDispatcher, RequestDispatcher
// Call the service() method for the allocated servlet instance
try {
- support.fireInstanceEvent(InstanceEvent.BEFORE_DISPATCH_EVENT,
- servlet, request, response);
// for includes/forwards
if ((servlet != null) && (filterChain != null)) {
filterChain.doFilter(request, response);
}
// Servlet Service Method is called by the FilterChain
- support.fireInstanceEvent(InstanceEvent.AFTER_DISPATCH_EVENT,
- servlet, request, response);
} catch (ClientAbortException e) {
- support.fireInstanceEvent(InstanceEvent.AFTER_DISPATCH_EVENT,
- servlet, request, response);
ioException = e;
} catch (IOException e) {
- support.fireInstanceEvent(InstanceEvent.AFTER_DISPATCH_EVENT,
- servlet, request, response);
wrapper.getLogger().error(sm.getString("applicationDispatcher.serviceException",
wrapper.getName()), e);
ioException = e;
} catch (UnavailableException e) {
- support.fireInstanceEvent(InstanceEvent.AFTER_DISPATCH_EVENT,
- servlet, request, response);
wrapper.getLogger().error(sm.getString("applicationDispatcher.serviceException",
wrapper.getName()), e);
servletException = e;
wrapper.unavailable(e);
} catch (ServletException e) {
- support.fireInstanceEvent(InstanceEvent.AFTER_DISPATCH_EVENT,
- servlet, request, response);
Throwable rootCause = StandardWrapper.getRootCause(e);
if (!(rootCause instanceof ClientAbortException)) {
wrapper.getLogger().error(sm.getString("applicationDispatcher.serviceException",
@@ -749,8 +745,6 @@ final class ApplicationDispatcher implements AsyncDispatcher, RequestDispatcher
}
servletException = e;
} catch (RuntimeException e) {
- support.fireInstanceEvent(InstanceEvent.AFTER_DISPATCH_EVENT,
- servlet, request, response);
wrapper.getLogger().error(sm.getString("applicationDispatcher.serviceException",
wrapper.getName()), e);
runtimeException = e;
diff --git a/java/org/apache/catalina/core/ApplicationFilterChain.java b/java/org/apache/catalina/core/ApplicationFilterChain.java
index 7b3ef4c..18e30bb 100644
--- a/java/org/apache/catalina/core/ApplicationFilterChain.java
+++ b/java/org/apache/catalina/core/ApplicationFilterChain.java
@@ -14,11 +14,8 @@
* 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.security.Principal;
import java.security.PrivilegedActionException;
@@ -33,13 +30,7 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.catalina.Globals;
-import org.apache.catalina.InstanceEvent;
-import org.apache.catalina.comet.CometEvent;
-import org.apache.catalina.comet.CometFilter;
-import org.apache.catalina.comet.CometFilterChain;
-import org.apache.catalina.comet.CometProcessor;
import org.apache.catalina.security.SecurityUtil;
-import org.apache.catalina.util.InstanceSupport;
import org.apache.tomcat.util.ExceptionUtils;
import org.apache.tomcat.util.res.StringManager;
@@ -52,8 +43,7 @@ import org.apache.tomcat.util.res.StringManager;
*
* @author Craig R. McClanahan
*/
- at SuppressWarnings("deprecation")
-final class ApplicationFilterChain implements FilterChain, CometFilterChain {
+final class ApplicationFilterChain implements FilterChain {
// Used to enforce requirements of SRV.8.2 / SRV.14.2.5.1
private static final ThreadLocal<ServletRequest> lastServicedRequest;
@@ -75,27 +65,12 @@ final class ApplicationFilterChain implements FilterChain, CometFilterChain {
public static final int INCREMENT = 10;
- // ----------------------------------------------------------- Constructors
-
-
- /**
- * Construct a new chain instance with no defined filters.
- */
- public ApplicationFilterChain() {
-
- super();
-
- }
-
-
// ----------------------------------------------------- Instance Variables
-
/**
* Filters.
*/
- private ApplicationFilterConfig[] filters =
- new ApplicationFilterConfig[0];
+ private ApplicationFilterConfig[] filters = new ApplicationFilterConfig[0];
/**
@@ -118,17 +93,15 @@ final class ApplicationFilterChain implements FilterChain, CometFilterChain {
/**
- * The string manager for our package.
+ * Does the associated servlet instance support async processing?
*/
- private static final StringManager sm =
- StringManager.getManager(Constants.Package);
-
+ private boolean servletSupportsAsync = false;
/**
- * The InstanceSupport instance associated with our Wrapper (used to
- * send "before filter" and "after filter" events.
+ * The string manager for our package.
*/
- private InstanceSupport support = null;
+ private static final StringManager sm =
+ StringManager.getManager(Constants.Package);
/**
@@ -145,24 +118,9 @@ final class ApplicationFilterChain implements FilterChain, CometFilterChain {
private static final Class<?>[] classTypeUsedInService = new Class[]{
ServletRequest.class, ServletResponse.class};
- /**
- * Static class array used when the SecurityManager is turned on and
- * <code>doFilterEvent</code> is invoked.
- */
- private static final Class<?>[] cometClassType =
- new Class[]{ CometEvent.class, CometFilterChain.class};
-
- /**
- * Static class array used when the SecurityManager is turned on and
- * <code>event</code> is invoked.
- */
- private static final Class<?>[] classTypeUsedInEvent =
- new Class[] { CometEvent.class };
-
// ---------------------------------------------------- FilterChain Methods
-
/**
* Invoke the next filter in this chain, passing the specified request
* and response. If there are no more filters in this chain, invoke
@@ -215,16 +173,12 @@ final class ApplicationFilterChain implements FilterChain, CometFilterChain {
// Call the next filter if there is one
if (pos < n) {
ApplicationFilterConfig filterConfig = filters[pos++];
- Filter filter = null;
try {
- filter = filterConfig.getFilter();
- support.fireInstanceEvent(InstanceEvent.BEFORE_FILTER_EVENT,
- filter, request, response);
+ Filter filter = filterConfig.getFilter();
if (request.isAsyncSupported() && "false".equalsIgnoreCase(
filterConfig.getFilterDef().getAsyncSupported())) {
- request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR,
- Boolean.FALSE);
+ request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR, Boolean.FALSE);
}
if( Globals.IS_SECURITY_ENABLED ) {
final ServletRequest req = request;
@@ -233,28 +187,16 @@ final class ApplicationFilterChain implements FilterChain, CometFilterChain {
((HttpServletRequest) req).getUserPrincipal();
Object[] args = new Object[]{req, res, this};
- SecurityUtil.doAsPrivilege
- ("doFilter", filter, classType, args, principal);
-
+ SecurityUtil.doAsPrivilege ("doFilter", filter, classType, args, principal);
} else {
filter.doFilter(request, response, this);
}
-
- support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT,
- filter, request, response);
} catch (IOException | ServletException | RuntimeException e) {
- if (filter != null)
- support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT,
- filter, request, response, e);
throw e;
} catch (Throwable e) {
e = ExceptionUtils.unwrapInvocationTargetException(e);
ExceptionUtils.handleThrowable(e);
- if (filter != null)
- support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT,
- filter, request, response, e);
- throw new ServletException
- (sm.getString("filterChain.filter"), e);
+ throw new ServletException(sm.getString("filterChain.filter"), e);
}
return;
}
@@ -266,103 +208,39 @@ final class ApplicationFilterChain implements FilterChain, CometFilterChain {
lastServicedResponse.set(response);
}
- support.fireInstanceEvent(InstanceEvent.BEFORE_SERVICE_EVENT,
- servlet, request, response);
- if (request.isAsyncSupported()
- && !support.getWrapper().isAsyncSupported()) {
+ if (request.isAsyncSupported() && !servletSupportsAsync) {
request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR,
Boolean.FALSE);
}
// Use potentially wrapped request from this point
if ((request instanceof HttpServletRequest) &&
- (response instanceof HttpServletResponse)) {
-
- if( Globals.IS_SECURITY_ENABLED ) {
- final ServletRequest req = request;
- final ServletResponse res = response;
- Principal principal =
- ((HttpServletRequest) req).getUserPrincipal();
- Object[] args = new Object[]{req, res};
- SecurityUtil.doAsPrivilege("service",
- servlet,
- classTypeUsedInService,
- args,
- principal);
- } else {
- servlet.service(request, response);
- }
+ (response instanceof HttpServletResponse) &&
+ Globals.IS_SECURITY_ENABLED ) {
+ final ServletRequest req = request;
+ final ServletResponse res = response;
+ Principal principal =
+ ((HttpServletRequest) req).getUserPrincipal();
+ Object[] args = new Object[]{req, res};
+ SecurityUtil.doAsPrivilege("service",
+ servlet,
+ classTypeUsedInService,
+ args,
+ principal);
} else {
servlet.service(request, response);
}
- support.fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT,
- servlet, request, response);
- } catch (IOException e) {
- support.fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT,
- servlet, request, response, e);
- throw e;
- } catch (ServletException e) {
- support.fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT,
- servlet, request, response, e);
- throw e;
- } catch (RuntimeException e) {
- support.fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT,
- servlet, request, response, e);
+ } catch (IOException | ServletException | RuntimeException e) {
throw e;
} catch (Throwable e) {
+ e = ExceptionUtils.unwrapInvocationTargetException(e);
ExceptionUtils.handleThrowable(e);
- support.fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT,
- servlet, request, response, e);
- throw new ServletException
- (sm.getString("filterChain.servlet"), e);
+ throw new ServletException(sm.getString("filterChain.servlet"), e);
} finally {
if (ApplicationDispatcher.WRAP_SAME_OBJECT) {
lastServicedRequest.set(null);
lastServicedResponse.set(null);
}
}
-
- }
-
-
- /**
- * Process the event, using the security manager if the option is enabled.
- *
- * @param event the event to process
- *
- * @exception IOException if an input/output error occurs
- * @exception ServletException if a servlet exception occurs
- */
- @Override
- public void doFilterEvent(CometEvent event)
- throws IOException, ServletException {
-
- if( Globals.IS_SECURITY_ENABLED ) {
- final CometEvent ev = event;
- try {
- java.security.AccessController.doPrivileged(
- new java.security.PrivilegedExceptionAction<Void>() {
- @Override
- public Void run()
- throws ServletException, IOException {
- internalDoFilterEvent(ev);
- return null;
- }
- }
- );
- } catch( PrivilegedActionException pe) {
- Exception e = pe.getException();
- if (e instanceof ServletException)
- throw (ServletException) e;
- else if (e instanceof IOException)
- throw (IOException) e;
- else if (e instanceof RuntimeException)
- throw (RuntimeException) e;
- else
- throw new ServletException(e.getMessage(), e);
- }
- } else {
- internalDoFilterEvent(event);
- }
}
@@ -388,125 +266,8 @@ final class ApplicationFilterChain implements FilterChain, CometFilterChain {
}
- private void internalDoFilterEvent(CometEvent event)
- throws IOException, ServletException {
-
- // Call the next filter if there is one
- if (pos < n) {
- ApplicationFilterConfig filterConfig = filters[pos++];
- CometFilter filter = null;
- try {
- filter = (CometFilter) filterConfig.getFilter();
- // FIXME: No instance listener processing for events for now
- /*
- support.fireInstanceEvent(InstanceEvent.BEFORE_FILTER_EVENT,
- filter, event);
- */
-
- if( Globals.IS_SECURITY_ENABLED ) {
- final CometEvent ev = event;
- Principal principal =
- ev.getHttpServletRequest().getUserPrincipal();
-
- Object[] args = new Object[]{ev, this};
- SecurityUtil.doAsPrivilege("doFilterEvent", filter,
- cometClassType, args, principal);
-
- } else {
- filter.doFilterEvent(event, this);
- }
-
- /*support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT,
- filter, event);*/
- } catch (IOException e) {
- /*
- if (filter != null)
- support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT,
- filter, event, e);
- */
- throw e;
- } catch (ServletException e) {
- /*
- if (filter != null)
- support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT,
- filter, event, e);
- */
- throw e;
- } catch (RuntimeException e) {
- /*
- if (filter != null)
- support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT,
- filter, event, e);
- */
- throw e;
- } catch (Throwable e) {
- e = ExceptionUtils.unwrapInvocationTargetException(e);
- ExceptionUtils.handleThrowable(e);
- /*if (filter != null)
- support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT,
- filter, event, e);*/
- throw new ServletException
- (sm.getString("filterChain.filter"), e);
- }
- return;
- }
-
- // We fell off the end of the chain -- call the servlet instance
- try {
- /*
- support.fireInstanceEvent(InstanceEvent.BEFORE_SERVICE_EVENT,
- servlet, request, response);
- */
- if( Globals.IS_SECURITY_ENABLED ) {
- final CometEvent ev = event;
- Principal principal =
- ev.getHttpServletRequest().getUserPrincipal();
- Object[] args = new Object[]{ ev };
- SecurityUtil.doAsPrivilege("event",
- servlet,
- classTypeUsedInEvent,
- args,
- principal);
- } else {
- ((CometProcessor) servlet).event(event);
- }
- /*
- support.fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT,
- servlet, request, response);*/
- } catch (IOException e) {
- /*
- support.fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT,
- servlet, request, response, e);
- */
- throw e;
- } catch (ServletException e) {
- /*
- support.fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT,
- servlet, request, response, e);
- */
- throw e;
- } catch (RuntimeException e) {
- /*
- support.fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT,
- servlet, request, response, e);
- */
- throw e;
- } catch (Throwable e) {
- ExceptionUtils.handleThrowable(e);
- /*
- support.fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT,
- servlet, request, response, e);
- */
- throw new ServletException
- (sm.getString("filterChain.servlet"), e);
- }
-
- }
-
-
// -------------------------------------------------------- Package Methods
-
/**
* Add a filter to the set of filters that will be executed in this chain.
*
@@ -534,15 +295,13 @@ final class ApplicationFilterChain implements FilterChain, CometFilterChain {
* Release references to the filters and wrapper executed by this chain.
*/
void release() {
-
for (int i = 0; i < n; i++) {
filters[i] = null;
}
n = 0;
pos = 0;
servlet = null;
- support = null;
-
+ servletSupportsAsync = false;
}
@@ -560,24 +319,11 @@ final class ApplicationFilterChain implements FilterChain, CometFilterChain {
* @param servlet The Wrapper for the servlet to be executed
*/
void setServlet(Servlet servlet) {
-
this.servlet = servlet;
-
}
- /**
- * Set the InstanceSupport object used for event notifications
- * for this filter chain.
- *
- * @param support The InstanceSupport object for our Wrapper
- *
- * @deprecated Will be removed in 8.5.x onwards
- */
- @Deprecated
- void setSupport(InstanceSupport support) {
-
- this.support = support;
-
+ void setServletSupportsAsync(boolean servletSupportsAsync) {
+ this.servletSupportsAsync = servletSupportsAsync;
}
}
diff --git a/java/org/apache/catalina/core/ApplicationFilterConfig.java b/java/org/apache/catalina/core/ApplicationFilterConfig.java
index 15092df..43318a3 100644
--- a/java/org/apache/catalina/core/ApplicationFilterConfig.java
+++ b/java/org/apache/catalina/core/ApplicationFilterConfig.java
@@ -37,6 +37,7 @@ import javax.servlet.ServletException;
import org.apache.catalina.Context;
import org.apache.catalina.Globals;
import org.apache.catalina.security.SecurityUtil;
+import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.InstanceManager;
import org.apache.tomcat.util.ExceptionUtils;
@@ -61,8 +62,7 @@ public final class ApplicationFilterConfig implements FilterConfig, Serializable
static final StringManager sm =
StringManager.getManager(Constants.Package);
- private static final org.apache.juli.logging.Log log =
- LogFactory.getLog(ApplicationFilterConfig.class);
+ private static final Log log = LogFactory.getLog(ApplicationFilterConfig.class);
/**
* Empty String collection to serve as the basis for empty enumerations.
@@ -153,7 +153,7 @@ public final class ApplicationFilterConfig implements FilterConfig, Serializable
}
/**
- * Return the class of the filter we are configuring.
+ * @return The class of the filter we are configuring.
*/
public String getFilterClass() {
return filterDef.getFilterClass();
diff --git a/java/org/apache/catalina/core/ApplicationFilterFactory.java b/java/org/apache/catalina/core/ApplicationFilterFactory.java
index 7ef6223..977eec7 100644
--- a/java/org/apache/catalina/core/ApplicationFilterFactory.java
+++ b/java/org/apache/catalina/core/ApplicationFilterFactory.java
@@ -22,9 +22,7 @@ import javax.servlet.ServletRequest;
import org.apache.catalina.Globals;
import org.apache.catalina.Wrapper;
-import org.apache.catalina.comet.CometFilter;
import org.apache.catalina.connector.Request;
-import org.apache.tomcat.util.ExceptionUtils;
import org.apache.tomcat.util.descriptor.web.FilterMap;
/**
@@ -42,49 +40,30 @@ public final class ApplicationFilterFactory {
/**
- * Construct and return a FilterChain implementation that will wrap the
- * execution of the specified servlet instance. If we should not execute
- * a filter chain at all, return <code>null</code>.
+ * Construct a FilterChain implementation that will wrap the execution of
+ * the specified servlet instance.
*
* @param request The servlet request we are processing
+ * @param wrapper The wrapper managing the servlet instance
* @param servlet The servlet instance to be wrapped
-
+ *
+ * @return The configured FilterChain instance or null if none is to be
+ * executed.
*/
- @SuppressWarnings("deprecation")
- public static ApplicationFilterChain createFilterChain
- (ServletRequest request, Wrapper wrapper, Servlet servlet) {
-
- // get the dispatcher type
- DispatcherType dispatcher = null;
- if (request.getAttribute(Globals.DISPATCHER_TYPE_ATTR) != null) {
- dispatcher = (DispatcherType) request.getAttribute(
- Globals.DISPATCHER_TYPE_ATTR);
- }
- String requestPath = null;
- Object attribute = request.getAttribute(
- Globals.DISPATCHER_REQUEST_PATH_ATTR);
-
- if (attribute != null){
- requestPath = attribute.toString();
- }
+ public static ApplicationFilterChain createFilterChain(ServletRequest request,
+ Wrapper wrapper, Servlet servlet) {
// If there is no servlet to execute, return null
if (servlet == null)
- return (null);
-
- boolean comet = false;
+ return null;
// Create and initialize a filter chain object
ApplicationFilterChain filterChain = null;
if (request instanceof Request) {
Request req = (Request) request;
- comet = req.isComet();
if (Globals.IS_SECURITY_ENABLED) {
// Security: Do not recycle
filterChain = new ApplicationFilterChain();
- if (comet) {
- req.setFilterChain(filterChain);
- }
} else {
filterChain = (ApplicationFilterChain) req.getFilterChain();
if (filterChain == null) {
@@ -98,9 +77,7 @@ public final class ApplicationFilterFactory {
}
filterChain.setServlet(servlet);
-
- filterChain.setSupport
- (((StandardWrapper)wrapper).getInstanceSupport());
+ filterChain.setServletSupportsAsync(wrapper.isAsyncSupported());
// Acquire the filter mappings for this Context
StandardContext context = (StandardContext) wrapper.getParent();
@@ -111,6 +88,15 @@ public final class ApplicationFilterFactory {
return (filterChain);
// Acquire the information we will need to match filter mappings
+ DispatcherType dispatcher =
+ (DispatcherType) request.getAttribute(Globals.DISPATCHER_TYPE_ATTR);
+
+ String requestPath = null;
+ Object attribute = request.getAttribute(Globals.DISPATCHER_REQUEST_PATH_ATTR);
+ if (attribute != null){
+ requestPath = attribute.toString();
+ }
+
String servletName = wrapper.getName();
// Add the relevant path-mapped filters to this filter chain
@@ -126,23 +112,7 @@ public final class ApplicationFilterFactory {
// FIXME - log configuration problem
continue;
}
- boolean isCometFilter = false;
- if (comet) {
- try {
- isCometFilter = filterConfig.getFilter() instanceof CometFilter;
- } catch (Exception e) {
- // Note: The try catch is there because getFilter has a lot of
- // declared exceptions. However, the filter is allocated much
- // earlier
- Throwable t = ExceptionUtils.unwrapInvocationTargetException(e);
- ExceptionUtils.handleThrowable(t);
- }
- if (isCometFilter) {
- filterChain.addFilter(filterConfig);
- }
- } else {
- filterChain.addFilter(filterConfig);
- }
+ filterChain.addFilter(filterConfig);
}
// Add filters that match on servlet name second
@@ -158,26 +128,11 @@ public final class ApplicationFilterFactory {
// FIXME - log configuration problem
continue;
}
- boolean isCometFilter = false;
- if (comet) {
- try {
- isCometFilter = filterConfig.getFilter() instanceof CometFilter;
- } catch (Exception e) {
- // Note: The try catch is there because getFilter has a lot of
- // declared exceptions. However, the filter is allocated much
- // earlier
- }
- if (isCometFilter) {
- filterChain.addFilter(filterConfig);
- }
- } else {
- filterChain.addFilter(filterConfig);
- }
+ filterChain.addFilter(filterConfig);
}
// Return the completed filter chain
- return (filterChain);
-
+ return filterChain;
}
@@ -197,22 +152,22 @@ public final class ApplicationFilterFactory {
// Check the specific "*" special URL pattern, which also matches
// named dispatches
if (filterMap.getMatchAllUrlPatterns())
- return (true);
+ return true;
if (requestPath == null)
- return (false);
+ return false;
// Match on context relative request path
String[] testPaths = filterMap.getURLPatterns();
for (int i = 0; i < testPaths.length; i++) {
if (matchFiltersURL(testPaths[i], requestPath)) {
- return (true);
+ return true;
}
}
// No match
- return (false);
+ return false;
}
@@ -228,25 +183,25 @@ public final class ApplicationFilterFactory {
private static boolean matchFiltersURL(String testPath, String requestPath) {
if (testPath == null)
- return (false);
+ return false;
// Case 1 - Exact Match
if (testPath.equals(requestPath))
- return (true);
+ return true;
// Case 2 - Path Match ("/.../*")
if (testPath.equals("/*"))
- return (true);
+ return true;
if (testPath.endsWith("/*")) {
if (testPath.regionMatches(0, requestPath, 0,
testPath.length() - 2)) {
if (requestPath.length() == (testPath.length() - 2)) {
- return (true);
+ return true;
} else if ('/' == requestPath.charAt(testPath.length() - 2)) {
- return (true);
+ return true;
}
}
- return (false);
+ return false;
}
// Case 3 - Extension Match
@@ -263,7 +218,7 @@ public final class ApplicationFilterFactory {
}
// Case 4 - "Default" Match
- return (false); // NOTE - Not relevant for selecting filters
+ return false; // NOTE - Not relevant for selecting filters
}
@@ -280,16 +235,16 @@ public final class ApplicationFilterFactory {
String servletName) {
if (servletName == null) {
- return (false);
+ return false;
}
// Check the specific "*" special servlet name
else if (filterMap.getMatchAllServletNames()) {
- return (true);
+ return true;
} else {
String[] servletNames = filterMap.getServletNames();
for (int i = 0; i < servletNames.length; i++) {
if (servletName.equals(servletNames[i])) {
- return (true);
+ return true;
}
}
return false;
diff --git a/java/org/apache/catalina/core/ApplicationHttpRequest.java b/java/org/apache/catalina/core/ApplicationHttpRequest.java
index dd73305..279a73d 100644
--- a/java/org/apache/catalina/core/ApplicationHttpRequest.java
+++ b/java/org/apache/catalina/core/ApplicationHttpRequest.java
@@ -31,13 +31,14 @@ import javax.servlet.DispatcherType;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpSession;
import org.apache.catalina.Context;
import org.apache.catalina.Globals;
import org.apache.catalina.Manager;
import org.apache.catalina.Session;
+import org.apache.catalina.servlet4preview.http.Mapping;
+import org.apache.catalina.servlet4preview.http.PushBuilder;
import org.apache.catalina.util.ParameterMap;
import org.apache.tomcat.util.buf.B2CConverter;
import org.apache.tomcat.util.buf.MessageBytes;
@@ -59,7 +60,8 @@ import org.apache.tomcat.util.http.Parameters;
* @author Craig R. McClanahan
* @author Remy Maucherat
*/
-class ApplicationHttpRequest extends HttpServletRequestWrapper {
+class ApplicationHttpRequest
+ extends org.apache.catalina.servlet4preview.http.HttpServletRequestWrapper {
// ------------------------------------------------------- Static Variables
@@ -74,11 +76,15 @@ class ApplicationHttpRequest extends HttpServletRequestWrapper {
RequestDispatcher.INCLUDE_SERVLET_PATH,
RequestDispatcher.INCLUDE_PATH_INFO,
RequestDispatcher.INCLUDE_QUERY_STRING,
+ org.apache.catalina.servlet4preview.RequestDispatcher.INCLUDE_MAPPING,
RequestDispatcher.FORWARD_REQUEST_URI,
RequestDispatcher.FORWARD_CONTEXT_PATH,
RequestDispatcher.FORWARD_SERVLET_PATH,
RequestDispatcher.FORWARD_PATH_INFO,
- RequestDispatcher.FORWARD_QUERY_STRING };
+ RequestDispatcher.FORWARD_QUERY_STRING,
+ org.apache.catalina.servlet4preview.RequestDispatcher.FORWARD_MAPPING};
+
+ private static final int SPECIALS_FIRST_FORWARD_INDEX = 6;
// ----------------------------------------------------------- Constructors
@@ -88,6 +94,9 @@ class ApplicationHttpRequest extends HttpServletRequestWrapper {
* Construct a new wrapped request around the specified servlet request.
*
* @param request The servlet request being wrapped
+ * @param context The target context for the wrapped request
+ * @param crossContext {@code true} if the wrapped request will be a
+ * cross-context request, otherwise {@code false}
*/
public ApplicationHttpRequest(HttpServletRequest request, Context context,
boolean crossContext) {
@@ -178,6 +187,12 @@ class ApplicationHttpRequest extends HttpServletRequestWrapper {
/**
+ * The mapping for this request.
+ */
+ private Mapping mapping = null;
+
+
+ /**
* The currently active session for this request.
*/
protected Session session = null;
@@ -222,8 +237,9 @@ class ApplicationHttpRequest extends HttpServletRequestWrapper {
if (pos == -1) {
return getRequest().getAttribute(name);
} else {
- if ((specialAttributes[pos] == null)
- && (specialAttributes[5] == null) && (pos >= 5)) {
+ if ((specialAttributes[pos] == null) &&
+ (specialAttributes[SPECIALS_FIRST_FORWARD_INDEX] == null) &&
+ (pos >= SPECIALS_FIRST_FORWARD_INDEX)) {
// If it's a forward special attribute, and null, it means this
// is an include, so we check the wrapped request since
// the request could have been forwarded before the include
@@ -506,6 +522,12 @@ class ApplicationHttpRequest extends HttpServletRequestWrapper {
}
+ @Override
+ public Mapping getMapping() {
+ return mapping;
+ }
+
+
/**
* Return the session associated with this Request, creating one
* if necessary.
@@ -587,12 +609,12 @@ class ApplicationHttpRequest extends HttpServletRequestWrapper {
String requestedSessionId = getRequestedSessionId();
if (requestedSessionId == null)
- return (false);
+ return false;
if (context == null)
- return (false);
+ return false;
Manager manager = context.getManager();
if (manager == null)
- return (false);
+ return false;
Session session = null;
try {
session = manager.findSession(requestedSessionId);
@@ -600,9 +622,9 @@ class ApplicationHttpRequest extends HttpServletRequestWrapper {
// Ignore
}
if ((session != null) && session.isValid()) {
- return (true);
+ return true;
} else {
- return (false);
+ return false;
}
} else {
@@ -611,9 +633,14 @@ class ApplicationHttpRequest extends HttpServletRequestWrapper {
}
- // -------------------------------------------------------- Package Methods
+ @Override
+ public PushBuilder getPushBuilder() {
+ return new ApplicationPushBuilder(this);
+ }
+ // -------------------------------------------------------- Package Methods
+
/**
* Recycle this request
*/
@@ -671,8 +698,7 @@ class ApplicationHttpRequest extends HttpServletRequestWrapper {
// Initialize the attributes for this request
dispatcherType = (DispatcherType)request.getAttribute(Globals.DISPATCHER_TYPE_ATTR);
- requestDispatcherPath =
- request.getAttribute(Globals.DISPATCHER_REQUEST_PATH_ATTR);
+ requestDispatcherPath = request.getAttribute(Globals.DISPATCHER_REQUEST_PATH_ATTR);
// Initialize the path elements for this request
contextPath = request.getContextPath();
@@ -680,7 +706,11 @@ class ApplicationHttpRequest extends HttpServletRequestWrapper {
queryString = request.getQueryString();
requestURI = request.getRequestURI();
servletPath = request.getServletPath();
-
+ if (request instanceof org.apache.catalina.servlet4preview.http.HttpServletRequest) {
+ mapping = ((org.apache.catalina.servlet4preview.http.HttpServletRequest) request).getMapping();
+ } else {
+ mapping = (new ApplicationMapping(null)).getMapping();
+ }
}
@@ -738,6 +768,12 @@ class ApplicationHttpRequest extends HttpServletRequestWrapper {
this.queryParamString = queryString;
}
+
+ void setMapping(Mapping mapping) {
+ this.mapping = mapping;
+ }
+
+
// ------------------------------------------------------ Protected Methods
/**
@@ -750,9 +786,9 @@ class ApplicationHttpRequest extends HttpServletRequestWrapper {
for (int i = 0; i < specials.length; i++) {
if (specials[i].equals(name))
- return (true);
+ return true;
}
- return (false);
+ return false;
}
@@ -782,10 +818,10 @@ class ApplicationHttpRequest extends HttpServletRequestWrapper {
for (int i = 0; i < specials.length; i++) {
if (specials[i].equals(name)) {
specialAttributes[i] = value;
- return (true);
+ return true;
}
}
- return (false);
+ return false;
}
@@ -798,10 +834,10 @@ class ApplicationHttpRequest extends HttpServletRequestWrapper {
for (int i = 0; i < specials.length; i++) {
if (specials[i].equals(name)) {
specialAttributes[i] = null;
- return (true);
+ return true;
}
}
- return (false);
+ return false;
}
@@ -811,33 +847,28 @@ class ApplicationHttpRequest extends HttpServletRequestWrapper {
* @param values1 First set of values
* @param values2 Second set of values
*/
- protected String[] mergeValues(Object values1, Object values2) {
+ private String[] mergeValues(String[] values1, String[] values2) {
ArrayList<Object> results = new ArrayList<>();
if (values1 == null) {
// Skip - nothing to merge
- } else if (values1 instanceof String[]) {
- for (String value : (String[]) values1) {
+ } else {
+ for (String value : values1) {
results.add(value);
}
- } else { // String
- results.add(values1.toString());
}
if (values2 == null) {
// Skip - nothing to merge
- } else if (values2 instanceof String[]) {
- for (String value : (String[]) values2) {
+ } else {
+ for (String value : values2) {
results.add(value);
}
- } else { // String
- results.add(values2.toString());
}
String values[] = new String[results.size()];
return results.toArray(values);
-
}
diff --git a/java/org/apache/catalina/core/ApplicationMapping.java b/java/org/apache/catalina/core/ApplicationMapping.java
new file mode 100644
index 0000000..17bcbe2
--- /dev/null
+++ b/java/org/apache/catalina/core/ApplicationMapping.java
@@ -0,0 +1,115 @@
+/*
+ * 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 org.apache.catalina.mapper.MappingData;
+import org.apache.catalina.servlet4preview.http.Mapping;
+import org.apache.catalina.servlet4preview.http.MappingMatch;
+
+public class ApplicationMapping {
+
+ private final MappingData mappingData;
+
+ private volatile Mapping mapping = null;
+
+ public ApplicationMapping(MappingData mappingData) {
+ this.mappingData = mappingData;
+ }
+
+ public Mapping getMapping() {
+ if (mapping == null) {
+ if (mappingData == null) {
+ mapping = new MappingImpl("", "", MappingMatch.UNKNOWN, "");
+ } else {
+ String servletName;
+ if (mappingData.wrapper == null) {
+ servletName = "";
+ } else {
+ servletName = mappingData.wrapper.getName();
+ }
+ switch (mappingData.matchType) {
+ case CONTEXT_ROOT:
+ mapping = new MappingImpl("", "", mappingData.matchType, servletName);
+ break;
+ case DEFAULT:
+ mapping = new MappingImpl("/", "/", mappingData.matchType, servletName);
+ break;
+ case EXACT:
+ mapping = new MappingImpl(mappingData.wrapperPath.toString(),
+ mappingData.wrapperPath.toString(), mappingData.matchType, servletName);
+ break;
+ case EXTENSION:
+ String path = mappingData.wrapperPath.toString();
+ int extIndex = path.lastIndexOf('.');
+ mapping = new MappingImpl(path.substring(0, extIndex),
+ "*" + path.substring(extIndex), mappingData.matchType, servletName);
+ break;
+ case PATH:
+ mapping = new MappingImpl(mappingData.pathInfo.toString(),
+ mappingData.wrapperPath.toString() + "/*",
+ mappingData.matchType, servletName);
+ break;
+ case UNKNOWN:
+ mapping = new MappingImpl("", "", mappingData.matchType, servletName);
+ break;
+ }
+ }
+ }
+
+ return mapping;
+ }
+
+ public void recycle() {
+ mapping = null;
+ }
+
+ private static class MappingImpl implements Mapping {
+
+ private final String matchValue;
+ private final String pattern;
+ private final MappingMatch mappingType;
+ private final String servletName;
+
+ public MappingImpl(String matchValue, String pattern, MappingMatch mappingType,
+ String servletName) {
+ this.matchValue = matchValue;
+ this.pattern = pattern;
+ this.mappingType = mappingType;
+ this.servletName = servletName;
+ }
+
+ @Override
+ public String getMatchValue() {
+ return matchValue;
+ }
+
+ @Override
+ public String getPattern() {
+ return pattern;
+ }
+
+ @Override
+ public MappingMatch getMappingMatch() {
+ return mappingType;
+ }
+
+ @Override
+ public String getServletName() {
+ return servletName;
+ }
+ }
+}
diff --git a/java/org/apache/catalina/core/ApplicationPushBuilder.java b/java/org/apache/catalina/core/ApplicationPushBuilder.java
new file mode 100644
index 0000000..1c29eac
--- /dev/null
+++ b/java/org/apache/catalina/core/ApplicationPushBuilder.java
@@ -0,0 +1,479 @@
+/*
+ * 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.UnsupportedEncodingException;
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletRequestWrapper;
+import javax.servlet.SessionTrackingMode;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
+
+import org.apache.catalina.Context;
+import org.apache.catalina.connector.Request;
+import org.apache.catalina.servlet4preview.http.PushBuilder;
+import org.apache.catalina.util.SessionConfig;
+import org.apache.coyote.ActionCode;
+import org.apache.coyote.PushToken;
+import org.apache.tomcat.util.buf.B2CConverter;
+import org.apache.tomcat.util.buf.HexUtils;
+import org.apache.tomcat.util.collections.CaseInsensitiveKeyMap;
+import org.apache.tomcat.util.http.CookieProcessor;
+import org.apache.tomcat.util.res.StringManager;
+
+public class ApplicationPushBuilder implements PushBuilder {
+
+ private static final StringManager sm = StringManager.getManager(ApplicationPushBuilder.class);
+
+ private final HttpServletRequest baseRequest;
+ private final Request catalinaRequest;
+ private final org.apache.coyote.Request coyoteRequest;
+ private final String sessionCookieName;
+ private final String sessionPathParameterName;
+ private final boolean addSessionCookie;
+ private final boolean addSessionPathParameter;
+
+ private final Map<String,List<String>> headers = new CaseInsensitiveKeyMap<>();
+ private final List<Cookie> cookies = new ArrayList<>();
+ private String method = "GET";
+ private String path;
+ private String etag;
+ private String lastModified;
+ private String queryString;
+ private String sessionId;
+ private boolean conditional;
+
+
+ public ApplicationPushBuilder(HttpServletRequest request) {
+ baseRequest = request;
+ // Need a reference to the CoyoteRequest in order to process the push
+ ServletRequest current = request;
+ while (current instanceof ServletRequestWrapper) {
+ current = ((ServletRequestWrapper) current).getRequest();
+ }
+ if (current instanceof Request) {
+ catalinaRequest = ((Request) current);
+ coyoteRequest = catalinaRequest.getCoyoteRequest();
+ } else {
+ throw new UnsupportedOperationException(sm.getString(
+ "applicationPushBuilder.noCoyoteRequest", current.getClass().getName()));
+ }
+
+ // Populate the initial list of HTTP headers
+ Enumeration<String> headerNames = request.getHeaderNames();
+ while (headerNames.hasMoreElements()) {
+ String headerName = headerNames.nextElement();
+ List<String> values = new ArrayList<>();
+ headers.put(headerName, values);
+ Enumeration<String> headerValues = request.getHeaders(headerName);
+ while (headerValues.hasMoreElements()) {
+ values.add(headerValues.nextElement());
+ }
+ }
+
+ // Remove the headers
+ headers.remove("if-match");
+ if (headers.remove("if-none-match") != null) {
+ conditional = true;
+ }
+ if (headers.remove("if-modified-since") != null) {
+ conditional = true;
+ }
+ headers.remove("if-unmodified-since");
+ headers.remove("if-range");
+ headers.remove("range");
+ headers.remove("expect");
+ headers.remove("authorization");
+ headers.remove("referer");
+ // Also remove the cookie header since it will be regenerated
+ headers.remove("cookie");
+
+ // set the referer header
+ StringBuffer referer = request.getRequestURL();
+ if (request.getQueryString() != null) {
+ referer.append('?');
+ referer.append(request.getQueryString());
+
+ }
+ addHeader("referer", referer.toString());
+
+ // Session
+ Context context = catalinaRequest.getContext();
+ sessionCookieName = SessionConfig.getSessionCookieName(context);
+ sessionPathParameterName = SessionConfig.getSessionUriParamName(context);
+
+ HttpSession session = request.getSession(false);
+ if (session != null) {
+ sessionId = session.getId();
+ }
+ if (sessionId == null) {
+ sessionId = request.getRequestedSessionId();
+ }
+ if (!request.isRequestedSessionIdFromCookie() && !request.isRequestedSessionIdFromURL() &&
+ sessionId != null) {
+ Set<SessionTrackingMode> sessionTrackingModes =
+ request.getServletContext().getEffectiveSessionTrackingModes();
+ addSessionCookie = sessionTrackingModes.contains(SessionTrackingMode.COOKIE);
+ addSessionPathParameter = sessionTrackingModes.contains(SessionTrackingMode.URL);
+ } else {
+ addSessionCookie = request.isRequestedSessionIdFromCookie();
+ addSessionPathParameter = request.isRequestedSessionIdFromURL();
+ }
+
+ // Cookies
+ if (request.getCookies() != null) {
+ for (Cookie requestCookie : request.getCookies()) {
+ cookies.add(requestCookie);
+ }
+ }
+ for (Cookie responseCookie : catalinaRequest.getResponse().getCookies()) {
+ if (responseCookie.getMaxAge() < 0) {
+ // Path information not available so can only remove based on
+ // name.
+ Iterator<Cookie> cookieIterator = cookies.iterator();
+ while (cookieIterator.hasNext()) {
+ Cookie cookie = cookieIterator.next();
+ if (cookie.getName().equals(responseCookie.getName())) {
+ cookieIterator.remove();
+ }
+ }
+ } else {
+ cookies.add(new Cookie(responseCookie.getName(), responseCookie.getValue()));
+ }
+ }
+ }
+
+
+ @Override
+ public ApplicationPushBuilder path(String path) {
+ if (path.startsWith("/")) {
+ this.path = path;
+ } else {
+ String contextPath = baseRequest.getContextPath();
+ int len = contextPath.length() + path.length() + 1;
+ StringBuilder sb = new StringBuilder(len);
+ sb.append(contextPath);
+ sb.append('/');
+ sb.append(path);
+ this.path = sb.toString();
+ }
+ return this;
+ }
+
+
+ @Override
+ public String getPath() {
+ return path;
+ }
+
+
+ @Override
+ public ApplicationPushBuilder method(String method) {
+ this.method = method;
+ return this;
+ }
+
+
+ @Override
+ public String getMethod() {
+ return method;
+ }
+
+
+ @Override
+ public ApplicationPushBuilder etag(String etag) {
+ this.etag = etag;
+ return this;
+ }
+
+
+ @Override
+ public String getEtag() {
+ return etag;
+ }
+
+
+ @Override
+ public ApplicationPushBuilder lastModified(String lastModified) {
+ this.lastModified = lastModified;
+ return this;
+ }
+
+
+ @Override
+ public String getLastModified() {
+ return lastModified;
+ }
+
+
+ @Override
+ public ApplicationPushBuilder queryString(String queryString) {
+ this.queryString = queryString;
+ return this;
+ }
+
+
+ @Override
+ public String getQueryString() {
+ return queryString;
+ }
+
+
+ @Override
+ public ApplicationPushBuilder sessionId(String sessionId) {
+ this.sessionId = sessionId;
+ return this;
+ }
+
+
+ @Override
+ public String getSessionId() {
+ return sessionId;
+ }
+
+
+ @Override
+ public ApplicationPushBuilder conditional(boolean conditional) {
+ this.conditional = conditional;
+ return this;
+ }
+
+
+ @Override
+ public boolean isConditional() {
+ return conditional;
+ }
+
+
+ @Override
+ public ApplicationPushBuilder addHeader(String name, String value) {
+ List<String> values = headers.get(name);
+ if (values == null) {
+ values = new ArrayList<>();
+ headers.put(name, values);
+ }
+ values.add(value);
+
+ return this;
+ }
+
+
+ @Override
+ public ApplicationPushBuilder setHeader(String name, String value) {
+ List<String> values = headers.get(name);
+ if (values == null) {
+ values = new ArrayList<>();
+ headers.put(name, values);
+ } else {
+ values.clear();
+ }
+ values.add(value);
+
+ return this;
+ }
+
+
+ @Override
+ public ApplicationPushBuilder removeHeader(String name) {
+ headers.remove(name);
+
+ return this;
+ }
+
+
+ @Override
+ public Set<String> getHeaderNames() {
+ return Collections.unmodifiableSet(headers.keySet());
+ }
+
+
+ @Override
+ public String getHeader(String name) {
+ List<String> values = headers.get(name);
+ if (values == null) {
+ return null;
+ } else {
+ return values.get(0);
+ }
+ }
+
+
+ @Override
+ public boolean push() {
+ if (path == null) {
+ throw new IllegalStateException(sm.getString("pushBuilder.noPath"));
+ }
+
+ org.apache.coyote.Request pushTarget = new org.apache.coyote.Request();
+
+ pushTarget.method().setString(method);
+ // The next three are implied by the Javadoc getPath()
+ pushTarget.serverName().setString(baseRequest.getServerName());
+ pushTarget.setServerPort(baseRequest.getServerPort());
+ pushTarget.scheme().setString(baseRequest.getScheme());
+
+ // Copy headers
+ for (Map.Entry<String,List<String>> header : headers.entrySet()) {
+ for (String value : header.getValue()) {
+ pushTarget.getMimeHeaders().addValue(header.getKey()).setString(value);
+ }
+ }
+
+ // Path and query string
+ int queryIndex = path.indexOf('?');
+ String pushPath;
+ String pushQueryString = null;
+ if (queryIndex > -1) {
+ pushPath = path.substring(0, queryIndex);
+ if (queryIndex + 1 < path.length()) {
+ pushQueryString = path.substring(queryIndex + 1);
+ }
+ } else {
+ pushPath = path;
+ }
+
+ // Session ID (do this before setting the path since it may change it)
+ if (sessionId != null) {
+ if (addSessionPathParameter) {
+ pushPath = pushPath + ";" + sessionPathParameterName + "=" + sessionId;
+ pushTarget.addPathParameter(sessionPathParameterName, sessionId);
+ }
+ if (addSessionCookie) {
+ cookies.add(new Cookie(sessionCookieName, sessionId));
+ }
+ }
+
+ // Undecoded path - just %nn encoded
+ pushTarget.requestURI().setString(pushPath);
+ pushTarget.decodedURI().setString(decode(pushPath,
+ catalinaRequest.getConnector().getURIEncodingLower()));
+
+ // Query string
+ if (pushQueryString == null && queryString != null) {
+ pushTarget.queryString().setString(queryString);
+ } else if (pushQueryString != null && queryString == null) {
+ pushTarget.queryString().setString(pushQueryString);
+ } else if (pushQueryString != null && queryString != null) {
+ pushTarget.queryString().setString(pushQueryString + "&" +queryString);
+ }
+
+ if (conditional) {
+ if (etag != null) {
+ setHeader("if-none-match", etag);
+ } else if (lastModified != null) {
+ setHeader("if-modified-since", lastModified);
+ }
+ }
+
+ // Cookies
+ setHeader("cookie", generateCookieHeader(cookies,
+ catalinaRequest.getContext().getCookieProcessor()));
+
+ PushToken pushToken = new PushToken(pushTarget);
+ coyoteRequest.action(ActionCode.PUSH_REQUEST, pushToken);
+
+ // Reset for next call to this method
+ pushTarget = null;
+ path = null;
+ etag = null;
+ lastModified = null;
+ headers.remove("if-none-match");
+ headers.remove("if-modified-since");
+
+ return pushToken.getResult();
+ }
+
+
+ // Package private so it can be tested. charsetName must be in lower case.
+ static String decode(String input, String charsetName) {
+ int start = input.indexOf('%');
+ int end = 0;
+
+ // Shortcut
+ if (start == -1) {
+ return input;
+ }
+
+ Charset charset;
+ try {
+ charset = B2CConverter.getCharsetLower(charsetName);
+ } catch (UnsupportedEncodingException uee) {
+ // Impossible since original request would have triggered an error
+ // before reaching here
+ throw new IllegalStateException(uee);
+ }
+
+ StringBuilder result = new StringBuilder(input.length());
+ while (start != -1) {
+ // Found the start of a %nn sequence. Copy everything form the last
+ // end to this start to the output.
+ result.append(input.substring(end, start));
+ // Advance the end 3 characters: %nn
+ end = start + 3;
+ while (end <input.length() && input.charAt(end) == '%') {
+ end += 3;
+ }
+ result.append(decode(input.substring(start, end), charset));
+ start = input.indexOf('%', end);
+ }
+ // Append the remaining text
+ result.append(input.substring(end));
+
+ return result.toString();
+ }
+
+
+ private static String decode(String percentSequence, Charset charset) {
+ byte[] bytes = new byte[percentSequence.length()/3];
+ for (int i = 0; i < bytes.length; i += 3) {
+ bytes[i] = (byte) (HexUtils.getDec(percentSequence.charAt(1 + 3 * i)) << 4 +
+ HexUtils.getDec(percentSequence.charAt(2 + 3 * i)));
+ }
+
+ return new String(bytes, charset);
+ }
+
+
+ private static String generateCookieHeader(List<Cookie> cookies, CookieProcessor cookieProcessor) {
+ StringBuilder result = new StringBuilder();
+ boolean first = true;
+ for (Cookie cookie : cookies) {
+ if (first) {
+ first = false;
+ } else {
+ result.append(';');
+ }
+ // The cookie header value generated by the CookieProcessor was
+ // originally intended for the Set-Cookie header on the response.
+ // However, if passed a Cookie with just a name and value set it
+ // will generate an appropriate header for the Cookie header on the
+ // pushed request.
+ result.append(cookieProcessor.generateHeader(cookie));
+ }
+ return result.toString();
+ }
+}
diff --git a/java/org/apache/catalina/core/ApplicationRequest.java b/java/org/apache/catalina/core/ApplicationRequest.java
index 23a852c..d60e946 100644
--- a/java/org/apache/catalina/core/ApplicationRequest.java
+++ b/java/org/apache/catalina/core/ApplicationRequest.java
@@ -199,9 +199,9 @@ class ApplicationRequest extends ServletRequestWrapper {
for (int i = 0; i < specials.length; i++) {
if (specials[i].equals(name))
- return (true);
+ return true;
}
- return (false);
+ return false;
}
diff --git a/java/org/apache/catalina/core/ApplicationSessionCookieConfig.java b/java/org/apache/catalina/core/ApplicationSessionCookieConfig.java
index fc827bb..5b99b47 100644
--- a/java/org/apache/catalina/core/ApplicationSessionCookieConfig.java
+++ b/java/org/apache/catalina/core/ApplicationSessionCookieConfig.java
@@ -158,6 +158,7 @@ public class ApplicationSessionCookieConfig implements SessionCookieConfig {
* @param sessionId The ID of the session for which the cookie will be
* created
* @param secure Should session cookie be configured as secure
+ * @return the cookie for the session
*/
public static Cookie createSessionCookie(Context context,
String sessionId, boolean secure) {
diff --git a/java/org/apache/catalina/core/AprLifecycleListener.java b/java/org/apache/catalina/core/AprLifecycleListener.java
index dff3de2..c76b94f 100644
--- a/java/org/apache/catalina/core/AprLifecycleListener.java
+++ b/java/org/apache/catalina/core/AprLifecycleListener.java
@@ -40,7 +40,6 @@ import org.apache.tomcat.util.res.StringManager;
* Implementation of <code>LifecycleListener</code> that will init and
* and destroy APR.
*
- * @author Remy Maucherat
* @since 4.1
*/
public class AprLifecycleListener
@@ -66,8 +65,8 @@ public class AprLifecycleListener
protected static final int TCN_REQUIRED_MAJOR = 1;
- protected static final int TCN_REQUIRED_MINOR = 1;
- protected static final int TCN_REQUIRED_PATCH = 32;
+ protected static final int TCN_REQUIRED_MINOR = 2;
+ protected static final int TCN_REQUIRED_PATCH = 6;
protected static final int TCN_RECOMMENDED_MINOR = 2;
protected static final int TCN_RECOMMENDED_PV = 8;
@@ -79,6 +78,8 @@ public class AprLifecycleListener
protected static boolean sslInitialized = false;
protected static boolean aprInitialized = false;
protected static boolean aprAvailable = false;
+ protected static boolean useAprConnector = false;
+ protected static boolean useOpenSSL = true;
protected static boolean fipsModeActive = false;
/**
@@ -252,6 +253,11 @@ public class AprLifecycleListener
Boolean.valueOf(Library.APR_HAS_SENDFILE),
Boolean.valueOf(Library.APR_HAS_SO_ACCEPTFILTER),
Boolean.valueOf(Library.APR_HAS_RANDOM)));
+
+ initInfoLogMessages.add(sm.getString("aprListener.config",
+ Boolean.valueOf(useAprConnector),
+ Boolean.valueOf(useOpenSSL)));
+
aprAvailable = true;
}
@@ -394,4 +400,25 @@ public class AprLifecycleListener
public boolean isFIPSModeActive() {
return fipsModeActive;
}
+
+ public void setUseAprConnector(boolean useAprConnector) {
+ if (useAprConnector != AprLifecycleListener.useAprConnector) {
+ AprLifecycleListener.useAprConnector = useAprConnector;
+ }
+ }
+
+ public static boolean getUseAprConnector() {
+ return useAprConnector;
+ }
+
+ public void setUseOpenSSL(boolean useOpenSSL) {
+ if (useOpenSSL != AprLifecycleListener.useOpenSSL) {
+ AprLifecycleListener.useOpenSSL = useOpenSSL;
+ }
+ }
+
+ public static boolean getUseOpenSSL() {
+ return useOpenSSL;
+ }
+
}
diff --git a/java/org/apache/catalina/core/AsyncContextImpl.java b/java/org/apache/catalina/core/AsyncContextImpl.java
index 5735407..dcc0902 100644
--- a/java/org/apache/catalina/core/AsyncContextImpl.java
+++ b/java/org/apache/catalina/core/AsyncContextImpl.java
@@ -17,7 +17,6 @@
package org.apache.catalina.core;
import java.io.IOException;
-import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -198,20 +197,8 @@ public class AsyncContextImpl implements AsyncContext, AsyncContextCallback {
(AsyncDispatcher) requestDispatcher;
final ServletRequest servletRequest = getRequest();
final ServletResponse servletResponse = getResponse();
- Runnable run = new Runnable() {
- @Override
- public void run() {
- request.getCoyoteRequest().action(ActionCode.ASYNC_DISPATCHED, null);
- try {
- applicationDispatcher.dispatch(servletRequest, servletResponse);
- }catch (Exception x) {
- //log.error("Async.dispatch",x);
- throw new RuntimeException(x);
- }
- }
- };
-
- this.dispatch = run;
+ this.dispatch = new AsyncRunnable(
+ request, applicationDispatcher, servletRequest, servletResponse);
this.request.getCoyoteRequest().action(ActionCode.ASYNC_DISPATCH, null);
clearServletRequestResponse();
}
@@ -275,20 +262,8 @@ public class AsyncContextImpl implements AsyncContext, AsyncContextCallback {
try {
listener = (T) getInstanceManager().newInstance(clazz.getName(),
clazz.getClassLoader());
- } catch (InstantiationException e) {
- ServletException se = new ServletException(e);
- throw se;
- } catch (IllegalAccessException e) {
- ServletException se = new ServletException(e);
- throw se;
- } catch (InvocationTargetException e) {
- ExceptionUtils.handleThrowable(e.getCause());
- ServletException se = new ServletException(e);
- throw se;
- } catch (NamingException e) {
- ServletException se = new ServletException(e);
- throw se;
- } catch (ClassNotFoundException e) {
+ } catch (InstantiationException | IllegalAccessException | NamingException |
+ ClassNotFoundException e) {
ServletException se = new ServletException(e);
throw se;
} catch (Exception e) {
@@ -556,4 +531,33 @@ public class AsyncContextImpl implements AsyncContext, AsyncContextCallback {
coyoteRequest.action(ActionCode.DISPATCH_EXECUTE, null);
}
}
+
+
+ private static class AsyncRunnable implements Runnable {
+
+ private final AsyncDispatcher applicationDispatcher;
+ private final Request request;
+ private final ServletRequest servletRequest;
+ private final ServletResponse servletResponse;
+
+ public AsyncRunnable(Request request, AsyncDispatcher applicationDispatcher,
+ ServletRequest servletRequest, ServletResponse servletResponse) {
+ this.request = request;
+ this.applicationDispatcher = applicationDispatcher;
+ this.servletRequest = servletRequest;
+ this.servletResponse = servletResponse;
+ }
+
+ @Override
+ public void run() {
+ request.getCoyoteRequest().action(ActionCode.ASYNC_DISPATCHED, null);
+ try {
+ applicationDispatcher.dispatch(servletRequest, servletResponse);
+ }catch (Exception x) {
+ //log.error("Async.dispatch",x);
+ throw new RuntimeException(x);
+ }
+ }
+
+ }
}
diff --git a/java/org/apache/catalina/core/Constants.java b/java/org/apache/catalina/core/Constants.java
index 44f916f..a8d0267 100644
--- a/java/org/apache/catalina/core/Constants.java
+++ b/java/org/apache/catalina/core/Constants.java
@@ -14,17 +14,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
-
package org.apache.catalina.core;
-
public class Constants {
public static final String Package = "org.apache.catalina.core";
public static final int MAJOR_VERSION = 3;
public static final int MINOR_VERSION = 1;
- public static final String JSP_SERVLET_CLASS =
- "org.apache.jasper.servlet.JspServlet";
+ public static final String JSP_SERVLET_CLASS = "org.apache.jasper.servlet.JspServlet";
}
diff --git a/java/org/apache/catalina/core/ContainerBase.java b/java/org/apache/catalina/core/ContainerBase.java
index f5fa271..353e4b2 100644
--- a/java/org/apache/catalina/core/ContainerBase.java
+++ b/java/org/apache/catalina/core/ContainerBase.java
@@ -124,15 +124,12 @@ import org.apache.tomcat.util.res.StringManager;
* Subclasses that fire additional events should document them in the
* class comments of the implementation class.
*
- * TODO: Review synchronisation around background processing. See bug 47024.
- *
* @author Craig R. McClanahan
*/
public abstract class ContainerBase extends LifecycleMBeanBase
implements Container {
- private static final org.apache.juli.logging.Log log=
- org.apache.juli.logging.LogFactory.getLog( ContainerBase.class );
+ private static final Log log = LogFactory.getLog(ContainerBase.class);
/**
* Perform addChild with the permissions of this class.
@@ -516,6 +513,8 @@ public abstract class ContainerBase extends LifecycleMBeanBase
/**
* Return if children of this container will be started automatically when
* they are added to this container.
+ *
+ * @return <code>true</code> if the children will be started
*/
public boolean getStartChildren() {
@@ -779,9 +778,7 @@ public abstract class ContainerBase extends LifecycleMBeanBase
*/
@Override
public void addPropertyChangeListener(PropertyChangeListener listener) {
-
support.addPropertyChangeListener(listener);
-
}
@@ -793,13 +790,12 @@ public abstract class ContainerBase extends LifecycleMBeanBase
*/
@Override
public Container findChild(String name) {
-
- if (name == null)
- return (null);
+ if (name == null) {
+ return null;
+ }
synchronized (children) {
return children.get(name);
}
-
}
@@ -809,12 +805,10 @@ public abstract class ContainerBase extends LifecycleMBeanBase
*/
@Override
public Container[] findChildren() {
-
synchronized (children) {
Container results[] = new Container[children.size()];
return children.values().toArray(results);
}
-
}
@@ -924,11 +918,13 @@ public abstract class ContainerBase extends LifecycleMBeanBase
logger = null;
getLogger();
Cluster cluster = getClusterInternal();
- if ((cluster != null) && (cluster instanceof Lifecycle))
+ if (cluster instanceof Lifecycle) {
((Lifecycle) cluster).start();
+ }
Realm realm = getRealmInternal();
- if ((realm != null) && (realm instanceof Lifecycle))
+ if (realm instanceof Lifecycle) {
((Lifecycle) realm).start();
+ }
// Start our child containers, if any
Container children[] = findChildren();
@@ -1009,11 +1005,11 @@ public abstract class ContainerBase extends LifecycleMBeanBase
// Stop our subordinate components, if any
Realm realm = getRealmInternal();
- if ((realm != null) && (realm instanceof Lifecycle)) {
+ if (realm instanceof Lifecycle) {
((Lifecycle) realm).stop();
}
Cluster cluster = getClusterInternal();
- if ((cluster != null) && (cluster instanceof Lifecycle)) {
+ if (cluster instanceof Lifecycle) {
((Lifecycle) cluster).stop();
}
}
@@ -1022,11 +1018,11 @@ public abstract class ContainerBase extends LifecycleMBeanBase
protected void destroyInternal() throws LifecycleException {
Realm realm = getRealmInternal();
- if ((realm != null) && (realm instanceof Lifecycle)) {
+ if (realm instanceof Lifecycle) {
((Lifecycle) realm).destroy();
}
Cluster cluster = getClusterInternal();
- if ((cluster != null) && (cluster instanceof Lifecycle)) {
+ if (cluster instanceof Lifecycle) {
((Lifecycle) cluster).destroy();
}
diff --git a/java/org/apache/catalina/core/DefaultInstanceManager.java b/java/org/apache/catalina/core/DefaultInstanceManager.java
index 47b3567..7b324d1 100644
--- a/java/org/apache/catalina/core/DefaultInstanceManager.java
+++ b/java/org/apache/catalina/core/DefaultInstanceManager.java
@@ -468,6 +468,8 @@ public class DefaultInstanceManager implements InstanceManager {
/**
* Makes cache size available to unit tests.
+ *
+ * @return the cache size
*/
protected int getAnnotationCacheSize() {
synchronized (annotationCache) {
diff --git a/java/org/apache/catalina/core/JreMemoryLeakPreventionListener.java b/java/org/apache/catalina/core/JreMemoryLeakPreventionListener.java
index 0affa7c..d016c31 100644
--- a/java/org/apache/catalina/core/JreMemoryLeakPreventionListener.java
+++ b/java/org/apache/catalina/core/JreMemoryLeakPreventionListener.java
@@ -297,16 +297,8 @@ public class JreMemoryLeakPreventionListener implements LifecycleListener {
log.debug(sm.getString(
"jreLeakListener.gcDaemonFail"), e);
}
- } catch (SecurityException e) {
- log.error(sm.getString("jreLeakListener.gcDaemonFail"),
- e);
- } catch (NoSuchMethodException e) {
- log.error(sm.getString("jreLeakListener.gcDaemonFail"),
- e);
- } catch (IllegalArgumentException e) {
- log.error(sm.getString("jreLeakListener.gcDaemonFail"),
- e);
- } catch (IllegalAccessException e) {
+ } catch (SecurityException | NoSuchMethodException | IllegalArgumentException |
+ IllegalAccessException e) {
log.error(sm.getString("jreLeakListener.gcDaemonFail"),
e);
} catch (InvocationTargetException e) {
diff --git a/java/org/apache/catalina/core/LocalStrings.properties b/java/org/apache/catalina/core/LocalStrings.properties
index d1758f9..4b84632 100644
--- a/java/org/apache/catalina/core/LocalStrings.properties
+++ b/java/org/apache/catalina/core/LocalStrings.properties
@@ -13,14 +13,14 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-applicationContext.addFilter.ise=Filters can not be added to context {0} as the context has been initialised
+applicationContext.addFilter.ise=Filters cannot be added to context {0} as the context has been initialised
applicationContext.addListener.iae.cnfe=Unable to create an instance of type [{0}]
applicationContext.addListener.iae.init=Unable to add an instance of type [{0}] as a listener
applicationContext.addListener.iae.wrongType=The type specified [{0}] is not one of the expected listener types
applicationContext.addListener.iae.sclNotAllowed=Once the first ServletContextListener has been called, no more ServletContextListeners may be added.
-applicationContext.addListener.ise=Listeners can not be added to context {0} as the context has been initialised
-applicationContext.addRole.ise=Roles can not be added to context {0} as the context has been initialised
-applicationContext.addServlet.ise=Servlets can not be added to context {0} as the context has been initialised
+applicationContext.addListener.ise=Listeners cannot be added to context {0} as the context has been initialised
+applicationContext.addRole.ise=Roles cannot be added to context {0} as the context has been initialised
+applicationContext.addServlet.ise=Servlets cannot be added to context {0} as the context has been initialised
applicationContext.attributeEvent=Exception thrown by attributes event listener
applicationContext.illegalDispatchPath=An application attempted to obtain a request dispatcher with an illegal path [{0}] that was rejected because it contained an encoded directory traversal attempt
applicationContext.invalidFilterName=Unable to add filter definition due to invalid filter name [{0}].
@@ -31,7 +31,7 @@ applicationContext.resourcePaths.iae=Path {0} does not start with a "/" characte
applicationContext.role.iae=An individual role to declare for context [{0}] may not be null nor the empty string
applicationContext.roles.iae=Array of roles to declare for context [{0}] cannot be null
applicationContext.setAttribute.namenull=Name cannot be null
-applicationContext.setInitParam.ise=Initialization parameters can not be set after the context has been initialized
+applicationContext.setInitParam.ise=Initialization parameters cannot be set after the context has been initialized
applicationContext.setSessionTracking.ise=The session tracking modes for context {0} cannot be set whilst the context is running
applicationContext.setSessionTracking.iae.invalid=The session tracking mode {0} requested for context {1} is not supported by that context
applicationContext.setSessionTracking.iae.ssl=The session tracking modes requested for context {0} included SSL and at least one other mode. SSL may not be configured with other modes.
@@ -50,9 +50,12 @@ applicationFilterConfig.preDestroy=Failed the call to preDestroy for the filter
applicationFilterConfig.release=Failed to destroy the filter named [{0}] of type [{1}]
applicationFilterRegistration.nullInitParam=Unable to set initialisation parameter for filter due to null name and/or value. Name [{0}], Value [{1}]
applicationFilterRegistration.nullInitParams=Unable to set initialisation parameters for filter due to null name and/or value. Name [{0}], Value [{1}]
+
+applicationPushBuilder.noCoyoteRequest=Unable to find the underlying Coyote request object (which is required to create a push request) from the request of type [{0}]
+
applicationServletRegistration.setServletSecurity.iae=Null constraint specified for servlet [{0}] deployed to context with name [{1}]
applicationServletRegistration.setServletSecurity.ise=Security constraints can't be added to servlet [{0}] deployed to context with name [{1}] as the context has already been initialised
-applicationSessionCookieConfig.ise=Property {0} can not be added to SessionCookieConfig for context {1} as the context has been initialised
+applicationSessionCookieConfig.ise=Property {0} cannot be added to SessionCookieConfig for context {1} as the context has been initialised
aprListener.aprInit=The APR based Apache Tomcat Native library which allows optimal performance in production environments was not found on the java.library.path: {0}
aprListener.aprInitDebug=The APR based Apache Tomcat Native library could not be found using names [{0}] on the java.library.path [{1}]. The errors reported were [{2}]
aprListener.aprInitError=The APR based Apache Tomcat Native library failed to load. The error reported was [{0}]
@@ -63,6 +66,7 @@ aprListener.sslInit=Failed to initialize the SSLEngine.
aprListener.tcnValid=Loaded APR based Apache Tomcat Native library {0} using APR version {1}.
aprListener.flags=APR capabilities: IPv6 [{0}], sendfile [{1}], accept filters [{2}], random [{3}].
aprListener.currentFIPSMode=Current FIPS mode: {0}
+aprListener.config=APR/OpenSSL configuration: useAprConnector [{0}], useOpenSSL [{1}]
aprListener.skipFIPSInitialization=Already in FIPS mode; skipping FIPS initialization.
aprListener.enterAlreadyInFIPSMode=AprLifecycleListener is configured to force entering FIPS mode, but library is already in FIPS mode ({0})
aprListener.requireNotInFIPSMode=AprLifecycleListener is configured to require the library to already be in FIPS mode, but it was not in FIPS mode
@@ -75,7 +79,6 @@ aprListener.tooLateForSSLRandomSeed=Cannot setSSLRandomSeed: SSL has already bee
aprListener.tooLateForFIPSMode=Cannot setFIPSMode: SSL has already been initialized
aprListener.initializedOpenSSL=OpenSSL successfully initialized ({0})
-asyncContextImpl.finishResponseError=Response did not finish cleanly after AsyncContext completed
asyncContextImpl.request.ise=It is illegal to call getRequest() after complete() or any of the dispatch() methods has been called
asyncContextImpl.requestEnded=The request associated with the AsyncContext has already completed processing.
asyncContextImpl.response.ise=It is illegal to call getResponse() after complete() or any of the dispatch() methods has been called
@@ -103,6 +106,9 @@ naming.invalidEnvEntryType=Environment entry {0} has an invalid type
naming.invalidEnvEntryValue=Environment entry {0} has an invalid value
naming.namingContextCreationFailed=Creation of the naming context failed: {0}
noPluggabilityServletContext.notAllowed=Section 4.4 of the Servlet 3.0 specification does not permit this method to be called from a ServletContextListener that was not defined in web.xml, a web-fragment.xml file nor annotated with @WebListener
+
+pushBuilder.noPath=It is illegal to call push() before setting a path
+
standardContext.invalidWrapperClass={0} is not a subclass of StandardWrapper
standardContext.applicationListener=Error configuring application listener of class {0}
standardContext.applicationSkipped=Skipped installing application listeners due to previous error(s)
@@ -189,6 +195,10 @@ standardService.connector.initFailed=Failed to initialize connector [{0}]
standardService.connector.pauseFailed=Failed to pause connector [{0}]
standardService.connector.startFailed=Failed to start connector [{0}]
standardService.connector.stopFailed=Failed to stop connector [{0}]
+standardService.engine.startFailed=Failed to start associated Engine
+standardService.engine.stopFailed=Failed to stop associated Engine
+standardService.mapperListener.startFailed=Failed to start associated MapperListener
+standardService.mapperListener.stopFailed=Failed to stop associated MapperListener
standardService.start.name=Starting service {0}
standardService.stop.name=Stopping service {0}
standardWrapper.allocate=Error allocating a servlet instance
diff --git a/java/org/apache/catalina/core/NamingContextListener.java b/java/org/apache/catalina/core/NamingContextListener.java
index f4c91f5..971c1c0 100644
--- a/java/org/apache/catalina/core/NamingContextListener.java
+++ b/java/org/apache/catalina/core/NamingContextListener.java
@@ -87,10 +87,6 @@ public class NamingContextListener
// ----------------------------------------------------- Instance Variables
-
- protected Log logger = log;
-
-
/**
* Name of the associated naming context.
*/
@@ -160,7 +156,7 @@ public class NamingContextListener
// ------------------------------------------------------------- Properties
/**
- * Returns whether or not an attempt to modify the JNDI context will trigger
+ * @return whether or not an attempt to modify the JNDI context will trigger
* an exception or if the request will be ignored.
*/
public boolean getExceptionOnFailedWrite() {
@@ -180,7 +176,7 @@ public class NamingContextListener
/**
- * Return the "name" property.
+ * @return the "name" property.
*/
public String getName() {
return (this.name);
@@ -198,7 +194,7 @@ public class NamingContextListener
/**
- * Return the env context.
+ * @return the naming environment context.
*/
public javax.naming.Context getEnvContext() {
return this.envCtx;
@@ -219,7 +215,6 @@ public class NamingContextListener
if (container instanceof Context) {
namingResources = ((Context) container).getNamingResources();
- logger = log;
token = ((Context) container).getNamingToken();
} else if (container instanceof Server) {
namingResources = ((Server) container).getGlobalNamingResources();
@@ -235,11 +230,7 @@ public class NamingContextListener
try {
Hashtable<String, Object> contextEnv = new Hashtable<>();
- try {
- namingContext = new NamingContext(contextEnv, getName());
- } catch (NamingException e) {
- // Never happens
- }
+ namingContext = new NamingContext(contextEnv, getName());
ContextAccessController.setSecurityToken(getName(), token);
ContextAccessController.setSecurityToken(container, token);
ContextBindings.bindContext(container, namingContext, token);
@@ -257,7 +248,7 @@ public class NamingContextListener
try {
createNamingContext();
} catch (NamingException e) {
- logger.error
+ log.error
(sm.getString("naming.namingContextCreationFailed", e));
}
@@ -271,7 +262,7 @@ public class NamingContextListener
ContextBindings.bindClassLoader(container, token,
((Context) container).getLoader().getClassLoader());
} catch (NamingException e) {
- logger.error(sm.getString("naming.bindFailed", e));
+ log.error(sm.getString("naming.bindFailed", e));
}
}
@@ -282,7 +273,7 @@ public class NamingContextListener
ContextBindings.bindClassLoader(container, token,
this.getClass().getClassLoader());
} catch (NamingException e) {
- logger.error(sm.getString("naming.bindFailed", e));
+ log.error(sm.getString("naming.bindFailed", e));
}
if (container instanceof StandardServer) {
((StandardServer) container).setGlobalNamingContext
@@ -714,7 +705,7 @@ public class NamingContextListener
// Ignore because UserTransaction was obviously
// added via ResourceLink
} catch (NamingException e) {
- logger.error(sm.getString("naming.bindFailed", e));
+ log.error(sm.getString("naming.bindFailed", e));
}
}
@@ -724,7 +715,7 @@ public class NamingContextListener
compCtx.bind("Resources",
((Context) container).getResources());
} catch (NamingException e) {
- logger.error(sm.getString("naming.bindFailed", e));
+ log.error(sm.getString("naming.bindFailed", e));
}
}
@@ -777,6 +768,8 @@ public class NamingContextListener
/**
* Set the specified EJBs in the naming context.
+ *
+ * @param ejb the EJB descriptor
*/
public void addEjb(ContextEjb ejb) {
@@ -795,7 +788,7 @@ public class NamingContextListener
createSubcontexts(envCtx, ejb.getName());
envCtx.bind(ejb.getName(), ref);
} catch (NamingException e) {
- logger.error(sm.getString("naming.bindFailed", e));
+ log.error(sm.getString("naming.bindFailed", e));
}
}
@@ -803,6 +796,8 @@ public class NamingContextListener
/**
* Set the specified environment entries in the naming context.
+ *
+ * @param env the environment entry
*/
public void addEnvironment(ContextEnvironment env) {
@@ -864,25 +859,25 @@ public class NamingContextListener
} else {
value = constructEnvEntry(env.getType(), env.getValue());
if (value == null) {
- logger.error(sm.getString(
+ log.error(sm.getString(
"naming.invalidEnvEntryType", env.getName()));
}
}
} catch (NumberFormatException e) {
- logger.error(sm.getString("naming.invalidEnvEntryValue", env.getName()));
+ log.error(sm.getString("naming.invalidEnvEntryValue", env.getName()));
} catch (IllegalArgumentException e) {
- logger.error(sm.getString("naming.invalidEnvEntryValue", env.getName()));
+ log.error(sm.getString("naming.invalidEnvEntryValue", env.getName()));
}
// Binding the object to the appropriate name
if (value != null) {
try {
- if (logger.isDebugEnabled())
- logger.debug(" Adding environment entry " + env.getName());
+ if (log.isDebugEnabled())
+ log.debug(" Adding environment entry " + env.getName());
createSubcontexts(envCtx, env.getName());
envCtx.bind(env.getName(), value);
} catch (NamingException e) {
- logger.error(sm.getString("naming.invalidEnvEntryValue", e));
+ log.error(sm.getString("naming.invalidEnvEntryValue", e));
}
}
@@ -918,15 +913,18 @@ public class NamingContextListener
/**
* Set the specified local EJBs in the naming context.
+ *
+ * @param localEjb the EJB descriptor (unused)
*/
- public void addLocalEjb(
- @SuppressWarnings("unused") ContextLocalEjb localEjb) {
+ public void addLocalEjb(ContextLocalEjb localEjb) {
// NO-OP
}
/**
* Set the specified web service in the naming context.
+ *
+ * @param service the web service descriptor
*/
public void addService(ContextService service) {
@@ -952,10 +950,10 @@ public class NamingContextListener
wsdlURL = ((Context) container).
getServletContext().
getResource("/" + service.getWsdlfile());
- logger.debug(" Changing service ref wsdl file for /"
+ log.debug(" Changing service ref wsdl file for /"
+ service.getWsdlfile());
} catch (MalformedURLException e) {
- logger.error(sm.getString("naming.wsdlFailed", e));
+ log.error(sm.getString("naming.wsdlFailed", e));
}
}
if (wsdlURL == null)
@@ -986,10 +984,10 @@ public class NamingContextListener
jaxrpcURL = ((Context) container).
getServletContext().
getResource("/" + service.getJaxrpcmappingfile());
- logger.debug(" Changing service ref jaxrpc file for /"
+ log.debug(" Changing service ref jaxrpc file for /"
+ service.getJaxrpcmappingfile());
} catch (MalformedURLException e) {
- logger.error(sm.getString("naming.wsdlFailed", e));
+ log.error(sm.getString("naming.wsdlFailed", e));
}
}
if (jaxrpcURL == null)
@@ -1042,14 +1040,14 @@ public class NamingContextListener
}
try {
- if (logger.isDebugEnabled()) {
- logger.debug(" Adding service ref "
+ if (log.isDebugEnabled()) {
+ log.debug(" Adding service ref "
+ service.getName() + " " + ref);
}
createSubcontexts(envCtx, service.getName());
envCtx.bind(service.getName(), ref);
} catch (NamingException e) {
- logger.error(sm.getString("naming.bindFailed", e));
+ log.error(sm.getString("naming.bindFailed", e));
}
}
@@ -1057,6 +1055,8 @@ public class NamingContextListener
/**
* Set the specified resources in the naming context.
+ *
+ * @param resource the resource descriptor
*/
public void addResource(ContextResource resource) {
@@ -1074,14 +1074,14 @@ public class NamingContextListener
ref.add(refAddr);
}
try {
- if (logger.isDebugEnabled()) {
- logger.debug(" Adding resource ref "
+ if (log.isDebugEnabled()) {
+ log.debug(" Adding resource ref "
+ resource.getName() + " " + ref);
}
createSubcontexts(envCtx, resource.getName());
envCtx.bind(resource.getName(), ref);
} catch (NamingException e) {
- logger.error(sm.getString("naming.bindFailed", e));
+ log.error(sm.getString("naming.bindFailed", e));
}
if ("javax.sql.DataSource".equals(ref.getClassName()) &&
@@ -1092,7 +1092,7 @@ public class NamingContextListener
Registry.getRegistry(null, null).registerComponent(actualResource, on, null);
objectNames.put(resource.getName(), on);
} catch (Exception e) {
- logger.warn(sm.getString("naming.jmxRegistrationFailed", e));
+ log.warn(sm.getString("naming.jmxRegistrationFailed", e));
}
}
@@ -1101,6 +1101,8 @@ public class NamingContextListener
/**
* Set the specified resources in the naming context.
+ *
+ * @param resourceEnvRef the resource reference
*/
public void addResourceEnvRef(ContextResourceEnvRef resourceEnvRef) {
@@ -1115,12 +1117,12 @@ public class NamingContextListener
ref.add(refAddr);
}
try {
- if (logger.isDebugEnabled())
+ if (log.isDebugEnabled())
log.debug(" Adding resource env ref " + resourceEnvRef.getName());
createSubcontexts(envCtx, resourceEnvRef.getName());
envCtx.bind(resourceEnvRef.getName(), ref);
} catch (NamingException e) {
- logger.error(sm.getString("naming.bindFailed", e));
+ log.error(sm.getString("naming.bindFailed", e));
}
}
@@ -1128,6 +1130,8 @@ public class NamingContextListener
/**
* Set the specified resource link in the naming context.
+ *
+ * @param resourceLink the resource link
*/
public void addResourceLink(ContextResourceLink resourceLink) {
@@ -1147,12 +1151,12 @@ public class NamingContextListener
"UserTransaction".equals(resourceLink.getName())
? compCtx : envCtx;
try {
- if (logger.isDebugEnabled())
+ if (log.isDebugEnabled())
log.debug(" Adding resource link " + resourceLink.getName());
createSubcontexts(envCtx, resourceLink.getName());
ctx.bind(resourceLink.getName(), ref);
} catch (NamingException e) {
- logger.error(sm.getString("naming.bindFailed", e));
+ log.error(sm.getString("naming.bindFailed", e));
}
ResourceLinkFactory.registerGlobalResourceAccess(
@@ -1171,13 +1175,15 @@ public class NamingContextListener
/**
* Set the specified EJBs in the naming context.
+ *
+ * @param name the name of the EJB which should be removed
*/
public void removeEjb(String name) {
try {
envCtx.unbind(name);
} catch (NamingException e) {
- logger.error(sm.getString("naming.unbindFailed", e));
+ log.error(sm.getString("naming.unbindFailed", e));
}
}
@@ -1185,13 +1191,15 @@ public class NamingContextListener
/**
* Set the specified environment entries in the naming context.
+ *
+ * @param name the name of the environment entry which should be removed
*/
public void removeEnvironment(String name) {
try {
envCtx.unbind(name);
} catch (NamingException e) {
- logger.error(sm.getString("naming.unbindFailed", e));
+ log.error(sm.getString("naming.unbindFailed", e));
}
}
@@ -1199,13 +1207,15 @@ public class NamingContextListener
/**
* Set the specified local EJBs in the naming context.
+ *
+ * @param name the name of the EJB which should be removed
*/
public void removeLocalEjb(String name) {
try {
envCtx.unbind(name);
} catch (NamingException e) {
- logger.error(sm.getString("naming.unbindFailed", e));
+ log.error(sm.getString("naming.unbindFailed", e));
}
}
@@ -1213,13 +1223,15 @@ public class NamingContextListener
/**
* Set the specified web services in the naming context.
+ *
+ * @param name the name of the web service which should be removed
*/
public void removeService(String name) {
try {
envCtx.unbind(name);
} catch (NamingException e) {
- logger.error(sm.getString("naming.unbindFailed", e));
+ log.error(sm.getString("naming.unbindFailed", e));
}
}
@@ -1227,13 +1239,15 @@ public class NamingContextListener
/**
* Set the specified resources in the naming context.
+ *
+ * @param name the name of the resource which should be removed
*/
public void removeResource(String name) {
try {
envCtx.unbind(name);
} catch (NamingException e) {
- logger.error(sm.getString("naming.unbindFailed", e));
+ log.error(sm.getString("naming.unbindFailed", e));
}
ObjectName on = objectNames.get(name);
@@ -1246,13 +1260,15 @@ public class NamingContextListener
/**
* Set the specified resources in the naming context.
+ *
+ * @param name the name of the resource reference which should be removed
*/
public void removeResourceEnvRef(String name) {
try {
envCtx.unbind(name);
} catch (NamingException e) {
- logger.error(sm.getString("naming.unbindFailed", e));
+ log.error(sm.getString("naming.unbindFailed", e));
}
}
@@ -1260,13 +1276,15 @@ public class NamingContextListener
/**
* Set the specified resources in the naming context.
+ *
+ * @param name the name of the resource link which should be removed
*/
public void removeResourceLink(String name) {
try {
envCtx.unbind(name);
} catch (NamingException e) {
- logger.error(sm.getString("naming.unbindFailed", e));
+ log.error(sm.getString("naming.unbindFailed", e));
}
ResourceLinkFactory.deregisterGlobalResourceAccess(getGlobalNamingContext(), name);
diff --git a/java/org/apache/catalina/core/StandardContext.java b/java/org/apache/catalina/core/StandardContext.java
index 70b8da4..b8ec2ad 100644
--- a/java/org/apache/catalina/core/StandardContext.java
+++ b/java/org/apache/catalina/core/StandardContext.java
@@ -16,11 +16,9 @@
*/
package org.apache.catalina.core;
-import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
-import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.AccessController;
@@ -88,7 +86,6 @@ import org.apache.catalina.ContainerListener;
import org.apache.catalina.Context;
import org.apache.catalina.CredentialHandler;
import org.apache.catalina.Globals;
-import org.apache.catalina.InstanceListener;
import org.apache.catalina.Lifecycle;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.LifecycleListener;
@@ -114,6 +111,7 @@ import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.naming.ContextBindings;
import org.apache.tomcat.InstanceManager;
+import org.apache.tomcat.InstanceManagerBindings;
import org.apache.tomcat.JarScanner;
import org.apache.tomcat.util.ExceptionUtils;
import org.apache.tomcat.util.IntrospectionUtils;
@@ -131,7 +129,7 @@ import org.apache.tomcat.util.descriptor.web.MessageDestinationRef;
import org.apache.tomcat.util.descriptor.web.SecurityCollection;
import org.apache.tomcat.util.descriptor.web.SecurityConstraint;
import org.apache.tomcat.util.http.CookieProcessor;
-import org.apache.tomcat.util.http.LegacyCookieProcessor;
+import org.apache.tomcat.util.http.Rfc6265CookieProcessor;
import org.apache.tomcat.util.scan.StandardJarScanner;
import org.apache.tomcat.util.security.PrivilegedGetTccl;
import org.apache.tomcat.util.security.PrivilegedSetTccl;
@@ -144,7 +142,6 @@ import org.apache.tomcat.util.security.PrivilegedSetTccl;
* @author Craig R. McClanahan
* @author Remy Maucherat
*/
- at SuppressWarnings("deprecation")
public class StandardContext extends ContainerBase
implements Context, NotificationEmitter {
@@ -406,15 +403,6 @@ public class StandardContext extends ContainerBase
/**
- * The set of classnames of InstanceListeners that will be added
- * to each newly created Wrapper by <code>createWrapper()</code>.
- */
- private String instanceListeners[] = new String[0];
-
- private final Object instanceListenersLock = new Object();
-
-
- /**
* The Loader implementation with which this Container is associated.
*/
private Loader loader = null;
@@ -726,20 +714,6 @@ public class StandardContext extends ContainerBase
private boolean clearReferencesRmiTargets = true;
/**
- * Should Tomcat attempt to null out any static or final fields from loaded
- * classes when a web application is stopped as a work around for apparent
- * garbage collection bugs and application coding errors? There have been
- * some issues reported with log4j when this option is true. Applications
- * without memory leaks using recent JVMs should operate correctly with this
- * option set to <code>false</code>. If not specified, the default value of
- * <code>false</code> will be used.
- *
- * @deprecated This option will be removed in Tomcat 8.5
- */
- @Deprecated
- private boolean clearReferencesStatic = false;
-
- /**
* Should Tomcat attempt to terminate threads that have been started by the
* web application? Stopping threads is performed via the deprecated (for
* good reason) <code>Thread.stop()</code> method and is likely to result in
@@ -1097,18 +1071,15 @@ public class StandardContext extends ContainerBase
@Override
public Authenticator getAuthenticator() {
- if (this instanceof Authenticator)
- return (Authenticator) this;
-
Pipeline pipeline = getPipeline();
if (pipeline != null) {
Valve basic = pipeline.getBasic();
- if ((basic != null) && (basic instanceof Authenticator))
+ if (basic instanceof Authenticator)
return (Authenticator) basic;
- Valve valves[] = pipeline.getValves();
- for (int i = 0; i < valves.length; i++) {
- if (valves[i] instanceof Authenticator)
- return (Authenticator) valves[i];
+ for (Valve valve : pipeline.getValves()) {
+ if (valve instanceof Authenticator) {
+ return (Authenticator) valve;
+ }
}
}
return null;
@@ -1216,6 +1187,8 @@ public class StandardContext extends ContainerBase
/**
* Return the "follow standard delegation model" flag used to configure
* our ClassLoader.
+ *
+ * @return <code>true</code> if classloading delegates to the parent classloader first
*/
public boolean getDelegate() {
@@ -1241,7 +1214,7 @@ public class StandardContext extends ContainerBase
/**
- * Returns true if the internal naming support is used.
+ * @return true if the internal naming support is used.
*/
public boolean isUseNaming() {
@@ -1252,6 +1225,8 @@ public class StandardContext extends ContainerBase
/**
* Enables or disables naming.
+ *
+ * @param useNaming <code>true</code> to enable the naming environment
*/
public void setUseNaming(boolean useNaming) {
this.useNaming = useNaming;
@@ -1313,6 +1288,8 @@ public class StandardContext extends ContainerBase
/**
* Add a listener to the end of the list of initialized application
* lifecycle listeners.
+ *
+ * @param listener The listener to add
*/
public void addApplicationLifecycleListener(Object listener) {
int len = applicationLifecycleListenersObjects.length;
@@ -1324,7 +1301,7 @@ public class StandardContext extends ContainerBase
/**
- * Return the antiResourceLocking flag for this Context.
+ * @return the antiResourceLocking flag for this Context.
*/
public boolean getAntiResourceLocking() {
@@ -1350,7 +1327,7 @@ public class StandardContext extends ContainerBase
/**
- * Return the Locale to character set mapper for this Context.
+ * @return the Locale to character set mapper for this Context.
*/
public CharsetMapper getCharsetMapper() {
@@ -1666,7 +1643,7 @@ public class StandardContext extends ContainerBase
/**
- * Return the display name of this web application.
+ * @return the display name of this web application.
*/
@Override
public String getDisplayName() {
@@ -1677,7 +1654,7 @@ public class StandardContext extends ContainerBase
/**
- * Return the alternate Deployment Descriptor name.
+ * @return the alternate Deployment Descriptor name.
*/
@Override
public String getAltDDName(){
@@ -1687,6 +1664,8 @@ public class StandardContext extends ContainerBase
/**
* Set an alternate Deployment Descriptor name.
+ *
+ * @param altDDName The new name
*/
@Override
public void setAltDDName(String altDDName) {
@@ -1713,7 +1692,7 @@ public class StandardContext extends ContainerBase
/**
- * Return the distributable flag for this web application.
+ * @return the distributable flag for this web application.
*/
@Override
public boolean getDistributable() {
@@ -1738,7 +1717,7 @@ public class StandardContext extends ContainerBase
/**
- * Return the document root for this Context. This can be an absolute
+ * @return the document root for this Context. This can be an absolute
* pathname, a relative pathname, or a URL.
*/
@Override
@@ -1889,7 +1868,7 @@ public class StandardContext extends ContainerBase
/**
- * Return the boolean on the annotations parsing.
+ * @return the boolean on the annotations parsing.
*/
@Override
public boolean getIgnoreAnnotations() {
@@ -1913,7 +1892,7 @@ public class StandardContext extends ContainerBase
/**
- * Return the login configuration descriptor for this web application.
+ * @return the login configuration descriptor for this web application.
*/
@Override
public LoginConfig getLoginConfig() {
@@ -1972,7 +1951,7 @@ public class StandardContext extends ContainerBase
/**
- * Return the naming resources associated with this web application.
+ * @return the naming resources associated with this web application.
*/
@Override
public NamingResourcesImpl getNamingResources() {
@@ -2038,7 +2017,7 @@ public class StandardContext extends ContainerBase
/**
- * Return the context path for this Context.
+ * @return the context path for this Context.
*/
@Override
public String getPath() {
@@ -2079,7 +2058,7 @@ public class StandardContext extends ContainerBase
/**
- * Return the public identifier of the deployment descriptor DTD that is
+ * @return the public identifier of the deployment descriptor DTD that is
* currently being parsed.
*/
@Override
@@ -2111,7 +2090,7 @@ public class StandardContext extends ContainerBase
/**
- * Return the reloadable flag for this web application.
+ * @return the reloadable flag for this web application.
*/
@Override
public boolean getReloadable() {
@@ -2122,7 +2101,7 @@ public class StandardContext extends ContainerBase
/**
- * Return the default context override flag for this web application.
+ * @return the default context override flag for this web application.
*/
@Override
public boolean getOverride() {
@@ -2133,7 +2112,7 @@ public class StandardContext extends ContainerBase
/**
- * Return the original document root for this Context. This can be an absolute
+ * @return the original document root for this Context. This can be an absolute
* pathname, a relative pathname, or a URL.
* Is only set as deployment has change docRoot!
*/
@@ -2156,7 +2135,7 @@ public class StandardContext extends ContainerBase
/**
- * Return the parent class loader (if any) for this web application.
+ * @return the parent class loader (if any) for this web application.
* This call is meaningful only <strong>after</strong> a Loader has
* been configured.
*/
@@ -2174,7 +2153,7 @@ public class StandardContext extends ContainerBase
/**
- * Return the privileged flag for this web application.
+ * @return the privileged flag for this web application.
*/
@Override
public boolean getPrivileged() {
@@ -2252,7 +2231,7 @@ public class StandardContext extends ContainerBase
/**
- * Return the servlet context for which this Context is a facade.
+ * @return the servlet context for which this Context is a facade.
*/
@Override
public ServletContext getServletContext() {
@@ -2268,7 +2247,7 @@ public class StandardContext extends ContainerBase
/**
- * Return the default session timeout (in minutes) for this
+ * @return the default session timeout (in minutes) for this
* web application.
*/
@Override
@@ -2303,7 +2282,7 @@ public class StandardContext extends ContainerBase
/**
- * Return the value of the swallowOutput flag.
+ * @return the value of the swallowOutput flag.
*/
@Override
public boolean getSwallowOutput() {
@@ -2333,7 +2312,7 @@ public class StandardContext extends ContainerBase
/**
- * Return the value of the unloadDelay flag.
+ * @return the value of the unloadDelay flag.
*/
public long getUnloadDelay() {
@@ -2362,7 +2341,7 @@ public class StandardContext extends ContainerBase
/**
- * Unpack WAR flag accessor.
+ * @return unpack WAR flag.
*/
public boolean getUnpackWAR() {
@@ -2373,6 +2352,8 @@ public class StandardContext extends ContainerBase
/**
* Unpack WAR flag mutator.
+ *
+ * @param unpackWAR <code>true</code> to unpack WARs on deployment
*/
public void setUnpackWAR(boolean unpackWAR) {
@@ -2381,18 +2362,31 @@ public class StandardContext extends ContainerBase
}
+ /**
+ * Flag which indicates if bundled context.xml files should be copied to the
+ * config folder. The doesn't occur by default.
+ *
+ * @return <code>true</code> if the <code>META-INF/context.xml</code> file included
+ * in a WAR will be copied to the host configuration base folder on deployment
+ */
public boolean getCopyXML() {
return copyXML;
}
+ /**
+ * Allows copying a bundled context.xml file to the host configuration base
+ * folder on deployment.
+ *
+ * @param copyXML the new flag value
+ */
public void setCopyXML(boolean copyXML) {
this.copyXML = copyXML;
}
/**
- * Return the Java class name of the Wrapper implementation used
+ * @return the Java class name of the Wrapper implementation used
* for servlets registered in this Context.
*/
@Override
@@ -2498,7 +2492,7 @@ public class StandardContext extends ContainerBase
// ------------------------------------------------------ Public Properties
/**
- * Returns whether or not an attempt to modify the JNDI context will trigger
+ * @return whether or not an attempt to modify the JNDI context will trigger
* an exception or if the request will be ignored.
*/
public boolean getJndiExceptionOnFailedWrite() {
@@ -2510,7 +2504,7 @@ public class StandardContext extends ContainerBase
* Controls whether or not an attempt to modify the JNDI context will
* trigger an exception or if the request will be ignored.
*
- * @param jndiExceptionOnFailedWrite
+ * @param jndiExceptionOnFailedWrite <code>false</code> to avoid an exception
*/
public void setJndiExceptionOnFailedWrite(
boolean jndiExceptionOnFailedWrite) {
@@ -2519,7 +2513,7 @@ public class StandardContext extends ContainerBase
/**
- * Return the Locale to character set mapper class for this Context.
+ * @return the Locale to character set mapper class for this Context.
*/
public String getCharsetMapperClass() {
@@ -2567,7 +2561,7 @@ public class StandardContext extends ContainerBase
}
/**
- * Return the work directory for this Context.
+ * @return the work directory for this Context.
*/
public String getWorkDir() {
@@ -2605,39 +2599,7 @@ public class StandardContext extends ContainerBase
/**
- * Return the clearReferencesStatic flag for this Context.
- *
- * @deprecated This option will be removed in Tomcat 8.5
- */
- @Deprecated
- public boolean getClearReferencesStatic() {
-
- return (this.clearReferencesStatic);
-
- }
-
-
- /**
- * Set the clearReferencesStatic feature for this Context.
- *
- * @param clearReferencesStatic The new flag value
- *
- * @deprecated This option will be removed in Tomcat 8.5
- */
- @Deprecated
- public void setClearReferencesStatic(boolean clearReferencesStatic) {
-
- boolean oldClearReferencesStatic = this.clearReferencesStatic;
- this.clearReferencesStatic = clearReferencesStatic;
- support.firePropertyChange("clearReferencesStatic",
- oldClearReferencesStatic,
- this.clearReferencesStatic);
-
- }
-
-
- /**
- * Return the clearReferencesStopThreads flag for this Context.
+ * @return the clearReferencesStopThreads flag for this Context.
*/
public boolean getClearReferencesStopThreads() {
@@ -2664,7 +2626,7 @@ public class StandardContext extends ContainerBase
/**
- * Return the clearReferencesStopTimerThreads flag for this Context.
+ * @return the clearReferencesStopTimerThreads flag for this Context.
*/
public boolean getClearReferencesStopTimerThreads() {
return (this.clearReferencesStopTimerThreads);
@@ -2689,7 +2651,7 @@ public class StandardContext extends ContainerBase
/**
- * Return the clearReferencesHttpClientKeepAliveThread flag for this
+ * @return the clearReferencesHttpClientKeepAliveThread flag for this
* Context.
*/
public boolean getClearReferencesHttpClientKeepAliveThread() {
@@ -2849,6 +2811,8 @@ public class StandardContext extends ContainerBase
/**
* Add a security constraint to the set for this web application.
+ *
+ * @param constraint the new security constraint
*/
@Override
public void addConstraint(SecurityConstraint constraint) {
@@ -2984,6 +2948,8 @@ public class StandardContext extends ContainerBase
/**
* Validate the supplied FilterMap.
+ *
+ * @param filterMap the filter mapping
*/
private void validateFilterMap(FilterMap filterMap) {
// Validate the proposed filter mapping
@@ -3014,28 +2980,6 @@ public class StandardContext extends ContainerBase
}
}
- /**
- * Add the classname of an InstanceListener to be added to each
- * Wrapper appended to this Context.
- *
- * @param listener Java class name of an InstanceListener class
- *
- * @deprecated Will be removed in 8.5.x onwards
- */
- @Deprecated
- @Override
- public void addInstanceListener(String listener) {
-
- synchronized (instanceListenersLock) {
- String results[] =new String[instanceListeners.length + 1];
- for (int i = 0; i < instanceListeners.length; i++)
- results[i] = instanceListeners[i];
- results[instanceListeners.length] = listener;
- instanceListeners = results;
- }
- fireContainerEvent("addInstanceListener", listener);
-
- }
/**
* Add a Locale Encoding Mapping (see Sec 5.4 of Servlet spec 2.4)
@@ -3166,7 +3110,7 @@ public class StandardContext extends ContainerBase
@Override
@Deprecated
public void addServletMapping(String pattern, String name) {
- addServletMapping(pattern, name, false);
+ addServletMappingDecoded(UDecoder.URLDecode(pattern, "UTF-8"), name);
}
@@ -3322,21 +3266,6 @@ public class StandardContext extends ContainerBase
wrapper = new StandardWrapper();
}
- synchronized (instanceListenersLock) {
- for (int i = 0; i < instanceListeners.length; i++) {
- try {
- Class<?> clazz = Class.forName(instanceListeners[i]);
- InstanceListener listener =
- (InstanceListener) clazz.newInstance();
- wrapper.addInstanceListener(listener);
- } catch (Throwable t) {
- ExceptionUtils.handleThrowable(t);
- log.error("createWrapper", t);
- return (null);
- }
- }
- }
-
synchronized (wrapperLifecyclesLock) {
for (int i = 0; i < wrapperLifecycles.length; i++) {
try {
@@ -3478,7 +3407,7 @@ public class StandardContext extends ContainerBase
/**
- * Return the set of defined filters for this Context.
+ * @return the set of defined filters for this Context.
*/
@Override
public FilterDef[] findFilterDefs() {
@@ -3492,7 +3421,7 @@ public class StandardContext extends ContainerBase
/**
- * Return the set of filter mappings for this Context.
+ * @return the set of filter mappings for this Context.
*/
@Override
public FilterMap[] findFilterMaps() {
@@ -3501,24 +3430,7 @@ public class StandardContext extends ContainerBase
/**
- * Return the set of InstanceListener classes that will be added to
- * newly created Wrappers automatically.
- *
- * @deprecated Will be removed in 8.5.x onwards
- */
- @Deprecated
- @Override
- public String[] findInstanceListeners() {
-
- synchronized (instanceListenersLock) {
- return (instanceListeners);
- }
-
- }
-
-
- /**
- * Return the message destination with the specified name, if any;
+ * @return the message destination with the specified name, if any;
* otherwise, return <code>null</code>.
*
* @param name Name of the desired message destination
@@ -3533,7 +3445,7 @@ public class StandardContext extends ContainerBase
/**
- * Return the set of defined message destinations for this web
+ * @return the set of defined message destinations for this web
* application. If none have been defined, a zero-length array
* is returned.
*/
@@ -3549,7 +3461,7 @@ public class StandardContext extends ContainerBase
/**
- * Return the message destination ref with the specified name, if any;
+ * @return the message destination ref with the specified name, if any;
* otherwise, return <code>null</code>.
*
* @param name Name of the desired message destination ref
@@ -3562,7 +3474,7 @@ public class StandardContext extends ContainerBase
/**
- * Return the set of defined message destination refs for this web
+ * @return the set of defined message destination refs for this web
* application. If none have been defined, a zero-length array
* is returned.
*/
@@ -3574,7 +3486,7 @@ public class StandardContext extends ContainerBase
/**
- * Return the MIME type to which the specified extension is mapped,
+ * @return the MIME type to which the specified extension is mapped,
* if any; otherwise return <code>null</code>.
*
* @param extension Extension to map to a MIME type
@@ -3588,7 +3500,7 @@ public class StandardContext extends ContainerBase
/**
- * Return the extensions for which MIME mappings are defined. If there
+ * @return the extensions for which MIME mappings are defined. If there
* are none, a zero-length array is returned.
*/
@Override
@@ -3604,7 +3516,7 @@ public class StandardContext extends ContainerBase
/**
- * Return the value for the specified context initialization
+ * @return the value for the specified context initialization
* parameter name, if any; otherwise return <code>null</code>.
*
* @param name Name of the parameter to return
@@ -3616,7 +3528,7 @@ public class StandardContext extends ContainerBase
/**
- * Return the names of all defined context initialization parameters
+ * @return the names of all defined context initialization parameters
* for this Context. If no parameters are defined, a zero-length
* array is returned.
*/
@@ -3634,6 +3546,7 @@ public class StandardContext extends ContainerBase
* is one. Otherwise, return the specified role unchanged.
*
* @param role Security role to map
+ * @return the role name
*/
@Override
public String findRoleMapping(String role) {
@@ -3651,7 +3564,7 @@ public class StandardContext extends ContainerBase
/**
- * Return <code>true</code> if the specified security role is defined
+ * @return <code>true</code> if the specified security role is defined
* for this application; otherwise return <code>false</code>.
*
* @param role Security role to verify
@@ -3662,16 +3575,16 @@ public class StandardContext extends ContainerBase
synchronized (securityRolesLock) {
for (int i = 0; i < securityRoles.length; i++) {
if (role.equals(securityRoles[i]))
- return (true);
+ return true;
}
}
- return (false);
+ return false;
}
/**
- * Return the security roles defined for this application. If none
+ * @return the security roles defined for this application. If none
* have been defined, a zero-length array is returned.
*/
@Override
@@ -3685,7 +3598,7 @@ public class StandardContext extends ContainerBase
/**
- * Return the servlet name mapped by the specified pattern (if any);
+ * @return the servlet name mapped by the specified pattern (if any);
* otherwise return <code>null</code>.
*
* @param pattern Pattern for which a mapping is requested
@@ -3701,7 +3614,7 @@ public class StandardContext extends ContainerBase
/**
- * Return the patterns of all defined servlet mappings for this
+ * @return the patterns of all defined servlet mappings for this
* Context. If no mappings are defined, a zero-length array is returned.
*/
@Override
@@ -3717,7 +3630,7 @@ public class StandardContext extends ContainerBase
/**
- * Return the context-relative URI of the error page for the specified
+ * @return the context-relative URI of the error page for the specified
* HTTP status code, if any; otherwise return <code>null</code>.
*
* @param status HTTP status code to look up
@@ -3735,7 +3648,7 @@ public class StandardContext extends ContainerBase
/**
- * Return the set of HTTP status codes for which error pages have
+ * @return the set of HTTP status codes for which error pages have
* been specified. If none are specified, a zero-length array
* is returned.
*/
@@ -3755,7 +3668,7 @@ public class StandardContext extends ContainerBase
/**
- * Return <code>true</code> if the specified welcome file is defined
+ * @return <code>true</code> if the specified welcome file is defined
* for this Context; otherwise return <code>false</code>.
*
* @param name Welcome file to verify
@@ -3766,16 +3679,16 @@ public class StandardContext extends ContainerBase
synchronized (welcomeFilesLock) {
for (int i = 0; i < welcomeFiles.length; i++) {
if (name.equals(welcomeFiles[i]))
- return (true);
+ return true;
}
}
- return (false);
+ return false;
}
/**
- * Return the set of watched resources for this Context. If none are
+ * @return the set of watched resources for this Context. If none are
* defined, a zero length array will be returned.
*/
@Override
@@ -3787,7 +3700,7 @@ public class StandardContext extends ContainerBase
/**
- * Return the set of welcome files defined for this Context. If none are
+ * @return the set of welcome files defined for this Context. If none are
* defined, a zero-length array is returned.
*/
@Override
@@ -3801,7 +3714,7 @@ public class StandardContext extends ContainerBase
/**
- * Return the set of LifecycleListener classes that will be added to
+ * @return the set of LifecycleListener classes that will be added to
* newly created Wrappers automatically.
*/
@Override
@@ -3815,7 +3728,7 @@ public class StandardContext extends ContainerBase
/**
- * Return the set of ContainerListener classes that will be added to
+ * @return the set of ContainerListener classes that will be added to
* newly created Wrappers automatically.
*/
@Override
@@ -4077,48 +3990,6 @@ public class StandardContext extends ContainerBase
/**
- * Remove a class name from the set of InstanceListener classes that
- * will be added to newly created Wrappers.
- *
- * @param listener Class name of an InstanceListener class to be removed
- *
- * @deprecated Will be removed in 8.5.x onwards
- */
- @Deprecated
- @Override
- public void removeInstanceListener(String listener) {
-
- synchronized (instanceListenersLock) {
-
- // Make sure this listener is currently present
- int n = -1;
- for (int i = 0; i < instanceListeners.length; i++) {
- if (instanceListeners[i].equals(listener)) {
- n = i;
- break;
- }
- }
- if (n < 0)
- return;
-
- // Remove the specified listener
- int j = 0;
- String results[] = new String[instanceListeners.length - 1];
- for (int i = 0; i < instanceListeners.length; i++) {
- if (i != n)
- results[j++] = instanceListeners[i];
- }
- instanceListeners = results;
-
- }
-
- // Inform interested listeners
- fireContainerEvent("removeInstanceListener", listener);
-
- }
-
-
- /**
* Remove any message destination with the specified name.
*
* @param name Name of the message destination to remove
@@ -4554,8 +4425,9 @@ public class StandardContext extends ContainerBase
}
/**
- * hook to register that we need to scan for security annotations.
+ * Hook to register that we need to scan for security annotations.
* @param wrapper The wrapper for the Servlet that was added
+ * @return the associated registration
*/
public ServletRegistration.Dynamic dynamicServletAdded(Wrapper wrapper) {
Servlet s = wrapper.getServlet();
@@ -4567,8 +4439,8 @@ public class StandardContext extends ContainerBase
}
/**
- * hook to track which registrations need annotation scanning
- * @param servlet
+ * Hook to track which registrations need annotation scanning
+ * @param servlet the Servlet to add
*/
public void dynamicServletCreated(Servlet servlet) {
createdServlets.add(servlet);
@@ -4601,7 +4473,7 @@ public class StandardContext extends ContainerBase
private int insertPoint = 0;
/**
- * Return the set of filter mappings.
+ * @return The set of filter mappings
*/
public FilterMap[] asArray() {
synchronized (lock) {
@@ -4679,7 +4551,7 @@ public class StandardContext extends ContainerBase
/**
* Configure and initialize the set of filters for this Context.
- * Return <code>true</code> if all filter initialization completed
+ * @return <code>true</code> if all filter initialization completed
* successfully, or <code>false</code> otherwise.
*/
public boolean filterStart() {
@@ -4716,7 +4588,7 @@ public class StandardContext extends ContainerBase
/**
* Finalize and release the set of filters for this Context.
- * Return <code>true</code> if all filter finalization completed
+ * @return <code>true</code> if all filter finalization completed
* successfully, or <code>false</code> otherwise.
*/
public boolean filterStop() {
@@ -4734,7 +4606,7 @@ public class StandardContext extends ContainerBase
}
filterConfigs.clear();
}
- return (true);
+ return true;
}
@@ -4744,6 +4616,7 @@ public class StandardContext extends ContainerBase
* specified filter name, if any; otherwise return <code>null</code>.
*
* @param name Name of the desired filter
+ * @return the filter config object
*/
public FilterConfig findFilterConfig(String name) {
@@ -4754,7 +4627,8 @@ public class StandardContext extends ContainerBase
/**
* Configure the set of instantiated application event listeners
- * for this Context. Return <code>true</code> if all listeners wre
+ * for this Context.
+ * @return <code>true</code> if all listeners wre
* initialized successfully, or <code>false</code> otherwise.
*/
public boolean listenerStart() {
@@ -4783,7 +4657,7 @@ public class StandardContext extends ContainerBase
}
if (!ok) {
getLogger().error(sm.getString("standardContext.applicationSkipped"));
- return (false);
+ return false;
}
// Sort listeners in two arrays
@@ -4869,7 +4743,7 @@ public class StandardContext extends ContainerBase
/**
* Send an application stop event to all interested listeners.
- * Return <code>true</code> if all events were sent successfully,
+ * @return <code>true</code> if all events were sent successfully,
* or <code>false</code> otherwise.
*/
public boolean listenerStop() {
@@ -4958,8 +4832,7 @@ public class StandardContext extends ContainerBase
/**
* Allocate resources, including proxy.
- * Return <code>true</code> if initialization was successfull,
- * or <code>false</code> otherwise.
+ * @throws LifecycleException if a start error occurs
*/
public void resourcesStart() throws LifecycleException {
@@ -4983,6 +4856,7 @@ public class StandardContext extends ContainerBase
/**
* Deallocate resources and destroy proxy.
+ * @return <code>true</code> if no error occurred
*/
public boolean resourcesStop() {
@@ -5012,6 +4886,7 @@ public class StandardContext extends ContainerBase
*
* @param children Array of wrappers for all currently defined
* servlets (including those not declared load on startup)
+ * @return <code>true</code> if load on startup was considered successful
*/
public boolean loadOnStartup(Container children[]) {
@@ -5107,7 +4982,7 @@ public class StandardContext extends ContainerBase
// An explicit cookie processor hasn't been specified; use the default
if (cookieProcessor == null) {
- cookieProcessor = new LegacyCookieProcessor();
+ cookieProcessor = new Rfc6265CookieProcessor();
}
// Initialize character set mapper
@@ -5160,15 +5035,14 @@ public class StandardContext extends ContainerBase
if (ok) {
// Start our subordinate components, if any
Loader loader = getLoader();
- if ((loader != null) && (loader instanceof Lifecycle))
+ if (loader instanceof Lifecycle) {
((Lifecycle) loader).start();
+ }
// since the loader just started, the webapp classloader is now
// created.
setClassLoaderProperty("clearReferencesRmiTargets",
getClearReferencesRmiTargets());
- setClassLoaderProperty("clearReferencesStatic",
- getClearReferencesStatic());
setClassLoaderProperty("clearReferencesStopThreads",
getClearReferencesStopThreads());
setClassLoaderProperty("clearReferencesStopTimerThreads",
@@ -5187,10 +5061,10 @@ public class StandardContext extends ContainerBase
getLogger();
Realm realm = getRealmInternal();
-
- if (realm != null) {
- if (realm instanceof Lifecycle)
+ if(null != realm) {
+ if (realm instanceof Lifecycle) {
((Lifecycle) realm).start();
+ }
// Place the CredentialHandler into the ServletContext so
// applications can have access to it. Wrap it in a "safe"
@@ -5282,9 +5156,10 @@ public class StandardContext extends ContainerBase
getIgnoreAnnotations() ? new NamingResourcesImpl(): getNamingResources());
setInstanceManager(new DefaultInstanceManager(context,
injectionMap, this, this.getClass().getClassLoader()));
- getServletContext().setAttribute(
- InstanceManager.class.getName(), getInstanceManager());
}
+ getServletContext().setAttribute(
+ InstanceManager.class.getName(), getInstanceManager());
+ InstanceManagerBindings.bind(getLoader().getClassLoader(), getInstanceManager());
}
// Create context attributes that will be required
@@ -5327,7 +5202,7 @@ public class StandardContext extends ContainerBase
try {
// Start manager
Manager manager = getManager();
- if ((manager != null) && (manager instanceof Lifecycle)) {
+ if (manager instanceof Lifecycle) {
((Lifecycle) manager).start();
}
} catch(Exception e) {
@@ -5528,8 +5403,7 @@ public class StandardContext extends ContainerBase
filterStop();
Manager manager = getManager();
- if (manager != null && manager instanceof Lifecycle &&
- ((Lifecycle) manager).getState().isAvailable()) {
+ if (manager instanceof Lifecycle && ((Lifecycle) manager).getState().isAvailable()) {
((Lifecycle) manager).stop();
}
@@ -5544,7 +5418,7 @@ public class StandardContext extends ContainerBase
log.debug("Processing standard container shutdown");
// JNDI resources are unbound in CONFIGURE_STOP_EVENT so stop
- // naming resoucres before they are unbound since NamingResoucres
+ // naming resources before they are unbound since NamingResoucres
// does a JNDI lookup to retrieve the resource. This needs to be
// after the application has finished with the resource
if (namingResources != null) {
@@ -5564,12 +5438,16 @@ public class StandardContext extends ContainerBase
context.clearAttributes();
Realm realm = getRealmInternal();
- if ((realm != null) && (realm instanceof Lifecycle)) {
+ if (realm instanceof Lifecycle) {
((Lifecycle) realm).stop();
}
Loader loader = getLoader();
- if ((loader != null) && (loader instanceof Lifecycle)) {
+ if (loader instanceof Lifecycle) {
+ ClassLoader classLoader = loader.getClassLoader();
((Lifecycle) loader).stop();
+ if (classLoader != null) {
+ InstanceManagerBindings.unbind(classLoader);
+ }
}
// Stop resources
@@ -5637,17 +5515,13 @@ public class StandardContext extends ContainerBase
namingResources.destroy();
}
- synchronized (instanceListenersLock) {
- instanceListeners = new String[0];
- }
-
Loader loader = getLoader();
- if ((loader != null) && (loader instanceof Lifecycle)) {
+ if (loader instanceof Lifecycle) {
((Lifecycle) loader).destroy();
}
Manager manager = getManager();
- if ((manager != null) && (manager instanceof Lifecycle)) {
+ if (manager instanceof Lifecycle) {
((Lifecycle) manager).destroy();
}
@@ -5760,6 +5634,7 @@ public class StandardContext extends ContainerBase
*
* @param urlPattern The URL pattern to be adjusted (if needed)
* and returned
+ * @return the URL pattern with a leading slash if needed
*/
protected String adjustURLPattern(String urlPattern) {
@@ -5779,6 +5654,8 @@ public class StandardContext extends ContainerBase
/**
* Are we processing a version 2.2 deployment descriptor?
+ *
+ * @return <code>true</code> if running a legacy Servlet 2.2 application
*/
@Override
public boolean isServlet22() {
@@ -5866,7 +5743,7 @@ public class StandardContext extends ContainerBase
/**
* Bind current thread, both for CL purposes and for JNDI ENC support
- * during : startup, shutdown and realoading of the context.
+ * during : startup, shutdown and reloading of the context.
*
* @return the previous context class loader
*/
@@ -5888,7 +5765,9 @@ public class StandardContext extends ContainerBase
/**
- * Unbind thread.
+ * Unbind thread and restore the specified context classloader.
+ *
+ * @param oldContextClassLoader the previous classloader
*/
protected void unbindThread(ClassLoader oldContextClassLoader) {
@@ -5973,6 +5852,8 @@ public class StandardContext extends ContainerBase
/**
* Get naming context full name.
+ *
+ * @return the context name
*/
private String getNamingContextName() {
if (namingContextName == null) {
@@ -5999,6 +5880,8 @@ public class StandardContext extends ContainerBase
/**
* Naming context listener accessor.
+ *
+ * @return the naming context listener associated with the webapp
*/
public NamingContextListener getNamingContextListener() {
return namingContextListener;
@@ -6007,6 +5890,8 @@ public class StandardContext extends ContainerBase
/**
* Naming context listener setter.
+ *
+ * @param namingContextListener the new naming context listener
*/
public void setNamingContextListener(NamingContextListener namingContextListener) {
this.namingContextListener = namingContextListener;
@@ -6014,7 +5899,7 @@ public class StandardContext extends ContainerBase
/**
- * Return the request processing paused flag for this Context.
+ * @return the request processing paused flag for this Context.
*/
@Override
public boolean getPaused() {
@@ -6246,13 +6131,14 @@ public class StandardContext extends ContainerBase
* for conformance with specification requirements.
*
* @param urlPattern URL pattern to be validated
+ * @return <code>true</code> if the URL pattern is conformant
*/
private boolean validateURLPattern(String urlPattern) {
if (urlPattern == null)
- return (false);
+ return false;
if (urlPattern.indexOf('\n') >= 0 || urlPattern.indexOf('\r') >= 0) {
- return (false);
+ return false;
}
if (urlPattern.equals("")) {
return true;
@@ -6260,16 +6146,16 @@ public class StandardContext extends ContainerBase
if (urlPattern.startsWith("*.")) {
if (urlPattern.indexOf('/') < 0) {
checkUnusualURLPattern(urlPattern);
- return (true);
+ return true;
} else
- return (false);
+ return false;
}
if ( (urlPattern.startsWith("/")) &&
(urlPattern.indexOf("*.") < 0)) {
checkUnusualURLPattern(urlPattern);
- return (true);
+ return true;
} else
- return (false);
+ return false;
}
@@ -6296,67 +6182,6 @@ public class StandardContext extends ContainerBase
// ------------------------------------------------------------- Operations
-
- /**
- * JSR77 deploymentDescriptor attribute
- *
- * @return string deployment descriptor
- *
- * @deprecated The JSR-77 implementation is incomplete and will be removed
- * in 8.5.x
- */
- @Deprecated
- public String getDeploymentDescriptor() {
-
- InputStream stream = null;
- ServletContext servletContext = getServletContext();
- if (servletContext != null) {
- stream = servletContext.getResourceAsStream(
- org.apache.catalina.startup.Constants.ApplicationWebXml);
- }
- if (stream == null) {
- return "";
- }
- StringBuilder sb = new StringBuilder();
- try (BufferedReader br = new BufferedReader(new InputStreamReader(stream))) {
- String strRead = "";
- while (strRead != null) {
- sb.append(strRead);
- strRead = br.readLine();
- }
- } catch (IOException e) {
- return "";
- }
-
- return sb.toString();
- }
-
-
- /**
- * JSR77 servlets attribute
- *
- * @return list of all servlets ( we know about )
- *
- * @deprecated The JSR-77 implementation is incomplete and will be removed
- * in 8.5.x
- */
- @Deprecated
- public String[] getServlets() {
-
- String[] result = null;
-
- Container[] children = findChildren();
- if (children != null) {
- result = new String[children.length];
- for( int i=0; i< children.length; i++ ) {
- result[i] = children[i].getObjectName().toString();
- }
- }
-
- return result;
- }
-
-
@Override
protected String getObjectNameKeyProperties() {
@@ -6466,7 +6291,8 @@ public class StandardContext extends ContainerBase
}
- /* Add a JMX-NotificationListener
+ /**
+ * Add a JMX NotificationListener
* @see javax.management.NotificationBroadcaster#addNotificationListener(javax.management.NotificationListener, javax.management.NotificationFilter, java.lang.Object)
*/
@Override
@@ -6490,7 +6316,7 @@ public class StandardContext extends ContainerBase
// ------------------------------------------------------------- Attributes
/**
- * Return the naming resources associated with this web application.
+ * @return the naming resources associated with this web application.
*/
public String[] getWelcomeFiles() {
@@ -6548,17 +6374,6 @@ public class StandardContext extends ContainerBase
/**
- * Support for "stateManageable" JSR77
- *
- * @deprecated The JSR-77 implementation is incomplete and will be removed
- * in 8.5.x
- */
- @Deprecated
- public boolean isStateManageable() {
- return true;
- }
-
- /**
* The J2EE Server ObjectName this module is deployed on.
*/
private String server = null;
diff --git a/java/org/apache/catalina/core/StandardContextValve.java b/java/org/apache/catalina/core/StandardContextValve.java
index 8419fce..bab7324 100644
--- a/java/org/apache/catalina/core/StandardContextValve.java
+++ b/java/org/apache/catalina/core/StandardContextValve.java
@@ -24,11 +24,11 @@ import javax.servlet.http.HttpServletResponse;
import org.apache.catalina.Container;
import org.apache.catalina.Wrapper;
-import org.apache.catalina.comet.CometEvent;
import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.Response;
import org.apache.catalina.valves.ValveBase;
import org.apache.tomcat.util.buf.MessageBytes;
+import org.apache.tomcat.util.res.StringManager;
/**
* Valve that implements the default basic behavior for the
@@ -41,6 +41,8 @@ import org.apache.tomcat.util.buf.MessageBytes;
*/
final class StandardContextValve extends ValveBase {
+ private static final StringManager sm = StringManager.getManager(StandardContextValve.class);
+
public StandardContextValve() {
super(true);
}
@@ -105,27 +107,4 @@ final class StandardContextValve extends ValveBase {
}
wrapper.getPipeline().getFirst().invoke(request, response);
}
-
-
- /**
- * Select the appropriate child Wrapper to process this request,
- * based on the specified request URI. If no matching Wrapper can
- * be found, return an appropriate HTTP error.
- *
- * @param request Request to be processed
- * @param response Response to be produced
- * @param event
- *
- * @exception IOException if an input/output error occurred
- * @exception ServletException if a servlet error occurred
- */
- @Override
- public final void event(Request request, Response response, CometEvent event)
- throws IOException, ServletException {
-
- // Select the Wrapper to be used for this Request
- Wrapper wrapper = request.getWrapper();
-
- wrapper.getPipeline().getFirst().event(request, response, event);
- }
}
diff --git a/java/org/apache/catalina/core/StandardEngineValve.java b/java/org/apache/catalina/core/StandardEngineValve.java
index 41c5a00..8ec5ef3 100644
--- a/java/org/apache/catalina/core/StandardEngineValve.java
+++ b/java/org/apache/catalina/core/StandardEngineValve.java
@@ -22,7 +22,6 @@ import javax.servlet.ServletException;
import javax.servlet.http.HttpServletResponse;
import org.apache.catalina.Host;
-import org.apache.catalina.comet.CometEvent;
import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.Response;
import org.apache.catalina.valves.ValveBase;
@@ -88,25 +87,4 @@ final class StandardEngineValve extends ValveBase {
host.getPipeline().getFirst().invoke(request, response);
}
-
-
- /**
- * Process Comet event.
- *
- * @param request Request to be processed
- * @param response Response to be produced
- * @param event the event
- *
- * @exception IOException if an input/output error occurred
- * @exception ServletException if a servlet error occurred
- */
- @Override
- public final void event(Request request, Response response, CometEvent event)
- throws IOException, ServletException {
-
- // Ask this Host to process this request
- request.getHost().getPipeline().getFirst().event(request, response, event);
-
- }
-
}
diff --git a/java/org/apache/catalina/core/StandardHost.java b/java/org/apache/catalina/core/StandardHost.java
index e298293..588b144 100644
--- a/java/org/apache/catalina/core/StandardHost.java
+++ b/java/org/apache/catalina/core/StandardHost.java
@@ -40,6 +40,8 @@ import org.apache.catalina.LifecycleException;
import org.apache.catalina.LifecycleListener;
import org.apache.catalina.Valve;
import org.apache.catalina.loader.WebappClassLoaderBase;
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.ExceptionUtils;
/**
@@ -52,8 +54,7 @@ import org.apache.tomcat.util.ExceptionUtils;
*/
public class StandardHost extends ContainerBase implements Host {
- private static final org.apache.juli.logging.Log log=
- org.apache.juli.logging.LogFactory.getLog( StandardHost.class );
+ private static final Log log = LogFactory.getLog(StandardHost.class);
// ----------------------------------------------------------- Constructors
@@ -330,7 +331,7 @@ public class StandardHost extends ContainerBase implements Host {
/**
- * Returns true if the Host will attempt to create directories for appBase and xmlBase
+ * @return <code>true</code> if the Host will attempt to create directories for appBase and xmlBase
* unless they already exist.
*/
@Override
@@ -339,8 +340,8 @@ public class StandardHost extends ContainerBase implements Host {
}
/**
- * Set to true if the Host should attempt to create directories for xmlBase and appBase upon startup
- * @param createDirs
+ * Set to <code>true</code> if the Host should attempt to create directories for xmlBase and appBase upon startup
+ * @param createDirs the new flag value
*/
@Override
public void setCreateDirs(boolean createDirs) {
@@ -348,7 +349,7 @@ public class StandardHost extends ContainerBase implements Host {
}
/**
- * Return the value of the auto deploy flag. If true, it indicates that
+ * @return the value of the auto deploy flag. If true, it indicates that
* this host's child webapps will be dynamically deployed.
*/
@Override
@@ -376,7 +377,7 @@ public class StandardHost extends ContainerBase implements Host {
/**
- * Return the Java class name of the context configuration class
+ * @return the Java class name of the context configuration class
* for new web applications.
*/
@Override
@@ -405,7 +406,7 @@ public class StandardHost extends ContainerBase implements Host {
/**
- * Return the Java class name of the Context implementation class
+ * @return the Java class name of the Context implementation class
* for new web applications.
*/
public String getContextClass() {
@@ -432,7 +433,7 @@ public class StandardHost extends ContainerBase implements Host {
/**
- * Return the value of the deploy on startup flag. If true, it indicates
+ * @return the value of the deploy on startup flag. If <code>true</code>, it indicates
* that this host's child webapps should be discovered and automatically
* deployed at startup time.
*/
@@ -461,7 +462,7 @@ public class StandardHost extends ContainerBase implements Host {
/**
- * Deploy XML Context config files flag accessor.
+ * @return <code>true</code> if XML context descriptors should be deployed.
*/
public boolean isDeployXML() {
@@ -472,6 +473,8 @@ public class StandardHost extends ContainerBase implements Host {
/**
* Deploy XML Context config files flag mutator.
+ *
+ * @param deployXML <code>true</code> if context descirptors should be deployed
*/
public void setDeployXML(boolean deployXML) {
@@ -481,7 +484,7 @@ public class StandardHost extends ContainerBase implements Host {
/**
- * Return the copy XML config file flag for this component.
+ * @return the copy XML config file flag for this component.
*/
public boolean isCopyXML() {
@@ -503,7 +506,7 @@ public class StandardHost extends ContainerBase implements Host {
/**
- * Return the Java class name of the error report valve class
+ * @return the Java class name of the error report valve class
* for new web applications.
*/
public String getErrorReportValveClass() {
@@ -531,7 +534,7 @@ public class StandardHost extends ContainerBase implements Host {
/**
- * Return the canonical, fully qualified, name of the virtual host
+ * @return the canonical, fully qualified, name of the virtual host
* this Container represents.
*/
@Override
@@ -567,7 +570,7 @@ public class StandardHost extends ContainerBase implements Host {
/**
- * Unpack WARs flag accessor.
+ * @return <code>true</code> if WARs should be unpacked on deployment.
*/
public boolean isUnpackWARs() {
@@ -578,6 +581,8 @@ public class StandardHost extends ContainerBase implements Host {
/**
* Unpack WARs flag mutator.
+ *
+ * @param unpackWARs <code>true</code> to unpack WARs on deployement
*/
public void setUnpackWARs(boolean unpackWARs) {
@@ -587,7 +592,7 @@ public class StandardHost extends ContainerBase implements Host {
/**
- * Host work directory base.
+ * @return host work directory base.
*/
public String getWorkDir() {
@@ -596,7 +601,9 @@ public class StandardHost extends ContainerBase implements Host {
/**
- * Host work directory base.
+ * Set host work directory base.
+ *
+ * @param workDir the new base work folder for this host
*/
public void setWorkDir(String workDir) {
@@ -605,7 +612,7 @@ public class StandardHost extends ContainerBase implements Host {
/**
- * Return the regular expression that defines the files and directories in
+ * @return the regular expression that defines the files and directories in
* the host's {@link #getAppBase} that will be ignored by the automatic
* deployment process.
*/
@@ -619,7 +626,7 @@ public class StandardHost extends ContainerBase implements Host {
/**
- * Return the compiled regular expression that defines the files and
+ * @return the compiled regular expression that defines the files and
* directories in the host's {@link #getAppBase} that will be ignored by the
* automatic deployment process.
*/
@@ -633,6 +640,8 @@ public class StandardHost extends ContainerBase implements Host {
* Set the regular expression that defines the files and directories in
* the host's {@link #getAppBase} that will be ignored by the automatic
* deployment process.
+ *
+ * @param deployIgnore the regexp
*/
@Override
public void setDeployIgnore(String deployIgnore) {
@@ -653,11 +662,19 @@ public class StandardHost extends ContainerBase implements Host {
}
+ /**
+ * @return <code>true</code> if a webapp start should fail if a Servlet startup fails
+ */
public boolean isFailCtxIfServletStartFails() {
return failCtxIfServletStartFails;
}
+ /**
+ * Change the behavior of Servlet startup errors on web application starts.
+ * @param failCtxIfServletStartFails <code>false</code> to ignore errors on Servlets which
+ * are stated when the web application starts
+ */
public void setFailCtxIfServletStartFails(
boolean failCtxIfServletStartFails) {
boolean oldFailCtxIfServletStartFails = this.failCtxIfServletStartFails;
@@ -742,6 +759,8 @@ public class StandardHost extends ContainerBase implements Host {
* This is usually triggered on context 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
*/
public String[] findReloadedContextMemoryLeaks() {
@@ -763,7 +782,7 @@ public class StandardHost extends ContainerBase implements Host {
}
/**
- * Return the set of alias names for this Host. If none are defined,
+ * @return the set of alias names for this Host. If none are defined,
* a zero length array is returned.
*/
@Override
@@ -817,7 +836,7 @@ public class StandardHost extends ContainerBase implements Host {
/**
- * Return a String representation of this component.
+ * @return a String representation of this component.
*/
@Override
public String toString() {
@@ -874,11 +893,11 @@ public class StandardHost extends ContainerBase implements Host {
// -------------------- JMX --------------------
/**
- * Return the MBean Names of the Valves associated with this Host
+ * @return the MBean Names of the Valves associated with this Host
*
* @exception Exception if an MBean cannot be created or registered
*/
- public String [] getValveNames() throws Exception {
+ public String[] getValveNames() throws Exception {
Valve [] valves = this.getPipeline().getValves();
String [] mbeanNames = new String[valves.length];
for (int i = 0; i < valves.length; i++) {
diff --git a/java/org/apache/catalina/core/StandardHostValve.java b/java/org/apache/catalina/core/StandardHostValve.java
index b81358a..8fe3e1a 100644
--- a/java/org/apache/catalina/core/StandardHostValve.java
+++ b/java/org/apache/catalina/core/StandardHostValve.java
@@ -27,7 +27,6 @@ import javax.servlet.http.HttpServletResponse;
import org.apache.catalina.Context;
import org.apache.catalina.Globals;
import org.apache.catalina.Wrapper;
-import org.apache.catalina.comet.CometEvent;
import org.apache.catalina.connector.ClientAbortException;
import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.Response;
@@ -194,52 +193,6 @@ final class StandardHostValve extends ValveBase {
}
- /**
- * Process Comet event.
- *
- * @param request Request to be processed
- * @param response Response to be produced
- * @param event the event
- *
- * @exception IOException if an input/output error occurred
- * @exception ServletException if a servlet error occurred
- */
- @Override
- public final void event(Request request, Response response, CometEvent event)
- throws IOException, ServletException {
-
- // Select the Context to be used for this Request
- Context context = request.getContext();
-
- context.bind(false, MY_CLASSLOADER);
-
- // Ask this Context to process this request
- context.getPipeline().getFirst().event(request, response, event);
-
-
- // Error page processing
- response.setSuspended(false);
-
- Throwable t = (Throwable) request.getAttribute(
- RequestDispatcher.ERROR_EXCEPTION);
-
- if (t != null) {
- throwable(request, response, t);
- } else {
- status(request, response);
- }
-
- // Access a session (if present) to update last accessed time, based on a
- // strict interpretation of the specification
- if (ACCESS_SESSION) {
- request.getSession(false);
- }
-
- // Restore the context classloader
- context.unbind(false, MY_CLASSLOADER);
- }
-
-
// -------------------------------------------------------- Private Methods
/**
@@ -446,13 +399,13 @@ final class StandardHostValve extends ValveBase {
}
// Indicate that we have successfully processed this custom page
- return (true);
+ return true;
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
// Report our failure to process this custom page
container.getLogger().error("Exception Processing " + errorPage, t);
- return (false);
+ return false;
}
}
diff --git a/java/org/apache/catalina/core/StandardServer.java b/java/org/apache/catalina/core/StandardServer.java
index 03fa66d..9a3c9ec 100644
--- a/java/org/apache/catalina/core/StandardServer.java
+++ b/java/org/apache/catalina/core/StandardServer.java
@@ -31,6 +31,8 @@ import java.net.URLClassLoader;
import java.security.AccessControlException;
import java.util.Random;
+import javax.management.InstanceNotFoundException;
+import javax.management.MBeanException;
import javax.management.ObjectName;
import org.apache.catalina.Context;
@@ -543,7 +545,7 @@ public final class StandardServer extends LifecycleMBeanBase implements Server {
/**
- * Return the specified Service (if it exists); otherwise return
+ * @return the specified Service (if it exists); otherwise return
* <code>null</code>.
*
* @param name Name of the Service to be returned
@@ -567,7 +569,7 @@ public final class StandardServer extends LifecycleMBeanBase implements Server {
/**
- * Return the set of Services defined within this Server.
+ * @return the set of Services defined within this Server.
*/
@Override
public Service[] findServices() {
@@ -577,7 +579,7 @@ public final class StandardServer extends LifecycleMBeanBase implements Server {
}
/**
- * Return the JMX service names.
+ * @return the JMX service names.
*/
public ObjectName[] getServiceNames() {
ObjectName onames[]=new ObjectName[ services.length ];
@@ -700,15 +702,15 @@ public final class StandardServer extends LifecycleMBeanBase implements Server {
* Write the configuration information for this entire <code>Server</code>
* out to the server.xml configuration file.
*
- * @exception javax.management.InstanceNotFoundException
- * if the managed resource object cannot be found
- * @exception javax.management.MBeanException
- * if the initializer of the object throws an exception, or
- * persistence is not supported
- * @exception javax.management.RuntimeOperationsException
- * if an exception is reported by the persistence mechanism
- */
- public synchronized void storeConfig() throws Exception {
+ * @exception InstanceNotFoundException
+ * if the managed resource object cannot be found
+ * @exception MBeanException
+ * if the initializer of the object throws an exception, or
+ * persistence is not supported
+ * @exception javax.management.RuntimeOperationsException
+ * if an exception is reported by the persistence mechanism
+ */
+ public synchronized void storeConfig() throws InstanceNotFoundException, MBeanException {
try {
// Note: Hard-coded domain used since this object is per Server/JVM
ObjectName sname = new ObjectName("Catalina:type=StoreConfig");
@@ -728,14 +730,16 @@ public final class StandardServer extends LifecycleMBeanBase implements Server {
* Write the configuration information for <code>Context</code>
* out to the specified configuration file.
*
- * @exception javax.management.InstanceNotFoundException if the managed resource object
- * cannot be found
- * @exception javax.management.MBeanException if the initializer of the object throws
- * an exception, or persistence is not supported
- * @exception javax.management.RuntimeOperationsException if an exception is reported
- * by the persistence mechanism
- */
- public synchronized void storeContext(Context context) throws Exception {
+ * @param context the context which should save its configuration
+ * @exception InstanceNotFoundException
+ * if the managed resource object cannot be found
+ * @exception MBeanException
+ * if the initializer of the object throws an exception
+ * or persistence is not supported
+ * @exception javax.management.RuntimeOperationsException
+ * if an exception is reported by the persistence mechanism
+ */
+ public synchronized void storeContext(Context context) throws InstanceNotFoundException, MBeanException {
try {
// Note: Hard-coded domain used since this object is per Server/JVM
ObjectName sname = new ObjectName("Catalina:type=StoreConfig");
@@ -754,7 +758,7 @@ public final class StandardServer extends LifecycleMBeanBase implements Server {
/**
- * Return true if naming should be used.
+ * @return <code>true</code> if naming should be used.
*/
private boolean isUseNaming() {
boolean useNaming = true;
diff --git a/java/org/apache/catalina/core/StandardService.java b/java/org/apache/catalina/core/StandardService.java
index 4bc460a..99092e8 100644
--- a/java/org/apache/catalina/core/StandardService.java
+++ b/java/org/apache/catalina/core/StandardService.java
@@ -89,13 +89,7 @@ public class StandardService extends LifecycleMBeanBase implements Service {
*/
protected final ArrayList<Executor> executors = new ArrayList<>();
- /**
- * The Container associated with this Service.
- *
- * @deprecated Will be made private in 8.5.x
- */
- @Deprecated
- protected Container container = null;
+ private Engine engine = null;
private ClassLoader parentClassLoader = null;
@@ -108,8 +102,7 @@ public class StandardService extends LifecycleMBeanBase implements Service {
/**
* Mapper listener.
*/
- protected final MapperListener mapperListener =
- new MapperListener(mapper, this);
+ protected final MapperListener mapperListener = new MapperListener(this);
// ------------------------------------------------------------- Properties
@@ -120,50 +113,52 @@ public class StandardService extends LifecycleMBeanBase implements Service {
}
- /**
- * Return the <code>Container</code> that handles requests for all
- * <code>Connectors</code> associated with this Service.
- */
@Override
- public Container getContainer() {
-
- return (this.container);
-
- }
-
-
- @Override
- public void setContainer(Container container) {
- setContainer((Engine) container);
+ public Engine getContainer() {
+ return engine;
}
@Override
public void setContainer(Engine engine) {
- Container oldContainer = this.container;
- if ((oldContainer != null) && (oldContainer instanceof Engine))
- ((Engine) oldContainer).setService(null);
- this.container = engine;
- if ((this.container != null) && (this.container instanceof Engine))
- ((Engine) this.container).setService(this);
- if (getState().isAvailable() && (this.container != null)) {
+ Engine oldEngine = this.engine;
+ if (oldEngine != null) {
+ oldEngine.setService(null);
+ }
+ this.engine = engine;
+ if (this.engine != null) {
+ this.engine.setService(this);
+ }
+ if (getState().isAvailable()) {
+ if (this.engine != null) {
+ try {
+ this.engine.start();
+ } catch (LifecycleException e) {
+ log.warn(sm.getString("standardService.engine.startFailed"), e);
+ }
+ }
+ // Restart MapperListener to pick up new engine.
try {
- this.container.start();
+ mapperListener.stop();
} catch (LifecycleException e) {
- // Ignore
+ log.warn(sm.getString("standardService.mapperListener.stopFailed"), e);
}
- }
- if (getState().isAvailable() && (oldContainer != null)) {
try {
- oldContainer.stop();
+ mapperListener.start();
} catch (LifecycleException e) {
- // Ignore
+ log.warn(sm.getString("standardService.mapperListener.startFailed"), e);
+ }
+ if (oldEngine != null) {
+ try {
+ oldEngine.stop();
+ } catch (LifecycleException e) {
+ log.warn(sm.getString("standardService.engine.stopFailed"), e);
+ }
}
}
// Report this property change to interested listeners
- support.firePropertyChange("container", oldContainer, this.container);
-
+ support.firePropertyChange("container", oldEngine, this.engine);
}
@@ -172,9 +167,7 @@ public class StandardService extends LifecycleMBeanBase implements Service {
*/
@Override
public String getName() {
-
- return (this.name);
-
+ return name;
}
@@ -185,9 +178,7 @@ public class StandardService extends LifecycleMBeanBase implements Service {
*/
@Override
public void setName(String name) {
-
this.name = name;
-
}
@@ -196,9 +187,7 @@ public class StandardService extends LifecycleMBeanBase implements Service {
*/
@Override
public Server getServer() {
-
- return (this.server);
-
+ return this.server;
}
@@ -209,15 +198,12 @@ public class StandardService extends LifecycleMBeanBase implements Service {
*/
@Override
public void setServer(Server server) {
-
this.server = server;
-
}
// --------------------------------------------------------- Public Methods
-
/**
* Add a new Connector to the set of defined Connectors, and associate it
* with this Service's Container.
@@ -250,6 +236,7 @@ public class StandardService extends LifecycleMBeanBase implements Service {
}
+
public ObjectName[] getConnectorNames() {
ObjectName results[] = new ObjectName[connectors.length];
for (int i=0; i<results.length; i++) {
@@ -265,9 +252,7 @@ public class StandardService extends LifecycleMBeanBase implements Service {
* @param listener The listener to add
*/
public void addPropertyChangeListener(PropertyChangeListener listener) {
-
support.addPropertyChangeListener(listener);
-
}
@@ -276,9 +261,7 @@ public class StandardService extends LifecycleMBeanBase implements Service {
*/
@Override
public Connector[] findConnectors() {
-
return connectors;
-
}
@@ -323,7 +306,6 @@ public class StandardService extends LifecycleMBeanBase implements Service {
// Report this property change to interested listeners
support.firePropertyChange("connector", connector, null);
}
-
}
@@ -333,9 +315,7 @@ public class StandardService extends LifecycleMBeanBase implements Service {
* @param listener The listener to remove
*/
public void removePropertyChangeListener(PropertyChangeListener listener) {
-
support.removePropertyChangeListener(listener);
-
}
@@ -344,12 +324,10 @@ public class StandardService extends LifecycleMBeanBase implements Service {
*/
@Override
public String toString() {
-
StringBuilder sb = new StringBuilder("StandardService[");
sb.append(getName());
sb.append("]");
return (sb.toString());
-
}
@@ -362,16 +340,18 @@ public class StandardService extends LifecycleMBeanBase implements Service {
synchronized (executors) {
if (!executors.contains(ex)) {
executors.add(ex);
- if (getState().isAvailable())
+ if (getState().isAvailable()) {
try {
ex.start();
} catch (LifecycleException x) {
log.error("Executor.start", x);
}
+ }
}
}
}
+
/**
* Retrieves all executors
* @return Executor[]
@@ -385,6 +365,7 @@ public class StandardService extends LifecycleMBeanBase implements Service {
}
}
+
/**
* Retrieves executor by name, null if not found
* @param executorName String
@@ -401,6 +382,7 @@ public class StandardService extends LifecycleMBeanBase implements Service {
return null;
}
+
/**
* Removes an executor from the service
* @param ex Executor
@@ -419,7 +401,6 @@ public class StandardService extends LifecycleMBeanBase implements Service {
}
-
/**
* Start nested components ({@link Executor}s, {@link Connector}s and
* {@link Container}s) and implement the requirements of
@@ -436,9 +417,9 @@ public class StandardService extends LifecycleMBeanBase implements Service {
setState(LifecycleState.STARTING);
// Start our defined Container first
- if (container != null) {
- synchronized (container) {
- container.start();
+ if (engine != null) {
+ synchronized (engine) {
+ engine.start();
}
}
@@ -497,9 +478,9 @@ public class StandardService extends LifecycleMBeanBase implements Service {
setState(LifecycleState.STOPPING);
// Stop our defined Container second
- if (container != null) {
- synchronized (container) {
- container.stop();
+ if (engine != null) {
+ synchronized (engine) {
+ engine.stop();
}
}
@@ -546,8 +527,8 @@ public class StandardService extends LifecycleMBeanBase implements Service {
super.initInternal();
- if (container != null) {
- container.init();
+ if (engine != null) {
+ engine.init();
}
// Initialize any Executors
@@ -578,6 +559,7 @@ public class StandardService extends LifecycleMBeanBase implements Service {
}
}
+
@Override
protected void destroyInternal() throws LifecycleException {
mapperListener.destroy();
@@ -589,8 +571,7 @@ public class StandardService extends LifecycleMBeanBase implements Service {
connector.destroy();
} catch (Exception e) {
log.error(sm.getString(
- "standardService.connector.destroyFailed",
- connector), e);
+ "standardService.connector.destroyFailed", connector), e);
}
}
}
@@ -600,26 +581,28 @@ public class StandardService extends LifecycleMBeanBase implements Service {
executor.destroy();
}
- if (container != null) {
- container.destroy();
+ if (engine != null) {
+ engine.destroy();
}
super.destroyInternal();
}
+
/**
* Return the parent class loader for this component.
*/
@Override
public ClassLoader getParentClassLoader() {
if (parentClassLoader != null)
- return (parentClassLoader);
+ return parentClassLoader;
if (server != null) {
- return (server.getParentClassLoader());
+ return server.getParentClassLoader();
}
- return (ClassLoader.getSystemClassLoader());
+ return ClassLoader.getSystemClassLoader();
}
+
/**
* Set the parent class loader for this server.
*
@@ -633,6 +616,7 @@ public class StandardService extends LifecycleMBeanBase implements Service {
this.parentClassLoader);
}
+
@Override
protected String getDomainInternal() {
String domain = null;
@@ -653,6 +637,7 @@ public class StandardService extends LifecycleMBeanBase implements Service {
return domain;
}
+
@Override
public final String getObjectNameKeyProperties() {
return "type=Service";
diff --git a/java/org/apache/catalina/core/StandardWrapper.java b/java/org/apache/catalina/core/StandardWrapper.java
index 2520f9e..bc43fb2 100644
--- a/java/org/apache/catalina/core/StandardWrapper.java
+++ b/java/org/apache/catalina/core/StandardWrapper.java
@@ -52,13 +52,10 @@ import org.apache.catalina.Container;
import org.apache.catalina.ContainerServlet;
import org.apache.catalina.Context;
import org.apache.catalina.Globals;
-import org.apache.catalina.InstanceEvent;
-import org.apache.catalina.InstanceListener;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.LifecycleState;
import org.apache.catalina.Wrapper;
import org.apache.catalina.security.SecurityUtil;
-import org.apache.catalina.util.InstanceSupport;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.InstanceManager;
@@ -141,14 +138,6 @@ public class StandardWrapper extends ContainerBase
*/
protected volatile boolean instanceInitialized = false;
- /**
- * The support object for our instance listeners.
- *
- * @deprecated Will be removed in 8.5.x onwards
- */
- @Deprecated
- protected final InstanceSupport instanceSupport = new InstanceSupport(this);
-
/**
* The load-on-startup order value (negative value means load on
@@ -339,32 +328,17 @@ public class StandardWrapper extends ContainerBase
/**
- * Return the number of active allocations of this servlet, even if they
+ * @return the number of active allocations of this servlet, even if they
* are all for the same instance (as will be true for servlets that do
* not implement <code>SingleThreadModel</code>.
*/
public int getCountAllocated() {
-
- return (this.countAllocated.get());
-
- }
-
-
- /**
- * Return the InstanceSupport object for this Wrapper instance.
- *
- * @deprecated Will be removed in 8.5.x onwards
- */
- @Deprecated
- public InstanceSupport getInstanceSupport() {
-
- return (this.instanceSupport);
-
+ return this.countAllocated.get();
}
/**
- * Return the load-on-startup order value (negative value means
+ * @return the load-on-startup order value (negative value means
* load on first call).
*/
@Override
@@ -419,13 +393,16 @@ public class StandardWrapper extends ContainerBase
}
}
+ /**
+ * @return the load-on-startup value that was parsed
+ */
public String getLoadOnStartupString() {
return Integer.toString( getLoadOnStartup());
}
/**
- * Return maximum number of instances that will be allocated when a single
+ * @return maximum number of instances that will be allocated when a single
* thread model servlet is used.
*/
public int getMaxInstances() {
@@ -473,7 +450,7 @@ public class StandardWrapper extends ContainerBase
/**
- * Return the run-as identity for this servlet.
+ * @return the run-as identity for this servlet.
*/
@Override
public String getRunAs() {
@@ -499,7 +476,7 @@ public class StandardWrapper extends ContainerBase
/**
- * Return the fully qualified servlet class name for this servlet.
+ * @return the fully qualified servlet class name for this servlet.
*/
@Override
public String getServletClass() {
@@ -564,7 +541,7 @@ public class StandardWrapper extends ContainerBase
/**
- * Is this servlet currently unavailable?
+ * @return <code>true</code> if the Servlet has been marked unavailable.
*/
@Override
public boolean isUnavailable() {
@@ -620,7 +597,7 @@ public class StandardWrapper extends ContainerBase
/**
- * Return the associated servlet instance.
+ * @return the associated servlet instance.
*/
@Override
public Servlet getServlet() {
@@ -660,7 +637,7 @@ public class StandardWrapper extends ContainerBase
if (!getState().isAvailable())
return;
- if (getServlet() != null && (getServlet() instanceof PeriodicEventListener)) {
+ if (getServlet() instanceof PeriodicEventListener) {
((PeriodicEventListener) getServlet()).periodicEvent();
}
}
@@ -670,6 +647,7 @@ public class StandardWrapper extends ContainerBase
* Extract the root cause from a servlet exception.
*
* @param e The servlet exception
+ * @return the root cause of the Servlet exception
*/
public static Throwable getRootCause(ServletException e) {
Throwable rootCause = e;
@@ -722,22 +700,6 @@ public class StandardWrapper extends ContainerBase
/**
- * Add a new listener interested in InstanceEvents.
- *
- * @param listener The new listener
- *
- * @deprecated Will be removed in 8.5.x onwards
- */
- @Deprecated
- @Override
- public void addInstanceListener(InstanceListener listener) {
-
- instanceSupport.addInstanceListener(listener);
-
- }
-
-
- /**
* Add a mapping associated with the Wrapper.
*
* @param mapping The new wrapper mapping
@@ -1059,14 +1021,11 @@ public class StandardWrapper extends ContainerBase
* at least one initialized instance. This can be used, for example, to
* load servlets that are marked in the deployment descriptor to be loaded
* at server startup time.
+ * @return the loaded Servlet instance
+ * @throws ServletException for a Servlet load error
*/
public synchronized Servlet loadServlet() throws ServletException {
- if (unloading) {
- throw new ServletException(
- sm.getString("standardWrapper.unloading", getName()));
- }
-
// Nothing to do if we already have an instance or an instance pool
if (!singleThreadModel && (instance != null))
return instance;
@@ -1205,9 +1164,6 @@ public class StandardWrapper extends ContainerBase
// Call the initialization method of this servlet
try {
- instanceSupport.fireInstanceEvent(InstanceEvent.BEFORE_INIT_EVENT,
- servlet);
-
if( Globals.IS_SECURITY_ENABLED) {
boolean success = false;
try {
@@ -1228,25 +1184,16 @@ public class StandardWrapper extends ContainerBase
}
instanceInitialized = true;
-
- instanceSupport.fireInstanceEvent(InstanceEvent.AFTER_INIT_EVENT,
- servlet);
} catch (UnavailableException f) {
- instanceSupport.fireInstanceEvent(InstanceEvent.AFTER_INIT_EVENT,
- servlet, f);
unavailable(f);
throw f;
} catch (ServletException f) {
- instanceSupport.fireInstanceEvent(InstanceEvent.AFTER_INIT_EVENT,
- servlet, f);
// If the servlet wanted to be unavailable it would have
// said so, so do not call unavailable(null).
throw f;
} catch (Throwable f) {
ExceptionUtils.handleThrowable(f);
getServletContext().log("StandardWrapper.Throwable", f );
- instanceSupport.fireInstanceEvent(InstanceEvent.AFTER_INIT_EVENT,
- servlet, f);
// If the servlet wanted to be unavailable it would have
// said so, so do not call unavailable(null).
throw new ServletException
@@ -1274,22 +1221,6 @@ public class StandardWrapper extends ContainerBase
/**
- * Remove a listener no longer interested in InstanceEvents.
- *
- * @param listener The listener to remove
- *
- * @deprecated Will be removed in 8.5.x onwards
- */
- @Deprecated
- @Override
- public void removeInstanceListener(InstanceListener listener) {
-
- instanceSupport.removeInstanceListener(listener);
-
- }
-
-
- /**
* Remove a mapping associated with the wrapper.
*
* @param mapping The pattern to remove
@@ -1329,7 +1260,7 @@ public class StandardWrapper extends ContainerBase
/**
- * Return a String representation of this component.
+ * @return a String representation of this component.
*/
@Override
public String toString() {
@@ -1418,13 +1349,9 @@ public class StandardWrapper extends ContainerBase
// Call the servlet destroy() method
try {
- instanceSupport.fireInstanceEvent
- (InstanceEvent.BEFORE_DESTROY_EVENT, instance);
-
if( Globals.IS_SECURITY_ENABLED) {
try {
- SecurityUtil.doAsPrivilege("destroy",
- instance);
+ SecurityUtil.doAsPrivilege("destroy", instance);
} finally {
SecurityUtil.remove(instance);
}
@@ -1432,14 +1359,9 @@ public class StandardWrapper extends ContainerBase
instance.destroy();
}
- instanceSupport.fireInstanceEvent
- (InstanceEvent.AFTER_DESTROY_EVENT, instance);
-
} catch (Throwable t) {
t = ExceptionUtils.unwrapInvocationTargetException(t);
ExceptionUtils.handleThrowable(t);
- instanceSupport.fireInstanceEvent
- (InstanceEvent.AFTER_DESTROY_EVENT, instance, t);
instance = null;
instancePool = null;
nInstances = 0;
@@ -1525,7 +1447,7 @@ public class StandardWrapper extends ContainerBase
/**
- * Return the initialization parameter value for the specified name,
+ * @return the initialization parameter value for the specified name,
* if any; otherwise return <code>null</code>.
*
* @param name Name of the initialization parameter to retrieve
@@ -1539,7 +1461,7 @@ public class StandardWrapper extends ContainerBase
/**
- * Return the set of initialization parameter names defined for this
+ * @return the set of initialization parameter names defined for this
* servlet. If none are defined, an empty Enumeration is returned.
*/
@Override
@@ -1556,7 +1478,7 @@ public class StandardWrapper extends ContainerBase
/**
- * Return the servlet context with which this servlet is associated.
+ * @return the servlet context with which this servlet is associated.
*/
@Override
public ServletContext getServletContext() {
@@ -1572,7 +1494,7 @@ public class StandardWrapper extends ContainerBase
/**
- * Return the name of this servlet.
+ * @return the name of this servlet.
*/
@Override
public String getServletName() {
@@ -1655,7 +1577,7 @@ public class StandardWrapper extends ContainerBase
/**
- * Return <code>true</code> if the specified class name represents a
+ * @return <code>true</code> if the specified class name represents a
* container provided servlet class that should be loaded by the
* server class loader.
*
@@ -1664,7 +1586,7 @@ public class StandardWrapper extends ContainerBase
protected boolean isContainerProvidedServlet(String classname) {
if (classname.startsWith("org.apache.catalina.")) {
- return (true);
+ return true;
}
try {
Class<?> clazz =
@@ -1672,7 +1594,7 @@ public class StandardWrapper extends ContainerBase
return (ContainerServlet.class.isAssignableFrom(clazz));
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
- return (false);
+ return false;
}
}
@@ -1858,18 +1780,7 @@ public class StandardWrapper extends ContainerBase
/**
- * JSR 77. Always return false.
- *
- * @deprecated The JSR-77 implementation is incomplete and will be removed
- * in 8.5.x
- */
- @Deprecated
- public boolean isStateManageable() {
- return false;
- }
-
-
- /* Remove a JMX notficationListener
+ * Remove a JMX notficationListener
* @see javax.management.NotificationEmitter#removeNotificationListener(javax.management.NotificationListener, javax.management.NotificationFilter, java.lang.Object)
*/
@Override
@@ -1880,9 +1791,9 @@ public class StandardWrapper extends ContainerBase
protected MBeanNotificationInfo[] notificationInfo;
- /* Get JMX Broadcaster Info
- * @TODO use StringManager for international support!
- * @TODO This two events we not send j2ee.state.failed and j2ee.attribute.changed!
+ /**
+ * Get JMX Broadcaster Info
+ * FIXME: This two events we not send j2ee.state.failed and j2ee.attribute.changed!
* @see javax.management.NotificationBroadcaster#getNotificationInfo()
*/
@Override
@@ -1927,7 +1838,8 @@ public class StandardWrapper extends ContainerBase
}
- /* Add a JMX-NotificationListener
+ /**
+ * Add a JMX-NotificationListener
* @see javax.management.NotificationBroadcaster#addNotificationListener(javax.management.NotificationListener, javax.management.NotificationFilter, java.lang.Object)
*/
@Override
diff --git a/java/org/apache/catalina/core/StandardWrapperFacade.java b/java/org/apache/catalina/core/StandardWrapperFacade.java
index 4ef64c0..7ebed58 100644
--- a/java/org/apache/catalina/core/StandardWrapperFacade.java
+++ b/java/org/apache/catalina/core/StandardWrapperFacade.java
@@ -39,6 +39,7 @@ public final class StandardWrapperFacade
/**
* Create a new facade around a StandardWrapper.
+ * @param config the associated wrapper
*/
public StandardWrapperFacade(StandardWrapper config) {
@@ -76,8 +77,9 @@ public final class StandardWrapperFacade
public ServletContext getServletContext() {
if (context == null) {
context = config.getServletContext();
- if ((context != null) && (context instanceof ApplicationContext))
+ if (context instanceof ApplicationContext) {
context = ((ApplicationContext) context).getFacade();
+ }
}
return (context);
}
diff --git a/java/org/apache/catalina/core/StandardWrapperValve.java b/java/org/apache/catalina/core/StandardWrapperValve.java
index ed775bb..f0fde35 100644
--- a/java/org/apache/catalina/core/StandardWrapperValve.java
+++ b/java/org/apache/catalina/core/StandardWrapperValve.java
@@ -32,8 +32,6 @@ import javax.servlet.http.HttpServletResponse;
import org.apache.catalina.Context;
import org.apache.catalina.Globals;
import org.apache.catalina.LifecycleException;
-import org.apache.catalina.comet.CometEvent;
-import org.apache.catalina.comet.CometProcessor;
import org.apache.catalina.connector.ClientAbortException;
import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.Response;
@@ -163,14 +161,6 @@ final class StandardWrapperValve
servlet = null;
}
- // Identify if the request is Comet related now that the servlet has been allocated
- boolean comet = false;
- if (servlet instanceof CometProcessor && Boolean.TRUE.equals(request.getAttribute(
- Globals.COMET_SUPPORTED_ATTR))) {
- comet = true;
- request.setComet(true);
- }
-
MessageBytes requestPathMB = request.getRequestPathMB();
DispatcherType dispatcherType = DispatcherType.REQUEST;
if (request.getDispatcherType()==DispatcherType.ASYNC) dispatcherType = DispatcherType.ASYNC;
@@ -191,8 +181,6 @@ final class StandardWrapperValve
SystemLogHandler.startCapture();
if (request.isAsyncDispatching()) {
request.getAsyncContextInternal().doInternalDispatch();
- } else if (comet) {
- filterChain.doFilterEvent(request.getEvent());
} else {
filterChain.doFilter(request.getRequest(),
response.getResponse());
@@ -206,8 +194,6 @@ final class StandardWrapperValve
} else {
if (request.isAsyncDispatching()) {
request.getAsyncContextInternal().doInternalDispatch();
- } else if (comet) {
- filterChain.doFilterEvent(request.getEvent());
} else {
filterChain.doFilter
(request.getRequest(), response.getResponse());
@@ -265,13 +251,7 @@ final class StandardWrapperValve
// Release the filter chain (if any) for this request
if (filterChain != null) {
- if (request.isComet()) {
- // If this is a Comet request, then the same chain will be used for the
- // processing of all subsequent events.
- filterChain.reuse();
- } else {
- filterChain.release();
- }
+ filterChain.release();
}
// Deallocate the allocated servlet instance
@@ -315,177 +295,8 @@ final class StandardWrapperValve
}
- /**
- * Process a Comet event. The main differences here are to not use sendError
- * (the response is committed), to avoid creating a new filter chain
- * (which would work but be pointless), and a few very minor tweaks.
- *
- * @param request The servlet request to be processed
- * @param response The servlet response to be created
- *
- * @exception IOException if an input/output error occurs, or is thrown
- * by a subsequently invoked Valve, Filter, or Servlet
- * @exception ServletException if a servlet error occurs, or is thrown
- * by a subsequently invoked Valve, Filter, or Servlet
- */
- @Override
- public void event(Request request, Response response, CometEvent event)
- throws IOException, ServletException {
-
- // Initialize local variables we may need
- Throwable throwable = null;
- // This should be a Request attribute...
- long t1=System.currentTimeMillis();
- // FIXME: Add a flag to count the total amount of events processed ? requestCount++;
-
- StandardWrapper wrapper = (StandardWrapper) getContainer();
- if (wrapper == null) {
- // Context has been shutdown. Nothing to do here.
- return;
- }
-
- Servlet servlet = null;
- Context context = (Context) wrapper.getParent();
-
- // Check for the application being marked unavailable
- boolean unavailable = !context.getState().isAvailable() ||
- wrapper.isUnavailable();
-
- // Allocate a servlet instance to process this request
- try {
- if (!unavailable) {
- servlet = wrapper.allocate();
- }
- } catch (UnavailableException e) {
- // The response is already committed, so it's not possible to do anything
- } catch (ServletException e) {
- container.getLogger().error(sm.getString("standardWrapper.allocateException",
- wrapper.getName()), 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;
- }
-
- MessageBytes requestPathMB = request.getRequestPathMB();
- request.setAttribute(Globals.DISPATCHER_TYPE_ATTR,
- DispatcherType.REQUEST);
- request.setAttribute(Globals.DISPATCHER_REQUEST_PATH_ATTR,
- requestPathMB);
- // Get the current (unchanged) filter chain for this request
- ApplicationFilterChain filterChain =
- (ApplicationFilterChain) request.getFilterChain();
-
- // Call the filter chain for this request
- // NOTE: This also calls the servlet's event() method
- try {
- if ((servlet != null) && (filterChain != null)) {
-
- // Swallow output if needed
- if (context.getSwallowOutput()) {
- try {
- SystemLogHandler.startCapture();
- filterChain.doFilterEvent(request.getEvent());
- } finally {
- String log = SystemLogHandler.stopCapture();
- if (log != null && log.length() > 0) {
- context.getLogger().info(log);
- }
- }
- } else {
- filterChain.doFilterEvent(request.getEvent());
- }
-
- }
- } catch (ClientAbortException e) {
- throwable = e;
- exception(request, response, e);
- } catch (IOException e) {
- container.getLogger().error(sm.getString(
- "standardWrapper.serviceException", wrapper.getName(),
- context.getName()), e);
- throwable = e;
- exception(request, response, e);
- } catch (UnavailableException e) {
- container.getLogger().error(sm.getString(
- "standardWrapper.serviceException", wrapper.getName(),
- context.getName()), e);
- // Do not save exception in 'throwable', because we
- // do not want to do exception(request, response, e) processing
- } catch (ServletException e) {
- Throwable rootCause = StandardWrapper.getRootCause(e);
- if (!(rootCause instanceof ClientAbortException)) {
- container.getLogger().error(sm.getString(
- "standardWrapper.serviceExceptionRoot",
- wrapper.getName(), context.getName(), e.getMessage()),
- rootCause);
- }
- throwable = e;
- exception(request, response, e);
- } catch (Throwable e) {
- ExceptionUtils.handleThrowable(e);
- container.getLogger().error(sm.getString(
- "standardWrapper.serviceException", wrapper.getName(),
- context.getName()), e);
- throwable = e;
- exception(request, response, e);
- }
-
- // Release the filter chain (if any) for this request
- if (filterChain != null) {
- filterChain.reuse();
- }
-
- // Deallocate the allocated servlet instance
- try {
- if (servlet != null) {
- wrapper.deallocate(servlet);
- }
- } catch (Throwable e) {
- ExceptionUtils.handleThrowable(e);
- container.getLogger().error(sm.getString("standardWrapper.deallocateException",
- wrapper.getName()), e);
- if (throwable == null) {
- throwable = e;
- exception(request, response, e);
- }
- }
-
- // If this servlet has been marked permanently unavailable,
- // unload it and release this instance
- try {
- 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);
- if (throwable == null) {
- throwable = e;
- exception(request, response, e);
- }
- }
-
- long t2=System.currentTimeMillis();
-
- long time=t2-t1;
- processingTime += time;
- if( time > maxTime) maxTime=time;
- if( time < minTime) minTime=time;
-
- }
-
-
// -------------------------------------------------------- Private Methods
-
/**
* Handle the specified ServletException encountered while processing
* the specified Request to produce the specified Response. Any
diff --git a/java/org/apache/catalina/core/ThreadLocalLeakPreventionListener.java b/java/org/apache/catalina/core/ThreadLocalLeakPreventionListener.java
index f8d3b97..d235dcd 100644
--- a/java/org/apache/catalina/core/ThreadLocalLeakPreventionListener.java
+++ b/java/org/apache/catalina/core/ThreadLocalLeakPreventionListener.java
@@ -127,8 +127,7 @@ public class ThreadLocalLeakPreventionListener implements LifecycleListener,
private void registerListenersForServer(Server server) {
for (Service service : server.findServices()) {
- @SuppressWarnings("deprecation")
- Engine engine = (Engine) service.getContainer();
+ Engine engine = service.getContainer();
engine.addContainerListener(this);
registerListenersForEngine(engine);
}
diff --git a/java/org/apache/catalina/core/mbeans-descriptors.xml b/java/org/apache/catalina/core/mbeans-descriptors.xml
index 67eea81..eef3860 100644
--- a/java/org/apache/catalina/core/mbeans-descriptors.xml
+++ b/java/org/apache/catalina/core/mbeans-descriptors.xml
@@ -1,4 +1,4 @@
-<?xml version="1.0"?>
+<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
@@ -86,10 +86,6 @@
description="Should Tomcat look for memory leaks in RMI Targets and clear them if found as a work around for application coding errors?"
type="boolean"/>
- <attribute name="clearReferencesStatic"
- description="Deprecated. Will be removed in Tomcat 8.5. Should Tomcat attempt to null out any static or final fields from loaded classes when a web application is stopped as a work around for apparent garbage collection bugs and application coding errors?"
- type="boolean"/>
-
<attribute name="clearReferencesStopThreads"
description="Should Tomcat attempt to terminate threads that have been started by the web application? Advisable to be used only in a development environment."
type="boolean"/>
@@ -127,11 +123,6 @@
description=""
type="boolean"/>
- <attribute name="deploymentDescriptor"
- description="String deployment descriptor "
- type="java.lang.String"
- writeable="false" />
-
<attribute name="displayName"
description="The display name of this web application"
type="java.lang.String"/>
@@ -303,12 +294,6 @@
description="Time (in milliseconds) it took to start this context"
type="long"/>
- <attribute name="stateManageable"
- description="Deprecated. State management support for this managed object"
- is="true"
- type="boolean"
- writeable="false" />
-
<attribute name="stateName"
description="The name of the LifecycleState that this component is currently in"
type="java.lang.String"
@@ -399,15 +384,6 @@
type="java.lang.String"/>
</operation>
- <operation name="addInstanceListener"
- description="Add the classname of an InstanceListener to be added to each Wrapper appended to this Context."
- impact="ACTION"
- returnType="void">
- <parameter name="listener"
- description="Java class name of an InstanceListener class"
- type="java.lang.String"/>
- </operation>
-
<operation name="addLifecycleListener"
description="Add a lifecycle listener to this Context"
impact="ACTION"
@@ -621,12 +597,6 @@
returnType="[Ljava.lang.String;">
</operation>
- <operation name="findInstanceListeners"
- description="Return the set of InstanceListener classes that will be added to newly created Wrappers automatically."
- impact="INFO"
- returnType="[Ljava.lang.String;">
- </operation>
-
<operation name="findLifecycleListenerNames"
description="Return the set of lifecycle listener class names configured for this application."
impact="INFO"
@@ -801,15 +771,6 @@
type="java.lang.String"/>
</operation>
- <operation name="removeInstanceListener"
- description="Remove the application parameter with the specified name from the set for this application."
- impact="ACTION"
- returnType="void">
- <parameter name="listener"
- description="Class name of an InstanceListener class to be removed"
- type="java.lang.String"/>
- </operation>
-
<operation name="removeLifecycleListeners"
description="Removes lifecycle listeners of given class type from this Context"
impact="ACTION"
@@ -1652,12 +1613,6 @@
is="true"
writeable="false" />
- <attribute name="stateManageable"
- description="State management support for this managed object"
- is="true"
- type="boolean"
- writeable="false"/>
-
<attribute name="stateName"
description="The name of the LifecycleState that this component is currently in"
type="java.lang.String"
@@ -1717,9 +1672,9 @@
<operation name="findInitParameter"
description="Add a mapping associated with the Wrapper."
impact="ACTION"
- returnType="void">
- <parameter name="mapping"
- description="The new wrapper mapping"
+ returnType="java.lang.String">
+ <parameter name="name"
+ description="The name of the initialization parameter"
type="java.lang.String"/>
</operation>
diff --git a/java/org/apache/catalina/deploy/Constants.java b/java/org/apache/catalina/deploy/Constants.java
deleted file mode 100644
index 6b3cf21..0000000
--- a/java/org/apache/catalina/deploy/Constants.java
+++ /dev/null
@@ -1,26 +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.deploy;
-
-
-public class Constants {
-
- public static final String Package = "org.apache.catalina.deploy";
-
-}
diff --git a/java/org/apache/catalina/deploy/NamingResourcesImpl.java b/java/org/apache/catalina/deploy/NamingResourcesImpl.java
index 9330440..582d22e 100644
--- a/java/org/apache/catalina/deploy/NamingResourcesImpl.java
+++ b/java/org/apache/catalina/deploy/NamingResourcesImpl.java
@@ -70,8 +70,7 @@ public class NamingResourcesImpl extends LifecycleMBeanBase
private static final Log log = LogFactory.getLog(NamingResourcesImpl.class);
- private static final StringManager sm =
- StringManager.getManager(Constants.Package);
+ private static final StringManager sm = StringManager.getManager(NamingResourcesImpl.class);
private volatile boolean resourceRequireExplicitRegistration = false;
@@ -173,7 +172,7 @@ public class NamingResourcesImpl extends LifecycleMBeanBase
/**
- * Get the container with which the naming resources are associated.
+ * @return the container with which the naming resources are associated.
*/
@Override
public Object getContainer() {
@@ -183,6 +182,7 @@ public class NamingResourcesImpl extends LifecycleMBeanBase
/**
* Set the container with which the naming resources are associated.
+ * @param container the associated with the resources
*/
public void setContainer(Object container) {
this.container = container;
@@ -191,6 +191,7 @@ public class NamingResourcesImpl extends LifecycleMBeanBase
/**
* Set the transaction object.
+ * @param transaction the transaction descriptor
*/
public void setTransaction(ContextTransaction transaction) {
this.transaction = transaction;
@@ -198,7 +199,7 @@ public class NamingResourcesImpl extends LifecycleMBeanBase
/**
- * Get the transaction object.
+ * @return the transaction object.
*/
public ContextTransaction getTransaction() {
return transaction;
@@ -480,7 +481,7 @@ public class NamingResourcesImpl extends LifecycleMBeanBase
/**
- * Return the EJB resource reference with the specified name, if any;
+ * @return the EJB resource reference with the specified name, if any;
* otherwise, return <code>null</code>.
*
* @param name Name of the desired EJB resource reference
@@ -495,7 +496,7 @@ public class NamingResourcesImpl extends LifecycleMBeanBase
/**
- * Return the defined EJB resource references for this application.
+ * @return the defined EJB resource references for this application.
* If there are none, a zero-length array is returned.
*/
public ContextEjb[] findEjbs() {
@@ -509,7 +510,7 @@ public class NamingResourcesImpl extends LifecycleMBeanBase
/**
- * Return the environment entry with the specified name, if any;
+ * @return the environment entry with the specified name, if any;
* otherwise, return <code>null</code>.
*
* @param name Name of the desired environment entry
@@ -524,7 +525,7 @@ public class NamingResourcesImpl extends LifecycleMBeanBase
/**
- * Return the set of defined environment entries for this web
+ * @return the set of defined environment entries for this web
* application. If none have been defined, a zero-length array
* is returned.
*/
@@ -539,7 +540,7 @@ public class NamingResourcesImpl extends LifecycleMBeanBase
/**
- * Return the local EJB resource reference with the specified name, if any;
+ * @return the local EJB resource reference with the specified name, if any;
* otherwise, return <code>null</code>.
*
* @param name Name of the desired EJB resource reference
@@ -554,7 +555,7 @@ public class NamingResourcesImpl extends LifecycleMBeanBase
/**
- * Return the defined local EJB resource references for this application.
+ * @return the defined local EJB resource references for this application.
* If there are none, a zero-length array is returned.
*/
public ContextLocalEjb[] findLocalEjbs() {
@@ -568,7 +569,7 @@ public class NamingResourcesImpl extends LifecycleMBeanBase
/**
- * Return the message destination reference with the specified name,
+ * @return the message destination reference with the specified name,
* if any; otherwise, return <code>null</code>.
*
* @param name Name of the desired message destination reference
@@ -583,7 +584,7 @@ public class NamingResourcesImpl extends LifecycleMBeanBase
/**
- * Return the defined message destination references for this application.
+ * @return the defined message destination references for this application.
* If there are none, a zero-length array is returned.
*/
public MessageDestinationRef[] findMessageDestinationRefs() {
@@ -598,7 +599,7 @@ public class NamingResourcesImpl extends LifecycleMBeanBase
/**
- * Return the resource reference with the specified name, if any;
+ * @return the resource reference with the specified name, if any;
* otherwise return <code>null</code>.
*
* @param name Name of the desired resource reference
@@ -613,7 +614,7 @@ public class NamingResourcesImpl extends LifecycleMBeanBase
/**
- * Return the resource link with the specified name, if any;
+ * @return the resource link with the specified name, if any;
* otherwise return <code>null</code>.
*
* @param name Name of the desired resource link
@@ -628,7 +629,7 @@ public class NamingResourcesImpl extends LifecycleMBeanBase
/**
- * Return the defined resource links for this application. If
+ * @return the defined resource links for this application. If
* none have been defined, a zero-length array is returned.
*/
public ContextResourceLink[] findResourceLinks() {
@@ -643,7 +644,7 @@ public class NamingResourcesImpl extends LifecycleMBeanBase
/**
- * Return the defined resource references for this application. If
+ * @return the defined resource references for this application. If
* none have been defined, a zero-length array is returned.
*/
public ContextResource[] findResources() {
@@ -657,7 +658,7 @@ public class NamingResourcesImpl extends LifecycleMBeanBase
/**
- * Return the resource environment reference type for the specified
+ * @return the resource environment reference type for the specified
* name, if any; otherwise return <code>null</code>.
*
* @param name Name of the desired resource environment reference
@@ -672,7 +673,7 @@ public class NamingResourcesImpl extends LifecycleMBeanBase
/**
- * Return the set of resource environment reference names for this
+ * @return the set of resource environment reference names for this
* web application. If none have been specified, a zero-length
* array is returned.
*/
@@ -687,7 +688,7 @@ public class NamingResourcesImpl extends LifecycleMBeanBase
/**
- * Return the web service reference for the specified
+ * @return the web service reference for the specified
* name, if any; otherwise return <code>null</code>.
*
* @param name Name of the desired web service
@@ -702,7 +703,7 @@ public class NamingResourcesImpl extends LifecycleMBeanBase
/**
- * Return the defined web service references for this application. If
+ * @return the defined web service references for this application. If
* none have been defined, a zero-length array is returned.
*/
public ContextService[] findServices() {
diff --git a/java/org/apache/catalina/deploy/mbeans-descriptors.xml b/java/org/apache/catalina/deploy/mbeans-descriptors.xml
index fc1cbe1..65eb3a9 100644
--- a/java/org/apache/catalina/deploy/mbeans-descriptors.xml
+++ b/java/org/apache/catalina/deploy/mbeans-descriptors.xml
@@ -1,4 +1,4 @@
-<?xml version="1.0"?>
+<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
diff --git a/java/org/apache/catalina/filters/Constants.java b/java/org/apache/catalina/filters/Constants.java
index 8c743e2..4fe41cd 100644
--- a/java/org/apache/catalina/filters/Constants.java
+++ b/java/org/apache/catalina/filters/Constants.java
@@ -25,8 +25,6 @@ package org.apache.catalina.filters;
*/
public final class Constants {
- public static final String Package = "org.apache.catalina.filters";
-
public static final String CSRF_NONCE_SESSION_ATTR_NAME =
"org.apache.catalina.filters.CSRF_NONCE";
diff --git a/java/org/apache/catalina/filters/CorsFilter.java b/java/org/apache/catalina/filters/CorsFilter.java
index 1ed64bf..e9c3c75 100644
--- a/java/org/apache/catalina/filters/CorsFilter.java
+++ b/java/org/apache/catalina/filters/CorsFilter.java
@@ -79,14 +79,14 @@ import org.apache.tomcat.util.res.StringManager;
public final class CorsFilter implements Filter {
private static final Log log = LogFactory.getLog(CorsFilter.class);
- private static final StringManager sm = StringManager.getManager(Constants.Package);
+ private static final StringManager sm = StringManager.getManager(CorsFilter.class);
/**
* A {@link Collection} of origins consisting of zero or more origins that
* are allowed access to the resource.
*/
- private final Collection<String> allowedOrigins;
+ private final Collection<String> allowedOrigins = new HashSet<>();
/**
* Determines if any origin is allowed to make request.
@@ -97,20 +97,20 @@ public final class CorsFilter implements Filter {
* A {@link Collection} of methods consisting of zero or more methods that
* are supported by the resource.
*/
- private final Collection<String> allowedHttpMethods;
+ private final Collection<String> allowedHttpMethods = new HashSet<>();
/**
* A {@link Collection} of headers consisting of zero or more header field
* names that are supported by the resource.
*/
- private final Collection<String> allowedHttpHeaders;
+ private final Collection<String> allowedHttpHeaders = new HashSet<>();
/**
* A {@link Collection} of exposed headers consisting of zero or more header
* field names of headers other than the simple response headers that the
* resource might use and can be exposed.
*/
- private final Collection<String> exposedHeaders;
+ private final Collection<String> exposedHeaders = new HashSet<>();
/**
* A supports credentials flag that indicates whether the resource supports
@@ -131,14 +131,6 @@ public final class CorsFilter implements Filter {
private boolean decorateRequest;
- public CorsFilter() {
- this.allowedOrigins = new HashSet<>();
- this.allowedHttpMethods = new HashSet<>();
- this.allowedHttpHeaders = new HashSet<>();
- this.exposedHeaders = new HashSet<>();
- }
-
-
@Override
public void doFilter(final ServletRequest servletRequest,
final ServletResponse servletResponse, final FilterChain filterChain)
@@ -208,25 +200,22 @@ public final class CorsFilter implements Filter {
String configDecorateRequest = filterConfig
.getInitParameter(PARAM_CORS_REQUEST_DECORATE);
- parseAndStore(configAllowedOrigins, configAllowedHttpMethods,
- configAllowedHttpHeaders, configExposedHeaders,
- configSupportsCredentials, configPreflightMaxAge,
- configDecorateRequest);
- }
+ parseAndStore(configAllowedOrigins, configAllowedHttpMethods,
+ configAllowedHttpHeaders, configExposedHeaders,
+ configSupportsCredentials, configPreflightMaxAge,
+ configDecorateRequest);
+ }
}
/**
* Handles a CORS request of type {@link CORSRequestType}.SIMPLE.
*
- * @param request
- * The {@link HttpServletRequest} object.
- * @param response
- * The {@link HttpServletResponse} object.
- * @param filterChain
- * The {@link FilterChain} object.
- * @throws IOException
- * @throws ServletException
+ * @param request The {@link HttpServletRequest} object.
+ * @param response The {@link HttpServletResponse} object.
+ * @param filterChain The {@link FilterChain} object.
+ * @throws IOException an IO error occurred
+ * @throws ServletException Servlet error propagation
* @see <a href="http://www.w3.org/TR/cors/#resource-requests">Simple
* Cross-Origin Request, Actual Request, and Redirects</a>
*/
@@ -305,14 +294,11 @@ public final class CorsFilter implements Filter {
/**
* Handles CORS pre-flight request.
*
- * @param request
- * The {@link HttpServletRequest} object.
- * @param response
- * The {@link HttpServletResponse} object.
- * @param filterChain
- * The {@link FilterChain} object.
- * @throws IOException
- * @throws ServletException
+ * @param request The {@link HttpServletRequest} object.
+ * @param response The {@link HttpServletResponse} object.
+ * @param filterChain The {@link FilterChain} object.
+ * @throws IOException an IO error occurred
+ * @throws ServletException Servlet error propagation
*/
protected void handlePreflightCORS(final HttpServletRequest request,
final HttpServletResponse response, final FilterChain filterChain)
@@ -420,14 +406,11 @@ public final class CorsFilter implements Filter {
* it is not a cross-origin request. This implementation, just forwards the
* request down the filter chain.
*
- * @param request
- * The {@link HttpServletRequest} object.
- * @param response
- * The {@link HttpServletResponse} object.
- * @param filterChain
- * The {@link FilterChain} object.
- * @throws IOException
- * @throws ServletException
+ * @param request The {@link HttpServletRequest} object.
+ * @param response The {@link HttpServletResponse} object.
+ * @param filterChain The {@link FilterChain} object.
+ * @throws IOException an IO error occurred
+ * @throws ServletException Servlet error propagation
*/
private void handleNonCORS(final HttpServletRequest request,
final HttpServletResponse response, final FilterChain filterChain)
@@ -440,12 +423,9 @@ public final class CorsFilter implements Filter {
/**
* Handles a CORS request that violates specification.
*
- * @param request
- * The {@link HttpServletRequest} object.
- * @param response
- * The {@link HttpServletResponse} object.
- * @param filterChain
- * The {@link FilterChain} object.
+ * @param request The {@link HttpServletRequest} object.
+ * @param response The {@link HttpServletResponse} object.
+ * @param filterChain The {@link FilterChain} object.
*/
private void handleInvalidCORS(final HttpServletRequest request,
final HttpServletResponse response, final FilterChain filterChain) {
@@ -494,10 +474,8 @@ public final class CorsFilter implements Filter {
* 'Access-Control-Request-Headers' header, for pre-flight request.</li>
* </ul>
*
- * @param request
- * The {@link HttpServletRequest} object.
- * @param corsRequestType
- * The {@link CORSRequestType} object.
+ * @param request The {@link HttpServletRequest} object.
+ * @param corsRequestType The {@link CORSRequestType} object.
*/
protected static void decorateCORSProperties(
final HttpServletRequest request,
@@ -566,10 +544,8 @@ public final class CorsFilter implements Filter {
* Joins elements of {@link Set} into a string, where each element is
* separated by the provided separator.
*
- * @param elements
- * The {@link Set} containing elements to join together.
- * @param joinSeparator
- * The character to be used for separating elements.
+ * @param elements The {@link Set} containing elements to join together.
+ * @param joinSeparator The character to be used for separating elements.
* @return The joined {@link String}; <code>null</code> if elements
* {@link Set} is null.
*/
@@ -603,7 +579,8 @@ public final class CorsFilter implements Filter {
/**
* Determines the request type.
*
- * @param request
+ * @param request The HTTP Servlet request
+ * @return the CORS type
*/
protected CORSRequestType checkRequestType(final HttpServletRequest request) {
CORSRequestType requestType = CORSRequestType.INVALID_CORS;
@@ -691,7 +668,7 @@ public final class CorsFilter implements Filter {
}
- /*
+ /**
* Return the lower case, trimmed value of the media type from the content
* type.
*/
@@ -849,7 +826,8 @@ public final class CorsFilter implements Filter {
* <li>Origin should be a valid {@link URI}</li>
* </ul>
*
- * @param origin
+ * @param origin The origin URI
+ * @return <code>true</code> if the origin was valid
* @see <a href="http://tools.ietf.org/html/rfc952">RFC952</a>
*/
protected static boolean isValidOrigin(String origin) {
@@ -893,7 +871,9 @@ public final class CorsFilter implements Filter {
/**
- * Returns a {@link Set} of headers that should be exposed by browser.
+ * Obtain the headers to expose.
+ *
+ * @return the headers that should be exposed by browser.
*/
public Collection<String> getExposedHeaders() {
return exposedHeaders;
@@ -902,6 +882,9 @@ public final class CorsFilter implements Filter {
/**
* Determines is supports credentials is enabled.
+ *
+ * @return <code>true</code> if the use of credentials is supported
+ * otherwise <code>false</code>
*/
public boolean isSupportsCredentials() {
return supportsCredentials;
@@ -1080,62 +1063,6 @@ public final class CorsFilter implements Filter {
}
/**
- * {@link Collection} of HTTP methods. Case sensitive.
- * @deprecated Not used. Will be removed in Tomcat 8.5.x onwards.
- */
- @Deprecated
- public static final Collection<String> HTTP_METHODS =
- new HashSet<>(Arrays.asList("OPTIONS", "GET", "HEAD", "POST", "PUT",
- "DELETE", "TRACE", "CONNECT"));
-
- /**
- * {@link Collection} of non-simple HTTP methods. Case sensitive.
- * @deprecated Not used. Will be removed in Tomcat 8.5.x onwards. All HTTP
- * methods not in {@link #SIMPLE_HTTP_METHODS} are assumed to be
- * non-simple.
- */
- @Deprecated
- public static final Collection<String> COMPLEX_HTTP_METHODS =
- new HashSet<>(Arrays.asList("PUT", "DELETE", "TRACE", "CONNECT"));
- /**
- * {@link Collection} of Simple HTTP methods. Case sensitive.
- *
- * @see <a href="http://www.w3.org/TR/cors/#terminology"
- * >http://www.w3.org/TR/cors/#terminology</a>
- *
- * @deprecated Unused. Will be removed in Tomcat 8.5.x onwards.
- */
- @Deprecated
- public static final Collection<String> SIMPLE_HTTP_METHODS =
- new HashSet<>(Arrays.asList("GET", "POST", "HEAD"));
-
- /**
- * {@link Collection} of Simple HTTP request headers. Case in-sensitive.
- *
- * @see <a href="http://www.w3.org/TR/cors/#terminology"
- * >http://www.w3.org/TR/cors/#terminology</a>
- *
- * @deprecated Unused. Will be removed in Tomcat 8.5.x onwards.
- */
- @Deprecated
- public static final Collection<String> SIMPLE_HTTP_REQUEST_HEADERS =
- new HashSet<>(Arrays.asList("Accept", "Accept-Language",
- "Content-Language"));
-
- /**
- * {@link Collection} of Simple HTTP request headers. Case in-sensitive.
- *
- * @see <a href="http://www.w3.org/TR/cors/#terminology"
- * >http://www.w3.org/TR/cors/#terminology</a>
- *
- * @deprecated Unused. Will be removed in Tomcat 8.5.x onwards.
- */
- @Deprecated
- public static final Collection<String> SIMPLE_HTTP_RESPONSE_HEADERS =
- new HashSet<>(Arrays.asList("Cache-Control", "Content-Language",
- "Content-Type", "Expires", "Last-Modified", "Pragma"));
-
- /**
* {@link Collection} of media type values for the Content-Type header that
* will be treated as 'simple'. Note media-type values are compared ignoring
* parameters and in a case-insensitive manner.
@@ -1190,37 +1117,39 @@ public final class CorsFilter implements Filter {
// ----------------------------------------Filter Config Init param-name(s)
/**
- * Key to retrieve allowed origins from {@link FilterConfig}.
+ * Key to retrieve allowed origins from {@link javax.servlet.FilterConfig}.
*/
public static final String PARAM_CORS_ALLOWED_ORIGINS =
"cors.allowed.origins";
/**
- * Key to retrieve support credentials from {@link FilterConfig}.
+ * Key to retrieve support credentials from
+ * {@link javax.servlet.FilterConfig}.
*/
public static final String PARAM_CORS_SUPPORT_CREDENTIALS =
"cors.support.credentials";
/**
- * Key to retrieve exposed headers from {@link FilterConfig}.
+ * Key to retrieve exposed headers from {@link javax.servlet.FilterConfig}.
*/
public static final String PARAM_CORS_EXPOSED_HEADERS =
"cors.exposed.headers";
/**
- * Key to retrieve allowed headers from {@link FilterConfig}.
+ * Key to retrieve allowed headers from {@link javax.servlet.FilterConfig}.
*/
public static final String PARAM_CORS_ALLOWED_HEADERS =
"cors.allowed.headers";
/**
- * Key to retrieve allowed methods from {@link FilterConfig}.
+ * Key to retrieve allowed methods from {@link javax.servlet.FilterConfig}.
*/
public static final String PARAM_CORS_ALLOWED_METHODS =
"cors.allowed.methods";
/**
- * Key to retrieve preflight max age from {@link FilterConfig}.
+ * Key to retrieve preflight max age from
+ * {@link javax.servlet.FilterConfig}.
*/
public static final String PARAM_CORS_PREFLIGHT_MAXAGE =
"cors.preflight.maxage";
diff --git a/java/org/apache/catalina/filters/CsrfPreventionFilterBase.java b/java/org/apache/catalina/filters/CsrfPreventionFilterBase.java
index ada2121..ab642f4 100644
--- a/java/org/apache/catalina/filters/CsrfPreventionFilterBase.java
+++ b/java/org/apache/catalina/filters/CsrfPreventionFilterBase.java
@@ -43,7 +43,7 @@ public abstract class CsrfPreventionFilterBase extends FilterBase {
}
/**
- * Return response status code that is used to reject denied request.
+ * @return response status code that is used to reject denied request.
*/
public int getDenyStatus() {
return denyStatus;
@@ -95,6 +95,8 @@ public abstract class CsrfPreventionFilterBase extends FilterBase {
* Generate a once time token (nonce) for authenticating subsequent
* requests. The nonce generation is a simplified version of
* ManagerBase.generateSessionId().
+ *
+ * @return the generated nonce
*/
protected String generateNonce() {
byte random[] = new byte[16];
diff --git a/java/org/apache/catalina/filters/ExpiresFilter.java b/java/org/apache/catalina/filters/ExpiresFilter.java
index b6a37fc..367b076 100644
--- a/java/org/apache/catalina/filters/ExpiresFilter.java
+++ b/java/org/apache/catalina/filters/ExpiresFilter.java
@@ -1085,6 +1085,7 @@ public class ExpiresFilter extends FilterBase {
/**
* Convert a given comma delimited list of strings into an array of String
*
+ * @param commaDelimitedStrings the string to be split
* @return array of patterns (non {@code null})
*/
protected static String[] commaDelimitedListToStringArray(
@@ -1094,8 +1095,10 @@ public class ExpiresFilter extends FilterBase {
}
/**
- * Return {@code true} if the given {@code str} contains the given
+ * @return {@code true} if the given {@code str} contains the given
* {@code searchStr}.
+ * @param str String that will be searched
+ * @param searchStr The substring to search
*/
protected static boolean contains(String str, String searchStr) {
if (str == null || searchStr == null) {
@@ -1106,6 +1109,8 @@ public class ExpiresFilter extends FilterBase {
/**
* Convert an array of ints into a comma delimited string
+ * @param ints The int array
+ * @return a comma separated string
*/
protected static String intsToCommaDelimitedString(int[] ints) {
if (ints == null) {
@@ -1124,7 +1129,8 @@ public class ExpiresFilter extends FilterBase {
}
/**
- * Return {@code true} if the given {@code str} is
+ * @param str The String to check
+ * @return {@code true} if the given {@code str} is
* {@code null} or has a zero characters length.
*/
protected static boolean isEmpty(String str) {
@@ -1132,7 +1138,8 @@ public class ExpiresFilter extends FilterBase {
}
/**
- * Return {@code true} if the given {@code str} has at least one
+ * @param str The String to check
+ * @return {@code true} if the given {@code str} has at least one
* character (can be a withespace).
*/
protected static boolean isNotEmpty(String str) {
@@ -1140,7 +1147,7 @@ public class ExpiresFilter extends FilterBase {
}
/**
- * Return {@code true} if the given {@code string} starts with the
+ * @return {@code true} if the given {@code string} starts with the
* given {@code prefix} ignoring case.
*
* @param string
@@ -1160,7 +1167,7 @@ public class ExpiresFilter extends FilterBase {
}
/**
- * Return the subset of the given {@code str} that is before the first
+ * @return the subset of the given {@code str} that is before the first
* occurence of the given {@code separator}. Return {@code null}
* if the given {@code str} or the given {@code separator} is
* null. Return and empty string if the {@code separator} is empty.
@@ -1254,6 +1261,8 @@ public class ExpiresFilter extends FilterBase {
* {@code protected} for extension.
* </p>
*
+ * @param response The Servlet response
+ * @return the expiration date
* @see HttpServletResponse#getContentType()
*/
protected Date getExpirationDate(XHttpServletResponse response) {
@@ -1330,6 +1339,9 @@ public class ExpiresFilter extends FilterBase {
* <p>
* {@code protected} for extension.
* </p>
+ * @param configuration The parsed expires
+ * @param response The Servlet response
+ * @return the expiration date
*/
protected Date getExpirationDate(ExpiresConfiguration configuration,
XHttpServletResponse response) {
@@ -1410,10 +1422,12 @@ public class ExpiresFilter extends FilterBase {
}
/**
- *
* <p>
* {@code protected} for extension.
* </p>
+ * @param request The Servlet request
+ * @param response The Servlet response
+ * @return <code>true</code> if an expire header may be added
*/
protected boolean isEligibleToExpirationHeaderGeneration(
HttpServletRequest request, XHttpServletResponse response) {
@@ -1462,6 +1476,8 @@ public class ExpiresFilter extends FilterBase {
* {@link HttpServletResponse#getContentType()} costs {@code String}
* objects instantiations (as of Tomcat 7).
* </p>
+ * @param request The Servlet request
+ * @param response The Servlet response
*/
public void onBeforeWriteResponseBody(HttpServletRequest request,
XHttpServletResponse response) {
@@ -1503,7 +1519,8 @@ public class ExpiresFilter extends FilterBase {
* '{@code access plus 1 month 15 days 2 hours}' or
* '{@code modification 1 day 2 hours 5 seconds}'
*
- * @param inputLine
+ * @param inputLine the input
+ * @return the parsed expires
*/
protected ExpiresConfiguration parseExpiresConfiguration(String inputLine) {
String line = inputLine.trim();
diff --git a/java/org/apache/catalina/filters/FailedRequestFilter.java b/java/org/apache/catalina/filters/FailedRequestFilter.java
index 301daf0..13a6128 100644
--- a/java/org/apache/catalina/filters/FailedRequestFilter.java
+++ b/java/org/apache/catalina/filters/FailedRequestFilter.java
@@ -25,9 +25,6 @@ import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;
import org.apache.catalina.Globals;
-import org.apache.catalina.comet.CometEvent;
-import org.apache.catalina.comet.CometFilter;
-import org.apache.catalina.comet.CometFilterChain;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.http.Parameters.FailReason;
@@ -44,7 +41,7 @@ import org.apache.tomcat.util.http.Parameters.FailReason;
* <code>request.getInputStream()</code> and <code>request.getReader()</code>,
* if requests parsed by them do not use standard value for content mime-type.
*/
-public class FailedRequestFilter extends FilterBase implements CometFilter {
+public class FailedRequestFilter extends FilterBase {
private static final Log log = LogFactory.getLog(FailedRequestFilter.class);
@@ -97,19 +94,6 @@ public class FailedRequestFilter extends FilterBase implements CometFilter {
chain.doFilter(request, response);
}
- @Override
- public void doFilterEvent(CometEvent event, CometFilterChain chain)
- throws IOException, ServletException {
- if (event.getEventType() == CometEvent.EventType.BEGIN
- && !isGoodRequest(event.getHttpServletRequest())) {
- event.getHttpServletResponse().sendError(
- HttpServletResponse.SC_BAD_REQUEST);
- event.close();
- return;
- }
- chain.doFilterEvent(event);
- }
-
private boolean isGoodRequest(ServletRequest request) {
// Trigger parsing of parameters
request.getParameter("none");
diff --git a/java/org/apache/catalina/filters/FilterBase.java b/java/org/apache/catalina/filters/FilterBase.java
index 3c41c2e..be23ad3 100644
--- a/java/org/apache/catalina/filters/FilterBase.java
+++ b/java/org/apache/catalina/filters/FilterBase.java
@@ -35,7 +35,7 @@ import org.apache.tomcat.util.res.StringManager;
*/
public abstract class FilterBase implements Filter {
- protected static final StringManager sm = StringManager.getManager(Constants.Package);
+ protected static final StringManager sm = StringManager.getManager(FilterBase.class);
protected abstract Log getLogger();
diff --git a/java/org/apache/catalina/filters/LocalStrings.properties b/java/org/apache/catalina/filters/LocalStrings.properties
index 5c4c792..0c20c26 100644
--- a/java/org/apache/catalina/filters/LocalStrings.properties
+++ b/java/org/apache/catalina/filters/LocalStrings.properties
@@ -29,7 +29,7 @@ expiresFilter.noExpirationConfigured=Request "{0}" with response status "{1}" co
expiresFilter.setExpirationDate=Request "{0}" with response status "{1}" content-type "{2}", set expiration date {3}
expiresFilter.startingPointNotFound=Starting point (access|now|modification|a<seconds>|m<seconds>) not found in directive "{0}"
expiresFilter.startingPointInvalid=Invalid starting point (access|now|modification|a<seconds>|m<seconds>) "{0}" in directive "{1}"
-expiresFilter.responseAlreadyCommited=Request "{0}", can not apply ExpiresFilter on already committed response.
+expiresFilter.responseAlreadyCommited=Request "{0}", cannot apply ExpiresFilter on already committed response.
expiresFilter.noExpirationConfiguredForContentType=No Expires configuration found for content-type "{0}"
expiresFilter.useMatchingConfiguration=Use {0} matching "{1}" for content-type "{2}" returns {3}
expiresFilter.useDefaultConfiguration=Use default {0} for content-type "{1}" returns {2}
diff --git a/java/org/apache/catalina/filters/RemoteAddrFilter.java b/java/org/apache/catalina/filters/RemoteAddrFilter.java
index 8fe9fc6..4da7d2c 100644
--- a/java/org/apache/catalina/filters/RemoteAddrFilter.java
+++ b/java/org/apache/catalina/filters/RemoteAddrFilter.java
@@ -16,7 +16,6 @@
*/
package org.apache.catalina.filters;
-
import java.io.IOException;
import javax.servlet.FilterChain;
@@ -24,12 +23,9 @@ import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
-import org.apache.catalina.comet.CometEvent;
-import org.apache.catalina.comet.CometFilterChain;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
-
/**
* Concrete implementation of <code>RequestFilter</code> that filters
* based on the string representation of the remote client's IP address.
@@ -72,24 +68,6 @@ public final class RemoteAddrFilter extends RequestFilter {
}
- /**
- * Extract the desired request property, and pass it (along with the comet
- * event and filter chain) to the protected <code>process()</code> method
- * to perform the actual filtering.
- *
- * @param event The comet event to be processed
- * @param chain The filter chain for this event
- *
- * @exception IOException if an input/output error occurs
- * @exception ServletException if a servlet error occurs
- */
- @Override
- public void doFilterEvent(CometEvent event, CometFilterChain chain)
- throws IOException, ServletException {
- processCometEvent(event.getHttpServletRequest().getRemoteAddr(),
- event, chain);
- }
-
@Override
protected Log getLogger() {
return log;
diff --git a/java/org/apache/catalina/filters/RemoteHostFilter.java b/java/org/apache/catalina/filters/RemoteHostFilter.java
index de8a327..0871a40 100644
--- a/java/org/apache/catalina/filters/RemoteHostFilter.java
+++ b/java/org/apache/catalina/filters/RemoteHostFilter.java
@@ -16,7 +16,6 @@
*/
package org.apache.catalina.filters;
-
import java.io.IOException;
import javax.servlet.FilterChain;
@@ -24,12 +23,9 @@ import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
-import org.apache.catalina.comet.CometEvent;
-import org.apache.catalina.comet.CometFilterChain;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
-
/**
* Concrete implementation of <code>RequestFilter</code> that filters
* based on the remote client's host name.
@@ -67,24 +63,6 @@ public final class RemoteHostFilter extends RequestFilter {
}
- /**
- * Extract the desired request property, and pass it (along with the comet
- * event and filter chain) to the protected <code>process()</code> method
- * to perform the actual filtering.
- *
- * @param event The comet event to be processed
- * @param chain The filter chain for this event
- *
- * @exception IOException if an input/output error occurs
- * @exception ServletException if a servlet error occurs
- */
- @Override
- public void doFilterEvent(CometEvent event, CometFilterChain chain)
- throws IOException, ServletException {
- processCometEvent(event.getHttpServletRequest().getRemoteHost(),
- event, chain);
- }
-
@Override
protected Log getLogger() {
return log;
diff --git a/java/org/apache/catalina/filters/RemoteIpFilter.java b/java/org/apache/catalina/filters/RemoteIpFilter.java
index b3d7a3a..1a7b1dc 100644
--- a/java/org/apache/catalina/filters/RemoteIpFilter.java
+++ b/java/org/apache/catalina/filters/RemoteIpFilter.java
@@ -43,6 +43,7 @@ import javax.servlet.http.HttpServletResponse;
import org.apache.catalina.AccessLog;
import org.apache.catalina.Globals;
+import org.apache.catalina.core.ApplicationPushBuilder;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
@@ -162,7 +163,6 @@ import org.apache.juli.logging.LogFactory;
* </tr>
* </table>
* <p>
- * <p>
* <strong>Regular expression vs. IP address blocks:</strong> <code>mod_remoteip</code> allows to use address blocks (e.g.
* <code>192.168/16</code>) to configure <code>RemoteIPInternalProxy</code> and <code>RemoteIPTrustedProxy</code> ; as the JVM doesn't have a
* library similar to <a
@@ -430,7 +430,7 @@ import org.apache.juli.logging.LogFactory;
* </table>
* <p>
* Note : <code>x-forwarded-by</code> holds the trusted proxy <code>proxy1</code>. <code>x-forwarded-by</code> holds
- * <code>140.211.11.130</code> because <code>untrusted-proxy</code> is not trusted and thus, we can not trust that
+ * <code>140.211.11.130</code> because <code>untrusted-proxy</code> is not trusted and thus, we cannot trust that
* <code>untrusted-proxy</code> is the actual remote ip. <code>request.remoteAddr</code> is <code>untrusted-proxy</code> that is an IP
* verified by <code>proxy1</code>.
* </p>
@@ -636,6 +636,10 @@ public class RemoteIpFilter implements Filter {
return url;
}
+
+ public ApplicationPushBuilder getPushBuilder() {
+ return new ApplicationPushBuilder(this);
+ }
}
@@ -672,6 +676,7 @@ public class RemoteIpFilter implements Filter {
/**
* Convert a given comma delimited list of regular expressions into an array of String
*
+ * @param commaDelimitedStrings The string to split
* @return array of patterns (non <code>null</code>)
*/
protected static String[] commaDelimitedListToStringArray(String commaDelimitedStrings) {
@@ -680,7 +685,10 @@ public class RemoteIpFilter implements Filter {
}
/**
- * Convert an array of strings in a comma delimited string
+ * Convert a list of strings in a comma delimited string.
+ *
+ * @param stringList List of strings
+ * @return concatenated string
*/
protected static String listToCommaDelimitedString(List<String> stringList) {
if (stringList == null) {
@@ -886,6 +894,7 @@ public class RemoteIpFilter implements Filter {
/**
* Wrap the incoming <code>request</code> in a {@link XForwardedRequest} if the http header <code>x-forwareded-for</code> is not empty.
+ * {@inheritDoc}
*/
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
@@ -996,12 +1005,13 @@ public class RemoteIpFilter implements Filter {
* <p>
* If <code>true</code>, the return values for both {@link
* ServletRequest#getLocalPort()} and {@link ServletRequest#getServerPort()}
- * wil be modified by this Filter rather than just
+ * will be modified by this Filter rather than just
* {@link ServletRequest#getServerPort()}.
* </p>
* <p>
* Default value : <code>false</code>
* </p>
+ * @param changeLocalPort The new flag value
*/
public void setChangeLocalPort(boolean changeLocalPort) {
this.changeLocalPort = changeLocalPort;
@@ -1015,6 +1025,7 @@ public class RemoteIpFilter implements Filter {
* <p>
* Default value : 80
* </p>
+ * @param httpServerPort The server port to use
*/
public void setHttpServerPort(int httpServerPort) {
this.httpServerPort = httpServerPort;
@@ -1027,6 +1038,7 @@ public class RemoteIpFilter implements Filter {
* <p>
* Default value : 443
* </p>
+ * @param httpsServerPort The server port to use
*/
public void setHttpsServerPort(int httpsServerPort) {
this.httpsServerPort = httpsServerPort;
@@ -1039,6 +1051,7 @@ public class RemoteIpFilter implements Filter {
* <p>
* Default value : 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}
* </p>
+ * @param internalProxies The regexp
*/
public void setInternalProxies(String internalProxies) {
if (internalProxies == null || internalProxies.length() == 0) {
@@ -1050,13 +1063,14 @@ public class RemoteIpFilter implements Filter {
/**
* <p>
- * Header that holds the incoming port, usally named
+ * Header that holds the incoming port, usually named
* <code>X-Forwarded-Port</code>. If <code>null</code>,
* {@link #httpServerPort} or {@link #httpsServerPort} will be used.
* </p>
* <p>
* Default value : <code>null</code>
* </p>
+ * @param portHeader The header name
*/
public void setPortHeader(String portHeader) {
this.portHeader = portHeader;
@@ -1064,12 +1078,13 @@ public class RemoteIpFilter implements Filter {
/**
* <p>
- * Header that holds the incoming protocol, usally named <code>X-Forwarded-Proto</code>. If <code>null</code>, request.scheme and
+ * Header that holds the incoming protocol, usually named <code>X-Forwarded-Proto</code>. If <code>null</code>, request.scheme and
* request.secure will not be modified.
* </p>
* <p>
* Default value : <code>null</code>
* </p>
+ * @param protocolHeader The header name
*/
public void setProtocolHeader(String protocolHeader) {
this.protocolHeader = protocolHeader;
@@ -1082,6 +1097,7 @@ public class RemoteIpFilter implements Filter {
* <p>
* Default value : <code>https</code>
* </p>
+ * @param protocolHeaderHttpsValue The header value
*/
public void setProtocolHeaderHttpsValue(String protocolHeaderHttpsValue) {
this.protocolHeaderHttpsValue = protocolHeaderHttpsValue;
@@ -1102,6 +1118,7 @@ public class RemoteIpFilter implements Filter {
* <p>
* Default value : <code>X-Forwarded-By</code>
* </p>
+ * @param proxiesHeader The header name
*/
public void setProxiesHeader(String proxiesHeader) {
this.proxiesHeader = proxiesHeader;
@@ -1117,6 +1134,7 @@ public class RemoteIpFilter implements Filter {
* <p>
* Default value : <code>X-Forwarded-For</code>
* </p>
+ * @param remoteIpHeader The header name
*/
public void setRemoteIpHeader(String remoteIpHeader) {
this.remoteIpHeader = remoteIpHeader;
@@ -1153,6 +1171,7 @@ public class RemoteIpFilter implements Filter {
* <p>
* Default value : empty list, no external proxy is trusted.
* </p>
+ * @param trustedProxies The trusted proxies regexp
*/
public void setTrustedProxies(String trustedProxies) {
if (trustedProxies == null || trustedProxies.length() == 0) {
diff --git a/java/org/apache/catalina/filters/RequestFilter.java b/java/org/apache/catalina/filters/RequestFilter.java
index 8e9c96c..a20a768 100644
--- a/java/org/apache/catalina/filters/RequestFilter.java
+++ b/java/org/apache/catalina/filters/RequestFilter.java
@@ -27,10 +27,6 @@ import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
-import org.apache.catalina.comet.CometEvent;
-import org.apache.catalina.comet.CometFilter;
-import org.apache.catalina.comet.CometFilterChain;
-
/**
* Implementation of a Filter that performs filtering based on comparing the
* appropriate request property (selected based on which subclass you choose
@@ -56,7 +52,7 @@ import org.apache.catalina.comet.CometFilterChain;
* <li>The request will be rejected with a "Forbidden" HTTP response.</li>
* </ul>
*/
-public abstract class RequestFilter extends FilterBase implements CometFilter {
+public abstract class RequestFilter extends FilterBase {
// ----------------------------------------------------- Instance Variables
@@ -87,7 +83,7 @@ public abstract class RequestFilter extends FilterBase implements CometFilter {
/**
- * Return the regular expression used to test for allowed requests for this
+ * @return the regular expression used to test for allowed requests for this
* Filter, if any; otherwise, return <code>null</code>.
*/
public String getAllow() {
@@ -114,7 +110,7 @@ public abstract class RequestFilter extends FilterBase implements CometFilter {
/**
- * Return the regular expression used to test for denied requests for this
+ * @return the regular expression used to test for denied requests for this
* Filter, if any; otherwise, return <code>null</code>.
*/
public String getDeny() {
@@ -141,7 +137,7 @@ public abstract class RequestFilter extends FilterBase implements CometFilter {
/**
- * Return response status code that is used to reject denied request.
+ * @return response status code that is used to reject denied request.
*/
public int getDenyStatus() {
return denyStatus;
@@ -150,6 +146,8 @@ public abstract class RequestFilter extends FilterBase implements CometFilter {
/**
* Set response status code that is used to reject denied request.
+ *
+ * @param denyStatus The status code for deny
*/
public void setDenyStatus(int denyStatus) {
this.denyStatus = denyStatus;
@@ -194,6 +192,7 @@ public abstract class RequestFilter extends FilterBase implements CometFilter {
* @param property The request property on which to filter
* @param request The servlet request to be processed
* @param response The servlet response to be processed
+ * @param chain The filter chain
*
* @exception IOException if an input/output error occurs
* @exception ServletException if a servlet error occurs
@@ -219,28 +218,6 @@ public abstract class RequestFilter extends FilterBase implements CometFilter {
/**
- * Perform the filtering that has been configured for this Filter, matching
- * against the specified request property.
- *
- * @param property The property to check against the allow/deny rules
- * @param event The comet event to be filtered
- * @param chain The comet filter chain
- * @exception IOException if an input/output error occurs
- * @exception ServletException if a servlet error occurs
- */
- protected void processCometEvent(String property, CometEvent event,
- CometFilterChain chain) throws IOException, ServletException {
- HttpServletResponse response = event.getHttpServletResponse();
-
- if (isAllowed(property)) {
- chain.doFilterEvent(event);
- } else {
- response.sendError(denyStatus);
- event.close();
- }
- }
-
- /**
* Process the allow and deny rules for the provided property.
*
* @param property The property to test against the allow and deny lists
diff --git a/java/org/apache/catalina/filters/SetCharacterEncodingFilter.java b/java/org/apache/catalina/filters/SetCharacterEncodingFilter.java
index 885d788..96e151d 100644
--- a/java/org/apache/catalina/filters/SetCharacterEncodingFilter.java
+++ b/java/org/apache/catalina/filters/SetCharacterEncodingFilter.java
@@ -128,6 +128,7 @@ public class SetCharacterEncodingFilter extends FilterBase {
* filter.
*
* @param request The servlet request we are processing
+ * @return the encoding that was configured
*/
protected String selectEncoding(ServletRequest request) {
return this.encoding;
diff --git a/java/org/apache/catalina/filters/WebdavFixFilter.java b/java/org/apache/catalina/filters/WebdavFixFilter.java
index 93608b8..e00d2ce 100644
--- a/java/org/apache/catalina/filters/WebdavFixFilter.java
+++ b/java/org/apache/catalina/filters/WebdavFixFilter.java
@@ -110,12 +110,11 @@ public class WebdavFixFilter implements Filter {
} else if (ua.startsWith(UA_MINIDIR_5_2_3790)) {
// XP 64-bit SP2
if (!"".equals(httpRequest.getContextPath())) {
- log(request,
- "XP-x64-SP2 clients only work with the root context");
+ log(httpRequest, "XP-x64-SP2 clients only work with the root context");
}
// Namespace issue maybe
// see http://greenbytes.de/tech/webdav/webdav-redirector-list.html
- log(request, "XP-x64-SP2 is known not to work with WebDAV Servlet");
+ log(httpRequest, "XP-x64-SP2 is known not to work with WebDAV Servlet");
chain.doFilter(request, response);
} else {
diff --git a/java/org/apache/catalina/ha/CatalinaCluster.java b/java/org/apache/catalina/ha/CatalinaCluster.java
index 1fe54cd..a9b79fb 100644
--- a/java/org/apache/catalina/ha/CatalinaCluster.java
+++ b/java/org/apache/catalina/ha/CatalinaCluster.java
@@ -48,21 +48,17 @@ public interface CatalinaCluster extends Cluster {
public void send(ClusterMessage msg, Member dest);
/**
- * Returns that cluster has members.
+ * @return <code>true</code> if the cluster has members.
*/
public boolean hasMembers();
/**
- * Returns all the members currently participating in the cluster.
- *
- * @return Member[]
+ * @return an array containing all the members currently participating in the cluster.
*/
public Member[] getMembers();
/**
- * Return the member that represents this node.
- *
- * @return Member
+ * @return the member that represents this node.
*/
public Member getLocalMember();
@@ -81,11 +77,25 @@ public interface CatalinaCluster extends Cluster {
*/
public Map<String,ClusterManager> getManagers();
+ /**
+ * Get Manager
+ * @param name The manager name
+ * @return The manager
+ */
public Manager getManager(String name);
+
+ /**
+ * Get a new cluster name for a manager.
+ * @param name Override name (optional)
+ * @param manager The manager
+ * @return the manager name in the cluster
+ */
public String getManagerName(String name, Manager manager);
+
public Valve[] getValves();
public void setChannel(Channel channel);
+
public Channel getChannel();
diff --git a/java/org/apache/catalina/ha/ClusterDeployer.java b/java/org/apache/catalina/ha/ClusterDeployer.java
index b2da59f..8763e66 100644
--- a/java/org/apache/catalina/ha/ClusterDeployer.java
+++ b/java/org/apache/catalina/ha/ClusterDeployer.java
@@ -37,7 +37,7 @@ public interface ClusterDeployer extends ChannelListener {
/**
* Stops the cluster deployer, the owning container will invoke this
- * @throws LifecycleException
+ * @throws LifecycleException Error stopping cluster deployer
*/
public void stop() throws LifecycleException;
diff --git a/java/org/apache/catalina/ha/ClusterListener.java b/java/org/apache/catalina/ha/ClusterListener.java
index 1c951f1..5ab903e 100644
--- a/java/org/apache/catalina/ha/ClusterListener.java
+++ b/java/org/apache/catalina/ha/ClusterListener.java
@@ -21,6 +21,8 @@ import java.io.Serializable;
import org.apache.catalina.tribes.ChannelListener;
import org.apache.catalina.tribes.Member;
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
/**
@@ -31,8 +33,7 @@ import org.apache.catalina.tribes.Member;
*/
public abstract class ClusterListener implements ChannelListener {
- private static final org.apache.juli.logging.Log log =
- org.apache.juli.logging.LogFactory.getLog(ClusterListener.class);
+ private static final Log log = LogFactory.getLog(ClusterListener.class);
//--Instance Variables--------------------------------------
diff --git a/java/org/apache/catalina/ha/ClusterManager.java b/java/org/apache/catalina/ha/ClusterManager.java
index ae896f7..f4d2c30 100644
--- a/java/org/apache/catalina/ha/ClusterManager.java
+++ b/java/org/apache/catalina/ha/ClusterManager.java
@@ -70,7 +70,7 @@ public interface ClusterManager extends Manager {
/**
* Set the name of the manager, at host /context name and at engine hostname+/context
- * @param name
+ * @param name The manager name
* @since 5.5.10
*/
public void setName(String name);
diff --git a/java/org/apache/catalina/ha/ClusterMessageBase.java b/java/org/apache/catalina/ha/ClusterMessageBase.java
index a39f150..794ebd6 100644
--- a/java/org/apache/catalina/ha/ClusterMessageBase.java
+++ b/java/org/apache/catalina/ha/ClusterMessageBase.java
@@ -22,18 +22,13 @@ public abstract class ClusterMessageBase implements ClusterMessage {
private static final long serialVersionUID = 1L;
- protected transient Member address;
private long timestamp;
+ protected transient Member address;
+
public ClusterMessageBase() {
// NO-OP
}
- /**
- * getAddress
- *
- * @return Member
- * TODO Implement this org.apache.catalina.ha.ClusterMessage method
- */
@Override
public Member getAddress() {
return address;
@@ -44,12 +39,6 @@ public abstract class ClusterMessageBase implements ClusterMessage {
return timestamp;
}
- /**
- * setAddress
- *
- * @param member Member
- * TODO Implement this org.apache.catalina.ha.ClusterMessage method
- */
@Override
public void setAddress(Member member) {
this.address = member;
diff --git a/java/org/apache/catalina/ha/authenticator/ClusterSingleSignOn.java b/java/org/apache/catalina/ha/authenticator/ClusterSingleSignOn.java
index f7535aa..cfa9f3f 100644
--- a/java/org/apache/catalina/ha/authenticator/ClusterSingleSignOn.java
+++ b/java/org/apache/catalina/ha/authenticator/ClusterSingleSignOn.java
@@ -54,8 +54,7 @@ import org.apache.tomcat.util.res.StringManager;
*/
public class ClusterSingleSignOn extends SingleSignOn implements ClusterValve, MapOwner {
- private static final StringManager sm =
- StringManager.getManager(ClusterSingleSignOn.class.getPackage().getName());
+ private static final StringManager sm = StringManager.getManager(ClusterSingleSignOn.class);
// -------------------------------------------------------------- Properties
diff --git a/java/org/apache/catalina/ha/authenticator/mbeans-descriptors.xml b/java/org/apache/catalina/ha/authenticator/mbeans-descriptors.xml
index 3f327c6..593bc52 100644
--- a/java/org/apache/catalina/ha/authenticator/mbeans-descriptors.xml
+++ b/java/org/apache/catalina/ha/authenticator/mbeans-descriptors.xml
@@ -1,4 +1,4 @@
-<?xml version="1.0"?>
+<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
diff --git a/java/org/apache/catalina/ha/backend/Sender.java b/java/org/apache/catalina/ha/backend/Sender.java
index 191a19c..8857445 100644
--- a/java/org/apache/catalina/ha/backend/Sender.java
+++ b/java/org/apache/catalina/ha/backend/Sender.java
@@ -18,19 +18,23 @@
package org.apache.catalina.ha.backend;
-/*
- * Interface to send data to proxies
- *
+/**
+ * Interface to send data to proxies.
*/
public interface Sender {
/**
* Set the configuration parameters
+ * @param config The heartbeat listener configuration
+ * @throws Exception An error occurred
*/
public void init(HeartbeatListener config) throws Exception;
/**
* Send the message to the proxies
+ * @param mess The message that will be sent
+ * @return <code>0</code> if no error occurred, <code>-1</code> otherwise
+ * @throws Exception An error occurred
*/
public int send(String mess) throws Exception;
}
diff --git a/java/org/apache/catalina/ha/backend/TcpSender.java b/java/org/apache/catalina/ha/backend/TcpSender.java
index 00d6100..af80512 100644
--- a/java/org/apache/catalina/ha/backend/TcpSender.java
+++ b/java/org/apache/catalina/ha/backend/TcpSender.java
@@ -180,6 +180,7 @@ public class TcpSender
/**
* Close connection.
+ * @param i The index of the connection that will be closed
*/
protected void close(int i) {
try {
diff --git a/java/org/apache/catalina/ha/context/ReplicatedContext.java b/java/org/apache/catalina/ha/context/ReplicatedContext.java
index 45e0748..34ac9f7 100644
--- a/java/org/apache/catalina/ha/context/ReplicatedContext.java
+++ b/java/org/apache/catalina/ha/context/ReplicatedContext.java
@@ -45,7 +45,7 @@ public class ReplicatedContext extends StandardContext implements MapOwner {
private int mapSendOptions = Channel.SEND_OPTIONS_DEFAULT;
private static final Log log = LogFactory.getLog( ReplicatedContext.class );
protected static final long DEFAULT_REPL_TIMEOUT = 15000;//15 seconds
- private static final StringManager sm = StringManager.getManager(ReplicatedContext.class.getPackage().getName());
+ private static final StringManager sm = StringManager.getManager(ReplicatedContext.class);
/**
* Start this component and implement the requirements
@@ -90,7 +90,7 @@ public class ReplicatedContext extends StandardContext implements MapOwner {
super.stopInternal();
- if ( map!=null && map instanceof ReplicatedMap) {
+ if (map instanceof ReplicatedMap) {
((ReplicatedMap<?, ?>) map).breakdown();
}
diff --git a/java/org/apache/catalina/ha/deploy/Constants.java b/java/org/apache/catalina/ha/deploy/Constants.java
deleted file mode 100644
index 9e85e27..0000000
--- a/java/org/apache/catalina/ha/deploy/Constants.java
+++ /dev/null
@@ -1,27 +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.ha.deploy;
-
-/**
- * Manifest constants for the <code>org.apache.catalina.ha.deploy</code>
- * package.
- */
-public class Constants {
-
- public static final String Package = "org.apache.catalina.ha.deploy";
-
-}
diff --git a/java/org/apache/catalina/ha/deploy/FarmWarDeployer.java b/java/org/apache/catalina/ha/deploy/FarmWarDeployer.java
index b3c64d7..a9ccebd 100644
--- a/java/org/apache/catalina/ha/deploy/FarmWarDeployer.java
+++ b/java/org/apache/catalina/ha/deploy/FarmWarDeployer.java
@@ -61,8 +61,7 @@ public class FarmWarDeployer extends ClusterListener
implements ClusterDeployer, FileChangeListener {
/*--Static Variables----------------------------------------*/
private static final Log log = LogFactory.getLog(FarmWarDeployer.class);
- private static final StringManager sm =
- StringManager.getManager(Constants.Package);
+ private static final StringManager sm = StringManager.getManager(FarmWarDeployer.class);
/*--Instance Variables--------------------------------------*/
protected boolean started = false;
@@ -288,12 +287,12 @@ public class FarmWarDeployer extends ClusterListener
}
/**
- * create factory for all transported war files
+ * Create factory for all transported war files
*
- * @param msg
+ * @param msg The file
* @return Factory for all app message (war files)
- * @throws java.io.FileNotFoundException
- * @throws java.io.IOException
+ * @throws java.io.FileNotFoundException Missing file error
+ * @throws java.io.IOException Other IO error
*/
public synchronized FileMessageFactory getFactory(FileMessage msg)
throws java.io.FileNotFoundException, java.io.IOException {
@@ -308,9 +307,9 @@ public class FarmWarDeployer extends ClusterListener
}
/**
- * Remove file (war) from messages)
+ * Remove file (war) from messages
*
- * @param msg
+ * @param msg The file
*/
public void removeFactory(FileMessage msg) {
fileFactories.remove(msg.getFileName());
@@ -321,8 +320,7 @@ public class FarmWarDeployer extends ClusterListener
* receiver to accept or decline the message, In the future, when messages
* get big, the accept method will only take a message header
*
- * @param msg
- * ClusterMessage
+ * @param msg ClusterMessage
* @return boolean - returns true to indicate that messageReceived should be
* invoked. If false is returned, the messageReceived method will
* not be invoked.
@@ -441,7 +439,7 @@ public class FarmWarDeployer extends ClusterListener
}
- /*
+ /**
* Modification from watchDir war detected!
*
* @see org.apache.catalina.ha.deploy.FileChangeListener#fileModified(File)
@@ -478,7 +476,7 @@ public class FarmWarDeployer extends ClusterListener
}
}
- /*
+ /**
* War remove from watchDir
*
* @see org.apache.catalina.ha.deploy.FileChangeListener#fileRemoved(File)
@@ -498,6 +496,8 @@ public class FarmWarDeployer extends ClusterListener
/**
* Invoke the remove method on the deployer.
+ * @param contextName The context to remove
+ * @throws Exception If an error occurs removing the context
*/
protected void remove(String contextName) throws Exception {
// TODO Handle remove also work dir content !
@@ -557,7 +557,7 @@ public class FarmWarDeployer extends ClusterListener
}
}
- /*
+ /**
* Call watcher to check for deploy changes
*
* @see org.apache.catalina.ha.ClusterDeployer#backgroundProcess()
@@ -579,7 +579,9 @@ public class FarmWarDeployer extends ClusterListener
/*--Deployer Operations ------------------------------------*/
/**
- * Invoke the check method on the deployer.
+ * Check a context for deployment operations.
+ * @param name The context name
+ * @throws Exception Error invoking the deployer
*/
protected void check(String name) throws Exception {
String[] params = { name };
@@ -588,7 +590,10 @@ public class FarmWarDeployer extends ClusterListener
}
/**
- * Invoke the check method on the deployer.
+ * Verified if a context is being services.
+ * @param name The context name
+ * @return <code>true</code> if the context is being serviced
+ * @throws Exception Error invoking the deployer
*/
protected boolean isServiced(String name) throws Exception {
String[] params = { name };
@@ -599,7 +604,9 @@ public class FarmWarDeployer extends ClusterListener
}
/**
- * Invoke the check method on the deployer.
+ * Mark a context as being services.
+ * @param name The context name
+ * @throws Exception Error invoking the deployer
*/
protected void addServiced(String name) throws Exception {
String[] params = { name };
@@ -608,7 +615,9 @@ public class FarmWarDeployer extends ClusterListener
}
/**
- * Invoke the check method on the deployer.
+ * Mark a context as no longer being serviced.
+ * @param name The context name
+ * @throws Exception Error invoking the deployer
*/
protected void removeServiced(String name) throws Exception {
String[] params = { name };
@@ -688,7 +697,7 @@ public class FarmWarDeployer extends ClusterListener
}
/**
- * Return the frequency of watcher checks.
+ * @return the frequency of watcher checks.
*/
public int getProcessDeployFrequency() {
diff --git a/java/org/apache/catalina/ha/deploy/FileMessageFactory.java b/java/org/apache/catalina/ha/deploy/FileMessageFactory.java
index 0e3ded0..5f999ec 100644
--- a/java/org/apache/catalina/ha/deploy/FileMessageFactory.java
+++ b/java/org/apache/catalina/ha/deploy/FileMessageFactory.java
@@ -47,8 +47,7 @@ import org.apache.tomcat.util.res.StringManager;
public class FileMessageFactory {
/*--Static Variables----------------------------------------*/
private static final Log log = LogFactory.getLog(FileMessageFactory.class);
- private static final StringManager sm =
- StringManager.getManager(Constants.Package);
+ private static final StringManager sm = StringManager.getManager(FileMessageFactory.class);
/**
* The number of bytes that we read from file
@@ -67,7 +66,7 @@ public class FileMessageFactory {
protected final boolean openForWrite;
/**
- * Once the factory is used, it can not be reused.
+ * Once the factory is used, it cannot be reused.
*/
protected boolean closed = false;
@@ -334,9 +333,8 @@ public class FileMessageFactory {
* asked to do. Invoked by readMessage/writeMessage before those methods
* proceed.
*
- * @param openForWrite
- * boolean
- * @throws IllegalArgumentException
+ * @param openForWrite The value to check
+ * @throws IllegalArgumentException if the state is not the expected one
*/
protected void checkState(boolean openForWrite)
throws IllegalArgumentException {
@@ -361,7 +359,7 @@ public class FileMessageFactory {
* @param args
* String[], args[0] - read from filename, args[1] write to
* filename
- * @throws Exception
+ * @throws Exception An error occurred
*/
public static void main(String[] args) throws Exception {
diff --git a/java/org/apache/catalina/ha/deploy/WarWatcher.java b/java/org/apache/catalina/ha/deploy/WarWatcher.java
index 3d4de71..bbec31a 100644
--- a/java/org/apache/catalina/ha/deploy/WarWatcher.java
+++ b/java/org/apache/catalina/ha/deploy/WarWatcher.java
@@ -37,8 +37,7 @@ public class WarWatcher {
/*--Static Variables----------------------------------------*/
private static final Log log = LogFactory.getLog(WarWatcher.class);
- private static final StringManager sm =
- StringManager.getManager(Constants.Package);
+ private static final StringManager sm = StringManager.getManager(WarWatcher.class);
/*--Instance Variables--------------------------------------*/
/**
@@ -113,7 +112,7 @@ public class WarWatcher {
/**
* add cluster war to the watcher state
- * @param warfile
+ * @param warfile The WAR to add
*/
protected void addWarInfo(File warfile) {
WarInfo info = currentStatus.get(warfile.getAbsolutePath());
diff --git a/java/org/apache/catalina/ha/deploy/mbeans-descriptors.xml b/java/org/apache/catalina/ha/deploy/mbeans-descriptors.xml
index 9fc61fb..3eb1e63 100644
--- a/java/org/apache/catalina/ha/deploy/mbeans-descriptors.xml
+++ b/java/org/apache/catalina/ha/deploy/mbeans-descriptors.xml
@@ -1,4 +1,4 @@
-<?xml version="1.0"?>
+<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
diff --git a/java/org/apache/catalina/ha/session/BackupManager.java b/java/org/apache/catalina/ha/session/BackupManager.java
index f674328..5fb90e8 100644
--- a/java/org/apache/catalina/ha/session/BackupManager.java
+++ b/java/org/apache/catalina/ha/session/BackupManager.java
@@ -44,7 +44,7 @@ public class BackupManager extends ClusterManagerBase
/**
* The string manager for this package.
*/
- protected static final StringManager sm = StringManager.getManager(Constants.Package);
+ protected static final StringManager sm = StringManager.getManager(BackupManager.class);
protected static final long DEFAULT_REPL_TIMEOUT = 15000;//15 seconds
@@ -100,7 +100,7 @@ public class BackupManager extends ClusterManagerBase
//=========================================================================
@Override
public void objectMadePrimary(Object key, Object value) {
- if (value!=null && value instanceof DeltaSession) {
+ if (value instanceof DeltaSession) {
DeltaSession session = (DeltaSession)value;
synchronized (session) {
session.access();
diff --git a/java/org/apache/catalina/ha/session/ClusterManagerBase.java b/java/org/apache/catalina/ha/session/ClusterManagerBase.java
index 25d8847..ede180a 100644
--- a/java/org/apache/catalina/ha/session/ClusterManagerBase.java
+++ b/java/org/apache/catalina/ha/session/ClusterManagerBase.java
@@ -76,37 +76,6 @@ public abstract class ClusterManagerBase extends ManagerBase implements ClusterM
this.notifyListenersOnReplication = notifyListenersOnReplication;
}
- /**
- * Return the string pattern used for including session attributes
- * to replication.
- *
- * @return the sessionAttributeFilter
- *
- * @deprecated Use {@link #getSessionAttributeNameFilter()}. Will be removed
- * in Tomcat 8.5.x
- */
- @Deprecated
- public String getSessionAttributeFilter() {
- return getSessionAttributeNameFilter();
- }
-
- /**
- * Set the pattern used for including session attributes to replication.
- * If not set, all session attributes will be eligible for replication.
- * <p>
- * E.g. <code>^(userName|sessionHistory)$</code>
- * </p>
- *
- * @param sessionAttributeFilter
- * the filter name pattern to set
- *
- * @deprecated Use {@link #setSessionAttributeNameFilter(String)}. Will be
- * removed in Tomcat 8.5.x
- */
- @Deprecated
- public void setSessionAttributeFilter(String sessionAttributeFilter) {
- setSessionAttributeNameFilter(sessionAttributeFilter);
- }
public boolean isRecordAllActions() {
return recordAllActions;
@@ -116,20 +85,6 @@ public abstract class ClusterManagerBase extends ManagerBase implements ClusterM
this.recordAllActions = recordAllActions;
}
- /**
- * Check whether the given session attribute should be distributed based on
- * attribute name only.
- *
- * @return true if the attribute should be distributed
- *
- * @deprecated Use {@link #willAttributeDistribute(String, Object)}. Will be
- * removed in Tomcat 8.5.x
- */
- @Deprecated
- public boolean willAttributeDistribute(String name) {
- return willAttributeDistribute(name, null);
- }
-
public static ClassLoader[] getClassLoaders(Context context) {
ClassLoader tccl = Thread.currentThread().getContextClassLoader();
diff --git a/java/org/apache/catalina/ha/session/ClusterSessionListener.java b/java/org/apache/catalina/ha/session/ClusterSessionListener.java
index 2934e27..e9f5d28 100644
--- a/java/org/apache/catalina/ha/session/ClusterSessionListener.java
+++ b/java/org/apache/catalina/ha/session/ClusterSessionListener.java
@@ -34,7 +34,7 @@ public class ClusterSessionListener extends ClusterListener {
private static final Log log =
LogFactory.getLog(ClusterSessionListener.class);
- private static final StringManager sm = StringManager.getManager(Constants.Package);
+ private static final StringManager sm = StringManager.getManager(ClusterSessionListener.class);
//--Constructor---------------------------------------------
@@ -53,7 +53,7 @@ public class ClusterSessionListener extends ClusterListener {
*/
@Override
public void messageReceived(ClusterMessage myobj) {
- if (myobj != null && myobj instanceof SessionMessage) {
+ if (myobj instanceof SessionMessage) {
SessionMessage msg = (SessionMessage) myobj;
String ctxname = msg.getContextName();
//check if the message is a EVT_GET_ALL_SESSIONS,
diff --git a/java/org/apache/catalina/ha/session/Constants.java b/java/org/apache/catalina/ha/session/Constants.java
deleted file mode 100644
index 27dcf47..0000000
--- a/java/org/apache/catalina/ha/session/Constants.java
+++ /dev/null
@@ -1,32 +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.ha.session;
-
-/**
- * Manifest constants for the <code>org.apache.catalina.ha.session</code>
- * package.
- *
- * @author Peter Rossbach Pero
- */
-
-public class Constants {
-
- public static final String Package = "org.apache.catalina.ha.session";
-
-}
diff --git a/java/org/apache/catalina/ha/session/DeltaManager.java b/java/org/apache/catalina/ha/session/DeltaManager.java
index d51a540..b47b356 100644
--- a/java/org/apache/catalina/ha/session/DeltaManager.java
+++ b/java/org/apache/catalina/ha/session/DeltaManager.java
@@ -36,6 +36,8 @@ import org.apache.catalina.ha.ClusterMessage;
import org.apache.catalina.session.ManagerBase;
import org.apache.catalina.tribes.Member;
import org.apache.catalina.tribes.io.ReplicationStream;
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.ExceptionUtils;
import org.apache.tomcat.util.res.StringManager;
@@ -57,19 +59,20 @@ import org.apache.tomcat.util.res.StringManager;
public class DeltaManager extends ClusterManagerBase{
// ---------------------------------------------------- Security Classes
- public final org.apache.juli.logging.Log log =
- org.apache.juli.logging.LogFactory.getLog(DeltaManager.class);
+ public final Log log = LogFactory.getLog(DeltaManager.class);
/**
* The string manager for this package.
*/
- protected static final StringManager sm = StringManager.getManager(Constants.Package);
+ protected static final StringManager sm = StringManager.getManager(DeltaManager.class);
// ----------------------------------------------------- Instance Variables
/**
* The descriptive name of this Manager implementation (for logging).
+ * @deprecated Unused. Will be removed in Tomcat 9
*/
+ @Deprecated
protected static final String managerName = "DeltaManager";
protected String name = null;
@@ -292,16 +295,15 @@ public class DeltaManager extends ClusterManagerBase{
}
/**
- * is session state transfered complete?
- *
+ * @return <code>true</code> if the state transfer is complete.
*/
public boolean getStateTransfered() {
return stateTransfered;
}
/**
- * set that state ist complete transfered
- * @param stateTransfered
+ * Set that state transfered is complete
+ * @param stateTransfered Fag value
*/
public void setStateTransfered(boolean stateTransfered) {
this.stateTransfered = stateTransfered;
@@ -316,7 +318,7 @@ public class DeltaManager extends ClusterManagerBase{
}
/**
- * @return Returns the sendAllSessionsWaitTime in msec
+ * @return the sendAllSessionsWaitTime in msec
*/
public int getSendAllSessionsWaitTime() {
return sendAllSessionsWaitTime;
@@ -330,7 +332,7 @@ public class DeltaManager extends ClusterManagerBase{
}
/**
- * @return Returns the stateTimestampDrop.
+ * @return the stateTimestampDrop.
*/
public boolean isStateTimestampDrop() {
return stateTimestampDrop;
@@ -345,7 +347,7 @@ public class DeltaManager extends ClusterManagerBase{
/**
*
- * @return Returns the sendAllSessions.
+ * @return the sendAllSessions.
*/
public boolean isSendAllSessions() {
return sendAllSessions;
@@ -359,7 +361,7 @@ public class DeltaManager extends ClusterManagerBase{
}
/**
- * @return Returns the sendAllSessionsSize.
+ * @return the sendAllSessionsSize.
*/
public int getSendAllSessionsSize() {
return sendAllSessionsSize;
@@ -373,7 +375,7 @@ public class DeltaManager extends ClusterManagerBase{
}
/**
- * @return Returns the notifySessionListenersOnReplication.
+ * @return the notifySessionListenersOnReplication.
*/
public boolean isNotifySessionListenersOnReplication() {
return notifySessionListenersOnReplication;
@@ -418,7 +420,8 @@ public class DeltaManager extends ClusterManagerBase{
* Create new session with check maxActiveSessions and send session creation
* to other cluster nodes.
*
- * @param distribute
+ * @param sessionId The session id that should be used for the session
+ * @param distribute <code>true</code> to replicate the new session
* @return The session
*/
public Session createSession(String sessionId, boolean distribute) {
@@ -433,9 +436,9 @@ public class DeltaManager extends ClusterManagerBase{
}
/**
- * Send create session evt to all backup node
- * @param sessionId
- * @param session
+ * Send create session event to all backup node
+ * @param sessionId The session id of the session
+ * @param session The session object
*/
protected void sendCreateSession(String sessionId, DeltaSession session) {
if(cluster.getMembers().length > 0 ) {
@@ -476,6 +479,7 @@ public class DeltaManager extends ClusterManagerBase{
/**
* Get new session class to be used in the doLoad() method.
+ * @return a new session
*/
protected DeltaSession getNewDeltaSession() {
return new DeltaSession(this);
@@ -525,6 +529,8 @@ public class DeltaManager extends ClusterManagerBase{
/**
* serialize sessionID
+ * @param sessionId Session id to serialize
+ * @return byte array with serialized session id
* @throws IOException if an input/output error occurs
*/
protected byte[] serializeSessionId(String sessionId) throws IOException {
@@ -538,6 +544,8 @@ public class DeltaManager extends ClusterManagerBase{
/**
* Load sessionID
+ * @param data serialized session id
+ * @return session id
* @throws IOException if an input/output error occurs
*/
protected String deserializeSessionId(byte[] data) throws IOException {
@@ -551,11 +559,11 @@ public class DeltaManager extends ClusterManagerBase{
* Load Deltarequest from external node
* Load the Class at container classloader
* @see DeltaRequest#readExternal(java.io.ObjectInput)
- * @param session
+ * @param session Corresponding session
* @param data message data
* @return The request
- * @throws ClassNotFoundException
- * @throws IOException
+ * @throws ClassNotFoundException Serialization error
+ * @throws IOException IO error with serialization
*/
protected DeltaRequest deserializeDeltaRequest(DeltaSession session, byte[] data)
throws ClassNotFoundException, IOException {
@@ -574,9 +582,10 @@ public class DeltaManager extends ClusterManagerBase{
* serialize DeltaRequest
* @see DeltaRequest#writeExternal(java.io.ObjectOutput)
*
- * @param deltaRequest
+ * @param session Associated session
+ * @param deltaRequest The request to serialize
* @return serialized delta request
- * @throws IOException
+ * @throws IOException IO error with serialization
*/
protected byte[] serializeDeltaRequest(DeltaSession session, DeltaRequest deltaRequest)
throws IOException {
@@ -592,6 +601,7 @@ public class DeltaManager extends ClusterManagerBase{
* Load sessions from other cluster node.
* FIXME replace currently sessions with same id without notification.
* FIXME SSO handling is not really correct with the session replacement!
+ * @param data Serialized data
* @exception ClassNotFoundException
* if a serialized class cannot be found during the reload
* @exception IOException
@@ -651,6 +661,8 @@ public class DeltaManager extends ClusterManagerBase{
* mechanism, if any. If persistence is not supported, this method returns
* without doing anything.
*
+ * @param currentSessions Sessions to serialize
+ * @return serialized data
* @exception IOException
* if an input/output error occurs
*/
@@ -802,8 +814,9 @@ public class DeltaManager extends ClusterManagerBase{
}
/**
- * Wait that cluster session state is transfer or timeout after 60 Sec
+ * Wait that cluster session state is transfered or timeout after 60 Sec
* With stateTransferTimeout == -1 wait that backup is transfered (forever mode)
+ * @param beforeSendTime Start instant of the operation
*/
protected void waitForSendAllSessions(long beforeSendTime) {
long reqStart = System.currentTimeMillis();
@@ -892,7 +905,7 @@ public class DeltaManager extends ClusterManagerBase{
*/
@Override
public void messageDataReceived(ClusterMessage cmsg) {
- if (cmsg != null && cmsg instanceof SessionMessage) {
+ if (cmsg instanceof SessionMessage) {
SessionMessage msg = (SessionMessage) cmsg;
switch (msg.getEventType()) {
case SessionMessage.EVT_GET_ALL_SESSIONS:
@@ -931,24 +944,25 @@ public class DeltaManager extends ClusterManagerBase{
@Override
public ClusterMessage requestCompleted(String sessionId) {
return requestCompleted(sessionId, false);
- }
-
- /**
- * When the request has been completed, the replication valve will notify
- * the manager, and the manager will decide whether any replication is
- * needed or not. If there is a need for replication, the manager will
- * create a session message and that will be replicated. The cluster
- * determines where it gets sent.
- *
- * Session expiration also calls this method, but with expires == true.
- *
- * @param sessionId -
- * the sessionId that just completed.
- * @param expires -
- * whether this method has been called during session expiration
- * @return a SessionMessage to be sent,
- */
- public ClusterMessage requestCompleted(String sessionId, boolean expires) {
+ }
+
+ /**
+ * When the request has been completed, the replication valve will notify
+ * the manager, and the manager will decide whether any replication is
+ * needed or not. If there is a need for replication, the manager will
+ * create a session message and that will be replicated. The cluster
+ * determines where it gets sent.
+ *
+ * Session expiration also calls this method, but with expires == true.
+ *
+ * @param sessionId -
+ * the sessionId that just completed.
+ * @param expires -
+ * whether this method has been called during session expiration
+ * @return a SessionMessage to be sent,
+ */
+ @SuppressWarnings("null") // session can't be null when it is used
+ public ClusterMessage requestCompleted(String sessionId, boolean expires) {
DeltaSession session = null;
SessionMessage msg = null;
try {
@@ -959,25 +973,23 @@ public class DeltaManager extends ClusterManagerBase{
return null;
}
DeltaRequest deltaRequest = session.getDeltaRequest();
- try {
- session.lock();
- if (deltaRequest.getSize() > 0) {
- counterSend_EVT_SESSION_DELTA++;
- byte[] data = serializeDeltaRequest(session,deltaRequest);
- msg = new SessionMessageImpl(getName(),
- SessionMessage.EVT_SESSION_DELTA,
- data,
- sessionId,
- sessionId + "-" + System.currentTimeMillis());
- session.resetDeltaRequest();
- }
- } finally {
- session.unlock();
+ session.lock();
+ if (deltaRequest.getSize() > 0) {
+ counterSend_EVT_SESSION_DELTA++;
+ byte[] data = serializeDeltaRequest(session,deltaRequest);
+ msg = new SessionMessageImpl(getName(),
+ SessionMessage.EVT_SESSION_DELTA,
+ data,
+ sessionId,
+ sessionId + "-" + System.currentTimeMillis());
+ session.resetDeltaRequest();
}
} catch (IOException x) {
log.error(sm.getString("deltaManager.createMessage.unableCreateDeltaRequest",
sessionId), x);
return null;
+ } finally {
+ if (session!=null) session.unlock();
}
if(msg == null) {
if(!expires && !session.isPrimarySession()) {
@@ -1198,8 +1210,8 @@ public class DeltaManager extends ClusterManagerBase{
/**
* handle receive session state is complete transfered
- * @param msg
- * @param sender
+ * @param msg Session message
+ * @param sender Member which sent the message
*/
protected void handleALL_SESSION_TRANSFERCOMPLETE(SessionMessage msg, Member sender) {
counterReceive_EVT_ALL_SESSION_TRANSFERCOMPLETE++ ;
@@ -1213,10 +1225,10 @@ public class DeltaManager extends ClusterManagerBase{
/**
* handle receive session delta
- * @param msg
- * @param sender
- * @throws IOException
- * @throws ClassNotFoundException
+ * @param msg Session message
+ * @param sender Member which sent the message
+ * @throws IOException IO error with serialization
+ * @throws ClassNotFoundException Serialization error
*/
protected void handleSESSION_DELTA(SessionMessage msg, Member sender)
throws IOException, ClassNotFoundException {
@@ -1241,9 +1253,9 @@ public class DeltaManager extends ClusterManagerBase{
/**
* handle receive session is access at other node ( primary session is now false)
- * @param msg
- * @param sender
- * @throws IOException
+ * @param msg Session message
+ * @param sender Member which sent the message
+ * @throws IOException Propagated IO error
*/
protected void handleSESSION_ACCESSED(SessionMessage msg,Member sender) throws IOException {
counterReceive_EVT_SESSION_ACCESSED++;
@@ -1261,9 +1273,9 @@ public class DeltaManager extends ClusterManagerBase{
/**
* handle receive session is expire at other node ( expire session also here)
- * @param msg
- * @param sender
- * @throws IOException
+ * @param msg Session message
+ * @param sender Member which sent the message
+ * @throws IOException Propagated IO error
*/
protected void handleSESSION_EXPIRED(SessionMessage msg,Member sender) throws IOException {
counterReceive_EVT_SESSION_EXPIRED++;
@@ -1279,8 +1291,8 @@ public class DeltaManager extends ClusterManagerBase{
/**
* handle receive new session is created at other node (create backup - primary false)
- * @param msg
- * @param sender
+ * @param msg Session message
+ * @param sender Member which sent the message
*/
protected void handleSESSION_CREATED(SessionMessage msg,Member sender) {
counterReceive_EVT_SESSION_CREATED++;
@@ -1305,10 +1317,10 @@ public class DeltaManager extends ClusterManagerBase{
/**
* handle receive sessions from other not ( restart )
- * @param msg
- * @param sender
- * @throws ClassNotFoundException
- * @throws IOException
+ * @param msg Session message
+ * @param sender Member which sent the message
+ * @throws ClassNotFoundException Serialization error
+ * @throws IOException IO error with serialization
*/
protected void handleALL_SESSION_DATA(SessionMessage msg,Member sender)
throws ClassNotFoundException, IOException {
@@ -1329,9 +1341,9 @@ public class DeltaManager extends ClusterManagerBase{
* a) send all sessions with one message
* b) send session at blocks
* After sending send state is complete transfered
- * @param msg
- * @param sender
- * @throws IOException
+ * @param msg Session message
+ * @param sender Member which sent the message
+ * @throws IOException IO error sending messages
*/
protected void handleGET_ALL_SESSIONS(SessionMessage msg, Member sender) throws IOException {
counterReceive_EVT_GET_ALL_SESSIONS++;
@@ -1378,9 +1390,9 @@ public class DeltaManager extends ClusterManagerBase{
/**
* handle receive change sessionID at other node
- * @param msg
- * @param sender
- * @throws IOException
+ * @param msg Session message
+ * @param sender Member which sent the message
+ * @throws IOException IO error with serialization
*/
protected void handleCHANGE_SESSION_ID(SessionMessage msg,Member sender) throws IOException {
counterReceive_EVT_CHANGE_SESSION_ID++;
@@ -1396,8 +1408,8 @@ public class DeltaManager extends ClusterManagerBase{
/**
* handle receive no context manager.
- * @param msg
- * @param sender
+ * @param msg Session message
+ * @param sender Member which sent the message
*/
protected void handleALL_SESSION_NOCONTEXTMANAGER(SessionMessage msg, Member sender) {
counterReceive_EVT_ALL_SESSION_NOCONTEXTMANAGER++ ;
@@ -1409,10 +1421,10 @@ public class DeltaManager extends ClusterManagerBase{
/**
* send a block of session to sender
- * @param sender
- * @param currentSessions
- * @param sendTimestamp
- * @throws IOException
+ * @param sender Sender member
+ * @param currentSessions Sessions to send
+ * @param sendTimestamp Timestamp
+ * @throws IOException IO error sending messages
*/
protected void sendSessions(Member sender, Session[] currentSessions,long sendTimestamp)
throws IOException {
diff --git a/java/org/apache/catalina/ha/session/DeltaRequest.java b/java/org/apache/catalina/ha/session/DeltaRequest.java
index d313068..c5c5a57 100644
--- a/java/org/apache/catalina/ha/session/DeltaRequest.java
+++ b/java/org/apache/catalina/ha/session/DeltaRequest.java
@@ -33,19 +33,19 @@ import java.util.LinkedList;
import org.apache.catalina.SessionListener;
import org.apache.catalina.realm.GenericPrincipal;
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.res.StringManager;
public class DeltaRequest implements Externalizable {
- public static final org.apache.juli.logging.Log log =
- org.apache.juli.logging.LogFactory.getLog( DeltaRequest.class );
+ public static final Log log = LogFactory.getLog(DeltaRequest.class);
/**
* The string manager for this package.
*/
- protected static final StringManager sm = StringManager
- .getManager(Constants.Package);
+ protected static final StringManager sm = StringManager.getManager(DeltaRequest.class);
public static final int TYPE_ATTRIBUTE = 0;
public static final int TYPE_PRINCIPAL = 1;
@@ -300,7 +300,7 @@ public class DeltaRequest implements Externalizable {
* @see DeltaRequest#writeExternal(java.io.ObjectOutput)
*
* @return serialized delta request
- * @throws IOException
+ * @throws IOException IO error serializing
*/
protected byte[] serialize() throws IOException {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
diff --git a/java/org/apache/catalina/ha/session/DeltaSession.java b/java/org/apache/catalina/ha/session/DeltaSession.java
index 6dc36b6..3e375f3 100644
--- a/java/org/apache/catalina/ha/session/DeltaSession.java
+++ b/java/org/apache/catalina/ha/session/DeltaSession.java
@@ -42,6 +42,8 @@ import org.apache.catalina.ha.ClusterSession;
import org.apache.catalina.session.ManagerBase;
import org.apache.catalina.session.StandardSession;
import org.apache.catalina.tribes.tipis.ReplicatedMapEntry;
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.res.StringManager;
/**
@@ -51,12 +53,12 @@ import org.apache.tomcat.util.res.StringManager;
*/
public class DeltaSession extends StandardSession implements Externalizable,ClusterSession,ReplicatedMapEntry {
- public static final org.apache.juli.logging.Log log = org.apache.juli.logging.LogFactory.getLog(DeltaSession.class);
+ public static final Log log = LogFactory.getLog(DeltaSession.class);
/**
* The string manager for this package.
*/
- protected static final StringManager sm = StringManager.getManager(Constants.Package);
+ protected static final StringManager sm = StringManager.getManager(DeltaSession.class);
// ----------------------------------------------------- Instance Variables
@@ -126,15 +128,15 @@ public class DeltaSession extends StandardSession implements Externalizable,Clus
/**
* Returns a diff and sets the dirty map to false
- * @return byte[]
- * @throws IOException
+ * @return a serialized view of the difference
+ * @throws IOException IO error serializing
*/
@Override
public byte[] getDiff() throws IOException {
lock();
try {
return getDeltaRequest().serialize();
- } finally {
+ } finally{
unlock();
}
}
@@ -151,10 +153,10 @@ public class DeltaSession extends StandardSession implements Externalizable,Clus
/**
* Applies a diff to an existing object.
- * @param diff byte[]
- * @param offset int
- * @param length int
- * @throws IOException
+ * @param diff Serialized diff data
+ * @param offset Array offset
+ * @param length Array length
+ * @throws IOException IO error deserializing
*/
@Override
public void applyDiff(byte[] diff, int offset, int length) throws IOException, ClassNotFoundException {
diff --git a/java/org/apache/catalina/ha/session/JvmRouteBinderValve.java b/java/org/apache/catalina/ha/session/JvmRouteBinderValve.java
index b52f1d2..37919e3 100644
--- a/java/org/apache/catalina/ha/session/JvmRouteBinderValve.java
+++ b/java/org/apache/catalina/ha/session/JvmRouteBinderValve.java
@@ -32,6 +32,8 @@ import org.apache.catalina.ha.ClusterValve;
import org.apache.catalina.session.ManagerBase;
import org.apache.catalina.session.PersistentManager;
import org.apache.catalina.valves.ValveBase;
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.res.StringManager;
/**
@@ -79,8 +81,7 @@ import org.apache.tomcat.util.res.StringManager;
public class JvmRouteBinderValve extends ValveBase implements ClusterValve {
/*--Static Variables----------------------------------------*/
- public static final org.apache.juli.logging.Log log = org.apache.juli.logging.LogFactory
- .getLog(JvmRouteBinderValve.class);
+ public static final Log log = LogFactory.getLog(JvmRouteBinderValve.class);
//------------------------------------------------------ Constructor
public JvmRouteBinderValve() {
@@ -97,7 +98,7 @@ public class JvmRouteBinderValve extends ValveBase implements ClusterValve {
/**
* The string manager for this package.
*/
- protected static final StringManager sm = StringManager.getManager(Constants.Package);
+ protected static final StringManager sm = StringManager.getManager(JvmRouteBinderValve.class);
/**
* enabled this component
diff --git a/java/org/apache/catalina/ha/session/LocalStrings.properties b/java/org/apache/catalina/ha/session/LocalStrings.properties
index 7549990..1f6a5b1 100644
--- a/java/org/apache/catalina/ha/session/LocalStrings.properties
+++ b/java/org/apache/catalina/ha/session/LocalStrings.properties
@@ -80,4 +80,4 @@ backupManager.noCluster=no cluster associated with this context: [{0}]
backupManager.startUnable=Unable to start BackupManager: [{0}]
backupManager.startFailed=Failed to start BackupManager: [{0}]
backupManager.stopped=Manager [{0}] is stopping
-clusterSessionListener.noManager=Context manager doesn''t exist:{0}
\ No newline at end of file
+clusterSessionListener.noManager=Context manager doesn''t exist:{0}
diff --git a/java/org/apache/catalina/ha/session/mbeans-descriptors.xml b/java/org/apache/catalina/ha/session/mbeans-descriptors.xml
index 082ff07..d89b35c 100644
--- a/java/org/apache/catalina/ha/session/mbeans-descriptors.xml
+++ b/java/org/apache/catalina/ha/session/mbeans-descriptors.xml
@@ -170,10 +170,6 @@
type="long"
writeable="false"/>
<attribute
- name="distributable"
- description="The distributable flag for Sessions created by this Manager"
- type="boolean"/>
- <attribute
name="duplicates"
description="Number of duplicated session ids generated"
type="int"/>
@@ -200,10 +196,6 @@
description="The maximum number of active Sessions allowed, or -1 for no limit"
type="int"/>
<attribute
- name="maxInactiveInterval"
- description="The default maximum inactive interval for Sessions created by this Manager"
- type="int"/>
- <attribute
name="name"
description="The descriptive name of this Manager implementation (for logging)"
type="java.lang.String"
@@ -253,10 +245,6 @@
description="Total number of sessions created by this manager"
type="long"/>
<attribute
- name="sessionIdLength"
- description="The session id length (in bytes) of Sessions created by this Manager"
- type="int"/>
- <attribute
name="sessionMaxAliveTime"
description="Longest time an expired session had been alive"
type="int"/>
@@ -444,10 +432,6 @@
type="java.lang.String"
writeable="false"/>
<attribute
- name="distributable"
- description="The distributable flag for Sessions created by this Manager"
- type="boolean"/>
- <attribute
name="duplicates"
description="Number of duplicated session ids generated"
type="int"/>
@@ -478,10 +462,6 @@
description="The maximum number of active Sessions allowed, or -1 for no limit"
type="int"/>
<attribute
- name="maxInactiveInterval"
- description="The default maximum inactive interval for Sessions created by this Manager"
- type="int"/>
- <attribute
name="name"
description="The name of component. "
type="java.lang.String"/>
@@ -507,10 +487,6 @@
description="Total number of sessions created by this manager"
type="long"/>
<attribute
- name="sessionIdLength"
- description="The session id length (in bytes) of Sessions created by this Manager"
- type="int"/>
- <attribute
name="sessionMaxAliveTime"
description="Longest time an expired session had been alive"
type="int"/>
diff --git a/java/org/apache/catalina/ha/tcp/LocalStrings.properties b/java/org/apache/catalina/ha/tcp/LocalStrings.properties
index 544b816..d4f368a 100644
--- a/java/org/apache/catalina/ha/tcp/LocalStrings.properties
+++ b/java/org/apache/catalina/ha/tcp/LocalStrings.properties
@@ -38,4 +38,4 @@ simpleTcpCluster.sendFailed=Unable to send message through cluster sender.
simpleTcpCluster.member.added=Replication member added:{0}
simpleTcpCluster.member.addFailed=Unable to connect to replication system.
simpleTcpCluster.member.disappeared=Received member disappeared:{0}
-simpleTcpCluster.member.removeFailed=Unable remove cluster node from replication system.
\ No newline at end of file
+simpleTcpCluster.member.removeFailed=Unable remove cluster node from replication system.
diff --git a/java/org/apache/catalina/ha/tcp/ReplicationValve.java b/java/org/apache/catalina/ha/tcp/ReplicationValve.java
index ff55ed5..8fac6b2 100644
--- a/java/org/apache/catalina/ha/tcp/ReplicationValve.java
+++ b/java/org/apache/catalina/ha/tcp/ReplicationValve.java
@@ -42,6 +42,8 @@ import org.apache.catalina.ha.ClusterValve;
import org.apache.catalina.ha.session.DeltaManager;
import org.apache.catalina.ha.session.DeltaSession;
import org.apache.catalina.valves.ValveBase;
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.res.StringManager;
/**
@@ -63,8 +65,7 @@ import org.apache.tomcat.util.res.StringManager;
public class ReplicationValve
extends ValveBase implements ClusterValve {
- private static final org.apache.juli.logging.Log log =
- org.apache.juli.logging.LogFactory.getLog( ReplicationValve.class );
+ private static final Log log = LogFactory.getLog(ReplicationValve.class);
// ----------------------------------------------------- Instance Variables
@@ -117,7 +118,7 @@ public class ReplicationValve
}
/**
- * @return Returns the cluster.
+ * @return the cluster.
*/
@Override
public CatalinaCluster getCluster() {
@@ -133,7 +134,7 @@ public class ReplicationValve
}
/**
- * @return Returns the filter
+ * @return the filter
*/
public String getFilter() {
if (filter == null) {
@@ -166,7 +167,7 @@ public class ReplicationValve
}
/**
- * @return Returns the primaryIndicator.
+ * @return the primaryIndicator.
*/
public boolean isPrimaryIndicator() {
return primaryIndicator;
@@ -180,7 +181,7 @@ public class ReplicationValve
}
/**
- * @return Returns the primaryIndicatorName.
+ * @return the primaryIndicatorName.
*/
public String getPrimaryIndicatorName() {
return primaryIndicatorName;
@@ -195,6 +196,7 @@ public class ReplicationValve
/**
* Calc processing stats
+ * @return <code>true</code> if statistics are enabled
*/
public boolean doStatistics() {
return doProcessingStats;
@@ -202,6 +204,8 @@ public class ReplicationValve
/**
* Set Calc processing stats
+ *
+ * @param doProcessingStats New flag value
* @see #resetStatistics()
*/
public void setStatistics(boolean doProcessingStats) {
@@ -209,49 +213,49 @@ public class ReplicationValve
}
/**
- * @return Returns the lastSendTime.
+ * @return the lastSendTime.
*/
public long getLastSendTime() {
return lastSendTime;
}
/**
- * @return Returns the nrOfRequests.
+ * @return the nrOfRequests.
*/
public long getNrOfRequests() {
return nrOfRequests;
}
/**
- * @return Returns the nrOfFilterRequests.
+ * @return the nrOfFilterRequests.
*/
public long getNrOfFilterRequests() {
return nrOfFilterRequests;
}
/**
- * @return Returns the nrOfCrossContextSendRequests.
+ * @return the nrOfCrossContextSendRequests.
*/
public long getNrOfCrossContextSendRequests() {
return nrOfCrossContextSendRequests;
}
/**
- * @return Returns the nrOfSendRequests.
+ * @return the nrOfSendRequests.
*/
public long getNrOfSendRequests() {
return nrOfSendRequests;
}
/**
- * @return Returns the totalRequestTime.
+ * @return the totalRequestTime.
*/
public long getTotalRequestTime() {
return totalRequestTime;
}
/**
- * @return Returns the totalSendTime.
+ * @return the totalSendTime.
*/
public long getTotalSendTime() {
return totalSendTime;
@@ -384,12 +388,6 @@ public class ReplicationValve
// --------------------------------------------------------- Protected Methods
- /**
- * @param request
- * @param totalstart
- * @param isCrossContext
- * @param clusterManager
- */
protected void sendReplicationMessage(Request request, long totalstart, boolean isCrossContext, ClusterManager clusterManager) {
//this happens after the request
long start = 0;
@@ -533,7 +531,7 @@ public class ReplicationValve
/**
* check for session invalidations
- * @param manager
+ * @param manager Associated manager
*/
protected void sendInvalidSessions(ClusterManager manager) {
String[] invalidIds=manager.getInvalidatedSessions();
@@ -560,8 +558,8 @@ public class ReplicationValve
/**
* Protocol cluster replications stats
- * @param requestTime
- * @param clusterTime
+ * @param requestTime Request time
+ * @param clusterTime Cluster time
*/
protected void updateStats(long requestTime, long clusterTime) {
// TODO: Async requests may trigger multiple replication requests. How,
@@ -593,8 +591,8 @@ public class ReplicationValve
* Mark Request that processed at primary node with attribute
* primaryIndicatorName
*
- * @param request
- * @throws IOException
+ * @param request The Servlet request
+ * @throws IOException IO error finding session
*/
protected void createPrimaryIndicator(Request request) throws IOException {
String id = request.getRequestedSessionId();
diff --git a/java/org/apache/catalina/ha/tcp/SendMessageData.java b/java/org/apache/catalina/ha/tcp/SendMessageData.java
index 304152b..bccfab3 100644
--- a/java/org/apache/catalina/ha/tcp/SendMessageData.java
+++ b/java/org/apache/catalina/ha/tcp/SendMessageData.java
@@ -30,9 +30,9 @@ public class SendMessageData {
/**
- * @param message
- * @param destination
- * @param exception
+ * @param message The message to send
+ * @param destination Member destination
+ * @param exception Associated error
*/
public SendMessageData(Object message, Member destination,
Exception exception) {
@@ -43,19 +43,19 @@ public class SendMessageData {
}
/**
- * @return Returns the destination.
+ * @return the destination.
*/
public Member getDestination() {
return destination;
}
/**
- * @return Returns the exception.
+ * @return the exception.
*/
public Exception getException() {
return exception;
}
/**
- * @return Returns the message.
+ * @return the message.
*/
public Object getMessage() {
return message;
diff --git a/java/org/apache/catalina/ha/tcp/SimpleTcpCluster.java b/java/org/apache/catalina/ha/tcp/SimpleTcpCluster.java
index 5c47a23..8b66625 100644
--- a/java/org/apache/catalina/ha/tcp/SimpleTcpCluster.java
+++ b/java/org/apache/catalina/ha/tcp/SimpleTcpCluster.java
@@ -269,6 +269,7 @@ public class SimpleTcpCluster extends LifecycleMBeanBase
/**
* Get the cluster listeners associated with this cluster. If this Array has
* no listeners registered, a zero-length array is returned.
+ * @return the listener array
*/
public ClusterListener[] findClusterListeners() {
if (clusterListeners.size() > 0) {
@@ -283,6 +284,7 @@ public class SimpleTcpCluster extends LifecycleMBeanBase
/**
* Add cluster message listener and register cluster to this listener.
*
+ * @param listener The new listener
* @see org.apache.catalina.ha.CatalinaCluster#addClusterListener(org.apache.catalina.ha.ClusterListener)
*/
@Override
@@ -296,6 +298,7 @@ public class SimpleTcpCluster extends LifecycleMBeanBase
/**
* Remove message listener and deregister Cluster from listener.
*
+ * @param listener The listener to remove
* @see org.apache.catalina.ha.CatalinaCluster#removeClusterListener(org.apache.catalina.ha.ClusterListener)
*/
@Override
@@ -307,7 +310,7 @@ public class SimpleTcpCluster extends LifecycleMBeanBase
}
/**
- * get current Deployer
+ * @return the current Deployer
*/
@Override
public ClusterDeployer getClusterDeployer() {
@@ -316,6 +319,7 @@ public class SimpleTcpCluster extends LifecycleMBeanBase
/**
* set a new Deployer, must be set before cluster started!
+ * @param clusterDeployer The associated deployer
*/
@Override
public void setClusterDeployer(ClusterDeployer clusterDeployer) {
@@ -436,11 +440,12 @@ public class SimpleTcpCluster extends LifecycleMBeanBase
/**
* Remove an application from cluster replication bus.
*
+ * @param manager The manager
* @see org.apache.catalina.Cluster#removeManager(Manager)
*/
@Override
public void removeManager(Manager manager) {
- if (manager != null && manager instanceof ClusterManager ) {
+ if (manager instanceof ClusterManager) {
ClusterManager cmgr = (ClusterManager) manager;
// Notify our interested LifecycleListeners
fireLifecycleEvent(BEFORE_MANAGERUNREGISTER_EVENT,manager);
@@ -451,11 +456,6 @@ public class SimpleTcpCluster extends LifecycleMBeanBase
}
}
- /**
- * @param name
- * @param manager
- * @return TODO
- */
@Override
public String getManagerName(String name, Manager manager) {
String clusterName = name ;
@@ -471,11 +471,6 @@ public class SimpleTcpCluster extends LifecycleMBeanBase
return clusterName;
}
- /*
- * Get Manager
- *
- * @see org.apache.catalina.ha.CatalinaCluster#getManager(java.lang.String)
- */
@Override
public Manager getManager(String name) {
return managers.get(name);
@@ -538,8 +533,7 @@ public class SimpleTcpCluster extends LifecycleMBeanBase
registerClusterValve();
channel.addMembershipListener(this);
channel.addChannelListener(this);
- if (channel instanceof GroupChannel)
- ((GroupChannel)channel).setName(getClusterName() + "-Channel");
+ channel.setName(getClusterName() + "-Channel");
channel.start(channelStartOptions);
if (clusterDeployer != null) clusterDeployer.start();
registerMember(channel.getLocalMember(false));
diff --git a/java/org/apache/catalina/loader/JdbcLeakPrevention.java b/java/org/apache/catalina/loader/JdbcLeakPrevention.java
index c6c4b03..a6acd18 100644
--- a/java/org/apache/catalina/loader/JdbcLeakPrevention.java
+++ b/java/org/apache/catalina/loader/JdbcLeakPrevention.java
@@ -34,7 +34,7 @@ import java.util.List;
* version is do not just create a new instance of this class with the new
* keyword.
*
- * Since this class is loaded by {@link WebappClassLoaderBase}, it can not refer
+ * Since this class is loaded by {@link WebappClassLoaderBase}, it cannot refer
* to any internal Tomcat classes as that will cause the security manager to
* complain.
*/
diff --git a/java/org/apache/catalina/loader/ParallelWebappClassLoader.java b/java/org/apache/catalina/loader/ParallelWebappClassLoader.java
index 312ed53..2235229 100644
--- a/java/org/apache/catalina/loader/ParallelWebappClassLoader.java
+++ b/java/org/apache/catalina/loader/ParallelWebappClassLoader.java
@@ -17,11 +17,12 @@
package org.apache.catalina.loader;
import org.apache.catalina.LifecycleException;
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
public class ParallelWebappClassLoader extends WebappClassLoaderBase {
- private static final org.apache.juli.logging.Log log =
- org.apache.juli.logging.LogFactory.getLog(ParallelWebappClassLoader.class);
+ private static final Log log = LogFactory.getLog(ParallelWebappClassLoader.class);
static {
boolean result = ClassLoader.registerAsParallelCapable();
diff --git a/java/org/apache/catalina/loader/ResourceEntry.java b/java/org/apache/catalina/loader/ResourceEntry.java
index 7063d65..4b70926 100644
--- a/java/org/apache/catalina/loader/ResourceEntry.java
+++ b/java/org/apache/catalina/loader/ResourceEntry.java
@@ -16,10 +16,6 @@
*/
package org.apache.catalina.loader;
-import java.net.URL;
-
-import org.apache.catalina.WebResource;
-
/**
* Resource entry.
*
@@ -27,32 +23,16 @@ import org.apache.catalina.WebResource;
*/
public class ResourceEntry {
-
/**
- * The "last modified" time of the origin file at the time this class
+ * The "last modified" time of the origin file at the time this resource
* was loaded, in milliseconds since the epoch.
*/
public long lastModified = -1;
/**
- * Binary content of the resource.
- */
- public byte[] binaryContent = null;
-
-
- /**
* Loaded class.
*/
public volatile Class<?> loadedClass = null;
-
-
- /**
- * URL source from where the object was loaded.
- */
- public URL source = null;
-
-
- public WebResource webResource = null;
}
diff --git a/java/org/apache/catalina/loader/WebappClassLoaderBase.java b/java/org/apache/catalina/loader/WebappClassLoaderBase.java
index add7943..357244d 100644
--- a/java/org/apache/catalina/loader/WebappClassLoaderBase.java
+++ b/java/org/apache/catalina/loader/WebappClassLoaderBase.java
@@ -16,7 +16,6 @@
*/
package org.apache.catalina.loader;
-import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FilePermission;
import java.io.IOException;
@@ -24,15 +23,12 @@ import java.io.InputStream;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.lang.ref.Reference;
-import java.lang.ref.WeakReference;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLClassLoader;
-import java.nio.charset.StandardCharsets;
import java.security.AccessControlException;
import java.security.AccessController;
import java.security.CodeSource;
@@ -55,8 +51,6 @@ import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
-import java.util.ResourceBundle;
-import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ThreadPoolExecutor;
@@ -74,11 +68,12 @@ import org.apache.catalina.WebResource;
import org.apache.catalina.WebResourceRoot;
import org.apache.catalina.webresources.TomcatURLStreamHandlerFactory;
import org.apache.juli.WebappProperties;
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.InstrumentableClassLoader;
import org.apache.tomcat.util.ExceptionUtils;
import org.apache.tomcat.util.IntrospectionUtils;
import org.apache.tomcat.util.compat.JreCompat;
-import org.apache.tomcat.util.compat.JreVendor;
import org.apache.tomcat.util.res.StringManager;
import org.apache.tomcat.util.security.PermissionCheck;
@@ -128,8 +123,7 @@ import org.apache.tomcat.util.security.PermissionCheck;
public abstract class WebappClassLoaderBase extends URLClassLoader
implements Lifecycle, InstrumentableClassLoader, WebappProperties, PermissionCheck {
- private static final org.apache.juli.logging.Log log =
- org.apache.juli.logging.LogFactory.getLog(WebappClassLoaderBase.class);
+ private static final Log log = LogFactory.getLog(WebappClassLoaderBase.class);
/**
* List of ThreadGroup names to ignore when scanning for web application
@@ -140,7 +134,6 @@ public abstract class WebappClassLoaderBase extends URLClassLoader
private static final String JVM_THREAD_GROUP_SYSTEM = "system";
private static final String CLASS_FILE_SUFFIX = ".class";
- private static final String SERVICES_PREFIX = "/META-INF/services/";
static {
ClassLoader.registerAsParallelCapable();
@@ -148,20 +141,18 @@ public abstract class WebappClassLoaderBase extends URLClassLoader
JVM_THREAD_GROUP_NAMES.add("RMI Runtime");
}
- protected class PrivilegedFindResourceByName
- implements PrivilegedAction<ResourceEntry> {
+ protected class PrivilegedFindClassByName
+ implements PrivilegedAction<Class<?>> {
protected final String name;
- protected final String path;
- PrivilegedFindResourceByName(String name, String path) {
+ PrivilegedFindClassByName(String name) {
this.name = name;
- this.path = path;
}
@Override
- public ResourceEntry run() {
- return findResourceInternal(name, path);
+ public Class<?> run() {
+ return findClassInternal(name);
}
}
@@ -328,14 +319,10 @@ public abstract class WebappClassLoaderBase extends URLClassLoader
/**
- * need conversion for properties files
- */
- protected boolean needConvert = false;
-
-
- /**
* All permission.
+ * @deprecated Unused. This will be removed in Tomcat 9.
*/
+ @Deprecated
protected final Permission allPermission = new java.security.AllPermission();
@@ -347,20 +334,6 @@ public abstract class WebappClassLoaderBase extends URLClassLoader
private boolean clearReferencesRmiTargets = true;
/**
- * Should Tomcat attempt to null out any static or final fields from loaded
- * classes when a web application is stopped as a work around for apparent
- * garbage collection bugs and application coding errors? There have been
- * some issues reported with log4j when this option is true. Applications
- * without memory leaks using recent JVMs should operate correctly with this
- * option set to <code>false</code>. If not specified, the default value of
- * <code>false</code> will be used.
- *
- * @deprecated This option will be removed in Tomcat 8.5
- */
- @Deprecated
- private boolean clearReferencesStatic = false;
-
- /**
* Should Tomcat attempt to terminate threads that have been started by the
* web application? Stopping threads is performed via the deprecated (for
* good reason) <code>Thread.stop()</code> method and is likely to result in
@@ -426,7 +399,7 @@ public abstract class WebappClassLoaderBase extends URLClassLoader
// ------------------------------------------------------------- Properties
/**
- * Get associated resources.
+ * @return associated resources.
*/
public WebResourceRoot getResources() {
return this.resources;
@@ -435,6 +408,8 @@ public abstract class WebappClassLoaderBase extends URLClassLoader
/**
* Set associated resources.
+ * @param resources the resources from which the classloader will
+ * load the classes
*/
public void setResources(WebResourceRoot resources) {
this.resources = resources;
@@ -442,7 +417,7 @@ public abstract class WebappClassLoaderBase extends URLClassLoader
/**
- * Return the context name for this class loader.
+ * @return the context name for this class loader.
*/
public String getContextName() {
if (resources == null) {
@@ -455,6 +430,8 @@ public abstract class WebappClassLoaderBase extends URLClassLoader
/**
* Return the "delegate first" flag for this class loader.
+ * @return <code>true</code> if the class lookup will delegate to
+ * the parent first. The default in Tomcat is <code>false</code>.
*/
public boolean getDelegate() {
@@ -550,31 +527,7 @@ public abstract class WebappClassLoaderBase extends URLClassLoader
/**
- * Return the clearReferencesStatic flag for this Context.
- *
- * @deprecated This option will be removed in Tomcat 8.5
- */
- @Deprecated
- public boolean getClearReferencesStatic() {
- return (this.clearReferencesStatic);
- }
-
-
- /**
- * Set the clearReferencesStatic feature for this Context.
- *
- * @param clearReferencesStatic The new flag value
- *
- * @deprecated This option will be removed in Tomcat 8.5
- */
- @Deprecated
- public void setClearReferencesStatic(boolean clearReferencesStatic) {
- this.clearReferencesStatic = clearReferencesStatic;
- }
-
-
- /**
- * Return the clearReferencesStopThreads flag for this Context.
+ * @return the clearReferencesStopThreads flag for this Context.
*/
public boolean getClearReferencesStopThreads() {
return (this.clearReferencesStopThreads);
@@ -593,7 +546,7 @@ public abstract class WebappClassLoaderBase extends URLClassLoader
/**
- * Return the clearReferencesStopTimerThreads flag for this Context.
+ * @return the clearReferencesStopTimerThreads flag for this Context.
*/
public boolean getClearReferencesStopTimerThreads() {
return (this.clearReferencesStopTimerThreads);
@@ -612,7 +565,7 @@ public abstract class WebappClassLoaderBase extends URLClassLoader
/**
- * Return the clearReferencesLogFactoryRelease flag for this Context.
+ * @return the clearReferencesLogFactoryRelease flag for this Context.
*/
public boolean getClearReferencesLogFactoryRelease() {
return (this.clearReferencesLogFactoryRelease);
@@ -632,7 +585,7 @@ public abstract class WebappClassLoaderBase extends URLClassLoader
/**
- * Return the clearReferencesHttpClientKeepAliveThread flag for this
+ * @return the clearReferencesHttpClientKeepAliveThread flag for this
* Context.
*/
public boolean getClearReferencesHttpClientKeepAliveThread() {
@@ -709,8 +662,6 @@ public abstract class WebappClassLoaderBase extends URLClassLoader
base.resources = this.resources;
base.delegate = this.delegate;
base.state = LifecycleState.NEW;
- base.needConvert = this.needConvert;
- base.clearReferencesStatic = this.clearReferencesStatic;
base.clearReferencesStopThreads = this.clearReferencesStopThreads;
base.clearReferencesStopTimerThreads = this.clearReferencesStopTimerThreads;
base.clearReferencesLogFactoryRelease = this.clearReferencesLogFactoryRelease;
@@ -723,6 +674,7 @@ public abstract class WebappClassLoaderBase extends URLClassLoader
/**
* Have one or more classes or resources been modified so that a reload
* is appropriate?
+ * @return <code>true</code> if there's been a modification
*/
public boolean modified() {
@@ -779,9 +731,6 @@ public abstract class WebappClassLoaderBase extends URLClassLoader
}
- /**
- * Render a String representation of this object.
- */
@Override
public String toString() {
@@ -809,9 +758,7 @@ public abstract class WebappClassLoaderBase extends URLClassLoader
// ---------------------------------------------------- ClassLoader Methods
- /**
- * Expose this method for use by the unit tests.
- */
+ // Note: exposed for use by tests
protected final Class<?> doDefineClass(String name, byte[] b, int off, int len,
ProtectionDomain protectionDomain) {
return super.defineClass(name, b, off, len, protectionDomain);
@@ -856,7 +803,13 @@ public abstract class WebappClassLoaderBase extends URLClassLoader
if (log.isTraceEnabled())
log.trace(" findClassInternal(" + name + ")");
try {
- clazz = findClassInternal(name);
+ if (securityManager != null) {
+ PrivilegedAction<Class<?>> dp =
+ new PrivilegedFindClassByName(name);
+ clazz = AccessController.doPrivileged(dp);
+ } else {
+ clazz = findClassInternal(name);
+ }
} catch(AccessControlException ace) {
log.warn("WebappClassLoader.findClassInternal(" + name
+ ") security exception: " + ace.getMessage(), ace);
@@ -928,19 +881,10 @@ public abstract class WebappClassLoaderBase extends URLClassLoader
String path = nameToPath(name);
- ResourceEntry entry = resourceEntries.get(path);
- if (entry == null) {
- if (securityManager != null) {
- PrivilegedAction<ResourceEntry> dp =
- new PrivilegedFindResourceByName(name, path);
- entry = AccessController.doPrivileged(dp);
- } else {
- entry = findResourceInternal(name, path);
- }
- }
- if (entry != null) {
- url = entry.source;
- entry.webResource = null;
+ WebResource resource = resources.getClassLoaderResource(path);
+ if (resource.exists()) {
+ url = resource.getURL();
+ trackLastModified(path, resource);
}
if ((url == null) && hasExternalRepositories) {
@@ -954,7 +898,20 @@ public abstract class WebappClassLoaderBase extends URLClassLoader
log.debug(" --> Resource not found, returning null");
}
return url;
+ }
+
+ private void trackLastModified(String path, WebResource resource) {
+ if (resourceEntries.containsKey(path)) {
+ return;
+ }
+ ResourceEntry entry = new ResourceEntry();
+ entry.lastModified = resource.getLastModified();
+ synchronized(resourceEntries) {
+ if (!resourceEntries.containsKey(path)) {
+ resourceEntries.put(path, entry);
+ }
+ }
}
@@ -1089,14 +1046,6 @@ public abstract class WebappClassLoaderBase extends URLClassLoader
InputStream stream = null;
- // (0) Check for a cached copy of this resource
- stream = findLoadedResource(name);
- if (stream != null) {
- if (log.isDebugEnabled())
- log.debug(" --> Returning stream from cache");
- return (stream);
- }
-
boolean delegateFirst = delegate || filter(name, false);
// (1) Delegate to parent if requested
@@ -1105,30 +1054,35 @@ public abstract class WebappClassLoaderBase extends URLClassLoader
log.debug(" Delegating to parent classloader " + parent);
stream = parent.getResourceAsStream(name);
if (stream != null) {
- // FIXME - cache???
if (log.isDebugEnabled())
log.debug(" --> Returning stream from parent");
- return (stream);
+ return stream;
}
}
// (2) Search local repositories
if (log.isDebugEnabled())
log.debug(" Searching local repositories");
- URL url = findResource(name);
- if (url != null) {
- // FIXME - cache???
- if (log.isDebugEnabled())
- log.debug(" --> Returning stream from local");
- stream = findLoadedResource(name);
- try {
- if (hasExternalRepositories && (stream == null))
+ String path = nameToPath(name);
+ WebResource resource = resources.getClassLoaderResource(path);
+ if (resource.exists()) {
+ stream = resource.getInputStream();
+ trackLastModified(path, resource);
+ }
+ try {
+ if (hasExternalRepositories && stream == null) {
+ URL url = super.findResource(name);
+ if (url != null) {
stream = url.openStream();
- } catch (IOException e) {
- // Ignore
+ }
}
- if (stream != null)
- return (stream);
+ } catch (IOException e) {
+ // Ignore
+ }
+ if (stream != null) {
+ if (log.isDebugEnabled())
+ log.debug(" --> Returning stream from local");
+ return stream;
}
// (3) Delegate to parent unconditionally
@@ -1137,18 +1091,16 @@ public abstract class WebappClassLoaderBase extends URLClassLoader
log.debug(" Delegating to parent classloader unconditionally " + parent);
stream = parent.getResourceAsStream(name);
if (stream != null) {
- // FIXME - cache???
if (log.isDebugEnabled())
log.debug(" --> Returning stream from parent");
- return (stream);
+ return stream;
}
}
// (4) Resource was not found
if (log.isDebugEnabled())
log.debug(" --> Resource not found, returning null");
- return (null);
-
+ return null;
}
@@ -1506,18 +1458,6 @@ public abstract class WebappClassLoaderBase extends URLClassLoader
}
}
- state = LifecycleState.STARTING;
-
- String encoding = null;
- try {
- encoding = System.getProperty("file.encoding");
- } catch (SecurityException e) {
- return;
- }
- if (encoding.indexOf("EBCDIC")!=-1) {
- needConvert = true;
- }
-
state = LifecycleState.STARTED;
}
@@ -1595,12 +1535,6 @@ public abstract class WebappClassLoaderBase extends URLClassLoader
clearReferencesRmiTargets();
}
- // Null out any static or final fields from loaded classes,
- // as a workaround for apparent garbage collection bugs
- if (clearReferencesStatic) {
- clearReferencesStaticFinal();
- }
-
// Clear the IntrospectionUtils cache.
IntrospectionUtils.clear();
@@ -1609,13 +1543,6 @@ public abstract class WebappClassLoaderBase extends URLClassLoader
org.apache.juli.logging.LogFactory.release(this);
}
- // Clear the resource bundle cache
- // This shouldn't be necessary, the cache uses weak references but
- // it has caused leaks. Oddly, using the leak detection code in
- // standard host allows the class loader to be GC'd. This has been seen
- // on Sun but not IBM JREs. Maybe a bug in Sun's GC impl?
- clearReferencesResourceBundles();
-
// Clear the classloader reference in the VM's bean introspector
java.beans.Introspector.flushCaches();
@@ -1681,131 +1608,6 @@ public abstract class WebappClassLoaderBase extends URLClassLoader
}
- private final void clearReferencesStaticFinal() {
-
- Collection<ResourceEntry> values = resourceEntries.values();
- Iterator<ResourceEntry> loadedClasses = values.iterator();
- //
- // walk through all loaded class to trigger initialization for
- // any uninitialized classes, otherwise initialization of
- // one class may call a previously cleared class.
- while(loadedClasses.hasNext()) {
- ResourceEntry entry = loadedClasses.next();
- if (entry.loadedClass != null) {
- Class<?> clazz = entry.loadedClass;
- try {
- Field[] fields = clazz.getDeclaredFields();
- for (int i = 0; i < fields.length; i++) {
- if(Modifier.isStatic(fields[i].getModifiers())) {
- fields[i].get(null);
- break;
- }
- }
- } catch(Throwable t) {
- // Ignore
- }
- }
- }
- loadedClasses = values.iterator();
- while (loadedClasses.hasNext()) {
- ResourceEntry entry = loadedClasses.next();
- if (entry.loadedClass != null) {
- Class<?> clazz = entry.loadedClass;
- try {
- Field[] fields = clazz.getDeclaredFields();
- for (int i = 0; i < fields.length; i++) {
- Field field = fields[i];
- int mods = field.getModifiers();
- if (field.getType().isPrimitive()
- || (field.getName().indexOf('$') != -1)) {
- continue;
- }
- if (Modifier.isStatic(mods)) {
- try {
- field.setAccessible(true);
- if (Modifier.isFinal(mods)) {
- if (!((field.getType().getName().startsWith("java."))
- || (field.getType().getName().startsWith("javax.")))) {
- nullInstance(field.get(null));
- }
- } else {
- field.set(null, null);
- if (log.isDebugEnabled()) {
- log.debug("Set field " + field.getName()
- + " to null in class " + clazz.getName());
- }
- }
- } catch (Throwable t) {
- ExceptionUtils.handleThrowable(t);
- if (log.isDebugEnabled()) {
- log.debug("Could not set field " + field.getName()
- + " to null in class " + clazz.getName(), t);
- }
- }
- }
- }
- } catch (Throwable t) {
- ExceptionUtils.handleThrowable(t);
- if (log.isDebugEnabled()) {
- log.debug("Could not clean fields for class " + clazz.getName(), t);
- }
- }
- }
- }
-
- }
-
-
- private void nullInstance(Object instance) {
- if (instance == null) {
- return;
- }
- Field[] fields = instance.getClass().getDeclaredFields();
- for (int i = 0; i < fields.length; i++) {
- Field field = fields[i];
- int mods = field.getModifiers();
- if (field.getType().isPrimitive()
- || (field.getName().indexOf('$') != -1)) {
- continue;
- }
- try {
- field.setAccessible(true);
- if (Modifier.isStatic(mods) && Modifier.isFinal(mods)) {
- // Doing something recursively is too risky
- continue;
- }
- Object value = field.get(instance);
- if (null != value) {
- Class<? extends Object> valueClass = value.getClass();
- if (!loadedByThisOrChild(valueClass)) {
- if (log.isDebugEnabled()) {
- log.debug("Not setting field " + field.getName() +
- " to null in object of class " +
- instance.getClass().getName() +
- " because the referenced object was of type " +
- valueClass.getName() +
- " which was not loaded by this web application class loader.");
- }
- } else {
- field.set(instance, null);
- if (log.isDebugEnabled()) {
- log.debug("Set field " + field.getName()
- + " to null in class " + instance.getClass().getName());
- }
- }
- }
- } catch (Throwable t) {
- ExceptionUtils.handleThrowable(t);
- if (log.isDebugEnabled()) {
- log.debug("Could not set field " + field.getName()
- + " to null in object instance of class "
- + instance.getClass().getName(), t);
- }
- }
- }
- }
-
-
@SuppressWarnings("deprecation") // thread.stop()
private void clearReferencesThreads() {
Thread[] threads = getThreads();
@@ -1825,15 +1627,12 @@ public abstract class WebappClassLoaderBase extends URLClassLoader
// JVM controlled threads
ThreadGroup tg = thread.getThreadGroup();
- if (tg != null &&
- JVM_THREAD_GROUP_NAMES.contains(tg.getName())) {
-
+ if (tg != null && JVM_THREAD_GROUP_NAMES.contains(tg.getName())) {
// HttpClient keep-alive threads
if (clearReferencesHttpClientKeepAliveThread &&
threadName.equals("Keep-Alive-Timer")) {
thread.setContextClassLoader(parent);
- log.debug(sm.getString(
- "webappClassLoader.checkThreadsHttpClient"));
+ log.debug(sm.getString("webappClassLoader.checkThreadsHttpClient"));
}
// Don't warn about remaining JVM controlled threads
@@ -1878,11 +1677,9 @@ public abstract class WebappClassLoaderBase extends URLClassLoader
// "runnable" in IBM JDK
// "action" in Apache Harmony
Object target = null;
- for (String fieldName : new String[] { "target",
- "runnable", "action" }) {
+ for (String fieldName : new String[] { "target", "runnable", "action" }) {
try {
- Field targetField = thread.getClass()
- .getDeclaredField(fieldName);
+ Field targetField = thread.getClass().getDeclaredField(fieldName);
targetField.setAccessible(true);
target = targetField.get(thread);
break;
@@ -1893,12 +1690,10 @@ public abstract class WebappClassLoaderBase extends URLClassLoader
// "java.util.concurrent" code is in public domain,
// so all implementations are similar
- if (target != null &&
- target.getClass().getCanonicalName() != null
- && target.getClass().getCanonicalName().equals(
+ if (target != null && target.getClass().getCanonicalName() != null &&
+ target.getClass().getCanonicalName().equals(
"java.util.concurrent.ThreadPoolExecutor.Worker")) {
- Field executorField =
- target.getClass().getDeclaredField("this$0");
+ Field executorField = target.getClass().getDeclaredField("this$0");
executorField.setAccessible(true);
Object executor = executorField.get(target);
if (executor instanceof ThreadPoolExecutor) {
@@ -1906,21 +1701,9 @@ public abstract class WebappClassLoaderBase extends URLClassLoader
usingExecutor = true;
}
}
- } catch (SecurityException e) {
- log.warn(sm.getString(
- "webappClassLoader.stopThreadFail",
- thread.getName(), getContextName()), e);
- } catch (NoSuchFieldException e) {
- log.warn(sm.getString(
- "webappClassLoader.stopThreadFail",
- thread.getName(), getContextName()), e);
- } catch (IllegalArgumentException e) {
- log.warn(sm.getString(
- "webappClassLoader.stopThreadFail",
- thread.getName(), getContextName()), e);
- } catch (IllegalAccessException e) {
- log.warn(sm.getString(
- "webappClassLoader.stopThreadFail",
+ } catch (SecurityException | NoSuchFieldException | IllegalArgumentException |
+ IllegalAccessException e) {
+ log.warn(sm.getString("webappClassLoader.stopThreadFail",
thread.getName(), getContextName()), e);
}
@@ -2353,94 +2136,6 @@ public abstract class WebappClassLoaderBase extends URLClassLoader
/**
- * Clear the {@link ResourceBundle} cache of any bundles loaded by this
- * class loader or any class loader where this loader is a parent class
- * loader. Whilst {@link ResourceBundle#clearCache()} could be used there
- * are complications around the
- * {@link org.apache.jasper.servlet.JasperLoader} that mean a reflection
- * based approach is more likely to be complete.
- *
- * The ResourceBundle is using WeakReferences so it shouldn't be pinning the
- * class loader in memory. However, it is. Therefore clear ou the
- * references.
- */
- private void clearReferencesResourceBundles() {
- // Get a reference to the cache
- try {
- Field cacheListField =
- ResourceBundle.class.getDeclaredField("cacheList");
- cacheListField.setAccessible(true);
-
- // Java 6 uses ConcurrentMap
- // Java 5 uses SoftCache extends Abstract Map
- // So use Map and it *should* work with both
- Map<?,?> cacheList = (Map<?,?>) cacheListField.get(null);
-
- // Get the keys (loader references are in the key)
- Set<?> keys = cacheList.keySet();
-
- Field loaderRefField = null;
-
- // Iterate over the keys looking at the loader instances
- Iterator<?> keysIter = keys.iterator();
-
- int countRemoved = 0;
-
- while (keysIter.hasNext()) {
- Object key = keysIter.next();
-
- if (loaderRefField == null) {
- loaderRefField =
- key.getClass().getDeclaredField("loaderRef");
- loaderRefField.setAccessible(true);
- }
- WeakReference<?> loaderRef =
- (WeakReference<?>) loaderRefField.get(key);
-
- ClassLoader loader = (ClassLoader) loaderRef.get();
-
- while (loader != null && loader != this) {
- loader = loader.getParent();
- }
-
- if (loader != null) {
- keysIter.remove();
- countRemoved++;
- }
- }
-
- if (countRemoved > 0 && log.isDebugEnabled()) {
- log.debug(sm.getString(
- "webappClassLoader.clearReferencesResourceBundlesCount",
- Integer.valueOf(countRemoved), getContextName()));
- }
- } catch (SecurityException e) {
- log.warn(sm.getString(
- "webappClassLoader.clearReferencesResourceBundlesFail",
- getContextName()), e);
- } catch (NoSuchFieldException e) {
- if (JreVendor.IS_ORACLE_JVM) {
- log.warn(sm.getString(
- "webappClassLoader.clearReferencesResourceBundlesFail",
- getContextName()), e);
- } else {
- log.debug(sm.getString(
- "webappClassLoader.clearReferencesResourceBundlesFail",
- getContextName()), e);
- }
- } catch (IllegalArgumentException e) {
- log.warn(sm.getString(
- "webappClassLoader.clearReferencesResourceBundlesFail",
- getContextName()), e);
- } catch (IllegalAccessException e) {
- log.warn(sm.getString(
- "webappClassLoader.clearReferencesResourceBundlesFail",
- getContextName()), e);
- }
- }
-
-
- /**
* Find specified class in local repositories.
*
* @param name The binary name of the class to be loaded
@@ -2449,20 +2144,38 @@ public abstract class WebappClassLoaderBase extends URLClassLoader
*/
protected Class<?> findClassInternal(String name) {
- String path = binaryNameToPath(name, true);
-
- ResourceEntry entry = null;
+ checkStateForResourceLoading(name);
- if (securityManager != null) {
- PrivilegedAction<ResourceEntry> dp =
- new PrivilegedFindResourceByName(name, path);
- entry = AccessController.doPrivileged(dp);
- } else {
- entry = findResourceInternal(name, path);
+ if (name == null) {
+ return null;
}
+ String path = binaryNameToPath(name, true);
+
+ ResourceEntry entry = resourceEntries.get(path);
+ WebResource resource = null;
if (entry == null) {
- return null;
+ resource = resources.getClassLoaderResource(path);
+
+ if (!resource.exists()) {
+ return null;
+ }
+
+ entry = new ResourceEntry();
+ entry.lastModified = resource.getLastModified();
+
+ // Add the entry in the local resource repository
+ synchronized (resourceEntries) {
+ // Ensures that all the threads which may be in a race to load
+ // a particular class all end up with the same ResourceEntry
+ // instance
+ ResourceEntry entry2 = resourceEntries.get(path);
+ if (entry2 == null) {
+ resourceEntries.put(path, entry);
+ } else {
+ entry = entry2;
+ }
+ }
}
Class<?> clazz = entry.loadedClass;
@@ -2474,21 +2187,18 @@ public abstract class WebappClassLoaderBase extends URLClassLoader
if (clazz != null)
return clazz;
- WebResource webResource = entry.webResource;
- if (webResource == null) {
- webResource = resources.getClassLoaderResource(path);
- } else {
- entry.webResource = null;
+ if (resource == null) {
+ resource = resources.getClassLoaderResource(path);
}
- if (!webResource.exists()) {
+ if (!resource.exists()) {
return null;
}
- byte[] binaryContent = webResource.getContent();
- Manifest manifest = webResource.getManifest();
- URL codeBase = webResource.getCodeBase();
- Certificate[] certificates = webResource.getCertificates();
+ byte[] binaryContent = resource.getContent();
+ Manifest manifest = resource.getManifest();
+ URL codeBase = resource.getCodeBase();
+ Certificate[] certificates = resource.getCertificates();
if (transformers.size() > 0) {
// If the resource is a class just being loaded, decorate it
@@ -2563,11 +2273,7 @@ public abstract class WebappClassLoaderBase extends URLClassLoader
sm.getString("webappClassLoader.wrongVersion",
name));
}
- // Now the class has been defined, clear the elements of the local
- // resource cache that are no longer required.
entry.loadedClass = clazz;
- // Retain entry.source in case of a getResourceAsStream() call on
- // the class file after the class has been defined.
}
return clazz;
@@ -2599,95 +2305,12 @@ public abstract class WebappClassLoaderBase extends URLClassLoader
/**
- * Find specified resource in local repositories.
- *
- * @param name the resource name
- * @param path the resource path
- *
- * @return the loaded resource, or null if the resource isn't found
- */
- protected ResourceEntry findResourceInternal(final String name, final String path) {
-
- checkStateForResourceLoading(name);
-
- if (name == null || path == null) {
- return null;
- }
-
- WebResource resource = null;
-
- ResourceEntry entry = resourceEntries.get(path);
- if (entry != null) {
- return entry;
- }
-
- resource = resources.getClassLoaderResource(path);
-
- if (!resource.exists()) {
- return null;
- }
-
- entry = new ResourceEntry();
- entry.source = resource.getURL();
- entry.lastModified = resource.getLastModified();
- entry.webResource = resource;
-
- boolean fileNeedConvert = false;
- if (needConvert && path.endsWith(".properties")) {
- fileNeedConvert = true;
- }
-
- /* Only cache the binary content if there is some content
- * available one of the following is true:
- * a) The file needs conversion to address encoding issues (see
- * below)
- * or
- * b) The resource is a service provider configuration file located
- * under META=INF/services
- *
- * In all other cases do not cache the content to prevent
- * excessive memory usage if large resources are present (see
- * https://bz.apache.org/bugzilla/show_bug.cgi?id=53081).
- */
- if (path.startsWith(SERVICES_PREFIX) || fileNeedConvert) {
- byte[] binaryContent = resource.getContent();
- if (binaryContent != null) {
- if (fileNeedConvert) {
- // Workaround for certain files on platforms that use
- // EBCDIC encoding, when they are read through FileInputStream.
- // See commit message of rev.303915 for details
- // http://svn.apache.org/viewvc?view=revision&revision=303915
- String str = new String(binaryContent);
- try {
- binaryContent = str.getBytes(StandardCharsets.UTF_8);
- } catch (Exception e) {
- return null;
- }
- }
- entry.binaryContent = binaryContent;
- }
- }
-
- // Add the entry in the local resource repository
- synchronized (resourceEntries) {
- // Ensures that all the threads which may be in a race to load
- // a particular class all end up with the same ResourceEntry
- // instance
- ResourceEntry entry2 = resourceEntries.get(path);
- if (entry2 == null) {
- resourceEntries.put(path, entry);
- } else {
- entry = entry2;
- }
- }
-
- return entry;
- }
-
-
- /**
* Returns true if the specified package name is sealed according to the
* given manifest.
+ *
+ * @param name Path name to check
+ * @param man Associated manifest
+ * @return <code>true</code> if the manifest associated says it is sealed
*/
protected boolean isPackageSealed(String name, Manifest man) {
@@ -2708,39 +2331,12 @@ public abstract class WebappClassLoaderBase extends URLClassLoader
/**
- * Finds the resource with the given name if it has previously been
- * loaded and cached by this class loader, and return an input stream
- * to the resource data. If this resource has not been cached, return
- * <code>null</code>.
- *
- * @param name Name of the resource to return
- */
- protected InputStream findLoadedResource(String name) {
-
- String path = nameToPath(name);
-
- ResourceEntry entry = resourceEntries.get(path);
- if (entry != null) {
- if (entry.binaryContent != null)
- return new ByteArrayInputStream(entry.binaryContent);
- else {
- try {
- return entry.source.openStream();
- } catch (IOException ioe) {
- // Ignore
- }
- }
- }
- return null;
- }
-
-
- /**
* Finds the class with the given name if it has previously been
* loaded and cached by this class loader, and return the Class object.
* If this class has not been cached, return <code>null</code>.
*
* @param name The binary name of the resource to return
+ * @return a loaded class
*/
protected Class<?> findLoadedClass0(String name) {
@@ -2800,7 +2396,8 @@ public abstract class WebappClassLoaderBase extends URLClassLoader
}
if (name.startsWith("el.", 6) ||
name.startsWith("servlet.", 6) ||
- name.startsWith("websocket.", 6)) {
+ name.startsWith("websocket.", 6) ||
+ name.startsWith("security.auth.message.", 6)) {
return true;
}
} else if (!isClassName && ch == '/') {
@@ -2810,7 +2407,8 @@ public abstract class WebappClassLoaderBase extends URLClassLoader
}
if (name.startsWith("el/", 6) ||
name.startsWith("servlet/", 6) ||
- name.startsWith("websocket/", 6)) {
+ name.startsWith("websocket/", 6) ||
+ name.startsWith("security/auth/message/", 6)) {
return true;
}
}
@@ -2865,8 +2463,8 @@ public abstract class WebappClassLoaderBase extends URLClassLoader
*
* @param name class name
* @return <code>true</code> if the class should be filtered
- * @deprecated Use {@link #filter(String, boolean)}.
- * Unused. Will be removed in Tomcat 9 onwards.
+ * @deprecated Use {@link #filter(String, boolean)} This will be removed in
+ * Tomcat 9
*/
@Deprecated
protected boolean filter(String name) {
@@ -2874,20 +2472,6 @@ public abstract class WebappClassLoaderBase extends URLClassLoader
}
- /**
- * Unused.
- *
- * @param name usused
- * @return Always <code>true</code>
- *
- * @deprecated Unused. Will be removed in Tomcat 9 onwards.
- */
- @Deprecated
- protected boolean validate(String name) {
- return true;
- }
-
-
@Override
protected void addURL(URL url) {
super.addURL(url);
diff --git a/java/org/apache/catalina/loader/WebappLoader.java b/java/org/apache/catalina/loader/WebappLoader.java
index 4a78e5f..826aa37 100644
--- a/java/org/apache/catalina/loader/WebappLoader.java
+++ b/java/org/apache/catalina/loader/WebappLoader.java
@@ -24,7 +24,6 @@ import java.io.FilePermission;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Constructor;
-import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.net.URLDecoder;
@@ -39,6 +38,8 @@ import org.apache.catalina.LifecycleException;
import org.apache.catalina.LifecycleState;
import org.apache.catalina.Loader;
import org.apache.catalina.util.LifecycleMBeanBase;
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.ExceptionUtils;
import org.apache.tomcat.util.modeler.Registry;
import org.apache.tomcat.util.res.StringManager;
@@ -112,7 +113,7 @@ public class WebappLoader extends LifecycleMBeanBase
* This class should extend WebappClassLoaderBase, otherwise, a different
* loader implementation must be used.
*/
- private String loaderClass = WebappClassLoader.class.getName();
+ private String loaderClass = ParallelWebappClassLoader.class.getName();
/**
@@ -219,7 +220,7 @@ public class WebappLoader extends LifecycleMBeanBase
/**
- * Return the ClassLoader class name.
+ * @return the ClassLoader class name.
*/
public String getLoaderClass() {
return (this.loaderClass);
@@ -601,8 +602,7 @@ public class WebappLoader extends LifecycleMBeanBase
private boolean buildClassPath(StringBuilder classpath, ClassLoader loader) {
if (loader instanceof URLClassLoader) {
- URL repositories[] =
- ((URLClassLoader) loader).getURLs();
+ URL repositories[] = ((URLClassLoader) loader).getURLs();
for (int i = 0; i < repositories.length; i++) {
String repository = repositories[i].toString();
if (repository.startsWith("file://"))
@@ -617,16 +617,20 @@ public class WebappLoader extends LifecycleMBeanBase
classpath.append(File.pathSeparator);
classpath.append(repository);
}
- } else {
- String cp = getClasspath(loader);
- if (cp == null) {
- log.info( "Unknown loader " + loader + " " + loader.getClass());
- } else {
- if (classpath.length() > 0)
+ } else if (loader == ClassLoader.getSystemClassLoader()){
+ // Java 9 onwards. The internal class loaders no longer extend
+ // URLCLassLoader
+ String cp = System.getProperty("java.class.path");
+ if (cp != null && cp.length() > 0) {
+ if (classpath.length() > 0) {
classpath.append(File.pathSeparator);
+ }
classpath.append(cp);
}
return false;
+ } else {
+ log.info( "Unknown loader " + loader + " " + loader.getClass());
+ return false;
}
return true;
}
@@ -641,30 +645,8 @@ public class WebappLoader extends LifecycleMBeanBase
return result;
}
- // try to extract the classpath from a loader that is not URLClassLoader
- private String getClasspath( ClassLoader loader ) {
- try {
- Method m=loader.getClass().getMethod("getClasspath", new Class[] {});
- if( log.isTraceEnabled())
- log.trace("getClasspath " + m );
- Object o=m.invoke( loader, new Object[] {} );
- if( log.isDebugEnabled() )
- log.debug("gotClasspath " + o);
- if( o instanceof String )
- return (String)o;
- return null;
- } catch( Exception ex ) {
- Throwable t = ExceptionUtils.unwrapInvocationTargetException(ex);
- ExceptionUtils.handleThrowable(t);
- if (log.isDebugEnabled())
- log.debug("getClasspath ", ex);
- }
- return null;
- }
-
- private static final org.apache.juli.logging.Log log=
- org.apache.juli.logging.LogFactory.getLog( WebappLoader.class );
+ private static final Log log = LogFactory.getLog(WebappLoader.class);
@Override
diff --git a/java/org/apache/catalina/loader/mbeans-descriptors.xml b/java/org/apache/catalina/loader/mbeans-descriptors.xml
index 932e919..597ea51 100644
--- a/java/org/apache/catalina/loader/mbeans-descriptors.xml
+++ b/java/org/apache/catalina/loader/mbeans-descriptors.xml
@@ -1,4 +1,4 @@
-<?xml version="1.0"?>
+<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
diff --git a/java/org/apache/catalina/manager/HTMLManagerServlet.java b/java/org/apache/catalina/manager/HTMLManagerServlet.java
index 2981e07..32f90a4 100644
--- a/java/org/apache/catalina/manager/HTMLManagerServlet.java
+++ b/java/org/apache/catalina/manager/HTMLManagerServlet.java
@@ -304,6 +304,7 @@ public final class HTMLManagerServlet extends ManagerServlet {
* @param config URL of the context configuration file to be deployed
* @param cn Name of the application to be deployed
* @param war URL of the web application archive to be deployed
+ * @param smClient internationalized strings
* @return message String
*/
protected String deployInternal(String config, ContextName cn, String war,
@@ -324,6 +325,8 @@ public final class HTMLManagerServlet extends ManagerServlet {
* @param request The request
* @param response The response
* @param message a message to display
+ * @param smClient internationalized strings
+ * @throws IOException an IO error occurred
*/
protected void list(HttpServletRequest request,
HttpServletResponse response,
@@ -756,8 +759,9 @@ public final class HTMLManagerServlet extends ManagerServlet {
* Extract the expiration request parameter
*
* @param cn Name of the application from which to expire sessions
- * @param req
- * @param smClient StringManager for the client's locale
+ * @param req The Servlet request
+ * @param smClient StringManager for the client's locale
+ * @return message string
*/
protected String expireSessions(ContextName cn, HttpServletRequest req,
StringManager smClient) {
@@ -774,12 +778,14 @@ public final class HTMLManagerServlet extends ManagerServlet {
}
/**
+ * Handle session operations.
*
- * @param req
- * @param resp
- * @param smClient StringManager for the client's locale
- * @throws ServletException
- * @throws IOException
+ * @param cn Name of the application for the sessions operation
+ * @param req The Servlet request
+ * @param resp The Servlet response
+ * @param smClient StringManager for the client's locale
+ * @throws ServletException Propagated Servlet error
+ * @throws IOException An IO error occurred
*/
protected void doSessions(ContextName cn, HttpServletRequest req,
HttpServletResponse resp, StringManager smClient)
@@ -864,13 +870,13 @@ public final class HTMLManagerServlet extends ManagerServlet {
}
/**
- *
+ * List session.
* @param cn Name of the application for which the sessions will be listed
- * @param req
- * @param resp
- * @param smClient StringManager for the client's locale
- * @throws ServletException
- * @throws IOException
+ * @param req The Servlet request
+ * @param resp The Servlet response
+ * @param smClient StringManager for the client's locale
+ * @throws ServletException Propagated Servlet error
+ * @throws IOException An IO error occurred
*/
protected void displaySessionsListPage(ContextName cn,
HttpServletRequest req, HttpServletResponse resp,
@@ -913,12 +919,15 @@ public final class HTMLManagerServlet extends ManagerServlet {
}
/**
+ * Display session details.
*
- * @param req
- * @param resp
- * @param smClient StringManager for the client's locale
- * @throws ServletException
- * @throws IOException
+ * @param req The Servlet request
+ * @param resp The Servlet response
+ * @param cn Name of the application for which the sessions will be listed
+ * @param sessionId the session id
+ * @param smClient StringManager for the client's locale
+ * @throws ServletException Propagated Servlet error
+ * @throws IOException An IO error occurred
*/
protected void displaySessionDetailPage(HttpServletRequest req,
HttpServletResponse resp, ContextName cn, String sessionId,
@@ -935,16 +944,16 @@ public final class HTMLManagerServlet extends ManagerServlet {
}
/**
- * Invalidate HttpSessions
+ * Invalidate specified sessions.
+ *
* @param cn Name of the application for which sessions are to be
* invalidated
- * @param sessionIds
+ * @param sessionIds the session ids of the sessions
* @param smClient StringManager for the client's locale
* @return number of invalidated sessions
- * @throws IOException
*/
protected int invalidateSessions(ContextName cn, String[] sessionIds,
- StringManager smClient) throws IOException {
+ StringManager smClient) {
if (null == sessionIds) {
return 0;
}
@@ -979,14 +988,13 @@ public final class HTMLManagerServlet extends ManagerServlet {
* Removes an attribute from an HttpSession
* @param cn Name of the application hosting the session from which the
* attribute is to be removed
- * @param sessionId
- * @param attributeName
+ * @param sessionId the session id
+ * @param attributeName the attribute name
* @param smClient StringManager for the client's locale
* @return true if there was an attribute removed, false otherwise
- * @throws IOException
*/
protected boolean removeSessionAttribute(ContextName cn, String sessionId,
- String attributeName, StringManager smClient) throws IOException {
+ String attributeName, StringManager smClient) {
HttpSession session =
getSessionForNameAndId(cn, sessionId, smClient).getSession();
if (null == session) {
diff --git a/java/org/apache/catalina/manager/JMXProxyServlet.java b/java/org/apache/catalina/manager/JMXProxyServlet.java
index 2204a53..d9f3246 100644
--- a/java/org/apache/catalina/manager/JMXProxyServlet.java
+++ b/java/org/apache/catalina/manager/JMXProxyServlet.java
@@ -38,12 +38,12 @@ import org.apache.catalina.mbeans.MBeanDumper;
import org.apache.tomcat.util.modeler.Registry;
/**
- * This servlet will dump JMX attributes in a simple format
- * and implement proxy services for modeler.
+ * This servlet will dump JMX attributes in a simple format and implement proxy
+ * services for modeler.
*
* @author Costin Manolache
*/
-public class JMXProxyServlet extends HttpServlet {
+public class JMXProxyServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@@ -58,6 +58,7 @@ public class JMXProxyServlet extends HttpServlet {
protected transient MBeanServer mBeanServer = null;
protected transient Registry registry;
+
// --------------------------------------------------------- Public Methods
/**
* Initialize this servlet.
@@ -80,55 +81,54 @@ public class JMXProxyServlet extends HttpServlet {
* @exception ServletException if a servlet-specified error occurs
*/
@Override
- public void doGet(HttpServletRequest request,
- HttpServletResponse response)
- throws IOException, ServletException
- {
+ public void doGet(HttpServletRequest request, HttpServletResponse response)
+ throws IOException, ServletException {
response.setContentType("text/plain");
PrintWriter writer = response.getWriter();
- if( mBeanServer==null ) {
+ if (mBeanServer == null) {
writer.println("Error - No mbean server");
return;
}
- String qry=request.getParameter("set");
- if( qry!= null ) {
- String name=request.getParameter("att");
- String val=request.getParameter("val");
+ String qry = request.getParameter("set");
+ if (qry != null) {
+ String name = request.getParameter("att");
+ String val = request.getParameter("val");
- setAttribute( writer, qry, name, val );
+ setAttribute(writer, qry, name, val);
return;
}
- qry=request.getParameter("get");
- if( qry!= null ) {
- String name=request.getParameter("att");
- getAttribute( writer, qry, name, request.getParameter("key") );
+ qry = request.getParameter("get");
+ if (qry != null) {
+ String name = request.getParameter("att");
+ getAttribute(writer, qry, name, request.getParameter("key"));
return;
}
qry = request.getParameter("invoke");
- if(qry != null) {
- String opName=request.getParameter("op");
+ if (qry != null) {
+ String opName = request.getParameter("op");
String[] params = getInvokeParameters(request.getParameter("ps"));
invokeOperation(writer, qry, opName, params);
return;
}
- qry=request.getParameter("qry");
- if( qry == null ) {
+ qry = request.getParameter("qry");
+ if (qry == null) {
qry = "*:*";
}
- listBeans( writer, qry );
+ listBeans(writer, qry);
}
+
public void getAttribute(PrintWriter writer, String onameStr, String att, String key) {
try {
ObjectName oname = new ObjectName(onameStr);
Object value = mBeanServer.getAttribute(oname, att);
- if(null != key && value instanceof CompositeData)
- value = ((CompositeData)value).get(key);
+ if (null != key && value instanceof CompositeData)
+ value = ((CompositeData) value).get(key);
String valueStr;
if (value != null) {
@@ -142,7 +142,7 @@ public class JMXProxyServlet extends HttpServlet {
writer.print("' - ");
writer.print(att);
- if(null != key) {
+ if (null != key) {
writer.print(" - key '");
writer.print(key);
writer.print("'");
@@ -157,24 +157,23 @@ public class JMXProxyServlet extends HttpServlet {
}
}
- public void setAttribute( PrintWriter writer,
- String onameStr, String att, String val )
- {
+
+ public void setAttribute(PrintWriter writer, String onameStr, String att, String val) {
try {
setAttributeInternal(onameStr, att, val);
writer.println("OK - Attribute set");
- } catch( Exception ex ) {
+ } catch (Exception ex) {
writer.println("Error - " + ex.toString());
ex.printStackTrace(writer);
}
}
- public void listBeans( PrintWriter writer, String qry )
- {
+
+ public void listBeans(PrintWriter writer, String qry) {
Set<ObjectName> names = null;
try {
- names=mBeanServer.queryNames(new ObjectName(qry), null);
+ names = mBeanServer.queryNames(new ObjectName(qry), null);
writer.println("OK - Number of results: " + names.size());
writer.println();
} catch (Exception ex) {
@@ -187,11 +186,12 @@ public class JMXProxyServlet extends HttpServlet {
writer.print(dump);
}
+
/**
* Determines if a type is supported by the {@link JMXProxyServlet}.
*
- * @param type The type to check
- * @return Always returns <code>true</code>
+ * @param type The type to check
+ * @return Always returns <code>true</code>
*/
public boolean isSupported(String type) {
return true;
@@ -208,7 +208,7 @@ public class JMXProxyServlet extends HttpServlet {
} else {
writer.println("OK - Operation " + op + " without return value");
}
- } catch( Exception ex ) {
+ } catch (Exception ex) {
writer.println("Error - " + ex.toString());
ex.printStackTrace(writer);
}
@@ -217,9 +217,10 @@ public class JMXProxyServlet extends HttpServlet {
/**
* Parses parameter values from a parameter string.
+ *
* @param paramString The string containing comma-separated
- * operation-invocation parameters, or
- * <code>null</code> if there are no parameters.
+ * operation-invocation parameters, or <code>null</code> if there
+ * are no parameters.
* @return An array of String parameters (empty array if
* <code>paramString</code> was <code>null</code>).
*/
@@ -230,46 +231,46 @@ public class JMXProxyServlet extends HttpServlet {
return paramString.split(",");
}
+
/**
* Sets an MBean attribute's value.
*/
- private void setAttributeInternal(String onameStr,
- String attributeName,
- String value)
- throws OperationsException, MBeanException, ReflectionException {
- ObjectName oname=new ObjectName( onameStr );
- String type=registry.getType(oname, attributeName);
- Object valueObj=registry.convertValue(type, value );
- mBeanServer.setAttribute( oname, new Attribute(attributeName, valueObj));
+ private void setAttributeInternal(String onameStr, String attributeName, String value)
+ throws OperationsException, MBeanException, ReflectionException {
+ ObjectName oname = new ObjectName(onameStr);
+ String type = registry.getType(oname, attributeName);
+ Object valueObj = registry.convertValue(type, value);
+ mBeanServer.setAttribute(oname, new Attribute(attributeName, valueObj));
}
+
/**
* Invokes an operation on an MBean.
+ *
* @param onameStr The name of the MBean.
* @param operation The name of the operation to invoke.
* @param parameters An array of Strings containing the parameters to the
- * operation. They will be converted to the appropriate
- * types to call the reuested operation.
+ * operation. They will be converted to the appropriate types to
+ * call the reuested operation.
* @return The value returned by the requested operation.
*/
- private Object invokeOperationInternal(String onameStr,
- String operation,
- String[] parameters)
- throws OperationsException, MBeanException, ReflectionException {
- ObjectName oname=new ObjectName( onameStr );
- MBeanOperationInfo methodInfo = registry.getMethodInfo(oname,operation);
+ private Object invokeOperationInternal(String onameStr, String operation, String[] parameters)
+ throws OperationsException, MBeanException, ReflectionException {
+ ObjectName oname = new ObjectName(onameStr);
+ MBeanOperationInfo methodInfo = registry.getMethodInfo(oname, operation);
MBeanParameterInfo[] signature = methodInfo.getSignature();
String[] signatureTypes = new String[signature.length];
Object[] values = new Object[signature.length];
for (int i = 0; i < signature.length; i++) {
- MBeanParameterInfo pi = signature[i];
- signatureTypes[i] = pi.getType();
- values[i] = registry.convertValue(pi.getType(), parameters[i] );
- }
+ MBeanParameterInfo pi = signature[i];
+ signatureTypes[i] = pi.getType();
+ values[i] = registry.convertValue(pi.getType(), parameters[i]);
+ }
- return mBeanServer.invoke(oname,operation,values,signatureTypes);
+ return mBeanServer.invoke(oname, operation, values, signatureTypes);
}
+
private void output(String indent, PrintWriter writer, Object result) {
if (result instanceof Object[]) {
for (Object obj : (Object[]) result) {
diff --git a/java/org/apache/catalina/manager/JspHelper.java b/java/org/apache/catalina/manager/JspHelper.java
index dd0cc8f..197618f 100644
--- a/java/org/apache/catalina/manager/JspHelper.java
+++ b/java/org/apache/catalina/manager/JspHelper.java
@@ -65,8 +65,8 @@ public class JspHelper {
/**
* Try to get user name from the session, if possible.
- * @param in_session
- * @return String
+ * @param in_session The Servlet session
+ * @return the user name
*/
public static String guessDisplayUserFromSession(Session in_session) {
Object user = SessionUtils.guessUserFromSession(in_session);
@@ -202,6 +202,8 @@ public class JspHelper {
* ' -> '
*
* See also OutSupport.writeEscapedXml().
+ * @param buffer The XML to escape
+ * @return the escaped XML
*/
@SuppressWarnings("null") // escapedBuffer cannot be null
public static String escapeXml(String buffer) {
diff --git a/java/org/apache/catalina/manager/LocalStrings.properties b/java/org/apache/catalina/manager/LocalStrings.properties
index 908f57e..8107cc2 100644
--- a/java/org/apache/catalina/manager/LocalStrings.properties
+++ b/java/org/apache/catalina/manager/LocalStrings.properties
@@ -90,7 +90,7 @@ managerServlet.noCommand=FAIL - No command was specified
managerServlet.noContext=FAIL - No context exists named {0}
managerServlet.noGlobal=FAIL - No global JNDI resources are available
managerServlet.noManager=FAIL - No manager exists for path {0}
-managerServlet.noSelf=FAIL - The manager can not reload, undeploy, stop, or undeploy itself
+managerServlet.noSelf=FAIL - The manager cannot reload, undeploy, stop, or undeploy itself
managerServlet.noWrapper=Container has not called setWrapper() for this servlet
managerServlet.notDeployed=FAIL - Context {0} is defined in server.xml and may not be undeployed
managerServlet.notSslConnector=SSL is not enabled for this connector
diff --git a/java/org/apache/catalina/manager/ManagerServlet.java b/java/org/apache/catalina/manager/ManagerServlet.java
index d9ec546..c10d1c9 100644
--- a/java/org/apache/catalina/manager/ManagerServlet.java
+++ b/java/org/apache/catalina/manager/ManagerServlet.java
@@ -61,6 +61,7 @@ import org.apache.catalina.util.ServerInfo;
import org.apache.tomcat.util.Diagnostics;
import org.apache.tomcat.util.ExceptionUtils;
import org.apache.tomcat.util.modeler.Registry;
+import org.apache.tomcat.util.net.SSLHostConfig;
import org.apache.tomcat.util.res.StringManager;
@@ -102,7 +103,7 @@ import org.apache.tomcat.util.res.StringManager;
* (fully qualified Java class name), if available.</li>
* <li><b>/serverinfo</b> - Display system OS and JVM properties.
* <li><b>/sessions</b> - Deprecated. Use expire.
- * <li><b>/expire?path=/xxx</b> - List session idle timeinformation about the
+ * <li><b>/expire?path=/xxx</b> - List session idle time information about the
* web application attached to context path <code>/xxx</code> for this
* virtual host.</li>
* <li><b>/expire?path=/xxx&idle=mm</b> - Expire sessions
@@ -500,6 +501,10 @@ public class ManagerServlet extends HttpServlet implements ContainerServlet {
/**
* Find potential memory leaks caused by web application reload.
+ *
+ * @param statusLine Print a status line
+ * @param writer The output writer
+ * @param smClient StringManager for the client's locale
*/
protected void findleaks(boolean statusLine, PrintWriter writer,
StringManager smClient) {
@@ -530,10 +535,11 @@ public class ManagerServlet extends HttpServlet implements ContainerServlet {
/**
+ * Write some VM info.
*
- * Write some VM info
- *
- * @param writer
+ * @param writer The output writer
+ * @param smClient StringManager for the client's locale
+ * @param requestedLocales the client's locales
*/
protected void vmInfo(PrintWriter writer, StringManager smClient,
Enumeration<Locale> requestedLocales) {
@@ -542,10 +548,11 @@ public class ManagerServlet extends HttpServlet implements ContainerServlet {
}
/**
+ * Write a JVM thread dump.
*
- * Write a JVM thread dump
- *
- * @param writer
+ * @param writer The output writer
+ * @param smClient StringManager for the client's locale
+ * @param requestedLocales the client's locales
*/
protected void threadDump(PrintWriter writer, StringManager smClient,
Enumeration<Locale> requestedLocales) {
@@ -932,6 +939,7 @@ public class ManagerServlet extends HttpServlet implements ContainerServlet {
* Render a list of the currently active Contexts in our virtual host.
*
* @param writer Writer to render to
+ * @param smClient i18n support for current client's locale
*/
protected void list(PrintWriter writer, StringManager smClient) {
@@ -971,6 +979,7 @@ public class ManagerServlet extends HttpServlet implements ContainerServlet {
*
* @param writer Writer to render to
* @param cn Name of the application to be restarted
+ * @param smClient i18n support for current client's locale
*/
protected void reload(PrintWriter writer, ContextName cn,
StringManager smClient) {
@@ -1010,8 +1019,10 @@ public class ManagerServlet extends HttpServlet implements ContainerServlet {
/**
* Render a list of available global JNDI resources.
*
+ * @param writer Writer to render to
* @param type Fully qualified class name of the resource type of interest,
* or <code>null</code> to list resources of all types
+ * @param smClient i18n support for current client's locale
*/
protected void resources(PrintWriter writer, String type,
StringManager smClient) {
@@ -1058,6 +1069,14 @@ public class ManagerServlet extends HttpServlet implements ContainerServlet {
/**
* List the resources of the given context.
+ * @param writer Writer to render to
+ * @param prefix Path for recursion
+ * @param namingContext The naming context for lookups
+ * @param type Fully qualified class name of the resource type of interest,
+ * or <code>null</code> to list resources of all types
+ * @param clazz The resource class or <code>null</code> to list
+ * resources of all types
+ * @param smClient i18n support for current client's locale
*/
protected void printResources(PrintWriter writer, String prefix,
javax.naming.Context namingContext,
@@ -1098,7 +1117,8 @@ public class ManagerServlet extends HttpServlet implements ContainerServlet {
/**
* Writes System OS and JVM properties.
* @param writer Writer to render to
- */
+ * @param smClient i18n support for current client's locale
+ */
protected void serverinfo(PrintWriter writer, StringManager smClient) {
if (debug >= 1)
log("serverinfo");
@@ -1134,6 +1154,7 @@ public class ManagerServlet extends HttpServlet implements ContainerServlet {
* @param writer Writer to render to
* @param cn Name of the application to list session information for
* @param idle Expire all sessions with idle time > idle for this context
+ * @param smClient i18n support for current client's locale
*/
protected void sessions(PrintWriter writer, ContextName cn, int idle,
StringManager smClient) {
@@ -1236,11 +1257,12 @@ public class ManagerServlet extends HttpServlet implements ContainerServlet {
/**
- *
* Extract the expiration request parameter
*
- * @param cn
- * @param req
+ * @param writer Writer to render to
+ * @param cn Name of the application to list session information for
+ * @param req The Servlet request
+ * @param smClient i18n support for current client's locale
*/
protected void expireSessions(PrintWriter writer, ContextName cn,
HttpServletRequest req, StringManager smClient) {
@@ -1261,6 +1283,7 @@ public class ManagerServlet extends HttpServlet implements ContainerServlet {
*
* @param writer Writer to render to
* @param cn Name of the application to be started
+ * @param smClient i18n support for current client's locale
*/
protected void start(PrintWriter writer, ContextName cn,
StringManager smClient) {
@@ -1306,6 +1329,7 @@ public class ManagerServlet extends HttpServlet implements ContainerServlet {
*
* @param writer Writer to render to
* @param cn Name of the application to be stopped
+ * @param smClient i18n support for current client's locale
*/
protected void stop(PrintWriter writer, ContextName cn,
StringManager smClient) {
@@ -1349,6 +1373,7 @@ public class ManagerServlet extends HttpServlet implements ContainerServlet {
*
* @param writer Writer to render to
* @param cn Name of the application to be removed
+ * @param smClient i18n support for current client's locale
*/
protected void undeploy(PrintWriter writer, ContextName cn,
StringManager smClient) {
@@ -1430,6 +1455,10 @@ public class ManagerServlet extends HttpServlet implements ContainerServlet {
/**
* Invoke the isDeployed method on the deployer.
+ *
+ * @param name The webapp name
+ * @return <code>true</code> if a webapp with that name is deployed
+ * @throws Exception Propagate JMX invocation error
*/
protected boolean isDeployed(String name)
throws Exception {
@@ -1443,6 +1472,9 @@ public class ManagerServlet extends HttpServlet implements ContainerServlet {
/**
* Invoke the check method on the deployer.
+ *
+ * @param name The webapp name
+ * @throws Exception Propagate JMX invocation error
*/
protected void check(String name)
throws Exception {
@@ -1454,6 +1486,10 @@ public class ManagerServlet extends HttpServlet implements ContainerServlet {
/**
* Invoke the isServiced method on the deployer.
+ *
+ * @param name The webapp name
+ * @return <code>true</code> if a webapp with that name is being serviced
+ * @throws Exception Propagate JMX invocation error
*/
protected boolean isServiced(String name)
throws Exception {
@@ -1467,6 +1503,9 @@ public class ManagerServlet extends HttpServlet implements ContainerServlet {
/**
* Invoke the addServiced method on the deployer.
+ *
+ * @param name The webapp name
+ * @throws Exception Propagate JMX invocation error
*/
protected void addServiced(String name)
throws Exception {
@@ -1478,6 +1517,9 @@ public class ManagerServlet extends HttpServlet implements ContainerServlet {
/**
* Invoke the removeServiced method on the deployer.
+ *
+ * @param name The webapp name
+ * @throws Exception Propagate JMX invocation error
*/
protected void removeServiced(String name)
throws Exception {
@@ -1492,6 +1534,7 @@ public class ManagerServlet extends HttpServlet implements ContainerServlet {
* subdirectories recursively. The code assumes that the directory exists.
*
* @param dir File object representing the directory to be deleted.
+ * @return <code>true</code> if the deletion was successful
*/
protected boolean undeployDir(File dir) {
@@ -1580,6 +1623,7 @@ public class ManagerServlet extends HttpServlet implements ContainerServlet {
*
* @param src File object representing the source
* @param dest File object representing the destination
+ * @return <code>true</code> if the copy was successful
*/
public static boolean copy(File src, File dest) {
boolean result = false;
@@ -1600,6 +1644,8 @@ public class ManagerServlet extends HttpServlet implements ContainerServlet {
*
* @param src File object representing the source
* @param dest File object representing the destination
+ * @param buf Temp byte buffer
+ * @return <code>true</code> if the copy was successful
*/
public static boolean copyInternal(File src, File dest, byte[] buf) {
@@ -1648,17 +1694,22 @@ public class ManagerServlet extends HttpServlet implements ContainerServlet {
Service s = e.getService();
Connector connectors[] = s.findConnectors();
for (Connector connector : connectors) {
- Set<String> cipherList = new HashSet<>();
if (Boolean.TRUE.equals(connector.getProperty("SSLEnabled"))) {
- String[] ciphersUsed =
- (String[]) connector.getProperty("ciphersUsed");
- for (String cipherUsed : ciphersUsed) {
- cipherList.add(cipherUsed);
+ SSLHostConfig[] sslHostConfigs = connector.getProtocolHandler().findSslHostConfigs();
+ for (SSLHostConfig sslHostConfig : sslHostConfigs) {
+ String name = connector.toString() + "-" + sslHostConfig.getHostName();
+ Set<String> cipherList = new HashSet<>();
+ String[] cipherNames = sslHostConfig.getEnabledCiphers();
+ for (String cipherName : cipherNames) {
+ cipherList.add(cipherName);
+ }
+ result.put(name, cipherList);
}
} else {
+ Set<String> cipherList = new HashSet<>();
cipherList.add(sm.getString("managerServlet.notSslConnector"));
+ result.put(connector.toString(), cipherList);
}
- result.put(connector.toString(), cipherList);
}
return result;
}
diff --git a/java/org/apache/catalina/manager/StatusTransformer.java b/java/org/apache/catalina/manager/StatusTransformer.java
index e656f5a..b95818d 100644
--- a/java/org/apache/catalina/manager/StatusTransformer.java
+++ b/java/org/apache/catalina/manager/StatusTransformer.java
@@ -132,9 +132,6 @@ public class StatusTransformer {
}
- /**
- *
- */
public static void writeFooter(PrintWriter writer, int mode) {
if (mode == 0){
// HTML Tail Section
@@ -146,8 +143,11 @@ public class StatusTransformer {
/**
- * Write the OS state. Mode 0 will generate HTML.
- * Mode 1 will generate XML.
+ * Write the OS state.
+ *
+ * @param writer The output writer
+ * @param mode Mode <code>0</code> will generate HTML.
+ * Mode <code>1</code> will generate XML.
*/
public static void writeOSState(PrintWriter writer, int mode) {
long[] result = new long[16];
@@ -197,8 +197,11 @@ public class StatusTransformer {
/**
- * Write the VM state. Mode 0 will generate HTML.
- * Mode 1 will generate XML.
+ * Write the VM state.
+ * @param writer The output writer
+ * @param mode Mode <code>0</code> will generate HTML.
+ * Mode <code>1</code> will generate XML.
+ * @throws Exception Propagated JMX error
*/
public static void writeVMState(PrintWriter writer, int mode)
throws Exception {
@@ -273,6 +276,15 @@ public class StatusTransformer {
/**
* Write connector state.
+ * @param writer The output writer
+ * @param tpName MBean name of the thread pool
+ * @param name Connector name
+ * @param mBeanServer MBean server
+ * @param globalRequestProcessors MBean names for the global request processors
+ * @param requestProcessors MBean names for the request processors
+ * @param mode Mode <code>0</code> will generate HTML.
+ * Mode <code>1</code> will generate XML.
+ * @throws Exception Propagated JMX error
*/
public static void writeConnectorState(PrintWriter writer,
ObjectName tpName, String name, MBeanServer mBeanServer,
@@ -401,6 +413,12 @@ public class StatusTransformer {
/**
* Write processor state.
+ * @param writer The output writer
+ * @param pName MBean name of the processor
+ * @param mBeanServer MBean server
+ * @param mode Mode <code>0</code> will generate HTML.
+ * Mode <code>1</code> will generate XML.
+ * @throws Exception Propagated JMX error
*/
protected static void writeProcessorState(PrintWriter writer,
ObjectName pName,
@@ -591,6 +609,11 @@ public class StatusTransformer {
/**
* Write applications state.
+ * @param writer The output writer
+ * @param mBeanServer MBean server
+ * @param mode Mode <code>0</code> will generate HTML.
+ * Mode <code>1</code> will generate XML.
+ * @throws Exception Propagated JMX error
*/
public static void writeDetailedState(PrintWriter writer,
MBeanServer mBeanServer, int mode)
@@ -649,6 +672,12 @@ public class StatusTransformer {
/**
* Write context state.
+ * @param writer The output writer
+ * @param objectName The context MBean name
+ * @param mBeanServer MBean server
+ * @param mode Mode <code>0</code> will generate HTML.
+ * Mode <code>1</code> will generate XML.
+ * @throws Exception Propagated JMX error
*/
protected static void writeContext(PrintWriter writer,
ObjectName objectName,
@@ -741,6 +770,12 @@ public class StatusTransformer {
/**
* Write detailed information about a manager.
+ * @param writer The output writer
+ * @param objectName The manager MBean name
+ * @param mBeanServer MBean server
+ * @param mode Mode <code>0</code> will generate HTML.
+ * Mode <code>1</code> will generate XML.
+ * @throws Exception Propagated JMX error
*/
public static void writeManager(PrintWriter writer, ObjectName objectName,
MBeanServer mBeanServer, int mode)
@@ -782,6 +817,12 @@ public class StatusTransformer {
/**
* Write JSP monitoring information.
+ * @param writer The output writer
+ * @param jspMonitorONs The JSP MBean names
+ * @param mBeanServer MBean server
+ * @param mode Mode <code>0</code> will generate HTML.
+ * Mode <code>1</code> will generate XML.
+ * @throws Exception Propagated JMX error
*/
public static void writeJspMonitor(PrintWriter writer,
Set<ObjectName> jspMonitorONs,
@@ -815,6 +856,12 @@ public class StatusTransformer {
/**
* Write detailed information about a wrapper.
+ * @param writer The output writer
+ * @param objectName The wrapper MBean names
+ * @param mBeanServer MBean server
+ * @param mode Mode <code>0</code> will generate HTML.
+ * Mode <code>1</code> will generate XML.
+ * @throws Exception Propagated JMX error
*/
public static void writeWrapper(PrintWriter writer, ObjectName objectName,
MBeanServer mBeanServer, int mode)
@@ -871,6 +918,7 @@ public class StatusTransformer {
* codes in the request URL that is often reported in error messages.
*
* @param obj The message string to be filtered
+ * @return filtered HTML content
*/
public static String filter(Object obj) {
@@ -907,7 +955,9 @@ public class StatusTransformer {
/**
* Display the given size in bytes, either as KB or MB.
*
+ * @param obj The object to format
* @param mb true to display megabytes, false for kilobytes
+ * @return formatted size
*/
public static String formatSize(Object obj, boolean mb) {
@@ -944,7 +994,9 @@ public class StatusTransformer {
/**
* Display the given time in ms, either as ms or s.
*
+ * @param obj The object to format
* @param seconds true to display seconds, false for milliseconds
+ * @return formatted time
*/
public static String formatTime(Object obj, boolean seconds) {
@@ -968,8 +1020,7 @@ public class StatusTransformer {
* Formats the given time (given in seconds) as a string.
*
* @param obj Time object to be formatted as string
- *
- * @return String formatted time
+ * @return formatted time
*/
public static String formatSeconds(Object obj) {
diff --git a/java/org/apache/catalina/manager/host/Constants.java b/java/org/apache/catalina/manager/host/Constants.java
index 83e9dda..c3eebfd 100644
--- a/java/org/apache/catalina/manager/host/Constants.java
+++ b/java/org/apache/catalina/manager/host/Constants.java
@@ -23,38 +23,6 @@ public class Constants {
public static final String Package = "org.apache.catalina.manager.host";
- public static final String BODY_HEADER_SECTION =
- "<title>{0}</title>\n" +
- "</head>\n" +
- "\n" +
- "<body bgcolor=\"#FFFFFF\">\n" +
- "\n" +
- "<table cellspacing=\"4\" border=\"0\">\n" +
- " <tr>\n" +
- " <td colspan=\"2\">\n" +
- " <a href=\"http://www.apache.org/\">\n" +
- " <img border=\"0\" alt=\"The Apache Software Foundation\" align=\"left\"\n" +
- " src=\"{0}/images/asf-logo.gif\">\n" +
- " </a>\n" +
- " <a href=\"http://tomcat.apache.org/\">\n" +
- " <img border=\"0\" alt=\"The Tomcat Servlet/JSP Container\"\n" +
- " align=\"right\" src=\"{0}/images/tomcat.gif\">\n" +
- " </a>\n" +
- " </td>\n" +
- " </tr>\n" +
- "</table>\n" +
- "<hr size=\"1\" noshade=\"noshade\">\n" +
- "<table cellspacing=\"4\" border=\"0\">\n" +
- " <tr>\n" +
- " <td class=\"page-title\" bordercolor=\"#000000\" " +
- "align=\"left\" nowrap>\n" +
- " <font size=\"+2\">{1}</font>\n" +
- " </td>\n" +
- " </tr>\n" +
- "</table>\n" +
- "<br>\n" +
- "\n";
-
public static final String MESSAGE_SECTION =
"<table border=\"1\" cellspacing=\"0\" cellpadding=\"3\">\n" +
" <tr>\n" +
diff --git a/java/org/apache/catalina/manager/host/HTMLHostManagerServlet.java b/java/org/apache/catalina/manager/host/HTMLHostManagerServlet.java
index f4319d1..87334eb 100644
--- a/java/org/apache/catalina/manager/host/HTMLHostManagerServlet.java
+++ b/java/org/apache/catalina/manager/host/HTMLHostManagerServlet.java
@@ -94,7 +94,8 @@ public final class HTMLHostManagerServlet extends HostManagerServlet {
} else if (command.equals("/list")) {
// Nothing to do - always generate list
} else if (command.equals("/add") || command.equals("/remove") ||
- command.equals("/start") || command.equals("/stop")) {
+ command.equals("/start") || command.equals("/stop") ||
+ command.equals("/persist")) {
message = smClient.getString(
"hostManagerServlet.postCommand", command);
} else {
@@ -143,6 +144,8 @@ public final class HTMLHostManagerServlet extends HostManagerServlet {
message = start(name, smClient);
} else if (command.equals("/stop")) {
message = stop(name, smClient);
+ } else if (command.equals("/persist")) {
+ message = persist(smClient);
} else {
//Try GET
doGet(request, response);
@@ -155,7 +158,10 @@ public final class HTMLHostManagerServlet extends HostManagerServlet {
/**
* Add a host using the specified parameters.
*
- * @param name host name
+ * @param request The Servlet request
+ * @param name Host name
+ * @param smClient StringManager for the client's locale
+ * @return output
*/
protected String add(HttpServletRequest request,String name,
StringManager smClient) {
@@ -172,7 +178,9 @@ public final class HTMLHostManagerServlet extends HostManagerServlet {
/**
* Remove the specified host.
*
- * @param name host name
+ * @param name Host name
+ * @param smClient StringManager for the client's locale
+ * @return output
*/
protected String remove(String name, StringManager smClient) {
@@ -189,6 +197,8 @@ public final class HTMLHostManagerServlet extends HostManagerServlet {
* Start the host with the specified name.
*
* @param name Host name
+ * @param smClient StringManager for the client's locale
+ * @return output
*/
protected String start(String name, StringManager smClient) {
@@ -205,6 +215,8 @@ public final class HTMLHostManagerServlet extends HostManagerServlet {
* Stop the host with the specified name.
*
* @param name Host name
+ * @param smClient StringManager for the client's locale
+ * @return output
*/
protected String stop(String name, StringManager smClient) {
@@ -218,12 +230,31 @@ public final class HTMLHostManagerServlet extends HostManagerServlet {
/**
+ * Persist the current configuration to server.xml.
+ *
+ * @param smClient i18n resources localized for the client
+ * @return output
+ */
+ protected String persist(StringManager smClient) {
+
+ StringWriter stringWriter = new StringWriter();
+ PrintWriter printWriter = new PrintWriter(stringWriter);
+
+ super.persist(printWriter, smClient);
+
+ return stringWriter.toString();
+ }
+
+
+ /**
* Render a HTML list of the currently active Contexts in our virtual host,
* and memory and server status information.
*
* @param request The request
* @param response The response
* @param message a message to display
+ * @param smClient StringManager for the client's locale
+ * @throws IOException An IO error occurred
*/
public void list(HttpServletRequest request,
HttpServletResponse response,
@@ -243,8 +274,8 @@ public final class HTMLHostManagerServlet extends HostManagerServlet {
Object[] args = new Object[2];
args[0] = request.getContextPath();
args[1] = smClient.getString("htmlHostManagerServlet.title");
- writer.print(MessageFormat.format
- (Constants.BODY_HEADER_SECTION, args));
+ writer.print(MessageFormat.format(
+ org.apache.catalina.manager.Constants.BODY_HEADER_SECTION, args));
// Message Section
args = new Object[3];
@@ -351,10 +382,10 @@ public final class HTMLHostManagerServlet extends HostManagerServlet {
args[3] = hostsRemove;
if (host == this.installedHost) {
writer.print(MessageFormat.format(
- MANAGER_HOST_ROW_BUTTON_SECTION, args));
+ MANAGER_HOST_ROW_BUTTON_SECTION, args));
} else {
writer.print(MessageFormat.format(
- HOSTS_ROW_BUTTON_SECTION, args));
+ HOSTS_ROW_BUTTON_SECTION, args));
}
}
}
@@ -402,6 +433,14 @@ public final class HTMLHostManagerServlet extends HostManagerServlet {
args[0] = smClient.getString("htmlHostManagerServlet.addButton");
writer.print(MessageFormat.format(ADD_SECTION_END, args));
+ // Persist Configuration Section
+ args = new Object[4];
+ args[0] = smClient.getString("htmlHostManagerServlet.persistTitle");
+ args[1] = response.encodeURL(request.getContextPath() + "/html/persist");
+ args[2] = smClient.getString("htmlHostManagerServlet.persistAllButton");
+ args[3] = smClient.getString("htmlHostManagerServlet.persistAll");
+ writer.print(MessageFormat.format(PERSIST_SECTION, args));
+
// Server Header Section
args = new Object[7];
args[0] = smClient.getString("htmlHostManagerServlet.serverTitle");
@@ -541,4 +580,20 @@ public final class HTMLHostManagerServlet extends HostManagerServlet {
"<br>\n" +
"\n";
+ private static final String PERSIST_SECTION =
+ "<table border=\"1\" cellspacing=\"0\" cellpadding=\"3\">\n" +
+ "<tr>\n" +
+ " <td class=\"title\">{0}</td>\n" +
+ "</tr>\n" +
+ "<tr>\n" +
+ " <td class=\"row-left\">\n" +
+ " <form class=\"inline\" method=\"POST\" action=\"{1}\">" +
+ " <small><input type=\"submit\" value=\"{2}\"></small>" +
+ " </form> {3}\n" +
+ " </td>\n" +
+ "</tr>\n" +
+ "</table>\n" +
+ "<br>\n" +
+ "\n";
+
}
diff --git a/java/org/apache/catalina/manager/host/HostManagerServlet.java b/java/org/apache/catalina/manager/host/HostManagerServlet.java
index 2e05230..267c58d 100644
--- a/java/org/apache/catalina/manager/host/HostManagerServlet.java
+++ b/java/org/apache/catalina/manager/host/HostManagerServlet.java
@@ -20,10 +20,14 @@ import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
+import java.lang.management.ManagementFactory;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.StringTokenizer;
+import javax.management.InstanceNotFoundException;
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
import javax.servlet.ServletException;
import javax.servlet.UnavailableException;
import javax.servlet.http.HttpServlet;
@@ -42,7 +46,6 @@ import org.apache.catalina.startup.HostConfig;
import org.apache.tomcat.util.ExceptionUtils;
import org.apache.tomcat.util.res.StringManager;
-
/**
* Servlet that enables remote management of the virtual hosts installed
* on the server. Normally, this functionality will be protected by
@@ -218,6 +221,8 @@ public class HostManagerServlet
start(writer, name, smClient);
} else if (command.equals("/stop")) {
stop(writer, name, smClient);
+ } else if (command.equals("/persist")) {
+ persist(writer, smClient);
} else {
writer.println(sm.getString("hostManagerServlet.unknownCommand",
command));
@@ -229,7 +234,6 @@ public class HostManagerServlet
}
-
/**
* Add host with the given parameters.
*
@@ -237,7 +241,8 @@ public class HostManagerServlet
* @param writer The output writer
* @param name The host name
* @param htmlMode Flag value
- */
+ * @param smClient StringManager for the client's locale
+ */
protected void add(HttpServletRequest request, PrintWriter writer,
String name, boolean htmlMode, StringManager smClient) {
String aliases = request.getParameter("aliases");
@@ -260,10 +265,11 @@ public class HostManagerServlet
/**
* Extract boolean value from checkbox with default.
- * @param request
- * @param parameter
- * @param theDefault
- * @param htmlMode
+ * @param request The Servlet request
+ * @param parameter The parameter name
+ * @param theDefault Default value
+ * @param htmlMode Flag value
+ * @return the boolean value for the parameter
*/
protected boolean booleanParameter(HttpServletRequest request,
String parameter, boolean theDefault, boolean htmlMode) {
@@ -287,9 +293,6 @@ public class HostManagerServlet
}
- /**
- * Initialize this servlet.
- */
@Override
public void init() throws ServletException {
@@ -322,6 +325,12 @@ public class HostManagerServlet
* @param aliases comma separated alias list
* @param appBase application base for the host
* @param manager should the manager webapp be deployed to the new host ?
+ * @param autoDeploy Flag value
+ * @param deployOnStartup Flag value
+ * @param deployXML Flag value
+ * @param unpackWARs Flag value
+ * @param copyXML Flag value
+ * @param smClient StringManager for the client's locale
*/
protected synchronized void add
(PrintWriter writer, String name, String aliases, String appBase,
@@ -436,6 +445,7 @@ public class HostManagerServlet
*
* @param writer Writer to render results to
* @param name host name
+ * @param smClient StringManager for the client's locale
*/
protected synchronized void remove(PrintWriter writer, String name,
StringManager smClient) {
@@ -494,6 +504,7 @@ public class HostManagerServlet
* Render a list of the currently active Contexts in our virtual host.
*
* @param writer Writer to render to
+ * @param smClient StringManager for the client's locale
*/
protected void list(PrintWriter writer, StringManager smClient) {
@@ -526,6 +537,7 @@ public class HostManagerServlet
*
* @param writer Writer to render to
* @param name Host name
+ * @param smClient StringManager for the client's locale
*/
protected void start(PrintWriter writer, String name,
StringManager smClient) {
@@ -587,6 +599,7 @@ public class HostManagerServlet
*
* @param writer Writer to render to
* @param name Host name
+ * @param smClient StringManager for the client's locale
*/
protected void stop(PrintWriter writer, String name,
StringManager smClient) {
@@ -643,11 +656,44 @@ public class HostManagerServlet
}
- // -------------------------------------------------------- Support Methods
+ /**
+ * Persist the current configuration to server.xml.
+ *
+ * @param writer Writer to render to
+ * @param smClient i18n resources localized for the client
+ */
+ protected void persist(PrintWriter writer, StringManager smClient) {
+ if (debug >= 1) {
+ log(sm.getString("hostManagerServlet.persist"));
+ }
+
+ try {
+ MBeanServer platformMBeanServer = ManagementFactory.getPlatformMBeanServer();
+ ObjectName oname = new ObjectName(engine.getDomain() + ":type=StoreConfig");
+ platformMBeanServer.invoke(oname, "storeConfig", null, null);
+ writer.println(smClient.getString("hostManagerServlet.persisted"));
+ } catch (Exception e) {
+ getServletContext().log(sm.getString("hostManagerServlet.persistFailed"), e);
+ writer.println(smClient.getString("hostManagerServlet.persistFailed"));
+ // catch InstanceNotFoundException when StoreConfig is not enabled instead of printing
+ // the failure message
+ if (e instanceof InstanceNotFoundException) {
+ writer.println("Please enable StoreConfig to use this feature.");
+ } else {
+ writer.println(smClient.getString("hostManagerServlet.exception", e.toString()));
+ }
+ return;
+ }
+ }
+
+
+ // -------------------------------------------------------- Support Methods
/**
* Get config base.
+ * @param hostName The host name
+ * @return the config base for the host
*/
protected File getConfigBase(String hostName) {
File configBase = new File(context.getCatalinaBase(), "conf");
diff --git a/java/org/apache/catalina/manager/host/LocalStrings.properties b/java/org/apache/catalina/manager/host/LocalStrings.properties
index a5b9757..1a22269 100644
--- a/java/org/apache/catalina/manager/host/LocalStrings.properties
+++ b/java/org/apache/catalina/manager/host/LocalStrings.properties
@@ -44,6 +44,9 @@ hostManagerServlet.remove=remove: Removing host [{0}]
hostManagerServlet.list=list: Listing hosts for engine [{0}]
hostManagerServlet.start=start: Starting host with name [{0}]
hostManagerServlet.stop=stop: Stopping host with name [{0}]
+hostManagerServlet.persist=persist: Persisting current configuration
+hostManagerServlet.persisted=OK - Configuration persisted
+hostManagerServlet.persistFailed=FAIL - Failed to persist configuration
htmlHostManagerServlet.title=Tomcat Virtual Host Manager
htmlHostManagerServlet.messageLabel=Message:
@@ -59,6 +62,7 @@ htmlHostManagerServlet.hostTasks=Commands
htmlHostManagerServlet.hostsStart=Start
htmlHostManagerServlet.hostsStop=Stop
htmlHostManagerServlet.hostsRemove=Remove
+htmlHostManagerServlet.hostsPersist=Persist
htmlHostManagerServlet.hostThis=Host Manager installed - commands disabled
htmlHostManagerServlet.addTitle=Add Virtual Host
htmlHostManagerServlet.addHost=Host
@@ -79,6 +83,9 @@ htmlHostManagerServlet.serverJVMVendor=JVM Vendor
htmlHostManagerServlet.serverOSName=OS Name
htmlHostManagerServlet.serverOSVersion=OS Version
htmlHostManagerServlet.serverOSArch=OS Architecture
+htmlHostManagerServlet.persistTitle=Persist configuration
+htmlHostManagerServlet.persistAll=Save current configuration (including virtual hosts) to server.xml and per web application context.xml files
+htmlHostManagerServlet.persistAllButton=All
statusServlet.title=Server Status
statusServlet.complete=Complete Server Status
diff --git a/java/org/apache/catalina/manager/util/BaseSessionComparator.java b/java/org/apache/catalina/manager/util/BaseSessionComparator.java
index 04e567e..2cf0793 100644
--- a/java/org/apache/catalina/manager/util/BaseSessionComparator.java
+++ b/java/org/apache/catalina/manager/util/BaseSessionComparator.java
@@ -22,7 +22,10 @@ import java.util.Comparator;
import org.apache.catalina.Session;
/**
- * Comparator which permits to compare on a session's content
+ * Comparator which permits to compare on a session's content.
+ *
+ * @param <T> The type of the session content to be compared
+ *
* @author Cédrik LIME
*/
public abstract class BaseSessionComparator<T> implements Comparator<Session> {
diff --git a/java/org/apache/catalina/manager/util/SessionUtils.java b/java/org/apache/catalina/manager/util/SessionUtils.java
index 334fb8a..a28c06e 100644
--- a/java/org/apache/catalina/manager/util/SessionUtils.java
+++ b/java/org/apache/catalina/manager/util/SessionUtils.java
@@ -71,8 +71,8 @@ public class SessionUtils {
* Try to get user locale from the session, if possible.
* IMPLEMENTATION NOTE: this method has explicit support for Tapestry 3, Struts 1.x and Spring
* JSF check the browser meta tag "accept languages" to choose what language to display.
- * @param in_session
- * @return String
+ * @param in_session The session
+ * @return the locale
*/
public static Locale guessLocaleFromSession(final Session in_session) {
return guessLocaleFromSession(in_session.getSession());
@@ -87,17 +87,17 @@ public class SessionUtils {
// First search "known locations"
for (int i = 0; i < LOCALE_TEST_ATTRIBUTES.length; ++i) {
Object obj = in_session.getAttribute(LOCALE_TEST_ATTRIBUTES[i]);
- if (null != obj && obj instanceof Locale) {
+ if (obj instanceof Locale) {
locale = (Locale) obj;
break;
}
obj = in_session.getAttribute(LOCALE_TEST_ATTRIBUTES[i].toLowerCase(Locale.ENGLISH));
- if (null != obj && obj instanceof Locale) {
+ if (obj instanceof Locale) {
locale = (Locale) obj;
break;
}
obj = in_session.getAttribute(LOCALE_TEST_ATTRIBUTES[i].toUpperCase(Locale.ENGLISH));
- if (null != obj && obj instanceof Locale) {
+ if (obj instanceof Locale) {
locale = (Locale) obj;
break;
}
@@ -124,7 +124,7 @@ public class SessionUtils {
Method readMethod = probableEngine.getClass().getMethod("getLocale", (Class<?>[])null);//$NON-NLS-1$
// Call the property getter and return the value
Object possibleLocale = readMethod.invoke(probableEngine, (Object[]) null);
- if (null != possibleLocale && possibleLocale instanceof Locale) {
+ if (possibleLocale instanceof Locale) {
locale = (Locale) possibleLocale;
}
} catch (Exception e) {
@@ -146,7 +146,7 @@ public class SessionUtils {
for (Enumeration<String> enumeration = in_session.getAttributeNames(); enumeration.hasMoreElements();) {
String name = enumeration.nextElement();
Object obj = in_session.getAttribute(name);
- if (null != obj && obj instanceof Locale) {
+ if (obj instanceof Locale) {
localeArray.add(obj);
}
}
@@ -163,8 +163,8 @@ public class SessionUtils {
/**
* Try to get user from the session, if possible.
- * @param in_session
- * @return Object
+ * @param in_session The session
+ * @return the user
*/
public static Object guessUserFromSession(final Session in_session) {
if (null == in_session) {
@@ -208,7 +208,7 @@ public class SessionUtils {
for (Enumeration<String> enumeration = httpSession.getAttributeNames(); enumeration.hasMoreElements();) {
String name = enumeration.nextElement();
Object obj = httpSession.getAttribute(name);
- if (null != obj && (obj instanceof Principal || obj instanceof Subject)) {
+ if (obj instanceof Principal || obj instanceof Subject) {
principalArray.add(obj);
}
}
diff --git a/java/org/apache/catalina/mapper/Mapper.java b/java/org/apache/catalina/mapper/Mapper.java
index 6de03dd..fa8d6f2 100644
--- a/java/org/apache/catalina/mapper/Mapper.java
+++ b/java/org/apache/catalina/mapper/Mapper.java
@@ -30,6 +30,9 @@ import org.apache.catalina.Host;
import org.apache.catalina.WebResource;
import org.apache.catalina.WebResourceRoot;
import org.apache.catalina.Wrapper;
+import org.apache.catalina.servlet4preview.http.MappingMatch;
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.buf.Ascii;
import org.apache.tomcat.util.buf.CharChunk;
import org.apache.tomcat.util.buf.MessageBytes;
@@ -44,11 +47,9 @@ import org.apache.tomcat.util.res.StringManager;
public final class Mapper {
- private static final org.apache.juli.logging.Log log =
- org.apache.juli.logging.LogFactory.getLog(Mapper.class);
+ private static final Log log = LogFactory.getLog(Mapper.class);
- static final StringManager sm =
- StringManager.getManager(Mapper.class.getPackage().getName());
+ private static final StringManager sm = StringManager.getManager(Mapper.class);
// ----------------------------------------------------- Instance Variables
@@ -56,20 +57,22 @@ public final class Mapper {
/**
* Array containing the virtual hosts definitions.
*/
+ // Package private to facilitate testing
volatile MappedHost[] hosts = new MappedHost[0];
/**
* Default host name.
*/
- String defaultHostName = null;
+ private String defaultHostName = null;
+ private volatile MappedHost defaultHost = null;
/**
* Mapping from Context object to Context version to support
* RequestDispatcher mappings.
*/
- Map<Context, ContextVersion> contextObjectToContextVersionMap =
+ private final Map<Context, ContextVersion> contextObjectToContextVersionMap =
new ConcurrentHashMap<>();
@@ -80,10 +83,16 @@ public final class Mapper {
*
* @param defaultHostName Default host name
*/
- public void setDefaultHostName(String defaultHostName) {
- this.defaultHostName = defaultHostName;
+ public synchronized void setDefaultHostName(String defaultHostName) {
+ this.defaultHostName = renameWildcardHost(defaultHostName);
+ if (this.defaultHostName == null) {
+ defaultHost = null;
+ } else {
+ defaultHost = exactFind(hosts, this.defaultHostName);
+ }
}
+
/**
* Add a new host to the mapper.
*
@@ -93,10 +102,14 @@ public final class Mapper {
*/
public synchronized void addHost(String name, String[] aliases,
Host host) {
+ name = renameWildcardHost(name);
MappedHost[] newHosts = new MappedHost[hosts.length + 1];
MappedHost newHost = new MappedHost(name, host);
if (insertMap(hosts, newHosts, newHost)) {
hosts = newHosts;
+ if (newHost.name.equals(defaultHostName)) {
+ defaultHost = newHost;
+ }
if (log.isDebugEnabled()) {
log.debug(sm.getString("mapper.addHost.success", name));
}
@@ -119,6 +132,7 @@ public final class Mapper {
}
List<MappedHost> newAliases = new ArrayList<>(aliases.length);
for (String alias : aliases) {
+ alias = renameWildcardHost(alias);
MappedHost newAlias = new MappedHost(alias, newHost);
if (addHostAliasImpl(newAlias)) {
newAliases.add(newAlias);
@@ -134,6 +148,7 @@ public final class Mapper {
* @param name Virtual host name
*/
public synchronized void removeHost(String name) {
+ name = renameWildcardHost(name);
// Find and remove the old host
MappedHost host = exactFind(hosts, name);
if (host == null || host.isAlias()) {
@@ -162,6 +177,7 @@ public final class Mapper {
// just in case...
return;
}
+ alias = renameWildcardHost(alias);
MappedHost newAlias = new MappedHost(alias, realHost);
if (addHostAliasImpl(newAlias)) {
realHost.addAlias(newAlias);
@@ -172,6 +188,9 @@ public final class Mapper {
MappedHost[] newHosts = new MappedHost[hosts.length + 1];
if (insertMap(hosts, newHosts, newAlias)) {
hosts = newHosts;
+ if (newAlias.name.equals(defaultHostName)) {
+ defaultHost = newAlias;
+ }
if (log.isDebugEnabled()) {
log.debug(sm.getString("mapper.addHostAlias.success",
newAlias.name, newAlias.getRealHostName()));
@@ -200,6 +219,7 @@ public final class Mapper {
* @param alias The alias to remove
*/
public synchronized void removeHostAlias(String alias) {
+ alias = renameWildcardHost(alias);
// Find and remove the alias
MappedHost hostMapping = exactFind(hosts, alias);
if (hostMapping == null || !hostMapping.isAlias()) {
@@ -236,32 +256,14 @@ public final class Mapper {
* @param context Context object
* @param welcomeResources Welcome files defined for this context
* @param resources Static resources of the context
- * @deprecated Use {@link #addContextVersion(String, Host, String, String, Context, String[], WebResourceRoot, Collection)}
- */
- @Deprecated
- public void addContextVersion(String hostName, Host host, String path,
- String version, Context context, String[] welcomeResources,
- WebResourceRoot resources) {
- addContextVersion(hostName, host, path, version, context,
- welcomeResources, resources, null);
- }
-
- /**
- * Add a new Context to an existing Host.
- *
- * @param hostName Virtual host name this context belongs to
- * @param host Host object
- * @param path Context path
- * @param version Context version
- * @param context Context object
- * @param welcomeResources Welcome files defined for this context
- * @param resources Static resources of the context
* @param wrappers Information on wrapper mappings
*/
public void addContextVersion(String hostName, Host host, String path,
String version, Context context, String[] welcomeResources,
WebResourceRoot resources, Collection<WrapperMappingInfo> wrappers) {
+ hostName = renameWildcardHost(hostName);
+
MappedHost mappedHost = exactFind(hosts, hostName);
if (mappedHost == null) {
addHost(hostName, new String[0], host);
@@ -326,6 +328,7 @@ public final class Mapper {
public void removeContextVersion(Context ctxt, String hostName,
String path, String version) {
+ hostName = renameWildcardHost(hostName);
contextObjectToContextVersionMap.remove(ctxt);
MappedHost host = exactFind(hosts, hostName);
@@ -369,7 +372,7 @@ public final class Mapper {
*/
public void pauseContextVersion(Context ctxt, String hostName,
String contextPath, String version) {
-
+ hostName = renameWildcardHost(hostName);
ContextVersion contextVersion = findContextVersion(hostName,
contextPath, version, true);
if (contextVersion == null || !ctxt.equals(contextVersion.object)) {
@@ -411,6 +414,7 @@ public final class Mapper {
public void addWrapper(String hostName, String contextPath, String version,
String path, Wrapper wrapper, boolean jspWildCard,
boolean resourceOnly) {
+ hostName = renameWildcardHost(hostName);
ContextVersion contextVersion = findContextVersion(hostName,
contextPath, version, false);
if (contextVersion == null) {
@@ -421,6 +425,7 @@ public final class Mapper {
public void addWrappers(String hostName, String contextPath,
String version, Collection<WrapperMappingInfo> wrappers) {
+ hostName = renameWildcardHost(hostName);
ContextVersion contextVersion = findContextVersion(hostName,
contextPath, version, false);
if (contextVersion == null) {
@@ -514,12 +519,14 @@ public final class Mapper {
/**
* Remove a wrapper from an existing context.
*
- * @param hostName Virtual host name this wrapper belongs to
+ * @param hostName Virtual host name this wrapper belongs to
* @param contextPath Context path this wrapper belongs to
- * @param path Wrapper mapping
+ * @param version Context version this wrapper belongs to
+ * @param path Wrapper mapping
*/
public void removeWrapper(String hostName, String contextPath,
String version, String path) {
+ hostName = renameWildcardHost(hostName);
ContextVersion contextVersion = findContextVersion(hostName,
contextPath, version, true);
if (contextVersion == null || contextVersion.isPaused()) {
@@ -597,36 +604,38 @@ public final class Mapper {
/**
* Add a welcome file to the given context.
*
- * @param hostName
- * @param contextPath
- * @param welcomeFile
+ * @param hostName The host where the given context can be found
+ * @param contextPath The path of the given context
+ * @param version The version of the given context
+ * @param welcomeFile The welcome file to add
*/
- public void addWelcomeFile(String hostName, String contextPath,
- String version, String welcomeFile) {
- ContextVersion contextVersion = findContextVersion(hostName,
- contextPath, version, false);
+ public void addWelcomeFile(String hostName, String contextPath, String version,
+ String welcomeFile) {
+ hostName = renameWildcardHost(hostName);
+ ContextVersion contextVersion = findContextVersion(hostName, contextPath, version, false);
if (contextVersion == null) {
return;
}
int len = contextVersion.welcomeResources.length + 1;
String[] newWelcomeResources = new String[len];
- System.arraycopy(contextVersion.welcomeResources, 0,
- newWelcomeResources, 0, len - 1);
+ System.arraycopy(contextVersion.welcomeResources, 0, newWelcomeResources, 0, len - 1);
newWelcomeResources[len - 1] = welcomeFile;
contextVersion.welcomeResources = newWelcomeResources;
}
+
/**
* Remove a welcome file from the given context.
*
- * @param hostName
- * @param contextPath
- * @param welcomeFile
+ * @param hostName The host where the given context can be found
+ * @param contextPath The path of the given context
+ * @param version The version of the given context
+ * @param welcomeFile The welcome file to remove
*/
public void removeWelcomeFile(String hostName, String contextPath,
String version, String welcomeFile) {
- ContextVersion contextVersion = findContextVersion(hostName,
- contextPath, version, false);
+ hostName = renameWildcardHost(hostName);
+ ContextVersion contextVersion = findContextVersion(hostName, contextPath, version, false);
if (contextVersion == null || contextVersion.isPaused()) {
return;
}
@@ -640,8 +649,7 @@ public final class Mapper {
if (match > -1) {
int len = contextVersion.welcomeResources.length - 1;
String[] newWelcomeResources = new String[len];
- System.arraycopy(contextVersion.welcomeResources, 0,
- newWelcomeResources, 0, match);
+ System.arraycopy(contextVersion.welcomeResources, 0, newWelcomeResources, 0, match);
if (match < len) {
System.arraycopy(contextVersion.welcomeResources, match + 1,
newWelcomeResources, match, len - match);
@@ -650,27 +658,30 @@ public final class Mapper {
}
}
+
/**
* Clear the welcome files for the given context.
*
- * @param hostName
- * @param contextPath
+ * @param hostName The host where the context to be cleared can be found
+ * @param contextPath The path of the context to be cleared
+ * @param version The version of the context to be cleared
*/
- public void clearWelcomeFiles(String hostName, String contextPath,
- String version) {
- ContextVersion contextVersion = findContextVersion(hostName,
- contextPath, version, false);
+ public void clearWelcomeFiles(String hostName, String contextPath, String version) {
+ hostName = renameWildcardHost(hostName);
+ ContextVersion contextVersion = findContextVersion(hostName, contextPath, version, false);
if (contextVersion == null) {
return;
}
contextVersion.welcomeResources = new String[0];
}
+
/**
* Map the specified host name and URI, mutating the given mapping data.
*
* @param host Virtual host name
* @param uri URI
+ * @param version The version, if any, included in the request to be mapped
* @param mappingData This structure will contain the result of the mapping
* operation
* @throws IOException if the buffers are too small to hold the results of
@@ -686,7 +697,6 @@ public final class Mapper {
uri.toChars();
internalMap(host.getCharChunk(), uri.getCharChunk(), version,
mappingData);
-
}
@@ -710,13 +720,11 @@ public final class Mapper {
CharChunk uricc = uri.getCharChunk();
uricc.setLimit(-1);
internalMapWrapper(contextVersion, uricc, mappingData);
-
}
// -------------------------------------------------------- Private Methods
-
/**
* Map the specified URI.
* @throws IOException
@@ -738,12 +746,24 @@ public final class Mapper {
MappedHost[] hosts = this.hosts;
MappedHost mappedHost = exactFindIgnoreCase(hosts, host);
if (mappedHost == null) {
- if (defaultHostName == null) {
- return;
+ // Note: Internally, the Mapper does not use the leading * on a
+ // wildcard host. This is to allow this shortcut.
+ int firstDot = host.indexOf('.');
+ if (firstDot > -1) {
+ int offset = host.getOffset();
+ try {
+ host.setOffset(firstDot + offset);
+ mappedHost = exactFindIgnoreCase(hosts, host);
+ } finally {
+ // Make absolutely sure this gets reset
+ host.setOffset(offset);
+ }
}
- mappedHost = exactFind(hosts, defaultHostName);
if (mappedHost == null) {
- return;
+ mappedHost = defaultHost;
+ if (mappedHost == null) {
+ return;
+ }
}
}
mappingData.host = mappedHost.object;
@@ -993,6 +1013,7 @@ public final class Mapper {
(path.getBuffer(), path.getStart(), path.getLength());
mappingData.wrapperPath.setChars
(path.getBuffer(), path.getStart(), path.getLength());
+ mappingData.matchType = MappingMatch.DEFAULT;
}
// Redirection to a folder
char[] buf = path.getBuffer();
@@ -1041,8 +1062,10 @@ public final class Mapper {
mappingData.wrapperPath.setString("");
// This seems wrong but it is what the spec says...
mappingData.contextPath.setString("");
+ mappingData.matchType = MappingMatch.CONTEXT_ROOT;
} else {
mappingData.wrapperPath.setString(wrapper.name);
+ mappingData.matchType = MappingMatch.EXACT;
}
}
}
@@ -1094,6 +1117,7 @@ public final class Mapper {
(path.getBuffer(), path.getOffset(), path.getLength());
mappingData.wrapper = wrappers[pos].object;
mappingData.jspWildCard = wrappers[pos].jspWildCard;
+ mappingData.matchType = MappingMatch.PATH;
}
}
}
@@ -1138,6 +1162,7 @@ public final class Mapper {
mappingData.requestPath.setChars(buf, servletPath, pathEnd
- servletPath);
mappingData.wrapper = wrapper.object;
+ mappingData.matchType = MappingMatch.EXTENSION;
}
path.setOffset(servletPath);
path.setEnd(pathEnd);
@@ -1510,6 +1535,22 @@ public final class Mapper {
}
+ /*
+ * To simplify the mapping process, wild card hosts take the form
+ * ".apache.org" rather than "*.apache.org" internally. However, for ease
+ * of use the external form remains "*.apache.org". Any host name passed
+ * into this class needs to be passed through this method to rename and
+ * wild card host names from the external to internal form.
+ */
+ private static String renameWildcardHost(String hostName) {
+ if (hostName.startsWith("*.")) {
+ return hostName.substring(1);
+ } else {
+ return hostName;
+ }
+ }
+
+
// ------------------------------------------------- MapElement Inner Class
@@ -1546,6 +1587,9 @@ public final class Mapper {
/**
* Constructor used for the primary Host
+ *
+ * @param name The name of the virtual host
+ * @param host The host
*/
public MappedHost(String name, Host host) {
super(name, host);
@@ -1556,6 +1600,9 @@ public final class Mapper {
/**
* Constructor used for an Alias
+ *
+ * @param alias The alias of the virtual host
+ * @param realHost The host the alias points to
*/
public MappedHost(String alias, MappedHost realHost) {
super(alias, realHost.object);
diff --git a/java/org/apache/catalina/mapper/MapperListener.java b/java/org/apache/catalina/mapper/MapperListener.java
index 6791da0..4df71ce 100644
--- a/java/org/apache/catalina/mapper/MapperListener.java
+++ b/java/org/apache/catalina/mapper/MapperListener.java
@@ -75,15 +75,17 @@ public class MapperListener extends LifecycleMBeanBase
*/
private final String domain = null;
- // ----------------------------------------------------------- Constructors
+ // ----------------------------------------------------------- Constructors
/**
* Create mapper listener.
+ *
+ * @param service The service this listener is associated with
*/
- public MapperListener(Mapper mapper, Service service) {
- this.mapper = mapper;
+ public MapperListener(Service service) {
this.service = service;
+ this.mapper = service.getMapper();
}
@@ -94,8 +96,7 @@ public class MapperListener extends LifecycleMBeanBase
setState(LifecycleState.STARTING);
- @SuppressWarnings("deprecation")
- Engine engine = (Engine) service.getContainer();
+ Engine engine = service.getContainer();
if (engine == null) {
return;
}
@@ -119,8 +120,7 @@ public class MapperListener extends LifecycleMBeanBase
public void stopInternal() throws LifecycleException {
setState(LifecycleState.STOPPING);
- @SuppressWarnings("deprecation")
- Engine engine = (Engine) service.getContainer();
+ Engine engine = service.getContainer();
if (engine == null) {
return;
}
@@ -261,8 +261,7 @@ public class MapperListener extends LifecycleMBeanBase
private void findDefaultHost() {
- @SuppressWarnings("deprecation")
- Engine engine = (Engine) service.getContainer();
+ Engine engine = service.getContainer();
String defaultHost = engine.getDefaultHost();
boolean found = false;
diff --git a/java/org/apache/catalina/mapper/MappingData.java b/java/org/apache/catalina/mapper/MappingData.java
index d6fdea0..63fba6d 100644
--- a/java/org/apache/catalina/mapper/MappingData.java
+++ b/java/org/apache/catalina/mapper/MappingData.java
@@ -20,6 +20,7 @@ package org.apache.catalina.mapper;
import org.apache.catalina.Context;
import org.apache.catalina.Host;
import org.apache.catalina.Wrapper;
+import org.apache.catalina.servlet4preview.http.MappingMatch;
import org.apache.tomcat.util.buf.MessageBytes;
/**
@@ -43,6 +44,9 @@ public class MappingData {
public final MessageBytes redirectPath = MessageBytes.newInstance();
+ // Fields used by ApplicationMapping to implement javax.servlet.http.Mapping
+ public MappingMatch matchType = MappingMatch.UNKNOWN;
+
public void recycle() {
host = null;
context = null;
@@ -55,6 +59,6 @@ public class MappingData {
wrapperPath.recycle();
pathInfo.recycle();
redirectPath.recycle();
+ matchType = MappingMatch.UNKNOWN;
}
-
}
diff --git a/java/org/apache/catalina/mapper/mbeans-descriptors.xml b/java/org/apache/catalina/mapper/mbeans-descriptors.xml
index 4f4c005..fc203a4 100644
--- a/java/org/apache/catalina/mapper/mbeans-descriptors.xml
+++ b/java/org/apache/catalina/mapper/mbeans-descriptors.xml
@@ -1,4 +1,4 @@
-<?xml version="1.0"?>
+<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
diff --git a/java/org/apache/catalina/mbeans-descriptors.xml b/java/org/apache/catalina/mbeans-descriptors.xml
index 27ecc13..2dcfe27 100644
--- a/java/org/apache/catalina/mbeans-descriptors.xml
+++ b/java/org/apache/catalina/mbeans-descriptors.xml
@@ -1,4 +1,4 @@
-<?xml version="1.0"?>
+<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
diff --git a/java/org/apache/catalina/mbeans/Constants.java b/java/org/apache/catalina/mbeans/Constants.java
deleted file mode 100644
index b6a56bf..0000000
--- a/java/org/apache/catalina/mbeans/Constants.java
+++ /dev/null
@@ -1,27 +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.mbeans;
-
-
-public class Constants {
-
- public static final String Package = "org.apache.catalina.mbeans";
-
-}
-
diff --git a/java/org/apache/catalina/mbeans/MBeanFactory.java b/java/org/apache/catalina/mbeans/MBeanFactory.java
index 930fe13..c899b67 100644
--- a/java/org/apache/catalina/mbeans/MBeanFactory.java
+++ b/java/org/apache/catalina/mbeans/MBeanFactory.java
@@ -44,6 +44,8 @@ import org.apache.catalina.realm.UserDatabaseRealm;
import org.apache.catalina.session.StandardManager;
import org.apache.catalina.startup.ContextConfig;
import org.apache.catalina.startup.HostConfig;
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.res.StringManager;
@@ -52,11 +54,9 @@ import org.apache.tomcat.util.res.StringManager;
*/
public class MBeanFactory {
- private static final org.apache.juli.logging.Log log =
- org.apache.juli.logging.LogFactory.getLog(MBeanFactory.class);
+ private static final Log log = LogFactory.getLog(MBeanFactory.class);
- protected static final StringManager sm =
- StringManager.getManager(Constants.Package);
+ protected static final StringManager sm = StringManager.getManager(MBeanFactory.class);
/**
* The <code>MBeanServer</code> for this application.
@@ -107,7 +107,6 @@ public class MBeanFactory {
String type = pname.getKeyProperty("type");
String j2eeType = pname.getKeyProperty("j2eeType");
Service service = getService(pname);
- @SuppressWarnings("deprecation")
StandardEngine engine = (StandardEngine) service.getContainer();
if ((j2eeType!=null) && (j2eeType.equals("WebModule"))) {
String name = pname.getKeyProperty("name");
@@ -143,7 +142,6 @@ public class MBeanFactory {
String hostName = oname.getKeyProperty("host");
String path = oname.getKeyProperty("path");
Service service = getService(oname);
- @SuppressWarnings("deprecation")
Container engine = service.getContainer();
if (hostName == null) {
// child's container is Engine
@@ -490,8 +488,7 @@ public class MBeanFactory {
} else {
log.warn("Deployer not found for "+pname.getKeyProperty("host"));
Service service = getService(pname);
- @SuppressWarnings("deprecation")
- Engine engine = (Engine) service.getContainer();
+ Engine engine = service.getContainer();
Host host = (Host) engine.findChild(pname.getKeyProperty("host"));
host.addChild(context);
}
@@ -540,8 +537,7 @@ public class MBeanFactory {
// Add the new instance to its parent component
ObjectName pname = new ObjectName(parent);
Service service = getService(pname);
- @SuppressWarnings("deprecation")
- Engine engine = (Engine) service.getContainer();
+ Engine engine = service.getContainer();
engine.addChild(host);
// Return the corresponding MBean name
@@ -769,7 +765,7 @@ public class MBeanFactory {
String domain = oname.getDomain();
StandardService service = (StandardService) getService(oname);
- Engine engine = (Engine) service.getContainer();
+ Engine engine = service.getContainer();
String name = oname.getKeyProperty("name");
name = name.substring(2);
int i = name.indexOf('/');
@@ -819,8 +815,7 @@ public class MBeanFactory {
ObjectName oname = new ObjectName(name);
String hostName = oname.getKeyProperty("host");
Service service = getService(oname);
- @SuppressWarnings("deprecation")
- Engine engine = (Engine) service.getContainer();
+ Engine engine = service.getContainer();
Host host = (Host) engine.findChild(hostName);
// Remove this component from its parent component
diff --git a/java/org/apache/catalina/mbeans/mbeans-descriptors.xml b/java/org/apache/catalina/mbeans/mbeans-descriptors.xml
index 0ba0a90..de4b367 100644
--- a/java/org/apache/catalina/mbeans/mbeans-descriptors.xml
+++ b/java/org/apache/catalina/mbeans/mbeans-descriptors.xml
@@ -1,4 +1,4 @@
-<?xml version="1.0"?>
+<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
diff --git a/java/org/apache/catalina/realm/Constants.java b/java/org/apache/catalina/realm/Constants.java
index e0df98a..ac9cc5c 100644
--- a/java/org/apache/catalina/realm/Constants.java
+++ b/java/org/apache/catalina/realm/Constants.java
@@ -14,22 +14,21 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
-
package org.apache.catalina.realm;
-
/**
* Manifest constants for this Java package.
*
- *
* @author Craig R. McClanahan
+ *
+ * @deprecated Unused. Will be removed in Tomcat 9
*/
+ at Deprecated
public final class Constants {
public static final String Package = "org.apache.catalina.realm";
- // Authentication methods for login configuration
+ // Authentication methods for login configuration
public static final String FORM_METHOD = "FORM";
// Form based authentication constants
diff --git a/java/org/apache/catalina/realm/DigestCredentialHandlerBase.java b/java/org/apache/catalina/realm/DigestCredentialHandlerBase.java
index 97bbdd2..c343ac6 100644
--- a/java/org/apache/catalina/realm/DigestCredentialHandlerBase.java
+++ b/java/org/apache/catalina/realm/DigestCredentialHandlerBase.java
@@ -30,7 +30,8 @@ import org.apache.tomcat.util.res.StringManager;
*/
public abstract class DigestCredentialHandlerBase implements CredentialHandler {
- protected static final StringManager sm = StringManager.getManager(Constants.Package);
+ protected static final StringManager sm =
+ StringManager.getManager(DigestCredentialHandlerBase.class);
public static final int DEFAULT_SALT_LENGTH = 32;
diff --git a/java/org/apache/catalina/realm/JAASCallbackHandler.java b/java/org/apache/catalina/realm/JAASCallbackHandler.java
index 8a161b4..3b439ce 100644
--- a/java/org/apache/catalina/realm/JAASCallbackHandler.java
+++ b/java/org/apache/catalina/realm/JAASCallbackHandler.java
@@ -110,8 +110,7 @@ public class JAASCallbackHandler implements CallbackHandler {
/**
* The string manager for this package.
*/
- protected static final StringManager sm =
- StringManager.getManager(Constants.Package);
+ protected static final StringManager sm = StringManager.getManager(JAASCallbackHandler.class);
/**
* The password to be authenticated with.
diff --git a/java/org/apache/catalina/realm/JAASMemoryLoginModule.java b/java/org/apache/catalina/realm/JAASMemoryLoginModule.java
index 96cf901..361de3a 100644
--- a/java/org/apache/catalina/realm/JAASMemoryLoginModule.java
+++ b/java/org/apache/catalina/realm/JAASMemoryLoginModule.java
@@ -149,7 +149,7 @@ public class JAASMemoryLoginModule extends MemoryRealm implements LoginModule {
// If our authentication was not successful, just return false
if (principal == null) {
- return (false);
+ return false;
}
// Clean up if overall authentication failed
@@ -162,7 +162,7 @@ public class JAASMemoryLoginModule extends MemoryRealm implements LoginModule {
if (log.isDebugEnabled()) {
log.debug("Abort");
}
- return (true);
+ return true;
}
@@ -185,7 +185,7 @@ public class JAASMemoryLoginModule extends MemoryRealm implements LoginModule {
// If authentication was not successful, just return false
if (principal == null) {
- return (false);
+ return false;
}
// Add our Principal to the Subject if needed
@@ -203,7 +203,7 @@ public class JAASMemoryLoginModule extends MemoryRealm implements LoginModule {
}
committed = true;
- return (true);
+ return true;
}
@@ -344,7 +344,7 @@ public class JAASMemoryLoginModule extends MemoryRealm implements LoginModule {
// Report results based on success or failure
if (principal != null) {
- return (true);
+ return true;
} else {
throw new FailedLoginException("Username or password is incorrect");
}
@@ -364,7 +364,7 @@ public class JAASMemoryLoginModule extends MemoryRealm implements LoginModule {
subject.getPrincipals().remove(principal);
committed = false;
principal = null;
- return (true);
+ return true;
}
@@ -386,7 +386,7 @@ public class JAASMemoryLoginModule extends MemoryRealm implements LoginModule {
file = new File(catalinaBase, pathname);
}
}
- if (!file.exists() || !file.canRead()) {
+ if (!file.canRead()) {
log.warn("Cannot load configuration file " + file.getAbsolutePath());
return;
}
diff --git a/java/org/apache/catalina/realm/JNDIRealm.java b/java/org/apache/catalina/realm/JNDIRealm.java
index 4586472..e24f741 100644
--- a/java/org/apache/catalina/realm/JNDIRealm.java
+++ b/java/org/apache/catalina/realm/JNDIRealm.java
@@ -29,7 +29,6 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Hashtable;
-import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
@@ -76,7 +75,7 @@ import org.ietf.jgss.GSSCredential;
* element in the top level <code>DirContext</code> that is accessed
* via the <code>connectionURL</code> property.</li>
*
- * <li>If a socket connection can not be made to the <code>connectURL</code>
+ * <li>If a socket connection cannot be made to the <code>connectURL</code>
* an attempt will be made to use the <code>alternateURL</code> if it
* exists.</li>
*
@@ -1115,7 +1114,7 @@ public class JNDIRealm extends RealmBase {
} else {
this.cipherSuitesArray = cipherSuites.trim().split("\\s*,\\s*");
containerLog.debug(sm.getString("jndiRealm.cipherSuites",
- Arrays.asList(this.cipherSuitesArray)));
+ Arrays.toString(this.cipherSuitesArray)));
}
return this.cipherSuitesArray;
}
@@ -1259,7 +1258,7 @@ public class JNDIRealm extends RealmBase {
// Ensure that we have a directory context available
context = open();
- // Occassionally the directory context will timeout. Try one more
+ // Occasionally the directory context will timeout. Try one more
// time before giving up.
try {
@@ -1354,15 +1353,9 @@ public class JNDIRealm extends RealmBase {
// Search for additional roles
List<String> roles = getRoles(context, user);
if (containerLog.isDebugEnabled()) {
- Iterator<String> it = roles.iterator();
- // TODO: Use a single log message
- while (it.hasNext()) {
- containerLog.debug("Found role: " + it.next());
- }
+ containerLog.debug("Found roles: " + roles.toString());
}
- return (new GenericPrincipal(username,
- credentials,
- roles));
+ return (new GenericPrincipal(username, credentials, roles));
}
} catch (InvalidNameException ine) {
// Log the problem for posterity
@@ -1388,11 +1381,7 @@ public class JNDIRealm extends RealmBase {
// Search for additional roles
List<String> roles = getRoles(context, user);
if (containerLog.isDebugEnabled()) {
- Iterator<String> it = roles.iterator();
- // TODO: Use a single log message
- while (it.hasNext()) {
- containerLog.debug("Found role: " + it.next());
- }
+ containerLog.debug("Found roles: " + roles.toString());
}
// Create and return a suitable Principal for this user
@@ -1778,7 +1767,7 @@ public class JNDIRealm extends RealmBase {
containerLog.trace(" validating credentials");
if (info == null || credentials == null)
- return (false);
+ return false;
String password = info.getPassword();
@@ -1801,11 +1790,11 @@ public class JNDIRealm extends RealmBase {
throws NamingException {
if (credentials == null || user == null)
- return (false);
+ return false;
String dn = user.getDN();
if (dn == null)
- return (false);
+ return false;
// Validate the credentials specified by the user
if (containerLog.isTraceEnabled()) {
@@ -1915,8 +1904,7 @@ public class JNDIRealm extends RealmBase {
if (containerLog.isTraceEnabled()) {
containerLog.trace(" Found " + list.size() + " user internal roles");
- for (int i=0; i<list.size(); i++)
- containerLog.trace( " Found user internal role " + list.get(i));
+ containerLog.trace(" Found user internal roles " + list.toString());
}
// Are we configured to do role searches?
@@ -2703,44 +2691,42 @@ public class JNDIRealm extends RealmBase {
// Get the entry's distinguished name. For relative results, this means
// we need to composite a name with the base name, the context name, and
// the result name. For non-relative names, use the returned name.
+ String resultName = result.getName();
if (result.isRelative()) {
if (containerLog.isTraceEnabled()) {
- containerLog.trace(" search returned relative name: " +
- result.getName());
+ containerLog.trace(" search returned relative name: " + resultName);
}
NameParser parser = context.getNameParser("");
Name contextName = parser.parse(context.getNameInNamespace());
Name baseName = parser.parse(base);
// Bugzilla 32269
- Name entryName =
- parser.parse(new CompositeName(result.getName()).get(0));
+ Name entryName = parser.parse(new CompositeName(resultName).get(0));
Name name = contextName.addAll(baseName);
name = name.addAll(entryName);
return name.toString();
} else {
- String absoluteName = result.getName();
- if (containerLog.isTraceEnabled())
- containerLog.trace(" search returned absolute name: " +
- result.getName());
+ if (containerLog.isTraceEnabled()) {
+ containerLog.trace(" search returned absolute name: " + resultName);
+ }
try {
// Normalize the name by running it through the name parser.
NameParser parser = context.getNameParser("");
- URI userNameUri = new URI(absoluteName);
+ 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 ) {
throw new InvalidNameException(
"Search returned unparseable absolute name: " +
- absoluteName );
+ resultName );
}
Name name = parser.parse(pathComponent.substring(1));
return name.toString();
} catch ( URISyntaxException e ) {
throw new InvalidNameException(
"Search returned unparseable absolute name: " +
- absoluteName );
+ resultName );
}
}
}
diff --git a/java/org/apache/catalina/realm/LocalStrings.properties b/java/org/apache/catalina/realm/LocalStrings.properties
index 52c8d63..4e691bf 100644
--- a/java/org/apache/catalina/realm/LocalStrings.properties
+++ b/java/org/apache/catalina/realm/LocalStrings.properties
@@ -72,6 +72,7 @@ realmBase.createUsernameRetriever.ClassNotFoundException=Cannot find class {0}.
realmBase.createUsernameRetriever.InstantiationException=Cannot create object of type {0}.
realmBase.createUsernameRetriever.IllegalAccessException=Cannot create object of type {0}.
realmBase.credentialHandler.customCredentialHandler=Unable to set the property [{0}] to value [{1}] as a custom CredentialHandler has been configured
+realmBase.cannotGetRoles=Cannot get roles from principal [{0}]
userDatabaseRealm.lookup=Exception looking up UserDatabase under key {0}
userDatabaseRealm.noDatabase=No UserDatabase component found under key {0}
dataSourceRealm.authenticateFailure=Username {0} NOT successfully authenticated
diff --git a/java/org/apache/catalina/realm/RealmBase.java b/java/org/apache/catalina/realm/RealmBase.java
index 4ec32aa..bcbfff7 100644
--- a/java/org/apache/catalina/realm/RealmBase.java
+++ b/java/org/apache/catalina/realm/RealmBase.java
@@ -28,10 +28,10 @@ import java.security.NoSuchAlgorithmException;
import java.security.Principal;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.List;
import java.util.Locale;
+import javax.servlet.annotation.ServletSecurity.TransportGuarantee;
import javax.servlet.http.HttpServletResponse;
import org.apache.catalina.Container;
@@ -54,7 +54,6 @@ import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.IntrospectionUtils;
import org.apache.tomcat.util.buf.B2CConverter;
import org.apache.tomcat.util.buf.HexUtils;
-import org.apache.tomcat.util.codec.binary.Base64;
import org.apache.tomcat.util.descriptor.web.SecurityCollection;
import org.apache.tomcat.util.descriptor.web.SecurityConstraint;
import org.apache.tomcat.util.res.StringManager;
@@ -102,52 +101,13 @@ public abstract class RealmBase extends LifecycleMBeanBase implements Realm {
protected Log containerLog = null;
- /**
- * Digest algorithm used in storing passwords in a non-plaintext format.
- * Valid values are those accepted for the algorithm name by the
- * MessageDigest class, or <code>null</code> if no digesting should
- * be performed.
- *
- * @deprecated Unused. Will be removed in Tomcat 8.5.x onwards.
- */
- @Deprecated
- protected String digest = null;
-
- /**
- * The encoding charset for the digest.
- *
- * @deprecated Unused. Will be removed in Tomcat 8.5.x onwards.
- */
- @Deprecated
- protected String digestEncoding = null;
-
-
private CredentialHandler credentialHandler;
/**
- * The MessageDigest object for digesting user credentials (passwords).
- *
- * @deprecated Unused. Will be removed in Tomcat 8.5.x onwards.
- */
- @Deprecated
- protected volatile MessageDigest md = null;
-
-
- /**
- * MD5 message digest provider.
- *
- * @deprecated Unused. Will be removed in Tomcat 8.5.x onwards.
- */
- @Deprecated
- protected static volatile MessageDigest md5Helper;
-
-
- /**
* The string manager for this package.
*/
- protected static final StringManager sm =
- StringManager.getManager(Constants.Package);
+ protected static final StringManager sm = StringManager.getManager(RealmBase.class);
/**
@@ -269,110 +229,6 @@ public abstract class RealmBase extends LifecycleMBeanBase implements Realm {
/**
- * Return the digest algorithm used for storing credentials.
- *
- * @return The currently configured algorithm used to digest stored
- * credentials
- *
- * @deprecated This will be removed in Tomcat 8.5.x as it has been replaced
- * by the CredentialHandler
- */
- @Deprecated
- public String getDigest() {
- CredentialHandler ch = credentialHandler;
- if (ch instanceof MessageDigestCredentialHandler) {
- return ((MessageDigestCredentialHandler) ch).getAlgorithm();
- }
- return null;
- }
-
-
- /**
- * Set the digest algorithm used for storing credentials.
- *
- * @param digest The new digest algorithm
- *
- * @deprecated This will be removed in Tomcat 8.5.x as it has been replaced
- * by the CredentialHandler
- */
- @Deprecated
- public void setDigest(String digest) {
- CredentialHandler ch = credentialHandler;
- if (ch == null) {
- ch = new MessageDigestCredentialHandler();
- credentialHandler = ch;
- }
- if (ch instanceof MessageDigestCredentialHandler) {
- try {
- ((MessageDigestCredentialHandler) ch).setAlgorithm(digest);
- } catch (NoSuchAlgorithmException e) {
- throw new IllegalArgumentException(e);
- }
- } else {
- log.warn(sm.getString("realmBase.credentialHandler.customCredentialHandler",
- "digest", digest));
- }
- this.digest = digest;
- }
-
- /**
- * Returns the digest encoding charset.
- *
- * @return The charset (may be null) for platform default
- *
- * @deprecated This will be removed in Tomcat 8.5.x as it has been replaced
- * by the CredentialHandler
- */
- @Deprecated
- public String getDigestEncoding() {
- CredentialHandler ch = credentialHandler;
- if (ch instanceof MessageDigestCredentialHandler) {
- return ((MessageDigestCredentialHandler) ch).getEncoding();
- }
- return null;
- }
-
- /**
- * Sets the digest encoding charset.
- *
- * @param charset The charset (null for platform default)
- *
- * @deprecated This will be removed in Tomcat 8.5.x as it has been replaced
- * by the CredentialHandler
- */
- @Deprecated
- public void setDigestEncoding(String charset) {
- CredentialHandler ch = credentialHandler;
- if (ch == null) {
- ch = new MessageDigestCredentialHandler();
- credentialHandler = ch;
- }
- if (ch instanceof MessageDigestCredentialHandler) {
- ((MessageDigestCredentialHandler) ch).setEncoding(charset);
- } else {
- log.warn(sm.getString("realmBase.credentialHandler.customCredentialHandler",
- "digestEncoding", charset));
- }
- this.digestEncoding = charset;
- }
-
-
- /**
- * @deprecated This will be removed in Tomcat 8.5.x as it has been replaced
- * by the CredentialHandler
- */
- @Deprecated
- protected Charset getDigestCharset() throws UnsupportedEncodingException {
- String charset = getDigestEncoding();
- if (charset == null) {
- return StandardCharsets.ISO_8859_1;
- } else {
- return B2CConverter.getCharset(charset);
- }
- }
-
-
- /**
* Return the "validate certificate chains" flag.
* @return The value of the validate certificate chains flag
*/
@@ -660,69 +516,6 @@ public abstract class RealmBase extends LifecycleMBeanBase implements Realm {
/**
- * @deprecated Unused. Will be removed in Tomcat 8.5.x onwards.
- */
- @Deprecated
- protected boolean compareCredentials(String userCredentials,
- String serverCredentials) {
-
- if (serverCredentials == null) {
- return false;
- }
-
- if (hasMessageDigest()) {
- // Some directories and databases prefix the password with the hash
- // type. The string is in a format compatible with Base64.encode not
- // the normal hex encoding of the digest
- if (serverCredentials.startsWith("{MD5}") ||
- serverCredentials.startsWith("{SHA}")) {
- // Server is storing digested passwords with a prefix indicating
- // the digest type
- String serverDigest = serverCredentials.substring(5);
- String userDigest = Base64.encodeBase64String(ConcurrentMessageDigest.digest(
- getDigest(), userCredentials.getBytes(StandardCharsets.ISO_8859_1)));
- return userDigest.equals(serverDigest);
-
- } else if (serverCredentials.startsWith("{SSHA}")) {
- // Server is storing digested passwords with a prefix indicating
- // the digest type and the salt used when creating that digest
-
- String serverDigestPlusSalt = serverCredentials.substring(6);
-
- // Need to convert the salt to bytes to apply it to the user's
- // digested password.
- byte[] serverDigestPlusSaltBytes =
- Base64.decodeBase64(serverDigestPlusSalt);
- final int saltPos = 20;
- byte[] serverDigestBytes = new byte[saltPos];
- System.arraycopy(serverDigestPlusSaltBytes, 0,
- serverDigestBytes, 0, saltPos);
- final int saltLength = serverDigestPlusSaltBytes.length - saltPos;
- byte[] serverSaltBytes = new byte[saltLength];
- System.arraycopy(serverDigestPlusSaltBytes, saltPos,
- serverSaltBytes, 0, saltLength);
-
- // Generate the digested form of the user provided password
- // using the salt
- byte[] userDigestBytes = ConcurrentMessageDigest.digest(getDigest(),
- userCredentials.getBytes(StandardCharsets.ISO_8859_1),
- serverSaltBytes);
-
- return Arrays.equals(userDigestBytes, serverDigestBytes);
-
- } else {
- // Hex hashes should be compared case-insensitively
- String userDigest = digest(userCredentials);
- return serverCredentials.equalsIgnoreCase(userDigest);
- }
- } else {
- // No digests, compare directly
- return serverCredentials.equals(userCredentials);
- }
- }
-
-
- /**
* Execute a periodic task, such as reloading, etc. This method will be
* invoked inside the classloading context of this container. Unexpected
* throwables will be caught and logged.
@@ -1014,7 +807,7 @@ public abstract class RealmBase extends LifecycleMBeanBase implements Realm {
throws IOException {
if (constraints == null || constraints.length == 0)
- return (true);
+ return true;
// Which user principal have we already authenticated?
Principal principal = request.getPrincipal();
@@ -1137,7 +930,7 @@ public abstract class RealmBase extends LifecycleMBeanBase implements Realm {
// Should be overridden in JAASRealm - to avoid pretty inefficient conversions
if ((principal == null) || (role == null) ||
!(principal instanceof GenericPrincipal))
- return (false);
+ return false;
GenericPrincipal gp = (GenericPrincipal) principal;
boolean result = gp.hasRole(role);
@@ -1175,7 +968,7 @@ public abstract class RealmBase extends LifecycleMBeanBase implements Realm {
if (constraints == null || constraints.length == 0) {
if (log.isDebugEnabled())
log.debug(" No applicable security constraint defined");
- return (true);
+ return true;
}
for(int i=0; i < constraints.length; i++) {
SecurityConstraint constraint = constraints[i];
@@ -1183,12 +976,12 @@ public abstract class RealmBase extends LifecycleMBeanBase implements Realm {
if (userConstraint == null) {
if (log.isDebugEnabled())
log.debug(" No applicable user data constraint defined");
- return (true);
+ return true;
}
- if (userConstraint.equals(Constants.NONE_TRANSPORT)) {
+ if (userConstraint.equals(TransportGuarantee.NONE.name())) {
if (log.isDebugEnabled())
log.debug(" User data constraint has no restrictions");
- return (true);
+ return true;
}
}
@@ -1196,7 +989,7 @@ public abstract class RealmBase extends LifecycleMBeanBase implements Realm {
if (request.getRequest().isSecure()) {
if (log.isDebugEnabled())
log.debug(" User data constraint already satisfied");
- return (true);
+ return true;
}
// Initialize variables we need to determine the appropriate action
int redirectPort = request.getConnector().getRedirectPort();
@@ -1208,7 +1001,7 @@ public abstract class RealmBase extends LifecycleMBeanBase implements Realm {
response.sendError
(HttpServletResponse.SC_FORBIDDEN,
request.getRequestURI());
- return (false);
+ return false;
}
// Redirect to the corresponding SSL port
@@ -1240,7 +1033,7 @@ public abstract class RealmBase extends LifecycleMBeanBase implements Realm {
if (log.isDebugEnabled())
log.debug(" Redirecting to " + file.toString());
response.sendRedirect(file.toString(), transportGuaranteeRedirectStatus);
- return (false);
+ return false;
}
@@ -1281,19 +1074,6 @@ public abstract class RealmBase extends LifecycleMBeanBase implements Realm {
*/
@Override
protected void startInternal() throws LifecycleException {
-
- // Create a MessageDigest instance for credentials, if desired
- if (getDigest() != null) {
- try {
- md = MessageDigest.getInstance(getDigest());
- ConcurrentMessageDigest.init(getDigest());
- } catch (NoSuchAlgorithmException e) {
- throw new LifecycleException
- (sm.getString("realmBase.algorithm", getDigest()), e);
- }
-
- }
-
if (credentialHandler == null) {
credentialHandler = new MessageDigestCredentialHandler();
}
@@ -1312,11 +1092,7 @@ public abstract class RealmBase extends LifecycleMBeanBase implements Realm {
*/
@Override
protected void stopInternal() throws LifecycleException {
-
setState(LifecycleState.STOPPING);
-
- // Clean up allocated resources
- md = null;
}
@@ -1334,47 +1110,14 @@ public abstract class RealmBase extends LifecycleMBeanBase implements Realm {
// ------------------------------------------------------ Protected Methods
-
- /**
- * Digest the password using the specified algorithm and
- * convert the result to a corresponding hexadecimal string.
- * If exception, the plain credentials string is returned.
- *
- * @param credentials Password or other credentials to use in
- * authenticating this username
- *
- * @deprecated Used. Will be removed in Tomcat 9.
- */
- @Deprecated
- protected String digest(String credentials) {
-
- // If no MessageDigest instance is specified, return unchanged
- if (hasMessageDigest() == false)
- return (credentials);
-
- // Digest the user credentials and return as hexadecimal
- synchronized (this) {
- try {
- byte[] bytes = null;
- try {
- bytes = credentials.getBytes(getDigestCharset());
- } catch (UnsupportedEncodingException uee) {
- log.error("Illegal digestEncoding: " + getDigestEncoding(), uee);
- throw new IllegalArgumentException(uee.getMessage());
- }
-
- return (HexUtils.toHexString(ConcurrentMessageDigest.digest(getDigest(), bytes)));
- } catch (Exception e) {
- log.error(sm.getString("realmBase.digest"), e);
- return (credentials);
- }
+ protected boolean hasMessageDigest() {
+ CredentialHandler ch = credentialHandler;
+ if (ch instanceof MessageDigestCredentialHandler) {
+ return ((MessageDigestCredentialHandler) ch).getAlgorithm() != null;
}
-
+ return false;
}
- protected boolean hasMessageDigest() {
- return getDigest() != null;
- }
/**
* Return the digest associated with given principal's user name.
@@ -1403,6 +1146,25 @@ public abstract class RealmBase extends LifecycleMBeanBase implements Realm {
}
+ private String getDigestEncoding() {
+ CredentialHandler ch = credentialHandler;
+ if (ch instanceof MessageDigestCredentialHandler) {
+ return ((MessageDigestCredentialHandler) ch).getEncoding();
+ }
+ return null;
+ }
+
+
+ private Charset getDigestCharset() throws UnsupportedEncodingException {
+ String charset = getDigestEncoding();
+ if (charset == null) {
+ return StandardCharsets.ISO_8859_1;
+ } else {
+ return B2CConverter.getCharset(charset);
+ }
+ }
+
+
/**
* @return a short name for this Realm implementation, for use in
* log messages.
@@ -1782,4 +1544,15 @@ public abstract class RealmBase extends LifecycleMBeanBase implements Realm {
throw new LifecycleException(sm.getString("realmBase.createUsernameRetriever.ClassCastException", className), e);
}
}
+
+
+ @Override
+ public String[] getRoles(Principal principal) {
+ if (principal instanceof GenericPrincipal) {
+ return ((GenericPrincipal) principal).getRoles();
+ }
+
+ String className = principal.getClass().getSimpleName();
+ throw new IllegalStateException(sm.getString("realmBase.cannotGetRoles", className));
+ }
}
diff --git a/java/org/apache/catalina/realm/mbeans-descriptors.xml b/java/org/apache/catalina/realm/mbeans-descriptors.xml
index bd36bd5..d8d7603 100644
--- a/java/org/apache/catalina/realm/mbeans-descriptors.xml
+++ b/java/org/apache/catalina/realm/mbeans-descriptors.xml
@@ -1,4 +1,4 @@
-<?xml version="1.0"?>
+<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
@@ -37,14 +37,6 @@
description="The JNDI named JDBC DataSource for your database"
type="java.lang.String"/>
- <attribute name="digest"
- description="Digest algorithm used in storing passwords in a non-plaintext format"
- type="java.lang.String"/>
-
- <attribute name="digestEncoding"
- description="The digest encoding charset."
- type="java.lang.String"/>
-
<attribute name="localDataSource"
description="Configures if the DataSource is local to the webapp"
type="boolean"/>
@@ -109,14 +101,6 @@
type="java.lang.String"
writeable="false"/>
- <attribute name="digest"
- description="Digest algorithm used in storing passwords in a non-plaintext format"
- type="java.lang.String"/>
-
- <attribute name="digestEncoding"
- description="The digest encoding charset."
- type="java.lang.String"/>
-
<attribute name="roleClassNames"
description="Comma-delimited list of javax.security.Principal classes that represent security roles"
type="java.lang.String"/>
@@ -178,14 +162,6 @@
description="The connection URL to use when trying to connect to the database"
type="java.lang.String"/>
- <attribute name="digest"
- description="Digest algorithm used in storing passwords in a non-plaintext format"
- type="java.lang.String"/>
-
- <attribute name="digestEncoding"
- description="The digest encoding charset."
- type="java.lang.String"/>
-
<attribute name="driverName"
description="The JDBC driver to use"
type="java.lang.String"/>
@@ -281,14 +257,6 @@
description="The JNDI context factory for this Realm"
type="java.lang.String"/>
- <attribute name="digest"
- description="Digest algorithm used in storing passwords in a non-plaintext format"
- type="java.lang.String"/>
-
- <attribute name="digestEncoding"
- description="The digest encoding charset."
- type="java.lang.String"/>
-
<attribute name="protocol"
description="The protocol to be used"
type="java.lang.String"/>
@@ -386,14 +354,6 @@
type="java.lang.String"
writeable="false"/>
- <attribute name="digest"
- description="Digest algorithm used in storing passwords in a non-plaintext format"
- type="java.lang.String"/>
-
- <attribute name="digestEncoding"
- description="The digest encoding charset."
- type="java.lang.String"/>
-
<attribute name="pathname"
description="The pathname of the XML file containing our database information"
type="java.lang.String"/>
@@ -433,14 +393,6 @@
type="java.lang.String"
writeable="false"/>
- <attribute name="digest"
- description="Digest algorithm used in storing passwords in a non-plaintext format"
- type="java.lang.String"/>
-
- <attribute name="digestEncoding"
- description="The digest encoding charset."
- type="java.lang.String"/>
-
<attribute name="resourceName"
description="The global JNDI name of the UserDatabase resource to use"
type="java.lang.String"/>
@@ -480,14 +432,6 @@
type="java.lang.String"
writeable="false"/>
- <attribute name="digest"
- description="Digest algorithm used in storing passwords in a non-plaintext format"
- type="java.lang.String"/>
-
- <attribute name="digestEncoding"
- description="The digest encoding charset."
- type="java.lang.String"/>
-
<attribute name="realms"
description="The set of realms that the combined realm is wrapping"
type="[Ljavax.management.ObjectName;"
@@ -545,10 +489,6 @@
type="java.lang.String"
writeable="false"/>
- <attribute name="digest"
- description="Digest algorithm used in storing passwords in a non-plaintext format"
- type="java.lang.String"/>
-
<attribute name="failureCount"
description="The number of times in a row a user has to fail authentication to be locked out. Defaults to 5."
type="int" />
@@ -557,10 +497,6 @@
description="The time (in seconds) a user is locked out for after too many authentication failures. Defaults to 300 (5 minutes)."
type="int" />
- <attribute name="digestEncoding"
- description="The digest encoding charset."
- type="java.lang.String"/>
-
<attribute name="realms"
description="The set of realms that the lockout realm is wrapping"
type="[Ljavax.management.ObjectName;"
diff --git a/java/org/apache/catalina/security/SecurityClassLoad.java b/java/org/apache/catalina/security/SecurityClassLoad.java
index 760879f..e63f9d7 100644
--- a/java/org/apache/catalina/security/SecurityClassLoad.java
+++ b/java/org/apache/catalina/security/SecurityClassLoad.java
@@ -61,7 +61,7 @@ public final class SecurityClassLoad {
"AccessLogAdapter");
loader.loadClass
(basePackage +
- "ApplicationContextFacade$1");
+ "ApplicationContextFacade$PrivilegedExecuteMethod");
loader.loadClass
(basePackage +
"ApplicationDispatcher$PrivilegedForward");
@@ -70,13 +70,16 @@ public final class SecurityClassLoad {
"ApplicationDispatcher$PrivilegedInclude");
loader.loadClass
(basePackage +
+ "ApplicationPushBuilder");
+ loader.loadClass
+ (basePackage +
"AsyncContextImpl");
loader.loadClass
(basePackage +
- "AsyncContextImpl$DebugException");
+ "AsyncContextImpl$AsyncRunnable");
loader.loadClass
(basePackage +
- "AsyncContextImpl$1");
+ "AsyncContextImpl$DebugException");
loader.loadClass
(basePackage +
"AsyncListenerWrapper");
@@ -109,10 +112,7 @@ public final class SecurityClassLoad {
final String basePackage = "org.apache.catalina.loader.";
loader.loadClass
(basePackage +
- "ResourceEntry");
- loader.loadClass
- (basePackage +
- "WebappClassLoaderBase$PrivilegedFindResourceByName");
+ "WebappClassLoaderBase$PrivilegedFindClassByName");
}
@@ -166,11 +166,12 @@ public final class SecurityClassLoad {
private static final void loadCoyotePackage(ClassLoader loader)
throws Exception {
final String basePackage = "org.apache.coyote.";
- loader.loadClass(basePackage + "http11.AbstractOutputBuffer$1");
+ loader.loadClass(basePackage + "PushToken");
loader.loadClass(basePackage + "http11.Constants");
// Make sure system property is read at this point
Class<?> clazz = loader.loadClass(basePackage + "Constants");
clazz.newInstance();
+ loader.loadClass(basePackage + "http2.Stream$1");
}
@@ -236,6 +237,9 @@ public final class SecurityClassLoad {
"OutputBuffer$1");
loader.loadClass
(basePackage +
+ "OutputBuffer$2");
+ loader.loadClass
+ (basePackage +
"CoyoteInputStream$1");
loader.loadClass
(basePackage +
@@ -267,18 +271,26 @@ public final class SecurityClassLoad {
throws Exception {
final String basePackage = "org.apache.tomcat.";
// buf
+ loader.loadClass(basePackage + "util.buf.ByteBufferUtils");
loader.loadClass(basePackage + "util.buf.HexUtils");
loader.loadClass(basePackage + "util.buf.StringCache");
loader.loadClass(basePackage + "util.buf.StringCache$ByteEntry");
loader.loadClass(basePackage + "util.buf.StringCache$CharEntry");
loader.loadClass(basePackage + "util.buf.UriUtil");
+ // collections
+ Class<?> clazz = loader.loadClass(basePackage + "util.collections.CaseInsensitiveKeyMap");
+ // Ensure StringManager is configured
+ clazz.newInstance();
+ loader.loadClass(basePackage + "util.collections.CaseInsensitiveKeyMap$EntryImpl");
+ loader.loadClass(basePackage + "util.collections.CaseInsensitiveKeyMap$EntryIterator");
+ loader.loadClass(basePackage + "util.collections.CaseInsensitiveKeyMap$EntrySet");
+ loader.loadClass(basePackage + "util.collections.CaseInsensitiveKeyMap$Key");
// http
- loader.loadClass(basePackage + "util.http.HttpMessages");
+ loader.loadClass(basePackage + "util.http.CookieProcessor");
+ loader.loadClass(basePackage + "util.http.NamesEnumerator");
// Make sure system property is read at this point
- Class<?> clazz = loader.loadClass(
- basePackage + "util.http.FastHttpDateFormat");
+ clazz = loader.loadClass(basePackage + "util.http.FastHttpDateFormat");
clazz.newInstance();
- loader.loadClass(basePackage + "util.http.HttpMessages");
loader.loadClass(basePackage + "util.http.parser.HttpParser");
loader.loadClass(basePackage + "util.http.parser.MediaType");
loader.loadClass(basePackage + "util.http.parser.MediaTypeCache");
diff --git a/java/org/apache/catalina/security/SecurityConfig.java b/java/org/apache/catalina/security/SecurityConfig.java
index b2292a4..5fd0b0a 100644
--- a/java/org/apache/catalina/security/SecurityConfig.java
+++ b/java/org/apache/catalina/security/SecurityConfig.java
@@ -19,6 +19,8 @@ package org.apache.catalina.security;
import java.security.Security;
import org.apache.catalina.startup.CatalinaProperties;
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
/**
* Util class to protect Catalina against package access and insertion.
@@ -28,8 +30,7 @@ import org.apache.catalina.startup.CatalinaProperties;
public final class SecurityConfig{
private static SecurityConfig singleton = null;
- private static final org.apache.juli.logging.Log log=
- org.apache.juli.logging.LogFactory.getLog( SecurityConfig.class );
+ private static final Log log = LogFactory.getLog(SecurityConfig.class);
private static final String PACKAGE_ACCESS = "sun.,"
diff --git a/java/org/apache/catalina/security/SecurityUtil.java b/java/org/apache/catalina/security/SecurityUtil.java
index 3406e10..c08361c 100644
--- a/java/org/apache/catalina/security/SecurityUtil.java
+++ b/java/org/apache/catalina/security/SecurityUtil.java
@@ -35,8 +35,8 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.apache.catalina.Globals;
-import org.apache.catalina.comet.CometFilter;
-import org.apache.catalina.comet.CometProcessor;
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.ExceptionUtils;
import org.apache.tomcat.util.res.StringManager;
/**
@@ -73,8 +73,7 @@ public final class SecurityUtil{
*/
private static final Map<Class<?>,Method[]> classCache = new ConcurrentHashMap<>();
- private static final org.apache.juli.logging.Log log=
- org.apache.juli.logging.LogFactory.getLog( SecurityUtil.class );
+ private static final Log log = LogFactory.getLog(SecurityUtil.class);
private static final boolean packageDefinitionEnabled =
(System.getProperty("package.definition") == null &&
@@ -151,23 +150,18 @@ public final class SecurityUtil{
Principal principal)
throws Exception {
- // CometProcessor instances must not be cached as Servlet or
- // NoSuchMethodException will be thrown.
- Class<? extends Servlet> targetType =
- targetObject instanceof CometProcessor ? CometProcessor.class : Servlet.class;
-
Method method = null;
Method[] methodsCache = classCache.get(Servlet.class);
if(methodsCache == null) {
method = createMethodAndCacheIt(methodsCache,
- targetType,
+ Servlet.class,
methodName,
targetParameterTypes);
} else {
method = findMethod(methodsCache, methodName);
if (method == null) {
method = createMethodAndCacheIt(methodsCache,
- targetType,
+ Servlet.class,
methodName,
targetParameterTypes);
}
@@ -239,23 +233,18 @@ public final class SecurityUtil{
Principal principal)
throws Exception {
- // CometFilter instances must not be cached as Filter or
- // NoSuchMethodException will be thrown.
- Class<? extends Filter> targetType =
- targetObject instanceof CometFilter ? CometFilter.class : Filter.class;
-
Method method = null;
Method[] methodsCache = classCache.get(Filter.class);
if(methodsCache == null) {
method = createMethodAndCacheIt(methodsCache,
- targetType,
+ Filter.class,
methodName,
targetParameterTypes);
} else {
method = findMethod(methodsCache, methodName);
if (method == null) {
method = createMethodAndCacheIt(methodsCache,
- targetType,
+ Filter.class,
methodName,
targetParameterTypes);
}
@@ -269,7 +258,7 @@ public final class SecurityUtil{
* Perform work as a particular <code>Subject</code>. Here the work
* will be granted to a <code>null</code> subject.
*
- * @param method the method to apply the security restriction
+ * @param methodName the method to apply the security restriction
* @param targetObject the <code>Servlet</code> on which the method will
* be called.
* @param targetArguments <code>Object</code> array contains the
diff --git a/java/org/apache/catalina/servlet4preview/GenericFilter.java b/java/org/apache/catalina/servlet4preview/GenericFilter.java
new file mode 100644
index 0000000..3567097
--- /dev/null
+++ b/java/org/apache/catalina/servlet4preview/GenericFilter.java
@@ -0,0 +1,86 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.catalina.servlet4preview;
+
+import java.io.Serializable;
+import java.util.Enumeration;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+
+public abstract class GenericFilter implements Filter, FilterConfig, Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ private volatile FilterConfig filterConfig;
+
+
+ @Override
+ public String getInitParameter(String name) {
+ return getFilterConfig().getInitParameter(name);
+ }
+
+
+ @Override
+ public Enumeration<String> getInitParameterNames() {
+ return getFilterConfig().getInitParameterNames();
+ }
+
+
+ /**
+ * Obtain the FilterConfig used to initialise this Filter instance.
+ *
+ * @return The config previously passed to the {@link #init(FilterConfig)}
+ * method
+ */
+ public FilterConfig getFilterConfig() {
+ return filterConfig;
+ }
+
+
+ @Override
+ public ServletContext getServletContext() {
+ return getFilterConfig().getServletContext();
+ }
+
+
+ @Override
+ public void init(FilterConfig filterConfig) throws ServletException {
+ this.filterConfig = filterConfig;
+ init();
+ }
+
+
+ /**
+ * Convenience method for sub-classes to save them having to call
+ * <code>super.init(config)</code>. This is a NO-OP by default.
+ *
+ * @throws ServletException If an exception occurs that interrupts the
+ * Filter's normal operation
+ */
+ public void init() throws ServletException {
+ // NO-OP
+ }
+
+
+ @Override
+ public String getFilterName() {
+ return getFilterConfig().getFilterName();
+ }
+}
diff --git a/java/org/apache/catalina/servlet4preview/RequestDispatcher.java b/java/org/apache/catalina/servlet4preview/RequestDispatcher.java
new file mode 100644
index 0000000..41de1e8
--- /dev/null
+++ b/java/org/apache/catalina/servlet4preview/RequestDispatcher.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.catalina.servlet4preview;
+
+/**
+ * Provides early access to some parts of the proposed Servlet 4.0 API.
+ */
+public interface RequestDispatcher extends javax.servlet.RequestDispatcher {
+
+ /**
+ * The name of the request attribute that should be set by the container
+ * when the {@link #forward(javax.servlet.ServletRequest,
+ * javax.servlet.ServletResponse)} method is called. It provides the
+ * original value of a path-related property of the request. See the chapter
+ * "Forwarded Request Parameters" in the Servlet Specification for details.
+ *
+ * @since Servlet 4.0
+ */
+ static final String FORWARD_MAPPING = "javax.servlet.forward.mapping";
+
+ /**
+ * The name of the request attribute that should be set by the container
+ * when the {@link #include(javax.servlet.ServletRequest,
+ * javax.servlet.ServletResponse)} method is called on the
+ * {@code RequestDispatcher} obtained by a path and not by a name.
+ * It provides information on the path that was used to obtain the
+ * {@code RequestDispatcher} instance for this include call. See the chapter
+ * "Included Request Parameters" in the Servlet Specification for details.
+ *
+ * @since Servlet 4.0
+ */
+ static final String INCLUDE_MAPPING = "javax.servlet.include.mapping";
+}
diff --git a/java/org/apache/catalina/servlet4preview/http/HttpFilter.java b/java/org/apache/catalina/servlet4preview/http/HttpFilter.java
new file mode 100644
index 0000000..bc1b2eb
--- /dev/null
+++ b/java/org/apache/catalina/servlet4preview/http/HttpFilter.java
@@ -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.catalina.servlet4preview.http;
+
+import java.io.IOException;
+
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.catalina.servlet4preview.GenericFilter;
+
+public abstract class HttpFilter extends GenericFilter {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * {@inheritDoc}
+ *
+ * This implementation tests the request and response to see if they are
+ * instances of {@link HttpServletRequest} and {@link HttpServletResponse}
+ * respectively. If they are then they are passed to
+ * {@link #doFilter(HttpServletRequest, HttpServletResponse, FilterChain)}.
+ * If not, a {@link ServletException} is thrown.
+ *
+ * @throws ServletException If either the request or response are not of the
+ * expected types or any other error occurs
+ */
+ @Override
+ public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
+ throws IOException, ServletException {
+ if (!(request instanceof HttpServletRequest)) {
+ throw new ServletException(request + " not HttpServletRequest");
+ }
+ if (!(response instanceof HttpServletResponse)) {
+ throw new ServletException(request + " not HttpServletResponse");
+ }
+ doFilter((HttpServletRequest) request, (HttpServletResponse) response, chain);
+ }
+
+
+ /**
+ * The <code>doFilter</code> method of the Filter is called by the container
+ * each time a request/response pair is passed through the chain due to a
+ * client request for a resource at the end of the chain. The FilterChain
+ * passed in to this method allows the Filter to pass on the request and
+ * response to the next entity in the chain.
+ * <p>
+ * A typical implementation of this method would follow the following
+ * pattern:- <br>
+ * 1. Examine the request<br>
+ * 2. Optionally wrap the request object with a custom implementation to
+ * filter content or headers for input filtering <br>
+ * 3. Optionally wrap the response object with a custom implementation to
+ * filter content or headers for output filtering <br>
+ * 4. a) <strong>Either</strong> invoke the next entity in the chain using
+ * the FilterChain object (<code>chain.doFilter()</code>), <br>
+ * 4. b) <strong>or</strong> not pass on the request/response pair to the
+ * next entity in the filter chain to block the request processing<br>
+ * 5. Directly set headers on the response after invocation of the next
+ * entity in the filter chain.
+ *
+ * This default implementation simply calls the next filter in the filter
+ * chain.
+ *
+ * @param request The request to process
+ * @param response The response associated with the request
+ * @param chain Provides access to the next filter in the chain for this
+ * filter to pass the request and response to for further
+ * processing
+ *
+ * @throws IOException if an I/O error occurs during this filter's
+ * processing of the request
+ * @throws ServletException if the processing fails for any other reason
+ */
+ protected void doFilter(HttpServletRequest request, HttpServletResponse response,
+ FilterChain chain) throws IOException, ServletException {
+ chain.doFilter(request, response);
+ }
+}
diff --git a/java/org/apache/catalina/servlet4preview/http/HttpServletRequest.java b/java/org/apache/catalina/servlet4preview/http/HttpServletRequest.java
new file mode 100644
index 0000000..5751226
--- /dev/null
+++ b/java/org/apache/catalina/servlet4preview/http/HttpServletRequest.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.catalina.servlet4preview.http;
+
+/**
+ * Provides early access to some parts of the proposed Servlet 4.0 API.
+ */
+public interface HttpServletRequest extends javax.servlet.http.HttpServletRequest {
+
+ public Mapping getMapping();
+
+ /**
+ * Does the current request allow push requests. This will return {@code
+ * true} only if the underlying protocol supports server push and if pushes
+ * are permitted from the current request.
+ *
+ * @return {@code true} if server push is supported for the current request
+ * otherwise {@code false}
+ */
+ public boolean isPushSupported();
+
+ /**
+ * Obtain a builder for generating push requests. {@link PushBuilder}
+ * documents how this request will be used as the basis for a push request.
+ * Each call to this method will return a new instance, independent of any
+ * previous instance obtained.
+ *
+ * @return A builder that can be used to generate push requests based on
+ * this request.
+ *
+ * @since Servlet 4.0
+ */
+ public PushBuilder getPushBuilder();
+}
diff --git a/java/org/apache/catalina/servlet4preview/http/HttpServletRequestWrapper.java b/java/org/apache/catalina/servlet4preview/http/HttpServletRequestWrapper.java
new file mode 100644
index 0000000..c6f66cc
--- /dev/null
+++ b/java/org/apache/catalina/servlet4preview/http/HttpServletRequestWrapper.java
@@ -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.servlet4preview.http;
+
+
+/**
+ * Provides early access to some parts of the proposed Servlet 4.0 API.
+ */
+public class HttpServletRequestWrapper extends javax.servlet.http.HttpServletRequestWrapper
+ implements HttpServletRequest {
+
+ /**
+ * Constructs a request object wrapping the given request.
+ *
+ * @param request The request to wrap
+ *
+ * @throws java.lang.IllegalArgumentException
+ * if the request is null
+ */
+ public HttpServletRequestWrapper(javax.servlet.http.HttpServletRequest request) {
+ super(request);
+ }
+
+ private HttpServletRequest _getHttpServletRequest() {
+ return (HttpServletRequest) super.getRequest();
+ }
+
+ /**
+ * {@inheritDoc}
+ * <p>
+ * The default behavior of this method is to return
+ * {@link HttpServletRequest#getMapping()} on the wrapped request object.
+ *
+ * @since Servlet 4.0
+ */
+ @Override
+ public Mapping getMapping() {
+ return this._getHttpServletRequest().getMapping();
+ }
+
+ /**
+ * {@inheritDoc}
+ * <p>
+ * The default behavior of this method is to return
+ * {@link HttpServletRequest#isPushSupported()} on the wrapped request object.
+ *
+ * @since Servlet 4.0
+ */
+ @Override
+ public boolean isPushSupported() {
+ return this._getHttpServletRequest().isPushSupported();
+ }
+
+
+ /**
+ * {@inheritDoc}
+ * <p>
+ * The default behavior of this method is to return
+ * {@link HttpServletRequest#getPushBuilder()} on the wrapped request object.
+ *
+ * @since Servlet 4.0
+ */
+ @Override
+ public PushBuilder getPushBuilder() {
+ return this._getHttpServletRequest().getPushBuilder();
+ }
+}
diff --git a/java/org/apache/catalina/servlet4preview/http/Mapping.java b/java/org/apache/catalina/servlet4preview/http/Mapping.java
new file mode 100644
index 0000000..5e44d7f
--- /dev/null
+++ b/java/org/apache/catalina/servlet4preview/http/Mapping.java
@@ -0,0 +1,51 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements. See the NOTICE file distributed with
+* this work for additional information regarding copyright ownership.
+* The ASF licenses this file to You under the Apache License, Version 2.0
+* (the "License"); you may not use this file except in compliance with
+* the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package org.apache.catalina.servlet4preview.http;
+
+/**
+ * Represents how the request from which this object was obtained was mapped to
+ * the associated servlet.
+ *
+ * @since 4.0
+ */
+public interface Mapping {
+
+ /**
+ * @return The value that was matched or the empty String if not known.
+ */
+ String getMatchValue();
+
+ /**
+ * @return The {@code url-pattern} that matched this request or the empty
+ * String if not known.
+ */
+ String getPattern();
+
+ /**
+ * @return The type of match ({@link MappingMatch#UNKNOWN} if not known)
+ */
+ MappingMatch getMappingMatch();
+
+ /**
+ * @return The name of the servlet (as specified in web.xml,
+ * {@link javax.servlet.annotation.WebServlet#name()},
+ * {@link javax.servlet.ServletContext#addServlet(String, Class)} or
+ * one of the other <code>addServlet()</code> methods) that the
+ * request was mapped to.
+ */
+ String getServletName();
+}
diff --git a/java/org/apache/catalina/servlet4preview/http/MappingMatch.java b/java/org/apache/catalina/servlet4preview/http/MappingMatch.java
new file mode 100644
index 0000000..14ccd26
--- /dev/null
+++ b/java/org/apache/catalina/servlet4preview/http/MappingMatch.java
@@ -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.catalina.servlet4preview.http;
+
+/**
+ * Represents the ways that a request can be mapped to a servlet
+ *
+ * @since 4.0
+ */
+public enum MappingMatch {
+
+ CONTEXT_ROOT,
+ DEFAULT,
+ EXACT,
+ EXTENSION,
+ PATH,
+ UNKNOWN
+}
diff --git a/java/org/apache/catalina/servlet4preview/http/PushBuilder.java b/java/org/apache/catalina/servlet4preview/http/PushBuilder.java
new file mode 100644
index 0000000..fe45c8d
--- /dev/null
+++ b/java/org/apache/catalina/servlet4preview/http/PushBuilder.java
@@ -0,0 +1,254 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements. See the NOTICE file distributed with
+* this work for additional information regarding copyright ownership.
+* The ASF licenses this file to You under the Apache License, Version 2.0
+* (the "License"); you may not use this file except in compliance with
+* the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package org.apache.catalina.servlet4preview.http;
+
+import java.util.Set;
+
+/**
+ * Builds a push request based on the {@link HttpServletRequest} from which this
+ * builder was obtained. The push request will be constructed on the following
+ * basis:
+ * <ul>
+ * <li>The request method is set to <code>GET</code>.</li>
+ * <li>The path will not be set. This must be set explicitly via a call to
+ * {@link #path(String)}.</li>
+ * <li>Conditional, range, expectation, authorization and referer headers will
+ * be removed.</li>
+ * <li>Cookies added to the associated response will be added to the push
+ * request unless maxAge <= 0 in which case any request cookie with the
+ * same name will be removed.</li>
+ * <li>The referer header will be set to
+ * {@link HttpServletRequest#getRequestURL()} plus, if present, the query
+ * string from {@link HttpServletRequest#getQueryString()}.
+ * <li>If either of the headers {@code If-Modified-Since} or
+ * {@code If-None-Match} were present then {@link #isConditional()} will be
+ * set to {@code true}.
+ * </ul>
+ *
+ * @since Servlet 4.0
+ */
+public interface PushBuilder {
+
+ /**
+ * Specify the HTTP method to use for the push request.
+ *
+ * @param method The method to use for the push request
+ *
+ * @return This builder instance
+ */
+ PushBuilder method(String method);
+
+ /**
+ * Specifies the query string to use in subsequent push requests generated
+ * by a call to {@link #push()}. This will be appended to any query string
+ * specified in the call to {@link #path(String)}.
+ *
+ * @param queryString The query string to use to generate push requests
+ *
+ * @return This builder instance
+ */
+ PushBuilder queryString(String queryString);
+
+ /**
+ * Specifies the session ID to use in subsequent push requests generated
+ * by a call to {@link #push()}. The session ID will be presented the same
+ * way as it is on the original request (cookie or URL parameter). The
+ * default is determined in the following order:
+ * <ul>
+ * <li>the requested session ID for the originating request</li>
+ * <li>the session ID generated in the originated request</li>
+ * <li>{@code null}</li>
+ * </ul>
+ *
+ * @param sessionId The session ID to use to generate push requests
+ *
+ * @return This builder instance
+ */
+ PushBuilder sessionId(String sessionId);
+
+ /**
+ * Sets if the request will be conditional. If {@code true} the values from
+ * {@link #getEtag()} and {@link #getLastModified()} will be used to
+ * construct appropriate headers.
+ *
+ * @param conditional Should generated push requests be conditional
+ *
+ * @return This builder instance
+ */
+ PushBuilder conditional(boolean conditional);
+
+ /**
+ * Sets a HTTP header on the request. Any existing headers of the same name
+ * are first remove.
+ *
+ * @param name The name of the header to set
+ * @param value The value of the header to set
+ *
+ * @return This builder instance
+ */
+ PushBuilder setHeader(String name, String value);
+
+ /**
+ * Adds a HTTP header to the request.
+ *
+ * @param name The name of the header to add
+ * @param value The value of the header to add
+ *
+ * @return This builder instance
+ */
+ PushBuilder addHeader(String name, String value);
+
+ /**
+ * Removes an HTTP header from the request.
+ *
+ * @param name The name of the header to remove
+ *
+ * @return This builder instance
+ */
+ PushBuilder removeHeader(String name);
+
+ /**
+ * Sets the URI path to be used for the push request. This must be called
+ * before every call to {@link #push()}. If the path includes a query
+ * string, the query string will be appended to the existing query string
+ * (if any) and no de-duplication will occur.
+ *
+ * @param path Paths beginning with '/' are treated as absolute paths. All
+ * other paths are treated as relative to the context path of
+ * the request used to create this builder instance. The path
+ * may include a query string.
+ *
+ * @return This builder instance
+ */
+ PushBuilder path(String path);
+
+ /**
+ * Sets the etag to be used for conditional push requests. This will be
+ * set to {@code null} after a call to {@link #push()} so it must be
+ * explicitly set for every push request that requires it.
+ *
+ * @param etag The etag use for the push request
+ *
+ * @return This builder instance
+ */
+ PushBuilder etag(String etag);
+
+ /**
+ * Sets the last modified to be used for conditional push requests. This
+ * will be set to {@code null} after a call to {@link #push()} so it must be
+ * explicitly set for every push request that requires it.
+ *
+ * @param lastModified The last modified value to use for the push request
+ *
+ * @return This builder instance
+ */
+ PushBuilder lastModified(String lastModified);
+
+ /**
+ * Generates the push request and sends it to the client unless pushes are
+ * not available for some reason. After calling this method the following
+ * fields are set to {@code null}:
+ * <ul>
+ * <li>{@code path}</li>
+ * <li>{@code etag}</li>
+ * <li>{@code lastModified}</li>
+ * </ul>
+ *
+ * @return {@code true} if the push request was sent to the client,
+ * otherwise {@code false}
+ *
+ * @throws IllegalStateException If this method is called when {@code path}
+ * is {@code null}
+ * @throws IllegalArgumentException If the request to push requires a body
+ */
+ boolean push();
+
+ /**
+ * Obtain the name of the HTTP method that will be used for push requests
+ * generated by future calls to {@code push()}.
+ *
+ * @return The HTTP method to be used for future push requests
+ */
+ String getMethod();
+
+ /**
+ * Obtain the query string that will be used for push requests generated by
+ * future calls to {@code push()}.
+ *
+ * @return The query string that will be appended to push requests.
+ */
+ String getQueryString();
+
+ /**
+ * Obtain the session ID that will be used for push requests generated by
+ * future calls to {@code push()}.
+ *
+ * @return The session that will be used for push requests.
+ */
+ String getSessionId();
+
+ /**
+ * Will push requests generated by future calls to {@code push()} be
+ * conditional.
+ *
+ * @return {@code true} if push requests will be conditional
+ */
+ boolean isConditional();
+
+ /**
+ * @return The current set of names of HTTP headers to be used the next time
+ * {@code push()} is called.
+ */
+ Set<String> getHeaderNames();
+
+ /**
+ * Obtain a value for the given HTTP header.
+ * TODO Servlet 4.0
+ * Clarify the behaviour of this method
+ *
+ * @param name The name of the header whose value is to be returned
+ *
+ * @return The value of the given header. If multiple values are defined
+ * then any may be returned
+ */
+ String getHeader(String name);
+
+ /**
+ * Obtain the path that will be used for the push request that will be
+ * generated by the next call to {@code push()}.
+ *
+ * @return The path value that will be associated with the next push request
+ */
+ String getPath();
+
+ /**
+ * Obtain the etag that will be used for the push request that will be
+ * generated by the next call to {@code push()}.
+ *
+ * @return The etag value that will be associated with the next push request
+ */
+ String getEtag();
+
+ /**
+ * Obtain the last modified that will be used for the push request that will
+ * be generated by the next call to {@code push()}.
+ *
+ * @return The last modified value that will be associated with the next
+ * push request
+ */
+ String getLastModified();
+}
diff --git a/java/org/apache/catalina/servlet4preview/package-info.java b/java/org/apache/catalina/servlet4preview/package-info.java
new file mode 100644
index 0000000..59692df
--- /dev/null
+++ b/java/org/apache/catalina/servlet4preview/package-info.java
@@ -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.
+ */
+
+/**
+ * This package provides early access to some of the new features in the Servlet
+ * 4.0 API. This package exists in Tomcat 8.5 only. The package will not exist
+ * in Tomcat 9 and applications depending on classes in this package will need
+ * to be modified before they will work with Tomcat 9. It is intended that any
+ * such modifications will be limited to replacing
+ * <code>import org.apache.catalina.servlet4preview...</code> with
+ * <code>import javax.servlet...</code> and removing casts.
+ * <p>
+ * This package is not a complete copy of the proposed Servlet 4.0 API. It
+ * contains only a sub-set of those classes that are new or modified in Servlet
+ * 4.0. Users may request the inclusion of additional Servlet 4.0 API changes in
+ * this package via a Bugzilla enhancement request against Tomcat 8.5.
+ * <p>
+ * The Servlet 4.0 API is a work in progress. The public API of classes in this
+ * package may change in incompatible ways - including classes being renamed or
+ * deleted - between point releases of Tomcat 8.5.
+ */
+package org.apache.catalina.servlet4preview;
diff --git a/java/org/apache/catalina/servlets/CGIServlet.java b/java/org/apache/catalina/servlets/CGIServlet.java
index bb94881..d3746d5 100644
--- a/java/org/apache/catalina/servlets/CGIServlet.java
+++ b/java/org/apache/catalina/servlets/CGIServlet.java
@@ -41,7 +41,6 @@ import javax.servlet.RequestDispatcher;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
-import javax.servlet.ServletOutputStream;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
@@ -349,28 +348,6 @@ public final class CGIServlet extends HttpServlet {
* Logs important Servlet API and container information.
*
* <p>
- * Copied from SnoopAllServlet by Craig R. McClanahan
- * </p>
- *
- * @param out Unused
- * @param req HttpServletRequest object used as source of information
- * @param res Unused
- *
- * @exception IOException if a write operation exception occurs
- *
- * @deprecated Use {@link #printServletEnvironment(HttpServletRequest)}.
- * This will be removed in Tomcat 8.5.X onwards
- */
- @Deprecated
- protected void printServletEnvironment(ServletOutputStream out,
- HttpServletRequest req, HttpServletResponse res) throws IOException {
- printServletEnvironment(req);
- }
-
- /**
- * Logs important Servlet API and container information.
- *
- * <p>
* Based on SnoopAllServlet by Craig R. McClanahan
* </p>
*
diff --git a/java/org/apache/catalina/servlets/DefaultServlet.java b/java/org/apache/catalina/servlets/DefaultServlet.java
index ea36964..25502b1 100644
--- a/java/org/apache/catalina/servlets/DefaultServlet.java
+++ b/java/org/apache/catalina/servlets/DefaultServlet.java
@@ -29,6 +29,7 @@ import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.RandomAccessFile;
import java.io.Reader;
+import java.io.Serializable;
import java.io.StringReader;
import java.io.StringWriter;
import java.security.AccessController;
@@ -36,6 +37,7 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.Iterator;
+import java.util.List;
import java.util.Locale;
import java.util.StringTokenizer;
@@ -146,7 +148,9 @@ public class DefaultServlet extends HttpServlet {
/**
* JNDI resources name.
+ * @deprecated Unused. Will be removed in Tomcat 9.
*/
+ @Deprecated
protected static final String RESOURCES_JNDI_NAME = "java:/comp/Resources";
/**
@@ -193,9 +197,9 @@ public class DefaultServlet extends HttpServlet {
protected boolean readOnly = true;
/**
- * Should be serve gzip versions of files. By default, it's set to false.
+ * List of compression formats to serve and their preference order.
*/
- protected boolean gzip = false;
+ protected CompressionFormat[] compressionFormats;
/**
* The output buffer size to use when serving resources.
@@ -280,8 +284,9 @@ public class DefaultServlet extends HttpServlet {
if (getServletConfig().getInitParameter("readonly") != null)
readOnly = Boolean.parseBoolean(getServletConfig().getInitParameter("readonly"));
- if (getServletConfig().getInitParameter("gzip") != null)
- gzip = Boolean.parseBoolean(getServletConfig().getInitParameter("gzip"));
+ compressionFormats = parseCompressionFormats(
+ getServletConfig().getInitParameter("precompressed"),
+ getServletConfig().getInitParameter("gzip"));
if (getServletConfig().getInitParameter("sendfileSize") != null)
sendfileSize =
@@ -321,6 +326,27 @@ public class DefaultServlet extends HttpServlet {
}
}
+ private CompressionFormat[] parseCompressionFormats(String precompressed, String gzip) {
+ List<CompressionFormat> ret = new ArrayList<>();
+ if (precompressed != null && precompressed.indexOf('=') > 0) {
+ for (String pair : precompressed.split(",")) {
+ String[] setting = pair.split("=");
+ String encoding = setting[0];
+ String extension = setting[1];
+ ret.add(new CompressionFormat(extension, encoding));
+ }
+ } else if (precompressed != null) {
+ if (Boolean.parseBoolean(precompressed)) {
+ ret.add(new CompressionFormat(".br", "br"));
+ ret.add(new CompressionFormat(".gz", "gzip"));
+ }
+ } else if (Boolean.parseBoolean(gzip)) {
+ // gzip handling is for backwards compatibility with Tomcat 8.x
+ ret.add(new CompressionFormat(".gz", "gzip"));
+ }
+ return ret.toArray(new CompressionFormat[ret.size()]);
+ }
+
// ------------------------------------------------------ Protected Methods
@@ -329,6 +355,7 @@ public class DefaultServlet extends HttpServlet {
* Return the relative path associated with this servlet.
*
* @param request The servlet request we are processing
+ * @return the relative path
*/
protected String getRelativePath(HttpServletRequest request) {
return getRelativePath(request, false);
@@ -337,7 +364,7 @@ public class DefaultServlet extends HttpServlet {
protected String getRelativePath(HttpServletRequest request, boolean allowEmptyPath) {
// IMPORTANT: DefaultServlet can be mapped to '/' or '/path/*' but always
// serves resources from the web app root with context rooted paths.
- // i.e. it can not be used to mount the web app root under a sub-path
+ // i.e. it cannot be used to mount the web app root under a sub-path
// This method must construct a complete context rooted path, although
// subclasses can change this behaviour.
@@ -544,6 +571,11 @@ public class DefaultServlet extends HttpServlet {
* Handle a partial PUT. New content specified in request is appended to
* existing content in oldRevisionContent (if present). This code does
* not support simultaneous partial updates to the same resource.
+ * @param req The Servlet request
+ * @param range The range that will be written
+ * @param path The path
+ * @return the associated file object
+ * @throws IOException an IO error occurred
*/
protected File executePartialPut(HttpServletRequest req, Range range,
String path)
@@ -642,9 +674,10 @@ public class DefaultServlet extends HttpServlet {
* @param request The servlet request we are processing
* @param response The servlet response we are creating
* @param resource The resource
- * @return boolean true if the resource meets all the specified conditions,
- * and false if any of the conditions is not satisfied, in which case
- * request processing is stopped
+ * @return <code>true</code> if the resource meets all the specified
+ * conditions, and <code>false</code> if any of the conditions is not
+ * satisfied, in which case request processing is stopped
+ * @throws IOException an IO error occurred
*/
protected boolean checkIfHeaders(HttpServletRequest request,
HttpServletResponse response,
@@ -663,6 +696,7 @@ public class DefaultServlet extends HttpServlet {
* URL rewriter.
*
* @param path Path which has to be rewritten
+ * @return the rewritten path
*/
protected String rewriteUrl(String path) {
return URLEncoder.DEFAULT.encode(path, "UTF-8");
@@ -782,7 +816,7 @@ public class DefaultServlet extends HttpServlet {
}
// These need to reflect the original resource, not the potentially
- // gzip'd version of the resource so get them now if they are going to
+ // precompressed version of the resource so get them now if they are going to
// be needed later
String eTag = null;
String lastModifiedHttp = null;
@@ -792,11 +826,13 @@ public class DefaultServlet extends HttpServlet {
}
- // Serve a gzipped version of the file if present
- boolean usingGzippedVersion = false;
- if (gzip && !included && resource.isFile() && !path.endsWith(".gz")) {
- WebResource gzipResource = resources.getResource(path + ".gz");
- if (gzipResource.exists() && gzipResource.isFile()) {
+ // Serve a precompressed version of the file if present
+ boolean usingPrecompressedVersion = false;
+ if (compressionFormats.length > 0 && !included && resource.isFile() &&
+ !pathEndsWithCompressedExtension(path)) {
+ List<PrecompressedResource> precompressedResources =
+ getAvailablePrecompressedResources(path);
+ if (!precompressedResources.isEmpty()) {
Collection<String> varyHeaders = response.getHeaders("Vary");
boolean addRequired = true;
for (String varyHeader : varyHeaders) {
@@ -809,10 +845,12 @@ public class DefaultServlet extends HttpServlet {
if (addRequired) {
response.addHeader("Vary", "accept-encoding");
}
- if (checkIfGzip(request)) {
- response.addHeader("Content-Encoding", "gzip");
- resource = gzipResource;
- usingGzippedVersion = true;
+ PrecompressedResource bestResource =
+ getBestPrecompressedResource(request, precompressedResources);
+ if (bestResource != null) {
+ response.addHeader("Content-Encoding", bestResource.format.encoding);
+ resource = bestResource.resource;
+ usingPrecompressedVersion = true;
}
}
}
@@ -870,7 +908,7 @@ public class DefaultServlet extends HttpServlet {
} catch (IllegalStateException e) {
// If it fails, we try to get a Writer instead if we're
// trying to serve a text file
- if (!usingGzippedVersion &&
+ if (!usingPrecompressedVersion &&
((contentType == null) ||
(contentType.startsWith("text")) ||
(contentType.endsWith("xml")) ||
@@ -1031,6 +1069,86 @@ public class DefaultServlet extends HttpServlet {
}
}
+ private boolean pathEndsWithCompressedExtension(String path) {
+ for (CompressionFormat format : compressionFormats) {
+ if (path.endsWith(format.extension)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private List<PrecompressedResource> getAvailablePrecompressedResources(String path) {
+ List<PrecompressedResource> ret = new ArrayList<>(compressionFormats.length);
+ for (CompressionFormat format : compressionFormats) {
+ WebResource precompressedResource = resources.getResource(path + format.extension);
+ if (precompressedResource.exists() && precompressedResource.isFile()) {
+ ret.add(new PrecompressedResource(precompressedResource, format));
+ }
+ }
+ return ret;
+ }
+
+ /**
+ * Match the client preferred encoding formts to the available precompressed resources.
+ *
+ * @param request The servlet request we are processing
+ * @param precompressedResources List of available precompressed resources.
+ * @return The best matching precompressed resource or null if no match was found.
+ */
+ private PrecompressedResource getBestPrecompressedResource(HttpServletRequest request,
+ List<PrecompressedResource> precompressedResources) {
+ Enumeration<String> headers = request.getHeaders("Accept-Encoding");
+ PrecompressedResource bestResource = null;
+ double bestResourceQuality = 0;
+ int bestResourcePreference = Integer.MAX_VALUE;
+ while (headers.hasMoreElements()) {
+ String header = headers.nextElement();
+ for (String preference : header.split(",")) {
+ double quality = 1;
+ int qualityIdx = preference.indexOf(';');
+ if (qualityIdx > 0) {
+ int equalsIdx = preference.indexOf('=', qualityIdx + 1);
+ if (equalsIdx == -1) {
+ continue;
+ }
+ quality = Double.parseDouble(preference.substring(equalsIdx + 1).trim());
+ }
+ if (quality >= bestResourceQuality) {
+ String encoding = preference;
+ if (qualityIdx > 0) {
+ encoding = encoding.substring(0, qualityIdx);
+ }
+ encoding = encoding.trim();
+ if ("identity".equals(encoding)) {
+ bestResource = null;
+ bestResourceQuality = quality;
+ bestResourcePreference = Integer.MAX_VALUE;
+ continue;
+ }
+ if ("*".equals(encoding)) {
+ bestResource = precompressedResources.get(0);
+ bestResourceQuality = quality;
+ bestResourcePreference = 0;
+ continue;
+ }
+ for (int i = 0; i < precompressedResources.size(); ++i) {
+ PrecompressedResource resource = precompressedResources.get(i);
+ if (encoding.equals(resource.format.encoding)) {
+ if (quality > bestResourceQuality || i < bestResourcePreference) {
+ bestResource = resource;
+ bestResourceQuality = quality;
+ bestResourcePreference = i;
+ }
+ break;
+ }
+ }
+ }
+ }
+ }
+ return bestResource;
+ }
+
private void doDirectoryRedirect(HttpServletRequest request, HttpServletResponse response)
throws IOException {
StringBuilder location = new StringBuilder(request.getRequestURI());
@@ -1047,7 +1165,8 @@ public class DefaultServlet extends HttpServlet {
*
* @param request The servlet request we a)re processing
* @param response The servlet response we are creating
- * @return Range
+ * @return the range object
+ * @throws IOException an IO error occurred
*/
protected Range parseContentRange(HttpServletRequest request,
HttpServletResponse response)
@@ -1109,7 +1228,8 @@ public class DefaultServlet extends HttpServlet {
* @param request The servlet request we are processing
* @param response The servlet response we are creating
* @param resource The resource
- * @return Vector of ranges
+ * @return a list of ranges
+ * @throws IOException an IO error occurred
*/
protected ArrayList<Range> parseRange(HttpServletRequest request,
HttpServletResponse response,
@@ -1242,10 +1362,20 @@ public class DefaultServlet extends HttpServlet {
}
-
/**
- * Decide which way to render. HTML or XML.
+ * Decide which way to render. HTML or XML.
+ *
+ * @param contextPath The path
+ * @param resource The resource
+ *
+ * @return the input stream with the rendered output
+ *
+ * @throws IOException an IO error occurred
+ * @throws ServletException rendering error
+ *
+ * @deprecated Unused. Will be removed in Tomcat 9
*/
+ @Deprecated
protected InputStream render(String contextPath, WebResource resource)
throws IOException, ServletException {
return render(contextPath, resource, null);
@@ -1260,16 +1390,25 @@ public class DefaultServlet extends HttpServlet {
return renderHtml(contextPath, resource, encoding);
}
return renderXml(contextPath, resource, xsltSource, encoding);
-
}
+
/**
- * Return an InputStream to an HTML representation of the contents
- * of this directory.
+ * Return an InputStream to an XML representation of the contents this
+ * directory.
*
- * @param contextPath Context path to which our internal paths are
- * relative
+ * @param contextPath Context path to which our internal paths are relative
+ * @param resource The associated resource
+ * @param xsltSource The XSL stylesheet
+ *
+ * @return the XML data
+ *
+ * @throws IOException an IO error occurred
+ * @throws ServletException rendering error
+ *
+ * @deprecated Unused. Will be removed in Tomcat 9
*/
+ @Deprecated
protected InputStream renderXml(String contextPath, WebResource resource, Source xsltSource)
throws IOException, ServletException {
return renderXml(contextPath, resource, xsltSource, null);
@@ -1394,12 +1533,19 @@ public class DefaultServlet extends HttpServlet {
}
/**
- * Return an InputStream to an HTML representation of the contents
- * of this directory.
+ * Return an InputStream to an HTML representation of the contents of this
+ * directory.
+ *
+ * @param contextPath Context path to which our internal paths are relative
+ * @param resource The associated resource
*
- * @param contextPath Context path to which our internal paths are
- * relative
+ * @return the HTML data
+ *
+ * @throws IOException an IO error occurred
+ *
+ * @deprecated Unused. Will be removed in Tomcat 9
*/
+ @Deprecated
protected InputStream renderHtml(String contextPath, WebResource resource)
throws IOException {
return renderHtml(contextPath, resource, null);
@@ -1550,6 +1696,7 @@ public class DefaultServlet extends HttpServlet {
* Render the specified file size (in bytes).
*
* @param size File size (in bytes)
+ * @return the formatted size
*/
protected String renderSize(long size) {
@@ -1565,11 +1712,22 @@ public class DefaultServlet extends HttpServlet {
/**
* Get the readme file as a string.
+ * @param directory The directory to search
+ * @return the readme for the specified directory
+ *
+ * @deprecated Unused. Will be removed in Tomcat 9
*/
+ @Deprecated
protected String getReadme(WebResource directory) {
return getReadme(directory, null);
}
+ /**
+ * Get the readme file as a string.
+ * @param directory The directory to search
+ * @param encoding The readme encoding
+ * @return the readme for the specified directory
+ */
protected String getReadme(WebResource directory, String encoding) {
if (readmeFile != null) {
@@ -1609,7 +1767,10 @@ public class DefaultServlet extends HttpServlet {
/**
- * Return a Source for the xsl template (if possible)
+ * Return a Source for the xsl template (if possible).
+ * @param directory The directory to search
+ * @return the source for the specified directory
+ * @throws IOException an IO error occurred
*/
protected Source findXsltSource(WebResource directory)
throws IOException {
@@ -1739,6 +1900,14 @@ public class DefaultServlet extends HttpServlet {
/**
* Check if sendfile can be used.
+ * @param request The Servlet request
+ * @param response The Servlet response
+ * @param resource The resource
+ * @param length The length which will be written (will be used only if
+ * range is null)
+ * @param range The range that will be written
+ * @return <code>true</code> if sendfile should be used (writing is then
+ * delegated to the endpoint)
*/
protected boolean checkSendfile(HttpServletRequest request,
HttpServletResponse response,
@@ -1771,9 +1940,10 @@ public class DefaultServlet extends HttpServlet {
* @param request The servlet request we are processing
* @param response The servlet response we are creating
* @param resource The resource
- * @return boolean true if the resource meets the specified condition,
- * and false if the condition is not satisfied, in which case request
- * processing is stopped
+ * @return <code>true</code> if the resource meets the specified condition,
+ * and <code>false</code> if the condition is not satisfied, in which case
+ * request processing is stopped
+ * @throws IOException an IO error occurred
*/
protected boolean checkIfMatch(HttpServletRequest request,
HttpServletResponse response, WebResource resource)
@@ -1814,9 +1984,9 @@ public class DefaultServlet extends HttpServlet {
* @param request The servlet request we are processing
* @param response The servlet response we are creating
* @param resource The resource
- * @return boolean true if the resource meets the specified condition,
- * and false if the condition is not satisfied, in which case request
- * processing is stopped
+ * @return <code>true</code> if the resource meets the specified condition,
+ * and <code>false</code> if the condition is not satisfied, in which case
+ * request processing is stopped
*/
protected boolean checkIfModifiedSince(HttpServletRequest request,
HttpServletResponse response, WebResource resource) {
@@ -1850,9 +2020,10 @@ public class DefaultServlet extends HttpServlet {
* @param request The servlet request we are processing
* @param response The servlet response we are creating
* @param resource The resource
- * @return boolean true if the resource meets the specified condition,
- * and false if the condition is not satisfied, in which case request
- * processing is stopped
+ * @return <code>true</code> if the resource meets the specified condition,
+ * and <code>false</code> if the condition is not satisfied, in which case
+ * request processing is stopped
+ * @throws IOException an IO error occurred
*/
protected boolean checkIfNoneMatch(HttpServletRequest request,
HttpServletResponse response, WebResource resource)
@@ -1900,33 +2071,15 @@ public class DefaultServlet extends HttpServlet {
}
/**
- * Check if the user agent supports gzip encoding.
- *
- * @param request The servlet request we are processing
- * @return boolean true if the user agent supports gzip encoding,
- * and false if the user agent does not support gzip encoding
- */
- protected boolean checkIfGzip(HttpServletRequest request) {
- Enumeration<String> headers = request.getHeaders("Accept-Encoding");
- while (headers.hasMoreElements()) {
- String header = headers.nextElement();
- if (header.indexOf("gzip") != -1) {
- return true;
- }
- }
- return false;
- }
-
-
- /**
* Check if the if-unmodified-since condition is satisfied.
*
* @param request The servlet request we are processing
* @param response The servlet response we are creating
* @param resource The resource
- * @return boolean true if the resource meets the specified condition,
- * and false if the condition is not satisfied, in which case request
- * processing is stopped
+ * @return <code>true</code> if the resource meets the specified condition,
+ * and <code>false</code> if the condition is not satisfied, in which case
+ * request processing is stopped
+ * @throws IOException an IO error occurred
*/
protected boolean checkIfUnmodifiedSince(HttpServletRequest request,
HttpServletResponse response, WebResource resource)
@@ -2061,7 +2214,7 @@ public class DefaultServlet extends HttpServlet {
* @param resource The source resource
* @param ostream The output stream to write to
* @param ranges Enumeration of the ranges the client wanted to
- * retrieve
+ * retrieve
* @param contentType Content type of the resource
* @exception IOException if an input/output error occurs
*/
@@ -2244,6 +2397,26 @@ public class DefaultServlet extends HttpServlet {
}
}
+ protected static class CompressionFormat implements Serializable {
+ private static final long serialVersionUID = 1L;
+ public final String extension;
+ public final String encoding;
+
+ public CompressionFormat(String extension, String encoding) {
+ this.extension = extension;
+ this.encoding = encoding;
+ }
+ }
+
+ private static class PrecompressedResource {
+ public final WebResource resource;
+ public final CompressionFormat format;
+
+ private PrecompressedResource(WebResource resource, CompressionFormat format) {
+ this.resource = resource;
+ this.format = format;
+ }
+ }
/**
* This is secure in the sense that any attempt to use an external entity
diff --git a/java/org/apache/catalina/servlets/LocalStrings.properties b/java/org/apache/catalina/servlets/LocalStrings.properties
index 31a0db6..b92c9b0 100644
--- a/java/org/apache/catalina/servlets/LocalStrings.properties
+++ b/java/org/apache/catalina/servlets/LocalStrings.properties
@@ -44,5 +44,3 @@ directory.lastModified=Last Modified
directory.parent=Up To {0}
directory.size=Size
directory.title=Directory Listing For {0}
-directory.version=Tomcat Catalina version 4.0
-
diff --git a/java/org/apache/catalina/servlets/LocalStrings_es.properties b/java/org/apache/catalina/servlets/LocalStrings_es.properties
index 9e82f99..47fdcfd 100644
--- a/java/org/apache/catalina/servlets/LocalStrings_es.properties
+++ b/java/org/apache/catalina/servlets/LocalStrings_es.properties
@@ -21,4 +21,3 @@ directory.lastModified = \u00DAltima Modificaci\u00F3n
directory.parent = Atr\u00E1s A {0}
directory.size = Medida
directory.title = Listado de Directorio Para {0}
-directory.version = Tomcat Catalina versi\u00F3n 4.0
diff --git a/java/org/apache/catalina/servlets/LocalStrings_fr.properties b/java/org/apache/catalina/servlets/LocalStrings_fr.properties
index 85d377f..c59726c 100644
--- a/java/org/apache/catalina/servlets/LocalStrings_fr.properties
+++ b/java/org/apache/catalina/servlets/LocalStrings_fr.properties
@@ -19,5 +19,4 @@ directory.lastModified=Derni\u00e8re modification
directory.parent=Jusqu''\u00e0 {0}
directory.size=Taille
directory.title=Liste du r\u00e9pertoire pour {0}
-directory.version=Tomcat Catalina version 4.0
diff --git a/java/org/apache/catalina/servlets/LocalStrings_ja.properties b/java/org/apache/catalina/servlets/LocalStrings_ja.properties
index 5c612fb..a10e28b 100644
--- a/java/org/apache/catalina/servlets/LocalStrings_ja.properties
+++ b/java/org/apache/catalina/servlets/LocalStrings_ja.properties
@@ -19,5 +19,3 @@ directory.lastModified=\u6700\u7d42\u66f4\u65b0
directory.parent={0} \u306b\u79fb\u52d5
directory.size=\u30b5\u30a4\u30ba
directory.title={0} \u306e\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u306e\u4e00\u89a7
-directory.version=Tomcat Catalina \u30d0\u30fc\u30b8\u30e7\u30f3 4.0
-
diff --git a/java/org/apache/catalina/servlets/WebdavServlet.java b/java/org/apache/catalina/servlets/WebdavServlet.java
index 1375475..4eeeeba 100644
--- a/java/org/apache/catalina/servlets/WebdavServlet.java
+++ b/java/org/apache/catalina/servlets/WebdavServlet.java
@@ -63,7 +63,7 @@ import org.xml.sax.SAXException;
* <p>
* Mapping a subpath (e.g. <code>/webdav/*</code> to this servlet has the effect
* of re-mounting the entire web application under that sub-path, with WebDAV
- * access to all the resources. This <code>WEB-INF</code> and <code>META-INF</code>
+ * access to all the resources. The <code>WEB-INF</code> and <code>META-INF</code>
* directories are protected in this re-mounted resource tree.
* <p>
* To enable WebDAV for a context add the following to web.xml:
@@ -272,6 +272,9 @@ public class WebdavServlet
/**
* Return JAXP document builder instance.
+ * @return the document builder
+ * @throws ServletException document builder creation failed
+ * (wrapped <code>ParserConfigurationException</code> exception)
*/
protected DocumentBuilder getDocumentBuilder()
throws ServletException {
@@ -419,8 +422,8 @@ public class WebdavServlet
/**
* OPTIONS Method.
*
- * @param req The request
- * @param resp The response
+ * @param req The Servlet request
+ * @param resp The Servlet response
* @throws ServletException If an error occurs
* @throws IOException If an IO error occurs
*/
@@ -439,6 +442,10 @@ public class WebdavServlet
/**
* PROPFIND Method.
+ * @param req The Servlet request
+ * @param resp The Servlet response
+ * @throws ServletException If an error occurs
+ * @throws IOException If an IO error occurs
*/
protected void doPropfind(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
@@ -672,6 +679,9 @@ public class WebdavServlet
/**
* PROPPATCH Method.
+ * @param req The Servlet request
+ * @param resp The Servlet response
+ * @throws IOException If an IO error occurs
*/
protected void doProppatch(HttpServletRequest req, HttpServletResponse resp)
throws IOException {
@@ -693,6 +703,10 @@ public class WebdavServlet
/**
* MKCOL Method.
+ * @param req The Servlet request
+ * @param resp The Servlet response
+ * @throws ServletException If an error occurs
+ * @throws IOException If an IO error occurs
*/
protected void doMkcol(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
@@ -753,6 +767,10 @@ public class WebdavServlet
/**
* DELETE Method.
+ * @param req The Servlet request
+ * @param resp The Servlet response
+ * @throws ServletException If an error occurs
+ * @throws IOException If an IO error occurs
*/
@Override
protected void doDelete(HttpServletRequest req, HttpServletResponse resp)
@@ -802,6 +820,9 @@ public class WebdavServlet
/**
* COPY Method.
+ * @param req The Servlet request
+ * @param resp The Servlet response
+ * @throws IOException If an IO error occurs
*/
protected void doCopy(HttpServletRequest req, HttpServletResponse resp)
throws IOException {
@@ -818,6 +839,9 @@ public class WebdavServlet
/**
* MOVE Method.
+ * @param req The Servlet request
+ * @param resp The Servlet response
+ * @throws IOException If an IO error occurs
*/
protected void doMove(HttpServletRequest req, HttpServletResponse resp)
throws IOException {
@@ -843,6 +867,10 @@ public class WebdavServlet
/**
* LOCK Method.
+ * @param req The Servlet request
+ * @param resp The Servlet response
+ * @throws ServletException If an error occurs
+ * @throws IOException If an IO error occurs
*/
protected void doLock(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
@@ -1304,6 +1332,9 @@ public class WebdavServlet
/**
* UNLOCK Method.
+ * @param req The Servlet request
+ * @param resp The Servlet response
+ * @throws IOException If an IO error occurs
*/
protected void doUnlock(HttpServletRequest req, HttpServletResponse resp)
throws IOException {
@@ -1385,9 +1416,9 @@ public class WebdavServlet
* has give the appropriate lock tokens.
*
* @param req Servlet request
- * @return boolean true if the resource is locked (and no appropriate
- * lock token has been found for at least one of the non-shared locks which
- * are present on the resource).
+ * @return <code>true</code> if the resource is locked (and no appropriate
+ * lock token has been found for at least one of
+ * the non-shared locks which are present on the resource).
*/
private boolean isLocked(HttpServletRequest req) {
@@ -1411,9 +1442,9 @@ public class WebdavServlet
*
* @param path Path of the resource
* @param ifHeader "If" HTTP header which was included in the request
- * @return boolean true if the resource is locked (and no appropriate
- * lock token has been found for at least one of the non-shared locks which
- * are present on the resource).
+ * @return <code>true</code> if the resource is locked (and no appropriate
+ * lock token has been found for at least one of
+ * the non-shared locks which are present on the resource).
*/
private boolean isLocked(String path, String ifHeader) {
@@ -1476,6 +1507,7 @@ public class WebdavServlet
* @param req Servlet request
* @param resp Servlet response
* @return boolean true if the copy is successful
+ * @throws IOException If an IO error occurs
*/
private boolean copyResource(HttpServletRequest req,
HttpServletResponse resp)
@@ -1632,6 +1664,7 @@ public class WebdavServlet
* during the copy operation
* @param source Path of the resource to be copied
* @param dest Destination path
+ * @return <code>true</code> if the copy was successful
*/
private boolean copyResource(Hashtable<String,Integer> errorList,
String source, String dest) {
@@ -1701,7 +1734,8 @@ public class WebdavServlet
*
* @param req Servlet request
* @param resp Servlet response
- * @return boolean true if the copy is successful
+ * @return <code>true</code> if the delete is successful
+ * @throws IOException If an IO error occurs
*/
private boolean deleteResource(HttpServletRequest req,
HttpServletResponse resp)
@@ -1722,6 +1756,8 @@ public class WebdavServlet
* @param resp Servlet response
* @param setStatus Should the response status be set on successful
* completion
+ * @return <code>true</code> if the delete is successful
+ * @throws IOException If an IO error occurs
*/
private boolean deleteResource(String path, HttpServletRequest req,
HttpServletResponse resp, boolean setStatus)
@@ -1776,7 +1812,7 @@ public class WebdavServlet
/**
* Deletes a collection.
- *
+ * @param req The Servlet request
* @param path Path to the collection to be deleted
* @param errorList Contains the list of the errors which occurred
*/
@@ -1839,6 +1875,7 @@ public class WebdavServlet
* @param req Servlet request
* @param resp Servlet response
* @param errorList List of error to be displayed
+ * @throws IOException If an IO error occurs
*/
private void sendReport(HttpServletRequest req, HttpServletResponse resp,
Hashtable<String,Integer> errorList)
@@ -1914,13 +1951,6 @@ public class WebdavServlet
return;
}
- generatedXML.writeElement("D", "response", XMLWriter.OPENING);
- String status = "HTTP/1.1 " + WebdavStatus.SC_OK + " " +
- WebdavStatus.getStatusText(WebdavStatus.SC_OK);
-
- // Generating href element
- generatedXML.writeElement("D", "href", XMLWriter.OPENING);
-
String href = req.getContextPath() + req.getServletPath();
if ((href.endsWith("/")) && (path.startsWith("/")))
href += path.substring(1);
@@ -1929,245 +1959,12 @@ public class WebdavServlet
if (resource.isDirectory() && (!href.endsWith("/")))
href += "/";
- generatedXML.writeText(rewriteUrl(href));
-
- generatedXML.writeElement("D", "href", XMLWriter.CLOSING);
-
- String resourceName = path;
- int lastSlash = path.lastIndexOf('/');
- if (lastSlash != -1)
- resourceName = resourceName.substring(lastSlash + 1);
-
- switch (type) {
-
- case FIND_ALL_PROP :
-
- generatedXML.writeElement("D", "propstat", XMLWriter.OPENING);
- generatedXML.writeElement("D", "prop", XMLWriter.OPENING);
-
- generatedXML.writeProperty("D", "creationdate",
- getISOCreationDate(resource.getCreation()));
- generatedXML.writeElement("D", "displayname", XMLWriter.OPENING);
- generatedXML.writeData(resourceName);
- generatedXML.writeElement("D", "displayname", XMLWriter.CLOSING);
- if (resource.isFile()) {
- generatedXML.writeProperty
- ("D", "getlastmodified", FastHttpDateFormat.formatDate
- (resource.getLastModified(), null));
- generatedXML.writeProperty
- ("D", "getcontentlength",
- String.valueOf(resource.getContentLength()));
- String contentType = getServletContext().getMimeType(
- resource.getName());
- if (contentType != null) {
- generatedXML.writeProperty("D", "getcontenttype",
- contentType);
- }
- generatedXML.writeProperty("D", "getetag",resource.getETag());
- generatedXML.writeElement("D", "resourcetype",
- XMLWriter.NO_CONTENT);
- } else {
- generatedXML.writeElement("D", "resourcetype",
- XMLWriter.OPENING);
- generatedXML.writeElement("D", "collection",
- XMLWriter.NO_CONTENT);
- generatedXML.writeElement("D", "resourcetype",
- XMLWriter.CLOSING);
- }
-
- generatedXML.writeProperty("D", "source", "");
-
- String supportedLocks = "<D:lockentry>"
- + "<D:lockscope><D:exclusive/></D:lockscope>"
- + "<D:locktype><D:write/></D:locktype>"
- + "</D:lockentry>" + "<D:lockentry>"
- + "<D:lockscope><D:shared/></D:lockscope>"
- + "<D:locktype><D:write/></D:locktype>"
- + "</D:lockentry>";
- generatedXML.writeElement("D", "supportedlock", XMLWriter.OPENING);
- generatedXML.writeText(supportedLocks);
- generatedXML.writeElement("D", "supportedlock", XMLWriter.CLOSING);
-
- generateLockDiscovery(path, generatedXML);
-
- generatedXML.writeElement("D", "prop", XMLWriter.CLOSING);
- generatedXML.writeElement("D", "status", XMLWriter.OPENING);
- generatedXML.writeText(status);
- generatedXML.writeElement("D", "status", XMLWriter.CLOSING);
- generatedXML.writeElement("D", "propstat", XMLWriter.CLOSING);
-
- break;
-
- case FIND_PROPERTY_NAMES :
-
- generatedXML.writeElement("D", "propstat", XMLWriter.OPENING);
- generatedXML.writeElement("D", "prop", XMLWriter.OPENING);
-
- generatedXML.writeElement("D", "creationdate",
- XMLWriter.NO_CONTENT);
- generatedXML.writeElement("D", "displayname", XMLWriter.NO_CONTENT);
- if (resource.isFile()) {
- generatedXML.writeElement("D", "getcontentlanguage",
- XMLWriter.NO_CONTENT);
- generatedXML.writeElement("D", "getcontentlength",
- XMLWriter.NO_CONTENT);
- generatedXML.writeElement("D", "getcontenttype",
- XMLWriter.NO_CONTENT);
- generatedXML.writeElement("D", "getetag", XMLWriter.NO_CONTENT);
- generatedXML.writeElement("D", "getlastmodified",
- XMLWriter.NO_CONTENT);
- }
- generatedXML.writeElement("D", "resourcetype",
- XMLWriter.NO_CONTENT);
- generatedXML.writeElement("D", "source", XMLWriter.NO_CONTENT);
- generatedXML.writeElement("D", "lockdiscovery",
- XMLWriter.NO_CONTENT);
-
- generatedXML.writeElement("D", "prop", XMLWriter.CLOSING);
- generatedXML.writeElement("D", "status", XMLWriter.OPENING);
- generatedXML.writeText(status);
- generatedXML.writeElement("D", "status", XMLWriter.CLOSING);
- generatedXML.writeElement("D", "propstat", XMLWriter.CLOSING);
-
- break;
-
- case FIND_BY_PROPERTY :
-
- Vector<String> propertiesNotFound = new Vector<>();
-
- // Parse the list of properties
-
- generatedXML.writeElement("D", "propstat", XMLWriter.OPENING);
- generatedXML.writeElement("D", "prop", XMLWriter.OPENING);
-
- Enumeration<String> properties = propertiesVector.elements();
-
- while (properties.hasMoreElements()) {
-
- String property = properties.nextElement();
-
- if (property.equals("creationdate")) {
- generatedXML.writeProperty
- ("D", "creationdate",
- getISOCreationDate(resource.getCreation()));
- } else if (property.equals("displayname")) {
- generatedXML.writeElement
- ("D", "displayname", XMLWriter.OPENING);
- generatedXML.writeData(resourceName);
- generatedXML.writeElement
- ("D", "displayname", XMLWriter.CLOSING);
- } else if (property.equals("getcontentlanguage")) {
- if (resource.isDirectory()) {
- propertiesNotFound.addElement(property);
- } else {
- generatedXML.writeElement("D", "getcontentlanguage",
- XMLWriter.NO_CONTENT);
- }
- } else if (property.equals("getcontentlength")) {
- if (resource.isDirectory()) {
- propertiesNotFound.addElement(property);
- } else {
- generatedXML.writeProperty
- ("D", "getcontentlength",
- (String.valueOf(resource.getContentLength())));
- }
- } else if (property.equals("getcontenttype")) {
- if (resource.isDirectory()) {
- propertiesNotFound.addElement(property);
- } else {
- generatedXML.writeProperty
- ("D", "getcontenttype",
- getServletContext().getMimeType
- (resource.getName()));
- }
- } else if (property.equals("getetag")) {
- if (resource.isDirectory()) {
- propertiesNotFound.addElement(property);
- } else {
- generatedXML.writeProperty
- ("D", "getetag", resource.getETag());
- }
- } else if (property.equals("getlastmodified")) {
- if (resource.isDirectory()) {
- propertiesNotFound.addElement(property);
- } else {
- generatedXML.writeProperty
- ("D", "getlastmodified", FastHttpDateFormat.formatDate
- (resource.getLastModified(), null));
- }
- } else if (property.equals("resourcetype")) {
- if (resource.isDirectory()) {
- generatedXML.writeElement("D", "resourcetype",
- XMLWriter.OPENING);
- generatedXML.writeElement("D", "collection",
- XMLWriter.NO_CONTENT);
- generatedXML.writeElement("D", "resourcetype",
- XMLWriter.CLOSING);
- } else {
- generatedXML.writeElement("D", "resourcetype",
- XMLWriter.NO_CONTENT);
- }
- } else if (property.equals("source")) {
- generatedXML.writeProperty("D", "source", "");
- } else if (property.equals("supportedlock")) {
- supportedLocks = "<D:lockentry>"
- + "<D:lockscope><D:exclusive/></D:lockscope>"
- + "<D:locktype><D:write/></D:locktype>"
- + "</D:lockentry>" + "<D:lockentry>"
- + "<D:lockscope><D:shared/></D:lockscope>"
- + "<D:locktype><D:write/></D:locktype>"
- + "</D:lockentry>";
- generatedXML.writeElement("D", "supportedlock",
- XMLWriter.OPENING);
- generatedXML.writeText(supportedLocks);
- generatedXML.writeElement("D", "supportedlock",
- XMLWriter.CLOSING);
- } else if (property.equals("lockdiscovery")) {
- if (!generateLockDiscovery(path, generatedXML))
- propertiesNotFound.addElement(property);
- } else {
- propertiesNotFound.addElement(property);
- }
-
- }
-
- generatedXML.writeElement("D", "prop", XMLWriter.CLOSING);
- generatedXML.writeElement("D", "status", XMLWriter.OPENING);
- generatedXML.writeText(status);
- generatedXML.writeElement("D", "status", XMLWriter.CLOSING);
- generatedXML.writeElement("D", "propstat", XMLWriter.CLOSING);
-
- Enumeration<String> propertiesNotFoundList =
- propertiesNotFound.elements();
-
- if (propertiesNotFoundList.hasMoreElements()) {
-
- status = "HTTP/1.1 " + WebdavStatus.SC_NOT_FOUND + " " +
- WebdavStatus.getStatusText(WebdavStatus.SC_NOT_FOUND);
-
- generatedXML.writeElement("D", "propstat", XMLWriter.OPENING);
- generatedXML.writeElement("D", "prop", XMLWriter.OPENING);
-
- while (propertiesNotFoundList.hasMoreElements()) {
- generatedXML.writeElement
- ("D", propertiesNotFoundList.nextElement(),
- XMLWriter.NO_CONTENT);
- }
-
- generatedXML.writeElement("D", "prop", XMLWriter.CLOSING);
- generatedXML.writeElement("D", "status", XMLWriter.OPENING);
- generatedXML.writeText(status);
- generatedXML.writeElement("D", "status", XMLWriter.CLOSING);
- generatedXML.writeElement("D", "propstat", XMLWriter.CLOSING);
-
- }
-
- break;
-
- }
-
- generatedXML.writeElement("D", "response", XMLWriter.CLOSING);
+ String rewrittenUrl = rewriteUrl(href);
+ generatePropFindResponse(generatedXML, rewrittenUrl, path, type, propertiesVector,
+ resource.isFile(), false, resource.getCreation(), resource.getLastModified(),
+ resource.getContentLength(), getServletContext().getMimeType(resource.getName()),
+ resource.getETag());
}
@@ -2196,22 +1993,33 @@ public class WebdavServlet
if (lock == null)
return;
- generatedXML.writeElement("D", "response", XMLWriter.OPENING);
- String status = "HTTP/1.1 " + WebdavStatus.SC_OK + " " +
- WebdavStatus.getStatusText(WebdavStatus.SC_OK);
-
- // Generating href element
- generatedXML.writeElement("D", "href", XMLWriter.OPENING);
-
String absoluteUri = req.getRequestURI();
String relativePath = getRelativePath(req);
String toAppend = path.substring(relativePath.length());
if (!toAppend.startsWith("/"))
toAppend = "/" + toAppend;
- generatedXML.writeText(rewriteUrl(RequestUtil.normalize(
- absoluteUri + toAppend)));
+ String rewrittenUrl = rewriteUrl(RequestUtil.normalize(
+ absoluteUri + toAppend));
+
+ generatePropFindResponse(generatedXML, rewrittenUrl, path, type, propertiesVector,
+ true, true, lock.creationDate.getTime(), lock.creationDate.getTime(),
+ 0, "", "");
+ }
+
+
+ private void generatePropFindResponse(XMLWriter generatedXML, String rewrittenUrl,
+ String path, int propFindType, Vector<String> propertiesVector, boolean isFile,
+ boolean isLockNull, long created, long lastModified, long contentLength,
+ String contentType, String eTag) {
+ generatedXML.writeElement("D", "response", XMLWriter.OPENING);
+ String status = "HTTP/1.1 " + WebdavStatus.SC_OK + " " +
+ WebdavStatus.getStatusText(WebdavStatus.SC_OK);
+
+ // Generating href element
+ generatedXML.writeElement("D", "href", XMLWriter.OPENING);
+ generatedXML.writeText(rewrittenUrl);
generatedXML.writeElement("D", "href", XMLWriter.CLOSING);
String resourceName = path;
@@ -2219,28 +2027,37 @@ public class WebdavServlet
if (lastSlash != -1)
resourceName = resourceName.substring(lastSlash + 1);
- switch (type) {
+ switch (propFindType) {
case FIND_ALL_PROP :
generatedXML.writeElement("D", "propstat", XMLWriter.OPENING);
generatedXML.writeElement("D", "prop", XMLWriter.OPENING);
- generatedXML.writeProperty("D", "creationdate",
- getISOCreationDate(lock.creationDate.getTime()));
+ generatedXML.writeProperty("D", "creationdate", getISOCreationDate(created));
generatedXML.writeElement("D", "displayname", XMLWriter.OPENING);
generatedXML.writeData(resourceName);
generatedXML.writeElement("D", "displayname", XMLWriter.CLOSING);
- generatedXML.writeProperty("D", "getlastmodified",
- FastHttpDateFormat.formatDate
- (lock.creationDate.getTime(), null));
- generatedXML.writeProperty("D", "getcontentlength",
- String.valueOf(0));
- generatedXML.writeProperty("D", "getcontenttype", "");
- generatedXML.writeProperty("D", "getetag", "");
- generatedXML.writeElement("D", "resourcetype", XMLWriter.OPENING);
- generatedXML.writeElement("D", "lock-null", XMLWriter.NO_CONTENT);
- generatedXML.writeElement("D", "resourcetype", XMLWriter.CLOSING);
+ if (isFile) {
+ generatedXML.writeProperty("D", "getlastmodified",
+ FastHttpDateFormat.formatDate(lastModified, null));
+ generatedXML.writeProperty("D", "getcontentlength", Long.toString(contentLength));
+ if (contentType != null) {
+ generatedXML.writeProperty("D", "getcontenttype", contentType);
+ }
+ generatedXML.writeProperty("D", "getetag", eTag);
+ if (isLockNull) {
+ generatedXML.writeElement("D", "resourcetype", XMLWriter.OPENING);
+ generatedXML.writeElement("D", "lock-null", XMLWriter.NO_CONTENT);
+ generatedXML.writeElement("D", "resourcetype", XMLWriter.CLOSING);
+ } else {
+ generatedXML.writeElement("D", "resourcetype", XMLWriter.NO_CONTENT);
+ }
+ } else {
+ generatedXML.writeElement("D", "resourcetype", XMLWriter.OPENING);
+ generatedXML.writeElement("D", "collection", XMLWriter.NO_CONTENT);
+ generatedXML.writeElement("D", "resourcetype", XMLWriter.CLOSING);
+ }
generatedXML.writeProperty("D", "source", "");
@@ -2270,23 +2087,18 @@ public class WebdavServlet
generatedXML.writeElement("D", "propstat", XMLWriter.OPENING);
generatedXML.writeElement("D", "prop", XMLWriter.OPENING);
- generatedXML.writeElement("D", "creationdate",
- XMLWriter.NO_CONTENT);
+ generatedXML.writeElement("D", "creationdate", XMLWriter.NO_CONTENT);
generatedXML.writeElement("D", "displayname", XMLWriter.NO_CONTENT);
- generatedXML.writeElement("D", "getcontentlanguage",
- XMLWriter.NO_CONTENT);
- generatedXML.writeElement("D", "getcontentlength",
- XMLWriter.NO_CONTENT);
- generatedXML.writeElement("D", "getcontenttype",
- XMLWriter.NO_CONTENT);
- generatedXML.writeElement("D", "getetag", XMLWriter.NO_CONTENT);
- generatedXML.writeElement("D", "getlastmodified",
- XMLWriter.NO_CONTENT);
- generatedXML.writeElement("D", "resourcetype",
- XMLWriter.NO_CONTENT);
+ if (isFile) {
+ generatedXML.writeElement("D", "getcontentlanguage", XMLWriter.NO_CONTENT);
+ generatedXML.writeElement("D", "getcontentlength", XMLWriter.NO_CONTENT);
+ generatedXML.writeElement("D", "getcontenttype", XMLWriter.NO_CONTENT);
+ generatedXML.writeElement("D", "getetag", XMLWriter.NO_CONTENT);
+ generatedXML.writeElement("D", "getlastmodified", XMLWriter.NO_CONTENT);
+ }
+ generatedXML.writeElement("D", "resourcetype", XMLWriter.NO_CONTENT);
generatedXML.writeElement("D", "source", XMLWriter.NO_CONTENT);
- generatedXML.writeElement("D", "lockdiscovery",
- XMLWriter.NO_CONTENT);
+ generatedXML.writeElement("D", "lockdiscovery", XMLWriter.NO_CONTENT);
generatedXML.writeElement("D", "prop", XMLWriter.CLOSING);
generatedXML.writeElement("D", "status", XMLWriter.OPENING);
@@ -2312,36 +2124,58 @@ public class WebdavServlet
String property = properties.nextElement();
if (property.equals("creationdate")) {
- generatedXML.writeProperty("D", "creationdate",
- getISOCreationDate(lock.creationDate.getTime()));
+ generatedXML.writeProperty ("D", "creationdate", getISOCreationDate(created));
} else if (property.equals("displayname")) {
- generatedXML.writeElement("D", "displayname",
- XMLWriter.OPENING);
+ generatedXML.writeElement("D", "displayname", XMLWriter.OPENING);
generatedXML.writeData(resourceName);
- generatedXML.writeElement("D", "displayname",
- XMLWriter.CLOSING);
+ generatedXML.writeElement("D", "displayname", XMLWriter.CLOSING);
} else if (property.equals("getcontentlanguage")) {
- generatedXML.writeElement("D", "getcontentlanguage",
- XMLWriter.NO_CONTENT);
+ if (isFile) {
+ generatedXML.writeElement("D", "getcontentlanguage",
+ XMLWriter.NO_CONTENT);
+ } else {
+ propertiesNotFound.addElement(property);
+ }
} else if (property.equals("getcontentlength")) {
- generatedXML.writeProperty("D", "getcontentlength",
- (String.valueOf(0)));
+ if (isFile) {
+ generatedXML.writeProperty("D", "getcontentlength",
+ Long.toString(contentLength));
+ } else {
+ propertiesNotFound.addElement(property);
+ }
} else if (property.equals("getcontenttype")) {
- generatedXML.writeProperty("D", "getcontenttype", "");
+ if (isFile) {
+ generatedXML.writeProperty("D", "getcontenttype", contentType);
+ } else {
+ propertiesNotFound.addElement(property);
+ }
} else if (property.equals("getetag")) {
- generatedXML.writeProperty("D", "getetag", "");
+ if (isFile) {
+ generatedXML.writeProperty("D", "getetag", eTag);
+ } else {
+ propertiesNotFound.addElement(property);
+ }
} else if (property.equals("getlastmodified")) {
- generatedXML.writeProperty
- ("D", "getlastmodified",
- FastHttpDateFormat.formatDate
- (lock.creationDate.getTime(), null));
+ if (isFile) {
+ generatedXML.writeProperty("D", "getlastmodified",
+ FastHttpDateFormat.formatDate(lastModified, null));
+ } else {
+ propertiesNotFound.addElement(property);
+ }
} else if (property.equals("resourcetype")) {
- generatedXML.writeElement("D", "resourcetype",
- XMLWriter.OPENING);
- generatedXML.writeElement("D", "lock-null",
- XMLWriter.NO_CONTENT);
- generatedXML.writeElement("D", "resourcetype",
- XMLWriter.CLOSING);
+ if (isFile) {
+ if(isLockNull) {
+ generatedXML.writeElement("D", "resourcetype", XMLWriter.OPENING);
+ generatedXML.writeElement("D", "lock-null", XMLWriter.NO_CONTENT);
+ generatedXML.writeElement("D", "resourcetype", XMLWriter.CLOSING);
+ } else {
+ generatedXML.writeElement("D", "resourcetype", XMLWriter.NO_CONTENT);
+ }
+ } else {
+ generatedXML.writeElement("D", "resourcetype", XMLWriter.OPENING);
+ generatedXML.writeElement("D", "collection", XMLWriter.NO_CONTENT);
+ generatedXML.writeElement("D", "resourcetype",XMLWriter.CLOSING);
+ }
} else if (property.equals("source")) {
generatedXML.writeProperty("D", "source", "");
} else if (property.equals("supportedlock")) {
@@ -2352,11 +2186,9 @@ public class WebdavServlet
+ "<D:lockscope><D:shared/></D:lockscope>"
+ "<D:locktype><D:write/></D:locktype>"
+ "</D:lockentry>";
- generatedXML.writeElement("D", "supportedlock",
- XMLWriter.OPENING);
+ generatedXML.writeElement("D", "supportedlock", XMLWriter.OPENING);
generatedXML.writeText(supportedLocks);
- generatedXML.writeElement("D", "supportedlock",
- XMLWriter.CLOSING);
+ generatedXML.writeElement("D", "supportedlock", XMLWriter.CLOSING);
} else if (property.equals("lockdiscovery")) {
if (!generateLockDiscovery(path, generatedXML))
propertiesNotFound.addElement(property);
@@ -2383,9 +2215,8 @@ public class WebdavServlet
generatedXML.writeElement("D", "prop", XMLWriter.OPENING);
while (propertiesNotFoundList.hasMoreElements()) {
- generatedXML.writeElement
- ("D", propertiesNotFoundList.nextElement(),
- XMLWriter.NO_CONTENT);
+ generatedXML.writeElement("D", propertiesNotFoundList.nextElement(),
+ XMLWriter.NO_CONTENT);
}
generatedXML.writeElement("D", "prop", XMLWriter.CLOSING);
@@ -2401,7 +2232,6 @@ public class WebdavServlet
}
generatedXML.writeElement("D", "response", XMLWriter.CLOSING);
-
}
@@ -2410,7 +2240,7 @@ public class WebdavServlet
*
* @param path Path
* @param generatedXML XML data to which the locks info will be appended
- * @return true if at least one lock was displayed
+ * @return <code>true</code> if at least one lock was displayed
*/
private boolean generateLockDiscovery
(String path, XMLWriter generatedXML) {
@@ -2451,6 +2281,7 @@ public class WebdavServlet
/**
* Get creation date in ISO format.
+ * @return the formatted creation date
*/
private String getISOCreationDate(long creationDate) {
return creationDateFormat.format(new Date(creationDate));
@@ -2458,7 +2289,8 @@ public class WebdavServlet
/**
* Determines the methods normally allowed for the resource.
- *
+ * @param req The Servlet request
+ * @return a string builder with the allowed HTTP methods
*/
private StringBuilder determineMethodsAllowed(HttpServletRequest req) {
@@ -2494,17 +2326,6 @@ public class WebdavServlet
private class LockInfo {
- // -------------------------------------------------------- Constructor
-
-
- /**
- * Constructor.
- */
- public LockInfo() {
- // Ignore
- }
-
-
// ------------------------------------------------- Instance Variables
@@ -2548,26 +2369,26 @@ public class WebdavServlet
/**
- * Return true if the lock has expired.
+ * @return true if the lock has expired.
*/
public boolean hasExpired() {
- return (System.currentTimeMillis() > expiresAt);
+ return System.currentTimeMillis() > expiresAt;
}
/**
- * Return true if the lock is exclusive.
+ * @return true if the lock is exclusive.
*/
public boolean isExclusive() {
-
- return (scope.equals("exclusive"));
-
+ return scope.equals("exclusive");
}
/**
- * Get an XML representation of this lock token. This method will
- * append an XML fragment to the given XML writer.
+ * Get an XML representation of this lock token.
+ *
+ * @param generatedXML The XML write to which the fragment will be
+ * appended
*/
public void toXML(XMLWriter generatedXML) {
diff --git a/java/org/apache/catalina/session/LocalStrings.properties b/java/org/apache/catalina/session/LocalStrings.properties
index 8f8c83d..4839e60 100644
--- a/java/org/apache/catalina/session/LocalStrings.properties
+++ b/java/org/apache/catalina/session/LocalStrings.properties
@@ -37,7 +37,6 @@ managerBase.sessionAttributeNameFilter=Skipped session attribute named [{0}] bec
managerBase.sessionAttributeValueClassNameFilter=Skipped session attribute named [{0}] because the value type [{1}] did not match the filter [{2}]
managerBase.sessionTimeout=Invalid session timeout setting {0}
managerBase.setContextNotNew=It is illegal to call setContext() to change the Context associated with a Manager if the Manager is not in the NEW state
-managerBase.setMaxInactiveIntervalUnused=Manager.setMaxInactiveInterval() is deprecated and calls to this method are ignored. Session timeouts should be configured in web.xml or via Context.setSessionTimeout(int timeoutInMinutes)
standardManager.loading=Loading persisted sessions from {0}
standardManager.loading.exception=Exception while loading persisted sessions
standardManager.unloading=Saving persisted sessions to {0}
diff --git a/java/org/apache/catalina/session/ManagerBase.java b/java/org/apache/catalina/session/ManagerBase.java
index 5291915..5b135a9 100644
--- a/java/org/apache/catalina/session/ManagerBase.java
+++ b/java/org/apache/catalina/session/ManagerBase.java
@@ -72,45 +72,12 @@ public abstract class ManagerBase extends LifecycleMBeanBase implements Manager
/**
- * The distributable flag for Sessions created by this Manager. If this
- * flag is set to <code>true</code>, any user attributes added to a
- * session controlled by this Manager must be Serializable.
- *
- * @deprecated Ignored. {@link Context#getDistributable()} always takes
- * precedence. Will be removed in Tomcat 8.5.x.
- */
- @Deprecated
- protected boolean distributable;
-
-
- /**
* The descriptive name of this Manager implementation (for logging).
*/
private static final String name = "ManagerBase";
/**
- * The default maximum inactive interval for Sessions created by
- * this Manager.
- *
- * @deprecated Ignored. {@link Context#getSessionTimeout()} always takes
- * precedence. Will be removed in Tomcat 8.5.x.
- */
- @Deprecated
- protected int maxInactiveInterval = 30 * 60;
-
-
- protected static final int SESSION_ID_LENGTH_UNSET = -1;
-
- /**
- * The session id length of Sessions created by this Manager.
- * The length should be set directly on the SessionIdGenerator.
- * Setting it here is deprecated.
- */
- protected int sessionIdLength = SESSION_ID_LENGTH_UNSET;
-
-
- /**
* The Java class name of the secure random number generator class to be
* used when generating session identifiers. The random number generator
* class must be self-seeding and have a zero-argument constructor. If not
@@ -260,7 +227,20 @@ public abstract class ManagerBase extends LifecycleMBeanBase implements Manager
}
- public void setSessionAttributeNameFilter(String sessionAttributeNameFilter) {
+ /**
+ * Set the regular expression to use to filter session attributes based on
+ * attribute name. The regular expression is anchored so it must match the
+ * entire name.
+ *
+ * @param sessionAttributeNameFilter The regular expression to use to filter
+ * session attributes based on attribute name. Use {@code null} if no
+ * filtering is required. If an empty string is specified then no
+ * names will match the filter and all attributes will be blocked.
+ *
+ * @throws PatternSyntaxException If the expression is not valid
+ */
+ public void setSessionAttributeNameFilter(String sessionAttributeNameFilter)
+ throws PatternSyntaxException {
if (sessionAttributeNameFilter == null || sessionAttributeNameFilter.length() == 0) {
sessionAttributeNamePattern = null;
} else {
@@ -269,6 +249,13 @@ public abstract class ManagerBase extends LifecycleMBeanBase implements Manager
}
+ /**
+ * Provides {@link #getSessionAttributeNameFilter()} as a pre-compiled
+ * regular expression pattern.
+ *
+ * @return The pre-compiled pattern used to filter session attributes based
+ * on attribute name. {@code null} means no filter is applied.
+ */
protected Pattern getSessionAttributeNamePattern() {
return sessionAttributeNamePattern;
}
@@ -356,25 +343,6 @@ public abstract class ManagerBase extends LifecycleMBeanBase implements Manager
@Override
- @Deprecated
- public Container getContainer() {
- return getContext();
- }
-
-
- @Override
- @Deprecated
- public void setContainer(Container container) {
-
- if (container instanceof Context || container == null) {
- setContext((Context) container);
- } else {
- log.warn(sm.getString("managerBase.container.noop"));
- }
- }
-
-
- @Override
public Context getContext() {
return context;
}
@@ -391,7 +359,6 @@ public abstract class ManagerBase extends LifecycleMBeanBase implements Manager
}
Context oldContext = this.context;
this.context = context;
- // TODO - delete the line below in Tomcat 9 onwards
support.firePropertyChange("context", oldContext, this.context);
}
@@ -404,87 +371,6 @@ public abstract class ManagerBase extends LifecycleMBeanBase implements Manager
}
- @Deprecated
- @Override
- public boolean getDistributable() {
- Context context = getContext();
- if (context == null) {
- return false;
- }
- return context.getDistributable();
- }
-
-
- @Deprecated
- @Override
- public void setDistributable(boolean distributable) {
- // NO-OP
- }
-
-
- @Deprecated
- @Override
- public int getMaxInactiveInterval() {
- Context context = getContext();
- if (context == null) {
- return -1;
- }
- return context.getSessionTimeout() * 60;
- }
-
-
- @Deprecated
- @Override
- public void setMaxInactiveInterval(int interval) {
- log.warn(sm.getString("managerBase.setMaxInactiveIntervalUnused"));
- }
-
-
- /**
- * Gets the session id length (in bytes) of Sessions created by
- * this Manager.
- *
- * @deprecated Use {@link SessionIdGenerator#getSessionIdLength()}.
- * This method will be removed in Tomcat 9 onwards.
- *
- * @return The session id length
- */
- @Override
- @Deprecated
- public int getSessionIdLength() {
-
- return (this.sessionIdLength);
-
- }
-
-
- /**
- * Sets the session id length (in bytes) for Sessions created by this
- * Manager.
- *
- * @deprecated Use {@link SessionIdGenerator#setSessionIdLength(int)}.
- * This method will be removed in Tomcat 9 onwards.
- *
- * @param idLength The session id length
- */
- @Override
- @Deprecated
- public void setSessionIdLength(int idLength) {
-
- int oldSessionIdLength = this.sessionIdLength;
- this.sessionIdLength = idLength;
- support.firePropertyChange("sessionIdLength",
- Integer.valueOf(oldSessionIdLength),
- Integer.valueOf(this.sessionIdLength));
-
- }
-
-
- /**
- * Gets the session id generator.
- *
- * @return The session id generator
- */
@Override
public SessionIdGenerator getSessionIdGenerator() {
if (sessionIdGenerator != null) {
@@ -704,9 +590,6 @@ public abstract class ManagerBase extends LifecycleMBeanBase implements Manager
setSessionIdGenerator(sessionIdGenerator);
}
- if (sessionIdLength != SESSION_ID_LENGTH_UNSET) {
- sessionIdGenerator.setSessionIdLength(sessionIdLength);
- }
sessionIdGenerator.setJvmRoute(getJvmRoute());
if (sessionIdGenerator instanceof SessionIdGeneratorBase) {
SessionIdGeneratorBase sig = (SessionIdGeneratorBase)sessionIdGenerator;
@@ -922,6 +805,7 @@ public abstract class ManagerBase extends LifecycleMBeanBase implements Manager
/**
* Get new session class to be used in the doLoad() method.
+ * @return a new session for use with this manager
*/
protected StandardSession getNewSession() {
return new StandardSession(this);
@@ -930,6 +814,7 @@ public abstract class ManagerBase extends LifecycleMBeanBase implements Manager
/**
* Generate and return a new session identifier.
+ * @return a new session id
*/
protected String generateSessionId() {
@@ -1131,37 +1016,13 @@ public abstract class ManagerBase extends LifecycleMBeanBase implements Manager
*/
@Override
public int getSessionCreateRate() {
- long now = System.currentTimeMillis();
// Copy current stats
List<SessionTiming> copy = new ArrayList<>();
synchronized (sessionCreationTiming) {
copy.addAll(sessionCreationTiming);
}
- // Init
- long oldest = now;
- int counter = 0;
- int result = 0;
- Iterator<SessionTiming> iter = copy.iterator();
-
- // Calculate rate
- while (iter.hasNext()) {
- SessionTiming timing = iter.next();
- if (timing != null) {
- counter++;
- if (timing.getTimestamp() < oldest) {
- oldest = timing.getTimestamp();
- }
- }
- }
- if (counter > 0) {
- if (oldest < now) {
- result = (1000*60*counter)/(int) (now - oldest);
- } else {
- result = Integer.MAX_VALUE;
- }
- }
- return result;
+ return calculateRate(copy);
}
@@ -1175,18 +1036,23 @@ public abstract class ManagerBase extends LifecycleMBeanBase implements Manager
*/
@Override
public int getSessionExpireRate() {
- long now = System.currentTimeMillis();
// Copy current stats
List<SessionTiming> copy = new ArrayList<>();
synchronized (sessionExpirationTiming) {
copy.addAll(sessionExpirationTiming);
}
+ return calculateRate(copy);
+ }
+
+
+ private static int calculateRate(List<SessionTiming> sessionTiming) {
// Init
+ long now = System.currentTimeMillis();
long oldest = now;
int counter = 0;
int result = 0;
- Iterator<SessionTiming> iter = copy.iterator();
+ Iterator<SessionTiming> iter = sessionTiming.iterator();
// Calculate rate
while (iter.hasNext()) {
diff --git a/java/org/apache/catalina/session/PersistentManagerBase.java b/java/org/apache/catalina/session/PersistentManagerBase.java
index d061e07..db7d687 100644
--- a/java/org/apache/catalina/session/PersistentManagerBase.java
+++ b/java/org/apache/catalina/session/PersistentManagerBase.java
@@ -14,8 +14,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
-
package org.apache.catalina.session;
import java.io.IOException;
@@ -431,7 +429,7 @@ public abstract class PersistentManagerBase extends ManagerBase
}
}
processPersistenceChecks();
- if ((getStore() != null) && (getStore() instanceof StoreBase)) {
+ if (getStore() instanceof StoreBase) {
((StoreBase) getStore()).processExpires();
}
@@ -674,7 +672,9 @@ public abstract class PersistentManagerBase extends ManagerBase
* in, but will not be added to the active session list if it
* is invalid or past its expiration.
*
+ * @param id The id of the session that should be swapped in
* @return restored session, or {@code null}, if none is found
+ * @throws IOException an IO error occurred
*/
protected Session swapIn(String id) throws IOException {
@@ -775,7 +775,8 @@ public abstract class PersistentManagerBase extends ManagerBase
* is past its expiration or invalid, this method does
* nothing.
*
- * @param session The Session to write out.
+ * @param session The Session to write out
+ * @throws IOException an IO error occurred
*/
protected void swapOut(Session session) throws IOException {
@@ -795,6 +796,8 @@ public abstract class PersistentManagerBase extends ManagerBase
* Write the provided session to the Store without modifying
* the copy in memory or triggering passivation events. Does
* nothing if the session is invalid or past its expiration.
+ * @param session The session that should be written
+ * @throws IOException an IO error occurred
*/
protected void writeSession(Session session) throws IOException {
@@ -875,8 +878,9 @@ public abstract class PersistentManagerBase extends ManagerBase
}
}
- if (getStore() != null && getStore() instanceof Lifecycle)
+ if (getStore() instanceof Lifecycle) {
((Lifecycle)getStore()).stop();
+ }
// Require a new random number generator if we are restarted
super.stopInternal();
diff --git a/java/org/apache/catalina/session/StandardManager.java b/java/org/apache/catalina/session/StandardManager.java
index 936f30f..2195886 100644
--- a/java/org/apache/catalina/session/StandardManager.java
+++ b/java/org/apache/catalina/session/StandardManager.java
@@ -414,6 +414,7 @@ public class StandardManager extends ManagerBase {
/**
* Return a File object representing the pathname to our
* persistence file, if any.
+ * @return the file
*/
protected File file() {
if (pathname == null || pathname.length() == 0) {
diff --git a/java/org/apache/catalina/session/StandardSession.java b/java/org/apache/catalina/session/StandardSession.java
index 8c1a386..5354fd4 100644
--- a/java/org/apache/catalina/session/StandardSession.java
+++ b/java/org/apache/catalina/session/StandardSession.java
@@ -162,19 +162,6 @@ public class StandardSession implements HttpSession, Session, Serializable {
/**
- * Set of attribute names which are not allowed to be persisted.
- *
- * @deprecated Use {@link Constants#excludedAttributeNames} instead. Will be
- * removed in Tomcat 9.
- */
- @Deprecated
- protected static final String[] excludedAttributes = {
- Globals.SUBJECT_ATTR,
- Globals.GSS_CREDENTIAL_ATTR
- };
-
-
- /**
* We are currently processing a session expiration, so bypass
* certain IllegalStateException tests. NOTE: This value is not
* included in the serialized version of this object.
@@ -1561,7 +1548,7 @@ public class StandardSession implements HttpSession, Session, Serializable {
/**
- * Return the <code>isValid</code> flag for this session without any expiration
+ * @return the <code>isValid</code> flag for this session without any expiration
* check.
*/
protected boolean isValidInternal() {
@@ -1726,19 +1713,6 @@ public class StandardSession implements HttpSession, Session, Serializable {
/**
- * Exclude standard attributes that cannot be serialized.
- * @param name the attribute's name
- *
- * @deprecated Use {@link #exclude(String, Object)}. Will be removed in
- * Tomcat 8.5.x.
- */
- @Deprecated
- protected boolean exclude(String name){
- return exclude(name, null);
- }
-
-
- /**
* Should the given session attribute be excluded? This implementation
* checks:
* <ul>
@@ -1801,7 +1775,7 @@ public class StandardSession implements HttpSession, Session, Serializable {
/**
- * Return the names of all currently defined session attributes
+ * @return the names of all currently defined session attributes
* as an array of Strings. If there are no defined attributes, a
* zero-length array is returned.
*/
diff --git a/java/org/apache/catalina/session/mbeans-descriptors.xml b/java/org/apache/catalina/session/mbeans-descriptors.xml
index 8156194..a886e4b 100644
--- a/java/org/apache/catalina/session/mbeans-descriptors.xml
+++ b/java/org/apache/catalina/session/mbeans-descriptors.xml
@@ -1,4 +1,4 @@
-<?xml version="1.0"?>
+<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
@@ -33,11 +33,6 @@
type="java.lang.String"
writeable="false"/>
- <attribute name="distributable"
- description="The distributable flag for Sessions created by this
- Manager"
- type="boolean"/>
-
<attribute name="duplicates"
description="Number of duplicated session ids generated"
type="int" />
@@ -60,11 +55,6 @@
for no limit"
type="int"/>
- <attribute name="maxInactiveInterval"
- description="The default maximum inactive interval for Sessions
- created by this Manager"
- type="int"/>
-
<attribute name="name"
description="The descriptive name of this Manager implementation
(for logging)"
@@ -114,11 +104,6 @@
type="int"
writeable="false" />
- <attribute name="sessionIdLength"
- description="The session id length (in bytes) of Sessions
- created by this Manager"
- type="int"/>
-
<attribute name="sessionMaxAliveTime"
description="Longest time an expired session had been alive"
type="int" />
@@ -232,10 +217,6 @@
type="java.lang.String"
writeable="false"/>
- <attribute name="distributable"
- description="The distributable flag for Sessions created by this Manager"
- type="boolean"/>
-
<attribute name="duplicates"
description="Number of duplicated session ids generated"
type="int" />
@@ -266,10 +247,6 @@
description="Indicates how many seconds old a session can get, after its last use in a request, before it should be backed up to the store. -1 means sessions are not backed up."
type="int"/>
- <attribute name="maxInactiveInterval"
- description="The default maximum inactive interval for Sessions created by this Manager"
- type="int"/>
-
<attribute name="minIdleSwap"
description=" The minimum time in seconds that a session must be idle before it can be swapped out of memory, or -1 if it can be swapped out at any time."
type="int"/>
@@ -314,11 +291,6 @@
type="int"
writeable="false" />
- <attribute name="sessionIdLength"
- description="The session id length (in bytes) of Sessions
- created by this Manager"
- type="int"/>
-
<attribute name="sessionMaxAliveTime"
description="Longest time an expired session had been alive"
type="int" />
diff --git a/java/org/apache/catalina/ssi/ExpressionParseTree.java b/java/org/apache/catalina/ssi/ExpressionParseTree.java
index 5efa038..8d0c7a1 100644
--- a/java/org/apache/catalina/ssi/ExpressionParseTree.java
+++ b/java/org/apache/catalina/ssi/ExpressionParseTree.java
@@ -50,6 +50,9 @@ public class ExpressionParseTree {
/**
* Creates a new parse tree for the specified expression.
+ * @param expr The expression string
+ * @param ssiMediator Used to evaluated the expressions
+ * @throws ParseException a parsing error occurred
*/
public ExpressionParseTree(String expr, SSIMediator ssiMediator)
throws ParseException {
@@ -61,6 +64,7 @@ public class ExpressionParseTree {
/**
* Evaluates the tree and returns true or false. The specified SSIMediator
* is used to resolve variable references.
+ * @return the evaluation result
*/
public boolean evaluateTree() {
return root.evaluate();
@@ -70,6 +74,7 @@ public class ExpressionParseTree {
/**
* Pushes a new operator onto the opp stack, resolving existing opps as
* needed.
+ * @param node The operator node
*/
private void pushOpp(OppNode node) {
// If node is null then it's just a group marker
@@ -115,6 +120,8 @@ public class ExpressionParseTree {
/**
* Parses the specified expression into a tree of parse nodes.
+ * @param expr The expression to parse
+ * @throws ParseException a parsing error occurred
*/
private void parseExpression(String expr) throws ParseException {
StringNode currStringNode = null;
@@ -204,7 +211,7 @@ public class ExpressionParseTree {
*/
private abstract class Node {
/**
- * Return true if the node evaluates to true.
+ * @return {@code true} if the node evaluates to true.
*/
public abstract boolean evaluate();
}
@@ -223,6 +230,8 @@ public class ExpressionParseTree {
/**
* Resolves any variable references and returns the value string.
+ *
+ * @return the value string
*/
public String getValue() {
if (resolved == null)
@@ -265,7 +274,7 @@ public class ExpressionParseTree {
/**
- * Returns a preference level suitable for comparison to other OppNode
+ * @return a precedence level suitable for comparison to other OppNode
* preference levels.
*/
public abstract int getPrecedence();
@@ -274,6 +283,8 @@ public class ExpressionParseTree {
/**
* Lets the node pop its own branch nodes off the front of the
* specified list. The default pulls two.
+ *
+ * @param values The list from which to pop the values
*/
public void popValues(List<Node> values) {
right = values.remove(0);
diff --git a/java/org/apache/catalina/ssi/ExpressionTokenizer.java b/java/org/apache/catalina/ssi/ExpressionTokenizer.java
index ad5aa6a..a99a17d 100644
--- a/java/org/apache/catalina/ssi/ExpressionTokenizer.java
+++ b/java/org/apache/catalina/ssi/ExpressionTokenizer.java
@@ -46,6 +46,7 @@ public class ExpressionTokenizer {
/**
* Creates a new parser for the specified expression.
+ * @param expr The expression
*/
public ExpressionTokenizer(String expr) {
this.expr = expr.trim().toCharArray();
@@ -54,7 +55,7 @@ public class ExpressionTokenizer {
/**
- * Returns true if there are more tokens.
+ * @return <code>true</code> if there are more tokens.
*/
public boolean hasMoreTokens() {
return index < length;
@@ -62,7 +63,7 @@ public class ExpressionTokenizer {
/**
- * Returns the current index for error reporting purposes.
+ * @return the current index for error reporting purposes.
*/
public int getIndex() {
return index;
@@ -76,7 +77,7 @@ public class ExpressionTokenizer {
/**
- * Returns the next token type and initializes any state variables
+ * @return the next token type and initializes any state variables
* accordingly.
*/
public int nextToken() {
@@ -176,7 +177,7 @@ public class ExpressionTokenizer {
/**
- * Returns the String value of the token if it was type TOKEN_STRING.
+ * @return the String value of the token if it was type TOKEN_STRING.
* Otherwise null is returned.
*/
public String getTokenValue() {
diff --git a/java/org/apache/catalina/ssi/ResponseIncludeWrapper.java b/java/org/apache/catalina/ssi/ResponseIncludeWrapper.java
index 313376f..c1d4394 100644
--- a/java/org/apache/catalina/ssi/ResponseIncludeWrapper.java
+++ b/java/org/apache/catalina/ssi/ResponseIncludeWrapper.java
@@ -90,6 +90,7 @@ public class ResponseIncludeWrapper extends HttpServletResponseWrapper {
* Flush the servletOutputStream or printWriter ( only one will be non-null )
* This must be called after a requestDispatcher.include, since we can't
* assume that the included servlet flushed its stream.
+ * @throws IOException an IO error occurred
*/
public void flushOutputStreamOrWriter() throws IOException {
if (servletOutputStream != null) {
diff --git a/java/org/apache/catalina/ssi/SSIFilter.java b/java/org/apache/catalina/ssi/SSIFilter.java
index 7083957..ecfc440 100644
--- a/java/org/apache/catalina/ssi/SSIFilter.java
+++ b/java/org/apache/catalina/ssi/SSIFilter.java
@@ -62,13 +62,6 @@ public class SSIFilter implements Filter {
protected boolean allowExec = false;
- //----------------- Public methods.
- /**
- * Initialize this servlet.
- *
- * @exception ServletException
- * if an error occurs
- */
@Override
public void init(FilterConfig config) throws ServletException {
this.config = config;
diff --git a/java/org/apache/catalina/ssi/SSIMediator.java b/java/org/apache/catalina/ssi/SSIMediator.java
index 444c535..c3cbdc9 100644
--- a/java/org/apache/catalina/ssi/SSIMediator.java
+++ b/java/org/apache/catalina/ssi/SSIMediator.java
@@ -25,9 +25,9 @@ import java.util.Locale;
import java.util.Set;
import java.util.TimeZone;
+import org.apache.catalina.util.RequestUtil;
import org.apache.catalina.util.Strftime;
import org.apache.catalina.util.URLEncoder;
-import org.apache.tomcat.util.http.HttpMessages;
/**
* Allows the different SSICommand implementations to share data/talk to each
@@ -201,6 +201,8 @@ public class SSIMediator {
/**
* Applies variable substitution to the specified String and returns the
* new resolved string.
+ * @param val The value which should be checked
+ * @return the value after variable substitution
*/
public String substituteVariables(String val) {
// If it has no references or HTML entities then no work
@@ -298,7 +300,7 @@ public class SSIMediator {
} else if (encoding.equalsIgnoreCase("none")) {
retVal = value;
} else if (encoding.equalsIgnoreCase("entity")) {
- retVal = HttpMessages.filter(value);
+ retVal = RequestUtil.filter(value);
} else {
//This shouldn't be possible
throw new IllegalArgumentException("Unknown encoding: " + encoding);
diff --git a/java/org/apache/catalina/ssi/SSIProcessor.java b/java/org/apache/catalina/ssi/SSIProcessor.java
index e2dbed3..9f5153f 100644
--- a/java/org/apache/catalina/ssi/SSIProcessor.java
+++ b/java/org/apache/catalina/ssi/SSIProcessor.java
@@ -85,6 +85,7 @@ public class SSIProcessor {
*
* @param reader
* the reader to read the file containing SSIs from
+ * @param lastModifiedDate resource last modification date
* @param writer
* the writer to write the file with the SSIs processed.
* @return the most current modified date resulting from any SSI commands
@@ -185,7 +186,8 @@ public class SSIProcessor {
*
* @param cmd
* a value of type 'StringBuilder'
- * @return a value of type 'String[]'
+ * @param start index on which parsing will start
+ * @return an array with the parameter names
*/
protected String[] parseParamNames(StringBuilder cmd, int start) {
int bIdx = start;
@@ -235,7 +237,9 @@ public class SSIProcessor {
*
* @param cmd
* a value of type 'StringBuilder'
- * @return a value of type 'String[]'
+ * @param start index on which parsing will start
+ * @param count number of values which should be parsed
+ * @return an array with the parameter values
*/
protected String[] parseParamValues(StringBuilder cmd, int start, int count) {
int valIndex = 0;
diff --git a/java/org/apache/catalina/ssi/SSIServlet.java b/java/org/apache/catalina/ssi/SSIServlet.java
index a98a32c..918d108 100644
--- a/java/org/apache/catalina/ssi/SSIServlet.java
+++ b/java/org/apache/catalina/ssi/SSIServlet.java
@@ -145,6 +145,7 @@ public class SSIServlet extends HttpServlet {
* a value of type 'HttpServletRequest'
* @param res
* a value of type 'HttpServletResponse'
+ * @throws IOException an IO error occurred
*/
protected void requestHandler(HttpServletRequest req,
HttpServletResponse res) throws IOException {
diff --git a/java/org/apache/catalina/ssi/SSIServletExternalResolver.java b/java/org/apache/catalina/ssi/SSIServletExternalResolver.java
index 89a1692..a41187d 100644
--- a/java/org/apache/catalina/ssi/SSIServletExternalResolver.java
+++ b/java/org/apache/catalina/ssi/SSIServletExternalResolver.java
@@ -550,8 +550,7 @@ public class SSIServletExternalResolver implements SSIExternalResolver {
// a problem
// if a truly empty file
//were included, but not sure how else to tell.
- if (retVal.equals("") && !req.getMethod().equalsIgnoreCase(
- org.apache.coyote.http11.Constants.HEAD)) {
+ if (retVal.equals("") && !req.getMethod().equalsIgnoreCase("HEAD")) {
throw new IOException("Couldn't find file: " + path);
}
return retVal;
diff --git a/java/org/apache/catalina/ssi/SSIServletRequestUtil.java b/java/org/apache/catalina/ssi/SSIServletRequestUtil.java
index 1ecd021..61b3594 100644
--- a/java/org/apache/catalina/ssi/SSIServletRequestUtil.java
+++ b/java/org/apache/catalina/ssi/SSIServletRequestUtil.java
@@ -29,6 +29,7 @@ public class SSIServletRequestUtil {
*
* @param request
* The servlet request we are processing
+ * @return the relative path
*/
public static String getRelativePath(HttpServletRequest request) {
// Are we being processed by a RequestDispatcher.include()?
diff --git a/java/org/apache/catalina/startup/Authenticators.properties b/java/org/apache/catalina/startup/Authenticators.properties
index a995844..70b5239 100644
--- a/java/org/apache/catalina/startup/Authenticators.properties
+++ b/java/org/apache/catalina/startup/Authenticators.properties
@@ -18,4 +18,4 @@ CLIENT-CERT=org.apache.catalina.authenticator.SSLAuthenticator
DIGEST=org.apache.catalina.authenticator.DigestAuthenticator
FORM=org.apache.catalina.authenticator.FormAuthenticator
NONE=org.apache.catalina.authenticator.NonLoginAuthenticator
-SPNEGO=org.apache.catalina.authenticator.SpnegoAuthenticator
\ No newline at end of file
+SPNEGO=org.apache.catalina.authenticator.SpnegoAuthenticator
diff --git a/java/org/apache/catalina/startup/Bootstrap.java b/java/org/apache/catalina/startup/Bootstrap.java
index 8337a8a..fc18b5a 100644
--- a/java/org/apache/catalina/startup/Bootstrap.java
+++ b/java/org/apache/catalina/startup/Bootstrap.java
@@ -250,6 +250,7 @@ public final class Bootstrap {
/**
* Initialize daemon.
+ * @throws Exception Fatal initialization error
*/
public void init() throws Exception {
@@ -330,6 +331,8 @@ public final class Bootstrap {
/**
* Load the Catalina daemon.
+ * @param arguments Initialization arguments
+ * @throws Exception Fatal initialization error
*/
public void init(String[] arguments)
throws Exception {
@@ -342,6 +345,7 @@ public final class Bootstrap {
/**
* Start the Catalina daemon.
+ * @throws Exception Fatal start error
*/
public void start()
throws Exception {
@@ -355,6 +359,7 @@ public final class Bootstrap {
/**
* Stop the Catalina Daemon.
+ * @throws Exception Fatal stop error
*/
public void stop()
throws Exception {
@@ -367,6 +372,7 @@ public final class Bootstrap {
/**
* Stop the standalone server.
+ * @throws Exception Fatal stop error
*/
public void stopServer()
throws Exception {
@@ -380,6 +386,8 @@ public final class Bootstrap {
/**
* Stop the standalone server.
+ * @param arguments Command line arguments
+ * @throws Exception Fatal stop error
*/
public void stopServer(String[] arguments)
throws Exception {
@@ -404,6 +412,8 @@ public final class Bootstrap {
/**
* Set flag.
+ * @param await <code>true</code> if the daemon should block
+ * @throws Exception Reflection error
*/
public void setAwait(boolean await)
throws Exception {
@@ -511,6 +521,7 @@ public final class Bootstrap {
/**
* Obtain the name of configured home (binary) directory. Note that home and
* base may be the same (and are by default).
+ * @return the catalina home
*/
public static String getCatalinaHome() {
return catalinaHomeFile.getPath();
@@ -521,6 +532,7 @@ public final class Bootstrap {
* Obtain the name of the configured base (instance) directory. Note that
* home and base may be the same (and are by default). If this is not set
* the value returned by {@link #getCatalinaHome()} will be used.
+ * @return the catalina base
*/
public static String getCatalinaBase() {
return catalinaBaseFile.getPath();
@@ -530,6 +542,7 @@ public final class Bootstrap {
/**
* Obtain the configured home (binary) directory. Note that home and
* base may be the same (and are by default).
+ * @return the catalina home as a file
*/
public static File getCatalinaHomeFile() {
return catalinaHomeFile;
@@ -540,6 +553,7 @@ public final class Bootstrap {
* Obtain the configured base (instance) directory. Note that
* home and base may be the same (and are by default). If this is not set
* the value returned by {@link #getCatalinaHomeFile()} will be used.
+ * @return the catalina base as a file
*/
public static File getCatalinaBaseFile() {
return catalinaBaseFile;
diff --git a/java/org/apache/catalina/startup/Catalina.java b/java/org/apache/catalina/startup/Catalina.java
index a086d22..22a8d34 100644
--- a/java/org/apache/catalina/startup/Catalina.java
+++ b/java/org/apache/catalina/startup/Catalina.java
@@ -36,6 +36,8 @@ import org.apache.catalina.LifecycleState;
import org.apache.catalina.Server;
import org.apache.catalina.security.SecurityConfig;
import org.apache.juli.ClassLoaderLogManager;
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.ExceptionUtils;
import org.apache.tomcat.util.digester.Digester;
import org.apache.tomcat.util.digester.Rule;
@@ -175,7 +177,7 @@ public class Catalina {
/**
- * Return true if naming is enabled.
+ * @return <code>true</code> if naming is enabled.
*/
public boolean isUseNaming() {
return (this.useNaming);
@@ -203,11 +205,10 @@ public class Catalina {
/**
- * Process the specified command line arguments, and return
- * <code>true</code> if we should continue processing; otherwise
- * return <code>false</code>.
+ * Process the specified command line arguments.
*
* @param args Command line arguments to process
+ * @return <code>true</code> if we should continue processing
*/
protected boolean arguments(String args[]) {
@@ -247,6 +248,7 @@ public class Catalina {
/**
* Return a File object representing our configuration file.
+ * @return the main configuration file
*/
protected File configFile() {
@@ -261,6 +263,7 @@ public class Catalina {
/**
* Create and configure the Digester we will be using for startup.
+ * @return the main digester to parse server.xml
*/
protected Digester createStartDigester() {
long t1=System.currentTimeMillis();
@@ -329,11 +332,25 @@ public class Catalina {
digester.addRule("Server/Service/Connector",
new ConnectorCreateRule());
digester.addRule("Server/Service/Connector",
- new SetAllPropertiesRule(new String[]{"executor"}));
+ new SetAllPropertiesRule(new String[]{"executor", "sslImplementationName"}));
digester.addSetNext("Server/Service/Connector",
"addConnector",
"org.apache.catalina.connector.Connector");
+ digester.addObjectCreate("Server/Service/Connector/SSLHostConfig",
+ "org.apache.tomcat.util.net.SSLHostConfig");
+ digester.addSetProperties("Server/Service/Connector/SSLHostConfig");
+ digester.addSetNext("Server/Service/Connector/SSLHostConfig",
+ "addSslHostConfig",
+ "org.apache.tomcat.util.net.SSLHostConfig");
+
+ digester.addRule("Server/Service/Connector/SSLHostConfig/Certificate",
+ new CertificateCreateRule());
+ digester.addRule("Server/Service/Connector/SSLHostConfig/Certificate",
+ new SetAllPropertiesRule(new String[]{"type"}));
+ digester.addSetNext("Server/Service/Connector/SSLHostConfig/Certificate",
+ "addCertificate",
+ "org.apache.tomcat.util.net.SSLHostConfigCertificate");
digester.addObjectCreate("Server/Service/Connector/Listener",
null, // MUST be specified in the element
@@ -343,6 +360,14 @@ public class Catalina {
"addLifecycleListener",
"org.apache.catalina.LifecycleListener");
+ digester.addObjectCreate("Server/Service/Connector/UpgradeProtocol",
+ null, // MUST be specified in the element
+ "className");
+ digester.addSetProperties("Server/Service/Connector/UpgradeProtocol");
+ digester.addSetNext("Server/Service/Connector/UpgradeProtocol",
+ "addUpgradeProtocol",
+ "org.apache.coyote.UpgradeProtocol");
+
// Add RuleSets for nested elements
digester.addRuleSet(new NamingRuleSet("Server/GlobalNamingResources/"));
digester.addRuleSet(new EngineRuleSet("Server/Service/"));
@@ -388,6 +413,7 @@ public class Catalina {
/**
* Create and configure the Digester we will be using for shutdown.
+ * @return the digester to process the stop operation
*/
protected Digester createStopDigester() {
@@ -817,8 +843,7 @@ public class Catalina {
}
- private static final org.apache.juli.logging.Log log=
- org.apache.juli.logging.LogFactory.getLog( Catalina.class );
+ private static final Log log = LogFactory.getLog(Catalina.class);
}
diff --git a/java/org/apache/catalina/startup/CatalinaProperties.java b/java/org/apache/catalina/startup/CatalinaProperties.java
index 40bdd25..211406f 100644
--- a/java/org/apache/catalina/startup/CatalinaProperties.java
+++ b/java/org/apache/catalina/startup/CatalinaProperties.java
@@ -24,6 +24,9 @@ import java.net.URL;
import java.util.Enumeration;
import java.util.Properties;
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
+
/**
* Utility class to read the bootstrap Catalina configuration.
@@ -32,8 +35,7 @@ import java.util.Properties;
*/
public class CatalinaProperties {
- private static final org.apache.juli.logging.Log log=
- org.apache.juli.logging.LogFactory.getLog( CatalinaProperties.class );
+ private static final Log log = LogFactory.getLog(CatalinaProperties.class);
private static Properties properties = null;
@@ -44,7 +46,8 @@ public class CatalinaProperties {
/**
- * Return specified property value.
+ * @param name The property name
+ * @return specified property value
*/
public static String getProperty(String name) {
return properties.getProperty(name);
@@ -57,8 +60,6 @@ public class CatalinaProperties {
private static void loadProperties() {
InputStream is = null;
- Throwable error = null;
-
try {
String configUrl = System.getProperty("catalina.config");
if (configUrl != null) {
@@ -94,7 +95,7 @@ public class CatalinaProperties {
properties.load(is);
} catch (Throwable t) {
handleThrowable(t);
- error = t;
+ log.warn(t);
} finally {
try {
is.close();
@@ -104,9 +105,9 @@ public class CatalinaProperties {
}
}
- if ((is == null) || (error != null)) {
+ if ((is == null)) {
// Do something
- log.warn("Failed to load catalina.properties", error);
+ log.warn("Failed to load catalina.properties");
// That's fine - we have reasonable defaults.
properties = new Properties();
}
diff --git a/java/org/apache/catalina/startup/CertificateCreateRule.java b/java/org/apache/catalina/startup/CertificateCreateRule.java
new file mode 100644
index 0000000..6e49e19
--- /dev/null
+++ b/java/org/apache/catalina/startup/CertificateCreateRule.java
@@ -0,0 +1,61 @@
+/*
+ * 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.apache.tomcat.util.digester.Rule;
+import org.apache.tomcat.util.net.SSLHostConfig;
+import org.apache.tomcat.util.net.SSLHostConfigCertificate;
+import org.apache.tomcat.util.net.SSLHostConfigCertificate.Type;
+import org.xml.sax.Attributes;
+
+/**
+ * Rule implementation that creates a SSLHostConfigCertificate.
+ */
+public class CertificateCreateRule extends Rule {
+
+ @Override
+ public void begin(String namespace, String name, Attributes attributes) throws Exception {
+ SSLHostConfig sslHostConfig = (SSLHostConfig)digester.peek();
+
+ Type type;
+ String typeValue = attributes.getValue("type");
+ if (typeValue == null || typeValue.length() == 0) {
+ type = Type.UNDEFINED;
+ } else {
+ type = Type.valueOf(typeValue);
+ }
+
+ SSLHostConfigCertificate certificate = new SSLHostConfigCertificate(sslHostConfig, type);
+
+ digester.push(certificate);
+ }
+
+
+ /**
+ * 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
+ */
+ @Override
+ public void end(String namespace, String name) throws Exception {
+ digester.pop();
+ }
+}
diff --git a/java/org/apache/catalina/startup/ClassLoaderFactory.java b/java/org/apache/catalina/startup/ClassLoaderFactory.java
index a72f2ac..c5a245e 100644
--- a/java/org/apache/catalina/startup/ClassLoaderFactory.java
+++ b/java/org/apache/catalina/startup/ClassLoaderFactory.java
@@ -68,6 +68,7 @@ public final class ClassLoaderFactory {
* or <code>null</code> for no directories of JAR files to be considered
* @param parent Parent class loader for the new class loader, or
* <code>null</code> for the system class loader.
+ * @return the new class loader
*
* @exception Exception if an error occurs constructing the class loader
*/
@@ -86,7 +87,7 @@ public final class ClassLoaderFactory {
if (unpacked != null) {
for (int i = 0; i < unpacked.length; i++) {
File file = unpacked[i];
- if (!file.exists() || !file.canRead())
+ if (!file.canRead())
continue;
file = new File(file.getCanonicalPath() + File.separator);
URL url = file.toURI().toURL();
@@ -100,8 +101,7 @@ public final class ClassLoaderFactory {
if (packed != null) {
for (int i = 0; i < packed.length; i++) {
File directory = packed[i];
- if (!directory.isDirectory() || !directory.exists() ||
- !directory.canRead())
+ if (!directory.isDirectory() || !directory.canRead())
continue;
String filenames[] = directory.list();
if (filenames == null) {
@@ -144,6 +144,7 @@ public final class ClassLoaderFactory {
* the class loader.
* @param parent Parent class loader for the new class loader, or
* <code>null</code> for the system class loader.
+ * @return the new class loader
*
* @exception Exception if an error occurs constructing the class loader
*/
@@ -238,7 +239,7 @@ public final class ClassLoaderFactory {
private static boolean validateFile(File file,
RepositoryType type) throws IOException {
if (RepositoryType.DIR == type || RepositoryType.GLOB == type) {
- if (!file.exists() || !file.isDirectory() || !file.canRead()) {
+ if (!file.isDirectory() || !file.canRead()) {
String msg = "Problem with directory [" + file +
"], exists: [" + file.exists() +
"], isDirectory: [" + file.isDirectory() +
@@ -263,7 +264,7 @@ public final class ClassLoaderFactory {
return false;
}
} else if (RepositoryType.JAR == type) {
- if (!file.exists() || !file.canRead()) {
+ if (!file.canRead()) {
log.warn("Problem with JAR file [" + file +
"], exists: [" + file.exists() +
"], canRead: [" + file.canRead() + "]");
diff --git a/java/org/apache/catalina/startup/ConnectorCreateRule.java b/java/org/apache/catalina/startup/ConnectorCreateRule.java
index 8f33905..83e488c 100644
--- a/java/org/apache/catalina/startup/ConnectorCreateRule.java
+++ b/java/org/apache/catalina/startup/ConnectorCreateRule.java
@@ -28,6 +28,7 @@ import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.IntrospectionUtils;
import org.apache.tomcat.util.digester.Rule;
+import org.apache.tomcat.util.res.StringManager;
import org.xml.sax.Attributes;
@@ -38,6 +39,7 @@ import org.xml.sax.Attributes;
public class ConnectorCreateRule extends Rule {
private static final Log log = LogFactory.getLog(ConnectorCreateRule.class);
+ protected static final StringManager sm = StringManager.getManager(ConnectorCreateRule.class);
// --------------------------------------------------------- Public Methods
@@ -60,20 +62,33 @@ public class ConnectorCreateRule extends Rule {
ex = svc.getExecutor(attributes.getValue("executor"));
}
Connector con = new Connector(attributes.getValue("protocol"));
- if ( ex != null ) _setExecutor(con,ex);
-
+ if (ex != null) {
+ setExecutor(con, ex);
+ }
+ String sslImplementationName = attributes.getValue("sslImplementationName");
+ if (sslImplementationName != null) {
+ setSSLImplementationName(con, sslImplementationName);
+ }
digester.push(con);
}
- public void _setExecutor(Connector con, Executor ex) throws Exception {
+ private static void setExecutor(Connector con, Executor ex) throws Exception {
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});
}else {
- log.warn("Connector ["+con+"] does not support external executors. Method setExecutor(java.util.concurrent.Executor) not found.");
+ log.warn(sm.getString("connector.noSetExecutor", con));
}
}
+ private static void setSSLImplementationName(Connector con, String sslImplementationName) throws Exception {
+ Method m = IntrospectionUtils.findMethod(con.getProtocolHandler().getClass(),"setSslImplementationName",new Class[] {String.class});
+ if (m != null) {
+ m.invoke(con.getProtocolHandler(), new Object[] {sslImplementationName});
+ } else {
+ log.warn(sm.getString("connector.noSetSSLImplementationName", con));
+ }
+ }
/**
* Process the end of this element.
diff --git a/java/org/apache/catalina/startup/ContextConfig.java b/java/org/apache/catalina/startup/ContextConfig.java
index 2796f3a..bbd87c9 100644
--- a/java/org/apache/catalina/startup/ContextConfig.java
+++ b/java/org/apache/catalina/startup/ContextConfig.java
@@ -224,14 +224,6 @@ public class ContextConfig implements LifecycleListener {
new HashMap<>();
/**
- * Cache of JavaClass objects (byte code) by fully qualified class name.
- * Only populated if it is necessary to scan the super types and interfaces
- * as part of the processing for {@link HandlesTypes}.
- */
- protected final Map<String,JavaClassCacheEntry> javaClassCache =
- new HashMap<>();
-
- /**
* Flag that indicates if at least one {@link HandlesTypes} entry is present
* that represents an annotation.
*/
@@ -243,31 +235,31 @@ public class ContextConfig implements LifecycleListener {
*/
protected boolean handlesTypesNonAnnotations = false;
- private WebXmlParser webXmlParser;
// ------------------------------------------------------------- Properties
+
/**
- * Return the location of the default deployment descriptor
+ * Obtain the location of the default deployment descriptor.
+ *
+ * @return The path to the default web.xml. If not absolute, it is relative
+ * to CATALINA_BASE.
*/
public String getDefaultWebXml() {
- if( defaultWebXml == null ) {
- defaultWebXml=Constants.DefaultWebXml;
+ if (defaultWebXml == null) {
+ defaultWebXml = Constants.DefaultWebXml;
}
-
- return (this.defaultWebXml);
-
+ return defaultWebXml;
}
/**
- * Set the location of the default deployment descriptor
+ * Set the location of the default deployment descriptor.
*
- * @param path Absolute/relative path to the default web.xml
+ * @param path The path to the default web.xml. If not absolute, it is
+ * relative to CATALINA_BASE.
*/
public void setDefaultWebXml(String path) {
-
this.defaultWebXml = path;
-
}
@@ -385,9 +377,9 @@ public class ContextConfig implements LifecycleListener {
*/
Valve authenticator = null;
if (customAuthenticators != null) {
- authenticator = (Valve)
- customAuthenticators.get(loginConfig.getAuthMethod());
+ authenticator = (Valve) customAuthenticators.get(loginConfig.getAuthMethod());
}
+
if (authenticator == null) {
if (authenticators == null) {
log.error(sm.getString("contextConfig.authenticatorResources"));
@@ -396,9 +388,7 @@ public class ContextConfig implements LifecycleListener {
}
// Identify the class name of the Valve we should configure
- String authenticatorName = null;
- authenticatorName =
- authenticators.getProperty(loginConfig.getAuthMethod());
+ String authenticatorName = authenticators.getProperty(loginConfig.getAuthMethod());
if (authenticatorName == null) {
log.error(sm.getString("contextConfig.authenticatorMissing",
loginConfig.getAuthMethod()));
@@ -437,6 +427,7 @@ public class ContextConfig implements LifecycleListener {
/**
* Create (if necessary) and return a Digester configured to process the
* context configuration descriptor for an application.
+ * @return the digester for context.xml files
*/
protected Digester createContextDigester() {
Digester digester = new Digester();
@@ -457,6 +448,7 @@ public class ContextConfig implements LifecycleListener {
/**
* Process the default configuration file, if it exists.
+ * @param digester The digester that will be used for XML parsing
*/
protected void contextConfig(Digester digester) {
@@ -507,6 +499,8 @@ public class ContextConfig implements LifecycleListener {
/**
* Process a context.xml.
+ * @param digester The digester that will be used for XML parsing
+ * @param contextXml The URL to the context.xml configuration
*/
protected void processContextConfig(Digester digester, URL contextXml) {
@@ -575,6 +569,7 @@ public class ContextConfig implements LifecycleListener {
/**
* Adjust docBase.
+ * @throws IOException cannot access the context base path
*/
protected void fixDocBase() throws IOException {
@@ -741,9 +736,6 @@ public class ContextConfig implements LifecycleListener {
ok = true;
contextConfig(contextDigester);
-
- webXmlParser = new WebXmlParser(context.getXmlNamespaceAware(),
- context.getXmlValidation(), context.getXmlBlockExternal());
}
@@ -1109,8 +1101,11 @@ public class ContextConfig implements LifecycleListener {
* those in JARs excluded from an absolute ordering) need to be
* scanned to check if they match.
*/
+ WebXmlParser webXmlParser = new WebXmlParser(context.getXmlNamespaceAware(),
+ context.getXmlValidation(), context.getXmlBlockExternal());
+
Set<WebXml> defaults = new HashSet<>();
- defaults.add(getDefaultWebXmlFragment());
+ defaults.add(getDefaultWebXmlFragment(webXmlParser));
WebXml webXml = createWebXml();
@@ -1128,7 +1123,7 @@ public class ContextConfig implements LifecycleListener {
// provided by the container. If any of the application JARs have a
// web-fragment.xml it will be parsed at this point. web-fragment.xml
// files are ignored for container provided JARs.
- Map<String,WebXml> fragments = processJarsForWebFragments(webXml);
+ Map<String,WebXml> fragments = processJarsForWebFragments(webXml, webXmlParser);
// Step 2. Order the fragments.
Set<WebXml> orderedFragments = null;
@@ -1143,6 +1138,8 @@ public class ContextConfig implements LifecycleListener {
if (!webXml.isMetadataComplete() || typeInitializerMap.size() > 0) {
// Step 4. Process /WEB-INF/classes for annotations and
// @HandlesTypes matches
+ Map<String,JavaClassCacheEntry> javaClassCache = new HashMap<>();
+
if (ok) {
WebResource[] webResources =
context.getResources().listResources("/WEB-INF/classes");
@@ -1154,7 +1151,7 @@ public class ContextConfig implements LifecycleListener {
continue;
}
processAnnotationsWebResource(webResource, webXml,
- webXml.isMetadataComplete());
+ webXml.isMetadataComplete(), javaClassCache);
}
}
@@ -1164,7 +1161,7 @@ public class ContextConfig implements LifecycleListener {
// container fragments)
if (ok) {
processAnnotations(
- orderedFragments, webXml.isMetadataComplete());
+ orderedFragments, webXml.isMetadataComplete(), javaClassCache);
}
// Cache, if used, is no longer required so clear it
@@ -1198,14 +1195,8 @@ public class ContextConfig implements LifecycleListener {
configureContext(webXml);
}
- // Step 9a. Make the merged web.xml available to other
- // components.
- String mergedWebXml = webXml.toXml();
- @SuppressWarnings("deprecation")
- String attributeName = org.apache.tomcat.util.scan.Constants.MERGED_WEB_XML;
- sContext.setAttribute(attributeName, mergedWebXml);
if (context.getLogEffectiveWebXml()) {
- log.info("web.xml:\n" + mergedWebXml);
+ log.info("web.xml:\n" + webXml.toXml());
}
// Always need to look for static resources
@@ -1459,7 +1450,7 @@ public class ContextConfig implements LifecycleListener {
}
- private WebXml getDefaultWebXmlFragment() {
+ private WebXml getDefaultWebXmlFragment(WebXmlParser webXmlParser) {
// Host should never be null
Host host = (Host) context.getParent();
@@ -1685,6 +1676,8 @@ public class ContextConfig implements LifecycleListener {
* configure this application to see if they also contain static resources.
* If static resources are found, add them to the context. Resources are
* added in web-fragment.xml priority order.
+ * @param fragments The set of fragments that will be scanned for
+ * static resources
*/
protected void processResourceJARs(Set<WebXml> fragments) {
for (WebXml fragment : fragments) {
@@ -1728,6 +1721,7 @@ public class ContextConfig implements LifecycleListener {
/**
* Identify the default web.xml to be used and obtain an input source for
* it.
+ * @return an input source to the default web.xml
*/
protected InputSource getGlobalWebXmlSource() {
// Is a default web.xml specified for the Context?
@@ -1750,6 +1744,7 @@ public class ContextConfig implements LifecycleListener {
/**
* Identify the host web.xml to be used and obtain an input source for
* it.
+ * @return an input source to the default per host web.xml
*/
protected InputSource getHostWebXmlSource() {
File hostConfigBase = getHostConfigBase();
@@ -1762,6 +1757,7 @@ public class ContextConfig implements LifecycleListener {
/**
* Identify the application web.xml to be used and obtain an input source
* for it.
+ * @return an input source to the context web.xml
*/
protected InputSource getContextWebXmlSource() {
InputStream stream = null;
@@ -1819,10 +1815,11 @@ public class ContextConfig implements LifecycleListener {
}
/**
- *
+ * Utility method to create an input source from the specified XML file.
* @param filename Name of the file (possibly with one or more leading path
* segments) to read
* @param path Location that filename is relative to
+ * @return the input source
*/
protected InputSource getWebXmlSource(String filename, String path) {
File file = new File(filename);
@@ -1875,9 +1872,12 @@ public class ContextConfig implements LifecycleListener {
* <code>null</code> will be used if no web-fragment.xml was found. Any JARs
* known not contain fragments will be skipped.
*
+ * @param application The main web.xml metadata
+ * @param webXmlParser The parser to use to process the web.xml file
* @return A map of JAR name to processed web fragment (if any)
*/
- protected Map<String,WebXml> processJarsForWebFragments(WebXml application) {
+ protected Map<String,WebXml> processJarsForWebFragments(WebXml application,
+ WebXmlParser webXmlParser) {
JarScanner jarScanner = context.getJarScanner();
boolean delegate = false;
@@ -1905,7 +1905,7 @@ public class ContextConfig implements LifecycleListener {
}
protected void processAnnotations(Set<WebXml> fragments,
- boolean handlesTypesOnly) {
+ boolean handlesTypesOnly, Map<String,JavaClassCacheEntry> javaClassCache) {
for(WebXml fragment : fragments) {
// Only need to scan for @HandlesTypes matches if any of the
// following are true:
@@ -1920,7 +1920,7 @@ public class ContextConfig implements LifecycleListener {
// no impact on distributable
annotations.setDistributable(true);
URL url = fragment.getURL();
- processAnnotationsUrl(url, annotations, htOnly);
+ processAnnotationsUrl(url, annotations, htOnly, javaClassCache);
Set<WebXml> set = new HashSet<>();
set.add(annotations);
// Merge annotations into fragment - fragment takes priority
@@ -1929,7 +1929,8 @@ public class ContextConfig implements LifecycleListener {
}
protected void processAnnotationsWebResource(WebResource webResource,
- WebXml fragment, boolean handlesTypesOnly) {
+ WebXml fragment, boolean handlesTypesOnly,
+ Map<String,JavaClassCacheEntry> javaClassCache) {
if (webResource.isDirectory()) {
WebResource[] webResources =
@@ -1942,13 +1943,13 @@ public class ContextConfig implements LifecycleListener {
webResource.getURL()));
}
for (WebResource r : webResources) {
- processAnnotationsWebResource(r, fragment, handlesTypesOnly);
+ processAnnotationsWebResource(r, fragment, handlesTypesOnly, javaClassCache);
}
}
} else if (webResource.isFile() &&
webResource.getName().endsWith(".class")) {
try (InputStream is = webResource.getInputStream()) {
- processAnnotationsStream(is, fragment, handlesTypesOnly);
+ processAnnotationsStream(is, fragment, handlesTypesOnly, javaClassCache);
} catch (IOException e) {
log.error(sm.getString("contextConfig.inputStreamWebResource",
webResource.getWebappPath()),e);
@@ -1961,16 +1962,16 @@ public class ContextConfig implements LifecycleListener {
protected void processAnnotationsUrl(URL url, WebXml fragment,
- boolean handlesTypesOnly) {
+ boolean handlesTypesOnly, Map<String,JavaClassCacheEntry> javaClassCache) {
if (url == null) {
// Nothing to do.
return;
} else if ("jar".equals(url.getProtocol()) || url.toString().endsWith(".jar")) {
- processAnnotationsJar(url, fragment, handlesTypesOnly);
+ processAnnotationsJar(url, fragment, handlesTypesOnly, javaClassCache);
} else if ("file".equals(url.getProtocol())) {
try {
processAnnotationsFile(
- new File(url.toURI()), fragment, handlesTypesOnly);
+ new File(url.toURI()), fragment, handlesTypesOnly, javaClassCache);
} catch (URISyntaxException e) {
log.error(sm.getString("contextConfig.fileUrl", url), e);
}
@@ -1983,7 +1984,7 @@ public class ContextConfig implements LifecycleListener {
protected void processAnnotationsJar(URL url, WebXml fragment,
- boolean handlesTypesOnly) {
+ boolean handlesTypesOnly, Map<String,JavaClassCacheEntry> javaClassCache) {
try (Jar jar = JarFactory.newInstance(url)) {
if (log.isDebugEnabled()) {
@@ -1996,8 +1997,7 @@ public class ContextConfig implements LifecycleListener {
while (entryName != null) {
if (entryName.endsWith(".class")) {
try (InputStream is = jar.getEntryInputStream()) {
- processAnnotationsStream(
- is, fragment, handlesTypesOnly);
+ processAnnotationsStream(is, fragment, handlesTypesOnly, javaClassCache);
} catch (IOException e) {
log.error(sm.getString("contextConfig.inputStreamJar",
entryName, url),e);
@@ -2016,7 +2016,7 @@ public class ContextConfig implements LifecycleListener {
protected void processAnnotationsFile(File file, WebXml fragment,
- boolean handlesTypesOnly) {
+ boolean handlesTypesOnly, Map<String,JavaClassCacheEntry> javaClassCache) {
if (file.isDirectory()) {
// Returns null if directory is not readable
@@ -2028,12 +2028,12 @@ public class ContextConfig implements LifecycleListener {
}
for (String dir : dirs) {
processAnnotationsFile(
- new File(file,dir), fragment, handlesTypesOnly);
+ new File(file,dir), fragment, handlesTypesOnly, javaClassCache);
}
}
} else if (file.getName().endsWith(".class") && file.canRead()) {
try (FileInputStream fis = new FileInputStream(file)) {
- processAnnotationsStream(fis, fragment, handlesTypesOnly);
+ processAnnotationsStream(fis, fragment, handlesTypesOnly, javaClassCache);
} catch (IOException e) {
log.error(sm.getString("contextConfig.inputStreamFile",
file.getAbsolutePath()),e);
@@ -2046,12 +2046,12 @@ public class ContextConfig implements LifecycleListener {
protected void processAnnotationsStream(InputStream is, WebXml fragment,
- boolean handlesTypesOnly)
+ boolean handlesTypesOnly, Map<String,JavaClassCacheEntry> javaClassCache)
throws ClassFormatException, IOException {
ClassParser parser = new ClassParser(is);
JavaClass clazz = parser.parse();
- checkHandlesTypes(clazz);
+ checkHandlesTypes(clazz, javaClassCache);
if (handlesTypesOnly) {
return;
@@ -2079,9 +2079,11 @@ public class ContextConfig implements LifecycleListener {
* For classes packaged with the web application, the class and each
* super class needs to be checked for a match with {@link HandlesTypes} or
* for an annotation that matches {@link HandlesTypes}.
- * @param javaClass
+ * @param javaClass the class to check
+ * @param javaClassCache a class cache
*/
- protected void checkHandlesTypes(JavaClass javaClass) {
+ protected void checkHandlesTypes(JavaClass javaClass,
+ Map<String,JavaClassCacheEntry> javaClassCache) {
// Skip this if we can
if (typeInitializerMap.size() == 0) {
@@ -2099,16 +2101,16 @@ public class ContextConfig implements LifecycleListener {
Class<?> clazz = null;
if (handlesTypesNonAnnotations) {
// This *might* be match for a HandlesType.
- populateJavaClassCache(className, javaClass);
+ populateJavaClassCache(className, javaClass, javaClassCache);
JavaClassCacheEntry entry = javaClassCache.get(className);
if (entry.getSciSet() == null) {
try {
- populateSCIsForCacheEntry(entry);
+ populateSCIsForCacheEntry(entry, javaClassCache);
} catch (StackOverflowError soe) {
throw new IllegalStateException(sm.getString(
"contextConfig.annotationsStackOverflow",
context.getName(),
- classHierarchyToString(className, entry)));
+ classHierarchyToString(className, entry, javaClassCache)));
}
}
if (!entry.getSciSet().isEmpty()) {
@@ -2163,7 +2165,7 @@ public class ContextConfig implements LifecycleListener {
private String classHierarchyToString(String className,
- JavaClassCacheEntry entry) {
+ JavaClassCacheEntry entry, Map<String,JavaClassCacheEntry> javaClassCache) {
JavaClassCacheEntry start = entry;
StringBuilder msg = new StringBuilder(className);
msg.append("->");
@@ -2186,7 +2188,8 @@ public class ContextConfig implements LifecycleListener {
return msg.toString();
}
- private void populateJavaClassCache(String className, JavaClass javaClass) {
+ private void populateJavaClassCache(String className, JavaClass javaClass,
+ Map<String,JavaClassCacheEntry> javaClassCache) {
if (javaClassCache.containsKey(className)) {
return;
}
@@ -2194,14 +2197,15 @@ public class ContextConfig implements LifecycleListener {
// Add this class to the cache
javaClassCache.put(className, new JavaClassCacheEntry(javaClass));
- populateJavaClassCache(javaClass.getSuperclassName());
+ populateJavaClassCache(javaClass.getSuperclassName(), javaClassCache);
for (String iterface : javaClass.getInterfaceNames()) {
- populateJavaClassCache(iterface);
+ populateJavaClassCache(iterface, javaClassCache);
}
}
- private void populateJavaClassCache(String className) {
+ private void populateJavaClassCache(String className,
+ Map<String,JavaClassCacheEntry> javaClassCache) {
if (!javaClassCache.containsKey(className)) {
String name = className.replace('.', '/') + ".class";
try (InputStream is = context.getLoader().getClassLoader().getResourceAsStream(name)) {
@@ -2210,7 +2214,7 @@ public class ContextConfig implements LifecycleListener {
}
ClassParser parser = new ClassParser(is);
JavaClass clazz = parser.parse();
- populateJavaClassCache(clazz.getClassName(), clazz);
+ populateJavaClassCache(clazz.getClassName(), clazz, javaClassCache);
} catch (ClassFormatException e) {
log.debug(sm.getString("contextConfig.invalidSciHandlesTypes",
className), e);
@@ -2221,7 +2225,8 @@ public class ContextConfig implements LifecycleListener {
}
}
- private void populateSCIsForCacheEntry(JavaClassCacheEntry cacheEntry) {
+ private void populateSCIsForCacheEntry(JavaClassCacheEntry cacheEntry,
+ Map<String,JavaClassCacheEntry> javaClassCache) {
Set<ServletContainerInitializer> result = new HashSet<>();
// Super class
@@ -2238,7 +2243,7 @@ public class ContextConfig implements LifecycleListener {
// May be null of the class is not present or could not be loaded.
if (superClassCacheEntry != null) {
if (superClassCacheEntry.getSciSet() == null) {
- populateSCIsForCacheEntry(superClassCacheEntry);
+ populateSCIsForCacheEntry(superClassCacheEntry, javaClassCache);
}
result.addAll(superClassCacheEntry.getSciSet());
}
@@ -2253,7 +2258,7 @@ public class ContextConfig implements LifecycleListener {
// so move along
if (interfaceEntry != null) {
if (interfaceEntry.getSciSet() == null) {
- populateSCIsForCacheEntry(interfaceEntry);
+ populateSCIsForCacheEntry(interfaceEntry, javaClassCache);
}
result.addAll(interfaceEntry.getSciSet());
}
@@ -2392,9 +2397,9 @@ public class ContextConfig implements LifecycleListener {
* process filter annotation and merge with existing one!
* FIXME: refactoring method too long and has redundant subroutines with
* processAnnotationWebServlet!
- * @param className
- * @param ae
- * @param fragment
+ * @param className The filter class name
+ * @param ae The filter annotation
+ * @param fragment The corresponding fragment
*/
protected void processAnnotationWebFilter(String className,
AnnotationEntry ae, WebXml fragment) {
@@ -2606,7 +2611,7 @@ public class ContextConfig implements LifecycleListener {
}
}
- private static class JavaClassCacheEntry {
+ static class JavaClassCacheEntry {
public final String superclassName;
public final String[] interfaceNames;
diff --git a/java/org/apache/catalina/startup/ContextRuleSet.java b/java/org/apache/catalina/startup/ContextRuleSet.java
index 0ca1045..f963212 100644
--- a/java/org/apache/catalina/startup/ContextRuleSet.java
+++ b/java/org/apache/catalina/startup/ContextRuleSet.java
@@ -77,6 +77,8 @@ public class ContextRuleSet extends RuleSetBase {
*
* @param prefix Prefix for matching pattern rules (including the
* trailing slash character)
+ * @param create <code>true</code> if the main context instance should be
+ * created
*/
public ContextRuleSet(String prefix, boolean create) {
this.namespaceURI = null;
@@ -117,8 +119,6 @@ public class ContextRuleSet extends RuleSetBase {
"addChild",
"org.apache.catalina.Container");
}
- digester.addCallMethod(prefix + "Context/InstanceListener",
- "addInstanceListener", 0);
digester.addObjectCreate(prefix + "Context/Listener",
null, // MUST be specified in the element
@@ -243,7 +243,7 @@ public class ContextRuleSet extends RuleSetBase {
"org.apache.tomcat.JarScanFilter");
digester.addObjectCreate(prefix + "Context/CookieProcessor",
- "org.apache.tomcat.util.http.LegacyCookieProcessor",
+ "org.apache.tomcat.util.http.Rfc6265CookieProcessor",
"className");
digester.addSetProperties(prefix + "Context/CookieProcessor");
digester.addSetNext(prefix + "Context/CookieProcessor",
diff --git a/java/org/apache/catalina/startup/EngineRuleSet.java b/java/org/apache/catalina/startup/EngineRuleSet.java
index c55516f..0fb139a 100644
--- a/java/org/apache/catalina/startup/EngineRuleSet.java
+++ b/java/org/apache/catalina/startup/EngineRuleSet.java
@@ -93,7 +93,7 @@ public class EngineRuleSet extends RuleSetBase {
"engineConfigClass"));
digester.addSetNext(prefix + "Engine",
"setContainer",
- "org.apache.catalina.Container");
+ "org.apache.catalina.Engine");
//Cluster configuration start
digester.addObjectCreate(prefix + "Engine/Cluster",
diff --git a/java/org/apache/catalina/startup/ExpandWar.java b/java/org/apache/catalina/startup/ExpandWar.java
index ba64d3b..967d338 100644
--- a/java/org/apache/catalina/startup/ExpandWar.java
+++ b/java/org/apache/catalina/startup/ExpandWar.java
@@ -241,6 +241,7 @@ public class ExpandWar {
*
* @param src File object representing the source
* @param dest File object representing the destination
+ * @return <code>true</code> if the copy was successful
*/
public static boolean copy(File src, File dest) {
@@ -281,6 +282,7 @@ public class ExpandWar {
* sub-directories recursively. Any failure will be logged.
*
* @param dir File object representing the directory to be deleted
+ * @return <code>true</code> if the deletion was successful
*/
public static boolean delete(File dir) {
// Log failure by default
@@ -295,6 +297,7 @@ public class ExpandWar {
* @param dir File object representing the directory to be deleted
* @param logFailure <code>true</code> if failure to delete the resource
* should be logged
+ * @return <code>true</code> if the deletion was successful
*/
public static boolean delete(File dir, boolean logFailure) {
boolean result;
@@ -320,6 +323,7 @@ public class ExpandWar {
* sub-directories recursively. Any failure will be logged.
*
* @param dir File object representing the directory to be deleted
+ * @return <code>true</code> if the deletion was successful
*/
public static boolean deleteDir(File dir) {
return deleteDir(dir, true);
@@ -333,6 +337,7 @@ public class ExpandWar {
* @param dir File object representing the directory to be deleted
* @param logFailure <code>true</code> if failure to delete the resource
* should be logged
+ * @return <code>true</code> if the deletion was successful
*/
public static boolean deleteDir(File dir, boolean logFailure) {
diff --git a/java/org/apache/catalina/startup/FailedContext.java b/java/org/apache/catalina/startup/FailedContext.java
index 6c70ce0..8d7c5a8 100644
--- a/java/org/apache/catalina/startup/FailedContext.java
+++ b/java/org/apache/catalina/startup/FailedContext.java
@@ -533,13 +533,6 @@ public class FailedContext extends LifecycleMBeanBase implements Context {
public void removeFilterMap(FilterMap filterMap) { /* NO-OP */ }
@Override
- public void addInstanceListener(String listener) { /* NO-OP */ }
- @Override
- public String[] findInstanceListeners() { return null; }
- @Override
- public void removeInstanceListener(String listener) { /* NO-OP */ }
-
- @Override
public void addLocaleEncodingMappingParameter(String locale, String encoding) { /* NO-OP */ }
@Override
diff --git a/java/org/apache/catalina/startup/HostConfig.java b/java/org/apache/catalina/startup/HostConfig.java
index 4399d4c..67a0145 100644
--- a/java/org/apache/catalina/startup/HostConfig.java
+++ b/java/org/apache/catalina/startup/HostConfig.java
@@ -63,7 +63,6 @@ import org.apache.tomcat.util.digester.Digester;
import org.apache.tomcat.util.modeler.Registry;
import org.apache.tomcat.util.res.StringManager;
-
/**
* Startup event listener for a <b>Host</b> that configures the properties
* of that Host, and the associated defined contexts.
@@ -71,10 +70,14 @@ import org.apache.tomcat.util.res.StringManager;
* @author Craig R. McClanahan
* @author Remy Maucherat
*/
-public class HostConfig
- implements LifecycleListener {
+public class HostConfig implements LifecycleListener {
+
+ private static final Log log = LogFactory.getLog(HostConfig.class);
- private static final Log log = LogFactory.getLog( HostConfig.class );
+ /**
+ * The string resources for this package.
+ */
+ protected static final StringManager sm = StringManager.getManager(HostConfig.class);
/**
* The resolution, in milliseconds, of file modification times.
@@ -103,13 +106,6 @@ public class HostConfig
/**
- * The string resources for this package.
- */
- protected static final StringManager sm =
- StringManager.getManager(Constants.Package);
-
-
- /**
* Should we deploy XML Context config files packaged with WAR files and
* directories?
*/
@@ -161,7 +157,7 @@ public class HostConfig
/**
- * Return the Context implementation class name.
+ * @return the Context implementation class name.
*/
public String getContextClass() {
@@ -189,7 +185,7 @@ public class HostConfig
/**
- * Return the deploy XML config file flag for this component.
+ * @return the deploy XML config file flag for this component.
*/
public boolean isDeployXML() {
@@ -211,7 +207,7 @@ public class HostConfig
/**
- * Return the copy XML config file flag for this component.
+ * @return the copy XML config file flag for this component.
*/
public boolean isCopyXML() {
@@ -233,7 +229,7 @@ public class HostConfig
/**
- * Return the unpack WARs flag.
+ * @return the unpack WARs flag.
*/
public boolean isUnpackWARs() {
@@ -294,6 +290,7 @@ public class HostConfig
/**
* Add a serviced application to the list.
+ * @param name the context name
*/
public synchronized void addServiced(String name) {
serviced.add(name);
@@ -302,6 +299,7 @@ public class HostConfig
/**
* Is application serviced ?
+ * @param name the context name
* @return state of the application
*/
public synchronized boolean isServiced(String name) {
@@ -311,6 +309,7 @@ public class HostConfig
/**
* Removed a serviced application from the list.
+ * @param name the context name
*/
public synchronized void removeServiced(String name) {
serviced.remove(name);
@@ -319,8 +318,9 @@ public class HostConfig
/**
* Get the instant where an application was deployed.
+ * @param name the context name
* @return 0L if no application with that name is deployed, or the instant
- * on which the application was deployed
+ * on which the application was deployed
*/
public long getDeploymentTime(String name) {
DeployedApplication app = deployed.get(name);
@@ -335,9 +335,10 @@ public class HostConfig
/**
* Has the specified application been deployed? Note applications defined
* in server.xml will not have been deployed.
+ * @param name the context name
* @return <code>true</code> if the application has been deployed and
- * <code>false</code> if the application has not been deployed or does not
- * exist
+ * <code>false</code> if the application has not been deployed or does not
+ * exist
*/
public boolean isDeployed(String name) {
DeployedApplication app = deployed.get(name);
@@ -354,6 +355,9 @@ public class HostConfig
/**
* Create the digester which will be used to parse context config files.
+ * @param contextClassName The class which will be used to create the
+ * context instance
+ * @return the digester
*/
protected static Digester createDigester(String contextClassName) {
Digester digester = new Digester();
@@ -381,6 +385,7 @@ public class HostConfig
/**
* Get the name of the configBase.
* For use with JMX management.
+ * @return the config base
*/
public String getConfigBaseName() {
return host.getConfigBaseFile().getAbsolutePath();
@@ -443,6 +448,7 @@ public class HostConfig
/**
* Deploy applications for any directories or WAR files that are found
* in our "application root" directory.
+ * @param name The context name which should be deployed
*/
protected void deployApps(String name) {
@@ -476,6 +482,8 @@ public class HostConfig
/**
* Deploy XML context descriptors.
+ * @param configBase The config base
+ * @param files The XML descriptors which should be deployed
*/
protected void deployDescriptors(File configBase, String[] files) {
@@ -511,8 +519,9 @@ public class HostConfig
/**
- * @param cn
- * @param contextXml
+ * Deploy specified context descriptor.
+ * @param cn The context name
+ * @param contextXml The descriptor
*/
@SuppressWarnings("null") // context is not null
protected void deployDescriptor(ContextName cn, File contextXml) {
@@ -667,6 +676,8 @@ public class HostConfig
/**
* Deploy WAR files.
+ * @param appBase The base path for applications
+ * @param files The WARs to deploy
*/
protected void deployWARs(File appBase, String[] files) {
@@ -776,8 +787,9 @@ public class HostConfig
}
/**
- * @param cn
- * @param war
+ * Deploy packed WAR.
+ * @param cn The context name
+ * @param war The WAR file
*/
protected void deployWAR(ContextName cn, File war) {
@@ -979,7 +991,9 @@ public class HostConfig
/**
- * Deploy directories.
+ * Deploy exploded webapps.
+ * @param appBase The base path for applications
+ * @param files The exploded webapps that should be deployed
*/
protected void deployDirectories(File appBase, String[] files) {
@@ -1018,8 +1032,9 @@ public class HostConfig
/**
- * @param cn
- * @param dir
+ * Deploy exploded webapp.
+ * @param cn The context name
+ * @param dir The path to the root folder of the weapp
*/
protected void deployDirectory(ContextName cn, File dir) {
@@ -1150,6 +1165,7 @@ public class HostConfig
* Check if a webapp is already deployed in this host.
*
* @param contextName of the context which will be checked
+ * @return <code>true</code> if the specified deployment exists
*/
protected boolean deploymentExists(String contextName) {
return (deployed.containsKey(contextName) ||
@@ -1219,17 +1235,6 @@ public class HostConfig
/**
* Check resources for redeployment and reloading.
*
- * @deprecated Use {@link #checkResources(DeployedApplication, boolean)}.
- * Will be removed in Tomcat 8.5.x
- */
- @Deprecated
- protected synchronized void checkResources(DeployedApplication app) {
- checkResources(app, false);
- }
-
- /**
- * Check resources for redeployment and reloading.
- *
* @param app The web application to check
* @param skipFileModificationResolutionCheck
* When checking files for modification should the check that
@@ -1669,6 +1674,7 @@ public class HostConfig
/**
* Add a new Context to be managed by us.
* Entry point for the admin webapp, and other JMX Context controllers.
+ * @param context The context instance
*/
public void manageApp(Context context) {
@@ -1714,6 +1720,7 @@ public class HostConfig
/**
* Remove a webapp from our control.
* Entry point for the admin webapp, and other JMX Context controllers.
+ * @param contextName The context name
*/
public void unmanageApp(String contextName) {
if(isServiced(contextName)) {
diff --git a/java/org/apache/catalina/startup/LocalStrings.properties b/java/org/apache/catalina/startup/LocalStrings.properties
index 459a58d..bb2e303 100644
--- a/java/org/apache/catalina/startup/LocalStrings.properties
+++ b/java/org/apache/catalina/startup/LocalStrings.properties
@@ -144,3 +144,6 @@ versionLoggerListener.arg =Command line argument: {0}
versionLoggerListener.env =Environment variable: {0} = {1}
versionLoggerListener.prop =System property: {0} = {1}
webAnnotationSet.invalidInjection=Invalid method resource injection annotation.
+
+connector.noSetExecutor=Connector [{0}] does not support external executors. Method setExecutor(java.util.concurrent.Executor) not found.
+connector.noSetSSLImplementationName=Connector [{0}] does not support changing the SSL implementation. Method setSslImplementationName(String) not found.
diff --git a/java/org/apache/catalina/startup/Tomcat.java b/java/org/apache/catalina/startup/Tomcat.java
index 78d3140..6688999 100644
--- a/java/org/apache/catalina/startup/Tomcat.java
+++ b/java/org/apache/catalina/startup/Tomcat.java
@@ -133,21 +133,7 @@ public class Tomcat {
// so that configuration is not lost.
private final Map<String, Logger> pinnedLoggers = new HashMap<>();
- // Single engine, service, server, connector - few cases need more,
- // they can use server.xml
protected Server server;
- protected Service service;
- protected Engine engine;
- protected Connector connector; // for more - customize the classes
-
- // To make it a bit easier to config for the common case
- // ( one host, one context ).
- protected Host host;
-
- // TODO: it's easy to add support for more hosts - but is it
- // really needed ?
-
- // TODO: allow use of in-memory connector
protected int port = 8080;
protected String hostname = "localhost";
@@ -171,6 +157,9 @@ public class Tomcat {
* (/tmp doesn't seem a good choice for security).
*
* TODO: disable work dir if not needed ( no jsp, etc ).
+ *
+ * @param basedir The Tomcat base folder on which all others
+ * will be derived
*/
public void setBaseDir(String basedir) {
this.basedir = basedir;
@@ -179,6 +168,7 @@ public class Tomcat {
/**
* Set the port for the default connector. Must
* be called before start().
+ * @param port The port number
*/
public void setPort(int port) {
this.port = port;
@@ -187,6 +177,7 @@ public class Tomcat {
/**
* The the hostname of the default host, default is
* 'localhost'.
+ * @param s The default host name
*/
public void setHostname(String s) {
hostname = s;
@@ -200,7 +191,11 @@ public class Tomcat {
* {@link javax.servlet.ServletContainerInitializer} processing will be
* applied.
*
- * @throws ServletException
+ * @param contextPath The context mapping to use, "" for root context.
+ * @param docBase Base directory for the context, for static files.
+ * Must exist, relative to the server home
+ * @return the deployed context
+ * @throws ServletException if a deployment error occurs
*/
public Context addWebapp(String contextPath, String docBase) throws ServletException {
return addWebapp(getHost(), contextPath, docBase);
@@ -238,9 +233,10 @@ public class Tomcat {
*
* TODO: add the rest
*
- * @param contextPath "" for root context.
- * @param docBase base dir for the context, for static files. Must exist,
- * relative to the server home
+ * @param contextPath The context mapping to use, "" for root context.
+ * @param docBase Base directory for the context, for static files.
+ * Must exist, relative to the server home
+ * @return the deployed context
*/
public Context addContext(String contextPath, String docBase) {
return addContext(getHost(), contextPath, docBase);
@@ -324,9 +320,9 @@ public class Tomcat {
/**
- * Initialise the server.
+ * Initialize the server.
*
- * @throws LifecycleException
+ * @throws LifecycleException Init error
*/
public void init() throws LifecycleException {
getServer();
@@ -338,7 +334,7 @@ public class Tomcat {
/**
* Start the server.
*
- * @throws LifecycleException
+ * @throws LifecycleException Start error
*/
public void start() throws LifecycleException {
getServer();
@@ -349,7 +345,7 @@ public class Tomcat {
/**
* Stop the server.
*
- * @throws LifecycleException
+ * @throws LifecycleException Stop error
*/
public void stop() throws LifecycleException {
getServer();
@@ -360,6 +356,8 @@ public class Tomcat {
/**
* Destroy the server. This object cannot be used once this method has been
* called.
+ *
+ * @throws LifecycleException Destroy error
*/
public void destroy() throws LifecycleException {
getServer();
@@ -370,14 +368,18 @@ public class Tomcat {
/**
* 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 pass The password
*/
public void addUser(String user, String pass) {
userPass.put(user, pass);
}
/**
+ * Add a role to a user.
* @see #addUser(String, String)
+ * @param user The user name
+ * @param role The role name
*/
public void addRole(String user, String role) {
List<String> roles = userRoles.get(user);
@@ -401,32 +403,41 @@ public class Tomcat {
* @return A connector object that can be customized
*/
public Connector getConnector() {
- getServer();
- if (connector != null) {
- return connector;
+ Service service = getService();
+ if (service.findConnectors().length > 0) {
+ return service.findConnectors()[0];
}
// The same as in standard Tomcat configuration.
// This creates an APR HTTP connector if AprLifecycleListener has been
// configured (created) and Tomcat Native library is available.
// Otherwise it creates a NIO HTTP connector.
- connector = new Connector("HTTP/1.1");
+ Connector connector = new Connector("HTTP/1.1");
connector.setPort(port);
- service.addConnector( connector );
+ service.addConnector(connector);
return connector;
}
public void setConnector(Connector connector) {
- this.connector = connector;
+ Service service = getService();
+ boolean found = false;
+ for (Connector serviceConnector : service.findConnectors()) {
+ if (connector == serviceConnector) {
+ found = true;
+ }
+ }
+ if (!found) {
+ service.addConnector(connector);
+ }
}
/**
* Get the service object. Can be used to add more
* connectors and few other global settings.
+ * @return The service
*/
public Service getService() {
- getServer();
- return service;
+ return getServer().findServices()[0];
}
/**
@@ -434,40 +445,54 @@ public class Tomcat {
* be added to this host. When tomcat starts, the
* host will be the default host.
*
- * @param host
+ * @param host The current host
*/
public void setHost(Host host) {
- this.host = host;
+ Engine engine = getEngine();
+ boolean found = false;
+ for (Container engineHost : engine.findChildren()) {
+ if (engineHost == host) {
+ found = true;
+ }
+ }
+ if (!found) {
+ engine.addChild(host);
+ }
}
public Host getHost() {
- if (host == null) {
- host = new StandardHost();
- host.setName(hostname);
-
- getEngine().addChild( host );
+ Engine engine = getEngine();
+ if (engine.findChildren().length > 0) {
+ return (Host) engine.findChildren()[0];
}
+
+ Host host = new StandardHost();
+ host.setName(hostname);
+ getEngine().addChild(host);
return host;
}
/**
* Access to the engine, for further customization.
+ * @return The engine
*/
public Engine getEngine() {
- if(engine == null ) {
- getServer();
- engine = new StandardEngine();
- engine.setName( "Tomcat" );
- engine.setDefaultHost(hostname);
- engine.setRealm(createDefaultRealm());
- service.setContainer(engine);
+ Service service = getServer().findServices()[0];
+ if (service.getContainer() != null) {
+ return service.getContainer();
}
+ Engine engine = new StandardEngine();
+ engine.setName( "Tomcat" );
+ engine.setDefaultHost(hostname);
+ engine.setRealm(createDefaultRealm());
+ service.setContainer(engine);
return engine;
}
/**
* Get the server object. You can add listeners and few more
* customizations. JNDI is disabled by default.
+ * @return The Server
*/
public Server getServer() {
@@ -483,13 +508,18 @@ public class Tomcat {
server.setPort( -1 );
- service = new StandardService();
+ Service service = new StandardService();
service.setName("Tomcat");
- server.addService( service );
+ server.addService(service);
return server;
}
/**
+ * @param host The host in which the context will be deployed
+ * @param contextPath The context mapping to use, "" for root context.
+ * @param dir Base directory for the context, for static files.
+ * Must exist, relative to the server home
+ * @return the deployed context
* @see #addContext(String, String)
*/
public Context addContext(Host host, String contextPath, String dir) {
@@ -497,6 +527,12 @@ public class Tomcat {
}
/**
+ * @param host The host in which the context will be deployed
+ * @param contextPath The context mapping to use, "" for root context.
+ * @param contextName The context name
+ * @param dir Base directory for the context, for static files.
+ * Must exist, relative to the server home
+ * @return the deployed context
* @see #addContext(String, String)
*/
public Context addContext(Host host, String contextPath, String contextName,
@@ -517,28 +553,28 @@ public class Tomcat {
}
/**
+ * @param host The host in which the context will be deployed
+ * @param contextPath The context mapping to use, "" for root context.
+ * @param docBase Base directory for the context, for static files.
+ * Must exist, relative to the server home
+ * @return the deployed context
* @see #addWebapp(String, String)
*/
public Context addWebapp(Host host, String contextPath, String docBase) {
- return addWebapp(host, contextPath, docBase, new ContextConfig());
- }
-
- /**
- * @see #addWebapp(String, String)
- *
- * @param name Ignored. The path will be used
- *
- * @deprecated Use {@link #addWebapp(Host, String, String)}
- */
- @Deprecated
- public Context addWebapp(Host host, String contextPath, String name, String docBase) {
return addWebapp(host, contextPath, docBase, new ContextConfig());
}
/**
+ * @param host The host in which the context will be deployed
+ * @param contextPath The context mapping to use, "" for root context.
+ * @param docBase Base directory for the context, for static files.
+ * Must exist, relative to the server home
+ * @param config Custom context configurator helper
+ * @return the deployed context
* @see #addWebapp(String, String)
*/
public Context addWebapp(Host host, String contextPath, String docBase, ContextConfig config) {
+
silence(host, contextPath);
Context ctx = createContext(host, contextPath);
@@ -589,6 +625,7 @@ public class Tomcat {
* one. The Realm created here will be added to the Engine by default and
* may be replaced at the Engine level or over-ridden (as per normal Tomcat
* behaviour) at the Host or Context level.
+ * @return a realm instance
*/
protected Realm createDefaultRealm() {
return new RealmBase() {
@@ -661,7 +698,7 @@ public class Tomcat {
}
static final String[] silences = new String[] {
- "org.apache.coyote.http11.Http11Protocol",
+ "org.apache.coyote.http11.Http11NioProtocol",
"org.apache.catalina.core.StandardService",
"org.apache.catalina.core.StandardEngine",
"org.apache.catalina.startup.ContextConfig",
diff --git a/java/org/apache/catalina/startup/UserConfig.java b/java/org/apache/catalina/startup/UserConfig.java
index 991276f..22baacf 100644
--- a/java/org/apache/catalina/startup/UserConfig.java
+++ b/java/org/apache/catalina/startup/UserConfig.java
@@ -32,6 +32,8 @@ import org.apache.catalina.Host;
import org.apache.catalina.Lifecycle;
import org.apache.catalina.LifecycleEvent;
import org.apache.catalina.LifecycleListener;
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.res.StringManager;
@@ -48,8 +50,7 @@ public final class UserConfig
implements LifecycleListener {
- private static final org.apache.juli.logging.Log log=
- org.apache.juli.logging.LogFactory.getLog( UserConfig.class );
+ private static final Log log = LogFactory.getLog(UserConfig.class);
// ----------------------------------------------------- Instance Variables
@@ -112,7 +113,7 @@ public final class UserConfig
/**
- * Return the Context configuration class name.
+ * @return the Context configuration class name.
*/
public String getConfigClass() {
@@ -134,7 +135,7 @@ public final class UserConfig
/**
- * Return the Context implementation class name.
+ * @return the Context implementation class name.
*/
public String getContextClass() {
@@ -156,7 +157,7 @@ public final class UserConfig
/**
- * Return the directory name for user web applications.
+ * @return the directory name for user web applications.
*/
public String getDirectoryName() {
@@ -178,7 +179,7 @@ public final class UserConfig
/**
- * Return the base directory containing user home directories.
+ * @return the base directory containing user home directories.
*/
public String getHomeBase() {
@@ -200,7 +201,7 @@ public final class UserConfig
/**
- * Return the user database class name for this component.
+ * @return the user database class name for this component.
*/
public String getUserClass() {
@@ -211,6 +212,7 @@ public final class UserConfig
/**
* Set the user database class name for this component.
+ * @param userClass The user database class name
*/
public void setUserClass(String userClass) {
@@ -219,7 +221,7 @@ public final class UserConfig
}
/**
- * Return the regular expression used to test for user who deployment is allowed.
+ * @return the regular expression used to test for user who deployment is allowed.
*/
public String getAllow() {
if (allow == null) return null;
@@ -242,7 +244,7 @@ public final class UserConfig
/**
- * Return the regular expression used to test for user who deployment is denied.
+ * @return the regular expression used to test for user who deployment is denied.
*/
public String getDeny() {
if (deny == null) return null;
@@ -352,11 +354,7 @@ public final class UserConfig
File app = new File(home, directoryName);
if (!app.exists() || !app.isDirectory())
return;
- /*
- File dd = new File(app, "/WEB-INF/web.xml");
- if (!dd.exists() || !dd.isFile() || !dd.canRead())
- return;
- */
+
host.getLogger().info(sm.getString("userConfig.deploy", user));
// Deploy the web application for this user
diff --git a/java/org/apache/catalina/startup/UserDatabase.java b/java/org/apache/catalina/startup/UserDatabase.java
index ea7467e..45db18a 100644
--- a/java/org/apache/catalina/startup/UserDatabase.java
+++ b/java/org/apache/catalina/startup/UserDatabase.java
@@ -35,7 +35,7 @@ public interface UserDatabase {
/**
- * Return the UserConfig listener with which we are associated.
+ * @return the UserConfig listener with which we are associated.
*/
public UserConfig getUserConfig();
@@ -52,7 +52,7 @@ public interface UserDatabase {
/**
- * Return an absolute pathname to the home directory for the specified user.
+ * @return an absolute pathname to the home directory for the specified user.
*
* @param user User for which a home directory should be retrieved
*/
@@ -60,7 +60,7 @@ public interface UserDatabase {
/**
- * Return an enumeration of the usernames defined on this server.
+ * @return an enumeration of the usernames defined on this server.
*/
public Enumeration<String> getUsers();
diff --git a/java/org/apache/catalina/startup/WebAnnotationSet.java b/java/org/apache/catalina/startup/WebAnnotationSet.java
index d75b02f..19e099b 100644
--- a/java/org/apache/catalina/startup/WebAnnotationSet.java
+++ b/java/org/apache/catalina/startup/WebAnnotationSet.java
@@ -57,6 +57,7 @@ public class WebAnnotationSet {
/**
* Process the annotations on a context.
+ * @param context The context which will have its annotations processed
*/
public static void loadApplicationAnnotations(Context context) {
@@ -73,6 +74,7 @@ public class WebAnnotationSet {
/**
* Process the annotations for the listeners.
+ * @param context The context which will have its annotations processed
*/
protected static void loadApplicationListenerAnnotations(Context context) {
String[] applicationListeners = context.findApplicationListeners();
@@ -91,6 +93,7 @@ public class WebAnnotationSet {
/**
* Process the annotations for the filters.
+ * @param context The context which will have its annotations processed
*/
protected static void loadApplicationFilterAnnotations(Context context) {
FilterDef[] filterDefs = context.findFilterDefs();
@@ -110,6 +113,7 @@ public class WebAnnotationSet {
/**
* Process the annotations for the servlets.
+ * @param context The context which will have its annotations processed
*/
protected static void loadApplicationServletAnnotations(Context context) {
@@ -148,6 +152,8 @@ public class WebAnnotationSet {
/**
* Process the annotations on a context for a given className.
+ * @param context The context which will have its annotations processed
+ * @param classClass The class to examine for Servlet annotations
*/
protected static void loadClassAnnotation(Context context,
Class<?> classClass) {
@@ -296,6 +302,8 @@ public class WebAnnotationSet {
* Ref JSR 250, equivalent to the resource-ref,
* message-destination-ref, env-ref, resource-env-ref
* or service-ref element in the deployment descriptor.
+ * @param context The context which will have its annotations processed
+ * @param annotation The annotation that was found
*/
protected static void addResource(Context context, Resource annotation) {
addResource(context, annotation, null, null);
diff --git a/java/org/apache/catalina/startup/WebappServiceLoader.java b/java/org/apache/catalina/startup/WebappServiceLoader.java
index 82db35e..220baac 100644
--- a/java/org/apache/catalina/startup/WebappServiceLoader.java
+++ b/java/org/apache/catalina/startup/WebappServiceLoader.java
@@ -52,6 +52,8 @@ import org.apache.tomcat.util.scan.JarFactory;
* <p>
* Provider classes will be loaded using the context's ClassLoader.
*
+ * @param <T> The type of service to load
+ *
* @see javax.servlet.ServletContainerInitializer
* @see java.util.ServiceLoader
*/
diff --git a/java/org/apache/catalina/startup/mbeans-descriptors.xml b/java/org/apache/catalina/startup/mbeans-descriptors.xml
index a274bff..ec0e94f 100644
--- a/java/org/apache/catalina/startup/mbeans-descriptors.xml
+++ b/java/org/apache/catalina/startup/mbeans-descriptors.xml
@@ -1,4 +1,4 @@
-<?xml version="1.0"?>
+<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
diff --git a/java/org/apache/catalina/storeconfig/ConnectorSF.java b/java/org/apache/catalina/storeconfig/ConnectorSF.java
index 611773a..b0a43ee 100644
--- a/java/org/apache/catalina/storeconfig/ConnectorSF.java
+++ b/java/org/apache/catalina/storeconfig/ConnectorSF.java
@@ -21,20 +21,14 @@ import java.io.PrintWriter;
import org.apache.catalina.LifecycleListener;
import org.apache.catalina.connector.Connector;
+import org.apache.coyote.UpgradeProtocol;
+import org.apache.tomcat.util.net.SSLHostConfig;
/**
* Store Connector and Listeners
*/
public class ConnectorSF extends StoreFactoryBase {
- /**
- * Store Connector description
- *
- * @param aWriter
- * @param indent
- * @param aConnector
- * @throws Exception
- */
@Override
public void storeChildren(PrintWriter aWriter, int indent, Object aConnector,
StoreDescription parentDesc) throws Exception {
@@ -44,6 +38,12 @@ public class ConnectorSF extends StoreFactoryBase {
// Store nested <Listener> elements
LifecycleListener listeners[] = connector.findLifecycleListeners();
storeElementArray(aWriter, indent, listeners);
+ // Store nested <UpgradeProtocol> elements
+ UpgradeProtocol[] upgradeProtocols = connector.findUpgradeProtocols();
+ storeElementArray(aWriter, indent, upgradeProtocols);
+ // Store nested <SSLHostConfig> elements
+ SSLHostConfig[] hostConfigs = connector.findSslHostConfigs();
+ storeElementArray(aWriter, indent, hostConfigs);
}
}
@@ -51,24 +51,15 @@ public class ConnectorSF extends StoreFactoryBase {
StoreDescription aDesc) throws Exception {
aWriter.print("<");
aWriter.print(aDesc.getTag());
- storeConnectorAttribtues(aWriter, indent, bean, aDesc);
+ storeConnectorAttributes(aWriter, indent, bean, aDesc);
aWriter.println(">");
}
- protected void storeConnectorAttribtues(PrintWriter aWriter, int indent,
+ protected void storeConnectorAttributes(PrintWriter aWriter, int indent,
Object bean, StoreDescription aDesc) throws Exception {
if (aDesc.isAttributes()) {
getStoreAppender().printAttributes(aWriter, indent, false, bean,
aDesc);
- /*
- * if (bean instanceof Connector) { StoreDescription elementDesc =
- * getRegistry().findDescription( bean.getClass().getName() +
- * ".[ProtocolHandler]"); if (elementDesc != null) { ProtocolHandler
- * protocolHandler = ((Connector) bean) .getProtocolHandler(); if
- * (protocolHandler != null)
- * getStoreAppender().printAttributes(aWriter, indent, false,
- * protocolHandler, elementDesc); } }
- */
}
}
@@ -76,7 +67,7 @@ public class ConnectorSF extends StoreFactoryBase {
StoreDescription aDesc) throws Exception {
aWriter.print("<");
aWriter.print(aDesc.getTag());
- storeConnectorAttribtues(aWriter, indent, bean, aDesc);
+ storeConnectorAttributes(aWriter, indent, bean, aDesc);
aWriter.println("/>");
}
diff --git a/java/org/apache/catalina/storeconfig/ConnectorStoreAppender.java b/java/org/apache/catalina/storeconfig/ConnectorStoreAppender.java
index c65f02e..abd1e78 100644
--- a/java/org/apache/catalina/storeconfig/ConnectorStoreAppender.java
+++ b/java/org/apache/catalina/storeconfig/ConnectorStoreAppender.java
@@ -42,7 +42,7 @@ import org.apache.tomcat.util.net.SocketProperties;
*/
public class ConnectorStoreAppender extends StoreAppender {
- protected static HashMap<String, String> replacements = new HashMap<>();
+ protected static final HashMap<String, String> replacements = new HashMap<>();
static {
replacements.put("backlog", "acceptCount");
replacements.put("soLinger", "connectionLinger");
@@ -58,21 +58,6 @@ public class ConnectorStoreAppender extends StoreAppender {
replacements.put("protocols", "sslProtocols");
}
- /**
- * Store the relevant attributes of the specified JavaBean.
- *
- * @param writer
- * PrintWriter to which we are storing
- * @param include
- * Should we include a <code>className</code> attribute?
- * @param bean
- * Bean whose properties are to be rendered as attributes,
- * @param desc
- * RegistryDescrpitor from this bean
- *
- * @exception Exception
- * if an exception occurs while storing
- */
@Override
public void printAttributes(PrintWriter writer, int indent,
boolean include, Object bean, StoreDescription desc)
@@ -118,11 +103,11 @@ public class ConnectorStoreAppender extends StoreAppender {
}
/**
- * Get all properties from Connector and current ProtocolHandler
+ * Get all properties from Connector and current ProtocolHandler.
*
- * @param bean
- * @return List of Connector Properties
- * @throws IntrospectionException
+ * @param bean The connector
+ * @return List of Connector property names
+ * @throws IntrospectionException Error intropecting connector
*/
protected List<String> getPropertyKeys(Connector bean)
throws IntrospectionException {
@@ -203,13 +188,13 @@ public class ConnectorStoreAppender extends StoreAppender {
}
/**
- * print Attributes
+ * Print Attributes for the connector
*
- * @param aWriter
- * @param indent
- * @param bean
- * @param aDesc
- * @throws Exception
+ * @param aWriter Current writer
+ * @param indent Indentation level
+ * @param bean The connector bean
+ * @param aDesc The connector description
+ * @throws Exception Store error occurred
*/
protected void storeConnectorAttribtues(PrintWriter aWriter, int indent,
Object bean, StoreDescription aDesc) throws Exception {
@@ -218,8 +203,8 @@ public class ConnectorStoreAppender extends StoreAppender {
}
}
- /*
- * Print the open tag for connector attributes (override)
+ /**
+ * Print the open tag for connector attributes (override).
*
* @see org.apache.catalina.storeconfig.StoreAppender#printOpenTag(java.io.PrintWriter,
* int, java.lang.Object,
@@ -235,7 +220,7 @@ public class ConnectorStoreAppender extends StoreAppender {
}
/**
- * print a tag for connector attributes (override)
+ * Print a tag for connector attributes (override).
*
* @see org.apache.catalina.storeconfig.StoreAppender#printTag(java.io.PrintWriter,
* int, java.lang.Object,
@@ -251,11 +236,8 @@ public class ConnectorStoreAppender extends StoreAppender {
}
/**
- * print a value but replace attribute name
+ * Print a value but replace certain attribute names.
*
- * @param writer
- * @param name
- * @param value
* @see org.apache.catalina.storeconfig.StoreAppender#printValue(java.io.PrintWriter,
* int, java.lang.String, java.lang.Object)
*/
@@ -269,13 +251,13 @@ public class ConnectorStoreAppender extends StoreAppender {
super.printValue(writer, indent, repl, value);
}
- /*
+ /**
* Print Connector Values. <ul><li> Spezial handling to default jkHome.
* </li><li> Don't save catalina.base path at server.xml</li><li></ul>
*
- * @see org.apache.catalina.config.StoreAppender#isPrintValue(java.lang.Object,
+ * @see org.apache.catalina.storeconfig.StoreAppender#isPrintValue(java.lang.Object,
* java.lang.Object, java.lang.String,
- * org.apache.catalina.config.StoreDescription)
+ * org.apache.catalina.storeconfig.StoreDescription)
*/
@Override
public boolean isPrintValue(Object bean, Object bean2, String attrName,
diff --git a/java/org/apache/catalina/storeconfig/CredentialHandlerSF.java b/java/org/apache/catalina/storeconfig/CredentialHandlerSF.java
index b6dcc9e..7e85497 100644
--- a/java/org/apache/catalina/storeconfig/CredentialHandlerSF.java
+++ b/java/org/apache/catalina/storeconfig/CredentialHandlerSF.java
@@ -59,23 +59,23 @@ public class CredentialHandlerSF extends StoreFactoryBase {
}
/**
- * Store the specified Realm properties and child (Realm)
+ * Store the specified CredentialHandler properties and child (CredentialHandler)
*
* @param aWriter
* PrintWriter to which we are storing
* @param indent
* Number of spaces to indent this element
- * @param aRealm
- * Realm whose properties are being stored
+ * @param aCredentialHandler
+ * CredentialHandler whose properties are being stored
*
* @exception Exception
* if an exception occurs while storing
*/
@Override
- public void storeChildren(PrintWriter aWriter, int indent, Object aRealm,
+ public void storeChildren(PrintWriter aWriter, int indent, Object aCredentialHandler,
StoreDescription parentDesc) throws Exception {
- if (aRealm instanceof NestedCredentialHandler) {
- NestedCredentialHandler nestedCredentialHandler = (NestedCredentialHandler) aRealm;
+ if (aCredentialHandler instanceof NestedCredentialHandler) {
+ NestedCredentialHandler nestedCredentialHandler = (NestedCredentialHandler) aCredentialHandler;
// Store nested <CredentialHandler> element
CredentialHandler[] credentialHandlers = nestedCredentialHandler.getCredentialHandlers();
diff --git a/java/org/apache/catalina/storeconfig/IStoreConfig.java b/java/org/apache/catalina/storeconfig/IStoreConfig.java
index 45e8578..c15cecd 100644
--- a/java/org/apache/catalina/storeconfig/IStoreConfig.java
+++ b/java/org/apache/catalina/storeconfig/IStoreConfig.java
@@ -65,6 +65,7 @@ public interface IStoreConfig {
*
* @param aServer
* Object to be stored
+ * @return <code>true</code> if the store operation was successful
*/
boolean store(Server aServer);
@@ -77,6 +78,7 @@ public interface IStoreConfig {
* Number of spaces to indent this element
* @param aServer
* Object to be stored
+ * @throws Exception Store error occurred
*/
void store(PrintWriter aWriter, int indent, Server aServer) throws Exception;
@@ -89,6 +91,7 @@ public interface IStoreConfig {
* Number of spaces to indent this element
* @param aService
* Object to be stored
+ * @throws Exception Store error occurred
*/
void store(PrintWriter aWriter, int indent, Service aService) throws Exception;
@@ -101,6 +104,7 @@ public interface IStoreConfig {
* Number of spaces to indent this element
* @param aHost
* Object to be stored
+ * @throws Exception Store error occurred
*/
void store(PrintWriter aWriter, int indent, Host aHost) throws Exception;
@@ -109,6 +113,7 @@ public interface IStoreConfig {
*
* @param aContext
* Object to be stored
+ * @return <code>true</code> if the store operation was successful
*/
boolean store(Context aContext);
@@ -121,6 +126,7 @@ public interface IStoreConfig {
* Number of spaces to indent this element
* @param aContext
* Object to be stored
+ * @throws Exception Store error occurred
*/
void store(PrintWriter aWriter, int indent, Context aContext) throws Exception;
}
\ No newline at end of file
diff --git a/java/org/apache/catalina/storeconfig/InstanceListenerSF.java b/java/org/apache/catalina/storeconfig/InstanceListenerSF.java
deleted file mode 100644
index f4c0338..0000000
--- a/java/org/apache/catalina/storeconfig/InstanceListenerSF.java
+++ /dev/null
@@ -1,60 +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.storeconfig;
-
-import java.io.PrintWriter;
-
-import org.apache.catalina.core.StandardContext;
-import org.apache.juli.logging.Log;
-import org.apache.juli.logging.LogFactory;
-
-/**
- * Store Context InstanceListener
- */
-public class InstanceListenerSF extends StoreFactoryBase {
- private static Log log = LogFactory.getLog(InstanceListenerSF.class);
-
- /*
- * Store nested Element Value Arrays
- *
- * @see org.apache.catalina.config.IStoreFactory#store(java.io.PrintWriter,
- * int, java.lang.Object)
- */
- @Override
- public void store(PrintWriter aWriter, int indent, Object aElement)
- throws Exception {
- if (aElement instanceof StandardContext) {
- StoreDescription elementDesc = getRegistry().findDescription(
- aElement.getClass().getName() + ".[InstanceListener]");
- @SuppressWarnings("deprecation")
- String[] listeners = ((StandardContext) aElement)
- .findInstanceListeners();
- if (elementDesc != null) {
- if (log.isDebugEnabled())
- log.debug("store " + elementDesc.getTag() + "( " + aElement
- + " )");
- getStoreAppender().printTagArray(aWriter, "InstanceListener",
- indent, listeners);
- }
- } else {
- if (log.isWarnEnabled())
- log.warn("Descriptor for element" + aElement.getClass()
- + ".[InstanceListener] not configured!");
- }
- }
-}
\ No newline at end of file
diff --git a/java/org/apache/catalina/storeconfig/LoaderSF.java b/java/org/apache/catalina/storeconfig/LoaderSF.java
index 14a0d82..1d89e9f 100644
--- a/java/org/apache/catalina/storeconfig/LoaderSF.java
+++ b/java/org/apache/catalina/storeconfig/LoaderSF.java
@@ -67,18 +67,19 @@ public class LoaderSF extends StoreFactoryBase {
*
* @param loader
* Loader to be tested
+ * @return <code>true</code> if this is an instance of the default loader
*/
protected boolean isDefaultLoader(Loader loader) {
if (!(loader instanceof WebappLoader)) {
- return (false);
+ return false;
}
WebappLoader wloader = (WebappLoader) loader;
if ((wloader.getDelegate() != false)
|| !wloader.getLoaderClass().equals(
"org.apache.catalina.loader.WebappClassLoader")) {
- return (false);
+ return false;
}
- return (true);
+ return true;
}
}
diff --git a/java/org/apache/catalina/storeconfig/ManagerSF.java b/java/org/apache/catalina/storeconfig/ManagerSF.java
index 9f98af7..e4e0a79 100644
--- a/java/org/apache/catalina/storeconfig/ManagerSF.java
+++ b/java/org/apache/catalina/storeconfig/ManagerSF.java
@@ -67,14 +67,15 @@ public class ManagerSF extends StoreFactoryBase {
*
* @param smanager
* Manager to be tested
+ * @return <code>true</code> if this is an instance of the default manager
*/
protected boolean isDefaultManager(StandardManager smanager) {
if (!"SESSIONS.ser".equals(smanager.getPathname())
|| (smanager.getMaxActiveSessions() != -1)) {
- return (false);
+ return false;
}
- return (true);
+ return true;
}
@@ -83,7 +84,7 @@ public class ManagerSF extends StoreFactoryBase {
StoreDescription parentDesc) throws Exception {
if (aManager instanceof Manager) {
Manager manager = (Manager) aManager;
- // Store nested <SessionIdGenerator> element
+ // Store nested <SessionIdGenerator> element;
SessionIdGenerator sessionIdGenerator = manager.getSessionIdGenerator();
if (sessionIdGenerator != null) {
storeElement(aWriter, indent, sessionIdGenerator);
diff --git a/java/org/apache/catalina/storeconfig/SSLHostConfigSF.java b/java/org/apache/catalina/storeconfig/SSLHostConfigSF.java
new file mode 100644
index 0000000..61ead84
--- /dev/null
+++ b/java/org/apache/catalina/storeconfig/SSLHostConfigSF.java
@@ -0,0 +1,45 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.storeconfig;
+
+import java.io.PrintWriter;
+
+import org.apache.tomcat.util.net.SSLHostConfig;
+import org.apache.tomcat.util.net.SSLHostConfigCertificate;
+
+/**
+ * Store SSLHostConfig
+ */
+public class SSLHostConfigSF extends StoreFactoryBase {
+
+ /**
+ * Store nested SSLHostConfigCertificate elements.
+ * {@inheritDoc}
+ */
+ @Override
+ public void storeChildren(PrintWriter aWriter, int indent, Object aSSLHostConfig,
+ StoreDescription parentDesc) throws Exception {
+ if (aSSLHostConfig instanceof SSLHostConfig) {
+ SSLHostConfig sslHostConfig = (SSLHostConfig) aSSLHostConfig;
+ // Store nested <SSLHostConfigCertificate> elements
+ SSLHostConfigCertificate[] hostConfigsCertificates = sslHostConfig.getCertificates().toArray(new SSLHostConfigCertificate[0]);
+ storeElementArray(aWriter, indent, hostConfigsCertificates);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/java/org/apache/catalina/storeconfig/StandardContextSF.java b/java/org/apache/catalina/storeconfig/StandardContextSF.java
index 888f37b..a6c5881 100644
--- a/java/org/apache/catalina/storeconfig/StandardContextSF.java
+++ b/java/org/apache/catalina/storeconfig/StandardContextSF.java
@@ -58,11 +58,11 @@ public class StandardContextSF extends StoreFactoryBase {
private static Log log = LogFactory.getLog(StandardContextSF.class);
- /*
+ /**
* Store a Context as Separate file as configFile value from context exists.
* filename can be relative to catalina.base.
*
- * @see org.apache.catalina.config.IStoreFactory#store(java.io.PrintWriter,
+ * @see org.apache.catalina.storeconfig.IStoreFactory#store(java.io.PrintWriter,
* int, java.lang.Object)
*/
@Override
@@ -110,10 +110,10 @@ public class StandardContextSF extends StoreFactoryBase {
* Store a Context without backup add separate file or when configFile =
* null a aWriter.
*
- * @param aWriter
- * @param indent
- * @param aContext
- * @throws Exception
+ * @param aWriter Current output writer
+ * @param indent Indentation level
+ * @param aContext The context which will be stored
+ * @throws Exception Configuration storing error
*/
protected void storeContextSeparate(PrintWriter aWriter, int indent,
StandardContext aContext) throws Exception {
@@ -146,10 +146,10 @@ public class StandardContextSF extends StoreFactoryBase {
}
/**
- * Store the Context with a Backup
+ * Store the Context with a Backup.
*
- * @param aContext
- * @throws Exception
+ * @param aContext The context which will be stored
+ * @throws Exception Configuration storing error
*/
protected void storeWithBackup(StandardContext aContext) throws Exception {
StoreFileMover mover = getConfigFileWriter(aContext);
@@ -188,9 +188,9 @@ public class StandardContextSF extends StoreFactoryBase {
/**
* Get explicit writer for context (context.getConfigFile()).
*
- * @param context
+ * @param context The context which will be stored
* @return The file mover
- * @throws IOException
+ * @throws Exception Error getting a writer for the configuration file
*/
protected StoreFileMover getConfigFileWriter(Context context)
throws Exception {
@@ -210,17 +210,13 @@ public class StandardContextSF extends StoreFactoryBase {
}
/**
- * Store the specified Host properties.
- *
- * @param aWriter
- * PrintWriter to which we are storing
- * @param indent
- * Number of spaces to indent this element
- * @param aContext
- * Context whose properties are being stored
+ * Store the specified context element children.
*
- * @exception Exception
- * if an exception occurs while storing
+ * @param aWriter Current output writer
+ * @param indent Indentation level
+ * @param aContext Context to store
+ * @param parentDesc The element description
+ * @throws Exception Configuration storing error
*/
@Override
public void storeChildren(PrintWriter aWriter, int indent, Object aContext,
@@ -267,12 +263,6 @@ public class StandardContextSF extends StoreFactoryBase {
WebResourceRoot resources = context.getResources();
storeElement(aWriter, indent, resources);
- // Store nested <InstanceListener> elements
- @SuppressWarnings("deprecation")
- String iListeners[] = context.findInstanceListeners();
- getStoreAppender().printTagArray(aWriter, "InstanceListener",
- indent + 2, iListeners);
-
// Store nested <WrapperListener> elements
String wLifecycles[] = context.findWrapperLifecycles();
getStoreAppender().printTagArray(aWriter, "WrapperListener",
@@ -310,15 +300,17 @@ public class StandardContextSF extends StoreFactoryBase {
/**
* Return a File object representing the "configuration root" directory for
* our associated Host.
+ * @param context The context instance
+ * @return a file to the configuration base path
*/
protected File configBase(Context context) {
File file = new File(System.getProperty("catalina.base"), "conf");
Container host = context.getParent();
- if ((host != null) && (host instanceof Host)) {
+ if (host instanceof Host) {
Container engine = host.getParent();
- if ((engine != null) && (engine instanceof Engine)) {
+ if (engine instanceof Engine) {
file = new File(file, engine.getName());
}
file = new File(file, host.getName());
@@ -333,16 +325,16 @@ public class StandardContextSF extends StoreFactoryBase {
}
/**
- * filter out the default watched resources
+ * Filter out the default watched resources, to remove standard ones.
*
- * @param context
- * @param wresources
- * @return The watched resources
- * @throws IOException
- * TODO relative watchedresource
+ * @param context The context instance
+ * @param wresources The raw watched resources list
+ * @return The filtered watched resources
+ * @throws Exception Configuration storing error
+ * TODO relative watched resources
* TODO absolute handling configFile
* TODO Filename case handling for Windows?
- * TODO digester variable subsitution $catalina.base, $catalina.home
+ * TODO digester variable substitution $catalina.base, $catalina.home
*/
protected String[] filterWatchedResources(StandardContext context,
String[] wresources) throws Exception {
diff --git a/java/org/apache/catalina/storeconfig/StandardServerSF.java b/java/org/apache/catalina/storeconfig/StandardServerSF.java
index 2e49faa..1f2665c 100644
--- a/java/org/apache/catalina/storeconfig/StandardServerSF.java
+++ b/java/org/apache/catalina/storeconfig/StandardServerSF.java
@@ -54,13 +54,13 @@ public class StandardServerSF extends StoreFactoryBase {
}
/**
- * Store Children from this StandardServer description
+ * Store the specified server element children.
*
- * @param aWriter
- * @param indent
- * @param aObject
- * @param parentDesc
- * @throws Exception
+ * @param aWriter Current output writer
+ * @param indent Indentation level
+ * @param aObject Server to store
+ * @param parentDesc The element description
+ * @throws Exception Configuration storing error
*/
@Override
public void storeChildren(PrintWriter aWriter, int indent, Object aObject,
diff --git a/java/org/apache/catalina/storeconfig/StandardServiceSF.java b/java/org/apache/catalina/storeconfig/StandardServiceSF.java
index d508f31..67b6e44 100644
--- a/java/org/apache/catalina/storeconfig/StandardServiceSF.java
+++ b/java/org/apache/catalina/storeconfig/StandardServiceSF.java
@@ -19,7 +19,7 @@ package org.apache.catalina.storeconfig;
import java.io.PrintWriter;
-import org.apache.catalina.Container;
+import org.apache.catalina.Engine;
import org.apache.catalina.Executor;
import org.apache.catalina.Lifecycle;
import org.apache.catalina.LifecycleListener;
@@ -32,12 +32,13 @@ import org.apache.catalina.core.StandardService;
public class StandardServiceSF extends StoreFactoryBase {
/**
- * Store Children from this StandardService description
+ * Store the specified service element children.
*
- * @param aWriter
- * @param indent
- * @param aService
- * @throws Exception
+ * @param aWriter Current output writer
+ * @param indent Indentation level
+ * @param aService Service to store
+ * @param parentDesc The element description
+ * @throws Exception Configuration storing error
*/
@Override
public void storeChildren(PrintWriter aWriter, int indent, Object aService,
@@ -56,18 +57,15 @@ public class StandardServiceSF extends StoreFactoryBase {
Connector connectors[] = service.findConnectors();
storeElementArray(aWriter, indent, connectors);
- // Store nested <Engine> element (or other appropriate container)
- Container container = service.getContainer();
+ // Store nested <Engine> element
+ Engine container = service.getContainer();
if (container != null) {
- StoreDescription elementDesc = getRegistry().findDescription(
- container.getClass());
+ StoreDescription elementDesc = getRegistry().findDescription(container.getClass());
if (elementDesc != null) {
IStoreFactory factory = elementDesc.getStoreFactory();
factory.store(aWriter, indent, container);
}
}
}
-
}
-
}
\ No newline at end of file
diff --git a/java/org/apache/catalina/storeconfig/StoreAppender.java b/java/org/apache/catalina/storeconfig/StoreAppender.java
index c626482..7a116df 100644
--- a/java/org/apache/catalina/storeconfig/StoreAppender.java
+++ b/java/org/apache/catalina/storeconfig/StoreAppender.java
@@ -44,11 +44,11 @@ public class StoreAppender {
private static int pos = 0;
/**
- * print the closing tag
+ * Print the closing tag.
*
- * @param aWriter
- * @param aDesc
- * @throws Exception
+ * @param aWriter The output writer
+ * @param aDesc Store description of the current element
+ * @throws Exception A store error occurred
*/
public void printCloseTag(PrintWriter aWriter, StoreDescription aDesc)
throws Exception {
@@ -58,13 +58,13 @@ public class StoreAppender {
}
/**
- * print only the open tag with all attributes
+ * Print only the open tag with all attributes.
*
- * @param aWriter
- * @param indent
- * @param bean
- * @param aDesc
- * @throws Exception
+ * @param aWriter The output writer
+ * @param indent Indentation level
+ * @param bean The current bean that is stored
+ * @param aDesc Store description of the current element
+ * @throws Exception A store error occurred
*/
public void printOpenTag(PrintWriter aWriter, int indent, Object bean,
StoreDescription aDesc) throws Exception {
@@ -78,11 +78,11 @@ public class StoreAppender {
/**
* Print tag with all attributes
*
- * @param aWriter
- * @param indent
- * @param bean
- * @param aDesc
- * @throws Exception
+ * @param aWriter The output writer
+ * @param indent Indentation level
+ * @param bean The current bean that is stored
+ * @param aDesc Store description of the current element
+ * @throws Exception A store error occurred
*/
public void printTag(PrintWriter aWriter, int indent, Object bean,
StoreDescription aDesc) throws Exception {
@@ -94,12 +94,12 @@ public class StoreAppender {
}
/**
- * print the value from tag as content
+ * Print the value from tag as content.
*
- * @param aWriter
- * @param tag
- * @param content
- * @throws Exception
+ * @param aWriter The output writer
+ * @param tag The element name
+ * @param content Element content
+ * @throws Exception A store error occurred
*/
public void printTagContent(PrintWriter aWriter, String tag, String content)
throws Exception {
@@ -113,12 +113,12 @@ public class StoreAppender {
}
/**
- * print an array of values
+ * Print an array of values.
*
- * @param aWriter
- * @param tag
- * @param indent
- * @param elements
+ * @param aWriter The output writer
+ * @param tag The element name
+ * @param indent Indentation level
+ * @param elements Array of element values
*/
public void printTagValueArray(PrintWriter aWriter, String tag, int indent,
String[] elements) {
@@ -141,12 +141,13 @@ public class StoreAppender {
}
/**
- * print a array of elements
+ * Print an array of elements.
*
- * @param aWriter
- * @param tag
- * @param indent
- * @param elements
+ * @param aWriter The output writer
+ * @param tag The element name
+ * @param indent Indentation level
+ * @param elements Array of elements
+ * @throws Exception Store error occurred
*/
public void printTagArray(PrintWriter aWriter, String tag, int indent,
String[] elements) throws Exception {
@@ -159,11 +160,10 @@ public class StoreAppender {
}
/**
- * Print some spaces
+ * Print some spaces.
*
- * @param aWriter
- * @param indent
- * number of spaces
+ * @param aWriter The output writer
+ * @param indent The number of spaces
*/
public void printIndent(PrintWriter aWriter, int indent) {
for (int i = 0; i < indent; i++) {
@@ -177,10 +177,11 @@ public class StoreAppender {
* <code>className</code> attribute defining the fully qualified Java
* class name of the bean.
*
- * @param writer
- * PrintWriter to which we are storing
+ * @param writer PrintWriter to which we are storing
+ * @param indent Indentation level
* @param bean
* Bean whose properties are to be rendered as attributes,
+ * @param desc Store description of the current element
*
* @exception Exception
* if an exception occurs while storing
@@ -195,8 +196,8 @@ public class StoreAppender {
/**
* Store the relevant attributes of the specified JavaBean.
*
- * @param writer
- * PrintWriter to which we are storing
+ * @param writer PrintWriter to which we are storing
+ * @param indent Indentation level
* @param include
* Should we include a <code>className</code> attribute?
* @param bean
@@ -271,13 +272,15 @@ public class StoreAppender {
}
/**
- * @param writer
- * @param indent
- * @param bean
- * @param desc
- * @param attributeName
- * @param bean2
- * @param value
+ * Store the specified of the specified JavaBean.
+ *
+ * @param writer PrintWriter to which we are storing
+ * @param indent Indentation level
+ * @param bean The current bean
+ * @param desc RegistryDescrpitor from this bean
+ * @param attributeName The attribute name to store
+ * @param bean2 A default instance of the bean for comparison
+ * @param value The attribute value
*/
protected void printAttribute(PrintWriter writer, int indent, Object bean, StoreDescription desc, String attributeName, Object bean2, Object value) {
if (isPrintValue(bean, bean2, attributeName, desc))
@@ -285,7 +288,7 @@ public class StoreAppender {
}
/**
- * print this Attribute value or not
+ * Determine if the attribute value needs to be stored.
*
* @param bean
* orginal bean
@@ -295,7 +298,7 @@ public class StoreAppender {
* attribute name
* @param desc
* StoreDescription from bean
- * @return True if it's a printing value
+ * @return <code>true</code> if the value should be stored
*/
public boolean isPrintValue(Object bean, Object bean2, String attrName,
StoreDescription desc) {
@@ -311,12 +314,12 @@ public class StoreAppender {
}
/**
- * generate default Instance
+ * Generate default Instance for the specified bean.
*
- * @param bean
+ * @param bean The bean
* @return an object from same class as bean parameter
- * @throws InstantiationException
- * @throws IllegalAccessException
+ * @throws InstantiationException Error creating a new instance
+ * @throws IllegalAccessException Another error occurred
*/
public Object defaultInstance(Object bean) throws InstantiationException,
IllegalAccessException {
@@ -324,11 +327,12 @@ public class StoreAppender {
}
/**
- * print an attribute value
+ * Print an attribute value.
*
- * @param writer
- * @param name
- * @param value
+ * @param writer PrintWriter to which we are storing
+ * @param indent Indentation level
+ * @param name Attribute name
+ * @param value Attribute value
*/
public void printValue(PrintWriter writer, int indent, String name,
Object value) {
@@ -356,6 +360,8 @@ public class StoreAppender {
/**
* Given a string, this method replaces all occurrences of '<', '>',
* '&', and '"'.
+ * @param input The string to escape
+ * @return the escaped string
*/
public String convertStr(String input) {
@@ -386,15 +392,16 @@ public class StoreAppender {
*
* @param clazz
* Java class to be tested
+ * @return <code>true</code> if the specified class should be stored
*/
protected boolean isPersistable(Class<?> clazz) {
for (int i = 0; i < persistables.length; i++) {
if (persistables[i] == clazz || persistables[i].isAssignableFrom(clazz)) {
- return (true);
+ return true;
}
}
- return (false);
+ return false;
}
}
diff --git a/java/org/apache/catalina/storeconfig/StoreConfig.java b/java/org/apache/catalina/storeconfig/StoreConfig.java
index 3cc881c..8a0fcad 100644
--- a/java/org/apache/catalina/storeconfig/StoreConfig.java
+++ b/java/org/apache/catalina/storeconfig/StoreConfig.java
@@ -49,7 +49,7 @@ public class StoreConfig implements IStoreConfig {
private Server server;
/**
- * get server.xml location
+ * Get server.xml location
*
* @return The server file name
*/
@@ -58,30 +58,25 @@ public class StoreConfig implements IStoreConfig {
}
/**
- * set new server.xml location
+ * Set new server.xml location.
*
- * @param string
+ * @param string The server.xml location
*/
public void setServerFilename(String string) {
serverFilename = string;
}
- /*
+ /**
* Get the StoreRegistry with all factory to generate the
- * server.xml/context.xml files
+ * server.xml/context.xml files.
*
- * @see org.apache.catalina.config.IStoreConfig#getRegistry()
+ * @see org.apache.catalina.storeconfig.IStoreConfig#getRegistry()
*/
@Override
public StoreRegistry getRegistry() {
return registry;
}
- /*
- * set StoreRegistry
- *
- * @see org.apache.catalina.config.IStoreConfig#setRegistry(org.apache.catalina.config.ConfigurationRegistry)
- */
@Override
public void setServer(Server aServer) {
server = aServer;
@@ -92,6 +87,11 @@ public class StoreConfig implements IStoreConfig {
return server;
}
+ /**
+ * set StoreRegistry
+ *
+ * @see org.apache.catalina.storeconfig.IStoreConfig#setRegistry(org.apache.catalina.storeconfig.StoreRegistry)
+ */
@Override
public void setRegistry(StoreRegistry aRegistry) {
registry = aRegistry;
@@ -106,14 +106,15 @@ public class StoreConfig implements IStoreConfig {
}
/**
- * Store Server from Object Name (Catalina:type=Server)
+ * Store Server from Object Name (Catalina:type=Server).
*
- * @param aServerName
- * Server ObjectName
- * @param backup
- * @param externalAllowed
- * s *
- * @throws MalformedObjectNameException
+ * @param aServerName Server ObjectName
+ * @param backup <code>true</code> to backup existing configuration files
+ * before rewriting them
+ * @param externalAllowed <code>true</code> to allow saving webapp
+ * configuration for webapps that are not inside the host's app
+ * directory
+ * @throws MalformedObjectNameException Bad MBean name
*/
public synchronized void storeServer(String aServerName, boolean backup,
boolean externalAllowed) throws MalformedObjectNameException {
@@ -157,13 +158,15 @@ public class StoreConfig implements IStoreConfig {
}
/**
- * Store a Context from ObjectName
+ * Store a Context from ObjectName.
*
- * @param aContextName
- * MBean ObjectName
- * @param backup
- * @param externalAllowed
- * @throws MalformedObjectNameException
+ * @param aContextName MBean ObjectName
+ * @param backup <code>true</code> to backup existing configuration files
+ * before rewriting them
+ * @param externalAllowed <code>true</code> to allow saving webapp
+ * configuration for webapps that are not inside the host's app
+ * directory
+ * @throws MalformedObjectNameException Bad MBean name
*/
public synchronized void storeContext(String aContextName, boolean backup,
boolean externalAllowed) throws MalformedObjectNameException {
@@ -224,6 +227,8 @@ public class StoreConfig implements IStoreConfig {
* Write the configuration information for this entire <code>Server</code>
* out to the server.xml configuration file.
*
+ * @param aServer Server instance
+ * @return <code>true</code> if the store operation was successful
*/
@Override
public synchronized boolean store(Server aServer) {
@@ -243,10 +248,8 @@ public class StoreConfig implements IStoreConfig {
return false;
}
- /*
- * (non-Javadoc)
- *
- * @see org.apache.catalina.config.IStoreConfig#store(org.apache.catalina.Context)
+ /**
+ * @see org.apache.catalina.storeconfig.IStoreConfig#store(org.apache.catalina.Context)
*/
@Override
public synchronized boolean store(Context aContext) {
@@ -274,10 +277,8 @@ public class StoreConfig implements IStoreConfig {
return false;
}
- /*
- * (non-Javadoc)
- *
- * @see org.apache.catalina.config.IStoreConfig#store(java.io.PrintWriter,
+ /**
+ * @see org.apache.catalina.storeconfig.IStoreConfig#store(java.io.PrintWriter,
* int, org.apache.catalina.Context)
*/
@Override
@@ -296,10 +297,8 @@ public class StoreConfig implements IStoreConfig {
}
}
- /*
- * (non-Javadoc)
- *
- * @see org.apache.catalina.config.IStoreConfig#store(java.io.PrintWriter,
+ /**
+ * @see org.apache.catalina.storeconfig.IStoreConfig#store(java.io.PrintWriter,
* int, org.apache.catalina.Host)
*/
@Override
@@ -310,10 +309,8 @@ public class StoreConfig implements IStoreConfig {
desc.getStoreFactory().store(aWriter, indent, aHost);
}
- /*
- * (non-Javadoc)
- *
- * @see org.apache.catalina.config.IStoreConfig#store(java.io.PrintWriter,
+ /**
+ * @see org.apache.catalina.storeconfig.IStoreConfig#store(java.io.PrintWriter,
* int, org.apache.catalina.Service)
*/
@Override
@@ -325,12 +322,8 @@ public class StoreConfig implements IStoreConfig {
}
/**
- * Store the state of this Server MBean (which will recursively store
- * everything)
- *
- * @param writer
- * @param indent
- * @param aServer
+ * @see org.apache.catalina.storeconfig.IStoreConfig#store(java.io.PrintWriter,
+ * int, org.apache.catalina.Server)
*/
@Override
public void store(PrintWriter writer, int indent,
diff --git a/java/org/apache/catalina/storeconfig/StoreConfigLifecycleListener.java b/java/org/apache/catalina/storeconfig/StoreConfigLifecycleListener.java
index 8451dbc..0ef487e 100644
--- a/java/org/apache/catalina/storeconfig/StoreConfigLifecycleListener.java
+++ b/java/org/apache/catalina/storeconfig/StoreConfigLifecycleListener.java
@@ -38,8 +38,7 @@ import org.apache.tomcat.util.res.StringManager;
public class StoreConfigLifecycleListener implements LifecycleListener {
private static Log log = LogFactory.getLog(StoreConfigLifecycleListener.class);
- private static StringManager sm =
- StringManager.getManager(StoreConfigLifecycleListener.class.getName());
+ private static StringManager sm = StringManager.getManager(StoreConfigLifecycleListener.class);
/**
* The configuration information registry for our managed beans.
@@ -54,8 +53,8 @@ public class StoreConfigLifecycleListener implements LifecycleListener {
private String storeRegistry = null;
private ObjectName oname = null;
- /*
- * register StoreRegistry after Start the complete Server
+ /**
+ * Register StoreRegistry after Start the complete Server.
*
* @see org.apache.catalina.LifecycleListener#lifecycleEvent(org.apache.catalina.LifecycleEvent)
*/
@@ -76,8 +75,9 @@ public class StoreConfigLifecycleListener implements LifecycleListener {
}
/**
- * create StoreConfig MBean and load StoreRgistry MBeans name is
- * <i>Catalina:type=StoreConfig </i>
+ * Create StoreConfig MBean and load StoreRgistry MBeans name is
+ * <code>Catalina:type=StoreConfig</code>.
+ * @param server The Server instance
*/
protected void createMBean(Server server) {
StoreLoader loader = new StoreLoader();
@@ -108,11 +108,11 @@ public class StoreConfigLifecycleListener implements LifecycleListener {
}
/**
- * Create a ManagedBean (StoreConfig)
+ * Create a ManagedBean (StoreConfig).
*
- * @param object
- * @return The bean
- * @throws Exception
+ * @param object The object to manage
+ * @return an MBean wrapping the object
+ * @throws Exception if an error occurred
*/
protected DynamicMBean getManagedBean(Object object) throws Exception {
ManagedBean managedBean = registry.findManagedBean("StoreConfig");
@@ -120,7 +120,7 @@ public class StoreConfigLifecycleListener implements LifecycleListener {
}
/**
- * @return Returns the storeConfig.
+ * @return the store config instance
*/
public IStoreConfig getStoreConfig() {
return storeConfig;
@@ -135,7 +135,7 @@ public class StoreConfigLifecycleListener implements LifecycleListener {
}
/**
- * @return Returns the storeConfigClass.
+ * @return the main store config class name
*/
public String getStoreConfigClass() {
return storeConfigClass;
@@ -150,7 +150,7 @@ public class StoreConfigLifecycleListener implements LifecycleListener {
}
/**
- * @return Returns the storeRegistry.
+ * @return the store registry
*/
public String getStoreRegistry() {
return storeRegistry;
diff --git a/java/org/apache/catalina/storeconfig/StoreContextAppender.java b/java/org/apache/catalina/storeconfig/StoreContextAppender.java
index 348bc44..b484727 100644
--- a/java/org/apache/catalina/storeconfig/StoreContextAppender.java
+++ b/java/org/apache/catalina/storeconfig/StoreContextAppender.java
@@ -30,13 +30,8 @@ import org.apache.catalina.core.StandardHost;
public class StoreContextAppender extends StoreAppender {
/**
- * @param writer
- * @param indent
- * @param bean
- * @param desc
- * @param attributeName
- * @param bean2
- * @param value
+ * {@inheritDoc}
+ * Adds special handling for <code>docBase</code>.
*/
@Override
protected void printAttribute(PrintWriter writer, int indent, Object bean, StoreDescription desc, String attributeName, Object bean2, Object value) {
@@ -52,14 +47,14 @@ public class StoreContextAppender extends StoreAppender {
}
}
- /*
- * Print Context Values. <ul><li> Spezial handling to default workDir.
+ /**
+ * Print Context Values. <ul><li> Special handling to default workDir.
* </li><li> Don't save path at external context.xml </li><li> Don't
* generate docBase for host.appBase webapps <LI></ul>
*
- * @see org.apache.catalina.config.StoreAppender#isPrintValue(java.lang.Object,
+ * @see org.apache.catalina.storeconfig.StoreAppender#isPrintValue(java.lang.Object,
* java.lang.Object, java.lang.String,
- * org.apache.catalina.config.StoreDescription)
+ * org.apache.catalina.storeconfig.StoreDescription)
*/
@Override
public boolean isPrintValue(Object bean, Object bean2, String attrName,
@@ -123,7 +118,7 @@ public class StoreContextAppender extends StoreAppender {
/**
* Make default Work Dir.
*
- * @param context
+ * @param context The context
* @return The default working directory for the context.
*/
protected String getDefaultWorkDir(StandardContext context) {
@@ -150,11 +145,11 @@ public class StoreContextAppender extends StoreAppender {
return defaultWorkDir;
}
- /*
+ /**
* Generate a real default StandardContext TODO read and interpret the
* default context.xml and context.xml.default TODO Cache a Default
* StandardContext ( with reloading strategy) TODO remove really all
- * elements, but detection is hard... To Listener or Valve from same class?>
+ * elements, but detection is hard... To Listener or Valve from same class?
*
* @see org.apache.catalina.storeconfig.StoreAppender#defaultInstance(java.lang.Object)
*/
diff --git a/java/org/apache/catalina/storeconfig/StoreDescription.java b/java/org/apache/catalina/storeconfig/StoreDescription.java
index 95fb7eb..4bb23b3 100644
--- a/java/org/apache/catalina/storeconfig/StoreDescription.java
+++ b/java/org/apache/catalina/storeconfig/StoreDescription.java
@@ -285,9 +285,9 @@ public class StoreDescription {
}
/**
- * is child transient, please don't save this.
+ * Is child transient, please don't save this.
*
- * @param classname
+ * @param classname The class name to check
* @return is classname attribute?
*/
public boolean isTransientChild(String classname) {
@@ -297,9 +297,9 @@ public class StoreDescription {
}
/**
- * is attribute transient, please don't save this.
+ * Is attribute transient, please don't save this.
*
- * @param attribute
+ * @param attribute The attribute name to check
* @return is transient attribute?
*/
public boolean isTransientAttribute(String attribute) {
diff --git a/java/org/apache/catalina/storeconfig/StoreFactoryBase.java b/java/org/apache/catalina/storeconfig/StoreFactoryBase.java
index de6b66a..159c8c2 100644
--- a/java/org/apache/catalina/storeconfig/StoreFactoryBase.java
+++ b/java/org/apache/catalina/storeconfig/StoreFactoryBase.java
@@ -47,7 +47,7 @@ public class StoreFactoryBase implements IStoreFactory {
private static final String info = "org.apache.catalina.config.StoreFactoryBase/1.0";
/**
- * Return descriptive information about this Facotry implementation and the
+ * @return descriptive information about this Facotry implementation and the
* corresponding version number, in the format
* <code><description>/<version></code>.
*/
@@ -74,10 +74,10 @@ public class StoreFactoryBase implements IStoreFactory {
this.storeAppender = storeAppender;
}
- /*
- * set Registry
+ /**
+ * Set Registry
*
- * @see org.apache.catalina.config.IStoreFactory#setRegistry(org.apache.catalina.config.ConfigurationRegistry)
+ * @see org.apache.catalina.storeconfig.IStoreFactory#setRegistry(org.apache.catalina.storeconfig.StoreRegistry)
*/
@Override
public void setRegistry(StoreRegistry aRegistry) {
@@ -85,10 +85,10 @@ public class StoreFactoryBase implements IStoreFactory {
}
- /*
+ /**
* get Registry
*
- * @see org.apache.catalina.config.IStoreFactory#getRegistry()
+ * @see org.apache.catalina.storeconfig.IStoreFactory#getRegistry()
*/
@Override
public StoreRegistry getRegistry() {
@@ -104,7 +104,7 @@ public class StoreFactoryBase implements IStoreFactory {
aWriter.println("\"?>");
}
- /*
+ /**
* Store a server.xml element with attributes and children
*
* @see org.apache.catalina.storeconfig.IStoreFactory#store(java.io.PrintWriter,
@@ -138,12 +138,13 @@ public class StoreFactoryBase implements IStoreFactory {
}
/**
- * Must Implement at subclass for sepzial store children handling
+ * Must Implement at subclass for custom store children handling.
*
- * @param aWriter
- * @param indent
- * @param aElement
- * @param elementDesc
+ * @param aWriter Current output writer
+ * @param indent Indentation level
+ * @param aElement Current element
+ * @param elementDesc The element description
+ * @throws Exception Configuration storing error
*/
public void storeChildren(PrintWriter aWriter, int indent, Object aElement,
StoreDescription elementDesc) throws Exception {
@@ -153,10 +154,10 @@ public class StoreFactoryBase implements IStoreFactory {
* Store only elements from storeChildren methods that are not a transient
* child.
*
- * @param aWriter current output writer
- * @param indent indentation level
- * @param aTagElement current tomcat element
- * @throws Exception
+ * @param aWriter Current output writer
+ * @param indent Indentation level
+ * @param aTagElement Current element
+ * @throws Exception Configuration storing error
*/
protected void storeElement(PrintWriter aWriter, int indent,
Object aTagElement) throws Exception {
@@ -176,11 +177,12 @@ public class StoreFactoryBase implements IStoreFactory {
}
}
- /*
- * Save a array of elements
- * @param aWriter current output writer
- * @param indent indentation level
- * @param elements list of child elments to store!
+ /**
+ * Save a array of elements.
+ * @param aWriter Current output writer
+ * @param indent Indentation level
+ * @param elements Array of elements
+ * @throws Exception Configuration storing error
*/
protected void storeElementArray(PrintWriter aWriter, int indent,
Object[] elements) throws Exception {
diff --git a/java/org/apache/catalina/storeconfig/StoreFactoryRule.java b/java/org/apache/catalina/storeconfig/StoreFactoryRule.java
index 9029dec..d0016b5 100644
--- a/java/org/apache/catalina/storeconfig/StoreFactoryRule.java
+++ b/java/org/apache/catalina/storeconfig/StoreFactoryRule.java
@@ -41,6 +41,9 @@ public class StoreFactoryRule extends Rule {
* @param attributeName
* Name of the attribute that optionally includes an override
* name of the IStoreFactory class
+ * @param storeAppenderClass The store appender class
+ * @param appenderAttributeName The attribute name for the store
+ * appender class
*/
public StoreFactoryRule(String storeFactoryClass, String attributeName,
String storeAppenderClass, String appenderAttributeName) {
@@ -74,11 +77,10 @@ public class StoreFactoryRule extends Rule {
/**
* Handle the beginning of an XML element.
*
- * @param attributes
- * The attributes of this element
- *
- * @exception Exception
- * if a processing error occurs
+ * @param namespace XML namespace
+ * @param name The element name
+ * @param attributes The attributes of this element
+ * @exception Exception if a processing error occurs
*/
@Override
public void begin(String namespace, String name, Attributes attributes)
@@ -99,15 +101,15 @@ public class StoreFactoryRule extends Rule {
}
/**
- * create new instance from attribte className!
+ * Create new instance from attribute className!
*
* @param attr class Name attribute
* @param defaultName Default Class
* @param attributes current digester attribute elements
- * @return new config object instance
- * @throws ClassNotFoundException
- * @throws InstantiationException
- * @throws IllegalAccessException
+ * @return new configured object instance
+ * @throws ClassNotFoundException Class was not found
+ * @throws InstantiationException Error creating an instance
+ * @throws IllegalAccessException Security exception
*/
protected Object newInstance(String attr, String defaultName,
Attributes attributes) throws ClassNotFoundException,
diff --git a/java/org/apache/catalina/storeconfig/StoreFileMover.java b/java/org/apache/catalina/storeconfig/StoreFileMover.java
index 03730c9..99a5c24 100644
--- a/java/org/apache/catalina/storeconfig/StoreFileMover.java
+++ b/java/org/apache/catalina/storeconfig/StoreFileMover.java
@@ -86,7 +86,7 @@ public class StoreFileMover {
}
/**
- * @param string
+ * @param string The file name
*/
public void setFilename(String string) {
filename = string;
@@ -100,7 +100,7 @@ public class StoreFileMover {
}
/**
- * @param string
+ * @param string The encoding
*/
public void setEncoding(String string) {
encoding = string;
@@ -108,6 +108,9 @@ public class StoreFileMover {
/**
* Calculate file objects for the old and new configuration files.
+ * @param basename The base path
+ * @param encoding The encoding of the file
+ * @param filename The file name
*/
public StoreFileMover(String basename, String filename, String encoding) {
setBasename(basename);
@@ -124,7 +127,7 @@ public class StoreFileMover {
}
/**
- * generate the Filename to new with TimeStamp
+ * Generate the Filename to new with TimeStamp.
*/
public void init() {
String configFile = getFilename();
@@ -147,9 +150,9 @@ public class StoreFileMover {
}
/**
- * Shuffle old->save and new->old
+ * Shuffle old->save and new->old.
*
- * @throws IOException
+ * @throws IOException a file operation error occurred
*/
public void move() throws IOException {
if (configOld.renameTo(configSave)) {
@@ -175,10 +178,10 @@ public class StoreFileMover {
}
/**
- * Open an output writer for the new configuration file
+ * Open an output writer for the new configuration file.
*
* @return The writer
- * @throws IOException
+ * @throws IOException Failed opening a writer to the new file
*/
public PrintWriter getWriter() throws IOException {
return new PrintWriter(new OutputStreamWriter(
@@ -186,7 +189,7 @@ public class StoreFileMover {
}
/**
- * Time value for backup yyyy-mm-dd.hh-mm-ss
+ * Time value for backup yyyy-mm-dd.hh-mm-ss.
*
* @return The time
*/
diff --git a/java/org/apache/catalina/storeconfig/StoreLoader.java b/java/org/apache/catalina/storeconfig/StoreLoader.java
index a3506d8..48cd059 100644
--- a/java/org/apache/catalina/storeconfig/StoreLoader.java
+++ b/java/org/apache/catalina/storeconfig/StoreLoader.java
@@ -78,7 +78,7 @@ public class StoreLoader {
/**
* The <code>Digester</code> instance used to parse registry descriptors.
*/
- protected static Digester digester = createDigester();
+ protected static final Digester digester = createDigester();
private StoreRegistry registry;
@@ -102,6 +102,7 @@ public class StoreLoader {
/**
* Create and configure the Digester we will be using for setup store
* registry.
+ * @return the XML digester that will be used to parse the configuration
*/
protected static Digester createDigester() {
long t1 = System.currentTimeMillis();
@@ -139,9 +140,11 @@ public class StoreLoader {
}
/**
- *
- * @param aFile
- * @return The server file
+ * Find main configuration file.
+ * @param aFile File name, absolute or relative
+ * to <code>${catalina.base}/conf</code>, if not specified
+ * <code>server-registry.xml</code> is used
+ * @return The file
*/
protected File serverFile(String aFile) {
@@ -160,9 +163,9 @@ public class StoreLoader {
}
/**
- * Load Description from external source
+ * Load main configuration file from external source.
*
- * @param aURL
+ * @param aURL URL to the configuration file
*/
public void load(String aURL) {
synchronized (digester) {
@@ -183,7 +186,7 @@ public class StoreLoader {
* Load from defaults
* <ul>
* <li>System Property URL catalina.storeregistry</li>
- * <li>File $catalina.base/conf/server-registry.xml</li>
+ * <li>File ${catalina.base}/conf/server-registry.xml</li>
* <li>class resource org/apache/catalina/storeconfig/server-registry.xml
* </li>
* </ul>
@@ -191,7 +194,6 @@ public class StoreLoader {
public void load() {
InputStream is = null;
- Throwable error = null;
registryResource = null ;
try {
String configUrl = getConfigUrl();
@@ -223,9 +225,8 @@ public class StoreLoader {
try {
is = StoreLoader.class
.getResourceAsStream("/org/apache/catalina/storeconfig/server-registry.xml");
- if (log.isInfoEnabled())
- log
- .info("Find registry server-registry.xml at classpath resource");
+ if (log.isDebugEnabled())
+ log.debug("Find registry server-registry.xml at classpath resource");
registryResource = StoreLoader.class
.getResource("/org/apache/catalina/storeconfig/server-registry.xml");
@@ -239,7 +240,7 @@ public class StoreLoader {
registry = (StoreRegistry) digester.parse(is);
}
} catch (Throwable t) {
- error = t;
+ log.error(t);
} finally {
try {
is.close();
@@ -247,13 +248,13 @@ public class StoreLoader {
}
}
}
- if ((is == null) || (error != null)) {
- log.error(error);
+ if (is == null) {
+ log.error("Failed to load server-registry.xml");
}
}
/**
- * Get the value of the catalina.home environment variable.
+ * @return the catalina.home environment variable.
*/
private static String getCatalinaHome() {
return System.getProperty("catalina.home", System
@@ -261,21 +262,21 @@ public class StoreLoader {
}
/**
- * Get the value of the catalina.base environment variable.
+ * @return the catalina.base environment variable.
*/
private static String getCatalinaBase() {
return System.getProperty("catalina.base", getCatalinaHome());
}
/**
- * Get the value of the configuration URL.
+ * @return the configuration URL.
*/
private static String getConfigUrl() {
return System.getProperty("catalina.storeconfig");
}
/**
- * @return Returns the registryResource.
+ * @return the registryResource.
*/
public URL getRegistryResource() {
return registryResource;
diff --git a/java/org/apache/catalina/storeconfig/StoreRegistry.java b/java/org/apache/catalina/storeconfig/StoreRegistry.java
index 112fcc0..d7e21a0 100644
--- a/java/org/apache/catalina/storeconfig/StoreRegistry.java
+++ b/java/org/apache/catalina/storeconfig/StoreRegistry.java
@@ -40,6 +40,7 @@ import org.apache.catalina.tribes.Member;
import org.apache.catalina.tribes.MembershipService;
import org.apache.catalina.tribes.MessageListener;
import org.apache.catalina.tribes.transport.DataSender;
+import org.apache.coyote.UpgradeProtocol;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.http.CookieProcessor;
@@ -66,7 +67,8 @@ public class StoreRegistry {
Valve.class, ClusterListener.class, MessageListener.class,
DataSender.class, ChannelInterceptor.class, Member.class,
WebResourceRoot.class, WebResourceSet.class,
- CredentialHandler.class, CookieProcessor.class };
+ CredentialHandler.class, UpgradeProtocol.class,
+ CookieProcessor.class };
/**
* @return Returns the name.
@@ -102,7 +104,7 @@ public class StoreRegistry {
* Find a description for id. Handle interface search when no direct match
* found.
*
- * @param id
+ * @param id The class name
* @return The description
*/
public StoreDescription findDescription(String id) {
@@ -136,9 +138,9 @@ public class StoreRegistry {
}
/**
- * Find Description by class
+ * Find Description by class.
*
- * @param aClass
+ * @param aClass The class
* @return The description
*/
public StoreDescription findDescription(Class<?> aClass) {
@@ -146,9 +148,9 @@ public class StoreRegistry {
}
/**
- * Find factory from classname
+ * Find factory from class name.
*
- * @param aClassName
+ * @param aClassName The class name
* @return The factory
*/
public IStoreFactory findStoreFactory(String aClassName) {
@@ -161,9 +163,9 @@ public class StoreRegistry {
}
/**
- * find factory from class
+ * Find factory from class.
*
- * @param aClass
+ * @param aClass The class
* @return The factory
*/
public IStoreFactory findStoreFactory(Class<?> aClass) {
@@ -171,9 +173,9 @@ public class StoreRegistry {
}
/**
- * Register a new description
+ * Register a new description.
*
- * @param desc
+ * @param desc New description
*/
public void registerDescription(StoreDescription desc) {
String key = desc.getId();
@@ -185,6 +187,12 @@ public class StoreRegistry {
+ "#" + desc.getTagClass());
}
+ /**
+ * Unregister a description.
+ *
+ * @param desc The description
+ * @return the description, or <code>null</code> if it was not registered
+ */
public StoreDescription unregisterDescription(StoreDescription desc) {
String key = desc.getId();
if (key == null || "".equals(key))
@@ -202,7 +210,8 @@ public class StoreRegistry {
}
/**
- * @param string
+ * Set the encoding to use when writing the configuration files.
+ * @param string The encoding
*/
public void setEncoding(String string) {
encoding = string;
diff --git a/java/org/apache/catalina/storeconfig/mbeans-descriptors.xml b/java/org/apache/catalina/storeconfig/mbeans-descriptors.xml
index eb97e0d..bec2fc2 100644
--- a/java/org/apache/catalina/storeconfig/mbeans-descriptors.xml
+++ b/java/org/apache/catalina/storeconfig/mbeans-descriptors.xml
@@ -1,4 +1,4 @@
-<?xml version="1.0"?>
+<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
diff --git a/java/org/apache/catalina/storeconfig/server-registry.xml b/java/org/apache/catalina/storeconfig/server-registry.xml
index a4d33e1..bac0dea 100644
--- a/java/org/apache/catalina/storeconfig/server-registry.xml
+++ b/java/org/apache/catalina/storeconfig/server-registry.xml
@@ -91,7 +91,7 @@
tagClass="org.apache.catalina.deploy.NamingResourcesImpl"
storeFactoryClass="org.apache.catalina.storeconfig.GlobalNamingResourcesSF">
</Description>
- <Description
+ <Description
tag="Connector"
standard="true"
default="true"
@@ -101,7 +101,32 @@
storeAppenderClass="org.apache.catalina.storeconfig.ConnectorStoreAppender">
<TransientAttribute>URIEncoding</TransientAttribute>
<TransientAttribute>maxProcessor</TransientAttribute>
- <TransientAttribute>minProcessor</TransientAttribute>
+ <TransientAttribute>minProcessor</TransientAttribute>
+ </Description>
+ <Description
+ tag="UpgradeProtocol"
+ standard="false"
+ default="false"
+ tagClass="org.apache.coyote.UpgradeProtocol"
+ children="false"
+ storeFactoryClass="org.apache.catalina.storeconfig.StoreFactoryBase">
+ </Description>
+ <Description
+ tag="SSLHostConfig"
+ standard="true"
+ default="true"
+ tagClass="org.apache.tomcat.util.net.SSLHostConfig"
+ children="true"
+ storeFactoryClass="org.apache.catalina.storeconfig.SSLHostConfigSF">
+ <TransientAttribute>openSslContext</TransientAttribute>
+ </Description>
+ <Description
+ tag="Certificate"
+ standard="true"
+ default="true"
+ tagClass="org.apache.tomcat.util.net.SSLHostConfigCertificate"
+ children="false"
+ storeFactoryClass="org.apache.catalina.storeconfig.StoreFactoryBase">
</Description>
<Description
tag="NamingResources"
@@ -119,7 +144,6 @@
children="true"
tagClass="org.apache.catalina.Manager"
storeFactoryClass="org.apache.catalina.storeconfig.ManagerSF">
- <TransientAttribute>distributable</TransientAttribute>
<TransientAttribute>domain</TransientAttribute>
</Description>
<Description
@@ -129,7 +153,6 @@
children="true"
tagClass="org.apache.catalina.session.PersistentManager"
storeFactoryClass="org.apache.catalina.storeconfig.PersistentManagerSF">
- <TransientAttribute>distributable</TransientAttribute>
</Description>
<Description
tag="Store"
@@ -185,6 +208,7 @@
default="false"
tagClass="org.apache.catalina.loader.WebappLoader"
storeFactoryClass="org.apache.catalina.storeconfig.LoaderSF">
+ <TransientAttribute>domain</TransientAttribute>
</Description>
<Description
tag="Listener"
@@ -317,14 +341,6 @@
storeFactoryClass="org.apache.catalina.storeconfig.StoreFactoryBase">
</Description>
<Description
- id="org.apache.catalina.core.StandardContext.[InstanceListener]"
- tag="InstanceListener"
- standard="true"
- default="false"
- attributes="false"
- storeFactoryClass="org.apache.catalina.storeconfig.InstanceListenerSF">
- </Description>
- <Description
id="org.apache.catalina.core.StandardContext.[WrapperLifecycle]"
tag="WrapperLifecycle"
standard="true"
diff --git a/java/org/apache/catalina/tribes/ByteMessage.java b/java/org/apache/catalina/tribes/ByteMessage.java
index 91e6be8..e300e74 100644
--- a/java/org/apache/catalina/tribes/ByteMessage.java
+++ b/java/org/apache/catalina/tribes/ByteMessage.java
@@ -77,7 +77,7 @@ public class ByteMessage implements Externalizable {
/**
* @see java.io.Externalizable#readExternal
* @param in ObjectInput
- * @throws IOException
+ * @throws IOException An IO error occurred
*/
@Override
public void readExternal(ObjectInput in ) throws IOException {
@@ -89,7 +89,7 @@ public class ByteMessage implements Externalizable {
/**
* @see java.io.Externalizable#writeExternal
* @param out ObjectOutput
- * @throws IOException
+ * @throws IOException An IO error occurred
*/
@Override
public void writeExternal(ObjectOutput out) throws IOException {
diff --git a/java/org/apache/catalina/tribes/Channel.java b/java/org/apache/catalina/tribes/Channel.java
index ca84ff5..e3f51bc 100644
--- a/java/org/apache/catalina/tribes/Channel.java
+++ b/java/org/apache/catalina/tribes/Channel.java
@@ -246,13 +246,14 @@ public interface Channel {
/**
* Send a message to one or more members in the cluster
- * @param destination Member[] - the destinations, can not be null or zero length, the reason for that
+ * @param destination Member[] - the destinations, cannot be null or zero length, the reason for that
* is that a membership change can occur and at that time the application is uncertain what group the message
* actually got sent to.
* @param msg Serializable - the message to send, has to be serializable, or a <code>ByteMessage</code> to
* send a pure byte array
* @param options int - sender options, see class documentation for each interceptor that is configured in order to trigger interceptors
* @return a unique Id that identifies the message that is sent
+ * @throws ChannelException if a serialization error happens.
* @see ByteMessage
* @see #SEND_OPTIONS_USE_ACK
* @see #SEND_OPTIONS_ASYNCHRONOUS
@@ -356,5 +357,16 @@ public interface Channel {
*/
public Member getMember(Member mbr);
+ /**
+ * Return the name of this channel.
+ * @return channel name
+ */
+ public String getName();
+
+ /**
+ * Set the name of this channel
+ * @param name The new channel name
+ */
+ public void setName(String name);
}
diff --git a/java/org/apache/catalina/tribes/ChannelException.java b/java/org/apache/catalina/tribes/ChannelException.java
index 544eefb..c81a39c 100644
--- a/java/org/apache/catalina/tribes/ChannelException.java
+++ b/java/org/apache/catalina/tribes/ChannelException.java
@@ -33,7 +33,7 @@ public class ChannelException extends Exception {
* Empty list to avoid reinstatiating lists
*/
protected static final FaultyMember[] EMPTY_LIST = new FaultyMember[0];
- /*
+ /**
* Holds a list of faulty members
*/
private ArrayList<FaultyMember> faultyMembers=null;
@@ -48,6 +48,7 @@ public class ChannelException extends Exception {
/**
* Constructor, creates a ChannelException with an error message
+ * @param message The error message
* @see java.lang.Exception#Exception(String)
*/
public ChannelException(String message) {
@@ -56,7 +57,7 @@ public class ChannelException extends Exception {
/**
* Constructor, creates a ChannelException with an error message and a cause
- * @param message String
+ * @param message The error message
* @param cause Throwable
* @see java.lang.Exception#Exception(String,Throwable)
*/
@@ -75,7 +76,7 @@ public class ChannelException extends Exception {
/**
* Returns the message for this exception
- * @return String
+ * @return the error message
* @see java.lang.Exception#getMessage()
*/
@Override
@@ -98,6 +99,7 @@ public class ChannelException extends Exception {
* Adds a faulty member, and the reason the member failed.
* @param mbr Member
* @param x Exception
+ * @return <code>true</code> if the member was added
*/
public boolean addFaultyMember(Member mbr, Exception x ) {
return addFaultyMember(new FaultyMember(mbr,x));
@@ -106,6 +108,7 @@ public class ChannelException extends Exception {
/**
* Adds a list of faulty members
* @param mbrs FaultyMember[]
+ * @return the number of members added
*/
public int addFaultyMember(FaultyMember[] mbrs) {
int result = 0;
@@ -118,6 +121,7 @@ public class ChannelException extends Exception {
/**
* Adds a faulty member
* @param mbr FaultyMember
+ * @return <code>true</code> if the member was added
*/
public boolean addFaultyMember(FaultyMember mbr) {
if ( this.faultyMembers==null ) this.faultyMembers = new ArrayList<>();
diff --git a/java/org/apache/catalina/tribes/ChannelInterceptor.java b/java/org/apache/catalina/tribes/ChannelInterceptor.java
index 28f86e4..aad9a8f 100644
--- a/java/org/apache/catalina/tribes/ChannelInterceptor.java
+++ b/java/org/apache/catalina/tribes/ChannelInterceptor.java
@@ -86,7 +86,7 @@ public interface ChannelInterceptor extends MembershipListener, Heartbeat {
* @param destination Member[] - the destination for this message
* @param msg ChannelMessage - the message to be sent
* @param payload InterceptorPayload - the payload, carrying an error handler and future useful data, can be null
- * @throws ChannelException
+ * @throws ChannelException if a serialization error happens.
* @see ErrorHandler
* @see InterceptorPayload
*/
@@ -168,6 +168,18 @@ public interface ChannelInterceptor extends MembershipListener, Heartbeat {
public void fireInterceptorEvent(InterceptorEvent event);
+ /**
+ * Return the channel that is related to this interceptor
+ * @return Channel
+ */
+ public Channel getChannel();
+
+ /**
+ * Set the channel that is related to this interceptor
+ * @param channel The channel
+ */
+ public void setChannel(Channel channel);
+
interface InterceptorEvent {
int getEventType();
String getEventTypeDesc();
diff --git a/java/org/apache/catalina/tribes/ChannelReceiver.java b/java/org/apache/catalina/tribes/ChannelReceiver.java
index 4484c6c..0ae6479 100644
--- a/java/org/apache/catalina/tribes/ChannelReceiver.java
+++ b/java/org/apache/catalina/tribes/ChannelReceiver.java
@@ -29,7 +29,7 @@ public interface ChannelReceiver extends Heartbeat {
/**
* Start listening for incoming messages on the host/port
- * @throws java.io.IOException
+ * @throws java.io.IOException Listen failed
*/
public void start() throws java.io.IOException;
@@ -78,4 +78,16 @@ public interface ChannelReceiver extends Heartbeat {
*/
public MessageListener getMessageListener();
+ /**
+ * Return the channel that is related to this ChannelReceiver
+ * @return Channel
+ */
+ public Channel getChannel();
+
+ /**
+ * Set the channel that is related to this ChannelReceiver
+ * @param channel The channel
+ */
+ public void setChannel(Channel channel);
+
}
diff --git a/java/org/apache/catalina/tribes/ChannelSender.java b/java/org/apache/catalina/tribes/ChannelSender.java
index 5e14cf1..2d33b50 100644
--- a/java/org/apache/catalina/tribes/ChannelSender.java
+++ b/java/org/apache/catalina/tribes/ChannelSender.java
@@ -68,4 +68,17 @@ public interface ChannelSender extends Heartbeat
* @see ChannelException#addFaultyMember(Member,java.lang.Exception)
*/
public void sendMessage(ChannelMessage message, Member[] destination) throws ChannelException;
+
+ /**
+ * Return the channel that is related to this ChannelSender
+ * @return Channel
+ */
+ public Channel getChannel();
+
+ /**
+ * Set the channel that is related to this ChannelSender
+ * @param channel The channel
+ */
+ public void setChannel(Channel channel);
+
}
diff --git a/java/org/apache/catalina/tribes/Member.java b/java/org/apache/catalina/tribes/Member.java
index 4dccacc..11484eb 100644
--- a/java/org/apache/catalina/tribes/Member.java
+++ b/java/org/apache/catalina/tribes/Member.java
@@ -36,7 +36,7 @@ public interface Member {
public static final byte[] SHUTDOWN_PAYLOAD = new byte[] {66, 65, 66, 89, 45, 65, 76, 69, 88};
/**
- * Returns the name of this node, should be unique within the group.
+ * @return the name of this node, should be unique within the group.
*/
public String getName();
@@ -129,6 +129,7 @@ public interface Member {
* Highly optimized version of serializing a member into a byte array
* Returns a cached byte[] reference, do not modify this data
* @param getalive calculate memberAlive time
+ * @return the data as a byte array
*/
public byte[] getData(boolean getalive);
@@ -137,12 +138,21 @@ public interface Member {
* Returns a cached byte[] reference, do not modify this data
* @param getalive calculate memberAlive time
* @param reset reset the cached data package, and create a new one
+ * @return the data as a byte array
*/
public byte[] getData(boolean getalive, boolean reset);
/**
* Length of a message obtained by {@link #getData(boolean)} or
* {@link #getData(boolean, boolean)}.
+ * @return the data length
*/
public int getDataLength();
+
+ /**
+ * @return boolean - true if the member is local member
+ */
+ public boolean isLocal();
+
+ public void setLocal(boolean local);
}
diff --git a/java/org/apache/catalina/tribes/MembershipService.java b/java/org/apache/catalina/tribes/MembershipService.java
index 7138686..f93ed8f 100644
--- a/java/org/apache/catalina/tribes/MembershipService.java
+++ b/java/org/apache/catalina/tribes/MembershipService.java
@@ -35,10 +35,12 @@ public interface MembershipService {
* @param properties - to be used to configure the membership service.
*/
public void setProperties(java.util.Properties properties);
+
/**
- * Returns the properties for the configuration used.
+ * @return the properties for the configuration used.
*/
public java.util.Properties getProperties();
+
/**
* Starts the membership service. If a membership listeners is added
* the listener will start to receive membership events.
@@ -65,7 +67,6 @@ public interface MembershipService {
* stops broad casting the server
* @throws java.lang.IllegalArgumentException if the level is incorrect.
*/
-
public void stop(int level);
/**
@@ -73,36 +74,45 @@ public interface MembershipService {
*/
public boolean hasMembers();
-
/**
- *
- * @param mbr Member
- * @return Member
+ * Retrieve the specified member from the membership.
+ * @param mbr The member to retrieve
+ * @return the member
*/
public Member getMember(Member mbr);
+
/**
- * Returns a list of all the members in the cluster.
+ * @return a list of all the members in the cluster.
*/
-
public Member[] getMembers();
/**
- * Returns the member object that defines this member
+ * Get the local member.
+ * @return the member object that defines this member
+ * @param incAliveTime <code>true</code> to set the alive time
+ * on the local member
*/
public Member getLocalMember(boolean incAliveTime);
/**
- * Return all members by name
+ * @return all members by name
*/
- public String[] getMembersByName() ;
+ public String[] getMembersByName();
/**
- * Return the member by name
+ * Get a member.
+ * @param name The member name
+ * @return the member
*/
- public Member findMemberByName(String name) ;
+ public Member findMemberByName(String name);
/**
- * Sets the local member properties for broadcasting
+ * Sets the local member properties for broadcasting.
+ *
+ * @param listenHost Listen to host
+ * @param listenPort Listen to port
+ * @param securePort Use a secure port
+ * @param udpPort Use UDP
*/
public void setLocalMemberProperties(String listenHost, int listenPort, int securePort, int udpPort);
@@ -114,7 +124,7 @@ public interface MembershipService {
public void setMembershipListener(MembershipListener listener);
/**
- * removes the membership listener.
+ * Removes the membership listener.
*/
public void removeMembershipListener();
@@ -128,10 +138,22 @@ public interface MembershipService {
public void setDomain(byte[] domain);
/**
- * Broadcasts a message to all members
- * @param message
- * @throws ChannelException
+ * Broadcasts a message to all members.
+ * @param message The message to broadcast
+ * @throws ChannelException Message broadcast failed
*/
public void broadcast(ChannelMessage message) throws ChannelException;
+ /**
+ * Return the channel that is related to this MembershipService
+ * @return Channel
+ */
+ public Channel getChannel();
+
+ /**
+ * Set the channel that is related to this MembershipService
+ * @param channel The channel
+ */
+ public void setChannel(Channel channel);
+
}
diff --git a/java/org/apache/catalina/tribes/group/ChannelCoordinator.java b/java/org/apache/catalina/tribes/group/ChannelCoordinator.java
index e419c30..ec55dc8 100644
--- a/java/org/apache/catalina/tribes/group/ChannelCoordinator.java
+++ b/java/org/apache/catalina/tribes/group/ChannelCoordinator.java
@@ -27,7 +27,6 @@ import org.apache.catalina.tribes.MessageListener;
import org.apache.catalina.tribes.UniqueId;
import org.apache.catalina.tribes.membership.McastService;
import org.apache.catalina.tribes.membership.StaticMember;
-import org.apache.catalina.tribes.transport.ReceiverBase;
import org.apache.catalina.tribes.transport.ReplicationTransmitter;
import org.apache.catalina.tribes.transport.SenderState;
import org.apache.catalina.tribes.transport.nio.NioReceiver;
@@ -42,8 +41,7 @@ import org.apache.catalina.tribes.util.StringManager;
* This is the last interceptor in the chain.
*/
public class ChannelCoordinator extends ChannelInterceptorBase implements MessageListener {
- protected static final StringManager sm =
- StringManager.getManager(ChannelCoordinator.class.getPackage().getName());
+ protected static final StringManager sm = StringManager.getManager(ChannelCoordinator.class);
private ChannelReceiver clusterReceiver;
private ChannelSender clusterSender;
private MembershipService membershipService;
@@ -153,9 +151,7 @@ public class ChannelCoordinator extends ChannelInterceptorBase implements Messag
//listens to with the local membership settings
if ( Channel.SND_RX_SEQ==(svc & Channel.SND_RX_SEQ) ) {
clusterReceiver.setMessageListener(this);
- if (clusterReceiver instanceof ReceiverBase) {
- ((ReceiverBase)clusterReceiver).setChannel(getChannel());
- }
+ clusterReceiver.setChannel(getChannel());
clusterReceiver.start();
//synchronize, big time FIXME
Member localMember = getChannel().getLocalMember(false);
@@ -175,26 +171,22 @@ public class ChannelCoordinator extends ChannelInterceptorBase implements Messag
valid = true;
}
if ( Channel.SND_TX_SEQ==(svc & Channel.SND_TX_SEQ) ) {
- if (clusterSender instanceof ReplicationTransmitter) {
- ((ReplicationTransmitter)clusterSender).setChannel(getChannel());
- }
+ clusterSender.setChannel(getChannel());
clusterSender.start();
valid = true;
}
if ( Channel.MBR_RX_SEQ==(svc & Channel.MBR_RX_SEQ) ) {
membershipService.setMembershipListener(this);
+ membershipService.setChannel(getChannel());
if (membershipService instanceof McastService) {
((McastService)membershipService).setMessageListener(this);
- ((McastService)membershipService).setChannel(getChannel());
}
membershipService.start(MembershipService.MBR_RX);
valid = true;
}
if ( Channel.MBR_TX_SEQ==(svc & Channel.MBR_TX_SEQ) ) {
- if (membershipService instanceof McastService) {
- ((McastService)membershipService).setChannel(getChannel());
- }
+ membershipService.setChannel(getChannel());
membershipService.start(MembershipService.MBR_TX);
valid = true;
}
diff --git a/java/org/apache/catalina/tribes/group/ChannelInterceptorBase.java b/java/org/apache/catalina/tribes/group/ChannelInterceptorBase.java
index 22eba9c..f2c6362 100644
--- a/java/org/apache/catalina/tribes/group/ChannelInterceptorBase.java
+++ b/java/org/apache/catalina/tribes/group/ChannelInterceptorBase.java
@@ -183,14 +183,16 @@ public abstract class ChannelInterceptorBase implements ChannelInterceptor {
* Return the channel that is related to this interceptor
* @return Channel
*/
+ @Override
public Channel getChannel() {
return channel;
}
/**
* Set the channel that is related to this interceptor
- * @param channel
+ * @param channel The channel
*/
+ @Override
public void setChannel(Channel channel) {
this.channel = channel;
}
diff --git a/java/org/apache/catalina/tribes/group/GroupChannel.java b/java/org/apache/catalina/tribes/group/GroupChannel.java
index caf15fc..1f01a9a 100644
--- a/java/org/apache/catalina/tribes/group/GroupChannel.java
+++ b/java/org/apache/catalina/tribes/group/GroupChannel.java
@@ -57,8 +57,7 @@ import org.apache.juli.logging.LogFactory;
*/
public class GroupChannel extends ChannelInterceptorBase implements ManagedChannel {
private static final Log log = LogFactory.getLog(GroupChannel.class);
- protected static final StringManager sm =
- StringManager.getManager(GroupChannel.class.getPackage().getName());
+ protected static final StringManager sm = StringManager.getManager(GroupChannel.class);
/**
* Flag to determine if the channel manages its own heartbeat
@@ -373,7 +372,7 @@ public class GroupChannel extends ChannelInterceptorBase implements ManagedChann
/**
* Sets up the default implementation interceptor stack
* if no interceptors have been added
- * @throws ChannelException
+ * @throws ChannelException Cluster error
*/
protected synchronized void setupDefaultStack() throws ChannelException {
if (getFirstInterceptor() != null &&
@@ -383,8 +382,7 @@ public class GroupChannel extends ChannelInterceptorBase implements ManagedChann
Iterator<ChannelInterceptor> interceptors = getInterceptors();
while (interceptors.hasNext()) {
ChannelInterceptor channelInterceptor = interceptors.next();
- if (channelInterceptor instanceof ChannelInterceptorBase)
- ((ChannelInterceptorBase)channelInterceptor).setChannel(this);
+ channelInterceptor.setChannel(this);
}
coordinator.setChannel(this);
}
@@ -392,7 +390,7 @@ public class GroupChannel extends ChannelInterceptorBase implements ManagedChann
/**
* Validates the option flags that each interceptor is using and reports
* an error if two interceptor share the same flag.
- * @throws ChannelException
+ * @throws ChannelException Error with option flag
*/
protected void checkOptionFlags() throws ChannelException {
StringBuilder conflicts = new StringBuilder();
@@ -425,9 +423,9 @@ public class GroupChannel extends ChannelInterceptorBase implements ManagedChann
}
/**
- * Starts the channel
+ * Starts the channel.
* @param svc int - what service to start
- * @throws ChannelException
+ * @throws ChannelException Start error
* @see org.apache.catalina.tribes.Channel#start(int)
*/
@Override
@@ -442,9 +440,9 @@ public class GroupChannel extends ChannelInterceptorBase implements ManagedChann
}
/**
- * Stops the channel
+ * Stops the channel.
* @param svc int
- * @throws ChannelException
+ * @throws ChannelException Stop error
* @see org.apache.catalina.tribes.Channel#stop(int)
*/
@Override
@@ -632,10 +630,12 @@ public class GroupChannel extends ChannelInterceptorBase implements ManagedChann
return heartbeatSleeptime;
}
+ @Override
public String getName() {
return name;
}
+ @Override
public void setName(String name) {
this.name = name;
}
diff --git a/java/org/apache/catalina/tribes/group/RpcCallback.java b/java/org/apache/catalina/tribes/group/RpcCallback.java
index c508374..ba70ea9 100644
--- a/java/org/apache/catalina/tribes/group/RpcCallback.java
+++ b/java/org/apache/catalina/tribes/group/RpcCallback.java
@@ -28,16 +28,17 @@ import org.apache.catalina.tribes.Member;
public interface RpcCallback {
/**
- *
- * @param msg Serializable
- * @return Serializable - null if no reply should be sent
+ * Allows sending a response to a recieved message.
+ * @param msg The message
+ * @param sender Member
+ * @return Serializable object, <code>null</code> if no reply should be sent
*/
public Serializable replyRequest(Serializable msg, Member sender);
/**
* If the reply has already been sent to the requesting thread,
* the rpc callback can handle any data that comes in after the fact.
- * @param msg Serializable
+ * @param msg The message
* @param sender Member
*/
public void leftOver(Serializable msg, Member sender);
diff --git a/java/org/apache/catalina/tribes/group/RpcChannel.java b/java/org/apache/catalina/tribes/group/RpcChannel.java
index 8f99497..fc56451 100644
--- a/java/org/apache/catalina/tribes/group/RpcChannel.java
+++ b/java/org/apache/catalina/tribes/group/RpcChannel.java
@@ -37,8 +37,7 @@ import org.apache.juli.logging.LogFactory;
*/
public class RpcChannel implements ChannelListener {
private static final Log log = LogFactory.getLog(RpcChannel.class);
- protected static final StringManager sm =
- StringManager.getManager(RpcChannel.class.getPackage().getName());
+ protected static final StringManager sm = StringManager.getManager(RpcChannel.class);
public static final int FIRST_REPLY = 1;
public static final int MAJORITY_REPLY = 2;
@@ -75,7 +74,7 @@ public class RpcChannel implements ChannelListener {
* @param channelOptions channel sender options
* @param timeout long - timeout in milliseconds, if no reply is received within this time null is returned
* @return Response[] - an array of response objects.
- * @throws ChannelException
+ * @throws ChannelException Error sending message
*/
public Response[] send(Member[] destination,
Serializable message,
diff --git a/java/org/apache/catalina/tribes/group/interceptors/DomainFilterInterceptor.java b/java/org/apache/catalina/tribes/group/interceptors/DomainFilterInterceptor.java
index 86b8187..87ea2cd 100644
--- a/java/org/apache/catalina/tribes/group/interceptors/DomainFilterInterceptor.java
+++ b/java/org/apache/catalina/tribes/group/interceptors/DomainFilterInterceptor.java
@@ -36,8 +36,7 @@ import org.apache.juli.logging.LogFactory;
*/
public class DomainFilterInterceptor extends ChannelInterceptorBase {
private static final Log log = LogFactory.getLog(DomainFilterInterceptor.class);
- protected static final StringManager sm =
- StringManager.getManager(DomainFilterInterceptor.class.getPackage().getName());
+ protected static final StringManager sm = StringManager.getManager(DomainFilterInterceptor.class);
protected volatile Membership membership = null;
protected byte[] domain = new byte[0];
diff --git a/java/org/apache/catalina/tribes/group/interceptors/FragmentationInterceptor.java b/java/org/apache/catalina/tribes/group/interceptors/FragmentationInterceptor.java
index ad3c65d..85d3564 100644
--- a/java/org/apache/catalina/tribes/group/interceptors/FragmentationInterceptor.java
+++ b/java/org/apache/catalina/tribes/group/interceptors/FragmentationInterceptor.java
@@ -44,8 +44,8 @@ import org.apache.juli.logging.LogFactory;
* @version 1.0
*/
public class FragmentationInterceptor extends ChannelInterceptorBase {
- private static final Log log = LogFactory.getLog(FragmentationInterceptor.class );
- protected static final StringManager sm = StringManager.getManager(FragmentationInterceptor.class.getPackage().getName());
+ private static final Log log = LogFactory.getLog(FragmentationInterceptor.class);
+ protected static final StringManager sm = StringManager.getManager(FragmentationInterceptor.class);
protected final HashMap<FragKey, FragCollection> fragpieces = new HashMap<>();
private int maxSize = 1024*100;
diff --git a/java/org/apache/catalina/tribes/group/interceptors/GzipInterceptor.java b/java/org/apache/catalina/tribes/group/interceptors/GzipInterceptor.java
index cd0d370..21c75e0 100644
--- a/java/org/apache/catalina/tribes/group/interceptors/GzipInterceptor.java
+++ b/java/org/apache/catalina/tribes/group/interceptors/GzipInterceptor.java
@@ -39,8 +39,7 @@ import org.apache.juli.logging.LogFactory;
public class GzipInterceptor extends ChannelInterceptorBase {
private static final Log log = LogFactory.getLog(GzipInterceptor.class);
- protected static final StringManager sm =
- StringManager.getManager(GzipInterceptor.class.getPackage().getName());
+ protected static final StringManager sm = StringManager.getManager(GzipInterceptor.class);
public static final int DEFAULT_BUFFER_SIZE = 2048;
@@ -81,7 +80,7 @@ public class GzipInterceptor extends ChannelInterceptorBase {
/**
* @param data Data to decompress
* @return Decompressed data
- * @throws IOException
+ * @throws IOException Compression error
*/
public static byte[] decompress(byte[] data) throws IOException {
ByteArrayOutputStream bout =
diff --git a/java/org/apache/catalina/tribes/group/interceptors/LocalStrings.properties b/java/org/apache/catalina/tribes/group/interceptors/LocalStrings.properties
index c806b21..bd240cf 100644
--- a/java/org/apache/catalina/tribes/group/interceptors/LocalStrings.properties
+++ b/java/org/apache/catalina/tribes/group/interceptors/LocalStrings.properties
@@ -31,6 +31,7 @@ nonBlockingCoordinator.memberAdded.failed=Unable to start election when member w
nonBlockingCoordinator.memberDisappeared.failed=Unable to start election when member was removed.
nonBlockingCoordinator.heartbeat.inconsistency=Heartbeat found inconsistency, restart election
nonBlockingCoordinator.heartbeat.failed=Unable to perform heartbeat.
+nonBlockingCoordinator.memberAlive.failed=Unable to perform member alive check, assuming member down.
orderInterceptor.messageAdded.sameCounter=Message added has the same counter, synchronization bug. Disable the order interceptor
staticMembershipInterceptor.no.failureDetector=There is no TcpFailureDetector. Automatic detection of static members does not work properly. By defining the StaticMembershipInterceptor under the TcpFailureDetector, automatic detection of the static members will work.
staticMembershipInterceptor.no.pingInterceptor=There is no TcpPingInterceptor. The health check of static members does not work properly. By defining the TcpPingInterceptor, the health check of static members will work.
diff --git a/java/org/apache/catalina/tribes/group/interceptors/MessageDispatch15Interceptor.java b/java/org/apache/catalina/tribes/group/interceptors/MessageDispatch15Interceptor.java
deleted file mode 100644
index b40ab63..0000000
--- a/java/org/apache/catalina/tribes/group/interceptors/MessageDispatch15Interceptor.java
+++ /dev/null
@@ -1,28 +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.tribes.group.interceptors;
-
-/**
- * @deprecated Originally provided an optional implementation that used Java 5+
- * features. Now the minimum Java version is >=5, those features
- * have been added to {@link MessageDispatchInterceptor} which
- * should be used instead. This class will be removed in Tomcat
- * 8.5.x onwards.
- */
- at Deprecated
-public class MessageDispatch15Interceptor extends MessageDispatchInterceptor {
-}
\ No newline at end of file
diff --git a/java/org/apache/catalina/tribes/group/interceptors/MessageDispatchInterceptor.java b/java/org/apache/catalina/tribes/group/interceptors/MessageDispatchInterceptor.java
index 4badc34..f9f9282 100644
--- a/java/org/apache/catalina/tribes/group/interceptors/MessageDispatchInterceptor.java
+++ b/java/org/apache/catalina/tribes/group/interceptors/MessageDispatchInterceptor.java
@@ -27,7 +27,6 @@ import org.apache.catalina.tribes.ErrorHandler;
import org.apache.catalina.tribes.Member;
import org.apache.catalina.tribes.UniqueId;
import org.apache.catalina.tribes.group.ChannelInterceptorBase;
-import org.apache.catalina.tribes.group.GroupChannel;
import org.apache.catalina.tribes.group.InterceptorPayload;
import org.apache.catalina.tribes.util.ExecutorFactory;
import org.apache.catalina.tribes.util.StringManager;
@@ -41,26 +40,14 @@ import org.apache.juli.logging.LogFactory;
* <code>Channel.SEND_OPTIONS_ASYNCHRONOUS</code> flag to be set, if it is, it
* will queue the message for delivery and immediately return to the sender.
*/
-public class MessageDispatchInterceptor extends ChannelInterceptorBase implements Runnable {
+public class MessageDispatchInterceptor extends ChannelInterceptorBase {
private static final Log log = LogFactory.getLog(MessageDispatchInterceptor.class);
protected static final StringManager sm =
- StringManager.getManager(MessageDispatchInterceptor.class.getPackage().getName());
+ StringManager.getManager(MessageDispatchInterceptor.class);
protected long maxQueueSize = 1024*1024*64; //64MB
- /**
- * @deprecated Unused. Will be removed in Tomcat 8.5.x.
- */
- @Deprecated
- // Use fully qualified name to avoid deprecation warning on import.
- protected final org.apache.catalina.tribes.transport.bio.util.FastQueue queue =
- new org.apache.catalina.tribes.transport.bio.util.FastQueue();
protected volatile boolean run = false;
- /**
- * @deprecated Unused. Will be removed in Tomcat 8.5.x.
- */
- @Deprecated
- protected Thread msgDispatchThread = null;
protected boolean useDeepClone = true;
protected boolean alwaysSend = true;
@@ -119,27 +106,12 @@ public class MessageDispatchInterceptor extends ChannelInterceptorBase implement
}
- /**
- * @deprecated Not used. The thread pool contains its own queue. This will
- * be removed in Tomcat 8.5.x onwards.
- *
- * @return Always null
- */
- @Deprecated
- public org.apache.catalina.tribes.transport.bio.util.LinkObject removeFromQueue() {
- return null;
- }
-
-
public void startQueue() {
if (run) {
return;
}
String channelName = "";
- if (getChannel() instanceof GroupChannel
- && ((GroupChannel)getChannel()).getName() != null) {
- channelName = "[" + ((GroupChannel)getChannel()).getName() + "]";
- }
+ if (getChannel().getName() != null) channelName = "[" + getChannel().getName() + "]";
executor = ExecutorFactory.newThreadPool(maxSpareThreads, maxThreads, keepAliveTime,
TimeUnit.MILLISECONDS,
new TcclThreadFactory("MessageDispatchInterceptor.MessageDispatchThread" + channelName));
@@ -268,27 +240,6 @@ public class MessageDispatchInterceptor extends ChannelInterceptorBase implement
}
- /**
- * @deprecated Unused. Will be removed in 8.5.x
- */
- @Deprecated
- @Override
- public void run() {
- // NO-OP since it is now unused.
- }
-
-
- /**
- * @deprecated Unused. Will be removed in 8.5.x
- */
- @Deprecated
- protected org.apache.catalina.tribes.transport.bio.util.LinkObject sendAsyncData(
- org.apache.catalina.tribes.transport.bio.util.LinkObject link) {
- sendAsyncData(link.data(), link.getDestination(), link.getPayload());
- return link.next();
- }
-
-
protected void sendAsyncData(ChannelMessage msg, Member[] destination,
InterceptorPayload payload) {
ErrorHandler handler = null;
diff --git a/java/org/apache/catalina/tribes/group/interceptors/NonBlockingCoordinator.java b/java/org/apache/catalina/tribes/group/interceptors/NonBlockingCoordinator.java
index 30fdf4d..72ea618 100644
--- a/java/org/apache/catalina/tribes/group/interceptors/NonBlockingCoordinator.java
+++ b/java/org/apache/catalina/tribes/group/interceptors/NonBlockingCoordinator.java
@@ -16,6 +16,11 @@
*/
package org.apache.catalina.tribes.group.interceptors;
+import java.net.ConnectException;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.net.SocketTimeoutException;
import java.util.concurrent.atomic.AtomicBoolean;
import org.apache.catalina.tribes.Channel;
@@ -97,7 +102,7 @@ import org.apache.juli.logging.LogFactory;
* Lets assume that C1 arrives, C1 has lower priority than C, but higher priority than D.<br>
* Lets also assume that C1 sees the following view {B,D,E}<br>
* C1 waits for a token to arrive. When the token arrives, the same scenario as above will happen.<br>
- * In the scenario where C1 sees {D,E} and A,B,C can not see C1, no token will ever arrive.<br>
+ * In the scenario where C1 sees {D,E} and A,B,C cannot see C1, no token will ever arrive.<br>
* In this case, C1 sends a Z{C1-ldr, C1-src, mbrs-C1,D,E} to D<br>
* D receives Z{C1-ldr, C1-src, mbrs-C1,D,E} and sends Z{A-ldr, C1-src, mbrs-A,B,C,C1,D,E} to E<br>
* E receives Z{A-ldr, C1-src, mbrs-A,B,C,C1,D,E} and sends it to A<br>
@@ -124,8 +129,7 @@ import org.apache.juli.logging.LogFactory;
public class NonBlockingCoordinator extends ChannelInterceptorBase {
private static final Log log = LogFactory.getLog(NonBlockingCoordinator.class);
- protected static final StringManager sm =
- StringManager.getManager(NonBlockingCoordinator.class.getPackage().getName());
+ protected static final StringManager sm = StringManager.getManager(NonBlockingCoordinator.class);
/**
* header for a coordination message
@@ -288,13 +292,26 @@ public class NonBlockingCoordinator extends ChannelInterceptorBase {
}
protected boolean alive(Member mbr) {
- return TcpFailureDetector.memberAlive(mbr,
- COORD_ALIVE,
- false,
- false,
- waitForCoordMsgTimeout,
- waitForCoordMsgTimeout,
- getOptionFlag());
+ return memberAlive(mbr, waitForCoordMsgTimeout);
+ }
+
+ protected boolean memberAlive(Member mbr, long conTimeout) {
+ //could be a shutdown notification
+ if ( Arrays.equals(mbr.getCommand(),Member.SHUTDOWN_PAYLOAD) ) return false;
+
+ try (Socket socket = new Socket()) {
+ InetAddress ia = InetAddress.getByAddress(mbr.getHost());
+ InetSocketAddress addr = new InetSocketAddress(ia, mbr.getPort());
+ socket.connect(addr, (int) conTimeout);
+ return true;
+ } catch (SocketTimeoutException sx) {
+ //do nothing, we couldn't connect
+ } catch (ConnectException cx) {
+ //do nothing, we couldn't connect
+ } catch (Exception x) {
+ log.error(sm.getString("nonBlockingCoordinator.memberAlive.failed"),x);
+ }
+ return false;
}
protected Membership mergeOnArrive(CoordinationMessage msg) {
diff --git a/java/org/apache/catalina/tribes/group/interceptors/OrderInterceptor.java b/java/org/apache/catalina/tribes/group/interceptors/OrderInterceptor.java
index 611785c..46e2410 100644
--- a/java/org/apache/catalina/tribes/group/interceptors/OrderInterceptor.java
+++ b/java/org/apache/catalina/tribes/group/interceptors/OrderInterceptor.java
@@ -54,8 +54,7 @@ import org.apache.catalina.tribes.util.StringManager;
* @version 1.1
*/
public class OrderInterceptor extends ChannelInterceptorBase {
- protected static final StringManager sm =
- StringManager.getManager(OrderInterceptor.class.getPackage().getName());
+ protected static final StringManager sm = StringManager.getManager(OrderInterceptor.class);
private final HashMap<Member, Counter> outcounter = new HashMap<>();
private final HashMap<Member, Counter> incounter = new HashMap<>();
private final HashMap<Member, MessageOrder> incoming = new HashMap<>();
diff --git a/java/org/apache/catalina/tribes/group/interceptors/SimpleCoordinator.java b/java/org/apache/catalina/tribes/group/interceptors/SimpleCoordinator.java
index a6933bb..ebdd6ae 100644
--- a/java/org/apache/catalina/tribes/group/interceptors/SimpleCoordinator.java
+++ b/java/org/apache/catalina/tribes/group/interceptors/SimpleCoordinator.java
@@ -58,7 +58,7 @@ public class SimpleCoordinator extends ChannelInterceptorBase {
/**
* Override to receive view changes.
*
- * @param view
+ * @param view The members array
*/
protected void viewChange(final Member[] view) {
}
diff --git a/java/org/apache/catalina/tribes/group/interceptors/StaticMembershipInterceptor.java b/java/org/apache/catalina/tribes/group/interceptors/StaticMembershipInterceptor.java
index 5fad532..f38feb3 100644
--- a/java/org/apache/catalina/tribes/group/interceptors/StaticMembershipInterceptor.java
+++ b/java/org/apache/catalina/tribes/group/interceptors/StaticMembershipInterceptor.java
@@ -28,7 +28,6 @@ import org.apache.catalina.tribes.group.AbsoluteOrder;
import org.apache.catalina.tribes.group.ChannelInterceptorBase;
import org.apache.catalina.tribes.io.ChannelData;
import org.apache.catalina.tribes.io.XByteBuffer;
-import org.apache.catalina.tribes.membership.StaticMember;
import org.apache.catalina.tribes.util.StringManager;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
@@ -37,7 +36,7 @@ public class StaticMembershipInterceptor extends ChannelInterceptorBase {
private static final Log log = LogFactory.getLog(StaticMembershipInterceptor.class);
protected static final StringManager sm =
- StringManager.getManager(StaticMembershipInterceptor.class.getPackage().getName());
+ StringManager.getManager(StaticMembershipInterceptor.class);
protected static final byte[] MEMBER_START = new byte[] {
76, 111, 99, 97, 108, 32, 83, 116, 97, 116, 105, 99, 77, 101, 109, 98, 101, 114, 32, 78,
@@ -68,7 +67,7 @@ public class StaticMembershipInterceptor extends ChannelInterceptorBase {
public void setLocalMember(Member member) {
this.localMember = member;
- ((StaticMember)localMember).setLocal(true);
+ localMember.setLocal(true);
}
@Override
@@ -81,7 +80,7 @@ public class StaticMembershipInterceptor extends ChannelInterceptorBase {
super.memberAdded(member);
}
} else if (msg.getMessage().getLength() == MEMBER_STOP.length &&
- Arrays.equals(MEMBER_STOP, msg.getMessage().getBytes())) {
+ Arrays.equals(MEMBER_STOP, msg.getMessage().getBytes())) {
// receive member shutdown
Member member = getMember(msg.getAddress());
if (member != null) {
@@ -147,9 +146,9 @@ public class StaticMembershipInterceptor extends ChannelInterceptorBase {
}
/**
- * Send notifications upwards
- * @param svc int
- * @throws ChannelException
+ * {@inheritDoc}
+ * <p>
+ * Sends notifications upwards.
*/
@Override
public void start(int svc) throws ChannelException {
diff --git a/java/org/apache/catalina/tribes/group/interceptors/TcpFailureDetector.java b/java/org/apache/catalina/tribes/group/interceptors/TcpFailureDetector.java
index 464cddb..26b80b3 100644
--- a/java/org/apache/catalina/tribes/group/interceptors/TcpFailureDetector.java
+++ b/java/org/apache/catalina/tribes/group/interceptors/TcpFailureDetector.java
@@ -61,9 +61,8 @@ import org.apache.juli.logging.LogFactory;
*/
public class TcpFailureDetector extends ChannelInterceptorBase {
- private static final Log log = LogFactory.getLog( TcpFailureDetector.class );
- protected static final StringManager sm =
- StringManager.getManager(TcpFailureDetector.class.getPackage().getName());
+ private static final Log log = LogFactory.getLog(TcpFailureDetector.class);
+ protected static final StringManager sm = StringManager.getManager(TcpFailureDetector.class);
protected static final byte[] TCP_FAIL_DETECT = new byte[] {
79, -89, 115, 72, 121, -126, 67, -55, -97, 111, -119, -128, -95, 91, 7, 20,
@@ -322,7 +321,7 @@ public class TcpFailureDetector extends ChannelInterceptorBase {
return memberAlive(mbr,TCP_FAIL_DETECT,performSendTest,performReadTest,readTestTimeout,connectTimeout,getOptionFlag());
}
- protected static boolean memberAlive(Member mbr, byte[] msgData,
+ protected boolean memberAlive(Member mbr, byte[] msgData,
boolean sendTest, boolean readTest,
long readTimeout, long conTimeout,
int optionFlag) {
@@ -336,7 +335,7 @@ public class TcpFailureDetector extends ChannelInterceptorBase {
socket.connect(addr, (int) conTimeout);
if ( sendTest ) {
ChannelData data = new ChannelData(true);
- data.setAddress(mbr);
+ data.setAddress(getLocalMember(false));
data.setMessage(new XByteBuffer(msgData,false));
data.setTimestamp(System.currentTimeMillis());
int options = optionFlag | Channel.SEND_OPTIONS_BYTE_MESSAGE;
diff --git a/java/org/apache/catalina/tribes/group/interceptors/TcpPingInterceptor.java b/java/org/apache/catalina/tribes/group/interceptors/TcpPingInterceptor.java
index 3003085..3d6a1c1 100644
--- a/java/org/apache/catalina/tribes/group/interceptors/TcpPingInterceptor.java
+++ b/java/org/apache/catalina/tribes/group/interceptors/TcpPingInterceptor.java
@@ -26,7 +26,6 @@ import org.apache.catalina.tribes.ChannelInterceptor;
import org.apache.catalina.tribes.ChannelMessage;
import org.apache.catalina.tribes.Member;
import org.apache.catalina.tribes.group.ChannelInterceptorBase;
-import org.apache.catalina.tribes.group.GroupChannel;
import org.apache.catalina.tribes.io.ChannelData;
import org.apache.catalina.tribes.io.XByteBuffer;
import org.apache.catalina.tribes.util.StringManager;
@@ -44,8 +43,7 @@ import org.apache.juli.logging.LogFactory;
public class TcpPingInterceptor extends ChannelInterceptorBase {
private static final Log log = LogFactory.getLog(TcpPingInterceptor.class);
- protected static final StringManager sm =
- StringManager.getManager(TcpPingInterceptor.class.getPackage().getName());
+ protected static final StringManager sm = StringManager.getManager(TcpPingInterceptor.class);
protected static final byte[] TCP_PING_DATA = new byte[] {
79, -89, 115, 72, 121, -33, 67, -55, -97, 111, -119, -128, -95, 91, 7, 20,
@@ -72,10 +70,7 @@ public class TcpPingInterceptor extends ChannelInterceptorBase {
thread = new PingThread();
thread.setDaemon(true);
String channelName = "";
- if (getChannel() instanceof GroupChannel
- && ((GroupChannel)getChannel()).getName() != null) {
- channelName = "[" + ((GroupChannel)getChannel()).getName() + "]";
- }
+ if (getChannel().getName() != null) channelName = "[" + getChannel().getName() + "]";
thread.setName("TcpPingInterceptor.PingThread" + channelName +"-"+cnt.addAndGet(1));
thread.start();
}
diff --git a/java/org/apache/catalina/tribes/group/interceptors/ThroughputInterceptor.java b/java/org/apache/catalina/tribes/group/interceptors/ThroughputInterceptor.java
index eb31185..cceb7b2 100644
--- a/java/org/apache/catalina/tribes/group/interceptors/ThroughputInterceptor.java
+++ b/java/org/apache/catalina/tribes/group/interceptors/ThroughputInterceptor.java
@@ -40,8 +40,7 @@ import org.apache.juli.logging.LogFactory;
*/
public class ThroughputInterceptor extends ChannelInterceptorBase {
private static final Log log = LogFactory.getLog(ThroughputInterceptor.class);
- protected static final StringManager sm =
- StringManager.getManager(ThroughputInterceptor.class.getPackage().getName());
+ protected static final StringManager sm = StringManager.getManager(ThroughputInterceptor.class);
double mbTx = 0;
double mbAppTx = 0;
diff --git a/java/org/apache/catalina/tribes/group/interceptors/TwoPhaseCommitInterceptor.java b/java/org/apache/catalina/tribes/group/interceptors/TwoPhaseCommitInterceptor.java
index 849549a..859c3ce 100644
--- a/java/org/apache/catalina/tribes/group/interceptors/TwoPhaseCommitInterceptor.java
+++ b/java/org/apache/catalina/tribes/group/interceptors/TwoPhaseCommitInterceptor.java
@@ -36,8 +36,7 @@ public class TwoPhaseCommitInterceptor extends ChannelInterceptorBase {
private static final byte[] START_DATA = new byte[] {113, 1, -58, 2, -34, -60, 75, -78, -101, -12, 32, -29, 32, 111, -40, 4};
private static final byte[] END_DATA = new byte[] {54, -13, 90, 110, 47, -31, 75, -24, -81, -29, 36, 52, -58, 77, -110, 56};
private static final Log log = LogFactory.getLog(TwoPhaseCommitInterceptor.class);
- protected static final StringManager sm =
- StringManager.getManager(TwoPhaseCommitInterceptor.class.getPackage().getName());
+ protected static final StringManager sm = StringManager.getManager(TwoPhaseCommitInterceptor.class);
protected final HashMap<UniqueId, MapEntry> messages = new HashMap<>();
protected long expire = 1000 * 60; //one minute expiration
diff --git a/java/org/apache/catalina/tribes/io/BufferPool.java b/java/org/apache/catalina/tribes/io/BufferPool.java
index d0f1332..a44a1da 100644
--- a/java/org/apache/catalina/tribes/io/BufferPool.java
+++ b/java/org/apache/catalina/tribes/io/BufferPool.java
@@ -31,8 +31,7 @@ public class BufferPool {
public static final int DEFAULT_POOL_SIZE = 100*1024*1024; //100MB
- protected static final StringManager sm =
- StringManager.getManager(BufferPool.class.getPackage().getName());
+ protected static final StringManager sm = StringManager.getManager(BufferPool.class);
diff --git a/java/org/apache/catalina/tribes/io/DirectByteArrayOutputStream.java b/java/org/apache/catalina/tribes/io/DirectByteArrayOutputStream.java
index ef58fa2..f76e991 100644
--- a/java/org/apache/catalina/tribes/io/DirectByteArrayOutputStream.java
+++ b/java/org/apache/catalina/tribes/io/DirectByteArrayOutputStream.java
@@ -40,7 +40,6 @@ public class DirectByteArrayOutputStream extends OutputStream {
* @throws IOException if an I/O error occurs. In particular, an
* <code>IOException</code> may be thrown if the output stream has
* been closed.
- * TODO Implement this java.io.OutputStream method
*/
@Override
public void write(int b) throws IOException {
diff --git a/java/org/apache/catalina/tribes/io/ObjectReader.java b/java/org/apache/catalina/tribes/io/ObjectReader.java
index 957f01f..b47e746 100644
--- a/java/org/apache/catalina/tribes/io/ObjectReader.java
+++ b/java/org/apache/catalina/tribes/io/ObjectReader.java
@@ -38,8 +38,7 @@ import org.apache.juli.logging.LogFactory;
public class ObjectReader {
private static final Log log = LogFactory.getLog(ObjectReader.class);
- protected static final StringManager sm =
- StringManager.getManager(ObjectReader.class.getPackage().getName());
+ protected static final StringManager sm = StringManager.getManager(ObjectReader.class);
private XByteBuffer buffer;
@@ -94,9 +93,8 @@ public class ObjectReader {
* @param len length in buffer
* @param count whether to return the count
* @return number of messages that was sent to callback (or -1 if count == false)
- * @throws java.io.IOException
*/
- public int append(ByteBuffer data, int len, boolean count) throws java.io.IOException {
+ public int append(ByteBuffer data, int len, boolean count) {
buffer.append(data,len);
int pkgCnt = -1;
if ( count ) pkgCnt = buffer.countPackages();
@@ -119,9 +117,8 @@ public class ObjectReader {
* @see XByteBuffer#extractPackage(boolean)
*
* @return number of received packages/messages
- * @throws java.io.IOException
*/
- public ChannelMessage[] execute() throws java.io.IOException {
+ public ChannelMessage[] execute() {
int pkgCnt = buffer.countPackages();
ChannelMessage[] result = new ChannelMessage[pkgCnt];
for (int i=0; i<pkgCnt; i++) {
diff --git a/java/org/apache/catalina/tribes/io/ReplicationStream.java b/java/org/apache/catalina/tribes/io/ReplicationStream.java
index e073b79..acf80ff 100644
--- a/java/org/apache/catalina/tribes/io/ReplicationStream.java
+++ b/java/org/apache/catalina/tribes/io/ReplicationStream.java
@@ -37,8 +37,7 @@ import org.apache.catalina.tribes.util.StringManager;
*/
public final class ReplicationStream extends ObjectInputStream {
- static final StringManager sm =
- StringManager.getManager(ReplicationStream.class.getPackage().getName());
+ static final StringManager sm = StringManager.getManager(ReplicationStream.class);
/**
* The class loader we will use to resolve classes.
diff --git a/java/org/apache/catalina/tribes/io/XByteBuffer.java b/java/org/apache/catalina/tribes/io/XByteBuffer.java
index da63655..7f86682 100644
--- a/java/org/apache/catalina/tribes/io/XByteBuffer.java
+++ b/java/org/apache/catalina/tribes/io/XByteBuffer.java
@@ -47,12 +47,10 @@ import org.apache.juli.logging.LogFactory;
* <li><b>END_DATA</b> - 7 bytes - <i>TLF2003</i></li>
* </ul>
*/
-public class XByteBuffer
-{
+public class XByteBuffer {
- private static final Log log = LogFactory.getLog( XByteBuffer.class );
- protected static final StringManager sm =
- StringManager.getManager(XByteBuffer.class.getPackage().getName());
+ private static final Log log = LogFactory.getLog(XByteBuffer.class);
+ protected static final StringManager sm = StringManager.getManager(XByteBuffer.class);
/**
* This is a package header, 7 bytes (FLT2002)
@@ -86,7 +84,8 @@ public class XByteBuffer
/**
* Constructs a new XByteBuffer.<br>
* TODO use a pool of byte[] for performance
- * @param size - the initial size of the byte buffer
+ * @param size the initial size of the byte buffer
+ * @param discard Flag for discarding invalid packages
*/
public XByteBuffer(int size, boolean discard) {
buf = new byte[size];
@@ -130,7 +129,7 @@ public class XByteBuffer
}
/**
- * Returns the bytes in the buffer, in its exact length
+ * @return the bytes in the buffer, in its exact length
*/
public byte[] getBytes() {
byte[] b = new byte[bufSize];
@@ -394,7 +393,6 @@ public class XByteBuffer
* @param b - the byte array containing the four bytes
* @param off - the offset
* @return the integer value constructed from the four bytes
- * @exception java.lang.ArrayIndexOutOfBoundsException
*/
public static int toInt(byte[] b,int off){
return ( ( b[off+3]) & 0xFF) +
@@ -408,7 +406,6 @@ public class XByteBuffer
* @param b - the byte array containing the four bytes
* @param off - the offset
* @return the long value constructed from the eight bytes
- * @exception java.lang.ArrayIndexOutOfBoundsException
*/
public static long toLong(byte[] b,int off){
return ( ( (long) b[off+7]) & 0xFF) +
@@ -423,9 +420,11 @@ public class XByteBuffer
/**
- * Converts a boolean to a 1-byte array
- * @param bool - the integer
- * @return - 1-byte array
+ * Converts a boolean and put it in a byte array.
+ * @param bool the integer
+ * @param data the byte buffer in which the boolean will be placed
+ * @param offset the offset in the byte array
+ * @return the byte array
*/
public static byte[] toBytes(boolean bool, byte[] data, int offset) {
data[offset] = (byte)(bool?1:0);
@@ -433,7 +432,7 @@ public class XByteBuffer
}
/**
- * Converts a byte array entry to boolean
+ * Converts a byte array entry to boolean.
* @param b byte array
* @param offset within byte array
* @return true if byte array entry is non-zero, false otherwise
@@ -444,11 +443,13 @@ public class XByteBuffer
/**
- * Converts an integer to four bytes
- * @param n - the integer
- * @return - four bytes in an array
+ * Converts an integer to four bytes.
+ * @param n the integer
+ * @param b the byte buffer in which the integer will be placed
+ * @param offset the offset in the byte array
+ * @return four bytes in an array
*/
- public static byte[] toBytes(int n,byte[] b, int offset) {
+ public static byte[] toBytes(int n, byte[] b, int offset) {
b[offset+3] = (byte) (n);
n >>>= 8;
b[offset+2] = (byte) (n);
@@ -460,9 +461,11 @@ public class XByteBuffer
}
/**
- * Converts an long to eight bytes
- * @param n - the long
- * @return - eight bytes in an array
+ * Converts an long to eight bytes.
+ * @param n the long
+ * @param b the byte buffer in which the integer will be placed
+ * @param offset the offset in the byte array
+ * @return eight bytes in an array
*/
public static byte[] toBytes(long n, byte[] b, int offset) {
b[offset+7] = (byte) (n);
@@ -484,7 +487,7 @@ public class XByteBuffer
}
/**
- * Similar to a String.IndexOf, but uses pure bytes
+ * Similar to a String.IndexOf, but uses pure bytes.
* @param src - the source bytes to be searched
* @param srcOff - offset on the source buffer
* @param find - the string to be found within src
@@ -569,7 +572,7 @@ public class XByteBuffer
* Serializes a message into cluster data
* @param msg ClusterMessage
* @return serialized content as byte[] array
- * @throws IOException
+ * @throws IOException Serialization error
*/
public static byte[] serialize(Serializable msg) throws IOException {
ByteArrayOutputStream outs = new ByteArrayOutputStream();
diff --git a/java/org/apache/catalina/tribes/membership/McastService.java b/java/org/apache/catalina/tribes/membership/McastService.java
index 660b608..a34e3d2 100644
--- a/java/org/apache/catalina/tribes/membership/McastService.java
+++ b/java/org/apache/catalina/tribes/membership/McastService.java
@@ -33,6 +33,8 @@ import org.apache.catalina.tribes.io.XByteBuffer;
import org.apache.catalina.tribes.util.Arrays;
import org.apache.catalina.tribes.util.StringManager;
import org.apache.catalina.tribes.util.UUIDGenerator;
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
/**
* A <b>membership</b> implementation using simple multicast.
@@ -42,8 +44,7 @@ import org.apache.catalina.tribes.util.UUIDGenerator;
*/
public class McastService implements MembershipService,MembershipListener,MessageListener {
- private static final org.apache.juli.logging.Log log =
- org.apache.juli.logging.LogFactory.getLog( McastService.class );
+ private static final Log log = LogFactory.getLog(McastService.class);
/**
* The string manager for this package.
@@ -92,7 +93,7 @@ public class McastService implements MembershipService,MembershipListener,Messag
}
/**
- *
+ * Sets the properties for the membership service.
* @param properties
* <br>All are required<br>
* 1. mcastPort - the port to listen to<BR>
@@ -116,7 +117,7 @@ public class McastService implements MembershipService,MembershipListener,Messag
}
/**
- * Return the properties, see setProperties
+ * {@inheritDoc}
*/
@Override
public Properties getProperties() {
@@ -124,14 +125,14 @@ public class McastService implements MembershipService,MembershipListener,Messag
}
/**
- * Return the local member name
+ * @return the local member name
*/
public String getLocalMemberName() {
return localMember.toString() ;
}
/**
- * Return the local member
+ * {@inheritDoc}
*/
@Override
public Member getLocalMember(boolean alive) {
@@ -140,7 +141,7 @@ public class McastService implements MembershipService,MembershipListener,Messag
}
/**
- * Sets the local member properties for broadcasting
+ * {@inheritDoc}
*/
@Override
public void setLocalMemberProperties(String listenHost, int listenPort, int securePort, int udpPort) {
@@ -570,10 +571,12 @@ public class McastService implements MembershipService,MembershipListener,Messag
else setDomain(Arrays.convert(domain));
}
+ @Override
public Channel getChannel() {
return channel;
}
+ @Override
public void setChannel(Channel channel) {
this.channel = channel;
}
diff --git a/java/org/apache/catalina/tribes/membership/McastServiceImpl.java b/java/org/apache/catalina/tribes/membership/McastServiceImpl.java
index de8e44a..c6219d0 100644
--- a/java/org/apache/catalina/tribes/membership/McastServiceImpl.java
+++ b/java/org/apache/catalina/tribes/membership/McastServiceImpl.java
@@ -34,7 +34,6 @@ import org.apache.catalina.tribes.Channel;
import org.apache.catalina.tribes.Member;
import org.apache.catalina.tribes.MembershipListener;
import org.apache.catalina.tribes.MessageListener;
-import org.apache.catalina.tribes.group.GroupChannel;
import org.apache.catalina.tribes.io.ChannelData;
import org.apache.catalina.tribes.io.XByteBuffer;
import org.apache.catalina.tribes.util.ExecutorFactory;
@@ -51,14 +50,13 @@ import org.apache.juli.logging.LogFactory;
* Need to fix this, could use java.nio and only need one thread to send and receive, or
* just use a timeout on the receive
*/
-public class McastServiceImpl
-{
- private static final Log log = LogFactory.getLog( McastService.class );
+public class McastServiceImpl {
+
+ private static final Log log = LogFactory.getLog(McastService.class);
protected static final int MAX_PACKET_SIZE = 65535;
protected static final StringManager sm = StringManager.getManager(Constants.Package);
-
/**
* Internal flag used for the listen thread that listens to the multicasting socket.
*/
@@ -160,16 +158,19 @@ public class McastServiceImpl
private Channel channel;
/**
- * Create a new mcast service impl
+ * Create a new mcast service instance.
* @param member - the local member
* @param sendFrequency - the time (ms) in between pings sent out
* @param expireTime - the time (ms) for a member to expire
* @param port - the mcast port
* @param bind - the bind address (not sure this is used yet)
* @param mcastAddress - the mcast address
+ * @param ttl multicast ttl that will be set on the socket
+ * @param soTimeout Socket timeout
* @param service - the callback service
+ * @param msgservice Message listener
* @param localLoopbackDisabled - disable loopbackMode
- * @throws IOException
+ * @throws IOException Init error
*/
public McastServiceImpl(
MemberImpl member,
@@ -302,7 +303,9 @@ public class McastServiceImpl
}
/**
- * Stops the service
+ * Stops the service.
+ * @param level Stop status
+ * @return <code>true</code> if the stop is complete
* @throws IOException if the service fails to disconnect from the sockets
*/
public synchronized boolean stop(int level) throws IOException {
@@ -340,7 +343,7 @@ public class McastServiceImpl
/**
* Receive a datagram packet, locking wait
- * @throws IOException
+ * @throws IOException Received failed
*/
public void receive() throws IOException {
boolean checkexpired = true;
@@ -481,16 +484,17 @@ public class McastServiceImpl
}
/**
- * Send a ping
- * @throws IOException
+ * Send a ping.
+ * @param checkexpired <code>true</code> to check for expiration
+ * @throws IOException Send error
*/
- public void send(boolean checkexpired) throws IOException{
+ public void send(boolean checkexpired) throws IOException {
send(checkexpired,null);
}
private final Object sendLock = new Object();
- public void send(boolean checkexpired, DatagramPacket packet) throws IOException{
+ public void send(boolean checkexpired, DatagramPacket packet) throws IOException {
checkexpired = (checkexpired && (packet==null));
//ignore if we haven't started the sender
//if ( (startLevel&Channel.MBR_TX_SEQ) != Channel.MBR_TX_SEQ ) return;
@@ -542,9 +546,7 @@ public class McastServiceImpl
public ReceiverThread() {
super();
String channelName = "";
- if (channel instanceof GroupChannel && ((GroupChannel)channel).getName() != null) {
- channelName = "[" + ((GroupChannel)channel).getName() + "]";
- }
+ if (channel.getName() != null) channelName = "[" + channel.getName() + "]";
setName("Tribes-MembershipReceiver" + channelName);
}
@Override
@@ -579,9 +581,7 @@ public class McastServiceImpl
public SenderThread(long time) {
this.time = time;
String channelName = "";
- if (channel instanceof GroupChannel && ((GroupChannel)channel).getName() != null) {
- channelName = "[" + ((GroupChannel)channel).getName() + "]";
- }
+ if (channel.getName() != null) channelName = "[" + channel.getName() + "]";
setName("Tribes-MembershipSender" + channelName);
}
@@ -620,10 +620,7 @@ public class McastServiceImpl
Thread t = new RecoveryThread(parent);
String channelName = "";
- if (parent.channel instanceof GroupChannel
- && ((GroupChannel)parent.channel).getName() != null) {
- channelName = "[" + ((GroupChannel)parent.channel).getName() + "]";
- }
+ if (parent.channel.getName() != null) channelName = "[" + parent.channel.getName() + "]";
t.setName("Tribes-MembershipRecovery" + channelName);
t.setDaemon(true);
t.start();
diff --git a/java/org/apache/catalina/tribes/membership/MemberImpl.java b/java/org/apache/catalina/tribes/membership/MemberImpl.java
index 54e6eac..df96c28 100644
--- a/java/org/apache/catalina/tribes/membership/MemberImpl.java
+++ b/java/org/apache/catalina/tribes/membership/MemberImpl.java
@@ -658,10 +658,12 @@ public class MemberImpl implements Member, java.io.Externalizable {
this.dataPkg = null;
}
+ @Override
public boolean isLocal() {
return local;
}
+ @Override
public void setLocal(boolean local) {
this.local = local;
}
diff --git a/java/org/apache/catalina/tribes/membership/Membership.java b/java/org/apache/catalina/tribes/membership/Membership.java
index ff3490d..c696834 100644
--- a/java/org/apache/catalina/tribes/membership/Membership.java
+++ b/java/org/apache/catalina/tribes/membership/Membership.java
@@ -22,7 +22,6 @@ import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
-import java.util.Map;
import org.apache.catalina.tribes.Member;
@@ -67,8 +66,7 @@ public class Membership implements Cloneable {
@SuppressWarnings("unchecked")
final HashMap<Member, MbrEntry> tmpclone = (HashMap<Member, MbrEntry>) map.clone();
clone.map = tmpclone;
- clone.members = new Member[members.length];
- System.arraycopy(members, 0, clone.members, 0, members.length);
+ clone.members = members.clone();
return clone;
}
}
@@ -138,8 +136,7 @@ public class Membership implements Cloneable {
updateMember.setCommand(member.getCommand());
// Re-order. Can't sort in place since a call to
// getMembers() may then receive an intermediate result.
- Member[] newMembers = new Member[members.length];
- System.arraycopy(members, 0, newMembers, 0, members.length);
+ Member[] newMembers = members.clone();
Arrays.sort(newMembers, memberComparator);
members = newMembers;
}
@@ -274,20 +271,6 @@ public class Membership implements Cloneable {
return members;
}
- /**
- * Get a copy from all member entries.
- *
- * @deprecated Unused. Will be removed in Tomcat 8.5.x.
- */
- @Deprecated
- protected synchronized MbrEntry[] getMemberEntries() {
- MbrEntry[] result = new MbrEntry[map.size()];
- Iterator<Map.Entry<Member,MbrEntry>> i = map.entrySet().iterator();
- int pos = 0;
- while ( i.hasNext() )
- result[pos++] = i.next().getValue();
- return result;
- }
// --------------------------------------------- Inner Class
diff --git a/java/org/apache/catalina/tribes/tipis/AbstractReplicatedMap.java b/java/org/apache/catalina/tribes/tipis/AbstractReplicatedMap.java
index 4c96918..a49dd21 100644
--- a/java/org/apache/catalina/tribes/tipis/AbstractReplicatedMap.java
+++ b/java/org/apache/catalina/tribes/tipis/AbstractReplicatedMap.java
@@ -27,6 +27,7 @@ import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Map;
+import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
@@ -48,8 +49,8 @@ import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
/**
- *
- * @version 1.0
+ * @param <K> The type of Key
+ * @param <V> The type of Value
*/
public abstract class AbstractReplicatedMap<K,V>
implements Map<K,V>, Serializable, RpcCallback, ChannelListener,
@@ -57,8 +58,7 @@ public abstract class AbstractReplicatedMap<K,V>
private static final long serialVersionUID = 1L;
- protected static final StringManager sm =
- StringManager.getManager(AbstractReplicatedMap.class.getPackage().getName());
+ protected static final StringManager sm = StringManager.getManager(AbstractReplicatedMap.class);
private final Log log = LogFactory.getLog(AbstractReplicatedMap.class);
@@ -163,12 +163,14 @@ public abstract class AbstractReplicatedMap<K,V>
//------------------------------------------------------------------------------
/**
- * Creates a new map
+ * Creates a new map.
+ * @param owner The map owner
* @param channel The channel to use for communication
* @param timeout long - timeout for RPC messags
* @param mapContextName String - unique name for this map, to allow multiple maps per channel
* @param initialCapacity int - the size of this map, see HashMap
* @param loadFactor float - load factor, see HashMap
+ * @param channelSendOptions Send options
* @param cls - a list of classloaders to be used for deserialization of objects.
* @param terminate - Flag for whether to terminate this map that failed to start.
*/
@@ -255,7 +257,7 @@ public abstract class AbstractReplicatedMap<K,V>
* Sends a ping out to all the members in the cluster, not just map members
* that this map is alive.
* @param timeout long
- * @throws ChannelException
+ * @throws ChannelException Send error
*/
protected void ping(long timeout) throws ChannelException {
MapMessage msg = new MapMessage(this.mapContextName,
@@ -327,7 +329,7 @@ public abstract class AbstractReplicatedMap<K,V>
* Helper method to broadcast a message to all members in a channel
* @param msgtype int
* @param rpc boolean
- * @throws ChannelException
+ * @throws ChannelException Send error
*/
protected void broadcast(int msgtype, boolean rpc) throws ChannelException {
Member[] members = channel.getMembers();
@@ -421,6 +423,7 @@ public abstract class AbstractReplicatedMap<K,V>
/**
* Replicates any changes to the object since the last time
* The object has to be primary, ie, if the object is a proxy or a backup, it will not be replicated<br>
+ * @param key The object to replicate
* @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
@@ -687,7 +690,7 @@ public abstract class AbstractReplicatedMap<K,V>
entry.setCopy(mapmsg.getMsgType() == MapMessage.MSG_COPY);
entry.setBackupNodes(mapmsg.getBackupNodes());
entry.setPrimary(mapmsg.getPrimary());
- if (mapmsg.getValue()!=null && mapmsg.getValue() instanceof ReplicatedMapEntry ) {
+ if (mapmsg.getValue() instanceof ReplicatedMapEntry ) {
((ReplicatedMapEntry)mapmsg.getValue()).setOwner(getMapOwner());
}
} else {
@@ -776,7 +779,6 @@ public abstract class AbstractReplicatedMap<K,V>
if (log.isInfoEnabled())
log.info(sm.getString("abstractReplicatedMap.mapMemberAdded.added", mapMember));
mapMembers.put(mapMember, Long.valueOf(System.currentTimeMillis()));
- mapMembers.put(mapMember, Long.valueOf(System.currentTimeMillis()));
memberAdded = true;
}
}
@@ -1119,9 +1121,7 @@ public abstract class AbstractReplicatedMap<K,V>
@Override
public boolean containsValue(Object value) {
- if (value == null) {
- throw new NullPointerException();
- }
+ Objects.requireNonNull(value);
Iterator<Map.Entry<K,MapEntry<K,V>>> i = innerMap.entrySet().iterator();
while (i.hasNext()) {
Map.Entry<K,MapEntry<K,V>> e = i.next();
@@ -1243,7 +1243,7 @@ public abstract class AbstractReplicatedMap<K,V>
}
public boolean isValueSerializable() {
- return (value==null) || (value instanceof Serializable);
+ return (value == null) || (value instanceof Serializable);
}
public boolean isSerializable() {
@@ -1342,8 +1342,8 @@ public abstract class AbstractReplicatedMap<K,V>
* @param offset int
* @param length int
* @param diff boolean
- * @throws IOException
- * @throws ClassNotFoundException
+ * @throws IOException IO error
+ * @throws ClassNotFoundException Deserialization error
*/
@SuppressWarnings("unchecked")
public void apply(byte[] data, int offset, int length, boolean diff) throws IOException, ClassNotFoundException {
diff --git a/java/org/apache/catalina/tribes/tipis/LazyReplicatedMap.java b/java/org/apache/catalina/tribes/tipis/LazyReplicatedMap.java
index 3281672..421c2b4 100644
--- a/java/org/apache/catalina/tribes/tipis/LazyReplicatedMap.java
+++ b/java/org/apache/catalina/tribes/tipis/LazyReplicatedMap.java
@@ -60,7 +60,9 @@ import org.apache.juli.logging.LogFactory;
* <br><br><b>REMBER TO CALL</b> <code>breakdown()</code> or <code>finalize()</code> when you are done with the map to
* avoid memory leaks.<br><br>
* TODO implement periodic sync/transfer thread
- * @version 1.0
+ *
+ * @param <K> The type of Key
+ * @param <V> The type of Value
*/
public class LazyReplicatedMap<K,V> extends AbstractReplicatedMap<K,V> {
private static final long serialVersionUID = 1L;
@@ -72,11 +74,13 @@ public class LazyReplicatedMap<K,V> extends AbstractReplicatedMap<K,V> {
//------------------------------------------------------------------------------
/**
* Creates a new map
+ * @param owner The map owner
* @param channel The channel to use for communication
* @param timeout long - timeout for RPC messags
* @param mapContextName String - unique name for this map, to allow multiple maps per channel
* @param initialCapacity int - the size of this map, see HashMap
* @param loadFactor float - load factor, see HashMap
+ * @param cls Class loaders
*/
public LazyReplicatedMap(MapOwner owner, Channel channel, long timeout, String mapContextName, int initialCapacity, float loadFactor, ClassLoader[] cls) {
super(owner,channel,timeout,mapContextName,initialCapacity,loadFactor, Channel.SEND_OPTIONS_DEFAULT,cls, true);
@@ -84,10 +88,12 @@ public class LazyReplicatedMap<K,V> extends AbstractReplicatedMap<K,V> {
/**
* Creates a new map
+ * @param owner The map owner
* @param channel The channel to use for communication
* @param timeout long - timeout for RPC messags
* @param mapContextName String - unique name for this map, to allow multiple maps per channel
* @param initialCapacity int - the size of this map, see HashMap
+ * @param cls Class loaders
*/
public LazyReplicatedMap(MapOwner owner, Channel channel, long timeout, String mapContextName, int initialCapacity, ClassLoader[] cls) {
super(owner, channel,timeout,mapContextName,initialCapacity, AbstractReplicatedMap.DEFAULT_LOAD_FACTOR, Channel.SEND_OPTIONS_DEFAULT, cls, true);
@@ -95,9 +101,11 @@ public class LazyReplicatedMap<K,V> extends AbstractReplicatedMap<K,V> {
/**
* Creates a new map
+ * @param owner The map owner
* @param channel The channel to use for communication
* @param timeout long - timeout for RPC messags
* @param mapContextName String - unique name for this map, to allow multiple maps per channel
+ * @param cls Class loaders
*/
public LazyReplicatedMap(MapOwner owner, Channel channel, long timeout, String mapContextName, ClassLoader[] cls) {
super(owner, channel,timeout,mapContextName, AbstractReplicatedMap.DEFAULT_INITIAL_CAPACITY,AbstractReplicatedMap.DEFAULT_LOAD_FACTOR,Channel.SEND_OPTIONS_DEFAULT, cls, true);
@@ -105,9 +113,11 @@ public class LazyReplicatedMap<K,V> extends AbstractReplicatedMap<K,V> {
/**
* Creates a new map
+ * @param owner The map owner
* @param channel The channel to use for communication
* @param timeout long - timeout for RPC messags
* @param mapContextName String - unique name for this map, to allow multiple maps per channel
+ * @param cls Class loaders
* @param terminate boolean - Flag for whether to terminate this map that failed to start.
*/
public LazyReplicatedMap(MapOwner owner, Channel channel, long timeout, String mapContextName, ClassLoader[] cls, boolean terminate) {
@@ -134,7 +144,7 @@ public class LazyReplicatedMap<K,V> extends AbstractReplicatedMap<K,V> {
* @param key Object
* @param value Object
* @return Member - the backup node
- * @throws ChannelException
+ * @throws ChannelException Cluster error
*/
@Override
protected Member[] publishEntryInfo(Object key, Object value) throws ChannelException {
diff --git a/java/org/apache/catalina/tribes/tipis/ReplicatedMap.java b/java/org/apache/catalina/tribes/tipis/ReplicatedMap.java
index ee26812..4914226 100644
--- a/java/org/apache/catalina/tribes/tipis/ReplicatedMap.java
+++ b/java/org/apache/catalina/tribes/tipis/ReplicatedMap.java
@@ -51,7 +51,8 @@ import org.apache.juli.logging.LogFactory;
* TODO memberDisappeared, should do nothing except change map membership
* by default it relocates the primary objects
*
- * @version 1.0
+ * @param <K> The type of Key
+ * @param <V> The type of Value
*/
public class ReplicatedMap<K,V> extends AbstractReplicatedMap<K,V> {
@@ -64,11 +65,13 @@ public class ReplicatedMap<K,V> extends AbstractReplicatedMap<K,V> {
//--------------------------------------------------------------------------
/**
* Creates a new map
+ * @param owner The map owner
* @param channel The channel to use for communication
* @param timeout long - timeout for RPC messags
* @param mapContextName String - unique name for this map, to allow multiple maps per channel
* @param initialCapacity int - the size of this map, see HashMap
* @param loadFactor float - load factor, see HashMap
+ * @param cls Class loaders
*/
public ReplicatedMap(MapOwner owner, Channel channel, long timeout, String mapContextName, int initialCapacity,float loadFactor, ClassLoader[] cls) {
super(owner,channel, timeout, mapContextName, initialCapacity, loadFactor, Channel.SEND_OPTIONS_DEFAULT, cls, true);
@@ -76,10 +79,12 @@ public class ReplicatedMap<K,V> extends AbstractReplicatedMap<K,V> {
/**
* Creates a new map
+ * @param owner The map owner
* @param channel The channel to use for communication
* @param timeout long - timeout for RPC messags
* @param mapContextName String - unique name for this map, to allow multiple maps per channel
* @param initialCapacity int - the size of this map, see HashMap
+ * @param cls Class loaders
*/
public ReplicatedMap(MapOwner owner, Channel channel, long timeout, String mapContextName, int initialCapacity, ClassLoader[] cls) {
super(owner,channel, timeout, mapContextName, initialCapacity, AbstractReplicatedMap.DEFAULT_LOAD_FACTOR,Channel.SEND_OPTIONS_DEFAULT, cls, true);
@@ -87,9 +92,11 @@ public class ReplicatedMap<K,V> extends AbstractReplicatedMap<K,V> {
/**
* Creates a new map
+ * @param owner The map owner
* @param channel The channel to use for communication
* @param timeout long - timeout for RPC messags
* @param mapContextName String - unique name for this map, to allow multiple maps per channel
+ * @param cls Class loaders
*/
public ReplicatedMap(MapOwner owner, Channel channel, long timeout, String mapContextName, ClassLoader[] cls) {
super(owner, channel, timeout, mapContextName,AbstractReplicatedMap.DEFAULT_INITIAL_CAPACITY, AbstractReplicatedMap.DEFAULT_LOAD_FACTOR, Channel.SEND_OPTIONS_DEFAULT, cls, true);
@@ -97,9 +104,11 @@ public class ReplicatedMap<K,V> extends AbstractReplicatedMap<K,V> {
/**
* Creates a new map
+ * @param owner The map owner
* @param channel The channel to use for communication
* @param timeout long - timeout for RPC messags
* @param mapContextName String - unique name for this map, to allow multiple maps per channel
+ * @param cls Class loaders
* @param terminate boolean - Flag for whether to terminate this map that failed to start.
*/
public ReplicatedMap(MapOwner owner, Channel channel, long timeout, String mapContextName, ClassLoader[] cls, boolean terminate) {
@@ -125,7 +134,7 @@ public class ReplicatedMap<K,V> extends AbstractReplicatedMap<K,V> {
* @param key Object
* @param value Object
* @return Member - the backup node
- * @throws ChannelException
+ * @throws ChannelException Cluster error
*/
@Override
protected Member[] publishEntryInfo(Object key, Object value) throws ChannelException {
diff --git a/java/org/apache/catalina/tribes/tipis/ReplicatedMapEntry.java b/java/org/apache/catalina/tribes/tipis/ReplicatedMapEntry.java
index d717c99..2110eae 100644
--- a/java/org/apache/catalina/tribes/tipis/ReplicatedMapEntry.java
+++ b/java/org/apache/catalina/tribes/tipis/ReplicatedMapEntry.java
@@ -64,18 +64,19 @@ public interface ReplicatedMapEntry extends Serializable {
/**
* Returns a diff and sets the dirty map to false
- * @return byte[]
- * @throws IOException
+ * @return Serialized diff data
+ * @throws IOException IO error serializing
*/
public byte[] getDiff() throws IOException;
/**
* Applies a diff to an existing object.
- * @param diff byte[]
- * @param offset int
- * @param length int
- * @throws IOException
+ * @param diff Serialized diff data
+ * @param offset Array offset
+ * @param length Array length
+ * @throws IOException IO error deserializing
+ * @throws ClassNotFoundException Serialization error
*/
public void applyDiff(byte[] diff, int offset, int length) throws IOException, ClassNotFoundException;
@@ -118,13 +119,13 @@ public interface ReplicatedMapEntry extends Serializable {
public void setVersion(long version);
/**
- * Return the last replicate time.
+ * @return the last replicate time.
*/
public long getLastTimeReplicated();
/**
* Set the last replicate time.
- * @param lastTimeReplicated
+ * @param lastTimeReplicated New timestamp
*/
public void setLastTimeReplicated(long lastTimeReplicated);
diff --git a/java/org/apache/catalina/tribes/transport/PooledSender.java b/java/org/apache/catalina/tribes/transport/PooledSender.java
index 1ebe6ad..afac822 100644
--- a/java/org/apache/catalina/tribes/transport/PooledSender.java
+++ b/java/org/apache/catalina/tribes/transport/PooledSender.java
@@ -27,7 +27,8 @@ import org.apache.juli.logging.LogFactory;
public abstract class PooledSender extends AbstractSender implements MultiPointSender {
private static final Log log = LogFactory.getLog(PooledSender.class);
- protected static final StringManager sm = StringManager.getManager(Constants.Package);
+ protected static final StringManager sm =
+ StringManager.getManager(Constants.Package);
private final SenderQueue queue;
private int poolSize = 25;
diff --git a/java/org/apache/catalina/tribes/transport/ReceiverBase.java b/java/org/apache/catalina/tribes/transport/ReceiverBase.java
index e982c87..c870676 100644
--- a/java/org/apache/catalina/tribes/transport/ReceiverBase.java
+++ b/java/org/apache/catalina/tribes/transport/ReceiverBase.java
@@ -30,7 +30,6 @@ import org.apache.catalina.tribes.Channel;
import org.apache.catalina.tribes.ChannelMessage;
import org.apache.catalina.tribes.ChannelReceiver;
import org.apache.catalina.tribes.MessageListener;
-import org.apache.catalina.tribes.group.GroupChannel;
import org.apache.catalina.tribes.io.ListenCallback;
import org.apache.catalina.tribes.util.ExecutorFactory;
import org.apache.catalina.tribes.util.StringManager;
@@ -91,9 +90,7 @@ public abstract class ReceiverBase implements ChannelReceiver, ListenCallback, R
if ( executor == null ) {
//executor = new ThreadPoolExecutor(minThreads,maxThreads,60,TimeUnit.SECONDS,new LinkedBlockingQueue<Runnable>());
String channelName = "";
- if (channel instanceof GroupChannel && ((GroupChannel)channel).getName() != null) {
- channelName = "[" + ((GroupChannel)channel).getName() + "]";
- }
+ if (channel.getName() != null) channelName = "[" + channel.getName() + "]";
TaskThreadFactory tf = new TaskThreadFactory("Tribes-Task-Receiver" + channelName + "-");
executor = ExecutorFactory.newThreadPool(minThreads, maxThreads, maxIdleTime, TimeUnit.MILLISECONDS, tf);
}
@@ -179,7 +176,7 @@ public abstract class ReceiverBase implements ChannelReceiver, ListenCallback, R
* @param portstart Starting port for bind attempts
* @param retries Number of times to attempt to bind (port incremented
* between attempts)
- * @throws IOException
+ * @throws IOException Socket bind error
*/
protected void bind(ServerSocket socket, int portstart, int retries) throws IOException {
synchronized (bindLock) {
@@ -206,11 +203,12 @@ public abstract class ReceiverBase implements ChannelReceiver, ListenCallback, R
/**
* Same as bind() except it does it for the UDP port
- * @param socket
- * @param portstart
- * @param retries
- * @return int
- * @throws IOException
+ * @param socket The socket to bind
+ * @param portstart Starting port for bind attempts
+ * @param retries Number of times to attempt to bind (port incremented
+ * between attempts)
+ * @return int The retry count
+ * @throws IOException Socket bind error
*/
protected int bindUdp(DatagramSocket socket, int portstart, int retries) throws IOException {
InetSocketAddress addr = null;
@@ -492,10 +490,12 @@ public abstract class ReceiverBase implements ChannelReceiver, ListenCallback, R
this.udpTxBufSize = udpTxBufSize;
}
+ @Override
public Channel getChannel() {
return channel;
}
+ @Override
public void setChannel(Channel channel) {
this.channel = channel;
}
diff --git a/java/org/apache/catalina/tribes/transport/ReplicationTransmitter.java b/java/org/apache/catalina/tribes/transport/ReplicationTransmitter.java
index 4c5ceb3..1ab66e9 100644
--- a/java/org/apache/catalina/tribes/transport/ReplicationTransmitter.java
+++ b/java/org/apache/catalina/tribes/transport/ReplicationTransmitter.java
@@ -110,10 +110,12 @@ public class ReplicationTransmitter implements ChannelSender {
getTransport().remove(member);
}
+ @Override
public Channel getChannel() {
return channel;
}
+ @Override
public void setChannel(Channel channel) {
this.channel = channel;
}
diff --git a/java/org/apache/catalina/tribes/transport/RxTaskPool.java b/java/org/apache/catalina/tribes/transport/RxTaskPool.java
index 7ae844b..4b6b2fb 100644
--- a/java/org/apache/catalina/tribes/transport/RxTaskPool.java
+++ b/java/org/apache/catalina/tribes/transport/RxTaskPool.java
@@ -59,6 +59,7 @@ public class RxTaskPool {
/**
* Find an idle worker thread, if any. Could return null.
+ * @return a worker
*/
public AbstractRxTask getRxTask()
{
@@ -95,6 +96,7 @@ public class RxTaskPool {
/**
* Called by the worker thread to return itself to the
* idle pool.
+ * @param worker The worker
*/
public void returnWorker (AbstractRxTask worker) {
if ( running ) {
diff --git a/java/org/apache/catalina/tribes/transport/SenderState.java b/java/org/apache/catalina/tribes/transport/SenderState.java
index 7f69551..6fed593 100644
--- a/java/org/apache/catalina/tribes/transport/SenderState.java
+++ b/java/org/apache/catalina/tribes/transport/SenderState.java
@@ -17,6 +17,7 @@
package org.apache.catalina.tribes.transport;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
import org.apache.catalina.tribes.Member;
@@ -26,7 +27,7 @@ public class SenderState {
public static final int SUSPECT = 1;
public static final int FAILING = 2;
- protected static final ConcurrentHashMap<Member, SenderState> memberStates = new ConcurrentHashMap<>();
+ protected static final ConcurrentMap<Member, SenderState> memberStates = new ConcurrentHashMap<>();
public static SenderState getSenderState(Member member) {
return getSenderState(member, true);
diff --git a/java/org/apache/catalina/tribes/transport/bio/BioReceiver.java b/java/org/apache/catalina/tribes/transport/bio/BioReceiver.java
index 1e8e4d4..965e59c 100644
--- a/java/org/apache/catalina/tribes/transport/bio/BioReceiver.java
+++ b/java/org/apache/catalina/tribes/transport/bio/BioReceiver.java
@@ -20,7 +20,6 @@ import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
-import org.apache.catalina.tribes.group.GroupChannel;
import org.apache.catalina.tribes.io.ObjectReader;
import org.apache.catalina.tribes.transport.AbstractRxTask;
import org.apache.catalina.tribes.transport.ReceiverBase;
@@ -33,8 +32,7 @@ public class BioReceiver extends ReceiverBase implements Runnable {
private static final Log log = LogFactory.getLog(BioReceiver.class);
- protected static final StringManager sm =
- StringManager.getManager(BioReceiver.class.getPackage().getName());
+ protected static final StringManager sm = StringManager.getManager(BioReceiver.class);
protected ServerSocket serverSocket;
@@ -56,10 +54,7 @@ public class BioReceiver extends ReceiverBase implements Runnable {
getBind();
bind();
String channelName = "";
- if (getChannel() instanceof GroupChannel
- && ((GroupChannel)getChannel()).getName() != null) {
- channelName = "[" + ((GroupChannel)getChannel()).getName() + "]";
- }
+ if (getChannel().getName() != null) channelName = "[" + getChannel().getName() + "]";
Thread t = new Thread(this, "BioReceiver" + channelName);
t.setDaemon(true);
t.start();
diff --git a/java/org/apache/catalina/tribes/transport/bio/BioReplicationTask.java b/java/org/apache/catalina/tribes/transport/bio/BioReplicationTask.java
index 1a57f67..77fe0ee 100644
--- a/java/org/apache/catalina/tribes/transport/bio/BioReplicationTask.java
+++ b/java/org/apache/catalina/tribes/transport/bio/BioReplicationTask.java
@@ -46,8 +46,7 @@ public class BioReplicationTask extends AbstractRxTask {
private static final Log log = LogFactory.getLog( BioReplicationTask.class );
- protected static final StringManager sm =
- StringManager.getManager(BioReplicationTask.class.getPackage().getName());
+ protected static final StringManager sm = StringManager.getManager(BioReplicationTask.class);
protected Socket socket;
protected ObjectReader reader;
@@ -135,8 +134,9 @@ public class BioReplicationTask extends AbstractRxTask {
* interest in OP_READ. When this method completes it
* re-enables OP_READ and calls wakeup() on the selector
* so the selector will resume watching this channel.
+ * @throws Exception IO exception or execute exception
*/
- protected void drainSocket () throws Exception {
+ protected void drainSocket() throws Exception {
InputStream in = socket.getInputStream();
// loop while data available, channel is non-blocking
byte[] buf = new byte[1024];
@@ -150,8 +150,8 @@ public class BioReplicationTask extends AbstractRxTask {
/**
- * send a reply-acknowledgment (6,2,3)
- * @param command
+ * Send a reply-acknowledgment (6,2,3)
+ * @param command The command to write
*/
protected void sendAck(byte[] command) {
try {
diff --git a/java/org/apache/catalina/tribes/transport/bio/BioSender.java b/java/org/apache/catalina/tribes/transport/bio/BioSender.java
index 3a7d814..266dc3e 100644
--- a/java/org/apache/catalina/tribes/transport/bio/BioSender.java
+++ b/java/org/apache/catalina/tribes/transport/bio/BioSender.java
@@ -30,6 +30,8 @@ import org.apache.catalina.tribes.transport.AbstractSender;
import org.apache.catalina.tribes.transport.Constants;
import org.apache.catalina.tribes.transport.SenderState;
import org.apache.catalina.tribes.util.StringManager;
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
/**
* Send cluster messages with only one socket. Ack and keep Alive Handling is
@@ -40,13 +42,12 @@ import org.apache.catalina.tribes.util.StringManager;
*/
public class BioSender extends AbstractSender {
- private static final org.apache.juli.logging.Log log = org.apache.juli.logging.LogFactory.getLog(BioSender.class);
+ private static final Log log = LogFactory.getLog(BioSender.class);
/**
* The string manager for this package.
*/
- protected static final StringManager sm =
- StringManager.getManager(BioSender.class.getPackage().getName());
+ protected static final StringManager sm = StringManager.getManager(BioSender.class);
// ----------------------------------------------------- Instance Variables
@@ -98,6 +99,9 @@ public class BioSender extends AbstractSender {
/**
* Send message.
+ * @param data The data to send
+ * @param waitForAck Wait for an ack
+ * @throws IOException An IO error occured sending the message
*/
public void sendMessage(byte[] data, boolean waitForAck) throws IOException {
IOException exception = null;
@@ -128,9 +132,6 @@ public class BioSender extends AbstractSender {
}
- /**
- * Name of this SockerSender
- */
@Override
public String toString() {
StringBuilder buf = new StringBuilder("DataSender[(");
@@ -142,8 +143,9 @@ public class BioSender extends AbstractSender {
// --------------------------------------------------------- Protected Methods
/**
- * open real socket and set time out when waitForAck is enabled
- * is socket open return directly
+ * Open real socket and set time out when waitForAck is enabled
+ * is socket open return directly.
+ * @throws IOException Error opening socket
*/
protected void openSocket() throws IOException {
if(isConnected()) return ;
@@ -214,8 +216,10 @@ public class BioSender extends AbstractSender {
* @see #openSocket()
* @see #sendMessage(byte[], boolean)
*
- * @param data
- * data to send
+ * @param data Data to send
+ * @param reconnect Do a reconnect (close socket then reopen)
+ * @param waitForAck Wait for an acknowledgement
+ * @throws IOException IO error writing data
* @since 5.5.10
*/
@@ -233,8 +237,7 @@ public class BioSender extends AbstractSender {
/**
* Wait for Acknowledgement from other server.
* FIXME Please, not wait only for three characters, better control that the wait ack message is correct.
- * @throws java.io.IOException
- * @throws java.net.SocketTimeoutException
+ * @throws IOException An IO error occurred
*/
protected void waitForAck() throws java.io.IOException {
try {
diff --git a/java/org/apache/catalina/tribes/transport/bio/PooledMultiSender.java b/java/org/apache/catalina/tribes/transport/bio/PooledMultiSender.java
index bd51396..8c92311 100644
--- a/java/org/apache/catalina/tribes/transport/bio/PooledMultiSender.java
+++ b/java/org/apache/catalina/tribes/transport/bio/PooledMultiSender.java
@@ -27,8 +27,7 @@ import org.apache.catalina.tribes.util.StringManager;
public class PooledMultiSender extends PooledSender {
- protected static final StringManager sm =
- StringManager.getManager(PooledMultiSender.class.getPackage().getName());
+ protected static final StringManager sm = StringManager.getManager(PooledMultiSender.class);
public PooledMultiSender() {
// NO-OP
@@ -54,18 +53,10 @@ public class PooledMultiSender extends PooledSender {
}
}
- /**
- * getNewDataSender
- *
- * @return DataSender
- * TODO Implement this org.apache.catalina.tribes.transport.PooledSender
- * method
- */
@Override
public DataSender getNewDataSender() {
MultipointBioSender sender = new MultipointBioSender();
AbstractSender.transferProperties(this,sender);
return sender;
}
-
}
diff --git a/java/org/apache/catalina/tribes/transport/bio/util/FastQueue.java b/java/org/apache/catalina/tribes/transport/bio/util/FastQueue.java
deleted file mode 100644
index 19941ef..0000000
--- a/java/org/apache/catalina/tribes/transport/bio/util/FastQueue.java
+++ /dev/null
@@ -1,300 +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.tribes.transport.bio.util;
-
-import java.util.concurrent.atomic.AtomicInteger;
-
-import org.apache.catalina.tribes.ChannelMessage;
-import org.apache.catalina.tribes.Member;
-import org.apache.catalina.tribes.group.InterceptorPayload;
-import org.apache.catalina.tribes.util.StringManager;
-import org.apache.juli.logging.Log;
-import org.apache.juli.logging.LogFactory;
-
-
-
-/**
- * A fast queue that remover thread lock the adder thread. <br>Limit the queue
- * length when you have strange producer thread problems.
- *
- * @author Peter Rossbach
- *
- * @deprecated Unused. Will be removed in Tomcat 8.5.x.
- */
- at Deprecated
-public class FastQueue {
-
- private static final Log log = LogFactory.getLog(FastQueue.class);
- protected static final StringManager sm =
- StringManager.getManager(FastQueue.class.getPackage().getName());
-
- /**
- * This is the actual queue
- */
- private final SingleRemoveSynchronizedAddLock lock;
-
- /**
- * First Object at queue (consumer message)
- */
- private LinkObject first = null;
-
- /**
- * Last object in queue (producer Object)
- */
- private LinkObject last = null;
-
- /**
- * Current Queue elements size
- */
- private AtomicInteger size = new AtomicInteger(0);
-
- /**
- * limit the queue length ( default is unlimited)
- */
- private int maxQueueLength = 0;
-
- /**
- * addWaitTimeout for producer
- */
- private long addWaitTimeout = 10000L;
-
-
- /**
- * removeWaitTimeout for consumer
- */
- private long removeWaitTimeout = 30000L;
-
- /**
- * enabled the queue
- */
- private volatile boolean enabled = true;
-
- /**
- * max queue size
- */
- private int maxSize = 0;
-
- /**
- * Generate Queue SingleRemoveSynchronizedAddLock and set add and wait
- * Timeouts
- */
- public FastQueue() {
- lock = new SingleRemoveSynchronizedAddLock();
- lock.setAddWaitTimeout(addWaitTimeout);
- lock.setRemoveWaitTimeout(removeWaitTimeout);
- }
-
- /**
- * get current add wait timeout
- *
- * @return current wait timeout
- */
- public long getAddWaitTimeout() {
- addWaitTimeout = lock.getAddWaitTimeout();
- return addWaitTimeout;
- }
-
- /**
- * Set add wait timeout (default 10000 msec)
- *
- * @param timeout
- */
- public void setAddWaitTimeout(long timeout) {
- addWaitTimeout = timeout;
- lock.setAddWaitTimeout(addWaitTimeout);
- }
-
- /**
- * get current remove wait timeout
- *
- * @return The timeout
- */
- public long getRemoveWaitTimeout() {
- removeWaitTimeout = lock.getRemoveWaitTimeout();
- return removeWaitTimeout;
- }
-
- /**
- * set remove wait timeout ( default 30000 msec)
- *
- * @param timeout
- */
- public void setRemoveWaitTimeout(long timeout) {
- removeWaitTimeout = timeout;
- lock.setRemoveWaitTimeout(removeWaitTimeout);
- }
-
- public int getMaxQueueLength() {
- return maxQueueLength;
- }
-
- public void setMaxQueueLength(int length) {
- maxQueueLength = length;
- }
-
- public boolean isEnabled() {
- return enabled;
- }
-
- public void setEnabled(boolean enable) {
- enabled = enable;
- if (!enable) {
- lock.abortRemove();
- last = first = null;
- }
- }
-
- /**
- * @return The max size
- */
- public int getMaxSize() {
- return maxSize;
- }
-
- /**
- * @param size
- */
- public void setMaxSize(int size) {
- maxSize = size;
- }
-
-
- /**
- * start queuing
- */
- public void start() {
- setEnabled(true);
- }
-
- /**
- * start queuing
- */
- public void stop() {
- setEnabled(false);
- }
-
- public int getSize() {
- return size.get();
- }
-
- /**
- * Add new data to the queue.
- *
- * FIXME extract some method
- */
- public boolean add(ChannelMessage msg, Member[] destination, InterceptorPayload payload) {
- boolean ok = true;
-
- if (!enabled) {
- if (log.isInfoEnabled())
- log.info(sm.getString("fastQueue.queue.disabled"));
- return false;
- }
-
- lock.lockAdd();
- try {
- if (log.isTraceEnabled()) {
- log.trace("FastQueue.add: starting with size " + size.get());
- }
-
- if ((maxQueueLength > 0) && (size.get() >= maxQueueLength)) {
- ok = false;
- if (log.isTraceEnabled()) {
- log.trace("FastQueue.add: Could not add, since queue is full (" + size.get() + ">=" + maxQueueLength + ")");
- }
- } else {
- LinkObject element = new LinkObject(msg,destination, payload);
- if (size.get() == 0) {
- first = last = element;
- size.set(1);
- } else {
- if (last == null) {
- ok = false;
- log.error(sm.getString("fastQueue.last.null",
- Integer.toString(size.get())));
- } else {
- last.append(element);
- last = element;
- size.incrementAndGet();
- }
- }
- }
-
- if (first == null) {
- log.error(sm.getString("fastQueue.first.null", Integer.toString(size.get())));
- }
- if (last == null) {
- log.error(sm.getString("fastQueue.last.null.end", Integer.toString(size.get())));
- }
-
- if (log.isTraceEnabled()) log.trace("FastQueue.add: add ending with size " + size.get());
-
- } finally {
- lock.unlockAdd(true);
- }
- return ok;
- }
-
- /**
- * Remove the complete queued object list.
- * FIXME extract some method
- */
- public LinkObject remove() {
- LinkObject element;
- boolean gotLock;
-
- if (!enabled) {
- if (log.isInfoEnabled())
- log.info(sm.getString("fastQueue.remove.queue.disabled"));
- return null;
- }
-
- gotLock = lock.lockRemove();
- try {
-
- if (!gotLock) {
- if (enabled) {
- if (log.isInfoEnabled())
- log.info(sm.getString("fastQueue.remove.aborted"));
- } else {
- if (log.isInfoEnabled())
- log.info(sm.getString("fastQueue.remove.queue.disabled"));
- }
- return null;
- }
-
- if (log.isTraceEnabled()) {
- log.trace("FastQueue.remove: remove starting with size " + size.get());
- }
-
- element = first;
-
- first = last = null;
- size.set(0);
-
- if (log.isTraceEnabled()) {
- log.trace("FastQueue.remove: remove ending with size " + size.get());
- }
-
- } finally {
- lock.unlockRemove();
- }
- return element;
- }
-
-}
diff --git a/java/org/apache/catalina/tribes/transport/bio/util/LinkObject.java b/java/org/apache/catalina/tribes/transport/bio/util/LinkObject.java
deleted file mode 100644
index c204537..0000000
--- a/java/org/apache/catalina/tribes/transport/bio/util/LinkObject.java
+++ /dev/null
@@ -1,107 +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.tribes.transport.bio.util;
-
-import org.apache.catalina.tribes.ChannelMessage;
-import org.apache.catalina.tribes.ErrorHandler;
-import org.apache.catalina.tribes.Member;
-import org.apache.catalina.tribes.group.InterceptorPayload;
-
-/**
- * The class <b>LinkObject</b> implements an element
- * for a linked list, consisting of a general
- * data object and a pointer to the next element.
- *
- * @author Peter Rossbach
- *
- * @deprecated Unused. Will be removed in Tomcat 8.5.x
- */
- at Deprecated
-public class LinkObject {
-
- private final ChannelMessage msg;
- private LinkObject next;
- private final byte[] key ;
- private final Member[] destination;
- private final InterceptorPayload payload;
-
- /**
- * Construct a new element from the data object.
- * Sets the pointer to null.
- *
- * @param msg the message
- * @param destination TBA
- * @param payload The data object.
- */
- public LinkObject(ChannelMessage msg, Member[] destination, InterceptorPayload payload) {
- this.msg = msg;
- this.next = null;
- this.key = msg.getUniqueId();
- this.payload = payload;
- this.destination = destination;
- }
-
- /**
- * Set the next element.
- * @param next The next element.
- */
- public void append(LinkObject next) {
- this.next = next;
- }
-
- /**
- * Get the next element.
- * @return The next element.
- */
- public LinkObject next() {
- return next;
- }
-
- public void setNext(LinkObject next) {
- this.next = next;
- }
-
- /**
- * Get the data object from the element.
- * @return The data object from the element.
- */
- public ChannelMessage data() {
- return msg;
- }
-
- /**
- * Get the unique message id
- * @return the unique message id
- */
- public byte[] getKey() {
- return key;
- }
-
- public ErrorHandler getHandler() {
- return payload!=null?payload.getErrorHandler():null;
- }
-
- public InterceptorPayload getPayload() {
- return payload;
- }
-
- public Member[] getDestination() {
- return destination;
- }
-
-}
diff --git a/java/org/apache/catalina/tribes/transport/bio/util/LocalStrings.properties b/java/org/apache/catalina/tribes/transport/bio/util/LocalStrings.properties
deleted file mode 100644
index ee758e9..0000000
--- a/java/org/apache/catalina/tribes/transport/bio/util/LocalStrings.properties
+++ /dev/null
@@ -1,21 +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.
-
-fastQueue.queue.disabled=FastQueue.add: queue disabled, add aborted
-fastQueue.last.null=FastQueue.add: Could not add, since last is null although size is {0} (>0)
-fastQueue.first.null=FastQueue.add: first is null, size is {0} at end of add
-fastQueue.last.null.end=FastQueue.add: last is null, size is {0} at end of add
-fastQueue.remove.queue.disabled=FastQueue.remove: queue disabled, remove aborted
-fastQueue.remove.aborted=FastQueue.remove: Remove aborted although queue enabled
\ No newline at end of file
diff --git a/java/org/apache/catalina/tribes/transport/bio/util/SingleRemoveSynchronizedAddLock.java b/java/org/apache/catalina/tribes/transport/bio/util/SingleRemoveSynchronizedAddLock.java
deleted file mode 100644
index 573c958..0000000
--- a/java/org/apache/catalina/tribes/transport/bio/util/SingleRemoveSynchronizedAddLock.java
+++ /dev/null
@@ -1,256 +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.tribes.transport.bio.util;
-
-/**
- * The class <b>SingleRemoveSynchronizedAddLock</b> implement locking for
- * accessing the queue by a single remove thread and multiple add threads.
- *
- * A thread is only allowed to be either the remove or
- * an add thread.
- *
- * The lock can either be owned by the remove thread
- * or by a single add thread.
- *
- * If the remove thread tries to get the lock,
- * but the queue is empty, it will block (poll)
- * until an add threads adds an entry to the queue and
- * releases the lock.
- *
- * If the remove thread and add threads compete for
- * the lock and an add thread releases the lock, then
- * the remove thread will get the lock first.
- *
- * The remove thread removes all entries in the queue
- * at once and processes them without further
- * polling the queue.
- *
- * The lock is not reentrant, in the sense, that all
- * threads must release an owned lock before competing
- * for the lock again!
- *
- * @author Peter Rossbach
- * @version 1.1
- *
- * @deprecated Unused. Will be removed in Tomcat 8.5.x
- */
- at Deprecated
-public class SingleRemoveSynchronizedAddLock {
-
- public SingleRemoveSynchronizedAddLock() {
- // NO-OP
- }
-
- public SingleRemoveSynchronizedAddLock(boolean dataAvailable) {
- this.dataAvailable=dataAvailable;
- }
-
- /**
- * Time in milliseconds after which threads
- * waiting for an add lock are woken up.
- * This is used as a safety measure in case
- * thread notification via the unlock methods
- * has a bug.
- */
- private long addWaitTimeout = 10000L;
-
- /**
- * Time in milliseconds after which threads
- * waiting for a remove lock are woken up.
- * This is used as a safety measure in case
- * thread notification via the unlock methods
- * has a bug.
- */
- private long removeWaitTimeout = 30000L;
-
- /**
- * The current remove thread.
- * It is set to the remove thread polling for entries.
- * It is reset to null when the remove thread
- * releases the lock and proceeds processing
- * the removed entries.
- */
- private Thread remover = null;
-
- /**
- * A flag indicating, if an add thread owns the lock.
- */
- private boolean addLocked = false;
-
- /**
- * A flag indicating, if the remove thread owns the lock.
- */
- private boolean removeLocked = false;
-
- /**
- * A flag indicating, if the remove thread is allowed
- * to wait for the lock. The flag is set to false, when aborting.
- */
- private boolean removeEnabled = true;
-
- /**
- * A flag indicating, if the remover needs polling.
- * It indicates, if the locked object has data available
- * to be removed.
- */
- private boolean dataAvailable = false;
-
- /**
- * @return Value of addWaitTimeout
- */
- public synchronized long getAddWaitTimeout() {
- return addWaitTimeout;
- }
-
- /**
- * Set value of addWaitTimeout
- */
- public synchronized void setAddWaitTimeout(long timeout) {
- addWaitTimeout = timeout;
- }
-
- /**
- * @return Value of removeWaitTimeout
- */
- public synchronized long getRemoveWaitTimeout() {
- return removeWaitTimeout;
- }
-
- /**
- * Set value of removeWaitTimeout
- */
- public synchronized void setRemoveWaitTimeout(long timeout) {
- removeWaitTimeout = timeout;
- }
-
- /**
- * Check if the locked object has data available
- * i.e. the remover can stop poling and get the lock.
- * @return True iff the lock Object has data available.
- */
- public synchronized boolean isDataAvailable() {
- return dataAvailable;
- }
-
- /**
- * Check if an add thread owns the lock.
- * @return True iff an add thread owns the lock.
- */
- public synchronized boolean isAddLocked() {
- return addLocked;
- }
-
- /**
- * Check if the remove thread owns the lock.
- * @return True iff the remove thread owns the lock.
- */
- public synchronized boolean isRemoveLocked() {
- return removeLocked;
- }
-
- /**
- * Check if the remove thread is polling.
- * @return True iff the remove thread is polling.
- */
- public synchronized boolean isRemovePolling() {
- if ( remover != null ) {
- return true;
- }
- return false;
- }
-
- /**
- * Acquires the lock by an add thread and sets the add flag.
- * If any add thread or the remove thread already acquired the lock
- * this add thread will block until the lock is released.
- */
- public synchronized void lockAdd() {
- if ( addLocked || removeLocked ) {
- do {
- try {
- wait(addWaitTimeout);
- } catch ( InterruptedException e ) {
- Thread.currentThread().interrupt();
- }
- } while ( addLocked || removeLocked );
- }
- addLocked=true;
- }
-
- /**
- * Acquires the lock by the remove thread and sets the remove flag.
- * If any add thread already acquired the lock or the queue is
- * empty, the remove thread will block until the lock is released
- * and the queue is not empty.
- */
- public synchronized boolean lockRemove() {
- removeLocked=false;
- removeEnabled=true;
- if ( ( addLocked || ! dataAvailable ) && removeEnabled ) {
- remover=Thread.currentThread();
- do {
- try {
- wait(removeWaitTimeout);
- } catch ( InterruptedException e ) {
- Thread.currentThread().interrupt();
- }
- } while ( ( addLocked || ! dataAvailable ) && removeEnabled );
- remover=null;
- }
- if ( removeEnabled ) {
- removeLocked=true;
- }
- return removeLocked;
- }
-
- /**
- * Releases the lock by an add thread and reset the remove flag.
- * If the reader thread is polling, notify it.
- */
- public synchronized void unlockAdd(boolean dataAvailable) {
- addLocked=false;
- this.dataAvailable=dataAvailable;
- if ( ( remover != null ) && ( dataAvailable || ! removeEnabled ) ) {
- remover.interrupt();
- } else {
- notifyAll();
- }
- }
-
- /**
- * Releases the lock by the remove thread and reset the add flag.
- * Notify all waiting add threads,
- * that the lock has been released by the remove thread.
- */
- public synchronized void unlockRemove() {
- removeLocked=false;
- dataAvailable=false;
- notifyAll();
- }
-
- /**
- * Abort any polling remover thread
- */
- public synchronized void abortRemove() {
- removeEnabled=false;
- if ( remover != null ) {
- remover.interrupt();
- }
- }
-
-}
diff --git a/java/org/apache/catalina/tribes/transport/nio/NioReceiver.java b/java/org/apache/catalina/tribes/transport/nio/NioReceiver.java
index cbaec90..6432b3f 100644
--- a/java/org/apache/catalina/tribes/transport/nio/NioReceiver.java
+++ b/java/org/apache/catalina/tribes/transport/nio/NioReceiver.java
@@ -33,7 +33,6 @@ import java.util.Set;
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.concurrent.atomic.AtomicReference;
-import org.apache.catalina.tribes.group.GroupChannel;
import org.apache.catalina.tribes.io.ObjectReader;
import org.apache.catalina.tribes.transport.AbstractRxTask;
import org.apache.catalina.tribes.transport.ReceiverBase;
@@ -50,8 +49,7 @@ public class NioReceiver extends ReceiverBase implements Runnable {
/**
* The string manager for this package.
*/
- protected static final StringManager sm =
- StringManager.getManager(NioReceiver.class.getPackage().getName());
+ protected static final StringManager sm = StringManager.getManager(NioReceiver.class);
private volatile boolean running = false;
@@ -91,10 +89,7 @@ public class NioReceiver extends ReceiverBase implements Runnable {
getBind();
bind();
String channelName = "";
- if (getChannel() instanceof GroupChannel
- && ((GroupChannel)getChannel()).getName() != null) {
- channelName = "[" + ((GroupChannel)getChannel()).getName() + "]";
- }
+ if (getChannel().getName() != null) channelName = "[" + getChannel().getName() + "]";
Thread t = new Thread(this, "NioReceiver" + channelName);
t.setDaemon(true);
t.start();
@@ -122,12 +117,7 @@ public class NioReceiver extends ReceiverBase implements Runnable {
// Get the associated ServerSocket to bind it with
ServerSocket serverSocket = serverChannel.socket();
// create a new Selector for use below
- synchronized (Selector.class) {
- // Selector.open() isn't thread safe
- // http://bugs.sun.com/view_bug.do?bug_id=6427854
- // Affects 1.6.0_29, fixed in 1.7.0_01
- this.selector.set(Selector.open());
- }
+ this.selector.set(Selector.open());
// set the port the server channel will listen to
//serverSocket.bind(new InetSocketAddress(getBind(), getTcpListenPort()));
bind(serverSocket,getPort(),getAutoBind());
@@ -248,10 +238,9 @@ public class NioReceiver extends ReceiverBase implements Runnable {
/**
- * get data from channel and store in byte array
+ * Get data from channel and store in byte array
* send it to cluster
- * @throws IOException
- * @throws java.nio.channels.ClosedChannelException
+ * @throws IOException IO error
*/
protected void listen() throws Exception {
if (doListen()) {
@@ -409,6 +398,11 @@ public class NioReceiver extends ReceiverBase implements Runnable {
/**
* Register the given channel with the given selector for
* the given operations of interest
+ * @param selector The selector to use
+ * @param channel The channel
+ * @param ops The operations to register
+ * @param attach Attachment object
+ * @throws Exception IO error with channel
*/
protected void registerChannel(Selector selector,
SelectableChannel channel,
@@ -445,6 +439,7 @@ public class NioReceiver extends ReceiverBase implements Runnable {
* channel returns an EOF condition, it is closed here, which
* automatically invalidates the associated key. The selector
* will then de-register the channel on the next select call.
+ * @throws Exception IO error with channel
*/
protected void readDataFromSocket(SelectionKey key) throws Exception {
NioReplicationTask task = (NioReplicationTask) getTaskPool().getRxTask();
diff --git a/java/org/apache/catalina/tribes/transport/nio/NioReplicationTask.java b/java/org/apache/catalina/tribes/transport/nio/NioReplicationTask.java
index 6acfeb1..7b7c353 100644
--- a/java/org/apache/catalina/tribes/transport/nio/NioReplicationTask.java
+++ b/java/org/apache/catalina/tribes/transport/nio/NioReplicationTask.java
@@ -53,9 +53,8 @@ import org.apache.juli.logging.LogFactory;
*/
public class NioReplicationTask extends AbstractRxTask {
- private static final Log log = LogFactory.getLog( NioReplicationTask.class );
- protected static final StringManager sm =
- StringManager.getManager(NioReplicationTask.class.getPackage().getName());
+ private static final Log log = LogFactory.getLog(NioReplicationTask.class);
+ protected static final StringManager sm = StringManager.getManager(NioReplicationTask.class);
private ByteBuffer buffer = null;
private SelectionKey key;
@@ -131,6 +130,7 @@ public class NioReplicationTask extends AbstractRxTask {
* updated to remove OP_READ. This will cause the selector
* to ignore read-readiness for this channel while the
* worker thread is servicing it.
+ * @param key The key to process
*/
public synchronized void serviceChannel (SelectionKey key) {
if ( log.isTraceEnabled() ) log.trace("About to service key:"+key);
@@ -148,9 +148,11 @@ public class NioReplicationTask extends AbstractRxTask {
* interest in OP_READ. When this method completes it
* re-enables OP_READ and calls wakeup() on the selector
* so the selector will resume watching this channel.
+ * @param key The key to process
+ * @param reader The reader
+ * @throws Exception IO error
*/
protected void drainChannel (final SelectionKey key, ObjectReader reader) throws Exception {
- reader.setLastAccess(System.currentTimeMillis());
reader.access();
ReadableByteChannel channel = (ReadableByteChannel) key.channel();
int count=-1;
@@ -294,10 +296,12 @@ public class NioReplicationTask extends AbstractRxTask {
/**
- * send a reply-acknowledgement (6,2,3), sends it doing a busy write, the ACK is so small
- * that it should always go to the buffer
- * @param key
- * @param channel
+ * Send a reply-acknowledgement (6,2,3), sends it doing a busy write, the ACK is so small
+ * that it should always go to the buffer.
+ * @param key The key to use
+ * @param channel The channel
+ * @param command The command to write
+ * @param udpaddr Target address
*/
protected void sendAck(SelectionKey key, WritableByteChannel channel, byte[] command, SocketAddress udpaddr) {
try {
diff --git a/java/org/apache/catalina/tribes/transport/nio/NioSender.java b/java/org/apache/catalina/tribes/transport/nio/NioSender.java
index deb9e07..7a766f2 100644
--- a/java/org/apache/catalina/tribes/transport/nio/NioSender.java
+++ b/java/org/apache/catalina/tribes/transport/nio/NioSender.java
@@ -50,8 +50,7 @@ import org.apache.juli.logging.LogFactory;
public class NioSender extends AbstractSender {
private static final Log log = LogFactory.getLog(NioSender.class);
- protected static final StringManager sm =
- StringManager.getManager(NioSender.class.getPackage().getName());
+ protected static final StringManager sm = StringManager.getManager(NioSender.class);
protected Selector selector;
@@ -76,10 +75,11 @@ public class NioSender extends AbstractSender {
}
/**
- * State machine to send data
- * @param key SelectionKey
- * @return boolean
- * @throws IOException
+ * State machine to send data.
+ * @param key The key to use
+ * @param waitForAck Wait for an ack
+ * @return <code>true</code> if the processing was successful
+ * @throws IOException An IO error occurred
*/
public boolean process(SelectionKey key, boolean waitForAck) throws IOException {
int ops = key.readyOps();
diff --git a/java/org/apache/catalina/tribes/transport/nio/ParallelNioSender.java b/java/org/apache/catalina/tribes/transport/nio/ParallelNioSender.java
index 6d0f703..e9fd891 100644
--- a/java/org/apache/catalina/tribes/transport/nio/ParallelNioSender.java
+++ b/java/org/apache/catalina/tribes/transport/nio/ParallelNioSender.java
@@ -43,19 +43,13 @@ import org.apache.juli.logging.LogFactory;
public class ParallelNioSender extends AbstractSender implements MultiPointSender {
private static final Log log = LogFactory.getLog(ParallelNioSender.class);
- protected static final StringManager sm =
- StringManager.getManager(ParallelNioSender.class.getPackage().getName());
+ protected static final StringManager sm = StringManager.getManager(ParallelNioSender.class);
protected final long selectTimeout = 5000; //default 5 seconds, same as send timeout
protected final Selector selector;
protected final HashMap<Member, NioSender> nioSenders = new HashMap<>();
public ParallelNioSender() throws IOException {
- synchronized (Selector.class) {
- // Selector.open() isn't thread safe
- // http://bugs.sun.com/view_bug.do?bug_id=6427854
- // Affects 1.6.0_29, fixed in 1.7.0_01
- selector = Selector.open();
- }
+ selector = Selector.open();
setConnected(true);
}
diff --git a/java/org/apache/catalina/tribes/transport/nio/PooledParallelSender.java b/java/org/apache/catalina/tribes/transport/nio/PooledParallelSender.java
index c117401..843011e 100644
--- a/java/org/apache/catalina/tribes/transport/nio/PooledParallelSender.java
+++ b/java/org/apache/catalina/tribes/transport/nio/PooledParallelSender.java
@@ -27,8 +27,7 @@ import org.apache.catalina.tribes.transport.PooledSender;
import org.apache.catalina.tribes.util.StringManager;
public class PooledParallelSender extends PooledSender {
- protected static final StringManager sm =
- StringManager.getManager(PooledParallelSender.class.getPackage().getName());
+ protected static final StringManager sm = StringManager.getManager(PooledParallelSender.class);
@Override
public void sendMessage(Member[] destination, ChannelMessage message) throws ChannelException {
diff --git a/java/org/apache/catalina/tribes/util/Arrays.java b/java/org/apache/catalina/tribes/util/Arrays.java
index 7828bd6..16f12ac 100644
--- a/java/org/apache/catalina/tribes/util/Arrays.java
+++ b/java/org/apache/catalina/tribes/util/Arrays.java
@@ -31,7 +31,7 @@ import org.apache.catalina.tribes.membership.Membership;
* @version 1.0
*/
public class Arrays {
- protected static final StringManager sm = StringManager.getManager(Arrays.class.getPackage().getName());
+ protected static final StringManager sm = StringManager.getManager(Arrays.class);
public static boolean contains(byte[] source, int srcoffset, byte[] key, int keyoffset, int length) {
if ( srcoffset < 0 || srcoffset >= source.length) throw new ArrayIndexOutOfBoundsException(sm.getString("arrays.srcoffset.outOfBounds"));
diff --git a/java/org/apache/catalina/tribes/util/ExecutorFactory.java b/java/org/apache/catalina/tribes/util/ExecutorFactory.java
index feeb04d..e047b17 100644
--- a/java/org/apache/catalina/tribes/util/ExecutorFactory.java
+++ b/java/org/apache/catalina/tribes/util/ExecutorFactory.java
@@ -27,7 +27,7 @@ import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class ExecutorFactory {
- protected static final StringManager sm = StringManager.getManager(ExecutorFactory.class.getPackage().getName());
+ protected static final StringManager sm = StringManager.getManager(ExecutorFactory.class);
public static ExecutorService newThreadPool(int minThreads, int maxThreads, long maxIdleTime, TimeUnit unit) {
TaskQueue taskqueue = new TaskQueue();
diff --git a/java/org/apache/catalina/tribes/util/StringManager.java b/java/org/apache/catalina/tribes/util/StringManager.java
index 805968b..2be3758 100644
--- a/java/org/apache/catalina/tribes/util/StringManager.java
+++ b/java/org/apache/catalina/tribes/util/StringManager.java
@@ -18,11 +18,15 @@
package org.apache.catalina.tribes.util;
import java.text.MessageFormat;
+import java.util.Enumeration;
import java.util.Hashtable;
+import java.util.LinkedHashMap;
import java.util.Locale;
+import java.util.Map;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
+
/**
* An internationalization / localization helper class which reduces
* the bother of handling ResourceBundles and takes care of the
@@ -49,12 +53,15 @@ import java.util.ResourceBundle;
*/
public class StringManager {
+ private static int LOCALE_CACHE_SIZE = 10;
+
/**
* The ResourceBundle for this StringManager.
*/
private final ResourceBundle bundle;
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
@@ -63,58 +70,67 @@ public class StringManager {
*
* @param packageName Name of package to create StringManager for.
*/
- private StringManager(String packageName) {
- ResourceBundle b = null;
-
+ private StringManager(String packageName, Locale locale) {
String bundleName = packageName + ".LocalStrings";
+ ResourceBundle bnd = null;
try {
- b = ResourceBundle.getBundle(bundleName, Locale.getDefault());
- } catch( MissingResourceException ex ) {
+ 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
ClassLoader cl = Thread.currentThread().getContextClassLoader();
- if( cl != null ) {
+ if (cl != null) {
try {
- b = ResourceBundle.getBundle(
- bundleName, Locale.getDefault(), cl);
- } catch(MissingResourceException ex2) {
+ bnd = ResourceBundle.getBundle(bundleName, locale, cl);
+ } catch (MissingResourceException ex2) {
// Ignore
}
}
}
+ bundle = bnd;
// Get the actual locale, which may be different from the requested one
- this.bundle = b;
if (bundle != null) {
- locale = bundle.getLocale();
+ Locale bundleLocale = bundle.getLocale();
+ if (bundleLocale.equals(Locale.ROOT)) {
+ this.locale = Locale.ENGLISH;
+ } else {
+ this.locale = bundleLocale;
+ }
} else {
- locale = null;
+ this.locale = null;
}
}
- /**
- 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 <i>key</i> from underlying
- bundle or null if not found.
- @throws IllegalArgumentException if <i>key</i> is null.
+ /**
+ * 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 <i>key</i> from underlying bundle or
+ * null if not found.
+ *
+ * @throws IllegalArgumentException if <i>key</i> is null
*/
public String getString(String key) {
- if(key == null){
+ if (key == null){
String msg = "key may not have a null value";
-
throw new IllegalArgumentException(msg);
}
String str = null;
try {
- str = bundle.getString(key);
- } catch(MissingResourceException mre) {
+ // Avoid NPE if bundle is null and treat it like an MRE
+ 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 + "]";
+ // 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)
@@ -129,12 +145,15 @@ public class StringManager {
return str;
}
+
/**
* Get a string from the underlying resource bundle and format
* it with the given set of arguments.
*
- * @param key
- * @param args
+ * @param key The key for the required message
+ * @param args The values to insert into the message
+ *
+ * @return The requested string formatted with the provided arguments
*/
public String getString(final String key, final Object... args) {
String value = getString(key);
@@ -147,27 +166,116 @@ public class StringManager {
return mf.format(args, new StringBuffer(), null).toString();
}
+
+ /**
+ * Identify the Locale this StringManager is associated with
+ *
+ * @return The Locale associated with this instance
+ */
+ public Locale getLocale() {
+ return locale;
+ }
+
+
// --------------------------------------------------------------
// STATIC SUPPORT METHODS
// --------------------------------------------------------------
- private static final Hashtable<String, StringManager> managers =
- new Hashtable<>();
+ private static final Map<String, Map<Locale,StringManager>> managers =
+ new Hashtable<>();
+
+
+ /**
+ * The StringManager will be returned for the package in which the class is
+ * located. If a manager for that package already exists, it will be reused,
+ * else a new StringManager will be created and returned.
+ *
+ * @param clazz The class for which to retrieve the StringManager
+ *
+ * @return The StringManager for the given class.
+ */
+ public static final StringManager getManager(Class<?> clazz) {
+ return getManager(clazz.getPackage().getName());
+ }
+
/**
- * Get the StringManager for a particular package. If a manager for
- * a package already exists, it will be reused, else a new
+ * 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 StringManager for the given package.
+ */
+ public static final StringManager getManager(String packageName) {
+ return getManager(packageName, Locale.getDefault());
+ }
+
+
+ /**
+ * If a manager for a package/Locale combination already exists, it will be
+ * reused, else a new StringManager will be created and returned.
+ *
+ * @param packageName The package name
+ * @param locale The Locale
+ *
+ * @return The StringManager for a particular package and Locale
*/
- public static final synchronized StringManager getManager(String packageName) {
- StringManager mgr = managers.get(packageName);
+ public static final synchronized StringManager getManager(
+ String packageName, Locale locale) {
+
+ Map<Locale,StringManager> map = managers.get(packageName);
+ if (map == null) {
+ /*
+ * Don't want the HashMap to be expanded beyond 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
+ *
+ */
+ map = new LinkedHashMap<Locale,StringManager>(LOCALE_CACHE_SIZE, 1, true) {
+ private static final long serialVersionUID = 1L;
+ @Override
+ protected boolean removeEldestEntry(
+ Map.Entry<Locale,StringManager> eldest) {
+ if (size() > (LOCALE_CACHE_SIZE - 1)) {
+ return true;
+ }
+ return false;
+ }
+ };
+ managers.put(packageName, map);
+ }
+
+ StringManager mgr = map.get(locale);
if (mgr == null) {
- mgr = new StringManager(packageName);
- managers.put(packageName, mgr);
+ mgr = new StringManager(packageName, locale);
+ map.put(locale, mgr);
}
return mgr;
}
+
+ /**
+ * Retrieve the StringManager for a list of Locales. The first StringManager
+ * found will be returned.
+ *
+ * @param packageName The package for which the StringManager is required
+ * @param requestedLocales the list of Locales
+ *
+ * @return the found StringManager or the default StringManager
+ */
+ public static StringManager getManager(String packageName,
+ Enumeration<Locale> requestedLocales) {
+ while (requestedLocales.hasMoreElements()) {
+ Locale locale = requestedLocales.nextElement();
+ StringManager result = getManager(packageName, locale);
+ if (result.getLocale().equals(locale)) {
+ return result;
+ }
+ }
+ // Return the default
+ return getManager(packageName);
+ }
}
diff --git a/java/org/apache/catalina/tribes/util/UUIDGenerator.java b/java/org/apache/catalina/tribes/util/UUIDGenerator.java
index 5b16739..44dfffc 100644
--- a/java/org/apache/catalina/tribes/util/UUIDGenerator.java
+++ b/java/org/apache/catalina/tribes/util/UUIDGenerator.java
@@ -27,9 +27,9 @@ import org.apache.juli.logging.LogFactory;
* @version 1.0
*/
public class UUIDGenerator {
- private static final Log log = LogFactory.getLog(UUIDGenerator.class.getPackage().getName());
+ private static final Log log = LogFactory.getLog(UUIDGenerator.class);
protected static final StringManager sm =
- StringManager.getManager("org.apache.catalina.tribes.util");
+ StringManager.getManager("org.apache.catalina.tribes.util");
public static final int UUID_LENGTH = 16;
public static final int UUID_VERSION = 4;
diff --git a/java/org/apache/catalina/users/MemoryUser.java b/java/org/apache/catalina/users/MemoryUser.java
index c54e97d..744c811 100644
--- a/java/org/apache/catalina/users/MemoryUser.java
+++ b/java/org/apache/catalina/users/MemoryUser.java
@@ -254,6 +254,7 @@ public class MemoryUser extends AbstractUser {
* the reader that processes this entry will accept either
* <code>username</code> or <code>name</code> for the username
* property.</p>
+ * @return the XML representation
*/
public String toXml() {
diff --git a/java/org/apache/catalina/users/MemoryUserDatabase.java b/java/org/apache/catalina/users/MemoryUserDatabase.java
index e3d2c43..10bba76 100644
--- a/java/org/apache/catalina/users/MemoryUserDatabase.java
+++ b/java/org/apache/catalina/users/MemoryUserDatabase.java
@@ -139,7 +139,7 @@ public class MemoryUserDatabase implements UserDatabase {
/**
- * Return the set of {@link Group}s defined in this user database.
+ * @return the set of {@link Group}s defined in this user database.
*/
@Override
public Iterator<Group> getGroups() {
@@ -152,7 +152,7 @@ public class MemoryUserDatabase implements UserDatabase {
/**
- * Return the unique global identifier of this user database.
+ * @return the unique global identifier of this user database.
*/
@Override
public String getId() {
@@ -163,7 +163,7 @@ public class MemoryUserDatabase implements UserDatabase {
/**
- * Return the relative or absolute pathname to the persistent storage file.
+ * @return the relative or absolute pathname to the persistent storage file.
*/
public String getPathname() {
@@ -187,7 +187,7 @@ public class MemoryUserDatabase implements UserDatabase {
/**
- * Returning the readonly status of the user database
+ * @return the readonly status of the user database
*/
public boolean getReadonly() {
@@ -209,7 +209,7 @@ public class MemoryUserDatabase implements UserDatabase {
/**
- * Return the set of {@link Role}s defined in this user database.
+ * @return the set of {@link Role}s defined in this user database.
*/
@Override
public Iterator<Role> getRoles() {
@@ -222,7 +222,7 @@ public class MemoryUserDatabase implements UserDatabase {
/**
- * Return the set of {@link User}s defined in this user database.
+ * @return the set of {@link User}s defined in this user database.
*/
@Override
public Iterator<User> getUsers() {
@@ -485,6 +485,7 @@ public class MemoryUserDatabase implements UserDatabase {
/**
* Check for permissions to save this user database to persistent storage
* location.
+ * @return <code>true</code> if the database is writable
*/
public boolean isWriteable() {
diff --git a/java/org/apache/catalina/users/mbeans-descriptors.xml b/java/org/apache/catalina/users/mbeans-descriptors.xml
index 504f706..11c3af8 100644
--- a/java/org/apache/catalina/users/mbeans-descriptors.xml
+++ b/java/org/apache/catalina/users/mbeans-descriptors.xml
@@ -1,4 +1,4 @@
-<?xml version="1.0"?>
+<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
diff --git a/java/org/apache/catalina/util/CharsetMapper.java b/java/org/apache/catalina/util/CharsetMapper.java
index fa8ea65..4436985 100644
--- a/java/org/apache/catalina/util/CharsetMapper.java
+++ b/java/org/apache/catalina/util/CharsetMapper.java
@@ -97,6 +97,7 @@ public class CharsetMapper {
* content type header.
*
* @param locale The locale for which to calculate a character set
+ * @return the charset name
*/
public String getCharset(Locale locale) {
// Match full language_country_variant first, then language_country,
diff --git a/java/org/apache/catalina/util/Conversions.java b/java/org/apache/catalina/util/Conversions.java
deleted file mode 100644
index 322fdbb..0000000
--- a/java/org/apache/catalina/util/Conversions.java
+++ /dev/null
@@ -1,42 +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.io.IOException;
-
-public class Conversions {
-
- private Conversions() {
- // Utility class. Hide default constructor.
- }
-
- public static long byteArrayToLong(byte[] input) throws IOException {
- if (input.length > 8) {
- // TODO: Better message
- throw new IOException();
- }
-
- int shift = 0;
- long result = 0;
- for (int i = input.length - 1; i >= 0; i--) {
- result = result + ((input[i] & 0xFF) << shift);
- shift += 8;
- }
-
- return result;
- }
-}
diff --git a/java/org/apache/catalina/util/CustomObjectInputStream.java b/java/org/apache/catalina/util/CustomObjectInputStream.java
index 5d1fb58..80bcb04 100644
--- a/java/org/apache/catalina/util/CustomObjectInputStream.java
+++ b/java/org/apache/catalina/util/CustomObjectInputStream.java
@@ -110,9 +110,18 @@ public final class CustomObjectInputStream extends ObjectInputStream {
Set<String> reportedClasses;
synchronized (reportedClassCache) {
reportedClasses = reportedClassCache.get(classLoader);
- if (reportedClasses == null) {
- reportedClasses = Collections.newSetFromMap(new ConcurrentHashMap<String,Boolean>());
- reportedClassCache.put(classLoader, reportedClasses);
+ }
+ if (reportedClasses == null) {
+ reportedClasses = Collections.newSetFromMap(new ConcurrentHashMap<String,Boolean>());
+ synchronized (reportedClassCache) {
+ Set<String> original = reportedClassCache.get(classLoader);
+ if (original == null) {
+ reportedClassCache.put(classLoader, reportedClasses);
+ } else {
+ // Concurrent attempts to create the new Set. Make sure all
+ // threads use the first successfully added Set.
+ reportedClasses = original;
+ }
}
}
this.reportedClasses = reportedClasses;
diff --git a/java/org/apache/catalina/util/DOMWriter.java b/java/org/apache/catalina/util/DOMWriter.java
index b3cda6e..787fed0 100644
--- a/java/org/apache/catalina/util/DOMWriter.java
+++ b/java/org/apache/catalina/util/DOMWriter.java
@@ -34,10 +34,18 @@ public class DOMWriter {
/** Default Encoding */
private static final String PRINTWRITER_ENCODING = "UTF8";
- /** Print writer. */
+ /** Print writer.
+ *
+ * @deprecated Will be made private in Tomcat 9.
+ */
+ @Deprecated
protected final PrintWriter out;
- /** Canonical output. */
+ /** Canonical output.
+ *
+ * @deprecated Will be made private in Tomcat 9.
+ */
+ @Deprecated
protected final boolean canonical;
@@ -47,12 +55,21 @@ public class DOMWriter {
}
+ /**
+ * @deprecated Unused. Will be removed in Tomcat 9.
+ *
+ * @return Always <code>UTF-8</code>
+ */
+ @Deprecated
public static String getWriterEncoding() {
return (PRINTWRITER_ENCODING);
}
- /** Prints the specified node, recursively. */
+ /**
+ * Prints the specified node, recursively.
+ * @param node The node to output
+ */
public void print(Node node) {
// is there anything to do?
@@ -65,16 +82,7 @@ public class DOMWriter {
// print document
case Node.DOCUMENT_NODE:
if (!canonical) {
- String Encoding = getWriterEncoding();
- if (Encoding.equalsIgnoreCase("DEFAULT"))
- Encoding = "UTF-8";
- else if (Encoding.equalsIgnoreCase("Unicode"))
- Encoding = "UTF-16";
- else
- Encoding = MIME2Java.reverse(Encoding);
-
- out.println("<?xml version=\"1.0\" encoding=\"" + Encoding +
- "\"?>");
+ out.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
}
print(((Document) node).getDocumentElement());
out.flush();
@@ -161,7 +169,14 @@ public class DOMWriter {
}
- /** Returns a sorted list of attributes. */
+ /**
+ * Returns a sorted list of attributes.
+ * @param attrs The map to sort
+ * @return a sorted attribute array
+ *
+ * @deprecated Will be made private in Tomcat 9.
+ */
+ @Deprecated
protected Attr[] sortAttributes(NamedNodeMap attrs) {
if (attrs == null) {
return new Attr[0];
@@ -195,7 +210,14 @@ public class DOMWriter {
}
- /** Normalizes the given string. */
+ /**
+ * Normalizes the given string.
+ * @param s The string to escape
+ * @return the escaped string
+ *
+ * @deprecated Will be made private in Tomcat 9.
+ */
+ @Deprecated
protected String normalize(String s) {
if (s == null) {
return "";
diff --git a/java/org/apache/catalina/util/Extension.java b/java/org/apache/catalina/util/Extension.java
index c26b0d6..442189b 100644
--- a/java/org/apache/catalina/util/Extension.java
+++ b/java/org/apache/catalina/util/Extension.java
@@ -176,46 +176,44 @@ public final class Extension {
* <code>false</code>.
*
* @param required Extension of the required optional package
+ * @return <code>true</code> if the extension is satisfied
*/
public boolean isCompatibleWith(Extension required) {
// Extension Name must match
if (extensionName == null)
- return (false);
+ return false;
if (!extensionName.equals(required.getExtensionName()))
- return (false);
+ return false;
// If specified, available specification version must be >= required
if (required.getSpecificationVersion() != null) {
if (!isNewer(specificationVersion,
required.getSpecificationVersion()))
- return (false);
+ return false;
}
// If specified, Implementation Vendor ID must match
if (required.getImplementationVendorId() != null) {
if (implementationVendorId == null)
- return (false);
+ return false;
if (!implementationVendorId.equals(required
.getImplementationVendorId()))
- return (false);
+ return false;
}
// If specified, Implementation version must be >= required
if (required.getImplementationVersion() != null) {
if (!isNewer(implementationVersion,
required.getImplementationVersion()))
- return (false);
+ return false;
}
// This available optional package satisfies the requirements
- return (true);
+ return true;
}
- /**
- * Return a String representation of this object.
- */
@Override
public String toString() {
@@ -268,9 +266,9 @@ public final class Extension {
throws NumberFormatException {
if ((first == null) || (second == null))
- return (false);
+ return false;
if (first.equals(second))
- return (true);
+ return true;
StringTokenizer fTok = new StringTokenizer(first, ".", true);
StringTokenizer sTok = new StringTokenizer(second, ".", true);
@@ -286,16 +284,16 @@ public final class Extension {
else
sVersion = 0;
if (fVersion < sVersion)
- return (false);
+ return false;
else if (fVersion > sVersion)
- return (true);
+ return true;
if (fTok.hasMoreTokens()) // Swallow the periods
fTok.nextToken();
if (sTok.hasMoreTokens())
sTok.nextToken();
}
- return (true); // Exact match
+ return true; // Exact match
}
diff --git a/java/org/apache/catalina/util/ExtensionValidator.java b/java/org/apache/catalina/util/ExtensionValidator.java
index 5db52c2..5122c4f 100644
--- a/java/org/apache/catalina/util/ExtensionValidator.java
+++ b/java/org/apache/catalina/util/ExtensionValidator.java
@@ -30,6 +30,8 @@ import java.util.jar.Manifest;
import org.apache.catalina.Context;
import org.apache.catalina.WebResource;
import org.apache.catalina.WebResourceRoot;
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.res.StringManager;
@@ -46,8 +48,7 @@ import org.apache.tomcat.util.res.StringManager;
*/
public final class ExtensionValidator {
- private static final org.apache.juli.logging.Log log=
- org.apache.juli.logging.LogFactory.getLog(ExtensionValidator.class);
+ private static final Log log = LogFactory.getLog(ExtensionValidator.class);
/**
* The string resources for this package.
@@ -120,6 +121,7 @@ public final class ExtensionValidator {
* application
*
* @return true if all required extensions satisfied
+ * @throws IOException Error reading resources needed for validation
*/
public static synchronized boolean validateApplication(
WebResourceRoot resources,
@@ -167,6 +169,7 @@ public final class ExtensionValidator {
* it to the container's manifest resources.
*
* @param jarFile The system JAR whose manifest to add
+ * @throws IOException Error reading JAR file
*/
public static void addSystemResource(File jarFile) throws IOException {
try (InputStream is = new FileInputStream(jarFile)) {
diff --git a/java/org/apache/catalina/util/IOTools.java b/java/org/apache/catalina/util/IOTools.java
index abcc559..e8aa56e 100644
--- a/java/org/apache/catalina/util/IOTools.java
+++ b/java/org/apache/catalina/util/IOTools.java
@@ -42,6 +42,7 @@ public class IOTools {
* @param reader the reader to read from.
* @param writer the writer to write to.
* @param buf the char array to use as a buffer
+ * @throws IOException IO error
*/
public static void flow( Reader reader, Writer writer, char[] buf )
throws IOException {
@@ -52,6 +53,12 @@ public class IOTools {
}
/**
+ * Read input from reader and write it to writer until there is no more
+ * input from reader.
+ *
+ * @param reader the reader to read from.
+ * @param writer the writer to write to.
+ * @throws IOException IO error
* @see #flow( Reader, Writer, char[] )
*/
public static void flow( Reader reader, Writer writer )
@@ -60,31 +67,22 @@ public class IOTools {
flow( reader, writer, buf );
}
+
/**
- * Read input from input stream and write it to output stream
- * until there is no more input from input stream.
+ * Read input from input stream and write it to output stream until there is
+ * no more input from input stream using a new buffer of the default size
+ * (4kB).
*
* @param is input stream the input stream to read from.
* @param os output stream the output stream to write to.
- * @param buf the byte array to use as a buffer
*
- * @deprecated Unused. Will be removed in 8.5.x
+ * @throws IOException If an I/O error occurs during the copy
*/
- @Deprecated
- public static void flow( InputStream is, OutputStream os, byte[] buf )
- throws IOException {
+ public static void flow(InputStream is, OutputStream os) throws IOException {
+ byte[] buf = new byte[DEFAULT_BUFFER_SIZE];
int numRead;
while ( (numRead = is.read(buf) ) >= 0) {
os.write(buf, 0, numRead);
}
}
-
- /**
- * @see #flow( java.io.InputStream, java.io.OutputStream, byte[] )
- */
- public static void flow( InputStream is, OutputStream os )
- throws IOException {
- byte[] buf = new byte[DEFAULT_BUFFER_SIZE];
- flow( is, os, buf );
- }
}
diff --git a/java/org/apache/catalina/util/InstanceSupport.java b/java/org/apache/catalina/util/InstanceSupport.java
deleted file mode 100644
index 950e2c6..0000000
--- a/java/org/apache/catalina/util/InstanceSupport.java
+++ /dev/null
@@ -1,342 +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 javax.servlet.Filter;
-import javax.servlet.Servlet;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
-
-import org.apache.catalina.InstanceEvent;
-import org.apache.catalina.InstanceListener;
-import org.apache.catalina.Wrapper;
-
-
-/**
- * Support class to assist in firing InstanceEvent notifications to
- * registered InstanceListeners.
- *
- * @author Craig R. McClanahan
- *
- * @deprecated Will be removed in 8.5.x onwards
- */
- at Deprecated
-public final class InstanceSupport {
-
-
- // ----------------------------------------------------------- Constructors
-
-
- /**
- * Construct a new InstanceSupport object associated with the specified
- * Instance component.
- *
- * @param wrapper The component that will be the source
- * of events that we fire
- */
- public InstanceSupport(Wrapper wrapper) {
-
- super();
- this.wrapper = wrapper;
-
- }
-
-
- // ----------------------------------------------------- Instance Variables
-
-
- /**
- * The set of registered InstanceListeners for event notifications.
- */
- private InstanceListener listeners[] = new InstanceListener[0];
-
- private final Object listenersLock = new Object(); // Lock object for changes to listeners
-
-
- /**
- * The source component for instance events that we will fire.
- */
- private final Wrapper wrapper;
-
-
- // ------------------------------------------------------------- Properties
-
-
- /**
- * Return the Wrapper with which we are associated.
- */
- public Wrapper getWrapper() {
-
- return (this.wrapper);
-
- }
-
-
- // --------------------------------------------------------- Public Methods
-
-
- /**
- * Add a lifecycle event listener to this component.
- *
- * @param listener The listener to add
- */
- public void addInstanceListener(InstanceListener listener) {
-
- synchronized (listenersLock) {
- InstanceListener results[] =
- new InstanceListener[listeners.length + 1];
- for (int i = 0; i < listeners.length; i++)
- results[i] = listeners[i];
- results[listeners.length] = listener;
- listeners = results;
- }
-
- }
-
-
- /**
- * Notify all lifecycle event listeners that a particular event has
- * occurred for this Container. The default implementation performs
- * this notification synchronously using the calling thread.
- *
- * @param type Event type
- * @param filter The relevant Filter for this event
- */
- public void fireInstanceEvent(String type, Filter filter) {
-
- if (listeners.length == 0)
- return;
-
- InstanceEvent event = new InstanceEvent(wrapper, filter, type);
- InstanceListener interested[] = listeners;
- for (int i = 0; i < interested.length; i++)
- interested[i].instanceEvent(event);
-
- }
-
-
- /**
- * Notify all lifecycle event listeners that a particular event has
- * occurred for this Container. The default implementation performs
- * this notification synchronously using the calling thread.
- *
- * @param type Event type
- * @param filter The relevant Filter for this event
- * @param exception Exception that occurred
- */
- public void fireInstanceEvent(String type, Filter filter,
- Throwable exception) {
-
- if (listeners.length == 0)
- return;
-
- InstanceEvent event = new InstanceEvent(wrapper, filter, type,
- exception);
- InstanceListener interested[] = listeners;
- for (int i = 0; i < interested.length; i++)
- interested[i].instanceEvent(event);
-
- }
-
-
- /**
- * Notify all lifecycle event listeners that a particular event has
- * occurred for this Container. The default implementation performs
- * this notification synchronously using the calling thread.
- *
- * @param type Event type
- * @param filter The relevant Filter for this event
- * @param request The servlet request we are processing
- * @param response The servlet response we are processing
- */
- public void fireInstanceEvent(String type, Filter filter,
- ServletRequest request,
- ServletResponse response) {
-
- if (listeners.length == 0)
- return;
-
- InstanceEvent event = new InstanceEvent(wrapper, filter, type,
- request, response);
- InstanceListener interested[] = listeners;
- for (int i = 0; i < interested.length; i++)
- interested[i].instanceEvent(event);
-
- }
-
-
- /**
- * Notify all lifecycle event listeners that a particular event has
- * occurred for this Container. The default implementation performs
- * this notification synchronously using the calling thread.
- *
- * @param type Event type
- * @param filter The relevant Filter for this event
- * @param request The servlet request we are processing
- * @param response The servlet response we are processing
- * @param exception Exception that occurred
- */
- public void fireInstanceEvent(String type, Filter filter,
- ServletRequest request,
- ServletResponse response,
- Throwable exception) {
-
- if (listeners.length == 0)
- return;
-
- InstanceEvent event = new InstanceEvent(wrapper, filter, type,
- request, response, exception);
- InstanceListener interested[] = listeners;
- for (int i = 0; i < interested.length; i++)
- interested[i].instanceEvent(event);
-
- }
-
-
- /**
- * Notify all lifecycle event listeners that a particular event has
- * occurred for this Container. The default implementation performs
- * this notification synchronously using the calling thread.
- *
- * @param type Event type
- * @param servlet The relevant Servlet for this event
- */
- public void fireInstanceEvent(String type, Servlet servlet) {
-
- if (listeners.length == 0)
- return;
-
- InstanceEvent event = new InstanceEvent(wrapper, servlet, type);
- InstanceListener interested[] = listeners;
- for (int i = 0; i < interested.length; i++)
- interested[i].instanceEvent(event);
-
- }
-
-
- /**
- * Notify all lifecycle event listeners that a particular event has
- * occurred for this Container. The default implementation performs
- * this notification synchronously using the calling thread.
- *
- * @param type Event type
- * @param servlet The relevant Servlet for this event
- * @param exception Exception that occurred
- */
- public void fireInstanceEvent(String type, Servlet servlet,
- Throwable exception) {
-
- if (listeners.length == 0)
- return;
-
- InstanceEvent event = new InstanceEvent(wrapper, servlet, type,
- exception);
- InstanceListener interested[] = listeners;
- for (int i = 0; i < interested.length; i++)
- interested[i].instanceEvent(event);
-
- }
-
-
- /**
- * Notify all lifecycle event listeners that a particular event has
- * occurred for this Container. The default implementation performs
- * this notification synchronously using the calling thread.
- *
- * @param type Event type
- * @param servlet The relevant Servlet for this event
- * @param request The servlet request we are processing
- * @param response The servlet response we are processing
- */
- public void fireInstanceEvent(String type, Servlet servlet,
- ServletRequest request,
- ServletResponse response) {
-
- if (listeners.length == 0)
- return;
-
- InstanceEvent event = new InstanceEvent(wrapper, servlet, type,
- request, response);
- InstanceListener interested[] = listeners;
- for (int i = 0; i < interested.length; i++)
- interested[i].instanceEvent(event);
-
- }
-
-
- /**
- * Notify all lifecycle event listeners that a particular event has
- * occurred for this Container. The default implementation performs
- * this notification synchronously using the calling thread.
- *
- * @param type Event type
- * @param servlet The relevant Servlet for this event
- * @param request The servlet request we are processing
- * @param response The servlet response we are processing
- * @param exception Exception that occurred
- */
- public void fireInstanceEvent(String type, Servlet servlet,
- ServletRequest request,
- ServletResponse response,
- Throwable exception) {
-
- if (listeners.length == 0)
- return;
-
- InstanceEvent event = new InstanceEvent(wrapper, servlet, type,
- request, response, exception);
- InstanceListener interested[] = listeners;
- for (int i = 0; i < interested.length; i++)
- interested[i].instanceEvent(event);
-
- }
-
-
- /**
- * Remove a lifecycle event listener from this component.
- *
- * @param listener The listener to remove
- */
- public void removeInstanceListener(InstanceListener listener) {
-
- synchronized (listenersLock) {
- int n = -1;
- for (int i = 0; i < listeners.length; i++) {
- if (listeners[i] == listener) {
- n = i;
- break;
- }
- }
- if (n < 0)
- return;
- InstanceListener results[] =
- new InstanceListener[listeners.length - 1];
- int j = 0;
- for (int i = 0; i < listeners.length; i++) {
- if (i != n)
- results[j++] = listeners[i];
- }
- listeners = results;
- }
-
- }
-
-
-}
diff --git a/java/org/apache/catalina/util/Introspection.java b/java/org/apache/catalina/util/Introspection.java
index 0e5a1db..3de1a92 100644
--- a/java/org/apache/catalina/util/Introspection.java
+++ b/java/org/apache/catalina/util/Introspection.java
@@ -44,6 +44,8 @@ public class Introspection {
*
* Note: This method assumes that the method name has already been checked
* for correctness.
+ * @param setter The setter method
+ * @return the bean property name
*/
public static String getPropertyName(Method setter) {
return Introspector.decapitalize(setter.getName().substring(3));
@@ -91,6 +93,8 @@ public class Introspection {
/**
* Obtain the declared fields for a class taking account of any security
* manager that may be configured.
+ * @param clazz The class to introspect
+ * @return the class fields as an array
*/
public static Field[] getDeclaredFields(final Class<?> clazz) {
Field[] fields = null;
@@ -112,6 +116,8 @@ public class Introspection {
/**
* Obtain the declared methods for a class taking account of any security
* manager that may be configured.
+ * @param clazz The class to introspect
+ * @return the class methods as an array
*/
public static Method[] getDeclaredMethods(final Class<?> clazz) {
Method[] methods = null;
@@ -134,6 +140,10 @@ public class Introspection {
* Attempt to load a class using the given Container's class loader. If the
* class cannot be loaded, a debug level log message will be written to the
* Container's log and null will be returned.
+ * @param context The class loader of this context will be used to attemmpt
+ * to load the class
+ * @param className The class name
+ * @return the loaded class or <code>null</code> if loading failed
*/
public static Class<?> loadClass(Context context, String className) {
ClassLoader cl = context.getLoader().getClassLoader();
@@ -141,11 +151,7 @@ public class Introspection {
Class<?> clazz = null;
try {
clazz = cl.loadClass(className);
- } catch (ClassNotFoundException e) {
- log.debug(sm.getString("introspection.classLoadFailed", className), e);
- } catch (NoClassDefFoundError e) {
- log.debug(sm.getString("introspection.classLoadFailed", className), e);
- } catch (ClassFormatError e) {
+ } catch (ClassNotFoundException | NoClassDefFoundError | ClassFormatError e) {
log.debug(sm.getString("introspection.classLoadFailed", className), e);
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
diff --git a/java/org/apache/catalina/util/LifecycleBase.java b/java/org/apache/catalina/util/LifecycleBase.java
index 4c822c0..0b6e77e 100644
--- a/java/org/apache/catalina/util/LifecycleBase.java
+++ b/java/org/apache/catalina/util/LifecycleBase.java
@@ -17,7 +17,11 @@
package org.apache.catalina.util;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+
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;
@@ -36,15 +40,13 @@ public abstract class LifecycleBase implements Lifecycle {
private static final Log log = LogFactory.getLog(LifecycleBase.class);
- private static final StringManager sm =
- StringManager.getManager("org.apache.catalina.util");
+ private static final StringManager sm = StringManager.getManager(LifecycleBase.class);
/**
- * Used to handle firing lifecycle events.
- * TODO: Consider merging LifecycleSupport into this class.
+ * The list of registered LifecycleListeners for event notifications.
*/
- private final LifecycleSupport lifecycle = new LifecycleSupport(this);
+ private final List<LifecycleListener> lifecycleListeners = new CopyOnWriteArrayList<>();
/**
@@ -58,7 +60,7 @@ public abstract class LifecycleBase implements Lifecycle {
*/
@Override
public void addLifecycleListener(LifecycleListener listener) {
- lifecycle.addLifecycleListener(listener);
+ lifecycleListeners.add(listener);
}
@@ -67,7 +69,7 @@ public abstract class LifecycleBase implements Lifecycle {
*/
@Override
public LifecycleListener[] findLifecycleListeners() {
- return lifecycle.findLifecycleListeners();
+ return lifecycleListeners.toArray(new LifecycleListener[0]);
}
@@ -76,7 +78,7 @@ public abstract class LifecycleBase implements Lifecycle {
*/
@Override
public void removeLifecycleListener(LifecycleListener listener) {
- lifecycle.removeLifecycleListener(listener);
+ lifecycleListeners.remove(listener);
}
@@ -87,7 +89,10 @@ public abstract class LifecycleBase implements Lifecycle {
* @param data Data associated with event.
*/
protected void fireLifecycleEvent(String type, Object data) {
- lifecycle.fireLifecycleEvent(type, data);
+ LifecycleEvent event = new LifecycleEvent(this, type, data);
+ for (LifecycleListener listener : lifecycleListeners) {
+ listener.lifecycleEvent(event);
+ }
}
@@ -175,7 +180,7 @@ public abstract class LifecycleBase implements Lifecycle {
* will be called on the failed component but the parent component will
* continue to start normally.
*
- * @throws LifecycleException
+ * @throws LifecycleException Start error occurred
*/
protected abstract void startInternal() throws LifecycleException;
@@ -246,7 +251,7 @@ public abstract class LifecycleBase implements Lifecycle {
* {@link LifecycleState#STOPPING} during the execution of this method.
* Changing state will trigger the {@link Lifecycle#STOP_EVENT} event.
*
- * @throws LifecycleException
+ * @throws LifecycleException Stop error occurred
*/
protected abstract void stopInternal() throws LifecycleException;
@@ -327,6 +332,7 @@ public abstract class LifecycleBase implements Lifecycle {
* transition is valid for a sub-class.
*
* @param state The new state for this component
+ * @throws LifecycleException when attempting to set an invalid state
*/
protected synchronized void setState(LifecycleState state)
throws LifecycleException {
@@ -342,6 +348,7 @@ public abstract class LifecycleBase implements Lifecycle {
*
* @param state The new state for this component
* @param data The data to pass to the associated {@link Lifecycle} event
+ * @throws LifecycleException when attempting to set an invalid state
*/
protected synchronized void setState(LifecycleState state, Object data)
throws LifecycleException {
diff --git a/java/org/apache/catalina/util/LifecycleSupport.java b/java/org/apache/catalina/util/LifecycleSupport.java
deleted file mode 100644
index 3e75c90..0000000
--- a/java/org/apache/catalina/util/LifecycleSupport.java
+++ /dev/null
@@ -1,108 +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.List;
-import java.util.concurrent.CopyOnWriteArrayList;
-
-import org.apache.catalina.Lifecycle;
-import org.apache.catalina.LifecycleEvent;
-import org.apache.catalina.LifecycleListener;
-
-/**
- * Support class to assist in firing LifecycleEvent notifications to
- * registered LifecycleListeners.
- *
- * @author Craig R. McClanahan
- */
-public final class LifecycleSupport {
-
- // ----------------------------------------------------------- Constructors
-
- /**
- * Construct a new LifecycleSupport object associated with the specified
- * Lifecycle component.
- *
- * @param lifecycle The Lifecycle component that will be the source
- * of events that we fire
- */
- public LifecycleSupport(Lifecycle lifecycle) {
- super();
- this.lifecycle = lifecycle;
- }
-
-
- // ----------------------------------------------------- Instance Variables
-
- /**
- * The source component for lifecycle events that we will fire.
- */
- private final Lifecycle lifecycle;
-
-
- /**
- * The list of registered LifecycleListeners for event notifications.
- */
- private final List<LifecycleListener> listeners = new CopyOnWriteArrayList<>();
-
-
- // --------------------------------------------------------- Public Methods
-
- /**
- * Add a lifecycle event listener to this component.
- *
- * @param listener The listener to add
- */
- public void addLifecycleListener(LifecycleListener listener) {
- listeners.add(listener);
- }
-
-
- /**
- * Get the lifecycle listeners associated with this lifecycle. If this
- * Lifecycle has no listeners registered, a zero-length array is returned.
- */
- public LifecycleListener[] findLifecycleListeners() {
- return listeners.toArray(new LifecycleListener[0]);
- }
-
-
- /**
- * Notify all lifecycle event listeners that a particular event has
- * occurred for this Container. The default implementation performs
- * this notification synchronously using the calling thread.
- *
- * @param type Event type
- * @param data Event data
- */
- public void fireLifecycleEvent(String type, Object data) {
- LifecycleEvent event = new LifecycleEvent(lifecycle, type, data);
- for (LifecycleListener listener : listeners) {
- listener.lifecycleEvent(event);
- }
- }
-
-
- /**
- * Remove a lifecycle event listener from this component.
- *
- * @param listener The listener to remove
- */
- public void removeLifecycleListener(LifecycleListener listener) {
- listeners.remove(listener);
- }
-}
diff --git a/java/org/apache/catalina/util/LocalStrings.properties b/java/org/apache/catalina/util/LocalStrings.properties
index ed4ef54..92ab4d8 100644
--- a/java/org/apache/catalina/util/LocalStrings.properties
+++ b/java/org/apache/catalina/util/LocalStrings.properties
@@ -38,8 +38,6 @@ lifecycleBase.stopFail=Failed to stop component [{0}]
lifecycleMBeanBase.registerFail=Failed to register object [{0}] with name [{1}] during component initialisation
lifecycleMBeanBase.unregisterFail=Failed to unregister MBean with name [{0}] during component destruction
lifecycleMBeanBase.unregisterNoServer=No MBean server was available to unregister the MBean [{0}]
-requestUtil.convertHexDigit.notHex=[{0}] is not a hexadecimal digit
-requestUtil.parseParameters.uee=Unable to parse the parameters since the encoding [{0}] is not supported.
SecurityUtil.doAsPrivilege=An exception occurs when running the PrivilegedExceptionAction block.
sessionIdGeneratorBase.createRandom=Creation of SecureRandom instance for session ID generation using [{0}] took [{1}] milliseconds.
sessionIdGeneratorBase.random=Exception initializing random number generator of class [{0}]. Falling back to java.secure.SecureRandom
diff --git a/java/org/apache/catalina/util/MIME2Java.java b/java/org/apache/catalina/util/MIME2Java.java
index ab9354f..bd441fe 100644
--- a/java/org/apache/catalina/util/MIME2Java.java
+++ b/java/org/apache/catalina/util/MIME2Java.java
@@ -30,447 +30,449 @@ import java.util.Map;
* as <code>TXDocument#setEncoding</code> and <code>DTD#setEncoding</code>.
* <p>Java encoding names are used on <var>encoding</var> parameters to
* methods such as <code>TXDocument#printWithFormat</code> and <code>DTD#printExternal</code>.
- * <P>
* <TABLE BORDER="0" WIDTH="100%">
* <caption>MIME charset name to Java encoding name mapping</caption>
* <TR>
- * <TD WIDTH="33%">
- * <P ALIGN="CENTER"><B>Common Name</B>
+ * <TD>
+ * <P><B>Common Name</B>
* </TD>
- * <TD WIDTH="15%">
- * <P ALIGN="CENTER"><B>Use this name in XML files</B>
+ * <TD>
+ * <P><B>Use this name in XML files</B>
* </TD>
- * <TD WIDTH="12%">
- * <P ALIGN="CENTER"><B>Name Type</B>
+ * <TD>
+ * <P><B>Name Type</B>
* </TD>
- * <TD WIDTH="31%">
- * <P ALIGN="CENTER"><B>Xerces converts to this Java Encoder Name</B>
+ * <TD>
+ * <P><B>Xerces converts to this Java Encoder Name</B>
* </TD>
* </TR>
* <TR>
- * <TD WIDTH="33%">8 bit Unicode</TD>
- * <TD WIDTH="15%">
- * <P ALIGN="CENTER">UTF-8
+ * <TD>8 bit Unicode</TD>
+ * <TD>
+ * <P>UTF-8
* </TD>
- * <TD WIDTH="12%">
- * <P ALIGN="CENTER">IANA
+ * <TD>
+ * <P>IANA
* </TD>
- * <TD WIDTH="31%">
- * <P ALIGN="CENTER">UTF8
+ * <TD>
+ * <P>UTF8
* </TD>
* </TR>
* <TR>
- * <TD WIDTH="33%">ISO Latin 1</TD>
- * <TD WIDTH="15%">
- * <P ALIGN="CENTER">ISO-8859-1
+ * <TD>ISO Latin 1</TD>
+ * <TD>
+ * <P>ISO-8859-1
* </TD>
- * <TD WIDTH="12%">
- * <P ALIGN="CENTER">MIME
+ * <TD>
+ * <P>MIME
* </TD>
- * <TD WIDTH="31%">
- * <P ALIGN="CENTER">ISO-8859-1
+ * <TD>
+ * <P>ISO-8859-1
* </TD>
* </TR>
* <TR>
- * <TD WIDTH="33%">ISO Latin 2</TD>
- * <TD WIDTH="15%">
- * <P ALIGN="CENTER">ISO-8859-2
+ * <TD>ISO Latin 2</TD>
+ * <TD>
+ * <P>ISO-8859-2
* </TD>
- * <TD WIDTH="12%">
- * <P ALIGN="CENTER">MIME
+ * <TD>
+ * <P>MIME
* </TD>
- * <TD WIDTH="31%">
- * <P ALIGN="CENTER">ISO-8859-2
+ * <TD>
+ * <P>ISO-8859-2
* </TD>
* </TR>
* <TR>
- * <TD WIDTH="33%">ISO Latin 3</TD>
- * <TD WIDTH="15%">
- * <P ALIGN="CENTER">ISO-8859-3
+ * <TD>ISO Latin 3</TD>
+ * <TD>
+ * <P>ISO-8859-3
* </TD>
- * <TD WIDTH="12%">
- * <P ALIGN="CENTER">MIME
+ * <TD>
+ * <P>MIME
* </TD>
- * <TD WIDTH="31%">
- * <P ALIGN="CENTER">ISO-8859-3
+ * <TD>
+ * <P>ISO-8859-3
* </TD>
* </TR>
* <TR>
- * <TD WIDTH="33%">ISO Latin 4</TD>
- * <TD WIDTH="15%">
- * <P ALIGN="CENTER">ISO-8859-4
+ * <TD>ISO Latin 4</TD>
+ * <TD>
+ * <P>ISO-8859-4
* </TD>
- * <TD WIDTH="12%">
- * <P ALIGN="CENTER">MIME
+ * <TD>
+ * <P>MIME
* </TD>
- * <TD WIDTH="31%">
- * <P ALIGN="CENTER">ISO-8859-4
+ * <TD>
+ * <P>ISO-8859-4
* </TD>
* </TR>
* <TR>
- * <TD WIDTH="33%">ISO Latin Cyrillic</TD>
- * <TD WIDTH="15%">
- * <P ALIGN="CENTER">ISO-8859-5
+ * <TD>ISO Latin Cyrillic</TD>
+ * <TD>
+ * <P>ISO-8859-5
* </TD>
- * <TD WIDTH="12%">
- * <P ALIGN="CENTER">MIME
+ * <TD>
+ * <P>MIME
* </TD>
- * <TD WIDTH="31%">
- * <P ALIGN="CENTER">ISO-8859-5
+ * <TD>
+ * <P>ISO-8859-5
* </TD>
* </TR>
* <TR>
- * <TD WIDTH="33%">ISO Latin Arabic</TD>
- * <TD WIDTH="15%">
- * <P ALIGN="CENTER">ISO-8859-6
+ * <TD>ISO Latin Arabic</TD>
+ * <TD>
+ * <P>ISO-8859-6
* </TD>
- * <TD WIDTH="12%">
- * <P ALIGN="CENTER">MIME
+ * <TD>
+ * <P>MIME
* </TD>
- * <TD WIDTH="31%">
- * <P ALIGN="CENTER">ISO-8859-6
+ * <TD>
+ * <P>ISO-8859-6
* </TD>
* </TR>
* <TR>
- * <TD WIDTH="33%">ISO Latin Greek</TD>
- * <TD WIDTH="15%">
- * <P ALIGN="CENTER">ISO-8859-7
+ * <TD>ISO Latin Greek</TD>
+ * <TD>
+ * <P>ISO-8859-7
* </TD>
- * <TD WIDTH="12%">
- * <P ALIGN="CENTER">MIME
+ * <TD>
+ * <P>MIME
* </TD>
- * <TD WIDTH="31%">
- * <P ALIGN="CENTER">ISO-8859-7
+ * <TD>
+ * <P>ISO-8859-7
* </TD>
* </TR>
* <TR>
- * <TD WIDTH="33%">ISO Latin Hebrew</TD>
- * <TD WIDTH="15%">
- * <P ALIGN="CENTER">ISO-8859-8
+ * <TD>ISO Latin Hebrew</TD>
+ * <TD>
+ * <P>ISO-8859-8
* </TD>
- * <TD WIDTH="12%">
- * <P ALIGN="CENTER">MIME
+ * <TD>
+ * <P>MIME
* </TD>
- * <TD WIDTH="31%">
- * <P ALIGN="CENTER">ISO-8859-8
+ * <TD>
+ * <P>ISO-8859-8
* </TD>
* </TR>
* <TR>
- * <TD WIDTH="33%">ISO Latin 5</TD>
- * <TD WIDTH="15%">
- * <P ALIGN="CENTER">ISO-8859-9
+ * <TD>ISO Latin 5</TD>
+ * <TD>
+ * <P>ISO-8859-9
* </TD>
- * <TD WIDTH="12%">
- * <P ALIGN="CENTER">MIME
+ * <TD>
+ * <P>MIME
* </TD>
- * <TD WIDTH="31%">
- * <P ALIGN="CENTER">ISO-8859-9
+ * <TD>
+ * <P>ISO-8859-9
* </TD>
* </TR>
* <TR>
- * <TD WIDTH="33%">EBCDIC: US</TD>
- * <TD WIDTH="15%">
- * <P ALIGN="CENTER">ebcdic-cp-us
+ * <TD>EBCDIC: US</TD>
+ * <TD>
+ * <P>ebcdic-cp-us
* </TD>
- * <TD WIDTH="12%">
- * <P ALIGN="CENTER">IANA
+ * <TD>
+ * <P>IANA
* </TD>
- * <TD WIDTH="31%">
- * <P ALIGN="CENTER">cp037
+ * <TD>
+ * <P>cp037
* </TD>
* </TR>
* <TR>
- * <TD WIDTH="33%">EBCDIC: Canada</TD>
- * <TD WIDTH="15%">
- * <P ALIGN="CENTER">ebcdic-cp-ca
+ * <TD>EBCDIC: Canada</TD>
+ * <TD>
+ * <P>ebcdic-cp-ca
* </TD>
- * <TD WIDTH="12%">
- * <P ALIGN="CENTER">IANA
+ * <TD>
+ * <P>IANA
* </TD>
- * <TD WIDTH="31%">
- * <P ALIGN="CENTER">cp037
+ * <TD>
+ * <P>cp037
* </TD>
* </TR>
* <TR>
- * <TD WIDTH="33%">EBCDIC: Netherlands</TD>
- * <TD WIDTH="15%">
- * <P ALIGN="CENTER">ebcdic-cp-nl
+ * <TD>EBCDIC: Netherlands</TD>
+ * <TD>
+ * <P>ebcdic-cp-nl
* </TD>
- * <TD WIDTH="12%">
- * <P ALIGN="CENTER">IANA
+ * <TD>
+ * <P>IANA
* </TD>
- * <TD WIDTH="31%">
- * <P ALIGN="CENTER">cp037
+ * <TD>
+ * <P>cp037
* </TD>
* </TR>
* <TR>
- * <TD WIDTH="33%">EBCDIC: Denmark</TD>
- * <TD WIDTH="15%">
- * <P ALIGN="CENTER">ebcdic-cp-dk
+ * <TD>EBCDIC: Denmark</TD>
+ * <TD>
+ * <P>ebcdic-cp-dk
* </TD>
- * <TD WIDTH="12%">
- * <P ALIGN="CENTER">IANA
+ * <TD>
+ * <P>IANA
* </TD>
- * <TD WIDTH="31%">
- * <P ALIGN="CENTER">cp277
+ * <TD>
+ * <P>cp277
* </TD>
* </TR>
* <TR>
- * <TD WIDTH="33%">EBCDIC: Norway</TD>
- * <TD WIDTH="15%">
- * <P ALIGN="CENTER">ebcdic-cp-no
+ * <TD>EBCDIC: Norway</TD>
+ * <TD>
+ * <P>ebcdic-cp-no
* </TD>
- * <TD WIDTH="12%">
- * <P ALIGN="CENTER">IANA
+ * <TD>
+ * <P>IANA
* </TD>
- * <TD WIDTH="31%">
- * <P ALIGN="CENTER">cp277
+ * <TD>
+ * <P>cp277
* </TD>
* </TR>
* <TR>
- * <TD WIDTH="33%">EBCDIC: Finland</TD>
- * <TD WIDTH="15%">
- * <P ALIGN="CENTER">ebcdic-cp-fi
+ * <TD>EBCDIC: Finland</TD>
+ * <TD>
+ * <P>ebcdic-cp-fi
* </TD>
- * <TD WIDTH="12%">
- * <P ALIGN="CENTER">IANA
+ * <TD>
+ * <P>IANA
* </TD>
- * <TD WIDTH="31%">
- * <P ALIGN="CENTER">cp278
+ * <TD>
+ * <P>cp278
* </TD>
* </TR>
* <TR>
- * <TD WIDTH="33%">EBCDIC: Sweden</TD>
- * <TD WIDTH="15%">
- * <P ALIGN="CENTER">ebcdic-cp-se
+ * <TD>EBCDIC: Sweden</TD>
+ * <TD>
+ * <P>ebcdic-cp-se
* </TD>
- * <TD WIDTH="12%">
- * <P ALIGN="CENTER">IANA
+ * <TD>
+ * <P>IANA
* </TD>
- * <TD WIDTH="31%">
- * <P ALIGN="CENTER">cp278
+ * <TD>
+ * <P>cp278
* </TD>
* </TR>
* <TR>
- * <TD WIDTH="33%">EBCDIC: Italy</TD>
- * <TD WIDTH="15%">
- * <P ALIGN="CENTER">ebcdic-cp-it
+ * <TD>EBCDIC: Italy</TD>
+ * <TD>
+ * <P>ebcdic-cp-it
* </TD>
- * <TD WIDTH="12%">
- * <P ALIGN="CENTER">IANA
+ * <TD>
+ * <P>IANA
* </TD>
- * <TD WIDTH="31%">
- * <P ALIGN="CENTER">cp280
+ * <TD>
+ * <P>cp280
* </TD>
* </TR>
* <TR>
- * <TD WIDTH="33%">EBCDIC: Spain, Latin America</TD>
- * <TD WIDTH="15%">
- * <P ALIGN="CENTER">ebcdic-cp-es
+ * <TD>EBCDIC: Spain, Latin America</TD>
+ * <TD>
+ * <P>ebcdic-cp-es
* </TD>
- * <TD WIDTH="12%">
- * <P ALIGN="CENTER">IANA
+ * <TD>
+ * <P>IANA
* </TD>
- * <TD WIDTH="31%">
- * <P ALIGN="CENTER">cp284
+ * <TD>
+ * <P>cp284
* </TD>
* </TR>
* <TR>
- * <TD WIDTH="33%">EBCDIC: Great Britain</TD>
- * <TD WIDTH="15%">
- * <P ALIGN="CENTER">ebcdic-cp-gb
+ * <TD>EBCDIC: Great Britain</TD>
+ * <TD>
+ * <P>ebcdic-cp-gb
* </TD>
- * <TD WIDTH="12%">
- * <P ALIGN="CENTER">IANA
+ * <TD>
+ * <P>IANA
* </TD>
- * <TD WIDTH="31%">
- * <P ALIGN="CENTER">cp285
+ * <TD>
+ * <P>cp285
* </TD>
* </TR>
* <TR>
- * <TD WIDTH="33%">EBCDIC: France</TD>
- * <TD WIDTH="15%">
- * <P ALIGN="CENTER">ebcdic-cp-fr
+ * <TD>EBCDIC: France</TD>
+ * <TD>
+ * <P>ebcdic-cp-fr
* </TD>
- * <TD WIDTH="12%">
- * <P ALIGN="CENTER">IANA
+ * <TD>
+ * <P>IANA
* </TD>
- * <TD WIDTH="31%">
- * <P ALIGN="CENTER">cp297
+ * <TD>
+ * <P>cp297
* </TD>
* </TR>
* <TR>
- * <TD WIDTH="33%">EBCDIC: Arabic</TD>
- * <TD WIDTH="15%">
- * <P ALIGN="CENTER">ebcdic-cp-ar1
+ * <TD>EBCDIC: Arabic</TD>
+ * <TD>
+ * <P>ebcdic-cp-ar1
* </TD>
- * <TD WIDTH="12%">
- * <P ALIGN="CENTER">IANA
+ * <TD>
+ * <P>IANA
* </TD>
- * <TD WIDTH="31%">
- * <P ALIGN="CENTER">cp420
+ * <TD>
+ * <P>cp420
* </TD>
* </TR>
* <TR>
- * <TD WIDTH="33%">EBCDIC: Hebrew</TD>
- * <TD WIDTH="15%">
- * <P ALIGN="CENTER">ebcdic-cp-he
+ * <TD>EBCDIC: Hebrew</TD>
+ * <TD>
+ * <P>ebcdic-cp-he
* </TD>
- * <TD WIDTH="12%">
- * <P ALIGN="CENTER">IANA
+ * <TD>
+ * <P>IANA
* </TD>
- * <TD WIDTH="31%">
- * <P ALIGN="CENTER">cp424
+ * <TD>
+ * <P>cp424
* </TD>
* </TR>
* <TR>
- * <TD WIDTH="33%">EBCDIC: Switzerland</TD>
- * <TD WIDTH="15%">
- * <P ALIGN="CENTER">ebcdic-cp-ch
+ * <TD>EBCDIC: Switzerland</TD>
+ * <TD>
+ * <P>ebcdic-cp-ch
* </TD>
- * <TD WIDTH="12%">
- * <P ALIGN="CENTER">IANA
+ * <TD>
+ * <P>IANA
* </TD>
- * <TD WIDTH="31%">
- * <P ALIGN="CENTER">cp500
+ * <TD>
+ * <P>cp500
* </TD>
* </TR>
* <TR>
- * <TD WIDTH="33%">EBCDIC: Roece</TD>
- * <TD WIDTH="15%">
- * <P ALIGN="CENTER">ebcdic-cp-roece
+ * <TD>EBCDIC: Roece</TD>
+ * <TD>
+ * <P>ebcdic-cp-roece
* </TD>
- * <TD WIDTH="12%">
- * <P ALIGN="CENTER">IANA
+ * <TD>
+ * <P>IANA
* </TD>
- * <TD WIDTH="31%">
- * <P ALIGN="CENTER">cp870
+ * <TD>
+ * <P>cp870
* </TD>
* </TR>
* <TR>
- * <TD WIDTH="33%">EBCDIC: Yogoslavia</TD>
- * <TD WIDTH="15%">
- * <P ALIGN="CENTER">ebcdic-cp-yu
+ * <TD>EBCDIC: Yogoslavia</TD>
+ * <TD>
+ * <P>ebcdic-cp-yu
* </TD>
- * <TD WIDTH="12%">
- * <P ALIGN="CENTER">IANA
+ * <TD>
+ * <P>IANA
* </TD>
- * <TD WIDTH="31%">
- * <P ALIGN="CENTER">cp870
+ * <TD>
+ * <P>cp870
* </TD>
* </TR>
* <TR>
- * <TD WIDTH="33%">EBCDIC: Iceland</TD>
- * <TD WIDTH="15%">
- * <P ALIGN="CENTER">ebcdic-cp-is
+ * <TD>EBCDIC: Iceland</TD>
+ * <TD>
+ * <P>ebcdic-cp-is
* </TD>
- * <TD WIDTH="12%">
- * <P ALIGN="CENTER">IANA
+ * <TD>
+ * <P>IANA
* </TD>
- * <TD WIDTH="31%">
- * <P ALIGN="CENTER">cp871
+ * <TD>
+ * <P>cp871
* </TD>
* </TR>
* <TR>
- * <TD WIDTH="33%">EBCDIC: Urdu</TD>
- * <TD WIDTH="15%">
- * <P ALIGN="CENTER">ebcdic-cp-ar2
+ * <TD>EBCDIC: Urdu</TD>
+ * <TD>
+ * <P>ebcdic-cp-ar2
* </TD>
- * <TD WIDTH="12%">
- * <P ALIGN="CENTER">IANA
+ * <TD>
+ * <P>IANA
* </TD>
- * <TD WIDTH="31%">
- * <P ALIGN="CENTER">cp918
+ * <TD>
+ * <P>cp918
* </TD>
* </TR>
* <TR>
- * <TD WIDTH="33%">Chinese for PRC, mixed 1/2 byte</TD>
- * <TD WIDTH="15%">
- * <P ALIGN="CENTER">gb2312
+ * <TD>Chinese for PRC, mixed 1/2 byte</TD>
+ * <TD>
+ * <P>gb2312
* </TD>
- * <TD WIDTH="12%">
- * <P ALIGN="CENTER">MIME
+ * <TD>
+ * <P>MIME
* </TD>
- * <TD WIDTH="31%">
- * <P ALIGN="CENTER">GB2312
+ * <TD>
+ * <P>GB2312
* </TD>
* </TR>
* <TR>
- * <TD WIDTH="33%">Extended Unix Code, packed for Japanese</TD>
- * <TD WIDTH="15%">
- * <P ALIGN="CENTER">euc-jp
+ * <TD>Extended Unix Code, packed for Japanese</TD>
+ * <TD>
+ * <P>euc-jp
* </TD>
- * <TD WIDTH="12%">
- * <P ALIGN="CENTER">MIME
+ * <TD>
+ * <P>MIME
* </TD>
- * <TD WIDTH="31%">
- * <P ALIGN="CENTER">eucjis
+ * <TD>
+ * <P>eucjis
* </TD>
* </TR>
* <TR>
- * <TD WIDTH="33%">Japanese: iso-2022-jp</TD>
- * <TD WIDTH="15%">
- * <P ALIGN="CENTER">iso-2020-jp
+ * <TD>Japanese: iso-2022-jp</TD>
+ * <TD>
+ * <P>iso-2020-jp
* </TD>
- * <TD WIDTH="12%">
- * <P ALIGN="CENTER">MIME
+ * <TD>
+ * <P>MIME
* </TD>
- * <TD WIDTH="31%">
- * <P ALIGN="CENTER">JIS
+ * <TD>
+ * <P>JIS
* </TD>
* </TR>
* <TR>
- * <TD WIDTH="33%">Japanese: Shift JIS</TD>
- * <TD WIDTH="15%">
- * <P ALIGN="CENTER">Shift_JIS
+ * <TD>Japanese: Shift JIS</TD>
+ * <TD>
+ * <P>Shift_JIS
* </TD>
- * <TD WIDTH="12%">
- * <P ALIGN="CENTER">MIME
+ * <TD>
+ * <P>MIME
* </TD>
- * <TD WIDTH="31%">
- * <P ALIGN="CENTER">SJIS
+ * <TD>
+ * <P>SJIS
* </TD>
* </TR>
* <TR>
- * <TD WIDTH="33%">Chinese: Big5</TD>
- * <TD WIDTH="15%">
- * <P ALIGN="CENTER">Big5
+ * <TD>Chinese: Big5</TD>
+ * <TD>
+ * <P>Big5
* </TD>
- * <TD WIDTH="12%">
- * <P ALIGN="CENTER">MIME
+ * <TD>
+ * <P>MIME
* </TD>
- * <TD WIDTH="31%">
- * <P ALIGN="CENTER">Big5
+ * <TD>
+ * <P>Big5
* </TD>
* </TR>
* <TR>
- * <TD WIDTH="33%">Extended Unix Code, packed for Korean</TD>
- * <TD WIDTH="15%">
- * <P ALIGN="CENTER">euc-kr
+ * <TD>Extended Unix Code, packed for Korean</TD>
+ * <TD>
+ * <P>euc-kr
* </TD>
- * <TD WIDTH="12%">
- * <P ALIGN="CENTER">MIME
+ * <TD>
+ * <P>MIME
* </TD>
- * <TD WIDTH="31%">
- * <P ALIGN="CENTER">iso2022kr
+ * <TD>
+ * <P>iso2022kr
* </TD>
* </TR>
* <TR>
- * <TD WIDTH="33%">Cyrillic</TD>
- * <TD WIDTH="15%">
- * <P ALIGN="CENTER">koi8-r
+ * <TD>Cyrillic</TD>
+ * <TD>
+ * <P>koi8-r
* </TD>
- * <TD WIDTH="12%">
- * <P ALIGN="CENTER">MIME
+ * <TD>
+ * <P>MIME
* </TD>
- * <TD WIDTH="31%">
- * <P ALIGN="CENTER">koi8-r
+ * <TD>
+ * <P>koi8-r
* </TD>
* </TR>
* </TABLE>
*
* @author TAMURA Kent <kent at trl.ibm.co.jp>
+ *
+ * @deprecated Unused. Will be removed in Tomcat 9.
*/
+ at Deprecated
public class MIME2Java {
private static final Map<String,String> s_enchash;
diff --git a/java/org/apache/catalina/util/ParameterMap.java b/java/org/apache/catalina/util/ParameterMap.java
index 492023f..a166818 100644
--- a/java/org/apache/catalina/util/ParameterMap.java
+++ b/java/org/apache/catalina/util/ParameterMap.java
@@ -28,6 +28,9 @@ import org.apache.tomcat.util.res.StringManager;
* to clone them in order to avoid modifications. When first created, a
* <code>ParmaeterMap</code> instance is not locked.
*
+ * @param <K> The type of Key
+ * @param <V> The type of Value
+ *
* @author Craig R. McClanahan
*/
public final class ParameterMap<K,V> extends LinkedHashMap<K,V> {
@@ -96,7 +99,7 @@ public final class ParameterMap<K,V> extends LinkedHashMap<K,V> {
/**
- * Return the locked state of this parameter map.
+ * @return the locked state of this parameter map.
*/
public boolean isLocked() {
diff --git a/java/org/apache/catalina/util/RequestUtil.java b/java/org/apache/catalina/util/RequestUtil.java
index 1824560..4ba1eb2 100644
--- a/java/org/apache/catalina/util/RequestUtil.java
+++ b/java/org/apache/catalina/util/RequestUtil.java
@@ -16,16 +16,6 @@
*/
package org.apache.catalina.util;
-import java.io.UnsupportedEncodingException;
-import java.nio.charset.Charset;
-import java.util.Map;
-
-import org.apache.juli.logging.Log;
-import org.apache.juli.logging.LogFactory;
-import org.apache.tomcat.util.buf.B2CConverter;
-import org.apache.tomcat.util.res.StringManager;
-
-
/**
* General purpose request parsing and encoding utility methods.
*
@@ -34,27 +24,20 @@ import org.apache.tomcat.util.res.StringManager;
*/
public final class RequestUtil {
-
- private static final Log log = LogFactory.getLog(RequestUtil.class);
-
- /**
- * The string resources for this package.
- */
- private static final StringManager sm =
- StringManager.getManager("org.apache.catalina.util");
-
-
/**
* Filter the specified message string for characters that are sensitive
* in HTML. This avoids potential attacks caused by including JavaScript
* codes in the request URL that is often reported in error messages.
*
* @param message The message string to be filtered
+ *
+ * @return the filtered message
*/
public static String filter(String message) {
- if (message == null)
- return (null);
+ if (message == null) {
+ return null;
+ }
char content[] = new char[message.length()];
message.getChars(0, message.length(), content, 0);
@@ -77,163 +60,6 @@ public final class RequestUtil {
result.append(content[i]);
}
}
- return (result.toString());
-
- }
-
-
- /**
- * Append request parameters from the specified String to the specified
- * Map. It is presumed that the specified Map is not accessed from any
- * other thread, so no synchronization is performed.
- * <p>
- * <strong>IMPLEMENTATION NOTE</strong>: URL decoding is performed
- * individually on the parsed name and value elements, rather than on
- * the entire query string ahead of time, to properly deal with the case
- * where the name or value includes an encoded "=" or "&" character
- * that would otherwise be interpreted as a delimiter.
- *
- * @param map Map that accumulates the resulting parameters
- * @param data Input string containing request parameters
- * @param encoding The encoding to use; encoding must not be null.
- * If an unsupported encoding is specified the parameters will not be
- * parsed and the map will not be modified
- *
- * @deprecated Unused. This will be removed in Tomcat 8.5.x.
- */
- @Deprecated
- public static void parseParameters(Map<String,String[]> map, String data,
- String encoding) {
-
- if ((data != null) && (data.length() > 0)) {
-
- // use the specified encoding to extract bytes out of the
- // given string so that the encoding is not lost.
- byte[] bytes = null;
- try {
- bytes = data.getBytes(B2CConverter.getCharset(encoding));
- parseParameters(map, bytes, encoding);
- } catch (UnsupportedEncodingException uee) {
- if (log.isDebugEnabled()) {
- log.debug(sm.getString("requestUtil.parseParameters.uee",
- encoding), uee);
- }
- }
-
- }
-
- }
-
-
- /**
- * Convert a byte character value to hexadecimal digit value.
- *
- * @param b the character value byte
- */
- private static byte convertHexDigit( byte b ) {
- if ((b >= '0') && (b <= '9')) return (byte)(b - '0');
- if ((b >= 'a') && (b <= 'f')) return (byte)(b - 'a' + 10);
- if ((b >= 'A') && (b <= 'F')) return (byte)(b - 'A' + 10);
- throw new IllegalArgumentException(
- sm.getString("requestUtil.convertHexDigit.notHex",
- Character.valueOf((char)b)));
- }
-
-
- /**
- * Put name and value pair in map. When name already exist, add value
- * to array of values.
- *
- * @param map The map to populate
- * @param name The parameter name
- * @param value The parameter value
- */
- private static void putMapEntry( Map<String,String[]> map, String name,
- String value) {
- String[] newValues = null;
- String[] oldValues = map.get(name);
- if (oldValues == null) {
- newValues = new String[1];
- newValues[0] = value;
- } else {
- newValues = new String[oldValues.length + 1];
- System.arraycopy(oldValues, 0, newValues, 0, oldValues.length);
- newValues[oldValues.length] = value;
- }
- map.put(name, newValues);
- }
-
-
- /**
- * Append request parameters from the specified String to the specified
- * Map. It is presumed that the specified Map is not accessed from any
- * other thread, so no synchronization is performed.
- * <p>
- * <strong>IMPLEMENTATION NOTE</strong>: URL decoding is performed
- * individually on the parsed name and value elements, rather than on
- * the entire query string ahead of time, to properly deal with the case
- * where the name or value includes an encoded "=" or "&" character
- * that would otherwise be interpreted as a delimiter.
- *
- * NOTE: byte array data is modified by this method. Caller beware.
- *
- * @param map Map that accumulates the resulting parameters
- * @param data Input string containing request parameters
- * @param encoding The encoding to use; if null, the default encoding is
- * used
- *
- * @exception UnsupportedEncodingException if the requested encoding is not
- * supported.
- *
- * @deprecated Unused. This will be removed in Tomcat 8.5.x.
- */
- @Deprecated
- public static void parseParameters(Map<String,String[]> map, byte[] data,
- String encoding) throws UnsupportedEncodingException {
-
- Charset charset = B2CConverter.getCharset(encoding);
-
- if (data != null && data.length > 0) {
- int ix = 0;
- int ox = 0;
- String key = null;
- String value = null;
- while (ix < data.length) {
- byte c = data[ix++];
- switch ((char) c) {
- case '&':
- value = new String(data, 0, ox, charset);
- if (key != null) {
- putMapEntry(map, key, value);
- key = null;
- }
- ox = 0;
- break;
- case '=':
- if (key == null) {
- key = new String(data, 0, ox, charset);
- ox = 0;
- } else {
- data[ox++] = c;
- }
- break;
- case '+':
- data[ox++] = (byte)' ';
- break;
- case '%':
- data[ox++] = (byte)((convertHexDigit(data[ix++]) << 4)
- + convertHexDigit(data[ix++]));
- break;
- default:
- data[ox++] = c;
- }
- }
- //The last value does not end in '&'. So save it now.
- if (key != null) {
- value = new String(data, 0, ox, charset);
- putMapEntry(map, key, value);
- }
- }
-
+ return result.toString();
}
}
diff --git a/java/org/apache/catalina/util/ResourceSet.java b/java/org/apache/catalina/util/ResourceSet.java
index d8442d3..40f86db 100644
--- a/java/org/apache/catalina/util/ResourceSet.java
+++ b/java/org/apache/catalina/util/ResourceSet.java
@@ -32,6 +32,8 @@ import org.apache.tomcat.util.res.StringManager;
* to avoid modifications. When first created, a <code>ResourceMap</code>
* is not locked.
*
+ * @param <T> The type of elements in the Set
+ *
* @author Craig R. McClanahan
*/
public final class ResourceSet<T> extends HashSet<T> {
@@ -99,7 +101,7 @@ public final class ResourceSet<T> extends HashSet<T> {
/**
- * Return the locked state of this parameter map.
+ * @return the locked state of this parameter map.
*/
public boolean isLocked() {
diff --git a/java/org/apache/catalina/util/ServerInfo.java b/java/org/apache/catalina/util/ServerInfo.java
index 6ca7c6b..295854b 100644
--- a/java/org/apache/catalina/util/ServerInfo.java
+++ b/java/org/apache/catalina/util/ServerInfo.java
@@ -69,11 +69,11 @@ public class ServerInfo {
ExceptionUtils.handleThrowable(t);
}
if (info == null)
- info = "Apache Tomcat 8.0.x-dev";
+ info = "Apache Tomcat 8.5.x-dev";
if (built == null)
built = "unknown";
if (number == null)
- number = "8.0.x";
+ number = "8.5.x";
serverInfo = info;
serverBuilt = built;
@@ -85,7 +85,7 @@ public class ServerInfo {
/**
- * Return the server identification for this version of Tomcat.
+ * @return the server identification for this version of Tomcat.
*/
public static String getServerInfo() {
@@ -94,7 +94,7 @@ public class ServerInfo {
}
/**
- * Return the server built time for this version of Tomcat.
+ * @return the server built time for this version of Tomcat.
*/
public static String getServerBuilt() {
@@ -103,7 +103,7 @@ public class ServerInfo {
}
/**
- * Return the server's version number.
+ * @return the server's version number.
*/
public static String getServerNumber() {
diff --git a/java/org/apache/catalina/util/SessionConfig.java b/java/org/apache/catalina/util/SessionConfig.java
index 6e3f45d..979ace5 100644
--- a/java/org/apache/catalina/util/SessionConfig.java
+++ b/java/org/apache/catalina/util/SessionConfig.java
@@ -28,7 +28,8 @@ public class SessionConfig {
/**
* Determine the name to use for the session cookie for the provided
* context.
- * @param context
+ * @param context The context
+ * @return the cookie name for the context
*/
public static String getSessionCookieName(Context context) {
@@ -44,7 +45,8 @@ public class SessionConfig {
/**
* Determine the name to use for the session cookie for the provided
* context.
- * @param context
+ * @param context The context
+ * @return the parameter name for the session
*/
public static String getSessionUriParamName(Context context) {
diff --git a/java/org/apache/catalina/util/SessionIdGeneratorBase.java b/java/org/apache/catalina/util/SessionIdGeneratorBase.java
index 2c84c07..f0e1282 100644
--- a/java/org/apache/catalina/util/SessionIdGeneratorBase.java
+++ b/java/org/apache/catalina/util/SessionIdGeneratorBase.java
@@ -48,36 +48,10 @@ public abstract class SessionIdGeneratorBase extends LifecycleBase
*/
private final Queue<SecureRandom> randoms = new ConcurrentLinkedQueue<>();
-
- /**
- * The Java class name of the secure random number generator class to be
- * used when generating session identifiers. The random number generator
- * class must be self-seeding and have a zero-argument constructor. If not
- * specified, an instance of {@link SecureRandom} will be generated.
- */
private String secureRandomClass = null;
-
- /**
- * The name of the algorithm to use to create instances of
- * {@link SecureRandom} which are used to generate session IDs. If no
- * algorithm is specified, SHA1PRNG is used. To use the platform default
- * (which may be SHA1PRNG), specify the empty string. If an invalid
- * algorithm and/or provider is specified the {@link SecureRandom} instances
- * will be created using the defaults. If that fails, the {@link
- * SecureRandom} instances will be created using platform defaults.
- */
private String secureRandomAlgorithm = "SHA1PRNG";
-
- /**
- * The name of the provider to use to create instances of
- * {@link SecureRandom} which are used to generate session IDs. If
- * no algorithm is specified the of SHA1PRNG default is used. If an invalid
- * algorithm and/or provider is specified the {@link SecureRandom} instances
- * will be created using the defaults. If that fails, the {@link
- * SecureRandom} instances will be created using platform defaults.
- */
private String secureRandomProvider = null;
@@ -90,7 +64,21 @@ public abstract class SessionIdGeneratorBase extends LifecycleBase
/**
- * Specify a non-default @{link {@link SecureRandom} implementation to use.
+ * Get the class name of the {@link SecureRandom} implementation used to
+ * generate session IDs.
+ *
+ * @return The fully qualified class name. {@code null} indicates that the
+ * JRE provided {@link SecureRandom} implementation will be used
+ */
+ public String getSecureRandomClass() {
+ return secureRandomClass;
+ }
+
+
+ /**
+ * Specify a non-default {@link SecureRandom} implementation to use. The
+ * implementation must be self-seeding and have a zero-argument constructor.
+ * If not specified, an instance of {@link SecureRandom} will be generated.
*
* @param secureRandomClass The fully-qualified class name
*/
@@ -100,7 +88,26 @@ public abstract class SessionIdGeneratorBase extends LifecycleBase
/**
- * Specify a non-default algorithm to use to generate random numbers.
+ * Get the name of the algorithm used to create the {@link SecureRandom}
+ * instances which generate new session IDs.
+ *
+ * @return The name of the algorithm. {@code null} or the empty string means
+ * that platform default will be used
+ */
+ public String getSecureRandomAlgorithm() {
+ return secureRandomAlgorithm;
+ }
+
+
+ /**
+ * Specify a non-default algorithm to use to create instances of
+ * {@link SecureRandom} which are used to generate session IDs. If no
+ * algorithm is specified, SHA1PRNG is used. To use the platform default
+ * (which may be SHA1PRNG), specify {@code null} or the empty string. If an
+ * invalid algorithm and/or provider is specified the {@link SecureRandom}
+ * instances will be created using the defaults for this
+ * {@link SessionIdGenerator} implementation. If that fails, the
+ * {@link SecureRandom} instances will be created using platform defaults.
*
* @param secureRandomAlgorithm The name of the algorithm
*/
@@ -110,7 +117,26 @@ public abstract class SessionIdGeneratorBase extends LifecycleBase
/**
- * Specify a non-default provider to use to generate random numbers.
+ * Get the name of the provider used to create the {@link SecureRandom}
+ * instances which generate new session IDs.
+ *
+ * @return The name of the provider. {@code null} or the empty string means
+ * that platform default will be used
+ */
+ public String getSecureRandomProvider() {
+ return secureRandomProvider;
+ }
+
+
+ /**
+ * Specify a non-default provider to use to create instances of
+ * {@link SecureRandom} which are used to generate session IDs. If no
+ * provider is specified, the platform default is used. To use the platform
+ * default specify {@code null} or the empty string. If an invalid algorithm
+ * and/or provider is specified the {@link SecureRandom} instances will be
+ * created using the defaults for this {@link SessionIdGenerator}
+ * implementation. If that fails, the {@link SecureRandom} instances will be
+ * created using platform defaults.
*
* @param secureRandomProvider The name of the provider
*/
@@ -148,6 +174,8 @@ public abstract class SessionIdGeneratorBase extends LifecycleBase
public int getSessionIdLength() {
return sessionIdLength;
}
+
+
/**
* Specify the number of bytes for a session ID
*
@@ -167,6 +195,7 @@ public abstract class SessionIdGeneratorBase extends LifecycleBase
return generateSessionId(jvmRoute);
}
+
protected void getRandomBytes(byte bytes[]) {
SecureRandom random = randoms.poll();
diff --git a/java/org/apache/catalina/util/Strftime.java b/java/org/apache/catalina/util/Strftime.java
index 37a4832..a09e5b1 100644
--- a/java/org/apache/catalina/util/Strftime.java
+++ b/java/org/apache/catalina/util/Strftime.java
@@ -139,6 +139,7 @@ public class Strftime {
/**
* Change the timezone used to format dates
*
+ * @param timeZone The new time zone
* @see SimpleDateFormat#setTimeZone
*/
public void setTimeZone( TimeZone timeZone ) {
diff --git a/java/org/apache/catalina/util/StringParser.java b/java/org/apache/catalina/util/StringParser.java
deleted file mode 100644
index 1a87da8..0000000
--- a/java/org/apache/catalina/util/StringParser.java
+++ /dev/null
@@ -1,198 +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;
-
-
-/**
- * Utility class for string parsing that is higher performance than
- * StringParser for simple delimited text cases. Parsing is performed
- * by setting the string, and then using the <code>findXxxx()</code> and
- * <code>skipXxxx()</code> families of methods to remember significant
- * offsets. To retrieve the parsed substrings, call the <code>extract()</code>
- * method with the appropriate saved offset values.
- *
- * @author Craig R. McClanahan
- *
- * @deprecated Unused. Will be removed in Tomcat 9 onwards.
- */
- at Deprecated
-public final class StringParser {
-
-
- // ----------------------------------------------------------- Constructors
-
-
- /**
- * Construct a string parser with no preset string to be parsed.
- */
- public StringParser() {
-
- this(null);
-
- }
-
-
- /**
- * Construct a string parser that is initialized to parse the specified
- * string.
- *
- * @param string The string to be parsed
- */
- public StringParser(String string) {
-
- super();
- setString(string);
-
- }
-
-
- // ----------------------------------------------------- Instance Variables
-
-
- /**
- * The characters of the current string, as a character array. Stored
- * when the string is first specified to speed up access to characters
- * being compared during parsing.
- */
- private char chars[] = null;
-
-
- /**
- * The zero-relative index of the current point at which we are
- * positioned within the string being parsed. <strong>NOTE</strong>:
- * the value of this index can be one larger than the index of the last
- * character of the string (i.e. equal to the string length) if you
- * parse off the end of the string. This value is useful for extracting
- * substrings that include the end of the string.
- */
- private int index = 0;
-
-
- /**
- * The length of the String we are currently parsing. Stored when the
- * string is first specified to avoid repeated recalculations.
- */
- private int length = 0;
-
-
- /**
- * The String we are currently parsing.
- */
- private String string = null;
-
-
- // ------------------------------------------------------------- Properties
-
-
- /**
- * Return the zero-relative index of our current parsing position
- * within the string being parsed.
- */
- public int getIndex() {
-
- return (this.index);
-
- }
-
-
- /**
- * Return the length of the string we are parsing.
- */
- public int getLength() {
- return length;
- }
-
-
- /**
- * Set the String we are currently parsing. The parser state is also reset
- * to begin at the start of this string.
- *
- * @param string The string to be parsed.
- */
- public void setString(String string) {
-
- this.string = string;
- if (string != null) {
- this.length = string.length();
- chars = this.string.toCharArray();
- } else {
- this.length = 0;
- chars = new char[0];
- }
- reset();
- }
-
-
- // --------------------------------------------------------- Public Methods
-
-
- /**
- * Advance the current parsing position by one, if we are not already
- * past the end of the string.
- */
- public void advance() {
-
- if (index < length)
- index++;
- }
-
-
- /**
- * Extract and return a substring that starts at the specified position,
- * and ends at the character before the specified position. If this is
- * not possible, a zero-length string is returned.
- *
- * @param start Starting index, zero relative, inclusive
- * @param end Ending index, zero relative, exclusive
- */
- public String extract(int start, int end) {
-
- if ((start < 0) || (start >= end) || (end > length))
- return ("");
- else
- return (string.substring(start, end));
-
- }
-
-
- /**
- * Return the index of the next occurrence of the specified character,
- * or the index of the character after the last position of the string
- * if no more occurrences of this character are found. The current
- * parsing position is updated to the returned value.
- *
- * @param ch Character to be found
- */
- public int findChar(char ch) {
-
- while ((index < length) && (ch != chars[index]))
- index++;
- return (index);
-
- }
-
-
- /**
- * Reset the current state of the parser to the beginning of the
- * current string being parsed.
- */
- public void reset() {
- index = 0;
- }
-}
diff --git a/java/org/apache/catalina/util/UriUtil.java b/java/org/apache/catalina/util/UriUtil.java
deleted file mode 100644
index ab085e0..0000000
--- a/java/org/apache/catalina/util/UriUtil.java
+++ /dev/null
@@ -1,42 +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;
-
-/**
- * Utility class for working with URIs and URLs.
- *
- * @deprecated Use {@link org.apache.tomcat.util.buf.UriUtil}
- */
- at Deprecated
-public final class UriUtil {
-
- private UriUtil() {
- // Utility class. Hide default constructor
- }
-
-
- /**
- * Determine if a URI string has a <code>scheme</code> component.
- *
- * @param uri The URI to test
- *
- * @return {@code true} if a scheme is present, otherwise {code @false}
- */
- public static boolean hasScheme(CharSequence uri) {
- return org.apache.tomcat.util.buf.UriUtil.hasScheme(uri);
- }
-}
diff --git a/java/org/apache/catalina/util/XMLWriter.java b/java/org/apache/catalina/util/XMLWriter.java
index dcae581..91cc78b 100644
--- a/java/org/apache/catalina/util/XMLWriter.java
+++ b/java/org/apache/catalina/util/XMLWriter.java
@@ -22,8 +22,6 @@ import java.io.Writer;
/**
* XMLWriter helper class.
- *
- * @author <a href="mailto:remm at apache.org">Remy Maucherat</a>
*/
public class XMLWriter {
@@ -68,7 +66,7 @@ public class XMLWriter {
/**
- * Constructor.
+ * New XML writer utility that will store its data in an internal buffer.
*/
public XMLWriter() {
this(null);
@@ -76,7 +74,12 @@ public class XMLWriter {
/**
- * Constructor.
+ * New XML writer utility that will store its data in an internal buffer
+ * and can write it to the specified writer.
+ * <p>
+ * See {@link #sendData()}
+ *
+ * @param writer The writer to use
*/
public XMLWriter(Writer writer) {
this.writer = writer;
@@ -204,7 +207,8 @@ public class XMLWriter {
/**
- * Send data and reinitializes buffer.
+ * Send data and reinitializes buffer, if a writer has been specified.
+ * @throws IOException Error writing XML data
*/
public void sendData()
throws IOException {
diff --git a/java/org/apache/catalina/valves/AbstractAccessLogValve.java b/java/org/apache/catalina/valves/AbstractAccessLogValve.java
index 0d73b96..2b1c220 100644
--- a/java/org/apache/catalina/valves/AbstractAccessLogValve.java
+++ b/java/org/apache/catalina/valves/AbstractAccessLogValve.java
@@ -475,7 +475,7 @@ public abstract class AbstractAccessLogValve extends ValveBase implements Access
}
/**
- * @return Returns the enabled.
+ * @return the enabled flag.
*/
public boolean getEnabled() {
return enabled;
@@ -490,7 +490,7 @@ public abstract class AbstractAccessLogValve extends ValveBase implements Access
}
/**
- * Return the format pattern.
+ * @return the format pattern.
*/
public String getPattern() {
return (this.pattern);
@@ -519,6 +519,7 @@ public abstract class AbstractAccessLogValve extends ValveBase implements Access
* Return whether the attribute name to look for when
* performing conditional logging. If null, every
* request is logged.
+ * @return the attribute name
*/
public String getCondition() {
return condition;
@@ -540,6 +541,7 @@ public abstract class AbstractAccessLogValve extends ValveBase implements Access
* Return whether the attribute name to look for when
* performing conditional logging. If null, every
* request is logged.
+ * @return the attribute name
*/
public String getConditionUnless() {
return getCondition();
@@ -560,6 +562,7 @@ public abstract class AbstractAccessLogValve extends ValveBase implements Access
* Return whether the attribute name to look for when
* performing conditional logging. If null, every
* request is logged.
+ * @return the attribute name
*/
public String getConditionIf() {
return conditionIf;
@@ -579,6 +582,7 @@ public abstract class AbstractAccessLogValve extends ValveBase implements Access
/**
* Return the locale used to format timestamps in log entries and in
* log file name suffix.
+ * @return the locale
*/
public String getLocale() {
return localeName;
@@ -671,8 +675,8 @@ public abstract class AbstractAccessLogValve extends ValveBase implements Access
* 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.
- *
- * @return Date
+ * @param systime The time
+ * @return the date object
*/
private static Date getDate(long systime) {
Date date = localDate.get();
@@ -682,7 +686,10 @@ public abstract class AbstractAccessLogValve extends ValveBase implements Access
/**
- * Find a locale by name
+ * Find a locale by name.
+ * @param name The locale name
+ * @param fallback Fallback locale if the name is not found
+ * @return the locale object
*/
protected static Locale findLocale(String name, Locale fallback) {
if (name == null || name.isEmpty()) {
@@ -1155,7 +1162,7 @@ public abstract class AbstractAccessLogValve extends ValveBase implements Access
private final boolean conversion;
/**
- * if conversion is true, write '-' instead of 0 - %b
+ * @param conversion <code>true</code> to write '-' instead of 0 - %b.
*/
public ByteSentElement(boolean conversion) {
this.conversion = conversion;
@@ -1209,8 +1216,8 @@ public abstract class AbstractAccessLogValve extends ValveBase implements Access
private final boolean millis;
/**
- * if millis is true, write time in millis - %D
- * if millis is false, write time in seconds - %T
+ * @param millis <code>true</code>, write time in millis - %D,
+ * if <code>false</code>, write time in seconds - %T
*/
public ElapsedTimeElement(boolean millis) {
this.millis = millis;
@@ -1477,7 +1484,8 @@ public abstract class AbstractAccessLogValve extends ValveBase implements Access
/**
- * parse pattern string and create the array of AccessLogElement
+ * Parse pattern string and create the array of AccessLogElement.
+ * @return the log elements array
*/
protected AccessLogElement[] createLogElements() {
List<AccessLogElement> list = new ArrayList<>();
@@ -1526,7 +1534,10 @@ public abstract class AbstractAccessLogValve extends ValveBase implements Access
}
/**
- * create an AccessLogElement implementation which needs an element name
+ * Create an AccessLogElement implementation which needs an element name.
+ * @param name Header name
+ * @param pattern char in the log pattern
+ * @return the log element
*/
protected AccessLogElement createAccessLogElement(String name, char pattern) {
switch (pattern) {
@@ -1550,7 +1561,9 @@ public abstract class AbstractAccessLogValve extends ValveBase implements Access
}
/**
- * create an AccessLogElement implementation
+ * Create an AccessLogElement implementation.
+ * @param pattern char in the log pattern
+ * @return the log element
*/
protected AccessLogElement createAccessLogElement(char pattern) {
switch (pattern) {
diff --git a/java/org/apache/catalina/valves/AccessLogValve.java b/java/org/apache/catalina/valves/AccessLogValve.java
index cbf5429..7d1c92f 100644
--- a/java/org/apache/catalina/valves/AccessLogValve.java
+++ b/java/org/apache/catalina/valves/AccessLogValve.java
@@ -160,7 +160,7 @@ public class AccessLogValve extends AbstractAccessLogValve {
/**
- * Return the directory in which we create log files.
+ * @return the directory in which we create log files.
*/
public String getDirectory() {
return (directory);
@@ -178,6 +178,7 @@ public class AccessLogValve extends AbstractAccessLogValve {
/**
* Check for file existence before logging.
+ * @return <code>true</code> if file existence is checked first
*/
public boolean isCheckExists() {
@@ -199,7 +200,7 @@ public class AccessLogValve extends AbstractAccessLogValve {
/**
- * Return the log file prefix.
+ * @return the log file prefix.
*/
public String getPrefix() {
return (prefix);
@@ -238,7 +239,9 @@ public class AccessLogValve extends AbstractAccessLogValve {
/**
* Should we defer inclusion of the date stamp in the file
- * name until rotate time
+ * name until rotate time.
+ * @return <code>true</code> if the logs file names are time stamped
+ * only when they are rotated
*/
public boolean isRenameOnRotate() {
return renameOnRotate;
@@ -257,7 +260,8 @@ public class AccessLogValve extends AbstractAccessLogValve {
/**
- * Is the logging buffered
+ * Is the logging buffered. Usually buffering can increase performance.
+ * @return <code>true</code> if the logging uses a buffer
*/
public boolean isBuffered() {
return buffered;
@@ -267,7 +271,7 @@ public class AccessLogValve extends AbstractAccessLogValve {
/**
* Set the value if the logging should be buffered
*
- * @param buffered true if buffered.
+ * @param buffered <code>true</code> if buffered.
*/
public void setBuffered(boolean buffered) {
this.buffered = buffered;
@@ -275,7 +279,7 @@ public class AccessLogValve extends AbstractAccessLogValve {
/**
- * Return the log file suffix.
+ * @return the log file suffix.
*/
public String getSuffix() {
return (suffix);
@@ -292,7 +296,7 @@ public class AccessLogValve extends AbstractAccessLogValve {
}
/**
- * Return the date format date based log rotation.
+ * @return the date format date based log rotation.
*/
public String getFileDateFormat() {
return fileDateFormat;
@@ -300,7 +304,8 @@ public class AccessLogValve extends AbstractAccessLogValve {
/**
- * Set the date format date based log rotation.
+ * Set the date format date based log rotation.
+ * @param fileDateFormat The format for the file timestamp
*/
public void setFileDateFormat(String fileDateFormat) {
String newFormat;
@@ -388,7 +393,6 @@ public class AccessLogValve extends AbstractAccessLogValve {
* old log file name up once again. Intended to be called by a JMX
* agent.
*
- *
* @param newFileName The file name to move the log file entry to
* @return true if a file was rotated with no error
*/
diff --git a/java/org/apache/catalina/valves/CometConnectionManagerValve.java b/java/org/apache/catalina/valves/CometConnectionManagerValve.java
deleted file mode 100644
index 1736746..0000000
--- a/java/org/apache/catalina/valves/CometConnectionManagerValve.java
+++ /dev/null
@@ -1,343 +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.valves;
-
-
-import java.io.IOException;
-import java.io.Serializable;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.List;
-
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpSession;
-import javax.servlet.http.HttpSessionEvent;
-import javax.servlet.http.HttpSessionListener;
-
-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.comet.CometEvent;
-import org.apache.catalina.comet.CometProcessor;
-import org.apache.catalina.connector.CometEventImpl;
-import org.apache.catalina.connector.Request;
-import org.apache.catalina.connector.Response;
-
-
-/**
- * <p>Implementation of a Valve that tracks Comet connections, and closes them
- * when the associated session expires or the webapp is reloaded.</p>
- *
- * <p>This Valve should be attached to a Context.</p>
- *
- * @author Remy Maucherat
- */
-public class CometConnectionManagerValve extends ValveBase
- implements HttpSessionListener, LifecycleListener {
-
- //------------------------------------------------------ Constructor
- public CometConnectionManagerValve() {
- super(false);
- }
-
-
- // ----------------------------------------------------- Instance Variables
-
- /**
- * List of current Comet connections.
- */
- protected final List<Request> cometRequests =
- Collections.synchronizedList(new ArrayList<Request>());
-
-
- /**
- * Name of session attribute used to store list of comet connections.
- */
- protected static final String cometRequestsAttribute =
- "org.apache.tomcat.comet.connectionList";
-
-
- /**
- * Start this component and implement the requirements
- * of {@link org.apache.catalina.util.LifecycleBase#startInternal()}.
- *
- * @exception LifecycleException if this component detects a fatal error
- * that prevents this component from being used
- */
- @Override
- protected synchronized void startInternal() throws LifecycleException {
-
- if (container instanceof Context) {
- container.addLifecycleListener(this);
- }
-
- setState(LifecycleState.STARTING);
- }
-
-
- /**
- * Stop this component and implement the requirements
- * of {@link org.apache.catalina.util.LifecycleBase#stopInternal()}.
- *
- * @exception LifecycleException if this component detects a fatal error
- * that prevents this component from being used
- */
- @Override
- protected synchronized void stopInternal() throws LifecycleException {
-
- setState(LifecycleState.STOPPING);
-
- if (container instanceof Context) {
- container.removeLifecycleListener(this);
- }
- }
-
-
- @Override
- public void lifecycleEvent(LifecycleEvent event) {
- if (Lifecycle.BEFORE_STOP_EVENT.equals(event.getType())) {
- // The container is getting stopped, close all current connections
- Iterator<Request> iterator = cometRequests.iterator();
- while (iterator.hasNext()) {
- Request request = iterator.next();
- // Remove the session tracking attribute as it isn't
- // serializable or required.
- HttpSession session = request.getSession(false);
- if (session != null) {
- session.removeAttribute(cometRequestsAttribute);
- }
- // Close the comet connection
- CometEventImpl cometEvent = request.getEvent();
- try {
- cometEvent.setEventType(CometEvent.EventType.END);
- cometEvent.setEventSubType(
- CometEvent.EventSubType.WEBAPP_RELOAD);
- getNext().event(request, request.getResponse(), cometEvent);
- } catch (Exception e) {
- container.getLogger().warn(
- sm.getString("cometConnectionManagerValve.event"),
- e);
- } finally {
- try {
- cometEvent.close();
- } catch (IOException e) {
- container.getLogger().warn(sm.getString(
- "cometConnectionManagerValve.event"), e);
- }
- }
- }
- cometRequests.clear();
- }
- }
-
-
- // --------------------------------------------------------- Public Methods
-
- /**
- * Register requests for tracking, whenever needed.
- *
- * @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 {
- // Perform the request
- getNext().invoke(request, response);
-
- if (request.isComet() && !response.isClosed()) {
- // Start tracking this connection, since this is a
- // begin event, and Comet mode is on
- HttpSession session = request.getSession(true);
-
- // Track the connection for webapp reload
- cometRequests.add(request);
-
- // Track the connection for session expiration
- synchronized (session) {
- ConnectionList list = (ConnectionList) session.getAttribute(
- cometRequestsAttribute);
- Request[] requests = null;
- if (list != null) {
- requests = list.get();
- }
- if (requests == null) {
- requests = new Request[1];
- requests[0] = request;
- session.setAttribute(cometRequestsAttribute,
- new ConnectionList(requests));
- } else {
- Request[] newRequests =
- new Request[requests.length + 1];
- for (int i = 0; i < requests.length; i++) {
- newRequests[i] = requests[i];
- }
- newRequests[requests.length] = request;
- session.setAttribute(cometRequestsAttribute,
- new ConnectionList(newRequests));
- }
- }
- }
-
- }
-
-
- /**
- * Use events to update the connection state.
- *
- * @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 event(Request request, Response response, CometEvent event)
- throws IOException, ServletException {
-
- // Perform the request
- boolean ok = false;
- try {
- getNext().event(request, response, event);
- ok = true;
- } finally {
- if (!ok || response.isClosed()
- || (event.getEventType() == CometEvent.EventType.END)
- || (event.getEventType() == CometEvent.EventType.ERROR
- && !(event.getEventSubType() ==
- CometEvent.EventSubType.TIMEOUT))) {
-
- // Remove the connection from webapp reload tracking
- cometRequests.remove(request);
-
- // Remove connection from session expiration tracking
- // Note: can't get the session if it has been invalidated but
- // OK since session listener will have done clean-up
- HttpSession session = request.getSession(false);
- if (session != null) {
- synchronized (session) {
- Request[] reqs = null;
- try {
- ConnectionList list =
- (ConnectionList) session.getAttribute(
- cometRequestsAttribute);
- if (list != null) {
- reqs = list.get();
- }
- } catch (IllegalStateException ise) {
- // Ignore - session has been invalidated
- // Listener will have cleaned up
- }
- if (reqs != null) {
- boolean found = false;
- for (int i = 0; !found && (i < reqs.length); i++) {
- found = (reqs[i] == request);
- }
- if (found) {
- if (reqs.length > 1) {
- Request[] newConnectionInfos =
- new Request[reqs.length - 1];
- int pos = 0;
- for (int i = 0; i < reqs.length; i++) {
- if (reqs[i] != request) {
- newConnectionInfos[pos++] = reqs[i];
- }
- }
- try {
- session.setAttribute(
- cometRequestsAttribute,
- new ConnectionList(
- newConnectionInfos));
- } catch (IllegalStateException ise) {
- // Ignore - session has been invalidated
- // Listener will have cleaned up
- }
- } else {
- try {
- session.removeAttribute(
- cometRequestsAttribute);
- } catch (IllegalStateException ise) {
- // Ignore - session has been invalidated
- // Listener will have cleaned up
- }
- }
- }
- }
- }
- }
- }
- }
-
- }
-
-
- @Override
- public void sessionCreated(HttpSessionEvent se) {
- // NOOP
- }
-
-
- @Override
- public void sessionDestroyed(HttpSessionEvent se) {
- // Close all Comet connections associated with this session
- ConnectionList list = (ConnectionList) se.getSession().getAttribute(
- cometRequestsAttribute);
- Request[] reqs = null;
- if (list != null) {
- reqs = list.get();
- }
- if (reqs != null) {
- for (int i = 0; i < reqs.length; i++) {
- Request req = reqs[i];
- try {
- CometEventImpl event = req.getEvent();
- event.setEventType(CometEvent.EventType.END);
- event.setEventSubType(CometEvent.EventSubType.SESSION_END);
- ((CometProcessor)
- req.getWrapper().getServlet()).event(event);
- event.close();
- } catch (Exception e) {
- req.getWrapper().getParent().getLogger().warn(sm.getString(
- "cometConnectionManagerValve.listenerEvent"), e);
- }
- }
- }
- }
-
-
- private static class ConnectionList implements Serializable {
-
- private static final long serialVersionUID = 1L;
-
- private transient Request[] connectionList = null;
-
- private ConnectionList(Request[] connectionList){
- this.connectionList = connectionList;
- }
-
- public Request[] get(){
- return connectionList;
- }
- }
-}
diff --git a/java/org/apache/catalina/valves/ErrorReportValve.java b/java/org/apache/catalina/valves/ErrorReportValve.java
index a4f150f..c67a4d4 100644
--- a/java/org/apache/catalina/valves/ErrorReportValve.java
+++ b/java/org/apache/catalina/valves/ErrorReportValve.java
@@ -287,6 +287,8 @@ public class ErrorReportValve extends ValveBase {
/**
* Print out a partial servlet stack trace (truncating at the last
* occurrence of javax.servlet.).
+ * @param t The stack trace to process
+ * @return the stack trace relative to the application layer
*/
protected String getPartialServletStackTrace(Throwable t) {
StringBuilder trace = new StringBuilder();
@@ -313,7 +315,7 @@ public class ErrorReportValve extends ValveBase {
/**
* Enables/Disables full error reports
*
- * @param showReport
+ * @param showReport <code>true</code> to show full error data
*/
public void setShowReport(boolean showReport) {
this.showReport = showReport;
@@ -326,7 +328,7 @@ public class ErrorReportValve extends ValveBase {
/**
* Enables/Disables server info on error pages
*
- * @param showServerInfo
+ * @param showServerInfo <code>true</code> to show server info
*/
public void setShowServerInfo(boolean showServerInfo) {
this.showServerInfo = showServerInfo;
diff --git a/java/org/apache/catalina/valves/JDBCAccessLogValve.java b/java/org/apache/catalina/valves/JDBCAccessLogValve.java
index 4e53d6e..e222f12 100644
--- a/java/org/apache/catalina/valves/JDBCAccessLogValve.java
+++ b/java/org/apache/catalina/valves/JDBCAccessLogValve.java
@@ -165,7 +165,7 @@ public final class JDBCAccessLogValve extends ValveBase implements AccessLog {
* Use long contentLength as you have more 4 GB output.
* @since 6.0.15
*/
- boolean useLongContentLength = false ;
+ boolean useLongContentLength = false;
/**
* The connection username to use when trying to connect to the database.
@@ -232,8 +232,7 @@ public final class JDBCAccessLogValve extends ValveBase implements AccessLog {
}
/**
- * Return the username to use to connect to the database.
- *
+ * @return the username to use to connect to the database.
*/
public String getConnectionName() {
return connectionName;
@@ -257,9 +256,8 @@ public final class JDBCAccessLogValve extends ValveBase implements AccessLog {
this.driverName = driverName;
}
- /**
- * Return the password to use to connect to the database.
- *
+ /**
+ * @return the password to use to connect to the database.
*/
public String getConnectionPassword() {
return connectionPassword;
@@ -356,11 +354,11 @@ public final class JDBCAccessLogValve extends ValveBase implements AccessLog {
}
- /**
- * Sets the name of the field containing the HTTP response status code.
- *
- * @param statusField The name of the HTTP response status code field.
- */
+ /**
+ * Sets the name of the field containing the HTTP response status code.
+ *
+ * @param statusField The name of the HTTP response status code field.
+ */
public void setStatusField(String statusField) {
this.statusField = statusField;
}
@@ -420,10 +418,11 @@ public final class JDBCAccessLogValve extends ValveBase implements AccessLog {
}
/**
- * get useLongContentLength
+ * @return <code>true</code> if content length should be considered a long
+ * rather than an int, defaults to <code>false</code>
*/
- public boolean getUseLongContentLength() {
- return this.useLongContentLength ;
+ public boolean getUseLongContentLength() {
+ return this.useLongContentLength;
}
/**
diff --git a/java/org/apache/catalina/valves/LocalStrings.properties b/java/org/apache/catalina/valves/LocalStrings.properties
index 54b5d73..75e3539 100644
--- a/java/org/apache/catalina/valves/LocalStrings.properties
+++ b/java/org/apache/catalina/valves/LocalStrings.properties
@@ -15,8 +15,6 @@
jdbcAccessLogValve.close=Failed to close database
jdbcAccessLogValve.exception=Exception performing insert access entry
-cometConnectionManagerValve.event=Exception processing event
-cometConnectionManagerValve.listenerEvent=Exception processing session listener event
# Access log valve
accessLogValve.openFail=Failed to open access log file [{0}]
diff --git a/java/org/apache/catalina/valves/LocalStrings_es.properties b/java/org/apache/catalina/valves/LocalStrings_es.properties
index bba69a5..412d12a 100644
--- a/java/org/apache/catalina/valves/LocalStrings_es.properties
+++ b/java/org/apache/catalina/valves/LocalStrings_es.properties
@@ -13,8 +13,6 @@
# See the License for the specific language governing permissions and
# limitations under the License.
jdbcAccessLogValve.exception = Excepci\u00F3n realizando entrada de acceso a inserci\u00F3n
-cometConnectionManagerValve.event = Excepci\u00F3n procesando evento
-cometConnectionManagerValve.listenerEvent = Excepci\u00F3n procesando evento de oyente de sesi\u00F3n
accessLogValve.closeFail = No pude cerrar fichero de historial
accessLogValve.openDirFail = No pude crear directorio [{0}] para historiales de acceso
accessLogValve.rotateFail = No pude rotar historial de acceso
diff --git a/java/org/apache/catalina/valves/PersistentValve.java b/java/org/apache/catalina/valves/PersistentValve.java
index dfa677f..01ec3fe 100644
--- a/java/org/apache/catalina/valves/PersistentValve.java
+++ b/java/org/apache/catalina/valves/PersistentValve.java
@@ -197,6 +197,9 @@ public class PersistentValve extends ValveBase {
* than its expiration date as of the supplied time.
*
* FIXME: Probably belongs in the Session class.
+ * @param session The session to check
+ * @param timeNow The current time to check for
+ * @return <code>true</code> if the session is past its expiration
*/
protected boolean isSessionStale(Session session, long timeNow) {
diff --git a/java/org/apache/catalina/valves/RemoteAddrValve.java b/java/org/apache/catalina/valves/RemoteAddrValve.java
index 39e790e..69c7160 100644
--- a/java/org/apache/catalina/valves/RemoteAddrValve.java
+++ b/java/org/apache/catalina/valves/RemoteAddrValve.java
@@ -55,6 +55,8 @@ public final class RemoteAddrValve extends RequestFilterValve {
* Get the flag deciding whether we add the server connector port to the
* property compared in the filtering method. The port will be appended
* using a ";" as a separator.
+ * @return <code>true</code> to add the connector port, the default is
+ * <code>false</code>
*/
public boolean getAddConnectorPort() {
return addConnectorPort;
diff --git a/java/org/apache/catalina/valves/RemoteHostValve.java b/java/org/apache/catalina/valves/RemoteHostValve.java
index 928b478..90a9c71 100644
--- a/java/org/apache/catalina/valves/RemoteHostValve.java
+++ b/java/org/apache/catalina/valves/RemoteHostValve.java
@@ -53,6 +53,7 @@ public final class RemoteHostValve extends RequestFilterValve {
* Get the flag deciding whether we add the server connector port to the
* property compared in the filtering method. The port will be appended
* using a ";" as a separator.
+ * @return <code>true</code> to add the connector port
*/
public boolean getAddConnectorPort() {
return addConnectorPort;
diff --git a/java/org/apache/catalina/valves/RemoteIpValve.java b/java/org/apache/catalina/valves/RemoteIpValve.java
index 867bcfa..b817b35 100644
--- a/java/org/apache/catalina/valves/RemoteIpValve.java
+++ b/java/org/apache/catalina/valves/RemoteIpValve.java
@@ -144,7 +144,6 @@ import org.apache.tomcat.util.http.MimeHeaders;
* </tr>
* </table>
* <p>
- * <p>
* This Valve may be attached to any Container, depending on the granularity of the filtering you wish to perform.
* </p>
* <p>
@@ -342,7 +341,7 @@ import org.apache.tomcat.util.http.MimeHeaders;
* </table>
* <p>
* Note : <code>x-forwarded-by</code> holds the trusted proxy <code>proxy1</code>. <code>x-forwarded-by</code> holds
- * <code>140.211.11.130</code> because <code>untrusted-proxy</code> is not trusted and thus, we can not trust that
+ * <code>140.211.11.130</code> because <code>untrusted-proxy</code> is not trusted and thus, we cannot trust that
* <code>untrusted-proxy</code> is the actual remote ip. <code>request.remoteAddr</code> is <code>untrusted-proxy</code> that is an IP
* verified by <code>proxy1</code>.
* </p>
@@ -361,7 +360,7 @@ public class RemoteIpValve extends ValveBase {
/**
* Convert a given comma delimited String into an array of String
- *
+ * @param commaDelimitedStrings The string to convert
* @return array of String (non <code>null</code>)
*/
protected static String[] commaDelimitedListToStringArray(String commaDelimitedStrings) {
@@ -371,6 +370,8 @@ public class RemoteIpValve extends ValveBase {
/**
* Convert an array of strings in a comma delimited string
+ * @param stringList The string list to convert
+ * @return The concatenated string
*/
protected static String listToCommaDelimitedString(List<String> stringList) {
if (stringList == null) {
@@ -729,6 +730,7 @@ public class RemoteIpValve extends ValveBase {
* <p>
* Default value : 80
* </p>
+ * @param httpServerPort The server port
*/
public void setHttpServerPort(int httpServerPort) {
this.httpServerPort = httpServerPort;
@@ -741,6 +743,7 @@ public class RemoteIpValve extends ValveBase {
* <p>
* Default value : 443
* </p>
+ * @param httpsServerPort The server port
*/
public void setHttpsServerPort(int httpsServerPort) {
this.httpsServerPort = httpsServerPort;
@@ -753,6 +756,7 @@ public class RemoteIpValve extends ValveBase {
* <p>
* Default value : 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}
* </p>
+ * @param internalProxies The proxy regular expression
*/
public void setInternalProxies(String internalProxies) {
if (internalProxies == null || internalProxies.length() == 0) {
@@ -770,6 +774,7 @@ public class RemoteIpValve extends ValveBase {
* <p>
* Default value : <code>null</code>
* </p>
+ * @param protocolHeader The header name
*/
public void setProtocolHeader(String protocolHeader) {
this.protocolHeader = protocolHeader;
@@ -782,6 +787,7 @@ public class RemoteIpValve extends ValveBase {
* <p>
* Default value : <code>https</code>
* </p>
+ * @param protocolHeaderHttpsValue The header name
*/
public void setProtocolHeaderHttpsValue(String protocolHeaderHttpsValue) {
this.protocolHeaderHttpsValue = protocolHeaderHttpsValue;
@@ -802,6 +808,7 @@ public class RemoteIpValve extends ValveBase {
* <p>
* Default value : <code>X-Forwarded-By</code>
* </p>
+ * @param proxiesHeader The header name
*/
public void setProxiesHeader(String proxiesHeader) {
this.proxiesHeader = proxiesHeader;
@@ -818,7 +825,7 @@ public class RemoteIpValve extends ValveBase {
* Default value : <code>X-Forwarded-For</code>
* </p>
*
- * @param remoteIpHeader
+ * @param remoteIpHeader The header name
*/
public void setRemoteIpHeader(String remoteIpHeader) {
this.remoteIpHeader = remoteIpHeader;
@@ -855,6 +862,7 @@ public class RemoteIpValve extends ValveBase {
* <p>
* Default value : empty list, no external proxy is trusted.
* </p>
+ * @param trustedProxies The regular expression
*/
public void setTrustedProxies(String trustedProxies) {
if (trustedProxies == null || trustedProxies.length() == 0) {
diff --git a/java/org/apache/catalina/valves/RequestFilterValve.java b/java/org/apache/catalina/valves/RequestFilterValve.java
index 4606036..ad6df92 100644
--- a/java/org/apache/catalina/valves/RequestFilterValve.java
+++ b/java/org/apache/catalina/valves/RequestFilterValve.java
@@ -138,6 +138,7 @@ public abstract class RequestFilterValve extends ValveBase {
/**
* Return the regular expression used to test for allowed requests for this
* Valve, if any; otherwise, return <code>null</code>.
+ * @return the regular expression
*/
public String getAllow() {
return allowValue;
@@ -171,6 +172,7 @@ public abstract class RequestFilterValve extends ValveBase {
/**
* Return the regular expression used to test for denied requests for this
* Valve, if any; otherwise, return <code>null</code>.
+ * @return the regular expression
*/
public String getDeny() {
return denyValue;
@@ -205,6 +207,7 @@ public abstract class RequestFilterValve extends ValveBase {
* Returns {@code false} if the last change to the {@code allow} pattern did
* not apply successfully. E.g. if the pattern is syntactically
* invalid.
+ * @return <code>false</code> if the current pattern is invalid
*/
public final boolean isAllowValid() {
return allowValid;
@@ -215,6 +218,7 @@ public abstract class RequestFilterValve extends ValveBase {
* Returns {@code false} if the last change to the {@code deny} pattern did
* not apply successfully. E.g. if the pattern is syntactically
* invalid.
+ * @return <code>false</code> if the current pattern is invalid
*/
public final boolean isDenyValid() {
return denyValid;
@@ -222,7 +226,7 @@ public abstract class RequestFilterValve extends ValveBase {
/**
- * Return response status code that is used to reject denied request.
+ * @return response status code that is used to reject denied request.
*/
public int getDenyStatus() {
return denyStatus;
@@ -231,6 +235,7 @@ public abstract class RequestFilterValve extends ValveBase {
/**
* Set response status code that is used to reject denied request.
+ * @param denyStatus The status code
*/
public void setDenyStatus(int denyStatus) {
this.denyStatus = denyStatus;
@@ -238,7 +243,7 @@ public abstract class RequestFilterValve extends ValveBase {
/**
- * Return true if a deny is handled by setting an invalid auth header.
+ * @return <code>true</code> if a deny is handled by setting an invalid auth header.
*/
public boolean getInvalidAuthenticationWhenDeny() {
return invalidAuthenticationWhenDeny;
@@ -247,6 +252,7 @@ public abstract class RequestFilterValve extends ValveBase {
/**
* Set invalidAuthenticationWhenDeny property.
+ * @param value <code>true</code> to handle a deny by setting an invalid auth header
*/
public void setInvalidAuthenticationWhenDeny(boolean value) {
invalidAuthenticationWhenDeny = value;
@@ -360,8 +366,8 @@ public abstract class RequestFilterValve extends ValveBase {
* called through JMX, e.g. to test whether certain IP address is allowed or
* denied by the valve configuration.
*
- * @param property
- * The request property value on which to filter
+ * @param property The request property value on which to filter
+ * @return <code>true</code> if the request is allowed
*/
public boolean isAllowed(String property) {
// Use local copies for thread safety
diff --git a/java/org/apache/catalina/valves/SemaphoreValve.java b/java/org/apache/catalina/valves/SemaphoreValve.java
index 61070e8..1562758 100644
--- a/java/org/apache/catalina/valves/SemaphoreValve.java
+++ b/java/org/apache/catalina/valves/SemaphoreValve.java
@@ -172,8 +172,10 @@ public class SemaphoreValve extends ValveBase {
/**
* Subclass friendly method to add conditions.
- * @param request
- * @param response
+ * @param request The Servlet request
+ * @param response The Servlet response
+ * @return <code>true</code> if the concurrency control should occur
+ * on this request
*/
public boolean controlConcurrency(Request request, Response response) {
return true;
@@ -183,10 +185,10 @@ public class SemaphoreValve extends ValveBase {
/**
* Subclass friendly method to add error handling when a permit isn't
* granted.
- * @param request
- * @param response
- * @throws IOException
- * @throws ServletException
+ * @param request The Servlet request
+ * @param response The Servlet response
+ * @throws IOException Error writing output
+ * @throws ServletException Other error
*/
public void permitDenied(Request request, Response response)
throws IOException, ServletException {
diff --git a/java/org/apache/catalina/valves/ValveBase.java b/java/org/apache/catalina/valves/ValveBase.java
index fcf0923..82ae932 100644
--- a/java/org/apache/catalina/valves/ValveBase.java
+++ b/java/org/apache/catalina/valves/ValveBase.java
@@ -16,25 +16,16 @@
*/
package org.apache.catalina.valves;
-
-import java.io.IOException;
-
-import javax.servlet.ServletException;
-
import org.apache.catalina.Contained;
import org.apache.catalina.Container;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.LifecycleState;
import org.apache.catalina.Pipeline;
import org.apache.catalina.Valve;
-import org.apache.catalina.comet.CometEvent;
-import org.apache.catalina.connector.Request;
-import org.apache.catalina.connector.Response;
import org.apache.catalina.util.LifecycleMBeanBase;
import org.apache.juli.logging.Log;
import org.apache.tomcat.util.res.StringManager;
-
/**
* Convenience base class for implementations of the <b>Valve</b> interface.
* A subclass <strong>MUST</strong> implement an <code>invoke()</code>
@@ -44,8 +35,10 @@ import org.apache.tomcat.util.res.StringManager;
*
* @author Craig R. McClanahan
*/
-public abstract class ValveBase extends LifecycleMBeanBase
- implements Contained, Valve {
+public abstract class ValveBase extends LifecycleMBeanBase implements Contained, Valve {
+
+ protected static final StringManager sm = StringManager.getManager(ValveBase.class);
+
//------------------------------------------------------ Constructor
@@ -53,16 +46,20 @@ public abstract class ValveBase extends LifecycleMBeanBase
this(false);
}
+
public ValveBase(boolean asyncSupported) {
this.asyncSupported = asyncSupported;
}
+
//------------------------------------------------------ Instance Variables
+
/**
* Does this valve support Servlet 3+ async requests?
*/
protected boolean asyncSupported;
+
/**
* The Container whose pipeline this Valve is a component of.
*/
@@ -81,24 +78,25 @@ public abstract class ValveBase extends LifecycleMBeanBase
protected Valve next = null;
- /**
- * The string manager for this package.
- */
- protected static final StringManager sm =
- StringManager.getManager(Constants.Package);
-
-
//-------------------------------------------------------------- Properties
-
/**
* Return the Container with which this Valve is associated, if any.
*/
@Override
public Container getContainer() {
+ return container;
+ }
- return (container);
+ /**
+ * Set the Container with which this Valve is associated, if any.
+ *
+ * @param container The new associated container
+ */
+ @Override
+ public void setContainer(Container container) {
+ this.container = container;
}
@@ -114,27 +112,12 @@ public abstract class ValveBase extends LifecycleMBeanBase
/**
- * Set the Container with which this Valve is associated, if any.
- *
- * @param container The new associated container
- */
- @Override
- public void setContainer(Container container) {
-
- this.container = container;
-
- }
-
-
- /**
* Return the next Valve in this pipeline, or <code>null</code> if this
* is the last Valve in the pipeline.
*/
@Override
public Valve getNext() {
-
- return (next);
-
+ return next;
}
@@ -145,15 +128,12 @@ public abstract class ValveBase extends LifecycleMBeanBase
*/
@Override
public void setNext(Valve valve) {
-
this.next = valve;
-
}
//---------------------------------------------------------- Public Methods
-
/**
* Execute a periodic task, such as reloading, etc. This method will be
* invoked inside the classloading context of this container. Unexpected
@@ -165,48 +145,9 @@ public abstract class ValveBase extends LifecycleMBeanBase
}
- /**
- * The implementation-specific logic represented by this Valve. See the
- * Valve description for the normal design patterns for this method.
- * <p>
- * This method <strong>MUST</strong> be provided by a subclass.
- *
- * @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 abstract void invoke(Request request, Response response)
- throws IOException, ServletException;
-
-
- /**
- * Process a Comet event. This method will rarely need to be provided by
- * a subclass, unless it needs to reassociate a particular object with
- * the thread that is processing the request.
- *
- * @param request The servlet request to be processed
- * @param response The servlet response to be created
- *
- * @exception IOException if an input/output error occurs, or is thrown
- * by a subsequently invoked Valve, Filter, or Servlet
- * @exception ServletException if a servlet error occurs, or is thrown
- * by a subsequently invoked Valve, Filter, or Servlet
- */
- @Override
- public void event(Request request, Response response, CometEvent event)
- throws IOException, ServletException {
- // Perform the request
- getNext().event(request, response, event);
- }
-
-
@Override
protected void initInternal() throws LifecycleException {
super.initInternal();
-
containerLog = getContainer().getLogger();
}
@@ -220,7 +161,6 @@ public abstract class ValveBase extends LifecycleMBeanBase
*/
@Override
protected synchronized void startInternal() throws LifecycleException {
-
setState(LifecycleState.STARTING);
}
@@ -234,7 +174,6 @@ public abstract class ValveBase extends LifecycleMBeanBase
*/
@Override
protected synchronized void stopInternal() throws LifecycleException {
-
setState(LifecycleState.STOPPING);
}
@@ -257,6 +196,7 @@ public abstract class ValveBase extends LifecycleMBeanBase
// -------------------- JMX and Registration --------------------
+
@Override
public String getObjectNameKeyProperties() {
StringBuilder name = new StringBuilder("type=Valve");
@@ -303,6 +243,7 @@ public abstract class ValveBase extends LifecycleMBeanBase
return name.toString();
}
+
@Override
public String getDomainInternal() {
Container c = getContainer();
diff --git a/java/org/apache/catalina/valves/mbeans-descriptors.xml b/java/org/apache/catalina/valves/mbeans-descriptors.xml
index 2817974..c2d25a2 100644
--- a/java/org/apache/catalina/valves/mbeans-descriptors.xml
+++ b/java/org/apache/catalina/valves/mbeans-descriptors.xml
@@ -1,4 +1,4 @@
-<?xml version="1.0"?>
+<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
diff --git a/java/org/apache/catalina/valves/rewrite/RewriteCond.java b/java/org/apache/catalina/valves/rewrite/RewriteCond.java
index 6b4dd81..cc2f4bd 100644
--- a/java/org/apache/catalina/valves/rewrite/RewriteCond.java
+++ b/java/org/apache/catalina/valves/rewrite/RewriteCond.java
@@ -182,6 +182,8 @@ public class RewriteCond {
*
* @param rule corresponding matched rule
* @param cond last matched condition
+ * @param resolver Property resolver
+ * @return <code>true</code> if the condition matches
*/
public boolean evaluate(Matcher rule, Matcher cond, Resolver resolver) {
String value = test.evaluate(rule, cond, resolver);
diff --git a/java/org/apache/catalina/valves/rewrite/RewriteRule.java b/java/org/apache/catalina/valves/rewrite/RewriteRule.java
index db8cee0..96bc841 100644
--- a/java/org/apache/catalina/valves/rewrite/RewriteRule.java
+++ b/java/org/apache/catalina/valves/rewrite/RewriteRule.java
@@ -77,8 +77,9 @@ public class RewriteRule {
/**
* Evaluate the rule based on the context
- *
- * @return null if no rewrite took place
+ * @param url The char sequence
+ * @param resolver Property resolver
+ * @return <code>null</code> if no rewrite took place
*/
public CharSequence evaluate(CharSequence url, Resolver resolver) {
Pattern pattern = this.pattern.get();
diff --git a/java/org/apache/catalina/valves/rewrite/RewriteValve.java b/java/org/apache/catalina/valves/rewrite/RewriteValve.java
index c343f87..ce4527d 100644
--- a/java/org/apache/catalina/valves/rewrite/RewriteValve.java
+++ b/java/org/apache/catalina/valves/rewrite/RewriteValve.java
@@ -569,7 +569,6 @@ public class RewriteValve extends ValveBase {
request.getCoyoteRequest(), response.getCoyoteResponse())) {
return;
}
- @SuppressWarnings("deprecation")
Pipeline pipeline = connector.getService().getContainer().getPipeline();
request.setAsyncSupported(pipeline.isAsyncSupported());
pipeline.getFirst().invoke(request, response);
@@ -589,7 +588,7 @@ public class RewriteValve extends ValveBase {
/**
- * Get config base.
+ * @return config base.
*/
protected File getConfigBase() {
File configBase =
@@ -605,8 +604,8 @@ public class RewriteValve extends ValveBase {
/**
* Find the configuration path where the rewrite configuration file
* will be stored.
- *
- * @param resourceName
+ * @param resourceName The rewrite configuration file name
+ * @return the full rewrite configuration path
*/
protected String getHostConfigPath(String resourceName) {
StringBuffer result = new StringBuffer();
@@ -638,7 +637,6 @@ public class RewriteValve extends ValveBase {
* RewriteCond %{REMOTE_HOST} ^host1.* [OR]
*
* @param line A line from the rewrite configuration
- *
* @return The condition, rule or map resulting from parsing the line
*/
public static Object parse(String line) {
@@ -715,9 +713,9 @@ public class RewriteValve extends ValveBase {
/**
* Parser for RewriteCond flags.
- *
- * @param condition
- * @param flag
+ * @param line The configuration line being parsed
+ * @param condition The current condition
+ * @param flag The flag
*/
protected static void parseCondFlag(String line, RewriteCond condition, String flag) {
if (flag.equals("NC") || flag.equals("nocase")) {
@@ -732,9 +730,9 @@ public class RewriteValve extends ValveBase {
/**
* Parser for ReweriteRule flags.
- *
- * @param rule
- * @param flag
+ * @param line The configuration line being parsed
+ * @param rule The current rule
+ * @param flag The flag
*/
protected static void parseRuleFlag(String line, RewriteRule rule, String flag) {
if (flag.equals("B")) {
@@ -837,15 +835,4 @@ public class RewriteValve extends ValveBase {
throw new IllegalArgumentException("Invalid flag in: " + line + " flag: " + flag);
}
}
-
-
- /**
- * Determine if a URI string has a <code>scheme</code> component.
- *
- * @deprecated Unused. Will be removed in 8.5.x.
- */
- @Deprecated
- protected static boolean hasScheme(StringBuffer uri) {
- return UriUtil.hasScheme(uri);
- }
}
diff --git a/java/org/apache/catalina/valves/rewrite/Substitution.java b/java/org/apache/catalina/valves/rewrite/Substitution.java
index fc0309c..d1b16de 100644
--- a/java/org/apache/catalina/valves/rewrite/Substitution.java
+++ b/java/org/apache/catalina/valves/rewrite/Substitution.java
@@ -251,10 +251,11 @@ public class Substitution {
}
/**
- * Evaluate the substitution based on the context
- *
+ * Evaluate the substitution based on the context.
* @param rule corresponding matched rule
* @param cond last matched condition
+ * @param resolver The property resolver
+ * @return The substitution result
*/
public String evaluate(Matcher rule, Matcher cond, Resolver resolver) {
StringBuffer buf = new StringBuffer();
diff --git a/java/org/apache/catalina/valves/rewrite/mbeans-descriptors.xml b/java/org/apache/catalina/valves/rewrite/mbeans-descriptors.xml
index 01be5ba..3f8150e 100644
--- a/java/org/apache/catalina/valves/rewrite/mbeans-descriptors.xml
+++ b/java/org/apache/catalina/valves/rewrite/mbeans-descriptors.xml
@@ -1,4 +1,4 @@
-<?xml version="1.0"?>
+<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
diff --git a/java/org/apache/catalina/webresources/AbstractResource.java b/java/org/apache/catalina/webresources/AbstractResource.java
index 4ed3ffd..4160a96 100644
--- a/java/org/apache/catalina/webresources/AbstractResource.java
+++ b/java/org/apache/catalina/webresources/AbstractResource.java
@@ -27,8 +27,7 @@ import org.apache.tomcat.util.res.StringManager;
public abstract class AbstractResource implements WebResource {
- protected static final StringManager sm =
- StringManager.getManager(Constants.Package);
+ protected static final StringManager sm = StringManager.getManager(AbstractResource.class);
private final WebResourceRoot root;
private final String webAppPath;
diff --git a/java/org/apache/catalina/webresources/AbstractResourceSet.java b/java/org/apache/catalina/webresources/AbstractResourceSet.java
index 6a67985..83c7b47 100644
--- a/java/org/apache/catalina/webresources/AbstractResourceSet.java
+++ b/java/org/apache/catalina/webresources/AbstractResourceSet.java
@@ -37,8 +37,7 @@ public abstract class AbstractResourceSet extends LifecycleBase
private Manifest manifest;
- protected static final StringManager sm =
- StringManager.getManager(Constants.Package);
+ protected static final StringManager sm = StringManager.getManager(AbstractResourceSet.class);
protected final void checkPath(String path) {
diff --git a/java/org/apache/catalina/webresources/Cache.java b/java/org/apache/catalina/webresources/Cache.java
index 0704480..f8797f4 100644
--- a/java/org/apache/catalina/webresources/Cache.java
+++ b/java/org/apache/catalina/webresources/Cache.java
@@ -31,8 +31,7 @@ import org.apache.tomcat.util.res.StringManager;
public class Cache {
private static final Log log = LogFactory.getLog(Cache.class);
- protected static final StringManager sm =
- StringManager.getManager(Constants.Package);
+ protected static final StringManager sm = StringManager.getManager(Cache.class);
private static final long TARGET_FREE_PERCENT_GET = 5;
private static final long TARGET_FREE_PERCENT_BACKGROUND = 10;
diff --git a/java/org/apache/catalina/webresources/ClasspathURLStreamHandler.java b/java/org/apache/catalina/webresources/ClasspathURLStreamHandler.java
index 6a6c832..9a9100c 100644
--- a/java/org/apache/catalina/webresources/ClasspathURLStreamHandler.java
+++ b/java/org/apache/catalina/webresources/ClasspathURLStreamHandler.java
@@ -27,7 +27,7 @@ import org.apache.tomcat.util.res.StringManager;
public class ClasspathURLStreamHandler extends URLStreamHandler {
private static final StringManager sm =
- StringManager.getManager(ClasspathURLStreamHandler.class.getPackage().getName());
+ StringManager.getManager(ClasspathURLStreamHandler.class);
@Override
diff --git a/java/org/apache/catalina/webresources/Constants.java b/java/org/apache/catalina/webresources/Constants.java
deleted file mode 100644
index 248a4fe..0000000
--- a/java/org/apache/catalina/webresources/Constants.java
+++ /dev/null
@@ -1,22 +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.webresources;
-
-public final class Constants {
-
- public static final String Package = "org.apache.catalina.webresources";
-}
diff --git a/java/org/apache/catalina/webresources/FileResource.java b/java/org/apache/catalina/webresources/FileResource.java
index 92d9f86..03697d2 100644
--- a/java/org/apache/catalina/webresources/FileResource.java
+++ b/java/org/apache/catalina/webresources/FileResource.java
@@ -16,6 +16,7 @@
*/
package org.apache.catalina.webresources;
+import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
@@ -23,6 +24,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
+import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.attribute.BasicFileAttributes;
import java.security.cert.Certificate;
@@ -40,10 +42,26 @@ public class FileResource extends AbstractResource {
private static final Log log = LogFactory.getLog(FileResource.class);
+ private static final boolean PROPERTIES_NEED_CONVERT;
+ static {
+ boolean isEBCDIC = false;
+ try {
+ String encoding = System.getProperty("file.encoding");
+ if (encoding.indexOf("EBCDIC") != -1) {
+ isEBCDIC = true;
+ }
+ } catch (SecurityException e) {
+ // Ignore
+ }
+ PROPERTIES_NEED_CONVERT = isEBCDIC;
+ }
+
+
private final File resource;
private final String name;
private final boolean readOnly;
private final Manifest manifest;
+ private final boolean needConvert;
public FileResource(WebResourceRoot root, String webAppPath,
File resource, boolean readOnly, Manifest manifest) {
@@ -69,6 +87,7 @@ public class FileResource extends AbstractResource {
this.readOnly = readOnly;
this.manifest = manifest;
+ this.needConvert = PROPERTIES_NEED_CONVERT && name.endsWith(".properties");
}
@Override
@@ -111,6 +130,19 @@ public class FileResource extends AbstractResource {
@Override
public long getContentLength() {
+ return getContentLengthInternal(needConvert);
+ }
+
+ private long getContentLengthInternal(boolean convert) {
+ if (convert) {
+ byte[] content = getContent();
+ if (content == null) {
+ return -1;
+ } else {
+ return content.length;
+ }
+ }
+
if (isDirectory()) {
return -1;
}
@@ -138,6 +170,14 @@ public class FileResource extends AbstractResource {
@Override
protected InputStream doGetInputStream() {
+ if (needConvert) {
+ byte[] content = getContent();
+ if (content == null) {
+ return null;
+ } else {
+ return new ByteArrayInputStream(content);
+ }
+ }
try {
return new FileInputStream(resource);
} catch (FileNotFoundException fnfe) {
@@ -148,7 +188,8 @@ public class FileResource extends AbstractResource {
@Override
public final byte[] getContent() {
- long len = getContentLength();
+ // Use internal version to avoid loop when needConvert is true
+ long len = getContentLengthInternal(false);
if (len > Integer.MAX_VALUE) {
// Can't create an array that big
@@ -182,6 +223,18 @@ public class FileResource extends AbstractResource {
return null;
}
+ if (needConvert) {
+ // Workaround for certain files on platforms that use
+ // EBCDIC encoding, when they are read through FileInputStream.
+ // See commit message of rev.303915 for original details
+ // http://svn.apache.org/viewvc?view=revision&revision=303915
+ String str = new String(result);
+ try {
+ result = str.getBytes(StandardCharsets.UTF_8);
+ } catch (Exception e) {
+ result = null;
+ }
+ }
return result;
}
diff --git a/java/org/apache/catalina/webresources/StandardRoot.java b/java/org/apache/catalina/webresources/StandardRoot.java
index 2eaeb25..1299938 100644
--- a/java/org/apache/catalina/webresources/StandardRoot.java
+++ b/java/org/apache/catalina/webresources/StandardRoot.java
@@ -62,8 +62,7 @@ import org.apache.tomcat.util.res.StringManager;
public class StandardRoot extends LifecycleMBeanBase implements WebResourceRoot {
private static final Log log = LogFactory.getLog(StandardRoot.class);
- protected static final StringManager sm =
- StringManager.getManager(Constants.Package);
+ protected static final StringManager sm = StringManager.getManager(StandardRoot.class);
private Context context;
private boolean allowLinking = false;
@@ -361,6 +360,8 @@ public class StandardRoot extends LifecycleMBeanBase implements WebResourceRoot
return result;
}
+ // TODO: Should the createWebResourceSet() methods be removed to some
+ // utility class for file system based resource sets?
@Override
public void createWebResourceSet(ResourceSetType type, String webAppMount,
@@ -562,10 +563,10 @@ public class StandardRoot extends LifecycleMBeanBase implements WebResourceRoot
this.context = context;
}
- /*
+ /**
* Class loader resources are handled by treating JARs in WEB-INF/lib as
* resource JARs (without the internal META-INF/resources/ prefix) mounted
- * at WEB-INF/claasses (rather than the web app root). This enables reuse
+ * at WEB-INF/classes (rather than the web app root). This enables reuse
* of the resource handling plumbing.
*
* These resources are marked as class loader only so they are only used in
@@ -585,7 +586,8 @@ public class StandardRoot extends LifecycleMBeanBase implements WebResourceRoot
}
/**
- * For unit testing
+ * For unit testing.
+ * @param main The main resources
*/
protected final void setMainResources(WebResourceSet main) {
this.main = main;
diff --git a/java/org/apache/catalina/webresources/mbeans-descriptors.xml b/java/org/apache/catalina/webresources/mbeans-descriptors.xml
index a83bbb6..95093db 100644
--- a/java/org/apache/catalina/webresources/mbeans-descriptors.xml
+++ b/java/org/apache/catalina/webresources/mbeans-descriptors.xml
@@ -1,4 +1,4 @@
-<?xml version="1.0"?>
+<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
diff --git a/java/org/apache/coyote/AbstractProcessor.java b/java/org/apache/coyote/AbstractProcessor.java
index f271139..6fb4085 100644
--- a/java/org/apache/coyote/AbstractProcessor.java
+++ b/java/org/apache/coyote/AbstractProcessor.java
@@ -17,32 +17,39 @@
package org.apache.coyote;
import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.nio.ByteBuffer;
import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicBoolean;
import javax.servlet.RequestDispatcher;
-import org.apache.juli.logging.Log;
+import org.apache.tomcat.util.ExceptionUtils;
+import org.apache.tomcat.util.buf.ByteChunk;
import org.apache.tomcat.util.net.AbstractEndpoint;
import org.apache.tomcat.util.net.AbstractEndpoint.Handler.SocketState;
-import org.apache.tomcat.util.net.SocketStatus;
-import org.apache.tomcat.util.net.SocketWrapper;
+import org.apache.tomcat.util.net.DispatchType;
+import org.apache.tomcat.util.net.SSLSupport;
+import org.apache.tomcat.util.net.SocketEvent;
+import org.apache.tomcat.util.net.SocketWrapperBase;
import org.apache.tomcat.util.res.StringManager;
/**
* Provides functionality and attributes common to all supported protocols
* (currently HTTP and AJP).
*/
-public abstract class AbstractProcessor<S> implements ActionHook, Processor<S> {
+public abstract class AbstractProcessor extends AbstractProcessorLight implements ActionHook {
- protected static final StringManager sm = StringManager.getManager(Constants.Package);
+ private static final StringManager sm = StringManager.getManager(AbstractProcessor.class);
protected Adapter adapter;
protected final AsyncStateMachine asyncStateMachine;
- protected final AbstractEndpoint<S> endpoint;
+ private volatile long asyncTimeout = -1;
+ protected final AbstractEndpoint<?> endpoint;
protected final Request request;
protected final Response response;
- protected volatile SocketWrapper<S> socketWrapper = null;
- private int maxCookieCount = 200;
+ protected volatile SocketWrapperBase<?> socketWrapper = null;
+ protected volatile SSLSupport sslSupport;
/**
@@ -52,30 +59,36 @@ public abstract class AbstractProcessor<S> implements ActionHook, Processor<S> {
/**
- * Intended for use by the Upgrade sub-classes that have no need to
- * initialise the request, response, etc.
+ * Used by HTTP/2.
+ * @param coyoteRequest The request
+ * @param coyoteResponse The response
*/
- protected AbstractProcessor() {
- asyncStateMachine = null;
- endpoint = null;
- request = null;
- response = null;
+ protected AbstractProcessor(Request coyoteRequest, Response coyoteResponse) {
+ this(null, coyoteRequest, coyoteResponse);
}
- public AbstractProcessor(AbstractEndpoint<S> endpoint) {
+
+ public AbstractProcessor(AbstractEndpoint<?> endpoint) {
+ this(endpoint, new Request(), new Response());
+ }
+
+
+ private AbstractProcessor(AbstractEndpoint<?> endpoint, Request coyoteRequest,
+ Response coyoteResponse) {
this.endpoint = endpoint;
asyncStateMachine = new AsyncStateMachine(this);
- request = new Request();
- response = new Response();
+ request = coyoteRequest;
+ response = coyoteResponse;
response.setHook(this);
request.setResponse(response);
request.setHook(this);
}
-
/**
* Update the current error state to the new error state if the new error
* state is more severe than the current error state.
+ * @param errorState The error status details
+ * @param t The error which occurred
*/
protected void setErrorState(ErrorState errorState, Throwable t) {
boolean blockIo = this.errorState.isIoAllowed() && !errorState.isIoAllowed();
@@ -93,31 +106,16 @@ public abstract class AbstractProcessor<S> implements ActionHook, Processor<S> {
// Set the request attribute so that the async onError() event is
// fired when the error event is processed
request.setAttribute(RequestDispatcher.ERROR_EXCEPTION, t);
- getEndpoint().processSocket(socketWrapper, SocketStatus.ERROR, true);
+ socketWrapper.processSocket(SocketEvent.ERROR, true);
}
}
- protected void resetErrorState() {
- errorState = ErrorState.NONE;
- }
-
-
protected ErrorState getErrorState() {
return errorState;
}
- /**
- * The endpoint receiving connections that are handled by this processor.
- */
- protected AbstractEndpoint<S> getEndpoint() {
- return endpoint;
- }
-
- /**
- * The request associated with this processor.
- */
@Override
public Request getRequest() {
return request;
@@ -146,32 +144,38 @@ public abstract class AbstractProcessor<S> implements ActionHook, Processor<S> {
/**
* Set the socket wrapper being used.
+ * @param socketWrapper The socket wrapper
*/
- protected final void setSocketWrapper(SocketWrapper<S> socketWrapper) {
+ protected final void setSocketWrapper(SocketWrapperBase<?> socketWrapper) {
this.socketWrapper = socketWrapper;
}
/**
- * Get the socket wrapper being used.
+ * @return the socket wrapper being used.
*/
- protected final SocketWrapper<S> getSocketWrapper() {
+ protected final SocketWrapperBase<?> getSocketWrapper() {
return socketWrapper;
}
+ @Override
+ public final void setSslSupport(SSLSupport sslSupport) {
+ this.sslSupport = sslSupport;
+ }
+
+
/**
- * Obtain the Executor used by the underlying endpoint.
+ * @return the Executor used by the underlying endpoint.
*/
- @Override
- public Executor getExecutor() {
+ protected Executor getExecutor() {
return endpoint.getExecutor();
}
@Override
public boolean isAsync() {
- return (asyncStateMachine != null && asyncStateMachine.isAsync());
+ return asyncStateMachine.isAsync();
}
@@ -180,65 +184,570 @@ public abstract class AbstractProcessor<S> implements ActionHook, Processor<S> {
return asyncStateMachine.asyncPostProcess();
}
+
@Override
- public void errorDispatch() {
- getAdapter().errorDispatch(request, response);
+ public final SocketState dispatch(SocketEvent status) {
+
+ if (status == SocketEvent.OPEN_WRITE && response.getWriteListener() != null) {
+ asyncStateMachine.asyncOperation();
+ try {
+ if (flushBufferedWrite()) {
+ return SocketState.LONG;
+ }
+ } catch (IOException ioe) {
+ if (getLog().isDebugEnabled()) {
+ getLog().debug("Unable to write async data.", ioe);
+ }
+ status = SocketEvent.ERROR;
+ request.setAttribute(RequestDispatcher.ERROR_EXCEPTION, ioe);
+ }
+ } else if (status == SocketEvent.OPEN_READ && request.getReadListener() != null) {
+ dispatchNonBlockingRead();
+ } else if (status == SocketEvent.ERROR) {
+ // An I/O error occurred on a non-container thread. This includes:
+ // - read/write timeouts fired by the Poller (NIO & APR)
+ // - completion handler failures in NIO2
+
+ if (request.getAttribute(RequestDispatcher.ERROR_EXCEPTION) == null) {
+ // Because the error did not occur on a container thread the
+ // request's error attribute has not been set. If an exception
+ // is available from the socketWrapper, use it to set the
+ // request's error attribute here so it is visible to the error
+ // handling.
+ request.setAttribute(RequestDispatcher.ERROR_EXCEPTION, socketWrapper.getError());
+ }
+
+ if (request.getReadListener() != null || response.getWriteListener() != null) {
+ // The error occurred during non-blocking I/O. Set the correct
+ // state else the error handling will trigger an ISE.
+ asyncStateMachine.asyncOperation();
+ }
+ }
+
+ RequestInfo rp = request.getRequestProcessor();
+ try {
+ rp.setStage(org.apache.coyote.Constants.STAGE_SERVICE);
+ if (!getAdapter().asyncDispatch(request, response, status)) {
+ setErrorState(ErrorState.CLOSE_NOW, null);
+ }
+ } catch (InterruptedIOException e) {
+ setErrorState(ErrorState.CLOSE_CONNECTION_NOW, e);
+ } catch (Throwable t) {
+ ExceptionUtils.handleThrowable(t);
+ setErrorState(ErrorState.CLOSE_NOW, t);
+ getLog().error(sm.getString("http11processor.request.process"), t);
+ }
+
+ rp.setStage(org.apache.coyote.Constants.STAGE_ENDED);
+
+ if (getErrorState().isError()) {
+ request.updateCounters();
+ return SocketState.CLOSED;
+ } else if (isAsync()) {
+ return SocketState.LONG;
+ } else {
+ request.updateCounters();
+ return dispatchEndRequest();
+ }
}
- @Override
- public abstract boolean isComet();
@Override
- public abstract boolean isUpgrade();
+ public final void action(ActionCode actionCode, Object param) {
+ switch (actionCode) {
+ // 'Normal' servlet support
+ case COMMIT: {
+ if (!response.isCommitted()) {
+ try {
+ // Validate and write response headers
+ prepareResponse();
+ } catch (IOException e) {
+ setErrorState(ErrorState.CLOSE_CONNECTION_NOW, e);
+ }
+ }
+ break;
+ }
+ case CLOSE: {
+ action(ActionCode.COMMIT, null);
+ try {
+ finishResponse();
+ } catch (CloseNowException cne) {
+ setErrorState(ErrorState.CLOSE_NOW, cne);
+ } catch (IOException e) {
+ setErrorState(ErrorState.CLOSE_CONNECTION_NOW, e);
+ }
+ break;
+ }
+ case ACK: {
+ ack();
+ break;
+ }
+ case CLIENT_FLUSH: {
+ action(ActionCode.COMMIT, null);
+ try {
+ flush();
+ } catch (IOException e) {
+ setErrorState(ErrorState.CLOSE_CONNECTION_NOW, e);
+ response.setErrorException(e);
+ }
+ break;
+ }
+ case AVAILABLE: {
+ request.setAvailable(available(Boolean.TRUE.equals(param)));
+ break;
+ }
+ case REQ_SET_BODY_REPLAY: {
+ ByteChunk body = (ByteChunk) param;
+ setRequestBody(body);
+ break;
+ }
+
+ // Error handling
+ case IS_ERROR: {
+ ((AtomicBoolean) param).set(getErrorState().isError());
+ break;
+ }
+ case CLOSE_NOW: {
+ // Prevent further writes to the response
+ setSwallowResponse();
+ if (param instanceof Throwable) {
+ setErrorState(ErrorState.CLOSE_NOW, (Throwable) param);
+ } else {
+ setErrorState(ErrorState.CLOSE_NOW, null);
+ }
+ break;
+ }
+ case DISABLE_SWALLOW_INPUT: {
+ // Aborted upload or similar.
+ // No point reading the remainder of the request.
+ disableSwallowRequest();
+ // This is an error state. Make sure it is marked as such.
+ setErrorState(ErrorState.CLOSE_CLEAN, null);
+ break;
+ }
+
+ // Request attribute support
+ case REQ_HOST_ADDR_ATTRIBUTE: {
+ if (getPopulateRequestAttributesFromSocket() && socketWrapper != null) {
+ request.remoteAddr().setString(socketWrapper.getRemoteAddr());
+ }
+ break;
+ }
+ case REQ_HOST_ATTRIBUTE: {
+ populateRequestAttributeRemoteHost();
+ break;
+ }
+ case REQ_LOCALPORT_ATTRIBUTE: {
+ if (getPopulateRequestAttributesFromSocket() && socketWrapper != null) {
+ request.setLocalPort(socketWrapper.getLocalPort());
+ }
+ break;
+ }
+ case REQ_LOCAL_ADDR_ATTRIBUTE: {
+ if (getPopulateRequestAttributesFromSocket() && socketWrapper != null) {
+ request.localAddr().setString(socketWrapper.getLocalAddr());
+ }
+ break;
+ }
+ case REQ_LOCAL_NAME_ATTRIBUTE: {
+ if (getPopulateRequestAttributesFromSocket() && socketWrapper != null) {
+ request.localName().setString(socketWrapper.getLocalName());
+ }
+ break;
+ }
+ case REQ_REMOTEPORT_ATTRIBUTE: {
+ if (getPopulateRequestAttributesFromSocket() && socketWrapper != null) {
+ request.setRemotePort(socketWrapper.getRemotePort());
+ }
+ break;
+ }
+
+ // SSL request attribute support
+ case REQ_SSL_ATTRIBUTE: {
+ populateSslRequestAttributes();
+ break;
+ }
+ case REQ_SSL_CERTIFICATE: {
+ sslReHandShake();
+ break;
+ }
+
+ // Servlet 3.0 asynchronous support
+ case ASYNC_START: {
+ asyncStateMachine.asyncStart((AsyncContextCallback) param);
+ break;
+ }
+ case ASYNC_COMPLETE: {
+ clearDispatches();
+ if (asyncStateMachine.asyncComplete()) {
+ socketWrapper.processSocket(SocketEvent.OPEN_READ, true);
+ }
+ break;
+ }
+ case ASYNC_DISPATCH: {
+ if (asyncStateMachine.asyncDispatch()) {
+ socketWrapper.processSocket(SocketEvent.OPEN_READ, true);
+ }
+ break;
+ }
+ case ASYNC_DISPATCHED: {
+ asyncStateMachine.asyncDispatched();
+ break;
+ }
+ case ASYNC_ERROR: {
+ asyncStateMachine.asyncError();
+ break;
+ }
+ case ASYNC_IS_ASYNC: {
+ ((AtomicBoolean) param).set(asyncStateMachine.isAsync());
+ break;
+ }
+ case ASYNC_IS_COMPLETING: {
+ ((AtomicBoolean) param).set(asyncStateMachine.isCompleting());
+ break;
+ }
+ case ASYNC_IS_DISPATCHING: {
+ ((AtomicBoolean) param).set(asyncStateMachine.isAsyncDispatching());
+ break;
+ }
+ case ASYNC_IS_ERROR: {
+ ((AtomicBoolean) param).set(asyncStateMachine.isAsyncError());
+ break;
+ }
+ case ASYNC_IS_STARTED: {
+ ((AtomicBoolean) param).set(asyncStateMachine.isAsyncStarted());
+ break;
+ }
+ case ASYNC_IS_TIMINGOUT: {
+ ((AtomicBoolean) param).set(asyncStateMachine.isAsyncTimingOut());
+ break;
+ }
+ case ASYNC_RUN: {
+ asyncStateMachine.asyncRun((Runnable) param);
+ break;
+ }
+ case ASYNC_SETTIMEOUT: {
+ if (param == null) {
+ return;
+ }
+ long timeout = ((Long) param).longValue();
+ setAsyncTimeout(timeout);
+ break;
+ }
+ case ASYNC_TIMEOUT: {
+ AtomicBoolean result = (AtomicBoolean) param;
+ result.set(asyncStateMachine.asyncTimeout());
+ break;
+ }
+ case ASYNC_POST_PROCESS: {
+ asyncStateMachine.asyncPostProcess();
+ break;
+ }
+
+ // Servlet 3.1 non-blocking I/O
+ case REQUEST_BODY_FULLY_READ: {
+ AtomicBoolean result = (AtomicBoolean) param;
+ result.set(isRequestBodyFullyRead());
+ break;
+ }
+ case NB_READ_INTEREST: {
+ if (!isRequestBodyFullyRead()) {
+ registerReadInterest();
+ }
+ break;
+ }
+ case NB_WRITE_INTEREST: {
+ AtomicBoolean isReady = (AtomicBoolean)param;
+ isReady.set(isReady());
+ break;
+ }
+ case DISPATCH_READ: {
+ addDispatch(DispatchType.NON_BLOCKING_READ);
+ break;
+ }
+ case DISPATCH_WRITE: {
+ addDispatch(DispatchType.NON_BLOCKING_WRITE);
+ break;
+ }
+ case DISPATCH_EXECUTE: {
+ SocketWrapperBase<?> wrapper = socketWrapper;
+ if (wrapper != null) {
+ executeDispatches(wrapper);
+ }
+ break;
+ }
+
+ // Servlet 3.1 HTTP Upgrade
+ case UPGRADE: {
+ doHttpUpgrade((UpgradeToken) param);
+ break;
+ }
+
+ // Servlet 4.0 Push requests
+ case IS_PUSH_SUPPORTED: {
+ AtomicBoolean result = (AtomicBoolean) param;
+ result.set(isPushSupported());
+ break;
+ }
+ case PUSH_REQUEST: {
+ doPush((PushToken) param);
+ break;
+ }
+ }
+ }
+
/**
- * Process HTTP requests. All requests are treated as HTTP requests to start
- * with although they may change type during processing.
+ * Perform any necessary processing for a non-blocking read before
+ * dispatching to the adapter.
*/
+ protected void dispatchNonBlockingRead() {
+ asyncStateMachine.asyncOperation();
+ }
+
+
+ @Override
+ public void timeoutAsync(long now) {
+ if (now < 0) {
+ doTimeoutAsync();
+ } else {
+ long asyncTimeout = getAsyncTimeout();
+ if (asyncTimeout > 0) {
+ long asyncStart = asyncStateMachine.getLastAsyncStart();
+ if ((now - asyncStart) > asyncTimeout) {
+ doTimeoutAsync();
+ }
+ }
+ }
+ }
+
+
+ private void doTimeoutAsync() {
+ // Avoid multiple timeouts
+ setAsyncTimeout(-1);
+ socketWrapper.processSocket(SocketEvent.TIMEOUT, true);
+ }
+
+
+ public void setAsyncTimeout(long timeout) {
+ asyncTimeout = timeout;
+ }
+
+
+ public long getAsyncTimeout() {
+ return asyncTimeout;
+ }
+
+
@Override
- public abstract SocketState process(SocketWrapper<S> socket) throws IOException;
+ public void recycle() {
+ errorState = ErrorState.NONE;
+ asyncStateMachine.recycle();
+ }
+
+
+ protected abstract void prepareResponse() throws IOException;
+
+
+ protected abstract void finishResponse() throws IOException;
+
+
+ protected abstract void ack();
+
+
+ protected abstract void flush() throws IOException;
+
+
+ protected abstract int available(boolean doRead);
+
+
+ protected abstract void setRequestBody(ByteChunk body);
+
+
+ protected abstract void setSwallowResponse();
+
+
+ protected abstract void disableSwallowRequest();
+
/**
- * Process in-progress Comet requests. These will start as HTTP requests.
+ * Processors that populate request attributes directly (e.g. AJP) should
+ * over-ride this method and return {@code false}.
+ *
+ * @return {@code true} if the SocketWrapper should be used to populate the
+ * request attributes, otherwise {@code false}.
*/
- @Override
- public abstract SocketState event(SocketStatus status) throws IOException;
+ protected boolean getPopulateRequestAttributesFromSocket() {
+ return true;
+ }
+
/**
- * Process in-progress Servlet 3.0 Async requests. These will start as HTTP
- * requests.
+ * Populate the remote host request attribute. Processors (e.g. AJP) that
+ * populate this from an alternative source should override this method.
*/
- @Override
- public abstract SocketState asyncDispatch(SocketStatus status);
+ protected void populateRequestAttributeRemoteHost() {
+ if (getPopulateRequestAttributesFromSocket() && socketWrapper != null) {
+ request.remoteHost().setString(socketWrapper.getRemoteHost());
+ }
+ }
+
+
+ /**
+ * Populate the TLS related request attributes from the {@link SSLSupport}
+ * instance associated with this processor. Protocols that populate TLS
+ * attributes from a different source (e.g. AJP) should override this
+ * method.
+ */
+ protected void populateSslRequestAttributes() {
+ try {
+ if (sslSupport != null) {
+ Object sslO = sslSupport.getCipherSuite();
+ if (sslO != null) {
+ request.setAttribute(SSLSupport.CIPHER_SUITE_KEY, sslO);
+ }
+ sslO = sslSupport.getPeerCertificateChain();
+ if (sslO != null) {
+ request.setAttribute(SSLSupport.CERTIFICATE_KEY, sslO);
+ }
+ sslO = sslSupport.getKeySize();
+ if (sslO != null) {
+ request.setAttribute (SSLSupport.KEY_SIZE_KEY, sslO);
+ }
+ sslO = sslSupport.getSessionId();
+ if (sslO != null) {
+ request.setAttribute(SSLSupport.SESSION_ID_KEY, sslO);
+ }
+ sslO = sslSupport.getProtocol();
+ if (sslO != null) {
+ request.setAttribute(SSLSupport.PROTOCOL_VERSION_KEY, sslO);
+ }
+ request.setAttribute(SSLSupport.SESSION_MGR, sslSupport);
+ }
+ } catch (Exception e) {
+ getLog().warn(sm.getString("abstractProcessor.socket.ssl"), e);
+ }
+ }
+
+
+ /**
+ * Processors that can perform a TLS re-handshake (e.g. HTTP/1.1) should
+ * override this method and implement the re-handshake.
+ */
+ protected void sslReHandShake() {
+ // NO-OP
+ }
+
+
+ protected abstract boolean isRequestBodyFullyRead();
+
+
+ protected abstract void registerReadInterest();
+
+
+ protected abstract boolean isReady();
+
+
+ protected abstract void executeDispatches(SocketWrapperBase<?> wrapper);
+
/**
- * Processes data received on a connection that has been through an HTTP
- * upgrade.
+ * {@inheritDoc}
+ * Processors that implement HTTP upgrade must override this method and
+ * provide the necessary token.
*/
@Override
- public abstract SocketState upgradeDispatch(SocketStatus status) throws IOException;
+ public UpgradeToken getUpgradeToken() {
+ // Should never reach this code but in case we do...
+ throw new IllegalStateException(
+ sm.getString("abstractProcessor.httpupgrade.notsupported"));
+ }
+
- public int getMaxCookieCount() {
- return maxCookieCount;
+ /**
+ * Process an HTTP upgrade. Processors that support HTTP upgrade should
+ * override this method and process the provided token.
+ *
+ * @param upgradeToken Contains all the information necessary for the
+ * Processor to process the upgrade
+ *
+ * @throws UnsupportedOperationException if the protocol does not support
+ * HTTP upgrade
+ */
+ protected void doHttpUpgrade(UpgradeToken upgradeToken) {
+ // Should never happen
+ throw new UnsupportedOperationException(
+ sm.getString("abstractProcessor.httpupgrade.notsupported"));
}
- public void setMaxCookieCount(int maxCookieCount) {
- this.maxCookieCount = maxCookieCount;
+ /**
+ * {@inheritDoc}
+ * Processors that implement HTTP upgrade must override this method.
+ */
+ @Override
+ public ByteBuffer getLeftoverInput() {
+ // Should never reach this code but in case we do...
+ throw new IllegalStateException(sm.getString("abstractProcessor.httpupgrade.notsupported"));
}
+ /**
+ * {@inheritDoc}
+ * Processors that implement HTTP upgrade must override this method.
+ */
@Override
- public abstract UpgradeToken getUpgradeToken();
+ public boolean isUpgrade() {
+ return false;
+ }
+
+
+ /**
+ * Protocols that support push should override this method and return {@code
+ * true}.
+ *
+ * @return {@code true} if push is supported by this processor, otherwise
+ * {@code false}.
+ */
+ protected boolean isPushSupported() {
+ return false;
+ }
+
+
+ /**
+ * Process a push. Processors that support push should override this method
+ * and process the provided token.
+ *
+ * @param pushToken Contains all the information necessary for the Processor
+ * to process the push request
+ *
+ * @throws UnsupportedOperationException if the protocol does not support
+ * push
+ */
+ protected void doPush(PushToken pushToken) {
+ throw new UnsupportedOperationException(
+ sm.getString("abstractProcessor.pushrequest.notsupported"));
+ }
/**
- * Register the socket for the specified events.
+ * Flush any pending writes. Used during non-blocking writes to flush any
+ * remaining data from a previous incomplete write.
+ *
+ * @return <code>true</code> if data remains to be flushed at the end of
+ * method
*
- * @param read Register the socket for read events
- * @param write Register the socket for write events
+ * @throws IOException If an I/O error occurs while attempting to flush the
+ * data
*/
- protected abstract void registerForEvent(boolean read, boolean write);
+ protected abstract boolean flushBufferedWrite() throws IOException ;
- protected abstract Log getLog();
+ /**
+ * Perform any necessary clean-up processing if the dispatch resulted in the
+ * completion of processing for the current request.
+ *
+ * @return The state to return for the socket once the clean-up for the
+ * current request has completed
+ */
+ protected abstract SocketState dispatchEndRequest();
}
diff --git a/java/org/apache/coyote/AbstractProcessorLight.java b/java/org/apache/coyote/AbstractProcessorLight.java
new file mode 100644
index 0000000..c199dac
--- /dev/null
+++ b/java/org/apache/coyote/AbstractProcessorLight.java
@@ -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.coyote;
+
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.concurrent.CopyOnWriteArraySet;
+
+import org.apache.juli.logging.Log;
+import org.apache.tomcat.util.net.AbstractEndpoint.Handler.SocketState;
+import org.apache.tomcat.util.net.DispatchType;
+import org.apache.tomcat.util.net.SocketEvent;
+import org.apache.tomcat.util.net.SocketWrapperBase;
+
+/**
+ * This is a light-weight abstract processor implementation that is intended as
+ * a basis for all Processor implementations from the light-weight upgrade
+ * processors to the HTTP/AJP processors.
+ */
+public abstract class AbstractProcessorLight implements Processor {
+
+ private Set<DispatchType> dispatches = new CopyOnWriteArraySet<>();
+
+
+ @Override
+ public SocketState process(SocketWrapperBase<?> socketWrapper, SocketEvent status)
+ throws IOException {
+
+ SocketState state = SocketState.CLOSED;
+ Iterator<DispatchType> dispatches = null;
+ do {
+ if (dispatches != null) {
+ DispatchType nextDispatch = dispatches.next();
+ state = dispatch(nextDispatch.getSocketStatus());
+ } else if (status == SocketEvent.DISCONNECT) {
+ // Do nothing here, just wait for it to get recycled
+ } else if (isAsync() || isUpgrade() || state == SocketState.ASYNC_END) {
+ state = dispatch(status);
+ if (state == SocketState.OPEN) {
+ // There may be pipe-lined data to read. If the data isn't
+ // processed now, execution will exit this loop and call
+ // release() which will recycle the processor (and input
+ // buffer) deleting any pipe-lined data. To avoid this,
+ // process it now.
+ state = service(socketWrapper);
+ }
+ } else if (status == SocketEvent.OPEN_WRITE) {
+ // Extra write event likely after async, ignore
+ state = SocketState.LONG;
+ } else {
+ state = service(socketWrapper);
+ }
+
+ if (state != SocketState.CLOSED && isAsync()) {
+ state = asyncPostProcess();
+ }
+
+ if (getLog().isDebugEnabled()) {
+ getLog().debug("Socket: [" + socketWrapper +
+ "], Status in: [" + status +
+ "], State out: [" + state + "]");
+ }
+
+ if (dispatches == null || !dispatches.hasNext()) {
+ // Only returns non-null iterator if there are
+ // dispatches to process.
+ dispatches = getIteratorAndClearDispatches();
+ }
+ } while (state == SocketState.ASYNC_END ||
+ dispatches != null && state != SocketState.CLOSED);
+
+ return state;
+ }
+
+
+ public void addDispatch(DispatchType dispatchType) {
+ synchronized (dispatches) {
+ dispatches.add(dispatchType);
+ }
+ }
+
+
+ public Iterator<DispatchType> getIteratorAndClearDispatches() {
+ // Note: Logic in AbstractProtocol depends on this method only returning
+ // a non-null value if the iterator is non-empty. i.e. it should never
+ // return an empty iterator.
+ Iterator<DispatchType> result;
+ synchronized (dispatches) {
+ // Synchronized as the generation of the iterator and the clearing
+ // of dispatches needs to be an atomic operation.
+ result = dispatches.iterator();
+ if (result.hasNext()) {
+ dispatches.clear();
+ } else {
+ result = null;
+ }
+ }
+ return result;
+ }
+
+
+ protected void clearDispatches() {
+ synchronized (dispatches) {
+ dispatches.clear();
+ }
+ }
+
+
+ /**
+ * Service a 'standard' HTTP request. This method is called for both new
+ * requests and for requests that have partially read the HTTP request line
+ * or HTTP headers. Once the headers have been fully read this method is not
+ * called again until there is a new HTTP request to process. Note that the
+ * request type may change during processing which may result in one or more
+ * calls to {@link #dispatch(SocketEvent)}. Requests may be pipe-lined.
+ *
+ * @param socketWrapper The connection to process
+ *
+ * @return The state the caller should put the socket in when this method
+ * returns
+ *
+ * @throws IOException If an I/O error occurs during the processing of the
+ * request
+ */
+ 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 and HTTP upgrade connections.
+ * Further uses may be added in the future. These will typically start as
+ * HTTP requests.
+ * @param status The event to process
+ * @return the socket state
+ */
+ protected abstract SocketState dispatch(SocketEvent status);
+
+ protected abstract SocketState asyncPostProcess();
+
+ protected abstract Log getLog();
+}
diff --git a/java/org/apache/coyote/AbstractProtocol.java b/java/org/apache/coyote/AbstractProtocol.java
index eb59fe3..b46f7c9 100644
--- a/java/org/apache/coyote/AbstractProtocol.java
+++ b/java/org/apache/coyote/AbstractProtocol.java
@@ -16,11 +16,11 @@
*/
package org.apache.coyote;
-import java.io.IOException;
import java.net.InetAddress;
import java.nio.ByteBuffer;
-import java.util.Iterator;
+import java.util.Collections;
import java.util.Map;
+import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicInteger;
@@ -42,9 +42,8 @@ import org.apache.tomcat.util.collections.SynchronizedStack;
import org.apache.tomcat.util.modeler.Registry;
import org.apache.tomcat.util.net.AbstractEndpoint;
import org.apache.tomcat.util.net.AbstractEndpoint.Handler;
-import org.apache.tomcat.util.net.DispatchType;
-import org.apache.tomcat.util.net.SocketStatus;
-import org.apache.tomcat.util.net.SocketWrapper;
+import org.apache.tomcat.util.net.SocketEvent;
+import org.apache.tomcat.util.net.SocketWrapperBase;
import org.apache.tomcat.util.res.StringManager;
public abstract class AbstractProtocol<S> implements ProtocolHandler,
@@ -53,8 +52,7 @@ public abstract class AbstractProtocol<S> implements ProtocolHandler,
/**
* The string manager for this package.
*/
- protected static final StringManager sm =
- StringManager.getManager(Constants.Package);
+ private static final StringManager sm = StringManager.getManager(AbstractProtocol.class);
/**
@@ -86,17 +84,30 @@ public abstract class AbstractProtocol<S> implements ProtocolHandler,
/**
* Endpoint that provides low-level network I/O - must be matched to the
- * ProtocolHandler implementation (ProtocolHandler using BIO, requires BIO
+ * ProtocolHandler implementation (ProtocolHandler using NIO, requires NIO
* Endpoint etc.).
*/
- protected AbstractEndpoint<S> endpoint = null;
+ private final AbstractEndpoint<S> endpoint;
+
+
+ private Handler<S> handler;
+
+
+ private final Set<Processor> waitingProcessors =
+ Collections.newSetFromMap(new ConcurrentHashMap<Processor, Boolean>());
/**
- * The maximum number of cookies permitted for a request. Use a value less
- * than zero for no limit. Defaults to 200.
+ * The async timeout thread.
*/
- private int maxCookieCount = 200;
+ private AsyncTimeout asyncTimeout = null;
+
+
+ public AbstractProtocol(AbstractEndpoint<S> endpoint) {
+ this.endpoint = endpoint;
+ setSoLinger(Constants.DEFAULT_CONNECTION_LINGER);
+ setTcpNoDelay(Constants.DEFAULT_TCP_NO_DELAY);
+ }
// ----------------------------------------------- Generic property handling
@@ -107,6 +118,12 @@ public abstract class AbstractProtocol<S> implements ProtocolHandler,
* more specific setter. That means the property belongs to the Endpoint,
* the ServerSocketFactory or some other lower level component. This method
* ensures that it is visible to both.
+ *
+ * @param name The name of the property to set
+ * @param value The value, in string form, to set for the property
+ *
+ * @return <code>true</code> if the property was set successfully, otherwise
+ * <code>false</code>
*/
public boolean setProperty(String name, String value) {
return endpoint.setProperty(name, value);
@@ -116,6 +133,10 @@ public abstract class AbstractProtocol<S> implements ProtocolHandler,
/**
* Generic property getter used by the digester. Other code should not need
* to use this.
+ *
+ * @param name The name of the property to get
+ *
+ * @return The value of the property converted to a string
*/
public String getProperty(String name) {
return endpoint.getProperty(name);
@@ -170,30 +191,13 @@ public abstract class AbstractProtocol<S> implements ProtocolHandler,
@Override
- public boolean isCometSupported() {
- return endpoint.getUseComet();
- }
-
-
- @Override
- public boolean isCometTimeoutSupported() {
- return endpoint.getUseCometTimeout();
- }
-
-
- @Override
public boolean isSendfileSupported() {
return endpoint.getUseSendfile();
}
- public int getMaxCookieCount() {
- return maxCookieCount;
- }
-
-
- public void setMaxCookieCount(int maxCookieCount) {
- this.maxCookieCount = maxCookieCount;
+ public AsyncTimeout getAsyncTimeout() {
+ return asyncTimeout;
}
@@ -296,6 +300,20 @@ public abstract class AbstractProtocol<S> implements ProtocolHandler,
return endpoint.getConnectionCount();
}
+ public void setAcceptorThreadCount(int threadCount) {
+ endpoint.setAcceptorThreadCount(threadCount);
+ }
+ public int getAcceptorThreadCount() {
+ return endpoint.getAcceptorThreadCount();
+ }
+
+ public void setAcceptorThreadPriority(int threadPriority) {
+ endpoint.setAcceptorThreadPriority(threadPriority);
+ }
+ public int getAcceptorThreadPriority() {
+ return endpoint.getAcceptorThreadPriority();
+ }
+
// ---------------------------------------------------------- Public methods
@@ -310,10 +328,17 @@ public abstract class AbstractProtocol<S> implements ProtocolHandler,
/**
* The name will be prefix-address-port if address is non-null and
- * prefix-port if the address is null. The name will be appropriately quoted
- * so it can be used directly in an ObjectName.
+ * prefix-port if the address is null.
+ *
+ * @return A name for this protocol instance that is appropriately quoted
+ * for use in an ObjectName.
*/
public String getName() {
+ return ObjectName.quote(getNameInternal());
+ }
+
+
+ private String getNameInternal() {
StringBuilder name = new StringBuilder(getNamePrefix());
name.append('-');
if (getAddress() != null) {
@@ -333,7 +358,33 @@ public abstract class AbstractProtocol<S> implements ProtocolHandler,
} else {
name.append(port);
}
- return ObjectName.quote(name.toString());
+ return name.toString();
+ }
+
+
+ public void addWaitingProcessor(Processor processor) {
+ waitingProcessors.add(processor);
+ }
+
+
+ public void removeWaitingProcessor(Processor processor) {
+ waitingProcessors.remove(processor);
+ }
+
+
+ // ----------------------------------------------- Accessors for sub-classes
+
+ protected AbstractEndpoint<S> getEndpoint() {
+ return endpoint;
+ }
+
+
+ protected Handler<S> getHandler() {
+ return handler;
+ }
+
+ protected void setHandler(Handler<S> handler) {
+ this.handler = handler;
}
@@ -342,6 +393,7 @@ public abstract class AbstractProtocol<S> implements ProtocolHandler,
/**
* Concrete implementations need to provide access to their logger to be
* used by the abstract classes.
+ * @return the logger
*/
protected abstract Log getLog();
@@ -349,20 +401,50 @@ public abstract class AbstractProtocol<S> implements ProtocolHandler,
/**
* Obtain the prefix to be used when construction a name for this protocol
* handler. The name will be prefix-address-port.
+ * @return the prefix
*/
protected abstract String getNamePrefix();
/**
* Obtain the name of the protocol, (Http, Ajp, etc.). Used with JMX.
+ * @return the protocol name
*/
protected abstract String getProtocolName();
/**
- * Obtain the handler associated with the underlying Endpoint
+ * Find a suitable handler for the protocol negotiated
+ * at the network layer.
+ * @param name The name of the requested negotiated protocol.
+ * @return The instance where {@link UpgradeProtocol#getAlpnName()} matches
+ * the requested protocol
+ */
+ protected abstract UpgradeProtocol getNegotiatedProtocol(String name);
+
+
+ /**
+ * Find a suitable handler for the protocol upgraded name specified. This
+ * is used for direct connection protocol selection.
+ * @param name The name of the requested negotiated protocol.
+ * @return The instance where {@link UpgradeProtocol#getAlpnName()} matches
+ * the requested protocol
+ */
+ protected abstract UpgradeProtocol getUpgradeProtocol(String name);
+
+
+ /**
+ * Create and configure a new Processor instance for the current protocol
+ * implementation.
+ *
+ * @return A fully configured Processor instance that is ready to use
*/
- protected abstract Handler getHandler();
+ protected abstract Processor createProcessor();
+
+
+ protected abstract Processor createUpgradeProcessor(
+ SocketWrapperBase<?> socket,
+ UpgradeToken upgradeToken);
// ----------------------------------------------------- JMX related methods
@@ -486,14 +568,25 @@ public abstract class AbstractProtocol<S> implements ProtocolHandler,
public void start() throws Exception {
if (getLog().isInfoEnabled())
getLog().info(sm.getString("abstractProtocolHandler.start",
- getName()));
+ getNameInternal()));
try {
endpoint.start();
} catch (Exception ex) {
getLog().error(sm.getString("abstractProtocolHandler.startError",
- getName()), ex);
+ getNameInternal()), ex);
throw ex;
}
+
+ // Start async timeout thread
+ asyncTimeout = new AsyncTimeout();
+ Thread timeoutThread = new Thread(asyncTimeout, getNameInternal() + "-AsyncTimeout");
+ int priority = endpoint.getThreadPriority();
+ if (priority < Thread.MIN_PRIORITY || priority > Thread.MAX_PRIORITY) {
+ priority = Thread.NORM_PRIORITY;
+ }
+ timeoutThread.setPriority(priority);
+ timeoutThread.setDaemon(true);
+ timeoutThread.start();
}
@@ -531,6 +624,11 @@ public abstract class AbstractProtocol<S> implements ProtocolHandler,
if(getLog().isInfoEnabled())
getLog().info(sm.getString("abstractProtocolHandler.stop",
getName()));
+
+ if (asyncTimeout != null) {
+ asyncTimeout.stop();
+ }
+
try {
endpoint.stop();
} catch (Exception ex) {
@@ -579,22 +677,25 @@ public abstract class AbstractProtocol<S> implements ProtocolHandler,
// ------------------------------------------- Connection handler base class
- protected abstract static class AbstractConnectionHandler<S,P extends Processor<S>>
- implements AbstractEndpoint.Handler {
+ protected static class ConnectionHandler<S> implements AbstractEndpoint.Handler<S> {
- protected abstract Log getLog();
-
- protected final RequestGroupInfo global = new RequestGroupInfo();
- protected final AtomicLong registerCount = new AtomicLong(0);
-
- protected final Map<S,Processor<S>> connections = new ConcurrentHashMap<>();
-
- protected final RecycledProcessors<P,S> recycledProcessors =
- new RecycledProcessors<>(this);
+ private final AbstractProtocol<S> proto;
+ private final RequestGroupInfo global = new RequestGroupInfo();
+ private final AtomicLong registerCount = new AtomicLong(0);
+ private final Map<S,Processor> connections = new ConcurrentHashMap<>();
+ private final RecycledProcessors recycledProcessors = new RecycledProcessors(this);
+ public ConnectionHandler(AbstractProtocol<S> proto) {
+ this.proto = proto;
+ }
- protected abstract AbstractProtocol<S> getProtocol();
+ protected AbstractProtocol<S> getProtocol() {
+ return proto;
+ }
+ protected Log getLog() {
+ return getProtocol().getLog();
+ }
@Override
public Object getGlobal() {
@@ -607,157 +708,166 @@ public abstract class AbstractProtocol<S> implements ProtocolHandler,
}
- public SocketState process(SocketWrapper<S> wrapper,
- SocketStatus status) {
+ @Override
+ public SocketState process(SocketWrapperBase<S> wrapper, SocketEvent status) {
+ if (getLog().isDebugEnabled()) {
+ getLog().debug(sm.getString("abstractConnectionHandler.process",
+ wrapper.getSocket(), status));
+ }
if (wrapper == null) {
// Nothing to do. Socket has been closed.
return SocketState.CLOSED;
}
S socket = wrapper.getSocket();
- if (socket == null) {
- // Nothing to do. Socket has been closed.
- return SocketState.CLOSED;
- }
- Processor<S> processor = connections.get(socket);
- if (status == SocketStatus.DISCONNECT && processor == null) {
+ Processor processor = connections.get(socket);
+ if ((status == SocketEvent.DISCONNECT || status == SocketEvent.ERROR)
+ && processor == null) {
// Nothing to do. Endpoint requested a close and there is no
// longer a processor associated with this socket.
return SocketState.CLOSED;
}
- wrapper.setAsync(false);
ContainerThreadMarker.set();
try {
if (processor == null) {
+ String negotiatedProtocol = wrapper.getNegotiatedProtocol();
+ if (negotiatedProtocol != null) {
+ UpgradeProtocol upgradeProtocol =
+ getProtocol().getNegotiatedProtocol(negotiatedProtocol);
+ if (upgradeProtocol != null) {
+ processor = upgradeProtocol.getProcessor(
+ wrapper, getProtocol().getAdapter());
+ } else if (negotiatedProtocol.equals("http/1.1")) {
+ // Explicitly negotiated the default protocol.
+ // Obtain a processor below.
+ } else {
+ // TODO:
+ // OpenSSL 1.0.2's ALPN callback doesn't support
+ // failing the handshake with an error if no
+ // protocol can be negotiated. Therefore, we need to
+ // fail the connection here. Once this is fixed,
+ // replace the code below with the commented out
+ // block.
+ if (getLog().isDebugEnabled()) {
+ getLog().debug(sm.getString(
+ "abstractConnectionHandler.negotiatedProcessor.fail",
+ negotiatedProtocol));
+ }
+ return SocketState.CLOSED;
+ /*
+ * To replace the code above once OpenSSL 1.1.0 is
+ * used.
+ // Failed to create processor. This is a bug.
+ throw new IllegalStateException(sm.getString(
+ "abstractConnectionHandler.negotiatedProcessor.fail",
+ negotiatedProtocol));
+ */
+ }
+ }
+ }
+ if (processor == null) {
processor = recycledProcessors.pop();
}
if (processor == null) {
- processor = createProcessor();
+ processor = getProtocol().createProcessor();
+ register(processor);
}
- initSsl(wrapper, processor);
+ processor.setSslSupport(
+ wrapper.getSslSupport(getProtocol().getClientCertProvider()));
+
+ // Associate the processor with the connection
+ connections.put(socket, processor);
+ // Make sure an async timeout doesn't fire
+ getProtocol().removeWaitingProcessor(processor);
SocketState state = SocketState.CLOSED;
- Iterator<DispatchType> dispatches = null;
do {
- if (dispatches != null) {
- // Associate the processor with the connection as
- // these calls may result in a nested call to process()
- connections.put(socket, processor);
- DispatchType nextDispatch = dispatches.next();
- if (processor.isUpgrade()) {
- state = processor.upgradeDispatch(
- nextDispatch.getSocketStatus());
- } else {
- state = processor.asyncDispatch(
- nextDispatch.getSocketStatus());
- }
- } else if (processor.isComet()) {
- state = processor.event(status);
- } else if (processor.isUpgrade()) {
- state = processor.upgradeDispatch(status);
- } else if (status == SocketStatus.DISCONNECT) {
- // Comet and upgrade need to see DISCONNECT but the
- // others don't. NO-OP and let socket close.
- } else if (processor.isAsync() || state == SocketState.ASYNC_END) {
- state = processor.asyncDispatch(status);
- if (state == SocketState.OPEN) {
- // release() won't get called so in case this request
- // takes a long time to process, remove the socket from
- // the waiting requests now else the async timeout will
- // fire
- getProtocol().endpoint.removeWaitingRequest(wrapper);
- // There may be pipe-lined data to read. If the data
- // isn't processed now, execution will exit this
- // loop and call release() which will recycle the
- // processor (and input buffer) deleting any
- // pipe-lined data. To avoid this, process it now.
- state = processor.process(wrapper);
- }
- } else if (status == SocketStatus.OPEN_WRITE) {
- // Extra write event likely after async, ignore
- state = SocketState.LONG;
- } else {
- state = processor.process(wrapper);
- }
-
- if (state != SocketState.CLOSED && processor.isAsync()) {
- state = processor.asyncPostProcess();
- }
+ state = processor.process(wrapper, status);
if (state == SocketState.UPGRADING) {
// Get the HTTP upgrade handler
UpgradeToken upgradeToken = processor.getUpgradeToken();
- HttpUpgradeHandler httpUpgradeHandler = upgradeToken.getHttpUpgradeHandler();
// Retrieve leftover input
- ByteBuffer leftoverInput = processor.getLeftoverInput();
- // Release the Http11 processor to be re-used
- release(wrapper, processor, false, false);
- // Create the upgrade processor
- processor = createUpgradeProcessor(
- wrapper, leftoverInput, upgradeToken);
- // Mark the connection as upgraded
- wrapper.setUpgraded(true);
- // Associate with the processor with the connection
- connections.put(socket, processor);
- // Initialise the upgrade handler (which may trigger
- // some IO using the new protocol which is why the lines
- // above are necessary)
- // This cast should be safe. If it fails the error
- // handling for the surrounding try/catch will deal with
- // it.
- if (upgradeToken.getInstanceManager() == null) {
- httpUpgradeHandler.init((WebConnection) processor);
+ ByteBuffer leftOverInput = processor.getLeftoverInput();
+ if (upgradeToken == null) {
+ // Assume direct HTTP/2 connection
+ UpgradeProtocol upgradeProtocol = getProtocol().getUpgradeProtocol("h2c");
+ if (upgradeProtocol != null) {
+ processor = upgradeProtocol.getProcessor(
+ wrapper, getProtocol().getAdapter());
+ wrapper.unRead(leftOverInput);
+ // Associate with the processor with the connection
+ connections.put(socket, processor);
+ } else {
+ if (getLog().isDebugEnabled()) {
+ getLog().debug(sm.getString(
+ "abstractConnectionHandler.negotiatedProcessor.fail",
+ "h2c"));
+ }
+ return SocketState.CLOSED;
+ }
} else {
- ClassLoader oldCL = upgradeToken.getContextBind().bind(false, null);
- try {
+ HttpUpgradeHandler httpUpgradeHandler = upgradeToken.getHttpUpgradeHandler();
+ // Release the Http11 processor to be re-used
+ release(processor);
+ // Create the upgrade processor
+ processor = getProtocol().createUpgradeProcessor(wrapper, upgradeToken);
+ wrapper.unRead(leftOverInput);
+ // Mark the connection as upgraded
+ wrapper.setUpgraded(true);
+ // Associate with the processor with the connection
+ connections.put(socket, processor);
+ // Initialise the upgrade handler (which may trigger
+ // some IO using the new protocol which is why the lines
+ // above are necessary)
+ // This cast should be safe. If it fails the error
+ // handling for the surrounding try/catch will deal with
+ // it.
+ if (upgradeToken.getInstanceManager() == null) {
httpUpgradeHandler.init((WebConnection) processor);
- } finally {
- upgradeToken.getContextBind().unbind(false, oldCL);
+ } else {
+ ClassLoader oldCL = upgradeToken.getContextBind().bind(false, null);
+ try {
+ httpUpgradeHandler.init((WebConnection) processor);
+ } finally {
+ upgradeToken.getContextBind().unbind(false, oldCL);
+ }
}
}
}
- if (getLog().isDebugEnabled()) {
- getLog().debug("Socket: [" + wrapper +
- "], Status in: [" + status +
- "], State out: [" + state + "]");
- }
- if (dispatches == null || !dispatches.hasNext()) {
- // Only returns non-null iterator if there are
- // dispatches to process.
- dispatches = wrapper.getIteratorAndClearDispatches();
- }
- } while (state == SocketState.ASYNC_END ||
- state == SocketState.UPGRADING ||
- dispatches != null && state != SocketState.CLOSED);
+ } while ( state == SocketState.UPGRADING);
if (state == SocketState.LONG) {
// In the middle of processing a request/response. Keep the
// socket associated with the processor. Exact requirements
// depend on type of long poll
- connections.put(socket, processor);
longPoll(wrapper, processor);
+ if (processor.isAsync()) {
+ getProtocol().addWaitingProcessor(processor);
+ }
} else if (state == SocketState.OPEN) {
// In keep-alive but between requests. OK to recycle
// processor. Continue to poll for the next request.
connections.remove(socket);
- release(wrapper, processor, false, true);
+ release(processor);
+ wrapper.registerReadInterest();
} else if (state == SocketState.SENDFILE) {
// Sendfile in progress. If it fails, the socket will be
// closed. If it works, the socket will be re-added to the
// poller
connections.remove(socket);
- release(wrapper, processor, false, false);
+ release(processor);
} else if (state == SocketState.UPGRADED) {
// Don't add sockets back to the poller if this was a
// non-blocking write otherwise the poller may trigger
// multiple read events which may lead to thread starvation
// in the connector. The write() method will add this socket
// to the poller if necessary.
- if (status != SocketStatus.OPEN_WRITE) {
+ if (status != SocketEvent.OPEN_WRITE) {
longPoll(wrapper, processor);
}
} else {
@@ -785,7 +895,7 @@ public abstract class AbstractProtocol<S> implements ProtocolHandler,
}
}
} else {
- release(wrapper, processor, true, false);
+ release(processor);
}
}
return state;
@@ -797,6 +907,11 @@ public abstract class AbstractProtocol<S> implements ProtocolHandler,
// IOExceptions are normal
getLog().debug(sm.getString(
"abstractConnectionHandler.ioexception.debug"), e);
+ } catch (ProtocolException e) {
+ // Protocol exceptions normally mean the client sent invalid or
+ // incomplete data.
+ 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
@@ -806,8 +921,7 @@ public abstract class AbstractProtocol<S> implements ProtocolHandler,
// 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"), e);
} finally {
ContainerThreadMarker.clear();
}
@@ -815,26 +929,64 @@ public abstract class AbstractProtocol<S> implements ProtocolHandler,
// Make sure socket/processor is removed from the list of current
// connections
connections.remove(socket);
- // Don't try to add upgrade processors back into the pool
- if (processor !=null && !processor.isUpgrade()) {
- release(wrapper, processor, true, false);
- }
+ release(processor);
return SocketState.CLOSED;
}
- protected abstract P createProcessor();
- protected abstract void initSsl(SocketWrapper<S> socket,
- Processor<S> processor);
- protected abstract void longPoll(SocketWrapper<S> socket,
- Processor<S> processor);
- protected abstract void release(SocketWrapper<S> socket,
- Processor<S> processor, boolean socketClosing,
- boolean addToPoller);
- protected abstract Processor<S> createUpgradeProcessor(
- SocketWrapper<S> socket, ByteBuffer leftoverInput,
- UpgradeToken upgradeToken) throws IOException;
- protected void register(AbstractProcessor<S> processor) {
+ protected void longPoll(SocketWrapperBase<?> socket, Processor processor) {
+ if (!processor.isAsync()) {
+ // This is currently only used with HTTP
+ // Either:
+ // - this is an upgraded connection
+ // - the request line/headers have not been completely
+ // read
+ socket.registerReadInterest();
+ }
+ }
+
+
+ @Override
+ public Set<S> getOpenSockets() {
+ return connections.keySet();
+ }
+
+
+ /**
+ * Expected to be used by the handler once the processor is no longer
+ * required.
+ *
+ * @param processor Processor being released (that was associated with
+ * the socket)
+ */
+ private void release(Processor processor) {
+ if (processor != null) {
+ processor.recycle();
+ // After recycling, only instances of UpgradeProcessorBase will
+ // return true for isUpgrade().
+ // Instances of UpgradeProcessorBase should not be added to
+ // recycledProcessors since that pool is only for AJP or HTTP
+ // processors
+ if (!processor.isUpgrade()) {
+ recycledProcessors.push(processor);
+ }
+ }
+ }
+
+
+ /**
+ * Expected to be used by the Endpoint to release resources on socket
+ * close, errors etc.
+ */
+ @Override
+ public void release(SocketWrapperBase<S> socketWrapper) {
+ S socket = socketWrapper.getSocket();
+ Processor processor = connections.remove(socket);
+ release(processor);
+ }
+
+
+ protected void register(Processor processor) {
if (getProtocol().getDomain() != null) {
synchronized (this) {
try {
@@ -861,7 +1013,7 @@ public abstract class AbstractProtocol<S> implements ProtocolHandler,
}
}
- protected void unregister(Processor<S> processor) {
+ protected void unregister(Processor processor) {
if (getProtocol().getDomain() != null) {
synchronized (this) {
try {
@@ -885,21 +1037,36 @@ public abstract class AbstractProtocol<S> implements ProtocolHandler,
}
}
}
+
+ @Override
+ public final void pause() {
+ /*
+ * Inform all the processors associated with current connections
+ * that the endpoint is being paused. Most won't care. Those
+ * processing multiplexed streams may wish to take action. For
+ * example, HTTP/2 may wish to stop accepting new streams.
+ *
+ * Note that even if the endpoint is resumed, there is (currently)
+ * no API to inform the Processors of this.
+ */
+ for (Processor processor : connections.values()) {
+ processor.pause();
+ }
+ }
}
- protected static class RecycledProcessors<P extends Processor<S>, S>
- extends SynchronizedStack<Processor<S>> {
+ protected static class RecycledProcessors extends SynchronizedStack<Processor> {
- private final transient AbstractConnectionHandler<S,P> handler;
+ private final transient ConnectionHandler<?> handler;
protected final AtomicInteger size = new AtomicInteger(0);
- public RecycledProcessors(AbstractConnectionHandler<S,P> handler) {
+ public RecycledProcessors(ConnectionHandler<?> handler) {
this.handler = handler;
}
@SuppressWarnings("sync-override") // Size may exceed cache size a bit
@Override
- public boolean push(Processor<S> processor) {
+ public boolean push(Processor processor) {
int cacheSize = handler.getProtocol().getProcessorCache();
boolean offer = cacheSize == -1 ? true : size.get() < cacheSize;
//avoid over growing our cache or add after we have stopped
@@ -916,8 +1083,8 @@ public abstract class AbstractProtocol<S> implements ProtocolHandler,
@SuppressWarnings("sync-override") // OK if size is too big briefly
@Override
- public Processor<S> pop() {
- Processor<S> result = super.pop();
+ public Processor pop() {
+ Processor result = super.pop();
if (result != null) {
size.decrementAndGet();
}
@@ -926,7 +1093,7 @@ public abstract class AbstractProtocol<S> implements ProtocolHandler,
@Override
public synchronized void clear() {
- Processor<S> next = pop();
+ Processor next = pop();
while (next != null) {
handler.unregister(next);
next = pop();
@@ -935,4 +1102,53 @@ public abstract class AbstractProtocol<S> implements ProtocolHandler,
size.set(0);
}
}
+
+
+ /**
+ * Async timeout thread
+ */
+ protected class AsyncTimeout implements Runnable {
+
+ private volatile boolean asyncTimeoutRunning = true;
+
+ /**
+ * The background thread that checks async requests and fires the
+ * timeout if there has been no activity.
+ */
+ @Override
+ public void run() {
+
+ // Loop until we receive a shutdown command
+ while (asyncTimeoutRunning) {
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ // Ignore
+ }
+ long now = System.currentTimeMillis();
+ for (Processor processor : waitingProcessors) {
+ processor.timeoutAsync(now);
+ }
+
+ // Loop if endpoint is paused
+ while (endpoint.isPaused() && asyncTimeoutRunning) {
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ // Ignore
+ }
+ }
+ }
+ }
+
+
+ protected void stop() {
+ asyncTimeoutRunning = false;
+
+ // Timeout any pending async request
+ for (Processor processor : waitingProcessors) {
+ processor.timeoutAsync(-1);
+ }
+ }
+ }
}
diff --git a/java/org/apache/coyote/ActionCode.java b/java/org/apache/coyote/ActionCode.java
index 3da5f6d..00f87c4 100644
--- a/java/org/apache/coyote/ActionCode.java
+++ b/java/org/apache/coyote/ActionCode.java
@@ -47,8 +47,6 @@ public enum ActionCode {
*/
CLIENT_FLUSH,
- RESET,
-
/**
* Has the processor been placed into the error state? Note that the
* response may not have an appropriate error code set.
@@ -63,24 +61,24 @@ public enum ActionCode {
DISABLE_SWALLOW_INPUT,
/**
- * Callback for lazy evaluation - extract the remote host address.
+ * Callback for lazy evaluation - extract the remote host name and address.
*/
REQ_HOST_ATTRIBUTE,
/**
- * Callback for lazy evaluation - extract the remote host infos (address,
- * name, port) and local address.
+ * Callback for lazy evaluation - extract the remote host address.
*/
REQ_HOST_ADDR_ATTRIBUTE,
/**
- * Callback for lazy evaluation - extract the SSL-related attributes.
+ * Callback for lazy evaluation - extract the SSL-related attributes
+ * including the client certificate if present.
*/
REQ_SSL_ATTRIBUTE,
/**
- * Callback for lazy evaluation - extract the SSL-certificate (including
- * forcing a re-handshake if necessary)
+ * Force a TLS re-handshake and make the resulting client certificate (if
+ * any) available as a request attribute.
*/
REQ_SSL_CERTIFICATE,
@@ -110,36 +108,11 @@ public enum ActionCode {
REQ_SET_BODY_REPLAY,
/**
- * Callback for begin Comet processing.
- */
- COMET_BEGIN,
-
- /**
- * Callback for end Comet processing.
- */
- COMET_END,
-
- /**
* Callback for getting the amount of available bytes.
*/
AVAILABLE,
/**
- * Callback for an asynchronous close of the Comet event
- */
- COMET_CLOSE,
-
- /**
- * Callback for setting the timeout asynchronously
- */
- COMET_SETTIMEOUT,
-
- /**
- * Callback to determine if the current request is a Comet request.
- */
- IS_COMET,
-
- /**
* Callback for an async request.
*/
ASYNC_START,
@@ -227,13 +200,15 @@ public enum ActionCode {
/**
* Indicator that Servlet is interested in being
- * notified when data is available to be read
+ * notified when data is available to be read.
*/
NB_READ_INTEREST,
/**
- *Indicator that the Servlet is interested
- *in being notified when it can write data
+ * Used with non-blocking writes to determine if a write is currently
+ * allowed (sets passed parameter to <code>true</code>) or not (sets passed
+ * parameter to <code>false</code>). If a write is not allowed then callback
+ * will be triggered at some future point when write becomes possible again.
*/
NB_WRITE_INTEREST,
@@ -263,8 +238,12 @@ public enum ActionCode {
DISPATCH_EXECUTE,
/**
- * Trigger end of request processing (remaining input swallowed, write any
- * remaining parts of the response etc.).
+ * Is server push supported and allowed for the current request?
+ */
+ IS_PUSH_SUPPORTED,
+
+ /**
+ * Push a request on behalf of the client of the current request.
*/
- END_REQUEST
+ PUSH_REQUEST
}
diff --git a/java/org/apache/coyote/ActionHook.java b/java/org/apache/coyote/ActionHook.java
index 0fac047..c9e0101 100644
--- a/java/org/apache/coyote/ActionHook.java
+++ b/java/org/apache/coyote/ActionHook.java
@@ -35,7 +35,6 @@ package org.apache.coyote;
*/
public interface ActionHook {
-
/**
* Send an action to the connector.
*
@@ -43,6 +42,4 @@ public interface ActionHook {
* @param param Action parameter
*/
public void action(ActionCode actionCode, Object param);
-
-
}
diff --git a/java/org/apache/coyote/Adapter.java b/java/org/apache/coyote/Adapter.java
index 0375593..c456a0b 100644
--- a/java/org/apache/coyote/Adapter.java
+++ b/java/org/apache/coyote/Adapter.java
@@ -16,7 +16,7 @@
*/
package org.apache.coyote;
-import org.apache.tomcat.util.net.SocketStatus;
+import org.apache.tomcat.util.net.SocketEvent;
/**
* Adapter. This represents the entry point in a coyote-based servlet container.
@@ -63,14 +63,9 @@ public interface Adapter {
*/
public boolean prepare(Request req, Response res) throws Exception;
- public boolean event(Request req, Response res, SocketStatus status)
+ public boolean asyncDispatch(Request req,Response res, SocketEvent status)
throws Exception;
- public boolean asyncDispatch(Request req,Response res, SocketStatus status)
- throws Exception;
-
- public void errorDispatch(Request request, Response response);
-
public void log(Request req, Response res, long time);
/**
diff --git a/java/org/apache/coyote/AsyncContextCallback.java b/java/org/apache/coyote/AsyncContextCallback.java
index 2278414..1684792 100644
--- a/java/org/apache/coyote/AsyncContextCallback.java
+++ b/java/org/apache/coyote/AsyncContextCallback.java
@@ -21,7 +21,7 @@ package org.apache.coyote;
* {@link javax.servlet.AsyncContext} implementation that an action, such as
* firing event listeners needs to be taken. It is implemented in this manner
* so that the org.apache.coyote package does not have a dependency on the
- * org.apache.coyote package.
+ * org.apache.catalina package.
*/
public interface AsyncContextCallback {
public void fireOnComplete();
diff --git a/java/org/apache/coyote/AsyncStateMachine.java b/java/org/apache/coyote/AsyncStateMachine.java
index ad2db0c..e503427 100644
--- a/java/org/apache/coyote/AsyncStateMachine.java
+++ b/java/org/apache/coyote/AsyncStateMachine.java
@@ -128,8 +128,7 @@ public class AsyncStateMachine {
/**
* The string manager for this package.
*/
- private static final StringManager sm =
- StringManager.getManager(Constants.Package);
+ private static final StringManager sm = StringManager.getManager(AsyncStateMachine.class);
private static enum AsyncState {
DISPATCHED (false, false, false, false),
@@ -177,12 +176,13 @@ public class AsyncStateMachine {
private volatile AsyncState state = AsyncState.DISPATCHED;
+ private volatile long lastAsyncStart = 0;
// Need this to fire listener on complete
private AsyncContextCallback asyncCtxt = null;
- private final Processor<?> processor;
+ private final AbstractProcessor processor;
- public AsyncStateMachine(Processor<?> processor) {
+ public AsyncStateMachine(AbstractProcessor processor) {
this.processor = processor;
}
@@ -211,10 +211,22 @@ public class AsyncStateMachine {
return state.isCompleting();
}
+ /**
+ * Obtain the time that this connection last transitioned to async
+ * processing.
+ *
+ * @return The time (as returned by {@link System#currentTimeMillis()}) that
+ * this connection last transitioned to async
+ */
+ public long getLastAsyncStart() {
+ return lastAsyncStart;
+ }
+
public synchronized void asyncStart(AsyncContextCallback asyncCtxt) {
if (state == AsyncState.DISPATCHED) {
state = AsyncState.STARTING;
this.asyncCtxt = asyncCtxt;
+ lastAsyncStart = System.currentTimeMillis();
} else {
throw new IllegalStateException(
sm.getString("asyncStateMachine.invalidAsyncState",
@@ -430,11 +442,18 @@ public class AsyncStateMachine {
public synchronized void recycle() {
+ // Use lastAsyncStart to determine if this instance has been used since
+ // it was last recycled. If it hasn't there is no need to recycle again
+ // which saves the relatively expensive call to notifyAll()
+ if (lastAsyncStart == 0) {
+ return;
+ }
// Ensure in case of error that any non-container threads that have been
// paused are unpaused.
notifyAll();
asyncCtxt = null;
state = AsyncState.DISPATCHED;
+ lastAsyncStart = 0;
}
diff --git a/java/org/apache/coyote/ByteBufferHolder.java b/java/org/apache/coyote/ByteBufferHolder.java
deleted file mode 100644
index e7d23ae..0000000
--- a/java/org/apache/coyote/ByteBufferHolder.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.coyote;
-
-import java.nio.ByteBuffer;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-/**
- * Simple wrapper for a {@link ByteBuffer} that remembers if the buffer has been
- * flipped or not.
- */
-public class ByteBufferHolder {
-
- private final ByteBuffer buf;
- private final AtomicBoolean flipped;
-
- public ByteBufferHolder(ByteBuffer buf, boolean flipped) {
- this.buf = buf;
- this.flipped = new AtomicBoolean(flipped);
- }
-
-
- public ByteBuffer getBuf() {
- return buf;
- }
-
-
- public boolean isFlipped() {
- return flipped.get();
- }
-
-
- public boolean flip() {
- if (flipped.compareAndSet(false, true)) {
- buf.flip();
- return true;
- } else {
- return false;
- }
- }
-
-
- public boolean hasData() {
- if (flipped.get()) {
- return buf.remaining()>0;
- } else {
- return buf.position()>0;
- }
- }
-}
\ No newline at end of file
diff --git a/java/org/apache/coyote/CloseNowException.java b/java/org/apache/coyote/CloseNowException.java
new file mode 100644
index 0000000..ff15125
--- /dev/null
+++ b/java/org/apache/coyote/CloseNowException.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.coyote;
+
+import java.io.IOException;
+
+/**
+ * This exception is thrown to signal to the Tomcat internals that an error has
+ * occurred that requires the connection to be closed. For multiplexed protocols
+ * such as HTTP/2, this means the channel must be closed but the connection can
+ * continue. For non-multiplexed protocols, the connection must be closed. It
+ * corresponds to {@link ErrorState#CLOSE_NOW}.
+ */
+public class CloseNowException extends IOException {
+
+ private static final long serialVersionUID = 1L;
+
+
+ public CloseNowException() {
+ super();
+ }
+
+
+ public CloseNowException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+
+ public CloseNowException(String message) {
+ super(message);
+ }
+
+
+ public CloseNowException(Throwable cause) {
+ super(cause);
+ }
+}
diff --git a/java/org/apache/coyote/Constants.java b/java/org/apache/coyote/Constants.java
index 1f6c32e..dc48b5f 100644
--- a/java/org/apache/coyote/Constants.java
+++ b/java/org/apache/coyote/Constants.java
@@ -16,7 +16,6 @@
*/
package org.apache.coyote;
-
/**
* Constants.
*
@@ -24,11 +23,6 @@ package org.apache.coyote;
*/
public final class Constants {
-
- // -------------------------------------------------------------- Constants
-
- public static final String Package = "org.apache.coyote";
-
public static final String DEFAULT_CHARACTER_ENCODING="ISO-8859-1";
public static final int MAX_NOTES = 32;
@@ -44,58 +38,21 @@ public final class Constants {
public static final int STAGE_KEEPALIVE = 6;
public static final int STAGE_ENDED = 7;
+ // Default protocol settings
+ public static final int DEFAULT_CONNECTION_LINGER = -1;
+ public static final boolean DEFAULT_TCP_NO_DELAY = true;
/**
* Has security been turned on?
*/
- public static final boolean IS_SECURITY_ENABLED =
- (System.getSecurityManager() != null);
-
-
- /**
- * If true, custom HTTP status messages will be used in headers.
- */
- public static final boolean USE_CUSTOM_STATUS_MSG_IN_HEADER =
- Boolean.parseBoolean(System.getProperty(
- "org.apache.coyote.USE_CUSTOM_STATUS_MSG_IN_HEADER",
- "false"));
-
- /**
- * The request attribute that is set to the value of {@code Boolean.TRUE}
- * if connector processing this request supports Comet API.
- */
- public static final String COMET_SUPPORTED_ATTR =
- "org.apache.tomcat.comet.support";
-
-
- /**
- * The request attribute that is set to the value of {@code Boolean.TRUE}
- * if connector processing this request supports setting
- * per-connection request timeout through Comet API.
- *
- * @see org.apache.catalina.comet.CometEvent#setTimeout(int)
- */
- public static final String COMET_TIMEOUT_SUPPORTED_ATTR =
- "org.apache.tomcat.comet.timeout.support";
-
-
- /**
- * The request attribute that can be set to a value of type
- * {@code java.lang.Integer} to specify per-connection request
- * timeout for Comet API. The value is in milliseconds.
- *
- * @see org.apache.catalina.comet.CometEvent#setTimeout(int)
- */
- public static final String COMET_TIMEOUT_ATTR =
- "org.apache.tomcat.comet.timeout";
+ public static final boolean IS_SECURITY_ENABLED = (System.getSecurityManager() != null);
/**
* The request attribute that is set to the value of {@code Boolean.TRUE}
* if connector processing this request supports use of sendfile.
*/
- public static final String SENDFILE_SUPPORTED_ATTR =
- "org.apache.tomcat.sendfile.support";
+ public static final String SENDFILE_SUPPORTED_ATTR = "org.apache.tomcat.sendfile.support";
/**
@@ -104,8 +61,7 @@ public final class Constants {
* by sendfile. The value should be {@code java.lang.String}
* that is {@code File.getCanonicalPath()} of the file to be served.
*/
- public static final String SENDFILE_FILENAME_ATTR =
- "org.apache.tomcat.sendfile.filename";
+ public static final String SENDFILE_FILENAME_ATTR = "org.apache.tomcat.sendfile.filename";
/**
@@ -115,8 +71,7 @@ public final class Constants {
* {@code java.lang.Long}. To serve complete file
* the value should be {@code Long.valueOf(0)}.
*/
- public static final String SENDFILE_FILE_START_ATTR =
- "org.apache.tomcat.sendfile.start";
+ public static final String SENDFILE_FILE_START_ATTR = "org.apache.tomcat.sendfile.start";
/**
@@ -126,8 +81,7 @@ public final class Constants {
* {@code java.lang.Long}. To serve complete file
* the value should be equal to the length of the file.
*/
- public static final String SENDFILE_FILE_END_ATTR =
- "org.apache.tomcat.sendfile.end";
+ public static final String SENDFILE_FILE_END_ATTR = "org.apache.tomcat.sendfile.end";
/**
@@ -137,6 +91,5 @@ public final class Constants {
* request is received via one or more proxies. It is typically provided via
* the X-Forwarded-For HTTP header.
*/
- public static final String REMOTE_ADDR_ATTRIBUTE =
- "org.apache.tomcat.remoteAddr";
+ public static final String REMOTE_ADDR_ATTRIBUTE = "org.apache.tomcat.remoteAddr";
}
diff --git a/java/org/apache/coyote/ErrorState.java b/java/org/apache/coyote/ErrorState.java
index f4cc524..7781def 100644
--- a/java/org/apache/coyote/ErrorState.java
+++ b/java/org/apache/coyote/ErrorState.java
@@ -21,30 +21,45 @@ public enum ErrorState {
/**
* Not in an error state.
*/
- NONE(false, 0, true),
+ NONE(false, 0, true, true),
/**
* The current request/response is in an error state and while it is safe to
* complete the current response it is not safe to continue to use the
* existing connection which must be closed once the response has been
- * completed.
+ * completed. For multiplexed protocols, the channel must be closed when the
+ * current request/response completes but the connection may continue.
*/
- CLOSE_CLEAN(true, 1, true),
+ CLOSE_CLEAN(true, 1, true, true),
/**
* The current request/response is in an error state and it is not safe to
- * continue to use the existing connection which must be closed immediately.
+ * continue to use them. For multiplexed protocols (such as HTTP/2) the
+ * stream/channel must be closed immediately but the connection may
+ * continue. For non-multiplexed protocols (AJP, HTTP/1.x) the current
+ * connection must be closed.
*/
- CLOSE_NOW(true, 2, false);
+ CLOSE_NOW(true, 2, false, true),
+
+ /**
+ * An error has been detected that impacts the underlying network
+ * connection. It is not safe to continue using the network connection which
+ * must be closed immediately. For multiplexed protocols (such as HTTP/2)
+ * this impacts all multiplexed channels.
+ */
+ CLOSE_CONNECTION_NOW(true, 3, false, false);
private final boolean error;
private final int severity;
private final boolean ioAllowed;
+ private final boolean connectionIoAllowed;
- private ErrorState(boolean error, int severity, boolean ioAllowed) {
+ private ErrorState(boolean error, int severity, boolean ioAllowed,
+ boolean connectionIoAllowed) {
this.error = error;
this.severity = severity;
this.ioAllowed = ioAllowed;
+ this.connectionIoAllowed = connectionIoAllowed;
}
public boolean isError() {
@@ -54,6 +69,11 @@ public enum ErrorState {
/**
* Compare this ErrorState with the provided ErrorState and return the most
* severe.
+ *
+ * @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
*/
public ErrorState getMostSevere(ErrorState input) {
if (input.severity > this.severity) {
@@ -66,4 +86,8 @@ public enum ErrorState {
public boolean isIoAllowed() {
return ioAllowed;
}
+
+ public boolean isConnectionIoAllowed() {
+ return connectionIoAllowed;
+ }
}
diff --git a/java/org/apache/coyote/InputBuffer.java b/java/org/apache/coyote/InputBuffer.java
index eec4da9..0aa17bc 100644
--- a/java/org/apache/coyote/InputBuffer.java
+++ b/java/org/apache/coyote/InputBuffer.java
@@ -14,30 +14,50 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
package org.apache.coyote;
import java.io.IOException;
import org.apache.tomcat.util.buf.ByteChunk;
-
+import org.apache.tomcat.util.net.ApplicationBufferHandler;
/**
- * Input buffer.
- *
- * This class is used only in the protocol implementation. All reading from
- * Tomcat ( or adapter ) should be done using Request.doRead().
+ * This class is only for internal use in the protocol implementation. All
+ * reading from Tomcat (or adapter) should be done using Request.doRead().
*/
public interface InputBuffer {
-
- /** Return from the input stream.
- IMPORTANT: the current model assumes that the protocol will 'own' the
- buffer and return a pointer to it in ByteChunk ( i.e. the param will
- have chunk.getBytes()==null before call, and the result after the call ).
- */
- public int doRead(ByteChunk chunk, Request request)
- throws IOException;
-
-
+ /**
+ * Read from the input stream into the given buffer.
+ * IMPORTANT: the current model assumes that the protocol will 'own' the
+ * buffer and return a pointer to it in ByteChunk (i.e. the param will
+ * have chunk.getBytes()==null before call, and the result after the call).
+ *
+ * @param chunk The buffer to read data into.
+ *
+ * @return The number of bytes that have been added to the buffer or -1 for
+ * end of stream
+ *
+ * @throws IOException If an I/O error occurs reading from the input stream
+ *
+ * @deprecated Unused. Will be removed in Tomcat 9. Use
+ * {@link #doRead(ApplicationBufferHandler)}
+ */
+ public int doRead(ByteChunk chunk) throws IOException;
+
+ /**
+ * Read from the input stream into the ByteBuffer provided by the
+ * ApplicaitonBufferHandler.
+ * IMPORTANT: the current model assumes that the protocol will 'own' the
+ * ByteBuffer and return a pointer to it.
+ *
+ * @param handler ApplicaitonBufferHandler that provides the buffer to read
+ * data into.
+ *
+ * @return The number of bytes that have been added to the buffer or -1 for
+ * end of stream
+ *
+ * @throws IOException If an I/O error occurs reading from the input stream
+ */
+ public int doRead(ApplicationBufferHandler handler) throws IOException;
}
diff --git a/java/org/apache/coyote/LocalStrings.properties b/java/org/apache/coyote/LocalStrings.properties
index 6fbdb21..8a75016 100644
--- a/java/org/apache/coyote/LocalStrings.properties
+++ b/java/org/apache/coyote/LocalStrings.properties
@@ -14,9 +14,15 @@
# limitations under the License.
abstractConnectionHandler.error=Error reading request, ignored
abstractConnectionHandler.ioexception.debug=IOExceptions are normal, ignored
+abstractConnectionHandler.process=Processing socket [{0}] with status [{1}]
+abstractConnectionHandler.protocolexception.debug=ProtocolExceptions are normal, ignored
abstractConnectionHandler.socketexception.debug=SocketExceptions are normal, ignored
+abstractConnectionHandler.negotiatedProcessor.fail=Failed to create Processor for negotiated protocol [{0}]
+abstractProcessor.httpupgrade.notsupported=HTTP upgrade is not supported by this protocol
abstractProcessor.nonContainerThreadError=An error occurred in processing while on a non-container thread. The connection will be closed immediately
+abstractProcessor.pushrequest.notsupported=Server push requests are not supported by this protocol
+abstractProcessor.socket.ssl=Exception getting SSL attributes
abstractProtocol.mbeanDeregistrationFailed=Failed to deregister MBean named [{0}] from MBean server [{1}]
@@ -43,5 +49,6 @@ request.nullReadListener=The listener passed to setReadListener() may not be nul
request.readListenerSet=The non-blocking read listener has already been set
response.notAsync=It is only valid to switch to non-blocking IO within async processing or HTTP upgrade processing
+response.notNonBlocking=It is invalid to call isReady() when the response has not been put into non-blocking mode
response.nullWriteListener=The listener passed to setWriteListener() may not be null
response.writeListenerSet=The non-blocking write listener has already been set
diff --git a/java/org/apache/coyote/LocalStrings_es.properties b/java/org/apache/coyote/LocalStrings_es.properties
index 477d970..6557055 100644
--- a/java/org/apache/coyote/LocalStrings_es.properties
+++ b/java/org/apache/coyote/LocalStrings_es.properties
@@ -12,4 +12,7 @@
# 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.
+
abstractConnectionHandler.error = Error leyendo requerimiento, ignorado
+
+abstractProcessor.socket.ssl = Excepci\u00F3n obteniendo atributos SSL
diff --git a/java/org/apache/coyote/OutputBuffer.java b/java/org/apache/coyote/OutputBuffer.java
index 74c6776..bcf558c 100644
--- a/java/org/apache/coyote/OutputBuffer.java
+++ b/java/org/apache/coyote/OutputBuffer.java
@@ -14,35 +14,51 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
package org.apache.coyote;
import java.io.IOException;
+import java.nio.ByteBuffer;
import org.apache.tomcat.util.buf.ByteChunk;
-
/**
* Output buffer.
*
* This class is used internally by the protocol implementation. All writes from
- * higher level code should happen via Resonse.doWrite().
+ * higher level code should happen via Response.doWrite().
*
* @author Remy Maucherat
*/
public interface OutputBuffer {
+ /**
+ * Write the given data to the response. The caller owns the chunks.
+ *
+ * @param chunk data to write
+ *
+ * @return The number of bytes written which may be less than available in
+ * the input chunk
+ *
+ * @throws IOException an underlying I/O error occurred
+ *
+ * @deprecated Unused. Will be removed in Tomcat 9. Use
+ * {@link #doWrite(ByteBuffer)}
+ */
+ public int doWrite(ByteChunk chunk) throws IOException;
+
/**
- * Write the response. The caller ( tomcat ) owns the chunks.
+ * Write the given data to the response. The caller owns the chunks.
*
* @param chunk data to write
- * @param response used to allow buffers that can be shared by multiple
- * responses.
- * @throws IOException
+ *
+ * @return The number of bytes written which may be less than available in
+ * the input chunk
+ *
+ * @throws IOException an underlying I/O error occurred
*/
- public int doWrite(ByteChunk chunk, Response response)
- throws IOException;
+ public int doWrite(ByteBuffer chunk) throws IOException;
+
/**
* Bytes written to the underlying socket. This includes the effects of
diff --git a/java/org/apache/coyote/Processor.java b/java/org/apache/coyote/Processor.java
index 71839f8..c016c3a 100644
--- a/java/org/apache/coyote/Processor.java
+++ b/java/org/apache/coyote/Processor.java
@@ -18,45 +18,96 @@ package org.apache.coyote;
import java.io.IOException;
import java.nio.ByteBuffer;
-import java.util.concurrent.Executor;
import org.apache.tomcat.util.net.AbstractEndpoint.Handler.SocketState;
import org.apache.tomcat.util.net.SSLSupport;
-import org.apache.tomcat.util.net.SocketStatus;
-import org.apache.tomcat.util.net.SocketWrapper;
-
+import org.apache.tomcat.util.net.SocketEvent;
+import org.apache.tomcat.util.net.SocketWrapperBase;
/**
* Common interface for processors of all protocols.
*/
-public interface Processor<S> {
- Executor getExecutor();
-
- SocketState process(SocketWrapper<S> socketWrapper) throws IOException;
-
- SocketState event(SocketStatus status) throws IOException;
+public interface Processor {
- SocketState asyncDispatch(SocketStatus status);
- SocketState asyncPostProcess();
+ /**
+ * Process a connection. This is called whenever an event occurs (e.g. more
+ * data arrives) that allows processing to continue for a connection that is
+ * not currently being processed.
+ *
+ * @param socketWrapper The connection to process
+ * @param status The status of the connection that triggered this additional
+ * processing
+ *
+ * @return The state the caller should put the socket in when this method
+ * returns
+ *
+ * @throws IOException If an I/O error occurs during the processing of the
+ * request
+ */
+ SocketState process(SocketWrapperBase<?> socketWrapper, SocketEvent status) throws IOException;
+ /**
+ * Generate an upgrade token.
+ *
+ * @return An upgrade token encapsulating the information required to
+ * process the upgrade request
+ *
+ * @throws IllegalStateException if this is called on a Processor that does
+ * not support upgrading
+ */
UpgradeToken getUpgradeToken();
- SocketState upgradeDispatch(SocketStatus status) throws IOException;
-
- void errorDispatch();
- boolean isComet();
- boolean isAsync();
+ /**
+ * @return {@code true} if the Processor is currently processing an upgrade
+ * request, otherwise {@code false}
+ */
boolean isUpgrade();
+ boolean isAsync();
+ /**
+ * Check this processor to see if the async timeout has expired and process
+ * a timeout if that is that case.
+ *
+ * @param now The time (as returned by {@link System#currentTimeMillis()} to
+ * use as the current time to determine whether the async timeout
+ * has expired. If negative, the timeout will always be treated
+ * as if it has expired.
+ */
+ void timeoutAsync(long now);
+
+ /**
+ * @return The request associated with this processor.
+ */
Request getRequest();
- void recycle(boolean socketClosing);
+ /**
+ * Recycle the processor, ready for the next request which may be on the
+ * same connection or a different connection.
+ */
+ void recycle();
+ /**
+ * Set the SSL information for this HTTP connection.
+ *
+ * @param sslSupport The SSL support object to use for this connection
+ */
void setSslSupport(SSLSupport sslSupport);
/**
- * Allows retrieving additional input during the upgrade process
+ * Allows retrieving additional input during the upgrade process.
+ *
* @return leftover bytes
+ *
+ * @throws IllegalStateException if this is called on a Processor that does
+ * not support upgrading
*/
ByteBuffer getLeftoverInput();
+
+ /**
+ * Informs the processor that the underlying I/O layer has stopped accepting
+ * new connections. This is primarily intended to enable processors that
+ * use multiplexed connections to prevent further 'streams' being added to
+ * an existing multiplexed connection.
+ */
+ void pause();
}
diff --git a/java/org/apache/coyote/ProtocolException.java b/java/org/apache/coyote/ProtocolException.java
new file mode 100644
index 0000000..9047f86
--- /dev/null
+++ b/java/org/apache/coyote/ProtocolException.java
@@ -0,0 +1,42 @@
+/*
+ * 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;
+
+/**
+ * Used when we need to indicate failure but the (Servlet) API doesn't declare
+ * any appropriate exceptions.
+ */
+public class ProtocolException extends RuntimeException {
+
+ private static final long serialVersionUID = 1L;
+
+ public ProtocolException() {
+ super();
+ }
+
+ public ProtocolException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public ProtocolException(String message) {
+ super(message);
+ }
+
+ public ProtocolException(Throwable cause) {
+ super(cause);
+ }
+}
diff --git a/java/org/apache/coyote/ProtocolHandler.java b/java/org/apache/coyote/ProtocolHandler.java
index ed1d0b8..33b00ce 100644
--- a/java/org/apache/coyote/ProtocolHandler.java
+++ b/java/org/apache/coyote/ProtocolHandler.java
@@ -14,11 +14,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
package org.apache.coyote;
import java.util.concurrent.Executor;
+import org.apache.tomcat.util.net.SSLHostConfig;
/**
* Abstract the protocol implementation, including threading, etc.
@@ -37,6 +37,8 @@ public interface ProtocolHandler {
/**
* The adapter, used to call the connector.
+ *
+ * @param adapter The adapter to associate
*/
public void setAdapter(Adapter adapter);
public Adapter getAdapter();
@@ -44,66 +46,82 @@ public interface ProtocolHandler {
/**
* The executor, provide access to the underlying thread pool.
+ *
+ * @return The executor used to process requests
*/
public Executor getExecutor();
/**
* Initialise the protocol.
+ *
+ * @throws Exception If the protocol handler fails to initialise
*/
public void init() throws Exception;
/**
* Start the protocol.
+ *
+ * @throws Exception If the protocol handler fails to start
*/
public void start() throws Exception;
/**
* Pause the protocol (optional).
+ *
+ * @throws Exception If the protocol handler fails to pause
*/
public void pause() throws Exception;
/**
* Resume the protocol (optional).
+ *
+ * @throws Exception If the protocol handler fails to resume
*/
public void resume() throws Exception;
/**
* Stop the protocol.
+ *
+ * @throws Exception If the protocol handler fails to stop
*/
public void stop() throws Exception;
/**
* Destroy the protocol (optional).
+ *
+ * @throws Exception If the protocol handler fails to destroy
*/
public void destroy() throws Exception;
/**
* Requires APR/native library
+ *
+ * @return <code>true</code> if this Protocol Handler requires the
+ * APR/native library, otherwise <code>false</code>
*/
public boolean isAprRequired();
/**
- * Does this ProtocolHandler support Comet?
+ * Does this ProtocolHandler support sendfile?
+ *
+ * @return <code>true</code> if this Protocol Handler supports sendfile,
+ * otherwise <code>false</code>
*/
- public boolean isCometSupported();
+ public boolean isSendfileSupported();
- /**
- * Does this ProtocolHandler support Comet timeouts?
- */
- public boolean isCometTimeoutSupported();
+ public void addSslHostConfig(SSLHostConfig sslHostConfig);
+ public SSLHostConfig[] findSslHostConfigs();
- /**
- * Does this ProtocolHandler support sendfile?
- */
- public boolean isSendfileSupported();
+ public void addUpgradeProtocol(UpgradeProtocol upgradeProtocol);
+ public UpgradeProtocol[] findUpgradeProtocols();
}
diff --git a/java/org/apache/coyote/PushToken.java b/java/org/apache/coyote/PushToken.java
new file mode 100644
index 0000000..83b5ba5
--- /dev/null
+++ b/java/org/apache/coyote/PushToken.java
@@ -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.coyote;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * Token used during the HTTP/2 server push process.
+ */
+public class PushToken {
+
+ private final AtomicBoolean result = new AtomicBoolean(false);
+ private final Request pushTarget;
+
+ public PushToken(Request pushTarget) {
+ this.pushTarget = pushTarget;
+ }
+
+ public Request getPushTarget() {
+ return pushTarget;
+ }
+
+ public void setResult(boolean result) {
+ this.result.set(result);
+ }
+
+ public boolean getResult() {
+ return result.get();
+ }
+}
diff --git a/java/org/apache/coyote/Request.java b/java/org/apache/coyote/Request.java
index 6bf7e8c..2598d59 100644
--- a/java/org/apache/coyote/Request.java
+++ b/java/org/apache/coyote/Request.java
@@ -18,6 +18,7 @@ package org.apache.coyote;
import java.io.IOException;
import java.util.HashMap;
+import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.servlet.ReadListener;
@@ -28,6 +29,7 @@ import org.apache.tomcat.util.buf.UDecoder;
import org.apache.tomcat.util.http.MimeHeaders;
import org.apache.tomcat.util.http.Parameters;
import org.apache.tomcat.util.http.ServerCookies;
+import org.apache.tomcat.util.net.ApplicationBufferHandler;
import org.apache.tomcat.util.res.StringManager;
/**
@@ -59,10 +61,9 @@ import org.apache.tomcat.util.res.StringManager;
*/
public final class Request {
- private static final StringManager sm =
- StringManager.getManager(Constants.Package);
+ private static final StringManager sm = StringManager.getManager(Request.class);
- // Expected maximum typica number of cookies per request.
+ // Expected maximum typical number of cookies per request.
private static final int INITIAL_COOKIE_SIZE = 4;
// ----------------------------------------------------------- Constructors
@@ -97,7 +98,11 @@ public final class Request {
private final MimeHeaders headers = new MimeHeaders();
- private final MessageBytes instanceId = MessageBytes.newInstance();
+
+ /**
+ * Path parameters
+ */
+ private final Map<String,String> pathParameters = new HashMap<>();
/**
* Notes.
@@ -123,6 +128,10 @@ public final class Request {
private long contentLength = -1;
private MessageBytes contentTypeMB = null;
private String charEncoding = null;
+ /**
+ * Is there an expectation ?
+ */
+ private boolean expectation = false;
private final ServerCookies serverCookies = new ServerCookies(INITIAL_COOKIE_SIZE);
private final Parameters parameters = new Parameters();
@@ -142,6 +151,7 @@ public final class Request {
private final RequestInfo reqProcessorMX=new RequestInfo(this);
+ private boolean sendfile = true;
volatile ReadListener listener;
@@ -179,18 +189,6 @@ public final class Request {
// ------------------------------------------------------------- Properties
- /**
- * Get the instance id (or JVM route). Currently Ajp is sending it with each
- * request. In future this should be fixed, and sent only once ( or
- * 'negotiated' at config time so both tomcat and apache share the same name.
- *
- * @return the instance id
- */
- public MessageBytes instanceId() {
- return instanceId;
- }
-
-
public MimeHeaders getMimeHeaders() {
return headers;
}
@@ -228,11 +226,11 @@ public final class Request {
}
/**
- * Return the buffer holding the server name, if
- * any. Use isNull() to check if there is no value
- * set.
- * This is the "virtual host", derived from the
- * Host: header.
+ * Get the "virtual host", derived from the Host: header associated with
+ * this request.
+ *
+ * @return The buffer holding the server name, if any. Use isNull() to check
+ * if there is no value set.
*/
public MessageBytes serverName() {
return serverNameMB;
@@ -283,6 +281,10 @@ public final class Request {
/**
* Get the character encoding used for this request.
+ *
+ * @return The value set via {@link #setCharacterEncoding(String)} or if no
+ * call has been made to that method try to obtain if from the
+ * content type.
*/
public String getCharacterEncoding() {
@@ -357,6 +359,17 @@ public final class Request {
return headers.getHeader(name);
}
+
+ public void setExpectation(boolean expectation) {
+ this.expectation = expectation;
+ }
+
+
+ public boolean hasExpectation() {
+ return expectation;
+ }
+
+
// -------------------- Associated response --------------------
public Response getResponse() {
@@ -397,6 +410,15 @@ public final class Request {
}
+ public void addPathParameter(String name, String value) {
+ pathParameters.put(name, value);
+ }
+
+ public String getPathParameter(String name) {
+ return pathParameters.get(name);
+ }
+
+
// -------------------- Other attributes --------------------
// We can use notes for most - need to discuss what is of general interest
@@ -436,6 +458,14 @@ public final class Request {
this.available = available;
}
+ public boolean getSendfile() {
+ return sendfile;
+ }
+
+ public void setSendfile(boolean sendfile) {
+ this.sendfile = sendfile;
+ }
+
public boolean isFinished() {
AtomicBoolean result = new AtomicBoolean(false);
action(ActionCode.REQUEST_BODY_FULLY_READ, result);
@@ -465,15 +495,49 @@ public final class Request {
/**
* Read data from the input buffer and put it into a byte chunk.
*
- * The buffer is owned by the protocol implementation - it will be reused on the next read.
- * The Adapter must either process the data in place or copy it to a separate buffer if it needs
- * to hold it. In most cases this is done during byte->char conversions or via InputStream. Unlike
- * InputStream, this interface allows the app to process data in place, without copy.
+ * The buffer is owned by the protocol implementation - it will be reused on
+ * the next read. The Adapter must either process the data in place or copy
+ * it to a separate buffer if it needs to hold it. In most cases this is
+ * done during byte->char conversions or via InputStream. Unlike
+ * InputStream, this interface allows the app to process data in place,
+ * without copy.
+ *
+ * @param chunk The destination to which to copy the data
*
+ * @return The number of bytes copied
+ *
+ * @throws IOException If an I/O error occurs during the copy
+ *
+ * @deprecated Unused. Will be removed in Tomcat 9. Use
+ * {@link #doRead(ApplicationBufferHandler)}
*/
- public int doRead(ByteChunk chunk)
- throws IOException {
- int n = inputBuffer.doRead(chunk, this);
+ public int doRead(ByteChunk chunk) throws IOException {
+ int n = inputBuffer.doRead(chunk);
+ if (n > 0) {
+ bytesRead+=n;
+ }
+ return n;
+ }
+
+
+ /**
+ * Read data from the input buffer and put it into ApplicationBufferHandler.
+ *
+ * The buffer is owned by the protocol implementation - it will be reused on
+ * the next read. The Adapter must either process the data in place or copy
+ * it to a separate buffer if it needs to hold it. In most cases this is
+ * done during byte->char conversions or via InputStream. Unlike
+ * InputStream, this interface allows the app to process data in place,
+ * without copy.
+ *
+ * @param handler The destination to which to copy the data
+ *
+ * @return The number of bytes copied
+ *
+ * @throws IOException If an I/O error occurs during the copy
+ */
+ public int doRead(ApplicationBufferHandler handler) throws IOException {
+ int n = inputBuffer.doRead(handler);
if (n > 0) {
bytesRead+=n;
}
@@ -505,10 +569,6 @@ public final class Request {
* be faster than ThreadLocal for very frequent operations.
*
* Example use:
- * Jk:
- * HandlerRequest.HOSTBUFFER = 10 CharChunk, buffer for Host decoding
- * WorkerEnv: SSL_CERT_NOTE=16 - MessageBytes containing the cert
- *
* Catalina CoyoteAdapter:
* ADAPTER_NOTES = 1 - stores the HttpServletRequest object ( req/res)
*
@@ -517,6 +577,9 @@ public final class Request {
* for connector use.
*
* 17-31 range is not allocated or used.
+ *
+ * @param pos Index to use to store the note
+ * @param value The value to store at that index
*/
public final void setNote(int pos, Object value) {
notes[pos] = value;
@@ -537,16 +600,22 @@ public final class Request {
contentLength = -1;
contentTypeMB = null;
charEncoding = null;
+ expectation = false;
headers.recycle();
serverNameMB.recycle();
serverPort=-1;
+ localAddrMB.recycle();
localNameMB.recycle();
localPort = -1;
+ remoteAddrMB.recycle();
+ remoteHostMB.recycle();
remotePort = -1;
available = 0;
+ sendfile = true;
serverCookies.recycle();
parameters.recycle();
+ pathParameters.clear();
uriMB.recycle();
decodedUriMB.recycle();
@@ -556,7 +625,6 @@ public final class Request {
schemeMB.recycle();
- instanceId.recycle();
remoteUser.recycle();
remoteUserNeedsAuthorization = false;
authType.recycle();
diff --git a/java/org/apache/coyote/RequestGroupInfo.java b/java/org/apache/coyote/RequestGroupInfo.java
index ad5b74b..c5568b1 100644
--- a/java/org/apache/coyote/RequestGroupInfo.java
+++ b/java/org/apache/coyote/RequestGroupInfo.java
@@ -14,7 +14,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
package org.apache.coyote;
import java.util.ArrayList;
@@ -51,10 +50,11 @@ public class RequestGroupInfo {
}
public synchronized long getMaxTime() {
- long maxTime=deadMaxTime;
- for( int i=0; i<processors.size(); i++ ) {
- RequestInfo rp=processors.get( i );
- if( maxTime < rp.getMaxTime() ) maxTime=rp.getMaxTime();
+ long maxTime = deadMaxTime;
+ for (RequestInfo rp : processors) {
+ if (maxTime < rp.getMaxTime()) {
+ maxTime=rp.getMaxTime();
+ }
}
return maxTime;
}
@@ -62,16 +62,14 @@ public class RequestGroupInfo {
// Used to reset the times
public synchronized void setMaxTime(long maxTime) {
deadMaxTime = maxTime;
- for( int i=0; i<processors.size(); i++ ) {
- RequestInfo rp=processors.get( i );
+ for (RequestInfo rp : processors) {
rp.setMaxTime(maxTime);
}
}
public synchronized long getProcessingTime() {
- long time=deadProcessingTime;
- for( int i=0; i<processors.size(); i++ ) {
- RequestInfo rp=processors.get( i );
+ long time = deadProcessingTime;
+ for (RequestInfo rp : processors) {
time += rp.getProcessingTime();
}
return time;
@@ -79,16 +77,14 @@ public class RequestGroupInfo {
public synchronized void setProcessingTime(long totalTime) {
deadProcessingTime = totalTime;
- for( int i=0; i<processors.size(); i++ ) {
- RequestInfo rp=processors.get( i );
+ for (RequestInfo rp : processors) {
rp.setProcessingTime( totalTime );
}
}
public synchronized int getRequestCount() {
- int requestCount=deadRequestCount;
- for( int i=0; i<processors.size(); i++ ) {
- RequestInfo rp=processors.get( i );
+ int requestCount = deadRequestCount;
+ for (RequestInfo rp : processors) {
requestCount += rp.getRequestCount();
}
return requestCount;
@@ -96,16 +92,14 @@ public class RequestGroupInfo {
public synchronized void setRequestCount(int requestCount) {
deadRequestCount = requestCount;
- for( int i=0; i<processors.size(); i++ ) {
- RequestInfo rp=processors.get( i );
+ for (RequestInfo rp : processors) {
rp.setRequestCount( requestCount );
}
}
public synchronized int getErrorCount() {
- int requestCount=deadErrorCount;
- for( int i=0; i<processors.size(); i++ ) {
- RequestInfo rp=processors.get( i );
+ int requestCount = deadErrorCount;
+ for (RequestInfo rp : processors) {
requestCount += rp.getErrorCount();
}
return requestCount;
@@ -113,16 +107,14 @@ public class RequestGroupInfo {
public synchronized void setErrorCount(int errorCount) {
deadErrorCount = errorCount;
- for( int i=0; i<processors.size(); i++ ) {
- RequestInfo rp=processors.get( i );
+ for (RequestInfo rp : processors) {
rp.setErrorCount( errorCount);
}
}
public synchronized long getBytesReceived() {
- long bytes=deadBytesReceived;
- for( int i=0; i<processors.size(); i++ ) {
- RequestInfo rp=processors.get( i );
+ long bytes = deadBytesReceived;
+ for (RequestInfo rp : processors) {
bytes += rp.getBytesReceived();
}
return bytes;
@@ -130,16 +122,14 @@ public class RequestGroupInfo {
public synchronized void setBytesReceived(long bytesReceived) {
deadBytesReceived = bytesReceived;
- for( int i=0; i<processors.size(); i++ ) {
- RequestInfo rp=processors.get( i );
+ for (RequestInfo rp : processors) {
rp.setBytesReceived( bytesReceived );
}
}
public synchronized long getBytesSent() {
long bytes=deadBytesSent;
- for( int i=0; i<processors.size(); i++ ) {
- RequestInfo rp=processors.get( i );
+ for (RequestInfo rp : processors) {
bytes += rp.getBytesSent();
}
return bytes;
@@ -147,8 +137,7 @@ public class RequestGroupInfo {
public synchronized void setBytesSent(long bytesSent) {
deadBytesSent = bytesSent;
- for( int i=0; i<processors.size(); i++ ) {
- RequestInfo rp=processors.get( i );
+ for (RequestInfo rp : processors) {
rp.setBytesSent( bytesSent );
}
}
diff --git a/java/org/apache/coyote/RequestInfo.java b/java/org/apache/coyote/RequestInfo.java
index cc66d06..30216b7 100644
--- a/java/org/apache/coyote/RequestInfo.java
+++ b/java/org/apache/coyote/RequestInfo.java
@@ -30,8 +30,6 @@ import javax.management.ObjectName;
* having to deal with synchronization ( since each thread will have it's own
* RequestProcessorMX ).
*
- * TODO: Request notifications will be registered here.
- *
* @author Costin Manolache
*/
public class RequestInfo {
@@ -101,6 +99,8 @@ public class RequestInfo {
/**
* Obtain the remote address for this connection as reported by an
* intermediate proxy (if any).
+ *
+ * @return The remote address for the this connection
*/
public String getRemoteAddrForwarded() {
String remoteAddrProxy = (String) req.getAttribute(Constants.REMOTE_ADDR_ATTRIBUTE);
diff --git a/java/org/apache/coyote/Response.java b/java/org/apache/coyote/Response.java
index 620b0f1..02de64c 100644
--- a/java/org/apache/coyote/Response.java
+++ b/java/org/apache/coyote/Response.java
@@ -18,6 +18,7 @@ package org.apache.coyote;
import java.io.IOException;
import java.io.StringReader;
+import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.util.Locale;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -42,8 +43,7 @@ import org.apache.tomcat.util.res.StringManager;
*/
public final class Response {
- private static final StringManager sm =
- StringManager.getManager(Constants.Package);
+ private static final StringManager sm = StringManager.getManager(Response.class);
// ----------------------------------------------------- Class Variables
@@ -94,7 +94,7 @@ public final class Response {
/**
* Action hook.
*/
- public volatile ActionHook hook;
+ volatile ActionHook hook;
/**
@@ -132,10 +132,6 @@ public final class Response {
this.req=req;
}
- public OutputBuffer getOutputBuffer() {
- return outputBuffer;
- }
-
public void setOutputBuffer(OutputBuffer outputBuffer) {
this.outputBuffer = outputBuffer;
@@ -147,12 +143,7 @@ public final class Response {
}
- public ActionHook getHook() {
- return hook;
- }
-
-
- public void setHook(ActionHook hook) {
+ protected void setHook(ActionHook hook) {
this.hook = hook;
}
@@ -190,15 +181,19 @@ public final class Response {
/**
- * Set the response status
+ * Set the response status.
+ *
+ * @param status The status value to set
*/
- public void setStatus( int status ) {
+ public void setStatus(int status) {
this.status = status;
}
/**
* Get the status message.
+ *
+ * @return The message associated with the current status
*/
public String getMessage() {
return message;
@@ -207,6 +202,8 @@ public final class Response {
/**
* Set the status message.
+ *
+ * @param message The status message to set
*/
public void setMessage(String message) {
this.message = message;
@@ -238,8 +235,9 @@ public final class Response {
/**
- * Set the error Exception that occurred during
- * request processing.
+ * Set the error Exception that occurred during request processing.
+ *
+ * @param ex The exception that occurred
*/
public void setErrorException(Exception ex) {
errorException = ex;
@@ -247,8 +245,9 @@ public final class Response {
/**
- * Get the Exception that occurred during request
- * processing.
+ * Get the Exception that occurred during request processing.
+ *
+ * @return The exception that occurred
*/
public Exception getErrorException() {
return errorException;
@@ -270,16 +269,19 @@ public final class Response {
}
recycle();
-
- // Reset the stream
- action(ActionCode.RESET, this);
}
// -------------------- Headers --------------------
/**
+ * Does the response contain the given header.
+ * <br>
* Warning: This method always returns <code>false</code> for Content-Type
* and Content-Length.
+ *
+ * @param name The name of the header of interest
+ *
+ * @return {@code true} if the response contains the header.
*/
public boolean containsHeader(String name) {
return headers.getHeader(name) != null;
@@ -360,8 +362,10 @@ public final class Response {
}
/**
- * Called explicitly by user to set the Content-Language and
- * the default encoding
+ * Called explicitly by user to set the Content-Language and the default
+ * encoding.
+ *
+ * @param locale The locale to use for this response
*/
public void setLocale(Locale locale) {
@@ -378,6 +382,9 @@ public final class Response {
/**
* Return the content language.
+ *
+ * @return The language code for the language currently associated with this
+ * response
*/
public String getContentLanguage() {
return contentLanguage;
@@ -484,14 +491,33 @@ public final class Response {
/**
* Write a chunk of bytes.
+ *
+ * @param chunk The bytes to write
+ *
+ * @throws IOException If an I/O error occurs during the write
+ *
+ * @deprecated Unused. Will be removed in Tomcat 9. Use
+ * {@link #doWrite(ByteBuffer)}
*/
- public void doWrite(ByteChunk chunk/*byte buffer[], int pos, int count*/)
- throws IOException
- {
- outputBuffer.doWrite(chunk, this);
+ public void doWrite(ByteChunk chunk) throws IOException {
+ outputBuffer.doWrite(chunk);
contentWritten+=chunk.getLength();
}
+
+ /**
+ * Write a chunk of bytes.
+ *
+ * @param chunk The ByteBuffer to write
+ *
+ * @throws IOException If an I/O error occurs during the write
+ */
+ public void doWrite(ByteBuffer chunk) throws IOException {
+ int len = chunk.remaining();
+ outputBuffer.doWrite(chunk);
+ contentWritten += len - chunk.remaining();
+ }
+
// --------------------
public void recycle() {
@@ -519,6 +545,10 @@ public final class Response {
/**
* Bytes written by application - i.e. before compression, chunking, etc.
+ *
+ * @return The total number of bytes written to the response by the
+ * application. This will not be the number of bytes written to the
+ * network which may be more or less than this value.
*/
public long getContentWritten() {
return contentWritten;
@@ -526,6 +556,12 @@ public final class Response {
/**
* Bytes written to socket - i.e. after compression, chunking, etc.
+ *
+ * @param flush Should any remaining bytes be flushed before returning the
+ * total? If {@code false} bytes remaining in the buffer will
+ * not be included in the returned value
+ *
+ * @return The total number of bytes written to the socket for this response
*/
public long getBytesWritten(boolean flush) {
if (flush) {
@@ -577,7 +613,7 @@ public final class Response {
if (isReady()) {
synchronized (nonBlockingStateLock) {
// Ensure we don't get multiple write registrations if
- // ServletOutoutStream.isReady() returns false during a call to
+ // ServletOutputStream.isReady() returns false during a call to
// onDataAvailable()
registeredForWrite = true;
// Need to set the fireListener flag otherwise when the
@@ -595,8 +631,7 @@ public final class Response {
public boolean isReady() {
if (listener == null) {
- // TODO i18n
- throw new IllegalStateException("not in non blocking mode.");
+ throw new IllegalStateException(sm.getString("response.notNonBlocking"));
}
// Assume write is not possible
boolean ready = false;
diff --git a/java/org/apache/coyote/UpgradeProtocol.java b/java/org/apache/coyote/UpgradeProtocol.java
new file mode 100644
index 0000000..807e903
--- /dev/null
+++ b/java/org/apache/coyote/UpgradeProtocol.java
@@ -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.coyote;
+
+import org.apache.coyote.http11.upgrade.InternalHttpUpgradeHandler;
+import org.apache.tomcat.util.net.SocketWrapperBase;
+
+public interface UpgradeProtocol {
+
+ /**
+ * @param isSSLEnabled Is this for a connector that is configured to support
+ * TLS. Some protocols (e.g. HTTP/2) only support HTTP
+ * upgrade over non-secure connections.
+ * @return The name that clients will use to request an upgrade to this
+ * protocol via an HTTP/1.1 upgrade request or <code>null</code> if
+ * upgrade via an HTTP/1.1 upgrade request is not supported.
+ */
+ public String getHttpUpgradeName(boolean isSSLEnabled);
+
+ /**
+ * @return The byte sequence as listed in the IANA registry for this
+ * protocol or <code>null</code> if upgrade via ALPN is not
+ * supported.
+ */
+ public byte[] getAlpnIdentifier();
+
+ /**
+ * @return The name of the protocol as listed in the IANA registry if and
+ * only if {@link #getAlpnIdentifier()} returns the UTF-8 encoding
+ * of this name. If {@link #getAlpnIdentifier()} returns some other
+ * byte sequence, then this method returns the empty string. If
+ * upgrade via ALPN is not supported then <code>null</code> is
+ * returned.
+ */
+ /*
+ * Implementation note: If Tomcat ever supports ALPN for a protocol where
+ * the identifier is not the UTF-8 encoding of the name
+ * then some refactoring is going to be required.
+ *
+ * Implementation note: Tomcat assumes that the UTF-8 encoding of this name
+ * will not exceed 255 bytes. Tomcat's behaviour if
+ * longer names are used is undefined.
+ */
+ public String getAlpnName();
+
+ /**
+ * @param socketWrapper The socketWrapper for the connection that requires
+ * a processor
+ * @param adapter The Adapter instance that provides access to the standard
+ * Engine/Host/Context/Wrapper processing chain
+ *
+ * @return A processor instance for processing a connection using this
+ * protocol.
+ */
+ public Processor getProcessor(SocketWrapperBase<?> socketWrapper, Adapter adapter);
+
+
+ /**
+ * @param adapter The Adapter to use to configure the new upgrade handler
+ * @param request A copy (may be incomplete) of the request that triggered
+ * the upgrade
+ *
+ * @return An instance of the HTTP upgrade handler for this protocol
+ */
+ public InternalHttpUpgradeHandler getInternalUpgradeHandler(Adapter adapter, Request request);
+
+
+ /**
+ * Allows the implementation to examine the request and accept or reject it
+ * based on what it finds.
+ *
+ * @param request The request that included an upgrade header for this
+ * protocol
+ *
+ * @return <code>true</code> if the request is accepted, otherwise
+ * <code>false</code>
+ */
+ public boolean accept(Request request);
+}
diff --git a/java/org/apache/coyote/ajp/AbstractAjpProcessor.java b/java/org/apache/coyote/ajp/AbstractAjpProcessor.java
deleted file mode 100644
index 077e904..0000000
--- a/java/org/apache/coyote/ajp/AbstractAjpProcessor.java
+++ /dev/null
@@ -1,1841 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.coyote.ajp;
-
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.InterruptedIOException;
-import java.net.InetAddress;
-import java.nio.ByteBuffer;
-import java.security.NoSuchProviderException;
-import java.security.cert.CertificateFactory;
-import java.security.cert.X509Certificate;
-import java.util.Iterator;
-import java.util.concurrent.LinkedBlockingDeque;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-import javax.servlet.RequestDispatcher;
-import javax.servlet.http.HttpServletResponse;
-
-import org.apache.coyote.AbstractProcessor;
-import org.apache.coyote.ActionCode;
-import org.apache.coyote.AsyncContextCallback;
-import org.apache.coyote.ByteBufferHolder;
-import org.apache.coyote.ErrorState;
-import org.apache.coyote.InputBuffer;
-import org.apache.coyote.OutputBuffer;
-import org.apache.coyote.Request;
-import org.apache.coyote.RequestInfo;
-import org.apache.coyote.Response;
-import org.apache.coyote.UpgradeToken;
-import org.apache.tomcat.util.ExceptionUtils;
-import org.apache.tomcat.util.buf.ByteChunk;
-import org.apache.tomcat.util.buf.HexUtils;
-import org.apache.tomcat.util.buf.MessageBytes;
-import org.apache.tomcat.util.http.HttpMessages;
-import org.apache.tomcat.util.http.MimeHeaders;
-import org.apache.tomcat.util.net.AbstractEndpoint;
-import org.apache.tomcat.util.net.AbstractEndpoint.Handler.SocketState;
-import org.apache.tomcat.util.net.DispatchType;
-import org.apache.tomcat.util.net.SSLSupport;
-import org.apache.tomcat.util.net.SocketStatus;
-import org.apache.tomcat.util.net.SocketWrapper;
-import org.apache.tomcat.util.res.StringManager;
-
-/**
- * Base class for AJP Processor implementations.
- */
-public abstract class AbstractAjpProcessor<S> extends AbstractProcessor<S> {
-
- /**
- * The string manager for this package.
- */
- protected static final StringManager sm =
- StringManager.getManager(Constants.Package);
-
-
- /**
- * End message array.
- */
- protected static final byte[] endMessageArray;
- protected static final byte[] endAndCloseMessageArray;
-
-
- /**
- * Flush message array.
- */
- protected static final byte[] flushMessageArray;
-
-
- /**
- * Pong message array.
- */
- protected static final byte[] pongMessageArray;
-
-
- static {
- // Allocate the end message array
- AjpMessage endMessage = new AjpMessage(16);
- endMessage.reset();
- endMessage.appendByte(Constants.JK_AJP13_END_RESPONSE);
- endMessage.appendByte(1);
- endMessage.end();
- endMessageArray = new byte[endMessage.getLen()];
- System.arraycopy(endMessage.getBuffer(), 0, endMessageArray, 0,
- endMessage.getLen());
-
- // Allocate the end and close message array
- AjpMessage endAndCloseMessage = new AjpMessage(16);
- endAndCloseMessage.reset();
- endAndCloseMessage.appendByte(Constants.JK_AJP13_END_RESPONSE);
- endAndCloseMessage.appendByte(0);
- endAndCloseMessage.end();
- endAndCloseMessageArray = new byte[endAndCloseMessage.getLen()];
- System.arraycopy(endAndCloseMessage.getBuffer(), 0, endAndCloseMessageArray, 0,
- endAndCloseMessage.getLen());
-
- // Allocate the flush message array
- AjpMessage flushMessage = new AjpMessage(16);
- flushMessage.reset();
- flushMessage.appendByte(Constants.JK_AJP13_SEND_BODY_CHUNK);
- flushMessage.appendInt(0);
- flushMessage.appendByte(0);
- flushMessage.end();
- flushMessageArray = new byte[flushMessage.getLen()];
- System.arraycopy(flushMessage.getBuffer(), 0, flushMessageArray, 0,
- flushMessage.getLen());
-
- // Allocate the pong message array
- AjpMessage pongMessage = new AjpMessage(16);
- pongMessage.reset();
- pongMessage.appendByte(Constants.JK_AJP13_CPONG_REPLY);
- pongMessage.end();
- pongMessageArray = new byte[pongMessage.getLen()];
- System.arraycopy(pongMessage.getBuffer(), 0, pongMessageArray,
- 0, pongMessage.getLen());
- }
-
-
- // ----------------------------------------------------- Instance Variables
-
-
- /**
- * GetBody message array. Not static like the other message arrays since the
- * message varies with packetSize and that can vary per connector.
- */
- protected final byte[] getBodyMessageArray;
-
-
- /**
- * AJP packet size.
- */
- private final int outputMaxChunkSize;
-
- /**
- * Header message. Note that this header is merely the one used during the
- * processing of the first message of a "request", so it might not be a
- * request header. It will stay unchanged during the processing of the whole
- * request.
- */
- protected final AjpMessage requestHeaderMessage;
-
-
- /**
- * Message used for response composition.
- */
- protected final AjpMessage responseMessage;
-
-
- /**
- * Location of next write of the response message (used withnon-blocking
- * writes when the message may not be written in a single write). Avalue of
- * -1 indicates that no message has been written to the buffer.
- */
- private int responseMsgPos = -1;
-
-
- /**
- * Body message.
- */
- protected final AjpMessage bodyMessage;
-
-
- /**
- * Body message.
- */
- protected final MessageBytes bodyBytes = MessageBytes.newInstance();
-
-
- /**
- * The max size of the buffered write buffer
- */
- private int bufferedWriteSize = 64*1024; //64k default write buffer
-
-
- /**
- * For "non-blocking" writes use an external set of buffers. Although the
- * API only allows one non-blocking write at a time, due to buffering and
- * the possible need to write HTTP headers, there may be more than one write
- * to the OutputBuffer.
- */
- private final LinkedBlockingDeque<ByteBufferHolder> bufferedWrites =
- new LinkedBlockingDeque<>();
-
-
- /**
- * Host name (used to avoid useless B2C conversion on the host name).
- */
- protected char[] hostNameC = new char[0];
-
-
- /**
- * Temp message bytes used for processing.
- */
- protected final MessageBytes tmpMB = MessageBytes.newInstance();
-
-
- /**
- * Byte chunk for certs.
- */
- protected final MessageBytes certificates = MessageBytes.newInstance();
-
-
- /**
- * End of stream flag.
- */
- protected boolean endOfStream = false;
-
-
- /**
- * Request body empty flag.
- */
- protected boolean empty = true;
-
-
- /**
- * First read.
- */
- protected boolean first = true;
-
-
- /**
- * Indicates that a 'get body chunk' message has been sent but the body
- * chunk has not yet been received.
- */
- private boolean waitingForBodyMessage = false;
-
-
- /**
- * Replay read.
- */
- protected boolean replay = false;
-
-
- /**
- * Should any response body be swallowed and not sent to the client.
- */
- private boolean swallowResponse = false;
-
-
- /**
- * Finished response.
- */
- protected boolean finished = false;
-
-
- /**
- * Bytes written to client for the current request.
- */
- protected long bytesWritten = 0;
-
-
- // ------------------------------------------------------------ Constructor
-
- public AbstractAjpProcessor(int packetSize, AbstractEndpoint<S> endpoint) {
-
- super(endpoint);
-
- // Calculate maximum chunk size as packetSize may have been changed from
- // the default (Constants.MAX_PACKET_SIZE)
- this.outputMaxChunkSize =
- Constants.MAX_SEND_SIZE + packetSize - Constants.MAX_PACKET_SIZE;
-
- request.setInputBuffer(new SocketInputBuffer());
-
- requestHeaderMessage = new AjpMessage(packetSize);
- responseMessage = new AjpMessage(packetSize);
- bodyMessage = new AjpMessage(packetSize);
-
- // Set the getBody message buffer
- AjpMessage getBodyMessage = new AjpMessage(16);
- getBodyMessage.reset();
- getBodyMessage.appendByte(Constants.JK_AJP13_GET_BODY_CHUNK);
- // Adjust read size if packetSize != default (Constants.MAX_PACKET_SIZE)
- getBodyMessage.appendInt(Constants.MAX_READ_SIZE + packetSize -
- Constants.MAX_PACKET_SIZE);
- getBodyMessage.end();
- getBodyMessageArray = new byte[getBodyMessage.getLen()];
- System.arraycopy(getBodyMessage.getBuffer(), 0, getBodyMessageArray,
- 0, getBodyMessage.getLen());
- }
-
-
- // ------------------------------------------------------------- Properties
-
-
- /**
- * Send AJP flush packet when flushing.
- * An flush packet is a zero byte AJP13 SEND_BODY_CHUNK
- * packet. mod_jk and mod_proxy_ajp interprete 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 extra flush packets.
- * For compatibility and to stay on the safe side, flush
- * packets are enabled by default.
- */
- protected boolean ajpFlush = true;
- public boolean getAjpFlush() { return ajpFlush; }
- public void setAjpFlush(boolean ajpFlush) {
- this.ajpFlush = ajpFlush;
- }
-
-
- /**
- * The number of milliseconds Tomcat will wait for a subsequent request
- * before closing the connection. The default is -1 which is an infinite
- * timeout.
- */
- protected int keepAliveTimeout = -1;
- public int getKeepAliveTimeout() { return keepAliveTimeout; }
- public void setKeepAliveTimeout(int timeout) { keepAliveTimeout = timeout; }
-
-
- /**
- * Use Tomcat authentication ?
- */
- protected boolean tomcatAuthentication = true;
- public boolean getTomcatAuthentication() { return tomcatAuthentication; }
- public void setTomcatAuthentication(boolean tomcatAuthentication) {
- this.tomcatAuthentication = tomcatAuthentication;
- }
-
-
- /**
- * Use Tomcat authorization ?
- */
- private boolean tomcatAuthorization = false;
- public boolean getTomcatAuthorization() { return tomcatAuthorization; }
- public void setTomcatAuthorization(boolean tomcatAuthorization) {
- this.tomcatAuthorization = tomcatAuthorization;
- }
-
-
- /**
- * Required secret.
- */
- protected String requiredSecret = null;
- public void setRequiredSecret(String requiredSecret) {
- this.requiredSecret = requiredSecret;
- }
-
-
- /**
- * When client certificate information is presented in a form other than
- * instances of {@link java.security.cert.X509Certificate} it needs to be
- * converted before it can be used and this property controls which JSSE
- * provider is used to perform the conversion. For example it is used with
- * the AJP connectors, the HTTP APR connector and with the
- * {@link org.apache.catalina.valves.SSLValve}. If not specified, the
- * default provider will be used.
- */
- protected String clientCertProvider = null;
- public String getClientCertProvider() { return clientCertProvider; }
- public void setClientCertProvider(String s) { this.clientCertProvider = s; }
-
- // --------------------------------------------------------- Public Methods
-
-
- /**
- * Send an action to the connector.
- *
- * @param actionCode Type of the action
- * @param param Action parameter
- */
- @Override
- public final void action(ActionCode actionCode, Object param) {
-
- switch (actionCode) {
- case CLOSE: {
- // End the processing of the current request, and stop any further
- // transactions with the client
-
- try {
- finish();
- } catch (IOException e) {
- setErrorState(ErrorState.CLOSE_NOW, e);
- }
- break;
- }
- case COMMIT: {
- if (response.isCommitted())
- return;
-
- // Validate and write response headers
- try {
- prepareResponse();
- } catch (IOException e) {
- setErrorState(ErrorState.CLOSE_NOW, e);
- }
-
- try {
- flush(false);
- } catch (IOException e) {
- setErrorState(ErrorState.CLOSE_NOW, e);
- }
- break;
- }
- case ACK: {
- // NO_OP for AJP
- break;
- }
- case CLIENT_FLUSH: {
- if (!response.isCommitted()) {
- // Validate and write response headers
- try {
- prepareResponse();
- } catch (IOException e) {
- setErrorState(ErrorState.CLOSE_NOW, e);
- return;
- }
- }
-
- try {
- flush(true);
- } catch (IOException e) {
- setErrorState(ErrorState.CLOSE_NOW, e);
- }
- break;
- }
- case IS_ERROR: {
- ((AtomicBoolean) param).set(getErrorState().isError());
- break;
- }
- case DISABLE_SWALLOW_INPUT: {
- // TODO: Do not swallow request input but
- // make sure we are closing the connection
- setErrorState(ErrorState.CLOSE_CLEAN, null);
- break;
- }
- case RESET: {
- // NO-OP
- break;
- }
- case REQ_SSL_ATTRIBUTE: {
- if (!certificates.isNull()) {
- ByteChunk certData = certificates.getByteChunk();
- X509Certificate jsseCerts[] = null;
- ByteArrayInputStream bais =
- new ByteArrayInputStream(certData.getBytes(),
- certData.getStart(),
- certData.getLength());
- // Fill the elements.
- try {
- CertificateFactory cf;
- if (clientCertProvider == null) {
- cf = CertificateFactory.getInstance("X.509");
- } else {
- cf = CertificateFactory.getInstance("X.509",
- clientCertProvider);
- }
- while(bais.available() > 0) {
- X509Certificate cert = (X509Certificate)
- cf.generateCertificate(bais);
- if(jsseCerts == null) {
- jsseCerts = new X509Certificate[1];
- jsseCerts[0] = cert;
- } else {
- X509Certificate [] temp = new X509Certificate[jsseCerts.length+1];
- System.arraycopy(jsseCerts,0,temp,0,jsseCerts.length);
- temp[jsseCerts.length] = cert;
- jsseCerts = temp;
- }
- }
- } catch (java.security.cert.CertificateException e) {
- getLog().error(sm.getString("ajpprocessor.certs.fail"), e);
- return;
- } catch (NoSuchProviderException e) {
- getLog().error(sm.getString("ajpprocessor.certs.fail"), e);
- return;
- }
- request.setAttribute(SSLSupport.CERTIFICATE_KEY, jsseCerts);
- }
- break;
- }
- case REQ_SSL_CERTIFICATE: {
- // NO-OP. Can't force a new SSL handshake with the client when using
- // AJP as the reverse proxy controls that connection.
- break;
- }
- case REQ_HOST_ATTRIBUTE: {
- // Get remote host name using a DNS resolution
- if (request.remoteHost().isNull()) {
- try {
- request.remoteHost().setString(InetAddress.getByName
- (request.remoteAddr().toString()).getHostName());
- } catch (IOException iex) {
- // Ignore
- }
- }
- break;
- }
- case REQ_HOST_ADDR_ATTRIBUTE: {
- // NO-OP
- // Automatically populated during prepareRequest()
- break;
- }
- case REQ_LOCAL_NAME_ATTRIBUTE: {
- // NO-OP
- // Automatically populated during prepareRequest()
- break;
- }
- case REQ_LOCAL_ADDR_ATTRIBUTE: {
- // Automatically populated during prepareRequest() when using
- // modern AJP forwarder, otherwise copy from local name
- if (request.localAddr().isNull()) {
- request.localAddr().setString(request.localName().toString());
- }
- break;
- }
- case REQ_REMOTEPORT_ATTRIBUTE: {
- // NO-OP
- // Automatically populated during prepareRequest() when using
- // modern AJP forwarder, otherwise not available
- break;
- }
- case REQ_LOCALPORT_ATTRIBUTE: {
- // NO-OP
- // Automatically populated during prepareRequest()
- break;
- }
- case REQ_SET_BODY_REPLAY: {
- // Set the given bytes as the content
- ByteChunk bc = (ByteChunk) param;
- int length = bc.getLength();
- bodyBytes.setBytes(bc.getBytes(), bc.getStart(), length);
- request.setContentLength(length);
- first = false;
- empty = false;
- replay = true;
- endOfStream = false;
- break;
- }
- case ASYNC_START: {
- asyncStateMachine.asyncStart((AsyncContextCallback) param);
- // Async time out is based on SocketWrapper access time
- getSocketWrapper().access();
- break;
- }
- case ASYNC_COMPLETE: {
- socketWrapper.clearDispatches();
- if (asyncStateMachine.asyncComplete()) {
- endpoint.processSocket(socketWrapper, SocketStatus.OPEN_READ, true);
- }
- break;
- }
- case ASYNC_DISPATCH: {
- if (asyncStateMachine.asyncDispatch()) {
- endpoint.processSocket(socketWrapper, SocketStatus.OPEN_READ, true);
- }
- break;
- }
- case ASYNC_DISPATCHED: {
- asyncStateMachine.asyncDispatched();
- break;
- }
- case ASYNC_SETTIMEOUT: {
- if (param == null) return;
- long timeout = ((Long)param).longValue();
- socketWrapper.setTimeout(timeout);
- break;
- }
- case ASYNC_TIMEOUT: {
- AtomicBoolean result = (AtomicBoolean) param;
- result.set(asyncStateMachine.asyncTimeout());
- break;
- }
- case ASYNC_RUN: {
- asyncStateMachine.asyncRun((Runnable) param);
- break;
- }
- case ASYNC_ERROR: {
- asyncStateMachine.asyncError();
- break;
- }
- case ASYNC_IS_STARTED: {
- ((AtomicBoolean) param).set(asyncStateMachine.isAsyncStarted());
- break;
- }
- case ASYNC_IS_COMPLETING: {
- ((AtomicBoolean) param).set(asyncStateMachine.isCompleting());
- break;
- }
- case ASYNC_IS_DISPATCHING: {
- ((AtomicBoolean) param).set(asyncStateMachine.isAsyncDispatching());
- break;
- }
- case ASYNC_IS_ASYNC: {
- ((AtomicBoolean) param).set(asyncStateMachine.isAsync());
- break;
- }
- case ASYNC_IS_TIMINGOUT: {
- ((AtomicBoolean) param).set(asyncStateMachine.isAsyncTimingOut());
- break;
- }
- case ASYNC_IS_ERROR: {
- ((AtomicBoolean) param).set(asyncStateMachine.isAsyncError());
- break;
- }
- case ASYNC_POST_PROCESS: {
- asyncStateMachine.asyncPostProcess();
- break;
- }
- case UPGRADE: {
- // HTTP connections only. Unsupported for AJP.
- throw new UnsupportedOperationException(
- sm.getString("ajpprocessor.httpupgrade.notsupported"));
- }
- case COMET_BEGIN: {
- // HTTP connections only. Unsupported for AJP.
- throw new UnsupportedOperationException(
- sm.getString("ajpprocessor.comet.notsupported"));
- }
- case COMET_END: {
- // HTTP connections only. Unsupported for AJP.
- throw new UnsupportedOperationException(
- sm.getString("ajpprocessor.comet.notsupported"));
- }
- case COMET_CLOSE: {
- // HTTP connections only. Unsupported for AJP.
- throw new UnsupportedOperationException(
- sm.getString("ajpprocessor.comet.notsupported"));
- }
- case COMET_SETTIMEOUT: {
- // HTTP connections only. Unsupported for AJP.
- throw new UnsupportedOperationException(
- sm.getString("ajpprocessor.comet.notsupported"));
- }
- case IS_COMET: {
- // HTTP connections only. Unsupported for AJP.
- AtomicBoolean result = (AtomicBoolean) param;
- result.set(false);
- break;
- }
- case AVAILABLE: {
- if (available()) {
- request.setAvailable(1);
- } else {
- request.setAvailable(0);
- }
- break;
- }
- case NB_READ_INTEREST: {
- if (!endOfStream) {
- registerForEvent(true, false);
- }
- break;
- }
- case NB_WRITE_INTEREST: {
- AtomicBoolean isReady = (AtomicBoolean)param;
- boolean result = bufferedWrites.size() == 0 && responseMsgPos == -1;
- isReady.set(result);
- if (!result) {
- registerForEvent(false, true);
- }
- break;
- }
- case REQUEST_BODY_FULLY_READ: {
- AtomicBoolean result = (AtomicBoolean) param;
- result.set(endOfStream);
- break;
- }
- case DISPATCH_READ: {
- socketWrapper.addDispatch(DispatchType.NON_BLOCKING_READ);
- break;
- }
- case DISPATCH_WRITE: {
- socketWrapper.addDispatch(DispatchType.NON_BLOCKING_WRITE);
- break;
- }
- case DISPATCH_EXECUTE: {
- getEndpoint().executeNonBlockingDispatches(socketWrapper);
- break;
- }
- case CLOSE_NOW: {
- // Prevent further writes to the response
- swallowResponse = true;
- if (param instanceof Throwable) {
- setErrorState(ErrorState.CLOSE_NOW, (Throwable) param);
- } else {
- setErrorState(ErrorState.CLOSE_NOW, null);
- }
- break;
- }
- case END_REQUEST: {
- // NO-OP for AJP
- break;
- }
- }
- }
-
-
- @Override
- public SocketState asyncDispatch(SocketStatus status) {
-
- if (status == SocketStatus.OPEN_WRITE && response.getWriteListener() != null) {
- try {
- asyncStateMachine.asyncOperation();
- try {
- if (hasDataToWrite()) {
- flushBufferedData();
- if (hasDataToWrite()) {
- // There is data to write but go via Response to
- // maintain a consistent view of non-blocking state
- response.checkRegisterForWrite();
- return SocketState.LONG;
- }
- }
- } catch (IOException x) {
- if (getLog().isDebugEnabled()) {
- getLog().debug("Unable to write async data.",x);
- }
- status = SocketStatus.ERROR;
- request.setAttribute(RequestDispatcher.ERROR_EXCEPTION, x);
- }
- } catch (IllegalStateException x) {
- registerForEvent(false, true);
- }
- } else if (status == SocketStatus.OPEN_READ && request.getReadListener() != null) {
- try {
- if (available()) {
- asyncStateMachine.asyncOperation();
- }
- } catch (IllegalStateException x) {
- registerForEvent(true, false);
- }
- }
-
- RequestInfo rp = request.getRequestProcessor();
- try {
- rp.setStage(org.apache.coyote.Constants.STAGE_SERVICE);
- if(!getAdapter().asyncDispatch(request, response, status)) {
- setErrorState(ErrorState.CLOSE_NOW, null);
- }
- resetTimeouts();
- } catch (InterruptedIOException e) {
- setErrorState(ErrorState.CLOSE_NOW, e);
- } catch (Throwable t) {
- ExceptionUtils.handleThrowable(t);
- setErrorState(ErrorState.CLOSE_NOW, t);
- getLog().error(sm.getString("http11processor.request.process"), t);
- }
-
- rp.setStage(org.apache.coyote.Constants.STAGE_ENDED);
-
- if (isAsync()) {
- if (getErrorState().isError()) {
- request.updateCounters();
- return SocketState.CLOSED;
- } else {
- return SocketState.LONG;
- }
- } else {
- request.updateCounters();
- if (getErrorState().isError()) {
- return SocketState.CLOSED;
- } else {
- recycle(false);
- return SocketState.OPEN;
- }
- }
- }
-
-
- /**
- * Process pipelined HTTP requests using the specified input and output
- * streams.
- *
- * @throws IOException error during an I/O operation
- */
- @Override
- public SocketState process(SocketWrapper<S> socket) throws IOException {
-
- RequestInfo rp = request.getRequestProcessor();
- rp.setStage(org.apache.coyote.Constants.STAGE_PARSE);
-
- // Setting up the socket
- this.socketWrapper = socket;
-
- setupSocket(socket);
-
- int soTimeout = endpoint.getSoTimeout();
- boolean cping = false;
-
- boolean keptAlive = false;
-
- while (!getErrorState().isError() && !endpoint.isPaused()) {
- // Parsing the request header
- try {
- // Get first message of the request
- if (!readMessage(requestHeaderMessage, !keptAlive)) {
- break;
- }
- // Set back timeout if keep alive timeout is enabled
- if (keepAliveTimeout > 0) {
- setTimeout(socketWrapper, soTimeout);
- }
- // Check message type, process right away and break if
- // not regular request processing
- int type = requestHeaderMessage.getByte();
- if (type == Constants.JK_AJP13_CPING_REQUEST) {
- if (endpoint.isPaused()) {
- recycle(true);
- break;
- }
- cping = true;
- try {
- output(pongMessageArray, 0, pongMessageArray.length, true);
- } catch (IOException e) {
- setErrorState(ErrorState.CLOSE_NOW, e);
- }
- recycle(false);
- continue;
- } else if(type != Constants.JK_AJP13_FORWARD_REQUEST) {
- // Unexpected packet type. Unread body packets should have
- // been swallowed in finish().
- if (getLog().isDebugEnabled()) {
- getLog().debug("Unexpected message: " + type);
- }
- setErrorState(ErrorState.CLOSE_NOW, null);
- break;
- }
- keptAlive = true;
- request.setStartTime(System.currentTimeMillis());
- } catch (IOException e) {
- setErrorState(ErrorState.CLOSE_NOW, e);
- break;
- } catch (Throwable t) {
- ExceptionUtils.handleThrowable(t);
- getLog().debug(sm.getString("ajpprocessor.header.error"), t);
- // 400 - Bad Request
- response.setStatus(400);
- setErrorState(ErrorState.CLOSE_CLEAN, t);
- getAdapter().log(request, response, 0);
- }
-
- if (!getErrorState().isError()) {
- // Setting up filters, and parse some request headers
- rp.setStage(org.apache.coyote.Constants.STAGE_PREPARE);
- try {
- prepareRequest();
- } catch (Throwable t) {
- ExceptionUtils.handleThrowable(t);
- getLog().debug(sm.getString("ajpprocessor.request.prepare"), t);
- // 500 - Internal Server Error
- response.setStatus(500);
- setErrorState(ErrorState.CLOSE_CLEAN, t);
- getAdapter().log(request, response, 0);
- }
- }
-
- if (!getErrorState().isError() && !cping && endpoint.isPaused()) {
- // 503 - Service unavailable
- response.setStatus(503);
- setErrorState(ErrorState.CLOSE_CLEAN, null);
- getAdapter().log(request, response, 0);
- }
- cping = false;
-
- // Process the request in the adapter
- if (!getErrorState().isError()) {
- try {
- rp.setStage(org.apache.coyote.Constants.STAGE_SERVICE);
- getAdapter().service(request, response);
- } catch (InterruptedIOException e) {
- setErrorState(ErrorState.CLOSE_NOW, e);
- } catch (Throwable t) {
- ExceptionUtils.handleThrowable(t);
- getLog().error(sm.getString("ajpprocessor.request.process"), t);
- // 500 - Internal Server Error
- response.setStatus(500);
- setErrorState(ErrorState.CLOSE_CLEAN, t);
- getAdapter().log(request, response, 0);
- }
- }
-
- if (isAsync() && !getErrorState().isError()) {
- break;
- }
-
- // Finish the response if not done yet
- if (!finished && getErrorState().isIoAllowed()) {
- try {
- finish();
- } catch (Throwable t) {
- ExceptionUtils.handleThrowable(t);
- setErrorState(ErrorState.CLOSE_NOW, t);
- }
- }
-
- // If there was an error, make sure the request is counted as
- // and error, and update the statistics counter
- if (getErrorState().isError()) {
- response.setStatus(500);
- }
- request.updateCounters();
-
- rp.setStage(org.apache.coyote.Constants.STAGE_KEEPALIVE);
- // Set keep alive timeout if enabled
- if (keepAliveTimeout > 0) {
- setTimeout(socketWrapper, keepAliveTimeout);
- }
-
- recycle(false);
- }
-
- rp.setStage(org.apache.coyote.Constants.STAGE_ENDED);
-
- if (getErrorState().isError() || endpoint.isPaused()) {
- return SocketState.CLOSED;
- } else {
- if (isAsync()) {
- return SocketState.LONG;
- } else {
- return SocketState.OPEN;
- }
- }
- }
-
-
- @Override
- public void setSslSupport(SSLSupport sslSupport) {
- // Should never reach this code but in case we do...
- throw new IllegalStateException(
- sm.getString("ajpprocessor.ssl.notsupported"));
- }
-
-
- @Override
- public SocketState event(SocketStatus status) throws IOException {
- // Should never reach this code but in case we do...
- throw new IOException(
- sm.getString("ajpprocessor.comet.notsupported"));
- }
-
-
- @Override
- public SocketState upgradeDispatch(SocketStatus status) throws IOException {
- // Should never reach this code but in case we do...
- throw new IOException(
- sm.getString("ajpprocessor.httpupgrade.notsupported"));
- }
-
-
- @Override
- public UpgradeToken getUpgradeToken() {
- // Should never reach this code but in case we do...
- throw new IllegalStateException(
- sm.getString("ajpprocessor.httpupgrade.notsupported"));
- }
-
-
- /**
- * Recycle the processor, ready for the next request which may be on the
- * same connection or a different connection.
- *
- * @param socketClosing Indicates if the socket is about to be closed
- * allowing the processor to perform any additional
- * clean-up that may be required
- */
- @Override
- public void recycle(boolean socketClosing) {
- getAdapter().checkRecycled(request, response);
-
- asyncStateMachine.recycle();
-
- // Recycle Request object
- first = true;
- endOfStream = false;
- waitingForBodyMessage = false;
- empty = true;
- replay = false;
- finished = false;
- request.recycle();
- response.recycle();
- certificates.recycle();
- swallowResponse = false;
- bytesWritten = 0;
- resetErrorState();
- bufferedWrites.clear();
- }
-
-
- // ------------------------------------------------------ Protected Methods
-
- // Methods called by asyncDispatch
- /**
- * Provides a mechanism for those connector implementations (currently only
- * NIO) that need to reset timeouts from Async timeouts to standard HTTP
- * timeouts once async processing completes.
- */
- protected abstract void resetTimeouts();
-
- // Methods called by prepareResponse()
- protected abstract int output(byte[] src, int offset, int length,
- boolean block) throws IOException;
-
- // Methods called by process()
- protected abstract void setupSocket(SocketWrapper<S> socketWrapper)
- throws IOException;
-
- protected abstract void setTimeout(SocketWrapper<S> socketWrapper,
- int timeout) throws IOException;
-
- // Methods used by readMessage
- /**
- * Read at least the specified amount of bytes, and place them
- * in the input buffer. Note that if any data is available to read then this
- * method will always block until at least the specified number of bytes
- * have been read.
- *
- * @param buf Buffer to read data into
- * @param pos Start position
- * @param n The minimum number of bytes to read
- * @param block If there is no data available to read when this method is
- * called, should this call block until data becomes available?
- * @return <code>true</code> if the requested number of bytes were read
- * else <code>false</code>
- * @throws IOException
- */
- protected abstract boolean read(byte[] buf, int pos, int n, boolean block)
- throws IOException;
-
- // Methods used by SocketInputBuffer
- /**
- * Read an AJP body message. Used to read both the 'special' packet in ajp13
- * and to receive the data after we send a GET_BODY packet.
- *
- * @param block If there is no data available to read when this method is
- * called, should this call block until data becomes available?
- *
- * @return <code>true</code> if at least one body byte was read, otherwise
- * <code>false</code>
- */
- protected boolean receive(boolean block) throws IOException {
-
- bodyMessage.reset();
-
- if (!readMessage(bodyMessage, block)) {
- return false;
- }
-
- waitingForBodyMessage = false;
-
- // No data received.
- if (bodyMessage.getLen() == 0) {
- // just the header
- return false;
- }
- int blen = bodyMessage.peekInt();
- if (blen == 0) {
- return false;
- }
-
- bodyMessage.getBodyBytes(bodyBytes);
- empty = false;
- return true;
- }
-
-
- /**
- * Read an AJP message.
- *
- * @param message The message to populate
- * @param block If there is no data available to read when this method is
- * called, should this call block until data becomes available?
-
- * @return true if the message has been read, false if no data was read
- *
- * @throws IOException any other failure, including incomplete reads
- */
- protected boolean readMessage(AjpMessage message, boolean block)
- throws IOException {
-
- byte[] buf = message.getBuffer();
- int headerLength = message.getHeaderLength();
-
- if (!read(buf, 0, headerLength, block)) {
- return false;
- }
-
- int messageLength = message.processHeader(true);
- if (messageLength < 0) {
- // Invalid AJP header signature
- throw new IOException(sm.getString("ajpmessage.invalidLength",
- Integer.valueOf(messageLength)));
- }
- else if (messageLength == 0) {
- // Zero length message.
- return true;
- }
- else {
- if (messageLength > message.getBuffer().length) {
- // Message too long for the buffer
- // Need to trigger a 400 response
- throw new IllegalArgumentException(sm.getString(
- "ajpprocessor.header.tooLong",
- Integer.valueOf(messageLength),
- Integer.valueOf(buf.length)));
- }
- read(buf, headerLength, messageLength, true);
- return true;
- }
- }
-
-
- @Override
- public final boolean isComet() {
- // AJP does not support Comet
- return false;
- }
-
-
- @Override
- public final boolean isUpgrade() {
- // AJP does not support HTTP upgrade
- return false;
- }
-
-
- @Override
- public ByteBuffer getLeftoverInput() {
- return null;
- }
-
-
- /**
- * Get more request body data from the web server and store it in the
- * internal buffer.
- *
- * @return true if there is more data, false if not.
- */
- protected boolean refillReadBuffer(boolean block) throws IOException {
- // When using replay (e.g. after FORM auth) all the data to read has
- // been buffered so there is no opportunity to refill the buffer.
- if (replay) {
- endOfStream = true; // we've read everything there is
- }
- if (endOfStream) {
- return false;
- }
-
- if (first) {
- first = false;
- long contentLength = request.getContentLengthLong();
- // - When content length > 0, AJP sends the first body message
- // automatically.
- // - When content length == 0, AJP does not send a body message.
- // - When content length is unknown, AJP does not send the first
- // body message automatically.
- if (contentLength > 0) {
- waitingForBodyMessage = true;
- } else if (contentLength == 0) {
- endOfStream = true;
- return false;
- }
- }
-
- // Request more data immediately
- if (!waitingForBodyMessage) {
- output(getBodyMessageArray, 0, getBodyMessageArray.length, true);
- waitingForBodyMessage = true;
- }
-
- boolean moreData = receive(block);
- if (!moreData && !waitingForBodyMessage) {
- endOfStream = true;
- }
- return moreData;
- }
-
-
- /**
- * After reading the request headers, we have to setup the request filters.
- */
- protected void prepareRequest() {
-
- // Translate the HTTP method code to a String.
- byte methodCode = requestHeaderMessage.getByte();
- if (methodCode != Constants.SC_M_JK_STORED) {
- String methodName = Constants.getMethodForCode(methodCode - 1);
- request.method().setString(methodName);
- }
-
- requestHeaderMessage.getBytes(request.protocol());
- requestHeaderMessage.getBytes(request.requestURI());
-
- requestHeaderMessage.getBytes(request.remoteAddr());
- requestHeaderMessage.getBytes(request.remoteHost());
- requestHeaderMessage.getBytes(request.localName());
- request.setLocalPort(requestHeaderMessage.getInt());
-
- boolean isSSL = requestHeaderMessage.getByte() != 0;
- if (isSSL) {
- request.scheme().setString("https");
- }
-
- // Decode headers
- MimeHeaders headers = request.getMimeHeaders();
-
- // Set this every time in case limit has been changed via JMX
- headers.setLimit(endpoint.getMaxHeaderCount());
- request.getCookies().setLimit(getMaxCookieCount());
-
- boolean contentLengthSet = false;
- int hCount = requestHeaderMessage.getInt();
- for(int i = 0 ; i < hCount ; i++) {
- String hName = null;
-
- // Header names are encoded as either an integer code starting
- // with 0xA0, or as a normal string (in which case the first
- // two bytes are the length).
- int isc = requestHeaderMessage.peekInt();
- int hId = isc & 0xFF;
-
- MessageBytes vMB = null;
- isc &= 0xFF00;
- if(0xA000 == isc) {
- requestHeaderMessage.getInt(); // To advance the read position
- hName = Constants.getHeaderForCode(hId - 1);
- vMB = headers.addValue(hName);
- } else {
- // reset hId -- if the header currently being read
- // happens to be 7 or 8 bytes long, the code below
- // will think it's the content-type header or the
- // content-length header - SC_REQ_CONTENT_TYPE=7,
- // SC_REQ_CONTENT_LENGTH=8 - leading to unexpected
- // behaviour. see bug 5861 for more information.
- hId = -1;
- requestHeaderMessage.getBytes(tmpMB);
- ByteChunk bc = tmpMB.getByteChunk();
- vMB = headers.addValue(bc.getBuffer(),
- bc.getStart(), bc.getLength());
- }
-
- requestHeaderMessage.getBytes(vMB);
-
- if (hId == Constants.SC_REQ_CONTENT_LENGTH ||
- (hId == -1 && tmpMB.equalsIgnoreCase("Content-Length"))) {
- long cl = vMB.getLong();
- if (contentLengthSet) {
- response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
- setErrorState(ErrorState.CLOSE_CLEAN, null);
- } else {
- contentLengthSet = true;
- // Set the content-length header for the request
- request.setContentLength(cl);
- }
- } else if (hId == Constants.SC_REQ_CONTENT_TYPE ||
- (hId == -1 && tmpMB.equalsIgnoreCase("Content-Type"))) {
- // just read the content-type header, so set it
- ByteChunk bchunk = vMB.getByteChunk();
- request.contentType().setBytes(bchunk.getBytes(),
- bchunk.getOffset(),
- bchunk.getLength());
- }
- }
-
- // Decode extra attributes
- boolean secret = false;
- byte attributeCode;
- while ((attributeCode = requestHeaderMessage.getByte())
- != Constants.SC_A_ARE_DONE) {
-
- switch (attributeCode) {
-
- case Constants.SC_A_REQ_ATTRIBUTE :
- requestHeaderMessage.getBytes(tmpMB);
- String n = tmpMB.toString();
- requestHeaderMessage.getBytes(tmpMB);
- String v = tmpMB.toString();
- /*
- * AJP13 misses to forward the local IP address and the
- * remote port. Allow the AJP connector to add this info via
- * private request attributes.
- * We will accept the forwarded data and remove it from the
- * public list of request attributes.
- */
- if(n.equals(Constants.SC_A_REQ_LOCAL_ADDR)) {
- request.localAddr().setString(v);
- } else if(n.equals(Constants.SC_A_REQ_REMOTE_PORT)) {
- try {
- request.setRemotePort(Integer.parseInt(v));
- } catch (NumberFormatException nfe) {
- // Ignore invalid value
- }
- } else if(n.equals(Constants.SC_A_SSL_PROTOCOL)) {
- request.setAttribute(SSLSupport.PROTOCOL_VERSION_KEY, v);
- } else {
- request.setAttribute(n, v );
- }
- break;
-
- case Constants.SC_A_CONTEXT :
- requestHeaderMessage.getBytes(tmpMB);
- // nothing
- break;
-
- case Constants.SC_A_SERVLET_PATH :
- requestHeaderMessage.getBytes(tmpMB);
- // nothing
- break;
-
- case Constants.SC_A_REMOTE_USER :
- if (tomcatAuthorization || !tomcatAuthentication) {
- // Implies tomcatAuthentication == false
- requestHeaderMessage.getBytes(request.getRemoteUser());
- request.setRemoteUserNeedsAuthorization(tomcatAuthorization);
- } else {
- // Ignore user information from reverse proxy
- requestHeaderMessage.getBytes(tmpMB);
- }
- break;
-
- case Constants.SC_A_AUTH_TYPE :
- if (tomcatAuthentication) {
- // ignore server
- requestHeaderMessage.getBytes(tmpMB);
- } else {
- requestHeaderMessage.getBytes(request.getAuthType());
- }
- break;
-
- case Constants.SC_A_QUERY_STRING :
- requestHeaderMessage.getBytes(request.queryString());
- break;
-
- case Constants.SC_A_JVM_ROUTE :
- requestHeaderMessage.getBytes(request.instanceId());
- break;
-
- case Constants.SC_A_SSL_CERT :
- // SSL certificate extraction is lazy, moved to JkCoyoteHandler
- requestHeaderMessage.getBytes(certificates);
- break;
-
- case Constants.SC_A_SSL_CIPHER :
- requestHeaderMessage.getBytes(tmpMB);
- request.setAttribute(SSLSupport.CIPHER_SUITE_KEY,
- tmpMB.toString());
- break;
-
- case Constants.SC_A_SSL_SESSION :
- requestHeaderMessage.getBytes(tmpMB);
- request.setAttribute(SSLSupport.SESSION_ID_KEY,
- tmpMB.toString());
- break;
-
- case Constants.SC_A_SSL_KEY_SIZE :
- request.setAttribute(SSLSupport.KEY_SIZE_KEY,
- Integer.valueOf(requestHeaderMessage.getInt()));
- break;
-
- case Constants.SC_A_STORED_METHOD:
- requestHeaderMessage.getBytes(request.method());
- break;
-
- case Constants.SC_A_SECRET:
- requestHeaderMessage.getBytes(tmpMB);
- if (requiredSecret != null) {
- secret = true;
- if (!tmpMB.equals(requiredSecret)) {
- response.setStatus(403);
- setErrorState(ErrorState.CLOSE_CLEAN, null);
- }
- }
- break;
-
- default:
- // Ignore unknown attribute for backward compatibility
- break;
-
- }
-
- }
-
- // Check if secret was submitted if required
- if ((requiredSecret != null) && !secret) {
- response.setStatus(403);
- setErrorState(ErrorState.CLOSE_CLEAN, null);
- }
-
- // Check for a full URI (including protocol://host:port/)
- ByteChunk uriBC = request.requestURI().getByteChunk();
- if (uriBC.startsWithIgnoreCase("http", 0)) {
-
- 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);
- if (slashPos == -1) {
- slashPos = uriBC.getLength();
- // Set URI as "/"
- request.requestURI().setBytes
- (uriB, uriBCStart + pos + 1, 1);
- } else {
- request.requestURI().setBytes
- (uriB, uriBCStart + slashPos,
- uriBC.getLength() - slashPos);
- }
- MessageBytes hostMB = headers.setValue("host");
- hostMB.setBytes(uriB, uriBCStart + pos + 3,
- slashPos - pos - 3);
- }
-
- }
-
- MessageBytes valueMB = request.getMimeHeaders().getValue("host");
- parseHost(valueMB);
-
- if (getErrorState().isError()) {
- getAdapter().log(request, response, 0);
- }
- }
-
-
- /**
- * Parse host.
- */
- protected void parseHost(MessageBytes valueMB) {
-
- if (valueMB == null || valueMB.isNull()) {
- // HTTP/1.0
- request.setServerPort(request.getLocalPort());
- try {
- request.serverName().duplicate(request.localName());
- } catch (IOException e) {
- response.setStatus(400);
- setErrorState(ErrorState.CLOSE_CLEAN, e);
- }
- return;
- }
-
- ByteChunk valueBC = valueMB.getByteChunk();
- byte[] valueB = valueBC.getBytes();
- int valueL = valueBC.getLength();
- int valueS = valueBC.getStart();
- int colonPos = -1;
- if (hostNameC.length < valueL) {
- hostNameC = new char[valueL];
- }
-
- boolean ipv6 = (valueB[valueS] == '[');
- boolean bracketClosed = false;
- for (int i = 0; i < valueL; i++) {
- char b = (char) valueB[i + valueS];
- hostNameC[i] = b;
- if (b == ']') {
- bracketClosed = true;
- } else if (b == ':') {
- if (!ipv6 || bracketClosed) {
- colonPos = i;
- break;
- }
- }
- }
-
- if (colonPos < 0) {
- if (request.scheme().equalsIgnoreCase("https")) {
- // 443 - Default HTTPS port
- request.setServerPort(443);
- } else {
- // 80 - Default HTTTP port
- request.setServerPort(80);
- }
- request.serverName().setChars(hostNameC, 0, valueL);
- } else {
-
- request.serverName().setChars(hostNameC, 0, colonPos);
-
- int port = 0;
- int mult = 1;
- for (int i = valueL - 1; i > colonPos; i--) {
- int charValue = HexUtils.getDec(valueB[i + valueS]);
- if (charValue == -1) {
- // Invalid character
- // 400 - Bad request
- response.setStatus(400);
- setErrorState(ErrorState.CLOSE_CLEAN, null);
- break;
- }
- port = port + (charValue * mult);
- mult = 10 * mult;
- }
- request.setServerPort(port);
- }
- }
-
-
- /**
- * When committing the response, we have to validate the set of headers, as
- * well as setup the response filters.
- */
- protected void prepareResponse() throws IOException {
-
- response.setCommitted(true);
-
- tmpMB.recycle();
- responseMsgPos = -1;
- responseMessage.reset();
- responseMessage.appendByte(Constants.JK_AJP13_SEND_HEADERS);
-
- // Responses with certain status codes are not permitted to include a
- // response body.
- int statusCode = response.getStatus();
- if (statusCode < 200 || statusCode == 204 || statusCode == 205 ||
- statusCode == 304) {
- // No entity body
- swallowResponse = true;
- }
-
- // Responses to HEAD requests are not permitted to include a response
- // body.
- MessageBytes methodMB = request.method();
- if (methodMB.equals("HEAD")) {
- // No entity body
- swallowResponse = true;
- }
-
- // HTTP header contents
- responseMessage.appendInt(statusCode);
- String message = null;
- if (org.apache.coyote.Constants.USE_CUSTOM_STATUS_MSG_IN_HEADER &&
- HttpMessages.isSafeInHttpHeader(response.getMessage())) {
- message = response.getMessage();
- }
- if (message == null){
- message = HttpMessages.getInstance(
- response.getLocale()).getMessage(response.getStatus());
- }
- if (message == null) {
- // mod_jk + httpd 2.x fails with a null status message - bug 45026
- message = Integer.toString(response.getStatus());
- }
- tmpMB.setString(message);
- responseMessage.appendBytes(tmpMB);
-
- // Special headers
- MimeHeaders headers = response.getMimeHeaders();
- String contentType = response.getContentType();
- if (contentType != null) {
- headers.setValue("Content-Type").setString(contentType);
- }
- String contentLanguage = response.getContentLanguage();
- if (contentLanguage != null) {
- headers.setValue("Content-Language").setString(contentLanguage);
- }
- long contentLength = response.getContentLengthLong();
- if (contentLength >= 0) {
- headers.setValue("Content-Length").setLong(contentLength);
- }
-
- // Other headers
- int numHeaders = headers.size();
- responseMessage.appendInt(numHeaders);
- for (int i = 0; i < numHeaders; i++) {
- MessageBytes hN = headers.getName(i);
- int hC = Constants.getResponseAjpIndex(hN.toString());
- if (hC > 0) {
- responseMessage.appendInt(hC);
- }
- else {
- responseMessage.appendBytes(hN);
- }
- MessageBytes hV=headers.getValue(i);
- responseMessage.appendBytes(hV);
- }
-
- // Write to buffer
- responseMessage.end();
- output(responseMessage.getBuffer(), 0, responseMessage.getLen(), true);
- }
-
-
- /**
- * Callback to write data from the buffer.
- */
- protected void flush(boolean explicit) throws IOException {
- // Calling code should ensure that there is no data in the buffers for
- // non-blocking writes.
- // TODO Validate the assertion above
- if (ajpFlush && explicit && !finished) {
- // Send the flush message
- output(flushMessageArray, 0, flushMessageArray.length, true);
- }
- }
-
-
- /**
- * Finish AJP response.
- */
- protected void finish() throws IOException {
-
- if (!response.isCommitted()) {
- // Validate and write response headers
- try {
- prepareResponse();
- } catch (IOException e) {
- setErrorState(ErrorState.CLOSE_NOW, e);
- return;
- }
- }
-
- if (finished)
- return;
-
- finished = true;
-
- // Swallow the unread body packet if present
- if (waitingForBodyMessage || first && request.getContentLengthLong() > 0) {
- refillReadBuffer(true);
- }
-
- // Add the end message
- if (getErrorState().isError()) {
- output(endAndCloseMessageArray, 0, endAndCloseMessageArray.length, true);
- } else {
- output(endMessageArray, 0, endMessageArray.length, true);
- }
- }
-
-
- private boolean available() {
- if (endOfStream) {
- return false;
- }
- if (empty) {
- 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.
- return true;
- }
- }
- return !empty;
- }
-
-
- private void writeData(ByteChunk chunk) throws IOException {
- // Prevent timeout
- socketWrapper.access();
-
- boolean blocking = (response.getWriteListener() == null);
- if (!blocking) {
- flushBufferedData();
- }
-
- int len = chunk.getLength();
- int off = 0;
-
- // Write this chunk
- while (responseMsgPos == -1 && len > 0) {
- int thisTime = len;
- if (thisTime > outputMaxChunkSize) {
- thisTime = outputMaxChunkSize;
- }
- responseMessage.reset();
- responseMessage.appendByte(Constants.JK_AJP13_SEND_BODY_CHUNK);
- responseMessage.appendBytes(chunk.getBytes(), chunk.getOffset() + off, thisTime);
- responseMessage.end();
- writeResponseMessage(blocking);
-
- len -= thisTime;
- off += thisTime;
- }
-
- bytesWritten += off;
-
- if (len > 0 && !blocking) {
- // Add this chunk to the buffer
- addToBuffers(chunk.getBuffer(), off, len);
- }
- }
-
-
- private void addToBuffers(byte[] buf, int offset, int length) {
- ByteBufferHolder holder = bufferedWrites.peekLast();
- if (holder == null || holder.isFlipped() || holder.getBuf().remaining() < length) {
- ByteBuffer buffer = ByteBuffer.allocate(Math.max(bufferedWriteSize,length));
- holder = new ByteBufferHolder(buffer, false);
- bufferedWrites.add(holder);
- }
- holder.getBuf().put(buf, offset, length);
- }
-
-
- private boolean hasDataToWrite() {
- return responseMsgPos != -1 || bufferedWrites.size() > 0;
- }
-
-
- private void flushBufferedData() throws IOException {
-
- if (responseMsgPos > -1) {
- // Must be using non-blocking IO
- // Partially written response message. Try and complete it.
- writeResponseMessage(false);
- }
-
- while (responseMsgPos == -1 && bufferedWrites.size() > 0) {
- // Try and write any remaining buffer data
- Iterator<ByteBufferHolder> holders = bufferedWrites.iterator();
- ByteBufferHolder holder = holders.next();
- holder.flip();
- ByteBuffer buffer = holder.getBuf();
- int initialBufferSize = buffer.remaining();
- while (responseMsgPos == -1 && buffer.remaining() > 0) {
- transferToResponseMsg(buffer);
- writeResponseMessage(false);
- }
- bytesWritten += (initialBufferSize - buffer.remaining());
- if (buffer.remaining() == 0) {
- holders.remove();
- }
- }
- }
-
-
- private void transferToResponseMsg(ByteBuffer buffer) {
-
- int thisTime = buffer.remaining();
- if (thisTime > outputMaxChunkSize) {
- thisTime = outputMaxChunkSize;
- }
-
- responseMessage.reset();
- responseMessage.appendByte(Constants.JK_AJP13_SEND_BODY_CHUNK);
- buffer.get(responseMessage.getBuffer(), responseMessage.pos, thisTime);
- responseMessage.end();
- }
-
-
- private void writeResponseMessage(boolean block) throws IOException {
- int len = responseMessage.getLen();
- int written = 1;
- if (responseMsgPos == -1) {
- // New message. Advance the write position to the beginning
- responseMsgPos = 0;
- }
-
- while (written > 0 && responseMsgPos < len) {
- written = output(
- responseMessage.getBuffer(), responseMsgPos, len - responseMsgPos, block);
- responseMsgPos += written;
- }
-
- // Message fully written, reset the position for a new message.
- if (responseMsgPos == len) {
- responseMsgPos = -1;
- }
- }
-
- // ------------------------------------- InputStreamInputBuffer Inner Class
-
-
- /**
- * This class is an input buffer which will read its data from an input
- * stream.
- */
- protected class SocketInputBuffer implements InputBuffer {
-
- /**
- * Read bytes into the specified chunk.
- */
- @Override
- public int doRead(ByteChunk chunk, Request req) throws IOException {
-
- if (endOfStream) {
- return -1;
- }
- if (empty) {
- if (!refillReadBuffer(true)) {
- return -1;
- }
- }
- ByteChunk bc = bodyBytes.getByteChunk();
- chunk.setBytes(bc.getBuffer(), bc.getStart(), bc.getLength());
- empty = true;
- return chunk.getLength();
- }
- }
-
-
- // ----------------------------------- OutputStreamOutputBuffer Inner Class
-
- /**
- * This class is an output buffer which will write data to an output
- * stream.
- */
- protected class SocketOutputBuffer implements OutputBuffer {
-
- /**
- * Write chunk.
- */
- @Override
- public int doWrite(ByteChunk chunk, Response res) throws IOException {
-
- if (!response.isCommitted()) {
- // Validate and write response headers
- try {
- prepareResponse();
- } catch (IOException e) {
- setErrorState(ErrorState.CLOSE_NOW, e);
- }
- }
-
- if (!swallowResponse) {
- try {
- writeData(chunk);
- } catch (IOException ioe) {
- response.action(ActionCode.CLOSE_NOW, ioe);
- // Re-throw
- throw ioe;
- }
- }
- return chunk.getLength();
- }
-
- @Override
- public long getBytesWritten() {
- return bytesWritten;
- }
- }
-}
diff --git a/java/org/apache/coyote/ajp/AbstractAjpProtocol.java b/java/org/apache/coyote/ajp/AbstractAjpProtocol.java
index 37ca61f..8eac1b7 100644
--- a/java/org/apache/coyote/ajp/AbstractAjpProtocol.java
+++ b/java/org/apache/coyote/ajp/AbstractAjpProtocol.java
@@ -16,21 +16,40 @@
*/
package org.apache.coyote.ajp;
-import java.nio.ByteBuffer;
-
import org.apache.coyote.AbstractProtocol;
import org.apache.coyote.Processor;
+import org.apache.coyote.UpgradeProtocol;
import org.apache.coyote.UpgradeToken;
-import org.apache.tomcat.util.net.SocketWrapper;
+import org.apache.tomcat.util.net.AbstractEndpoint;
+import org.apache.tomcat.util.net.SSLHostConfig;
+import org.apache.tomcat.util.net.SocketWrapperBase;
import org.apache.tomcat.util.res.StringManager;
+/**
+ * The is 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.
+ *
+ * @param <S> The type of socket used by the implementation
+ */
public abstract class AbstractAjpProtocol<S> extends AbstractProtocol<S> {
/**
* The string manager for this package.
*/
- protected static final StringManager sm =
- StringManager.getManager(Constants.Package);
+ protected static final StringManager sm = StringManager.getManager(AbstractAjpProtocol.class);
+
+
+ public AbstractAjpProtocol(AbstractEndpoint<S> endpoint) {
+ super(endpoint);
+ setSoTimeout(Constants.DEFAULT_CONNECTION_TIMEOUT);
+ // AJP does not use Send File
+ getEndpoint().setUseSendfile(false);
+ ConnectionHandler<S> cHandler = new ConnectionHandler<>(this);
+ setHandler(cHandler);
+ getEndpoint().setHandler(cHandler);
+ }
@Override
@@ -39,6 +58,37 @@ public abstract class AbstractAjpProtocol<S> extends AbstractProtocol<S> {
}
+ /**
+ * {@inheritDoc}
+ *
+ * Overridden to make getter accessible to other classes in this package.
+ */
+ @Override
+ protected AbstractEndpoint<S> getEndpoint() {
+ return super.getEndpoint();
+ }
+
+
+ /**
+ * {@inheritDoc}
+ *
+ * AJP does not support protocol negotiation so this always returns null.
+ */
+ @Override
+ protected UpgradeProtocol getNegotiatedProtocol(String name) {
+ return null;
+ }
+
+
+ /**
+ * {@inheritDoc}
+ *
+ * AJP does not support protocol upgrade so this always returns null.
+ */
+ @Override
+ protected UpgradeProtocol getUpgradeProtocol(String name) {
+ return null;
+ }
// ------------------------------------------------- AJP specific properties
// ------------------------------------------ managed in the ProtocolHandler
@@ -65,7 +115,7 @@ public abstract class AbstractAjpProtocol<S> extends AbstractProtocol<S> {
* Should authentication be done in the native web server layer,
* or in the Servlet container ?
*/
- protected boolean tomcatAuthentication = true;
+ private boolean tomcatAuthentication = true;
public boolean getTomcatAuthentication() { return tomcatAuthentication; }
public void setTomcatAuthentication(boolean tomcatAuthentication) {
this.tomcatAuthentication = tomcatAuthentication;
@@ -86,7 +136,7 @@ public abstract class AbstractAjpProtocol<S> extends AbstractProtocol<S> {
/**
* Required secret.
*/
- protected String requiredSecret = null;
+ private String requiredSecret = null;
public void setRequiredSecret(String requiredSecret) {
this.requiredSecret = requiredSecret;
}
@@ -95,7 +145,7 @@ public abstract class AbstractAjpProtocol<S> extends AbstractProtocol<S> {
/**
* AJP packet size.
*/
- protected int packetSize = Constants.MAX_PACKET_SIZE;
+ private int packetSize = Constants.MAX_PACKET_SIZE;
public int getPacketSize() { return packetSize; }
public void setPacketSize(int packetSize) {
if(packetSize < Constants.MAX_PACKET_SIZE) {
@@ -105,7 +155,36 @@ public abstract class AbstractAjpProtocol<S> extends AbstractProtocol<S> {
}
}
- protected void configureProcessor(AbstractAjpProcessor<S> processor) {
+
+ // --------------------------------------------- SSL is not supported in AJP
+
+ @Override
+ public void addSslHostConfig(SSLHostConfig sslHostConfig) {
+ getLog().warn(sm.getString("ajpprotocol.noSSL", sslHostConfig.getHostName()));
+ }
+
+
+ @Override
+ public SSLHostConfig[] findSslHostConfigs() {
+ return new SSLHostConfig[0];
+ }
+
+
+ @Override
+ public void addUpgradeProtocol(UpgradeProtocol upgradeProtocol) {
+ getLog().warn(sm.getString("ajpprotocol.noUpgrade", upgradeProtocol.getClass().getName()));
+ }
+
+
+ @Override
+ public UpgradeProtocol[] findUpgradeProtocols() {
+ return new UpgradeProtocol[0];
+ }
+
+
+ @Override
+ protected Processor createProcessor() {
+ AjpProcessor processor = new AjpProcessor(getPacketSize(), getEndpoint());
processor.setAdapter(getAdapter());
processor.setAjpFlush(getAjpFlush());
processor.setTomcatAuthentication(getTomcatAuthentication());
@@ -113,29 +192,14 @@ public abstract class AbstractAjpProtocol<S> extends AbstractProtocol<S> {
processor.setRequiredSecret(requiredSecret);
processor.setKeepAliveTimeout(getKeepAliveTimeout());
processor.setClientCertProvider(getClientCertProvider());
- processor.setMaxCookieCount(getMaxCookieCount());
+ return processor;
}
- protected abstract static class AbstractAjpConnectionHandler<S,P extends AbstractAjpProcessor<S>>
- extends AbstractConnectionHandler<S, P> {
- @Override
- protected void initSsl(SocketWrapper<S> socket, Processor<S> processor) {
- // NOOP for AJP
- }
-
- @Override
- protected void longPoll(SocketWrapper<S> socket,
- Processor<S> processor) {
- // Same requirements for all AJP connectors
- socket.setAsync(true);
- }
-
- @Override
- protected P createUpgradeProcessor(SocketWrapper<S> socket, ByteBuffer leftoverInput,
- UpgradeToken upgradeToken) {
- // TODO should fail - throw IOE
- return null;
- }
+ @Override
+ protected Processor createUpgradeProcessor(SocketWrapperBase<?> socket,
+ UpgradeToken upgradeToken) {
+ throw new IllegalStateException(sm.getString("ajpprotocol.noUpgradeHandler",
+ upgradeToken.getHttpUpgradeHandler().getClass().getName()));
}
}
diff --git a/java/org/apache/coyote/ajp/AjpAprProcessor.java b/java/org/apache/coyote/ajp/AjpAprProcessor.java
deleted file mode 100644
index ec2ba3f..0000000
--- a/java/org/apache/coyote/ajp/AjpAprProcessor.java
+++ /dev/null
@@ -1,285 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.coyote.ajp;
-
-import java.io.IOException;
-import java.net.SocketTimeoutException;
-import java.nio.ByteBuffer;
-import java.util.concurrent.locks.Lock;
-import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
-
-import org.apache.juli.logging.Log;
-import org.apache.juli.logging.LogFactory;
-import org.apache.tomcat.jni.Socket;
-import org.apache.tomcat.jni.Status;
-import org.apache.tomcat.util.net.AprEndpoint;
-import org.apache.tomcat.util.net.SocketWrapper;
-
-/**
- * Processes AJP requests.
- *
- * @author Remy Maucherat
- * @author Henri Gomez
- * @author Dan Milstein
- * @author Keith Wannamaker
- * @author Kevin Seguin
- * @author Costin Manolache
- */
-public class AjpAprProcessor extends AbstractAjpProcessor<Long> {
-
- private static final Log log = LogFactory.getLog(AjpAprProcessor.class);
- @Override
- protected Log getLog() {
- return log;
- }
-
-
- public AjpAprProcessor(int packetSize, AprEndpoint endpoint) {
-
- super(packetSize, endpoint);
-
- response.setOutputBuffer(new SocketOutputBuffer());
-
- // Allocate input and output buffers
- inputBuffer = ByteBuffer.allocateDirect(packetSize * 2);
- inputBuffer.limit(0);
- outputBuffer = ByteBuffer.allocateDirect(packetSize * 2);
- }
-
-
- /**
- * Direct buffer used for input.
- */
- protected final ByteBuffer inputBuffer;
-
-
- /**
- * Direct buffer used for output.
- */
- protected final ByteBuffer outputBuffer;
-
-
- @Override
- protected void registerForEvent(boolean read, boolean write) {
- socketWrapper.registerforEvent(-1, read, write);
- }
-
- @Override
- protected void resetTimeouts() {
- // NO-OP. The AJP APR/native connector only uses the timeout value on
- // time SocketWrapper for async timeouts.
- }
-
-
- @Override
- protected void setupSocket(SocketWrapper<Long> socketWrapper) {
- long socketRef = socketWrapper.getSocket().longValue();
- Socket.setrbb(socketRef, inputBuffer);
- Socket.setsbb(socketRef, outputBuffer);
- }
-
-
- @Override
- protected void setTimeout(SocketWrapper<Long> socketWrapper,
- int timeout) throws IOException {
- Socket.timeoutSet(
- socketWrapper.getSocket().longValue(), timeout * 1000);
- }
-
-
- @Override
- protected int output(byte[] src, int offset, int length, boolean block)
- throws IOException {
-
- if (length == 0) {
- return 0;
- }
-
- outputBuffer.put(src, offset, length);
-
- int result = -1;
-
- if (socketWrapper.getSocket().longValue() != 0) {
- result = writeSocket(0, outputBuffer.position(), block);
- if (Status.APR_STATUS_IS_EAGAIN(-result)) {
- result = 0;
- }
- if (result < 0) {
- // There are no re-tries so clear the buffer to prevent a
- // possible overflow if the buffer is used again. BZ53119.
- outputBuffer.clear();
- throw new IOException(sm.getString("ajpprocessor.failedsend"));
- }
- }
- outputBuffer.clear();
-
- return result;
- }
-
-
- private int writeSocket(int pos, int len, boolean block) {
-
- Lock readLock = socketWrapper.getBlockingStatusReadLock();
- WriteLock writeLock = socketWrapper.getBlockingStatusWriteLock();
- long socket = socketWrapper.getSocket().longValue();
-
- boolean writeDone = false;
- int result = 0;
- readLock.lock();
- try {
- if (socketWrapper.getBlockingStatus() == block) {
- result = Socket.sendbb(socket, pos, len);
- writeDone = true;
- }
- } finally {
- readLock.unlock();
- }
-
- if (!writeDone) {
- writeLock.lock();
- try {
- socketWrapper.setBlockingStatus(block);
- // Set the current settings for this socket
- Socket.optSet(socket, Socket.APR_SO_NONBLOCK, (block ? 0 : 1));
- // Downgrade the lock
- readLock.lock();
- try {
- writeLock.unlock();
- result = Socket.sendbb(socket, pos, len);
- } finally {
- readLock.unlock();
- }
- } finally {
- // Should have been released above but may not have been on some
- // exception paths
- if (writeLock.isHeldByCurrentThread()) {
- writeLock.unlock();
- }
- }
- }
-
- return result;
- }
-
-
- @Override
- protected boolean read(byte[] buf, int pos, int n, boolean block)
- throws IOException {
-
- boolean nextReadBlocks = block;
-
- if (!block && inputBuffer.remaining() > 0) {
- nextReadBlocks = true;
- }
-
- if (inputBuffer.capacity() - inputBuffer.limit() <=
- n - inputBuffer.remaining()) {
- inputBuffer.compact();
- inputBuffer.limit(inputBuffer.position());
- inputBuffer.position(0);
- }
- int nRead;
- while (inputBuffer.remaining() < n) {
- nRead = readSocket(inputBuffer.limit(),
- inputBuffer.capacity() - inputBuffer.limit(),
- nextReadBlocks);
- if (nRead > 0) {
- inputBuffer.limit(inputBuffer.limit() + nRead);
- nextReadBlocks = true;
- } else if (-nRead == Status.EAGAIN) {
- return false;
- } else if ((-nRead) == Status.ETIMEDOUT || (-nRead) == Status.TIMEUP) {
- if (block) {
- throw new SocketTimeoutException(
- sm.getString("ajpprocessor.readtimeout"));
- } else {
- // Attempting to read from the socket when the poller
- // has not signalled that there is data to read appears
- // to behave like a blocking read with a short timeout
- // on OSX rather than like a non-blocking read. If no
- // data is read, treat the resulting timeout like a
- // non-blocking read that returned no data.
- return false;
- }
- } else {
- throw new IOException(sm.getString("ajpprocessor.failedread"));
- }
- }
-
- inputBuffer.get(buf, pos, n);
- return true;
- }
-
-
- private int readSocket(int pos, int len, boolean block) {
-
- Lock readLock = socketWrapper.getBlockingStatusReadLock();
- WriteLock writeLock = socketWrapper.getBlockingStatusWriteLock();
- long socket = socketWrapper.getSocket().longValue();
-
- boolean readDone = false;
- int result = 0;
- readLock.lock();
- try {
- if (socketWrapper.getBlockingStatus() == block) {
- result = Socket.recvbb(socket, pos, len);
- readDone = true;
- }
- } finally {
- readLock.unlock();
- }
-
- if (!readDone) {
- writeLock.lock();
- try {
- socketWrapper.setBlockingStatus(block);
- // Set the current settings for this socket
- Socket.optSet(socket, Socket.APR_SO_NONBLOCK, (block ? 0 : 1));
- // Downgrade the lock
- readLock.lock();
- try {
- writeLock.unlock();
- result = Socket.recvbb(socket, pos, len);
- } finally {
- readLock.unlock();
- }
- } finally {
- // Should have been released above but may not have been on some
- // exception paths
- if (writeLock.isHeldByCurrentThread()) {
- writeLock.unlock();
- }
- }
- }
-
- return result;
- }
-
-
- /**
- * Recycle the processor.
- */
- @Override
- public void recycle(boolean socketClosing) {
- super.recycle(socketClosing);
-
- inputBuffer.clear();
- inputBuffer.limit(0);
- outputBuffer.clear();
-
- }
-}
diff --git a/java/org/apache/coyote/ajp/AjpAprProtocol.java b/java/org/apache/coyote/ajp/AjpAprProtocol.java
index 63fe077..936810f 100644
--- a/java/org/apache/coyote/ajp/AjpAprProtocol.java
+++ b/java/org/apache/coyote/ajp/AjpAprProtocol.java
@@ -16,27 +16,16 @@
*/
package org.apache.coyote.ajp;
-import org.apache.coyote.AbstractProtocol;
-import org.apache.coyote.Processor;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
-import org.apache.tomcat.util.net.AbstractEndpoint;
import org.apache.tomcat.util.net.AprEndpoint;
-import org.apache.tomcat.util.net.AprEndpoint.Handler;
-import org.apache.tomcat.util.net.SocketWrapper;
/**
- * Abstract the protocol implementation, including threading, etc.
- * Processor is single threaded and specific to stream-based protocols,
- * will not fit Jk protocols like JNI.
- *
- * @author Remy Maucherat
- * @author Costin Manolache
+ * This the APR/native based protocol handler implementation for AJP.
*/
public class AjpAprProtocol extends AbstractAjpProtocol<Long> {
-
private static final Log log = LogFactory.getLog(AjpAprProtocol.class);
@Override
@@ -44,12 +33,6 @@ public class AjpAprProtocol extends AbstractAjpProtocol<Long> {
@Override
- protected AbstractEndpoint.Handler getHandler() {
- return cHandler;
- }
-
-
- @Override
public boolean isAprRequired() {
// Override since this protocol implementation requires the APR/native
// library
@@ -60,35 +43,14 @@ public class AjpAprProtocol extends AbstractAjpProtocol<Long> {
// ------------------------------------------------------------ Constructor
public AjpAprProtocol() {
- endpoint = new AprEndpoint();
- cHandler = new AjpConnectionHandler(this);
- ((AprEndpoint) endpoint).setHandler(cHandler);
- setSoLinger(Constants.DEFAULT_CONNECTION_LINGER);
- setSoTimeout(Constants.DEFAULT_CONNECTION_TIMEOUT);
- setTcpNoDelay(Constants.DEFAULT_TCP_NO_DELAY);
- // AJP does not use Send File
- ((AprEndpoint) endpoint).setUseSendfile(false);
+ super(new AprEndpoint());
}
- // ----------------------------------------------------- Instance Variables
-
-
- /**
- * Connection handler for AJP.
- */
- private final AjpConnectionHandler cHandler;
-
-
// --------------------------------------------------------- Public Methods
-
- public int getPollTime() { return ((AprEndpoint)endpoint).getPollTime(); }
- public void setPollTime(int pollTime) { ((AprEndpoint)endpoint).setPollTime(pollTime); }
-
- // pollerSize is now a synonym for maxConnections
- public void setPollerSize(int pollerSize) { endpoint.setMaxConnections(pollerSize); }
- public int getPollerSize() { return endpoint.getMaxConnections(); }
+ public int getPollTime() { return ((AprEndpoint)getEndpoint()).getPollTime(); }
+ public void setPollTime(int pollTime) { ((AprEndpoint)getEndpoint()).setPollTime(pollTime); }
// ----------------------------------------------------- JMX related methods
@@ -97,53 +59,4 @@ public class AjpAprProtocol extends AbstractAjpProtocol<Long> {
protected String getNamePrefix() {
return ("ajp-apr");
}
-
-
- // -------------------------------------- AjpConnectionHandler Inner Class
-
-
- protected static class AjpConnectionHandler
- extends AbstractAjpConnectionHandler<Long,AjpAprProcessor>
- implements Handler {
-
- protected final AjpAprProtocol proto;
-
- public AjpConnectionHandler(AjpAprProtocol proto) {
- this.proto = proto;
- }
-
- @Override
- protected AbstractProtocol<Long> getProtocol() {
- return proto;
- }
-
- @Override
- protected Log getLog() {
- return log;
- }
-
- /**
- * Expected to be used by the handler once the processor is no longer
- * required.
- */
- @Override
- public void release(SocketWrapper<Long> socket,
- Processor<Long> processor, boolean isSocketClosing,
- boolean addToPoller) {
- processor.recycle(isSocketClosing);
- recycledProcessors.push(processor);
- if (addToPoller) {
- socket.registerforEvent(proto.endpoint.getKeepAliveTimeout(), true, false);
- }
- }
-
-
- @Override
- protected AjpAprProcessor createProcessor() {
- AjpAprProcessor processor = new AjpAprProcessor(proto.packetSize, (AprEndpoint)proto.endpoint);
- proto.configureProcessor(processor);
- register(processor);
- return processor;
- }
- }
}
diff --git a/java/org/apache/coyote/ajp/AjpMessage.java b/java/org/apache/coyote/ajp/AjpMessage.java
index e4a5ee7..dc6a6f4 100644
--- a/java/org/apache/coyote/ajp/AjpMessage.java
+++ b/java/org/apache/coyote/ajp/AjpMessage.java
@@ -17,6 +17,8 @@
package org.apache.coyote.ajp;
+import java.nio.ByteBuffer;
+
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.buf.ByteChunk;
@@ -45,8 +47,7 @@ public class AjpMessage {
/**
* The string manager for this package.
*/
- protected static final StringManager sm =
- StringManager.getManager(Constants.Package);
+ protected static final StringManager sm = StringManager.getManager(AjpMessage.class);
// ------------------------------------------------------------ Constructor
@@ -113,6 +114,8 @@ public class AjpMessage {
/**
* Return the underlying byte buffer.
+ *
+ * @return The buffer
*/
public byte[] getBuffer() {
return buf;
@@ -120,9 +123,11 @@ public class AjpMessage {
/**
- * Return the current message length. For read, it's the length of the
- * payload (excluding the header). For write, it's the length of
- * the packet as a whole (counting the header).
+ * Return the current message length.
+ *
+ * @return For read, it's the length of the payload (excluding the header).
+ * For write, it's the length of the packet as a whole (counting the
+ * header).
*/
public int getLen() {
return len;
@@ -131,6 +136,8 @@ public class AjpMessage {
/**
* Add a short integer (2 bytes) to the message.
+ *
+ * @param val The integer to append
*/
public void appendInt(int val) {
buf[pos++] = (byte) ((val >>> 8) & 0xFF);
@@ -140,6 +147,8 @@ public class AjpMessage {
/**
* Append a byte (1 byte) to the message.
+ *
+ * @param val The byte value to append
*/
public void appendByte(int val) {
buf[pos++] = (byte) val;
@@ -147,8 +156,10 @@ public class AjpMessage {
/**
- * Write a MessageBytes out at the current write position.
- * A null MessageBytes is encoded as a string with length 0.
+ * Write a MessageBytes out at the current write position. A null
+ * MessageBytes is encoded as a string with length 0.
+ *
+ * @param mb The data to write
*/
public void appendBytes(MessageBytes mb) {
if (mb == null) {
@@ -182,8 +193,10 @@ public class AjpMessage {
/**
- * Write a ByteChunk out at the current write position.
- * A null ByteChunk is encoded as a string with length 0.
+ * Write a ByteChunk out at the current write position. A null ByteChunk is
+ * encoded as a string with length 0.
+ *
+ * @param bc The data to write
*/
public void appendByteChunk(ByteChunk bc) {
if (bc == null) {
@@ -209,18 +222,47 @@ public class AjpMessage {
* @param numBytes The number of bytes to copy.
*/
public void appendBytes(byte[] b, int off, int numBytes) {
+ if (checkOverflow(numBytes)) {
+ return;
+ }
+ appendInt(numBytes);
+ System.arraycopy(b, off, buf, pos, numBytes);
+ pos += numBytes;
+ appendByte(0);
+ }
+
+
+ /**
+ * Copy a chunk of bytes into the packet, starting at the current
+ * write position. The chunk of bytes is encoded with the length
+ * in two bytes first, then the data itself, and finally a
+ * terminating \0 (which is <B>not</B> included in the encoded
+ * length).
+ *
+ * @param b The ByteBuffer from which to copy bytes.
+ */
+ public void appendBytes(ByteBuffer b) {
+ int numBytes = b.remaining();
+ if (checkOverflow(numBytes)) {
+ return;
+ }
+ appendInt(numBytes);
+ b.get(buf, pos, numBytes);
+ pos += numBytes;
+ appendByte(0);
+ }
+
+
+ private boolean checkOverflow(int numBytes) {
if (pos + numBytes + 3 > buf.length) {
log.error(sm.getString("ajpmessage.overflow", "" + numBytes, "" + pos),
new ArrayIndexOutOfBoundsException());
if (log.isDebugEnabled()) {
dump("Overflow/coBytes");
}
- return;
+ return true;
}
- appendInt(numBytes);
- System.arraycopy(b, off, buf, pos, numBytes);
- pos += numBytes;
- appendByte(0);
+ return false;
}
@@ -229,6 +271,8 @@ public class AjpMessage {
* it. Integers are encoded as two unsigned bytes with the
* high-order byte first, and, as far as I can tell, in
* little-endian order within each byte.
+ *
+ * @return The integer value read from the message
*/
public int getInt() {
int b1 = buf[pos++] & 0xFF;
@@ -286,6 +330,8 @@ public class AjpMessage {
* it. Integers are encoded as four unsigned bytes with the
* high-order byte first, and, as far as I can tell, in
* little-endian order within each byte.
+ *
+ * @return The long value read from the message
*/
public int getLongInt() {
int b1 = buf[pos++] & 0xFF; // No swap, Java order
@@ -300,16 +346,6 @@ public class AjpMessage {
}
- public int getHeaderLength() {
- return Constants.H_SIZE;
- }
-
-
- public int getPacketSize() {
- return buf.length;
- }
-
-
public int processHeader(boolean toContainer) {
pos = 0;
int mark = getInt();
@@ -319,7 +355,7 @@ public class AjpMessage {
(!toContainer && mark != 0x4142)) {
log.error(sm.getString("ajpmessage.invalid", "" + mark));
if (log.isDebugEnabled()) {
- dump("In: ");
+ dump("In");
}
return -1;
}
@@ -330,12 +366,9 @@ public class AjpMessage {
}
- /**
- * Dump the contents of the message, prefixed with the given String.
- */
- public void dump(String msg) {
+ private void dump(String prefix) {
if (log.isDebugEnabled()) {
- log.debug(msg + ": " + HexUtils.toHexString(buf) + " " + pos +"/" + (len + 4));
+ log.debug(prefix + ": " + HexUtils.toHexString(buf) + " " + pos +"/" + (len + 4));
}
int max = pos;
if (len + 4 > pos)
diff --git a/java/org/apache/coyote/ajp/AjpNio2Processor.java b/java/org/apache/coyote/ajp/AjpNio2Processor.java
deleted file mode 100644
index 963e6ab..0000000
--- a/java/org/apache/coyote/ajp/AjpNio2Processor.java
+++ /dev/null
@@ -1,265 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.coyote.ajp;
-
-import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.nio.channels.CompletionHandler;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-
-import org.apache.juli.logging.Log;
-import org.apache.juli.logging.LogFactory;
-import org.apache.tomcat.util.net.Nio2Channel;
-import org.apache.tomcat.util.net.Nio2Endpoint;
-import org.apache.tomcat.util.net.SocketStatus;
-import org.apache.tomcat.util.net.SocketWrapper;
-
-/**
- * Processes AJP requests using NIO2.
- */
-public class AjpNio2Processor extends AbstractAjpProcessor<Nio2Channel> {
-
- private static final Log log = LogFactory.getLog(AjpNio2Processor.class);
- @Override
- protected Log getLog() {
- return log;
- }
-
- /**
- * The completion handler used for asynchronous write operations
- */
- protected CompletionHandler<Integer, SocketWrapper<Nio2Channel>> writeCompletionHandler;
-
- /**
- * Flipped flag for read buffer.
- */
- protected boolean flipped = false;
-
- /**
- * Write pending flag.
- */
- protected volatile boolean writePending = false;
-
- public AjpNio2Processor(int packetSize, Nio2Endpoint endpoint0) {
- super(packetSize, endpoint0);
- response.setOutputBuffer(new SocketOutputBuffer());
- this.writeCompletionHandler = new CompletionHandler<Integer, SocketWrapper<Nio2Channel>>() {
- @Override
- public void completed(Integer nBytes, SocketWrapper<Nio2Channel> attachment) {
- boolean notify = false;
- synchronized (writeCompletionHandler) {
- if (nBytes.intValue() < 0) {
- failed(new IOException(sm.getString("ajpprocessor.failedsend")), attachment);
- return;
- }
- writePending = false;
- if (!Nio2Endpoint.isInline()) {
- notify = true;
- }
- }
- if (notify) {
- endpoint.processSocket(attachment, SocketStatus.OPEN_WRITE, false);
- }
- }
- @Override
- public void failed(Throwable exc, SocketWrapper<Nio2Channel> attachment) {
- attachment.setError(true);
- writePending = false;
- endpoint.processSocket(attachment, SocketStatus.DISCONNECT, true);
- }
- };
- }
-
- @Override
- public void recycle(boolean socketClosing) {
- super.recycle(socketClosing);
- writePending = false;
- flipped = false;
- }
-
- @Override
- protected void registerForEvent(boolean read, boolean write) {
- // Nothing to do here, the appropriate operations should
- // already be pending
- }
-
- @Override
- protected void resetTimeouts() {
- // The NIO connector uses the timeout configured on the wrapper in the
- // poller. Therefore, it needs to be reset once asycn processing has
- // finished.
- if (!getErrorState().isError() && socketWrapper != null &&
- asyncStateMachine.isAsyncDispatching()) {
- long soTimeout = endpoint.getSoTimeout();
-
- //reset the timeout
- if (keepAliveTimeout > 0) {
- socketWrapper.setTimeout(keepAliveTimeout);
- } else {
- socketWrapper.setTimeout(soTimeout);
- }
- }
-
- }
-
-
- @Override
- protected void setupSocket(SocketWrapper<Nio2Channel> socketWrapper)
- throws IOException {
- // NO-OP
- }
-
-
- @Override
- protected void setTimeout(SocketWrapper<Nio2Channel> socketWrapper,
- int timeout) throws IOException {
- socketWrapper.setTimeout(timeout);
- }
-
-
- @Override
- protected int output(byte[] src, int offset, int length, boolean block)
- throws IOException {
-
- if (socketWrapper == null || socketWrapper.getSocket() == null)
- return -1;
-
- ByteBuffer writeBuffer =
- socketWrapper.getSocket().getBufHandler().getWriteBuffer();
- int toWrite = Math.min(length, writeBuffer.capacity());
-
- int result = 0;
- if (block) {
- writeBuffer.clear();
- writeBuffer.put(src, offset, toWrite);
- writeBuffer.flip();
- try {
- result = socketWrapper.getSocket().write(writeBuffer)
- .get(socketWrapper.getTimeout(), TimeUnit.MILLISECONDS).intValue();
- } catch (InterruptedException | ExecutionException
- | TimeoutException e) {
- throw new IOException(sm.getString("ajpprocessor.failedsend"), e);
- }
- } else {
- synchronized (writeCompletionHandler) {
- if (!writePending) {
- writeBuffer.clear();
- writeBuffer.put(src, offset, toWrite);
- writeBuffer.flip();
- writePending = true;
- Nio2Endpoint.startInline();
- socketWrapper.getSocket().write(writeBuffer, socketWrapper.getTimeout(),
- TimeUnit.MILLISECONDS, socketWrapper, writeCompletionHandler);
- Nio2Endpoint.endInline();
- result = toWrite;
- }
- }
- }
- return result;
- }
-
-
- @Override
- protected boolean read(byte[] buf, int pos, int n, boolean blockFirstRead)
- throws IOException {
-
- int read = 0;
- int res = 0;
- boolean block = blockFirstRead;
-
- while (read < n) {
- res = readSocket(buf, read + pos, n - read, block);
- if (res > 0) {
- read += res;
- } else if (res == 0 && !block) {
- return false;
- } else {
- throw new IOException(sm.getString("ajpprocessor.failedread"));
- }
- block = true;
- }
- return true;
- }
-
-
- private int readSocket(byte[] buf, int pos, int n, boolean block)
- throws IOException {
- int nRead = 0;
- ByteBuffer readBuffer =
- socketWrapper.getSocket().getBufHandler().getReadBuffer();
-
- if (block) {
- if (!flipped) {
- readBuffer.flip();
- flipped = true;
- }
- if (readBuffer.remaining() > 0) {
- nRead = Math.min(n, readBuffer.remaining());
- readBuffer.get(buf, pos, nRead);
- if (readBuffer.remaining() == 0) {
- readBuffer.clear();
- flipped = false;
- }
- } else {
- readBuffer.clear();
- flipped = false;
- if (n < readBuffer.capacity()) {
- readBuffer.limit(n);
- }
- try {
- nRead = socketWrapper.getSocket().read(readBuffer)
- .get(socketWrapper.getTimeout(), TimeUnit.MILLISECONDS).intValue();
- } catch (InterruptedException | ExecutionException
- | TimeoutException e) {
- throw new IOException(sm.getString("ajpprocessor.failedread"), e);
- }
- if (nRead > 0) {
- if (!flipped) {
- readBuffer.flip();
- flipped = true;
- }
- nRead = Math.min(n, readBuffer.remaining());
- readBuffer.get(buf, pos, nRead);
- if (readBuffer.remaining() == 0) {
- readBuffer.clear();
- flipped = false;
- }
- }
- }
- } else {
- if (!flipped) {
- readBuffer.flip();
- flipped = true;
- }
- if (readBuffer.remaining() > 0) {
- nRead = Math.min(n, readBuffer.remaining());
- readBuffer.get(buf, pos, nRead);
- if (readBuffer.remaining() == 0) {
- readBuffer.clear();
- flipped = false;
- }
- } else {
- readBuffer.clear();
- flipped = false;
- readBuffer.limit(n);
- }
- }
- return nRead;
- }
-}
diff --git a/java/org/apache/coyote/ajp/AjpNio2Protocol.java b/java/org/apache/coyote/ajp/AjpNio2Protocol.java
index 76e3a7f..eee83cc 100644
--- a/java/org/apache/coyote/ajp/AjpNio2Protocol.java
+++ b/java/org/apache/coyote/ajp/AjpNio2Protocol.java
@@ -16,142 +16,34 @@
*/
package org.apache.coyote.ajp;
-import org.apache.coyote.AbstractProtocol;
-import org.apache.coyote.Processor;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
-import org.apache.tomcat.util.net.AbstractEndpoint;
import org.apache.tomcat.util.net.Nio2Channel;
import org.apache.tomcat.util.net.Nio2Endpoint;
-import org.apache.tomcat.util.net.Nio2Endpoint.Handler;
-import org.apache.tomcat.util.net.SSLImplementation;
-import org.apache.tomcat.util.net.SocketStatus;
-import org.apache.tomcat.util.net.SocketWrapper;
/**
- * Abstract the protocol implementation, including threading, etc.
- * Processor is single threaded and specific to stream-based protocols,
- * will not fit Jk protocols like JNI.
+ * This the NIO2 based protocol handler implementation for AJP.
*/
public class AjpNio2Protocol extends AbstractAjpProtocol<Nio2Channel> {
-
private static final Log log = LogFactory.getLog(AjpNio2Protocol.class);
@Override
protected Log getLog() { return log; }
- @Override
- protected AbstractEndpoint.Handler getHandler() {
- return cHandler;
- }
-
-
// ------------------------------------------------------------ Constructor
-
public AjpNio2Protocol() {
- endpoint = new Nio2Endpoint();
- cHandler = new AjpConnectionHandler(this);
- ((Nio2Endpoint) endpoint).setHandler(cHandler);
- setSoLinger(Constants.DEFAULT_CONNECTION_LINGER);
- setSoTimeout(Constants.DEFAULT_CONNECTION_TIMEOUT);
- setTcpNoDelay(Constants.DEFAULT_TCP_NO_DELAY);
- // AJP does not use Send File
- ((Nio2Endpoint) endpoint).setUseSendfile(false);
+ super(new Nio2Endpoint());
}
- // ----------------------------------------------------- Instance Variables
-
-
- /**
- * Connection handler for AJP.
- */
- private final AjpConnectionHandler cHandler;
-
-
// ----------------------------------------------------- JMX related methods
@Override
protected String getNamePrefix() {
return ("ajp-nio2");
}
-
-
- // -------------------------------------- AjpConnectionHandler Inner Class
-
-
- protected static class AjpConnectionHandler
- extends AbstractAjpConnectionHandler<Nio2Channel, AjpNio2Processor>
- implements Handler {
-
- protected final AjpNio2Protocol proto;
-
- public AjpConnectionHandler(AjpNio2Protocol proto) {
- this.proto = proto;
- }
-
- @Override
- protected AbstractProtocol<Nio2Channel> getProtocol() {
- return proto;
- }
-
- @Override
- protected Log getLog() {
- return log;
- }
-
- @Override
- public SSLImplementation getSslImplementation() {
- // AJP does not support SSL
- return null;
- }
-
- /**
- * Expected to be used by the Poller to release resources on socket
- * close, errors etc.
- */
- @Override
- public void release(SocketWrapper<Nio2Channel> socket) {
- Processor<Nio2Channel> processor =
- connections.remove(socket.getSocket());
- if (processor != null) {
- processor.recycle(true);
- recycledProcessors.push(processor);
- }
- }
-
- /**
- * Expected to be used by the handler once the processor is no longer
- * required.
- */
- @Override
- public void release(SocketWrapper<Nio2Channel> socket,
- Processor<Nio2Channel> processor, boolean isSocketClosing,
- boolean addToPoller) {
- processor.recycle(isSocketClosing);
- recycledProcessors.push(processor);
- if (addToPoller) {
- ((Nio2Endpoint) proto.endpoint).awaitBytes(socket);
- }
- }
-
- @Override
- protected AjpNio2Processor createProcessor() {
- AjpNio2Processor processor = new AjpNio2Processor(proto.packetSize, (Nio2Endpoint) proto.endpoint);
- proto.configureProcessor(processor);
- register(processor);
- return processor;
- }
-
- @Override
- public void closeAll() {
- for (Nio2Channel channel : connections.keySet()) {
- ((Nio2Endpoint) proto.endpoint).closeSocket(channel.getSocket(), SocketStatus.STOP);
- }
- }
- }
}
diff --git a/java/org/apache/coyote/ajp/AjpNioProcessor.java b/java/org/apache/coyote/ajp/AjpNioProcessor.java
deleted file mode 100644
index 71f8410..0000000
--- a/java/org/apache/coyote/ajp/AjpNioProcessor.java
+++ /dev/null
@@ -1,216 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.coyote.ajp;
-
-import java.io.EOFException;
-import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.nio.channels.SelectionKey;
-import java.nio.channels.Selector;
-
-import org.apache.juli.logging.Log;
-import org.apache.juli.logging.LogFactory;
-import org.apache.tomcat.util.net.NioChannel;
-import org.apache.tomcat.util.net.NioEndpoint;
-import org.apache.tomcat.util.net.NioSelectorPool;
-import org.apache.tomcat.util.net.SocketWrapper;
-
-/**
- * Processes AJP requests using NIO.
- */
-public class AjpNioProcessor extends AbstractAjpProcessor<NioChannel> {
-
- private static final Log log = LogFactory.getLog(AjpNioProcessor.class);
- @Override
- protected Log getLog() {
- return log;
- }
-
-
- public AjpNioProcessor(int packetSize, NioEndpoint endpoint) {
-
- super(packetSize, endpoint);
-
- response.setOutputBuffer(new SocketOutputBuffer());
-
- pool = endpoint.getSelectorPool();
- }
-
-
- /**
- * Selector pool for the associated endpoint.
- */
- protected final NioSelectorPool pool;
-
-
- @Override
- protected void registerForEvent(boolean read, boolean write) {
- final NioChannel socket = socketWrapper.getSocket();
- final NioEndpoint.KeyAttachment attach =
- (NioEndpoint.KeyAttachment) socket.getAttachment();
- if (attach == null) {
- return;
- }
- SelectionKey key = socket.getIOChannel().keyFor(socket.getPoller().getSelector());
- if (read) {
- attach.interestOps(attach.interestOps() | SelectionKey.OP_READ);
- key.interestOps(key.interestOps() | SelectionKey.OP_READ);
- }
- if (write) {
- attach.interestOps(attach.interestOps() | SelectionKey.OP_WRITE);
- key.interestOps(key.interestOps() | SelectionKey.OP_READ);
- }
- }
-
-
- @Override
- protected void resetTimeouts() {
- // The NIO connector uses the timeout configured on the wrapper in the
- // poller. Therefore, it needs to be reset once asycn processing has
- // finished.
- final NioEndpoint.KeyAttachment attach =
- (NioEndpoint.KeyAttachment)socketWrapper.getSocket().getAttachment();
- if (!getErrorState().isError() && attach != null &&
- asyncStateMachine.isAsyncDispatching()) {
- long soTimeout = endpoint.getSoTimeout();
-
- //reset the timeout
- if (keepAliveTimeout > 0) {
- attach.setTimeout(keepAliveTimeout);
- } else {
- attach.setTimeout(soTimeout);
- }
- }
-
- }
-
-
- @Override
- protected void setupSocket(SocketWrapper<NioChannel> socketWrapper)
- throws IOException {
- // NO-OP
- }
-
-
- @Override
- protected void setTimeout(SocketWrapper<NioChannel> socketWrapper,
- int timeout) throws IOException {
- socketWrapper.setTimeout(timeout);
- }
-
-
- @Override
- protected int output(byte[] src, int offset, int length, boolean block)
- throws IOException {
-
- NioEndpoint.KeyAttachment att =
- (NioEndpoint.KeyAttachment) socketWrapper.getSocket().getAttachment();
- if ( att == null ) throw new IOException("Key must be cancelled");
-
- ByteBuffer writeBuffer =
- socketWrapper.getSocket().getBufHandler().getWriteBuffer();
-
- int toWrite = Math.min(length, writeBuffer.remaining());
- writeBuffer.put(src, offset, toWrite);
-
- writeBuffer.flip();
-
- long writeTimeout = att.getWriteTimeout();
- Selector selector = null;
- try {
- selector = pool.get();
- } catch (IOException x) {
- //ignore
- }
- try {
- return pool.write(writeBuffer, socketWrapper.getSocket(), selector,
- writeTimeout, block);
- } finally {
- writeBuffer.clear();
- if (selector != null) {
- pool.put(selector);
- }
- }
- }
-
-
- @Override
- protected boolean read(byte[] buf, int pos, int n, boolean blockFirstRead)
- throws IOException {
-
- int read = 0;
- int res = 0;
- boolean block = blockFirstRead;
-
- while (read < n) {
- res = readSocket(buf, read + pos, n - read, block);
- if (res > 0) {
- read += res;
- } else if (res == 0 && !block) {
- return false;
- } else {
- throw new IOException(sm.getString("ajpprocessor.failedread"));
- }
- block = true;
- }
- return true;
- }
-
-
- private int readSocket(byte[] buf, int pos, int n, boolean block)
- throws IOException {
- int nRead = 0;
- ByteBuffer readBuffer =
- socketWrapper.getSocket().getBufHandler().getReadBuffer();
- readBuffer.clear();
- if (n < readBuffer.capacity()) {
- readBuffer.limit(n);
- }
- if ( block ) {
- Selector selector = null;
- try {
- selector = pool.get();
- } catch ( IOException x ) {
- // Ignore
- }
- try {
- NioEndpoint.KeyAttachment att =
- (NioEndpoint.KeyAttachment) socketWrapper.getSocket().getAttachment();
- if ( att == null ) throw new IOException("Key must be cancelled.");
- nRead = pool.read(readBuffer, socketWrapper.getSocket(),
- selector, att.getTimeout());
- } catch ( EOFException eof ) {
- nRead = -1;
- } finally {
- if ( selector != null ) pool.put(selector);
- }
- } else {
- nRead = socketWrapper.getSocket().read(readBuffer);
- }
- if (nRead > 0) {
- readBuffer.flip();
- readBuffer.limit(nRead);
- readBuffer.get(buf, pos, nRead);
- return nRead;
- } else if (nRead == -1) {
- //return false;
- throw new EOFException(sm.getString("iib.eof.error"));
- } else {
- return 0;
- }
- }
-}
diff --git a/java/org/apache/coyote/ajp/AjpNioProtocol.java b/java/org/apache/coyote/ajp/AjpNioProtocol.java
index 986217f..6876f18 100644
--- a/java/org/apache/coyote/ajp/AjpNioProtocol.java
+++ b/java/org/apache/coyote/ajp/AjpNioProtocol.java
@@ -16,168 +16,33 @@
*/
package org.apache.coyote.ajp;
-import java.nio.channels.SocketChannel;
-import java.util.Iterator;
-
-import org.apache.coyote.AbstractProtocol;
-import org.apache.coyote.Processor;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
-import org.apache.tomcat.util.net.AbstractEndpoint;
import org.apache.tomcat.util.net.NioChannel;
import org.apache.tomcat.util.net.NioEndpoint;
-import org.apache.tomcat.util.net.NioEndpoint.Handler;
-import org.apache.tomcat.util.net.SSLImplementation;
-import org.apache.tomcat.util.net.SocketWrapper;
-
/**
- * Abstract the protocol implementation, including threading, etc.
- * Processor is single threaded and specific to stream-based protocols,
- * will not fit Jk protocols like JNI.
+ * This the NIO based protocol handler implementation for AJP.
*/
public class AjpNioProtocol extends AbstractAjpProtocol<NioChannel> {
-
private static final Log log = LogFactory.getLog(AjpNioProtocol.class);
@Override
protected Log getLog() { return log; }
- @Override
- protected AbstractEndpoint.Handler getHandler() {
- return cHandler;
- }
-
-
// ------------------------------------------------------------ Constructor
-
public AjpNioProtocol() {
- endpoint = new NioEndpoint();
- cHandler = new AjpConnectionHandler(this);
- ((NioEndpoint) endpoint).setHandler(cHandler);
- setSoLinger(Constants.DEFAULT_CONNECTION_LINGER);
- setSoTimeout(Constants.DEFAULT_CONNECTION_TIMEOUT);
- setTcpNoDelay(Constants.DEFAULT_TCP_NO_DELAY);
- // AJP does not use Send File
- ((NioEndpoint) endpoint).setUseSendfile(false);
+ super(new NioEndpoint());
}
- // ----------------------------------------------------- Instance Variables
-
-
- /**
- * Connection handler for AJP.
- */
- private final AjpConnectionHandler cHandler;
-
-
// ----------------------------------------------------- JMX related methods
@Override
protected String getNamePrefix() {
return ("ajp-nio");
}
-
-
- // -------------------------------------- AjpConnectionHandler Inner Class
-
-
- protected static class AjpConnectionHandler
- extends AbstractAjpConnectionHandler<NioChannel, AjpNioProcessor>
- implements Handler {
-
- protected final AjpNioProtocol proto;
-
- public AjpConnectionHandler(AjpNioProtocol proto) {
- this.proto = proto;
- }
-
- @Override
- protected AbstractProtocol<NioChannel> getProtocol() {
- return proto;
- }
-
- @Override
- protected Log getLog() {
- return log;
- }
-
- @Override
- public SSLImplementation getSslImplementation() {
- // AJP does not support SSL
- return null;
- }
-
- /**
- * Expected to be used by the Poller to release resources on socket
- * close, errors etc.
- */
- @Override
- public void release(SocketChannel socket) {
- if (log.isDebugEnabled())
- log.debug(sm.getString("ajpnioprotocol.releaseStart", socket));
- boolean released = false;
- Iterator<java.util.Map.Entry<NioChannel, Processor<NioChannel>>> it = connections.entrySet().iterator();
- while (it.hasNext()) {
- java.util.Map.Entry<NioChannel, Processor<NioChannel>> entry = it.next();
- if (entry.getKey().getIOChannel()==socket) {
- it.remove();
- Processor<NioChannel> result = entry.getValue();
- result.recycle(true);
- unregister(result);
- released = true;
- break;
- }
- }
- if (log.isDebugEnabled())
- log.debug(sm.getString("ajpnioprotocol.releaseEnd",
- socket, Boolean.valueOf(released)));
- }
-
- /**
- * Expected to be used by the Poller to release resources on socket
- * close, errors etc.
- */
- @Override
- public void release(SocketWrapper<NioChannel> socket) {
- Processor<NioChannel> processor =
- connections.remove(socket.getSocket());
- if (processor != null) {
- processor.recycle(true);
- recycledProcessors.push(processor);
- }
- }
-
- /**
- * Expected to be used by the handler once the processor is no longer
- * required.
- */
- @Override
- public void release(SocketWrapper<NioChannel> socket,
- Processor<NioChannel> processor, boolean isSocketClosing,
- boolean addToPoller) {
- processor.recycle(isSocketClosing);
- recycledProcessors.push(processor);
- if (addToPoller) {
- // The only time this method is called with addToPoller == true
- // is when the socket is in keep-alive so set the appropriate
- // timeout.
- socket.setTimeout(getProtocol().getKeepAliveTimeout());
- socket.getSocket().getPoller().add(socket.getSocket());
- }
- }
-
-
- @Override
- protected AjpNioProcessor createProcessor() {
- AjpNioProcessor processor = new AjpNioProcessor(proto.packetSize, (NioEndpoint)proto.endpoint);
- proto.configureProcessor(processor);
- register(processor);
- return processor;
- }
- }
}
diff --git a/java/org/apache/coyote/ajp/AjpProcessor.java b/java/org/apache/coyote/ajp/AjpProcessor.java
index adc090b..90c0e39 100644
--- a/java/org/apache/coyote/ajp/AjpProcessor.java
+++ b/java/org/apache/coyote/ajp/AjpProcessor.java
@@ -16,108 +16,1470 @@
*/
package org.apache.coyote.ajp;
+import java.io.ByteArrayInputStream;
+import java.io.EOFException;
import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.net.Socket;
+import java.io.InterruptedIOException;
+import java.net.InetAddress;
+import java.nio.ByteBuffer;
+import java.security.NoSuchProviderException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.coyote.AbstractProcessor;
+import org.apache.coyote.ActionCode;
+import org.apache.coyote.ErrorState;
+import org.apache.coyote.InputBuffer;
+import org.apache.coyote.OutputBuffer;
+import org.apache.coyote.RequestInfo;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
-import org.apache.tomcat.util.net.JIoEndpoint;
-import org.apache.tomcat.util.net.SocketWrapper;
+import org.apache.tomcat.util.ExceptionUtils;
+import org.apache.tomcat.util.buf.ByteChunk;
+import org.apache.tomcat.util.buf.HexUtils;
+import org.apache.tomcat.util.buf.MessageBytes;
+import org.apache.tomcat.util.http.MimeHeaders;
+import org.apache.tomcat.util.net.AbstractEndpoint;
+import org.apache.tomcat.util.net.AbstractEndpoint.Handler.SocketState;
+import org.apache.tomcat.util.net.ApplicationBufferHandler;
+import org.apache.tomcat.util.net.SSLSupport;
+import org.apache.tomcat.util.net.SocketWrapperBase;
+import org.apache.tomcat.util.res.StringManager;
/**
- * Processes AJP requests.
- *
- * @author Remy Maucherat
- * @author Henri Gomez
- * @author Dan Milstein
- * @author Keith Wannamaker
- * @author Kevin Seguin
- * @author Costin Manolache
+ * AJP Processor implementation.
*/
-public class AjpProcessor extends AbstractAjpProcessor<Socket> {
+public class AjpProcessor extends AbstractProcessor {
private static final Log log = LogFactory.getLog(AjpProcessor.class);
- @Override
- protected Log getLog() {
- return log;
+ /**
+ * The string manager for this package.
+ */
+ private static final StringManager sm = StringManager.getManager(AjpProcessor.class);
+
+
+ /**
+ * End message array.
+ */
+ private static final byte[] endMessageArray;
+ private static final byte[] endAndCloseMessageArray;
+
+
+ /**
+ * Flush message array.
+ */
+ private static final byte[] flushMessageArray;
+
+
+ /**
+ * Pong message array.
+ */
+ private static final byte[] pongMessageArray;
+
+
+ static {
+ // Allocate the end message array
+ AjpMessage endMessage = new AjpMessage(16);
+ endMessage.reset();
+ endMessage.appendByte(Constants.JK_AJP13_END_RESPONSE);
+ endMessage.appendByte(1);
+ endMessage.end();
+ endMessageArray = new byte[endMessage.getLen()];
+ System.arraycopy(endMessage.getBuffer(), 0, endMessageArray, 0,
+ endMessage.getLen());
+
+ // Allocate the end and close message array
+ AjpMessage endAndCloseMessage = new AjpMessage(16);
+ endAndCloseMessage.reset();
+ endAndCloseMessage.appendByte(Constants.JK_AJP13_END_RESPONSE);
+ endAndCloseMessage.appendByte(0);
+ endAndCloseMessage.end();
+ endAndCloseMessageArray = new byte[endAndCloseMessage.getLen()];
+ System.arraycopy(endAndCloseMessage.getBuffer(), 0, endAndCloseMessageArray, 0,
+ endAndCloseMessage.getLen());
+
+ // Allocate the flush message array
+ AjpMessage flushMessage = new AjpMessage(16);
+ flushMessage.reset();
+ flushMessage.appendByte(Constants.JK_AJP13_SEND_BODY_CHUNK);
+ flushMessage.appendInt(0);
+ flushMessage.appendByte(0);
+ flushMessage.end();
+ flushMessageArray = new byte[flushMessage.getLen()];
+ System.arraycopy(flushMessage.getBuffer(), 0, flushMessageArray, 0,
+ flushMessage.getLen());
+
+ // Allocate the pong message array
+ AjpMessage pongMessage = new AjpMessage(16);
+ pongMessage.reset();
+ pongMessage.appendByte(Constants.JK_AJP13_CPONG_REPLY);
+ pongMessage.end();
+ pongMessageArray = new byte[pongMessage.getLen()];
+ System.arraycopy(pongMessage.getBuffer(), 0, pongMessageArray,
+ 0, pongMessage.getLen());
}
- public AjpProcessor(int packetSize, JIoEndpoint endpoint) {
+ // ----------------------------------------------------- Instance Variables
+
+
+ /**
+ * GetBody message array. Not static like the other message arrays since the
+ * message varies with packetSize and that can vary per connector.
+ */
+ private final byte[] getBodyMessageArray;
+
+
+ /**
+ * AJP packet size.
+ */
+ private final int outputMaxChunkSize;
+
+ /**
+ * Header message. Note that this header is merely the one used during the
+ * processing of the first message of a "request", so it might not be a
+ * request header. It will stay unchanged during the processing of the whole
+ * request.
+ */
+ private final AjpMessage requestHeaderMessage;
+
+
+ /**
+ * Message used for response composition.
+ */
+ private final AjpMessage responseMessage;
+
+
+ /**
+ * Location of next write of the response message (used with non-blocking
+ * writes when the message may not be written in a single write). A value of
+ * -1 indicates that no message has been written to the buffer.
+ */
+ private int responseMsgPos = -1;
+
+
+ /**
+ * Body message.
+ */
+ private final AjpMessage bodyMessage;
+
+
+ /**
+ * Body message.
+ */
+ private final MessageBytes bodyBytes = MessageBytes.newInstance();
+
+
+ /**
+ * Host name (used to avoid useless B2C conversion on the host name).
+ */
+ private char[] hostNameC = new char[0];
+
+
+ /**
+ * Temp message bytes used for processing.
+ */
+ private final MessageBytes tmpMB = MessageBytes.newInstance();
+
+
+ /**
+ * Byte chunk for certs.
+ */
+ private final MessageBytes certificates = MessageBytes.newInstance();
+
+
+ /**
+ * End of stream flag.
+ */
+ private boolean endOfStream = false;
+
+
+ /**
+ * Request body empty flag.
+ */
+ private boolean empty = true;
+
+
+ /**
+ * First read.
+ */
+ private boolean first = true;
+
+
+ /**
+ * Indicates that a 'get body chunk' message has been sent but the body
+ * chunk has not yet been received.
+ */
+ private boolean waitingForBodyMessage = false;
+
+
+ /**
+ * Replay read.
+ */
+ private boolean replay = false;
+
- super(packetSize, endpoint);
+ /**
+ * Should any response body be swallowed and not sent to the client.
+ */
+ private boolean swallowResponse = false;
+
+
+ /**
+ * Finished response.
+ */
+ private boolean responseFinished = false;
+
+
+ /**
+ * Bytes written to client for the current request.
+ */
+ private long bytesWritten = 0;
+
+
+
+
+ // ------------------------------------------------------------ Constructor
+
+ public AjpProcessor(int packetSize, AbstractEndpoint<?> endpoint) {
+
+ super(endpoint);
+
+ // Calculate maximum chunk size as packetSize may have been changed from
+ // the default (Constants.MAX_PACKET_SIZE)
+ this.outputMaxChunkSize =
+ Constants.MAX_SEND_SIZE + packetSize - Constants.MAX_PACKET_SIZE;
+
+ request.setInputBuffer(new SocketInputBuffer());
+
+ requestHeaderMessage = new AjpMessage(packetSize);
+ responseMessage = new AjpMessage(packetSize);
+ bodyMessage = new AjpMessage(packetSize);
+
+ // Set the getBody message buffer
+ AjpMessage getBodyMessage = new AjpMessage(16);
+ getBodyMessage.reset();
+ getBodyMessage.appendByte(Constants.JK_AJP13_GET_BODY_CHUNK);
+ // Adjust read size if packetSize != default (Constants.MAX_PACKET_SIZE)
+ getBodyMessage.appendInt(Constants.MAX_READ_SIZE + packetSize -
+ Constants.MAX_PACKET_SIZE);
+ getBodyMessage.end();
+ getBodyMessageArray = new byte[getBodyMessage.getLen()];
+ System.arraycopy(getBodyMessage.getBuffer(), 0, getBodyMessageArray,
+ 0, getBodyMessage.getLen());
response.setOutputBuffer(new SocketOutputBuffer());
}
- protected InputStream input;
+ // ------------------------------------------------------------- Properties
+
+
+ /**
+ * Send AJP flush packet when flushing.
+ * An flush packet is a zero byte AJP13 SEND_BODY_CHUNK
+ * packet. mod_jk and mod_proxy_ajp interprete 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 extra flush packets.
+ * For compatibility and to stay on the safe side, flush
+ * packets are enabled by default.
+ */
+ protected boolean ajpFlush = true;
+ public boolean getAjpFlush() { return ajpFlush; }
+ public void setAjpFlush(boolean ajpFlush) {
+ this.ajpFlush = ajpFlush;
+ }
+
- protected OutputStream output;
+ /**
+ * The number of milliseconds Tomcat will wait for a subsequent request
+ * before closing the connection. The default is -1 which is an infinite
+ * timeout.
+ */
+ private int keepAliveTimeout = -1;
+ public int getKeepAliveTimeout() { return keepAliveTimeout; }
+ public void setKeepAliveTimeout(int timeout) { keepAliveTimeout = timeout; }
+ /**
+ * Use Tomcat authentication ?
+ */
+ private boolean tomcatAuthentication = true;
+ public boolean getTomcatAuthentication() { return tomcatAuthentication; }
+ public void setTomcatAuthentication(boolean tomcatAuthentication) {
+ this.tomcatAuthentication = tomcatAuthentication;
+ }
+
+
+ /**
+ * Use Tomcat authorization ?
+ */
+ private boolean tomcatAuthorization = false;
+ public boolean getTomcatAuthorization() { return tomcatAuthorization; }
+ public void setTomcatAuthorization(boolean tomcatAuthorization) {
+ this.tomcatAuthorization = tomcatAuthorization;
+ }
+
+
+ /**
+ * Required secret.
+ */
+ private String requiredSecret = null;
+ public void setRequiredSecret(String requiredSecret) {
+ this.requiredSecret = requiredSecret;
+ }
+
+
+ /**
+ * When client certificate information is presented in a form other than
+ * instances of {@link java.security.cert.X509Certificate} it needs to be
+ * converted before it can be used and this property controls which JSSE
+ * provider is used to perform the conversion.
+ */
+ private String clientCertProvider = null;
+ public String getClientCertProvider() { return clientCertProvider; }
+ public void setClientCertProvider(String clientCertProvider) {
+ this.clientCertProvider = clientCertProvider;
+ }
+
+
+ // --------------------------------------------------------- Public Methods
+
@Override
- public void recycle(boolean socketClosing) {
- super.recycle(socketClosing);
- if (socketClosing) {
- input = null;
- output = null;
+ protected boolean flushBufferedWrite() throws IOException {
+ if (hasDataToWrite()) {
+ socketWrapper.flush(false);
+ if (hasDataToWrite()) {
+ // There is data to write but go via Response to
+ // maintain a consistent view of non-blocking state
+ response.checkRegisterForWrite();
+ return true;
+ }
}
+ return false;
}
@Override
- protected void registerForEvent(boolean read, boolean write) {
- // NO-OP for BIO
+ protected void dispatchNonBlockingRead() {
+ if (available(true) > 0) {
+ super.dispatchNonBlockingRead();
+ }
}
+
@Override
- protected void resetTimeouts() {
- // NO-OP. The AJP BIO connector only uses the timeout value on the
- // SocketWrapper for async timeouts.
+ protected SocketState dispatchEndRequest() {
+ // Set keep alive timeout for next request if enabled
+ if (keepAliveTimeout > 0) {
+ socketWrapper.setReadTimeout(keepAliveTimeout);
+ }
+ recycle();
+ return SocketState.OPEN;
}
@Override
- protected void setupSocket(SocketWrapper<Socket> socketWrapper)
- throws IOException {
- input = socketWrapper.getSocket().getInputStream();
- output = socketWrapper.getSocket().getOutputStream();
+ public SocketState service(SocketWrapperBase<?> socket) throws IOException {
+
+ RequestInfo rp = request.getRequestProcessor();
+ rp.setStage(org.apache.coyote.Constants.STAGE_PARSE);
+
+ // Setting up the socket
+ this.socketWrapper = socket;
+
+ int soTimeout = endpoint.getSoTimeout();
+ boolean cping = false;
+
+ boolean keptAlive = false;
+
+ while (!getErrorState().isError() && !endpoint.isPaused()) {
+ // Parsing the request header
+ try {
+ // Get first message of the request
+ if (!readMessage(requestHeaderMessage, !keptAlive)) {
+ break;
+ }
+ // Set back timeout if keep alive timeout is enabled
+ if (keepAliveTimeout > 0) {
+ socketWrapper.setReadTimeout(soTimeout);
+ }
+ // Check message type, process right away and break if
+ // not regular request processing
+ int type = requestHeaderMessage.getByte();
+ if (type == Constants.JK_AJP13_CPING_REQUEST) {
+ if (endpoint.isPaused()) {
+ recycle();
+ break;
+ }
+ cping = true;
+ try {
+ socketWrapper.write(true, pongMessageArray, 0, pongMessageArray.length);
+ socketWrapper.flush(true);
+ } catch (IOException e) {
+ setErrorState(ErrorState.CLOSE_CONNECTION_NOW, e);
+ }
+ recycle();
+ continue;
+ } else if(type != Constants.JK_AJP13_FORWARD_REQUEST) {
+ // Unexpected packet type. Unread body packets should have
+ // been swallowed in finish().
+ if (getLog().isDebugEnabled()) {
+ getLog().debug("Unexpected message: " + type);
+ }
+ setErrorState(ErrorState.CLOSE_CONNECTION_NOW, null);
+ break;
+ }
+ keptAlive = true;
+ request.setStartTime(System.currentTimeMillis());
+ } catch (IOException e) {
+ setErrorState(ErrorState.CLOSE_CONNECTION_NOW, e);
+ break;
+ } catch (Throwable t) {
+ ExceptionUtils.handleThrowable(t);
+ getLog().debug(sm.getString("ajpprocessor.header.error"), t);
+ // 400 - Bad Request
+ response.setStatus(400);
+ setErrorState(ErrorState.CLOSE_CLEAN, t);
+ getAdapter().log(request, response, 0);
+ }
+
+ if (!getErrorState().isError()) {
+ // Setting up filters, and parse some request headers
+ rp.setStage(org.apache.coyote.Constants.STAGE_PREPARE);
+ try {
+ prepareRequest();
+ } catch (Throwable t) {
+ ExceptionUtils.handleThrowable(t);
+ getLog().debug(sm.getString("ajpprocessor.request.prepare"), t);
+ // 500 - Internal Server Error
+ response.setStatus(500);
+ setErrorState(ErrorState.CLOSE_CLEAN, t);
+ getAdapter().log(request, response, 0);
+ }
+ }
+
+ if (!getErrorState().isError() && !cping && endpoint.isPaused()) {
+ // 503 - Service unavailable
+ response.setStatus(503);
+ setErrorState(ErrorState.CLOSE_CLEAN, null);
+ getAdapter().log(request, response, 0);
+ }
+ cping = false;
+
+ // Process the request in the adapter
+ if (!getErrorState().isError()) {
+ try {
+ rp.setStage(org.apache.coyote.Constants.STAGE_SERVICE);
+ getAdapter().service(request, response);
+ } catch (InterruptedIOException e) {
+ setErrorState(ErrorState.CLOSE_CONNECTION_NOW, e);
+ } catch (Throwable t) {
+ ExceptionUtils.handleThrowable(t);
+ getLog().error(sm.getString("ajpprocessor.request.process"), t);
+ // 500 - Internal Server Error
+ response.setStatus(500);
+ setErrorState(ErrorState.CLOSE_CLEAN, t);
+ getAdapter().log(request, response, 0);
+ }
+ }
+
+ if (isAsync() && !getErrorState().isError()) {
+ break;
+ }
+
+ // Finish the response if not done yet
+ if (!responseFinished && getErrorState().isIoAllowed()) {
+ try {
+ action(ActionCode.COMMIT, null);
+ finishResponse();
+ } catch (IOException ioe){
+ setErrorState(ErrorState.CLOSE_CONNECTION_NOW, ioe);
+ } catch (Throwable t) {
+ ExceptionUtils.handleThrowable(t);
+ setErrorState(ErrorState.CLOSE_NOW, t);
+ }
+ }
+
+ // If there was an error, make sure the request is counted as
+ // and error, and update the statistics counter
+ if (getErrorState().isError()) {
+ response.setStatus(500);
+ }
+ request.updateCounters();
+
+ rp.setStage(org.apache.coyote.Constants.STAGE_KEEPALIVE);
+ // Set keep alive timeout for next request if enabled
+ if (keepAliveTimeout > 0) {
+ socketWrapper.setReadTimeout(keepAliveTimeout);
+ }
+
+ recycle();
+ }
+
+ rp.setStage(org.apache.coyote.Constants.STAGE_ENDED);
+
+ if (getErrorState().isError() || endpoint.isPaused()) {
+ return SocketState.CLOSED;
+ } else {
+ if (isAsync()) {
+ return SocketState.LONG;
+ } else {
+ return SocketState.OPEN;
+ }
+ }
}
@Override
- protected void setTimeout(SocketWrapper<Socket> socketWrapper,
- int timeout) throws IOException {
- socketWrapper.getSocket().setSoTimeout(timeout);
+ public void recycle() {
+ getAdapter().checkRecycled(request, response);
+ super.recycle();
+ request.recycle();
+ response.recycle();
+ first = true;
+ endOfStream = false;
+ waitingForBodyMessage = false;
+ empty = true;
+ replay = false;
+ responseFinished = false;
+ certificates.recycle();
+ swallowResponse = false;
+ bytesWritten = 0;
}
@Override
- protected int output(byte[] src, int offset, int length, boolean block)
- throws IOException {
- output.write(src, offset, length);
- return length;
+ public void pause() {
+ // NOOP for AJP
}
- @Override
- protected boolean read(byte[] buf, int pos, int n, boolean blockFirstRead)
+ // ------------------------------------------------------ Protected Methods
+
+ // Methods used by SocketInputBuffer
+ /**
+ * Read an AJP body message. Used to read both the 'special' packet in ajp13
+ * and to receive the data after we send a GET_BODY packet.
+ *
+ * @param block If there is no data available to read when this method is
+ * called, should this call block until data becomes available?
+ *
+ * @return <code>true</code> if at least one body byte was read, otherwise
+ * <code>false</code>
+ */
+ private boolean receive(boolean block) throws IOException {
+
+ bodyMessage.reset();
+
+ if (!readMessage(bodyMessage, block)) {
+ return false;
+ }
+
+ waitingForBodyMessage = false;
+
+ // No data received.
+ if (bodyMessage.getLen() == 0) {
+ // just the header
+ return false;
+ }
+ int blen = bodyMessage.peekInt();
+ if (blen == 0) {
+ return false;
+ }
+
+ bodyMessage.getBodyBytes(bodyBytes);
+ empty = false;
+ return true;
+ }
+
+
+ /**
+ * Read an AJP message.
+ *
+ * @param message The message to populate
+ * @param block If there is no data available to read when this method is
+ * called, should this call block until data becomes available?
+
+ * @return true if the message has been read, false if no data was read
+ *
+ * @throws IOException any other failure, including incomplete reads
+ */
+ private boolean readMessage(AjpMessage message, boolean block)
throws IOException {
- int read = 0;
- int res = 0;
- while (read < n) {
- res = input.read(buf, read + pos, n - read);
- if (res > 0) {
- read += res;
+ byte[] buf = message.getBuffer();
+
+ if (!read(buf, 0, Constants.H_SIZE, block)) {
+ return false;
+ }
+
+ int messageLength = message.processHeader(true);
+ if (messageLength < 0) {
+ // Invalid AJP header signature
+ throw new IOException(sm.getString("ajpmessage.invalidLength",
+ Integer.valueOf(messageLength)));
+ }
+ else if (messageLength == 0) {
+ // Zero length message.
+ return true;
+ }
+ else {
+ if (messageLength > message.getBuffer().length) {
+ // Message too long for the buffer
+ // Need to trigger a 400 response
+ throw new IllegalArgumentException(sm.getString(
+ "ajpprocessor.header.tooLong",
+ Integer.valueOf(messageLength),
+ Integer.valueOf(buf.length)));
+ }
+ read(buf, Constants.H_SIZE, messageLength, true);
+ return true;
+ }
+ }
+
+
+ /**
+ * Get more request body data from the web server and store it in the
+ * internal buffer.
+ * @param block <code>true</code> if this is blocking IO
+ * @return <code>true</code> if there is more data,
+ * <code>false</code> if not.
+ * @throws IOException An IO error occurred
+ */
+ protected boolean refillReadBuffer(boolean block) throws IOException {
+ // When using replay (e.g. after FORM auth) all the data to read has
+ // been buffered so there is no opportunity to refill the buffer.
+ if (replay) {
+ endOfStream = true; // we've read everything there is
+ }
+ if (endOfStream) {
+ return false;
+ }
+
+ if (first) {
+ first = false;
+ long contentLength = request.getContentLengthLong();
+ // - When content length > 0, AJP sends the first body message
+ // automatically.
+ // - When content length == 0, AJP does not send a body message.
+ // - When content length is unknown, AJP does not send the first
+ // body message automatically.
+ if (contentLength > 0) {
+ waitingForBodyMessage = true;
+ } else if (contentLength == 0) {
+ endOfStream = true;
+ return false;
+ }
+ }
+
+ // Request more data immediately
+ if (!waitingForBodyMessage) {
+ socketWrapper.write(true, getBodyMessageArray, 0, getBodyMessageArray.length);
+ socketWrapper.flush(true);
+ waitingForBodyMessage = true;
+ }
+
+ boolean moreData = receive(block);
+ if (!moreData && !waitingForBodyMessage) {
+ endOfStream = true;
+ }
+ return moreData;
+ }
+
+
+ /**
+ * After reading the request headers, we have to setup the request filters.
+ */
+ private void prepareRequest() {
+
+ // Translate the HTTP method code to a String.
+ byte methodCode = requestHeaderMessage.getByte();
+ if (methodCode != Constants.SC_M_JK_STORED) {
+ String methodName = Constants.getMethodForCode(methodCode - 1);
+ request.method().setString(methodName);
+ }
+
+ requestHeaderMessage.getBytes(request.protocol());
+ requestHeaderMessage.getBytes(request.requestURI());
+
+ requestHeaderMessage.getBytes(request.remoteAddr());
+ requestHeaderMessage.getBytes(request.remoteHost());
+ requestHeaderMessage.getBytes(request.localName());
+ request.setLocalPort(requestHeaderMessage.getInt());
+
+ boolean isSSL = requestHeaderMessage.getByte() != 0;
+ if (isSSL) {
+ request.scheme().setString("https");
+ }
+
+ // Decode headers
+ MimeHeaders headers = request.getMimeHeaders();
+
+ // Set this every time in case limit has been changed via JMX
+ headers.setLimit(endpoint.getMaxHeaderCount());
+
+ boolean contentLengthSet = false;
+ int hCount = requestHeaderMessage.getInt();
+ for(int i = 0 ; i < hCount ; i++) {
+ String hName = null;
+
+ // Header names are encoded as either an integer code starting
+ // with 0xA0, or as a normal string (in which case the first
+ // two bytes are the length).
+ int isc = requestHeaderMessage.peekInt();
+ int hId = isc & 0xFF;
+
+ MessageBytes vMB = null;
+ isc &= 0xFF00;
+ if(0xA000 == isc) {
+ requestHeaderMessage.getInt(); // To advance the read position
+ hName = Constants.getHeaderForCode(hId - 1);
+ vMB = headers.addValue(hName);
} else {
- throw new IOException(sm.getString("ajpprocessor.failedread"));
+ // reset hId -- if the header currently being read
+ // happens to be 7 or 8 bytes long, the code below
+ // will think it's the content-type header or the
+ // content-length header - SC_REQ_CONTENT_TYPE=7,
+ // SC_REQ_CONTENT_LENGTH=8 - leading to unexpected
+ // behaviour. see bug 5861 for more information.
+ hId = -1;
+ requestHeaderMessage.getBytes(tmpMB);
+ ByteChunk bc = tmpMB.getByteChunk();
+ vMB = headers.addValue(bc.getBuffer(),
+ bc.getStart(), bc.getLength());
+ }
+
+ requestHeaderMessage.getBytes(vMB);
+
+ if (hId == Constants.SC_REQ_CONTENT_LENGTH ||
+ (hId == -1 && tmpMB.equalsIgnoreCase("Content-Length"))) {
+ long cl = vMB.getLong();
+ if (contentLengthSet) {
+ response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
+ setErrorState(ErrorState.CLOSE_CLEAN, null);
+ } else {
+ contentLengthSet = true;
+ // Set the content-length header for the request
+ request.setContentLength(cl);
+ }
+ } else if (hId == Constants.SC_REQ_CONTENT_TYPE ||
+ (hId == -1 && tmpMB.equalsIgnoreCase("Content-Type"))) {
+ // just read the content-type header, so set it
+ ByteChunk bchunk = vMB.getByteChunk();
+ request.contentType().setBytes(bchunk.getBytes(),
+ bchunk.getOffset(),
+ bchunk.getLength());
}
}
- return true;
+ // Decode extra attributes
+ boolean secret = false;
+ byte attributeCode;
+ while ((attributeCode = requestHeaderMessage.getByte())
+ != Constants.SC_A_ARE_DONE) {
+
+ switch (attributeCode) {
+
+ case Constants.SC_A_REQ_ATTRIBUTE :
+ requestHeaderMessage.getBytes(tmpMB);
+ String n = tmpMB.toString();
+ requestHeaderMessage.getBytes(tmpMB);
+ String v = tmpMB.toString();
+ /*
+ * AJP13 misses to forward the local IP address and the
+ * remote port. Allow the AJP connector to add this info via
+ * private request attributes.
+ * We will accept the forwarded data and remove it from the
+ * public list of request attributes.
+ */
+ if(n.equals(Constants.SC_A_REQ_LOCAL_ADDR)) {
+ request.localAddr().setString(v);
+ } else if(n.equals(Constants.SC_A_REQ_REMOTE_PORT)) {
+ try {
+ request.setRemotePort(Integer.parseInt(v));
+ } catch (NumberFormatException nfe) {
+ // Ignore invalid value
+ }
+ } else if(n.equals(Constants.SC_A_SSL_PROTOCOL)) {
+ request.setAttribute(SSLSupport.PROTOCOL_VERSION_KEY, v);
+ } else {
+ request.setAttribute(n, v );
+ }
+ break;
+
+ case Constants.SC_A_CONTEXT :
+ requestHeaderMessage.getBytes(tmpMB);
+ // nothing
+ break;
+
+ case Constants.SC_A_SERVLET_PATH :
+ requestHeaderMessage.getBytes(tmpMB);
+ // nothing
+ break;
+
+ case Constants.SC_A_REMOTE_USER :
+ if (tomcatAuthorization || !tomcatAuthentication) {
+ // Implies tomcatAuthentication == false
+ requestHeaderMessage.getBytes(request.getRemoteUser());
+ request.setRemoteUserNeedsAuthorization(tomcatAuthorization);
+ } else {
+ // Ignore user information from reverse proxy
+ requestHeaderMessage.getBytes(tmpMB);
+ }
+ break;
+
+ case Constants.SC_A_AUTH_TYPE :
+ if (tomcatAuthentication) {
+ // ignore server
+ requestHeaderMessage.getBytes(tmpMB);
+ } else {
+ requestHeaderMessage.getBytes(request.getAuthType());
+ }
+ break;
+
+ case Constants.SC_A_QUERY_STRING :
+ requestHeaderMessage.getBytes(request.queryString());
+ break;
+
+ case Constants.SC_A_JVM_ROUTE :
+ requestHeaderMessage.getBytes(tmpMB);
+ // nothing
+ break;
+
+ case Constants.SC_A_SSL_CERT :
+ // SSL certificate extraction is lazy, moved to JkCoyoteHandler
+ requestHeaderMessage.getBytes(certificates);
+ break;
+
+ case Constants.SC_A_SSL_CIPHER :
+ requestHeaderMessage.getBytes(tmpMB);
+ request.setAttribute(SSLSupport.CIPHER_SUITE_KEY,
+ tmpMB.toString());
+ break;
+
+ case Constants.SC_A_SSL_SESSION :
+ requestHeaderMessage.getBytes(tmpMB);
+ request.setAttribute(SSLSupport.SESSION_ID_KEY,
+ tmpMB.toString());
+ break;
+
+ case Constants.SC_A_SSL_KEY_SIZE :
+ request.setAttribute(SSLSupport.KEY_SIZE_KEY,
+ Integer.valueOf(requestHeaderMessage.getInt()));
+ break;
+
+ case Constants.SC_A_STORED_METHOD:
+ requestHeaderMessage.getBytes(request.method());
+ break;
+
+ case Constants.SC_A_SECRET:
+ requestHeaderMessage.getBytes(tmpMB);
+ if (requiredSecret != null) {
+ secret = true;
+ if (!tmpMB.equals(requiredSecret)) {
+ response.setStatus(403);
+ setErrorState(ErrorState.CLOSE_CLEAN, null);
+ }
+ }
+ break;
+
+ default:
+ // Ignore unknown attribute for backward compatibility
+ break;
+
+ }
+
+ }
+
+ // Check if secret was submitted if required
+ if ((requiredSecret != null) && !secret) {
+ response.setStatus(403);
+ setErrorState(ErrorState.CLOSE_CLEAN, null);
+ }
+
+ // Check for a full URI (including protocol://host:port/)
+ ByteChunk uriBC = request.requestURI().getByteChunk();
+ if (uriBC.startsWithIgnoreCase("http", 0)) {
+
+ 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);
+ if (slashPos == -1) {
+ slashPos = uriBC.getLength();
+ // Set URI as "/"
+ request.requestURI().setBytes
+ (uriB, uriBCStart + pos + 1, 1);
+ } else {
+ request.requestURI().setBytes
+ (uriB, uriBCStart + slashPos,
+ uriBC.getLength() - slashPos);
+ }
+ MessageBytes hostMB = headers.setValue("host");
+ hostMB.setBytes(uriB, uriBCStart + pos + 3,
+ slashPos - pos - 3);
+ }
+
+ }
+
+ MessageBytes valueMB = request.getMimeHeaders().getValue("host");
+ parseHost(valueMB);
+
+ if (getErrorState().isError()) {
+ getAdapter().log(request, response, 0);
+ }
+ }
+
+
+ /**
+ * Parse host.
+ */
+ private void parseHost(MessageBytes valueMB) {
+
+ if (valueMB == null || valueMB.isNull()) {
+ // HTTP/1.0
+ request.setServerPort(request.getLocalPort());
+ try {
+ request.serverName().duplicate(request.localName());
+ } catch (IOException e) {
+ response.setStatus(400);
+ setErrorState(ErrorState.CLOSE_CLEAN, e);
+ }
+ return;
+ }
+
+ ByteChunk valueBC = valueMB.getByteChunk();
+ byte[] valueB = valueBC.getBytes();
+ int valueL = valueBC.getLength();
+ int valueS = valueBC.getStart();
+ int colonPos = -1;
+ if (hostNameC.length < valueL) {
+ hostNameC = new char[valueL];
+ }
+
+ boolean ipv6 = (valueB[valueS] == '[');
+ boolean bracketClosed = false;
+ for (int i = 0; i < valueL; i++) {
+ char b = (char) valueB[i + valueS];
+ hostNameC[i] = b;
+ if (b == ']') {
+ bracketClosed = true;
+ } else if (b == ':') {
+ if (!ipv6 || bracketClosed) {
+ colonPos = i;
+ break;
+ }
+ }
+ }
+
+ if (colonPos < 0) {
+ request.serverName().setChars(hostNameC, 0, valueL);
+ } else {
+
+ request.serverName().setChars(hostNameC, 0, colonPos);
+
+ int port = 0;
+ int mult = 1;
+ for (int i = valueL - 1; i > colonPos; i--) {
+ int charValue = HexUtils.getDec(valueB[i + valueS]);
+ if (charValue == -1) {
+ // Invalid character
+ // 400 - Bad request
+ response.setStatus(400);
+ setErrorState(ErrorState.CLOSE_CLEAN, null);
+ break;
+ }
+ port = port + (charValue * mult);
+ mult = 10 * mult;
+ }
+ request.setServerPort(port);
+ }
+ }
+
+
+ /**
+ * When committing the response, we have to validate the set of headers, as
+ * well as setup the response filters.
+ */
+ @Override
+ protected final void prepareResponse() throws IOException {
+
+ response.setCommitted(true);
+
+ tmpMB.recycle();
+ responseMsgPos = -1;
+ responseMessage.reset();
+ responseMessage.appendByte(Constants.JK_AJP13_SEND_HEADERS);
+
+ // Responses with certain status codes are not permitted to include a
+ // response body.
+ int statusCode = response.getStatus();
+ if (statusCode < 200 || statusCode == 204 || statusCode == 205 ||
+ statusCode == 304) {
+ // No entity body
+ swallowResponse = true;
+ }
+
+ // Responses to HEAD requests are not permitted to include a response
+ // body.
+ MessageBytes methodMB = request.method();
+ if (methodMB.equals("HEAD")) {
+ // No entity body
+ swallowResponse = true;
+ }
+
+ // HTTP header contents
+ responseMessage.appendInt(statusCode);
+ // Reason phrase is optional but mod_jk + httpd 2.x fails with a null
+ // reason phrase - bug 45026
+ tmpMB.setString(Integer.toString(response.getStatus()));
+ responseMessage.appendBytes(tmpMB);
+
+ // Special headers
+ MimeHeaders headers = response.getMimeHeaders();
+ String contentType = response.getContentType();
+ if (contentType != null) {
+ headers.setValue("Content-Type").setString(contentType);
+ }
+ String contentLanguage = response.getContentLanguage();
+ if (contentLanguage != null) {
+ headers.setValue("Content-Language").setString(contentLanguage);
+ }
+ long contentLength = response.getContentLengthLong();
+ if (contentLength >= 0) {
+ headers.setValue("Content-Length").setLong(contentLength);
+ }
+
+ // Other headers
+ int numHeaders = headers.size();
+ responseMessage.appendInt(numHeaders);
+ for (int i = 0; i < numHeaders; i++) {
+ MessageBytes hN = headers.getName(i);
+ int hC = Constants.getResponseAjpIndex(hN.toString());
+ if (hC > 0) {
+ responseMessage.appendInt(hC);
+ }
+ else {
+ responseMessage.appendBytes(hN);
+ }
+ MessageBytes hV=headers.getValue(i);
+ responseMessage.appendBytes(hV);
+ }
+
+ // Write to buffer
+ responseMessage.end();
+ socketWrapper.write(true, responseMessage.getBuffer(), 0, responseMessage.getLen());
+ socketWrapper.flush(true);
+ }
+
+
+ /**
+ * Callback to write data from the buffer.
+ */
+ @Override
+ protected final void flush() throws IOException {
+ // Calling code should ensure that there is no data in the buffers for
+ // non-blocking writes.
+ // TODO Validate the assertion above
+ if (!responseFinished) {
+ if (ajpFlush) {
+ // Send the flush message
+ socketWrapper.write(true, flushMessageArray, 0, flushMessageArray.length);
+ }
+ socketWrapper.flush(true);
+ }
+ }
+
+
+ /**
+ * Finish AJP response.
+ */
+ @Override
+ protected final void finishResponse() throws IOException {
+ if (responseFinished)
+ return;
+
+ responseFinished = true;
+
+ // Swallow the unread body packet if present
+ if (waitingForBodyMessage || first && request.getContentLengthLong() > 0) {
+ refillReadBuffer(true);
+ }
+
+ // Add the end message
+ if (getErrorState().isError()) {
+ socketWrapper.write(true, endAndCloseMessageArray, 0, endAndCloseMessageArray.length);
+ } else {
+ socketWrapper.write(true, endMessageArray, 0, endMessageArray.length);
+ }
+ socketWrapper.flush(true);
+ }
+
+
+ @Override
+ protected final void ack() {
+ // NO-OP for AJP
+ }
+
+
+ @Override
+ protected final int available(boolean doRead) {
+ if (endOfStream) {
+ return 0;
+ }
+ 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.
+ return 1;
+ }
+ }
+ if (empty) {
+ return 0;
+ } else {
+ return bodyBytes.getByteChunk().getLength();
+ }
+ }
+
+
+ @Override
+ protected final void setRequestBody(ByteChunk body) {
+ int length = body.getLength();
+ bodyBytes.setBytes(body.getBytes(), body.getStart(), length);
+ request.setContentLength(length);
+ first = false;
+ empty = false;
+ replay = true;
+ endOfStream = false;
+ }
+
+
+ @Override
+ protected final void setSwallowResponse() {
+ swallowResponse = true;
+ }
+
+
+ @Override
+ protected final void disableSwallowRequest() {
+ /* NO-OP
+ * With AJP, Tomcat controls when the client sends request body data. At
+ * most there will be a single packet to read and that will be handled
+ * in finishResponse().
+ */
+ }
+
+
+ @Override
+ protected final boolean getPopulateRequestAttributesFromSocket() {
+ // NO-OPs the attribute requests since they are pre-populated when
+ // parsing the first AJP message.
+ return false;
+ }
+
+
+ @Override
+ protected final void populateRequestAttributeRemoteHost() {
+ // Get remote host name using a DNS resolution
+ if (request.remoteHost().isNull()) {
+ try {
+ request.remoteHost().setString(InetAddress.getByName
+ (request.remoteAddr().toString()).getHostName());
+ } catch (IOException iex) {
+ // Ignore
+ }
+ }
+ }
+
+
+ @Override
+ protected final void populateSslRequestAttributes() {
+ if (!certificates.isNull()) {
+ ByteChunk certData = certificates.getByteChunk();
+ X509Certificate jsseCerts[] = null;
+ ByteArrayInputStream bais =
+ new ByteArrayInputStream(certData.getBytes(),
+ certData.getStart(),
+ certData.getLength());
+ // Fill the elements.
+ try {
+ CertificateFactory cf;
+ String clientCertProvider = getClientCertProvider();
+ if (clientCertProvider == null) {
+ cf = CertificateFactory.getInstance("X.509");
+ } else {
+ cf = CertificateFactory.getInstance("X.509",
+ clientCertProvider);
+ }
+ while(bais.available() > 0) {
+ X509Certificate cert = (X509Certificate)
+ cf.generateCertificate(bais);
+ if(jsseCerts == null) {
+ jsseCerts = new X509Certificate[1];
+ jsseCerts[0] = cert;
+ } else {
+ X509Certificate [] temp = new X509Certificate[jsseCerts.length+1];
+ System.arraycopy(jsseCerts,0,temp,0,jsseCerts.length);
+ temp[jsseCerts.length] = cert;
+ jsseCerts = temp;
+ }
+ }
+ } catch (java.security.cert.CertificateException e) {
+ getLog().error(sm.getString("ajpprocessor.certs.fail"), e);
+ return;
+ } catch (NoSuchProviderException e) {
+ getLog().error(sm.getString("ajpprocessor.certs.fail"), e);
+ return;
+ }
+ request.setAttribute(SSLSupport.CERTIFICATE_KEY, jsseCerts);
+ }
+ }
+
+
+ @Override
+ protected final boolean isRequestBodyFullyRead() {
+ return endOfStream;
+ }
+
+
+ @Override
+ protected final void registerReadInterest() {
+ socketWrapper.registerReadInterest();
+ }
+
+
+ @Override
+ protected final boolean isReady() {
+ return responseMsgPos == -1 && socketWrapper.isReadyForWrite();
+ }
+
+
+ @Override
+ protected final void executeDispatches(SocketWrapperBase<?> wrapper) {
+ wrapper.executeNonBlockingDispatches(getIteratorAndClearDispatches());
+ }
+
+
+ /**
+ * Read at least the specified amount of bytes, and place them
+ * in the input buffer. Note that if any data is available to read then this
+ * method will always block until at least the specified number of bytes
+ * have been read.
+ *
+ * @param buf Buffer to read data into
+ * @param pos Start position
+ * @param n The minimum number of bytes to read
+ * @param block If there is no data available to read when this method is
+ * called, should this call block until data becomes available?
+ * @return <code>true</code> if the requested number of bytes were read
+ * else <code>false</code>
+ * @throws IOException
+ */
+ private boolean read(byte[] buf, int pos, int n, boolean block) throws IOException {
+ int read = socketWrapper.read(block, buf, pos, n);
+ if (read > 0 && read < n) {
+ int left = n - read;
+ int start = pos + read;
+ while (left > 0) {
+ read = socketWrapper.read(true, buf, start, left);
+ if (read == -1) {
+ throw new EOFException();
+ }
+ left = left - read;
+ start = start + read;
+ }
+ } else if (read == -1) {
+ throw new EOFException();
+ }
+
+ return read > 0;
+ }
+
+
+ /**
+ * @deprecated Unused. Will be removed in Tomcat 9. Use
+ * {@link #doWrite(ByteBuffer)}
+ */
+ private void writeData(ByteChunk chunk) throws IOException {
+ boolean blocking = (response.getWriteListener() == null);
+
+ int len = chunk.getLength();
+ int off = 0;
+
+ // Write this chunk
+ while (len > 0) {
+ int thisTime = Math.min(len, outputMaxChunkSize);
+
+ responseMessage.reset();
+ responseMessage.appendByte(Constants.JK_AJP13_SEND_BODY_CHUNK);
+ responseMessage.appendBytes(chunk.getBytes(), chunk.getOffset() + off, thisTime);
+ responseMessage.end();
+ socketWrapper.write(blocking, responseMessage.getBuffer(), 0, responseMessage.getLen());
+ socketWrapper.flush(blocking);
+
+ len -= thisTime;
+ off += thisTime;
+ }
+
+ bytesWritten += off;
+ }
+
+
+ private void writeData(ByteBuffer chunk) throws IOException {
+ boolean blocking = (response.getWriteListener() == null);
+
+ int len = chunk.remaining();
+ int off = 0;
+
+ // Write this chunk
+ while (len > 0) {
+ int thisTime = Math.min(len, outputMaxChunkSize);
+
+ responseMessage.reset();
+ responseMessage.appendByte(Constants.JK_AJP13_SEND_BODY_CHUNK);
+ chunk.limit(chunk.position() + thisTime);
+ responseMessage.appendBytes(chunk);
+ responseMessage.end();
+ socketWrapper.write(blocking, responseMessage.getBuffer(), 0, responseMessage.getLen());
+ socketWrapper.flush(blocking);
+
+ len -= thisTime;
+ off += thisTime;
+ }
+
+ bytesWritten += off;
+ }
+
+
+ private boolean hasDataToWrite() {
+ return responseMsgPos != -1 || socketWrapper.hasDataToWrite();
+ }
+
+
+ @Override
+ protected Log getLog() {
+ return log;
+ }
+
+
+ // ------------------------------------- InputStreamInputBuffer Inner Class
+
+ /**
+ * This class is an input buffer which will read its data from an input
+ * stream.
+ */
+ protected class SocketInputBuffer implements InputBuffer {
+
+ /**
+ * @deprecated Unused. Will be removed in Tomcat 9. Use
+ * {@link #doRead(ApplicationBufferHandler)}
+ */
+ @Override
+ public int doRead(ByteChunk chunk) throws IOException {
+
+ if (endOfStream) {
+ return -1;
+ }
+ if (empty) {
+ if (!refillReadBuffer(true)) {
+ return -1;
+ }
+ }
+ ByteChunk bc = bodyBytes.getByteChunk();
+ chunk.setBytes(bc.getBuffer(), bc.getStart(), bc.getLength());
+ empty = true;
+ return chunk.getLength();
+ }
+
+ @Override
+ public int doRead(ApplicationBufferHandler handler) throws IOException {
+
+ if (endOfStream) {
+ return -1;
+ }
+ if (empty) {
+ if (!refillReadBuffer(true)) {
+ return -1;
+ }
+ }
+ ByteChunk bc = bodyBytes.getByteChunk();
+ handler.setByteBuffer(ByteBuffer.wrap(bc.getBuffer(), bc.getStart(), bc.getLength()));
+ empty = true;
+ return handler.getByteBuffer().remaining();
+ }
+ }
+
+
+ // ----------------------------------- OutputStreamOutputBuffer Inner Class
+
+ /**
+ * This class is an output buffer which will write data to an output
+ * stream.
+ */
+ protected class SocketOutputBuffer implements OutputBuffer {
+
+ /**
+ * @deprecated Unused. Will be removed in Tomcat 9. Use
+ * {@link #doWrite(ByteBuffer)}
+ */
+ @Override
+ public int doWrite(ByteChunk chunk) throws IOException {
+
+ if (!response.isCommitted()) {
+ // Validate and write response headers
+ try {
+ prepareResponse();
+ } catch (IOException e) {
+ setErrorState(ErrorState.CLOSE_CONNECTION_NOW, e);
+ }
+ }
+
+ if (!swallowResponse) {
+ writeData(chunk);
+ }
+ return chunk.getLength();
+ }
+
+ @Override
+ public int doWrite(ByteBuffer chunk) throws IOException {
+
+ if (!response.isCommitted()) {
+ // Validate and write response headers
+ try {
+ prepareResponse();
+ } catch (IOException e) {
+ setErrorState(ErrorState.CLOSE_CONNECTION_NOW, e);
+ }
+ }
+
+ int len = 0;
+ if (!swallowResponse) {
+ try {
+ len = chunk.remaining();
+ writeData(chunk);
+ len -= chunk.remaining();
+ } catch (IOException ioe) {
+ setErrorState(ErrorState.CLOSE_CONNECTION_NOW, ioe);
+ // Re-throw
+ throw ioe;
+ }
+ }
+ return len;
+ }
+
+ @Override
+ public long getBytesWritten() {
+ return bytesWritten;
+ }
}
}
diff --git a/java/org/apache/coyote/ajp/AjpProtocol.java b/java/org/apache/coyote/ajp/AjpProtocol.java
deleted file mode 100644
index 8cf946b..0000000
--- a/java/org/apache/coyote/ajp/AjpProtocol.java
+++ /dev/null
@@ -1,144 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.coyote.ajp;
-
-import java.net.Socket;
-
-import org.apache.coyote.AbstractProtocol;
-import org.apache.coyote.Processor;
-import org.apache.juli.logging.Log;
-import org.apache.juli.logging.LogFactory;
-import org.apache.tomcat.util.net.AbstractEndpoint;
-import org.apache.tomcat.util.net.JIoEndpoint;
-import org.apache.tomcat.util.net.JIoEndpoint.Handler;
-import org.apache.tomcat.util.net.SSLImplementation;
-import org.apache.tomcat.util.net.SocketWrapper;
-
-
-/**
- * Abstract the protocol implementation, including threading, etc.
- * Processor is single threaded and specific to stream-based protocols,
- * will not fit Jk protocols like JNI.
- *
- * @author Remy Maucherat
- * @author Costin Manolache
- */
-public class AjpProtocol extends AbstractAjpProtocol<Socket> {
-
-
- private static final Log log = LogFactory.getLog(AjpProtocol.class);
-
- @Override
- protected Log getLog() { return log; }
-
-
- @Override
- protected AbstractEndpoint.Handler getHandler() {
- return cHandler;
- }
-
-
- // ------------------------------------------------------------ Constructor
-
-
- public AjpProtocol() {
- endpoint = new JIoEndpoint();
- cHandler = new AjpConnectionHandler(this);
- ((JIoEndpoint) endpoint).setHandler(cHandler);
- setSoLinger(Constants.DEFAULT_CONNECTION_LINGER);
- setSoTimeout(Constants.DEFAULT_CONNECTION_TIMEOUT);
- setTcpNoDelay(Constants.DEFAULT_TCP_NO_DELAY);
- }
-
-
- // ----------------------------------------------------- Instance Variables
-
-
- /**
- * Connection handler for AJP.
- */
- private final AjpConnectionHandler cHandler;
-
-
- // ----------------------------------------------------- JMX related methods
-
- @Override
- protected String getNamePrefix() {
- return ("ajp-bio");
- }
-
-
- // -------------------------------------- AjpConnectionHandler Inner Class
-
-
- protected static class AjpConnectionHandler
- extends AbstractAjpConnectionHandler<Socket,AjpProcessor>
- implements Handler {
-
- protected final AjpProtocol proto;
-
- public AjpConnectionHandler(AjpProtocol proto) {
- this.proto = proto;
- }
-
- @Override
- protected AbstractProtocol<Socket> getProtocol() {
- return proto;
- }
-
- @Override
- protected Log getLog() {
- return log;
- }
-
- @Override
- public SSLImplementation getSslImplementation() {
- // AJP does not support SSL
- return null;
- }
-
- /**
- * Expected to be used by the handler once the processor is no longer
- * required.
- *
- * @param socket Ignored for BIO
- * @param processor
- * @param isSocketClosing
- * @param addToPoller Ignored for BIO
- */
- @Override
- public void release(SocketWrapper<Socket> socket,
- Processor<Socket> processor, boolean isSocketClosing,
- boolean addToPoller) {
- processor.recycle(isSocketClosing);
- recycledProcessors.push(processor);
- }
-
-
- @Override
- protected AjpProcessor createProcessor() {
- AjpProcessor processor = new AjpProcessor(proto.packetSize, (JIoEndpoint)proto.endpoint);
- proto.configureProcessor(processor);
- register(processor);
- return processor;
- }
-
- @Override
- public void beforeHandshake(SocketWrapper<Socket> socket) {
- }
- }
-}
diff --git a/java/org/apache/coyote/ajp/Constants.java b/java/org/apache/coyote/ajp/Constants.java
index 2ffa29f..2fb6c34 100644
--- a/java/org/apache/coyote/ajp/Constants.java
+++ b/java/org/apache/coyote/ajp/Constants.java
@@ -25,14 +25,7 @@ import java.util.Hashtable;
*/
public final class Constants {
- /**
- * Package name.
- */
- public static final String Package = "org.apache.coyote.ajp";
-
- public static final int DEFAULT_CONNECTION_LINGER = -1;
public static final int DEFAULT_CONNECTION_TIMEOUT = -1;
- public static final boolean DEFAULT_TCP_NO_DELAY = true;
// Prefix codes for message types from server to container
public static final byte JK_AJP13_FORWARD_REQUEST = 2;
diff --git a/java/org/apache/coyote/ajp/LocalStrings.properties b/java/org/apache/coyote/ajp/LocalStrings.properties
index fc7b387..b9dddeb 100644
--- a/java/org/apache/coyote/ajp/LocalStrings.properties
+++ b/java/org/apache/coyote/ajp/LocalStrings.properties
@@ -12,10 +12,13 @@
# 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.
+ajpprotocol.noSSL=SSL is not supported with AJP. The SSL host configuration for [{0}] was ignored
+ajpprotocol.noUpgrade=Upgrade is not supported with AJP. The UpgradeProtocol configuration for [{0}] was ignored
+ajpprotocol.noUpgradeHandler=Upgrade is not supported with AJP. The HttpUpgradeHandler [{0}] can not be processed
+
ajpnioprotocol.releaseStart=Iterating through our connections to release a socket channel [{0}]
ajpnioprotocol.releaseEnd=Done iterating through our connections to release a socket channel [{0}] released [{1}]
-ajpprocessor.comet.notsupported=Comet is not supported by the AJP protocol
ajpprocessor.failedread=Socket read failed
ajpprocessor.failedsend=Failed to send AJP message
ajpprocessor.header.error=Header message parsing failed
@@ -24,9 +27,6 @@ ajpprocessor.readtimeout=Timeout attempting to read data from the socket
ajpprocessor.request.prepare=Error preparing request
ajpprocessor.request.process=Error processing request
ajpprocessor.certs.fail=Certificate conversion failed
-ajpprocessor.comet.notsupported=The Comet protocol is not supported by this connector
-ajpprocessor.ssl.notsupported=The SSL protocol is not supported by this connector
-ajpprocessor.httpupgrade.notsupported=HTTP upgrade is not supported by the AJP protocol
ajpmessage.null=Cannot append null value
ajpmessage.overflow=Overflow error for buffer adding {0} bytes at position {1}
diff --git a/java/org/apache/coyote/http11/AbstractHttp11JsseProtocol.java b/java/org/apache/coyote/http11/AbstractHttp11JsseProtocol.java
index e2f4ac1..92be417 100644
--- a/java/org/apache/coyote/http11/AbstractHttp11JsseProtocol.java
+++ b/java/org/apache/coyote/http11/AbstractHttp11JsseProtocol.java
@@ -16,107 +16,37 @@
*/
package org.apache.coyote.http11;
-import org.apache.tomcat.util.net.SSLImplementation;
+import org.apache.tomcat.util.net.AbstractJsseEndpoint;
+import org.apache.tomcat.util.net.openssl.OpenSSLImplementation;
public abstract class AbstractHttp11JsseProtocol<S>
extends AbstractHttp11Protocol<S> {
- protected SSLImplementation sslImplementation = null;
-
- public String getAlgorithm() { return endpoint.getAlgorithm();}
- public void setAlgorithm(String s ) { endpoint.setAlgorithm(s);}
-
- public String getClientAuth() { return endpoint.getClientAuth();}
- public void setClientAuth(String s ) { endpoint.setClientAuth(s);}
-
- public String getKeystoreFile() { return endpoint.getKeystoreFile();}
- public void setKeystoreFile(String s ) { endpoint.setKeystoreFile(s);}
-
- public String getKeystorePass() { return endpoint.getKeystorePass();}
- public void setKeystorePass(String s ) { endpoint.setKeystorePass(s);}
-
- public String getKeystoreType() { return endpoint.getKeystoreType();}
- public void setKeystoreType(String s ) { endpoint.setKeystoreType(s);}
-
- public String getKeystoreProvider() {
- return endpoint.getKeystoreProvider();
- }
- public void setKeystoreProvider(String s ) {
- endpoint.setKeystoreProvider(s);
- }
-
- public String getSslProtocol() { return endpoint.getSslProtocol();}
- public void setSslProtocol(String s) { endpoint.setSslProtocol(s);}
-
- public String getCiphers() { return endpoint.getCiphers();}
- public void setCiphers(String s) { endpoint.setCiphers(s);}
- public String[] getCiphersUsed() { return endpoint.getCiphersUsed();}
-
- public String getKeyAlias() { return endpoint.getKeyAlias();}
- public void setKeyAlias(String s ) { endpoint.setKeyAlias(s);}
-
- public String getKeyPass() { return endpoint.getKeyPass();}
- public void setKeyPass(String s ) { endpoint.setKeyPass(s);}
-
- public void setTruststoreFile(String f){ endpoint.setTruststoreFile(f);}
- public String getTruststoreFile(){ return endpoint.getTruststoreFile();}
-
- public void setTruststorePass(String p){ endpoint.setTruststorePass(p);}
- public String getTruststorePass(){return endpoint.getTruststorePass();}
-
- public void setTruststoreType(String t){ endpoint.setTruststoreType(t);}
- public String getTruststoreType(){ return endpoint.getTruststoreType();}
-
- public void setTruststoreProvider(String t){
- endpoint.setTruststoreProvider(t);
- }
- public String getTruststoreProvider(){
- return endpoint.getTruststoreProvider();
+ public AbstractHttp11JsseProtocol(AbstractJsseEndpoint<S> endpoint) {
+ super(endpoint);
}
- public void setTruststoreAlgorithm(String a){
- endpoint.setTruststoreAlgorithm(a);
- }
- public String getTruststoreAlgorithm(){
- return endpoint.getTruststoreAlgorithm();
- }
- public void setTrustMaxCertLength(String s){
- endpoint.setTrustMaxCertLength(s);
- }
- public String getTrustMaxCertLength(){
- return endpoint.getTrustMaxCertLength();
+ @Override
+ protected AbstractJsseEndpoint<S> getEndpoint() {
+ // Over-ridden to add cast
+ return (AbstractJsseEndpoint<S>) super.getEndpoint();
}
- public void setCrlFile(String s){endpoint.setCrlFile(s);}
- public String getCrlFile(){ return endpoint.getCrlFile();}
-
- public void setSessionCacheSize(String s){endpoint.setSessionCacheSize(s);}
- public String getSessionCacheSize(){ return endpoint.getSessionCacheSize();}
-
- public void setSessionTimeout(String s){endpoint.setSessionTimeout(s);}
- public String getSessionTimeout(){ return endpoint.getSessionTimeout();}
- public void setAllowUnsafeLegacyRenegotiation(String s) {
- endpoint.setAllowUnsafeLegacyRenegotiation(s);
- }
- public String getAllowUnsafeLegacyRenegotiation() {
- return endpoint.getAllowUnsafeLegacyRenegotiation();
+ protected String getSslImplemenationShortName() {
+ if (OpenSSLImplementation.class.getName().equals(getSslImplementationName())) {
+ return "openssl";
+ }
+ return "jsse";
}
- private String sslImplementationName = null;
- public String getSslImplementationName() { return sslImplementationName; }
- public void setSslImplementationName(String s) {
- this.sslImplementationName = s;
- }
+ public String getSslImplementationName() { return getEndpoint().getSslImplementationName(); }
+ public void setSslImplementationName(String s) { getEndpoint().setSslImplementationName(s); }
- // ------------------------------------------------------- Lifecycle methods
- @Override
- public void init() throws Exception {
- // SSL implementation needs to be in place before end point is
- // initialized
- sslImplementation = SSLImplementation.getInstance(sslImplementationName);
- super.init();
+ public int getSniParseLimit() { return getEndpoint().getSniParseLimit(); }
+ public void setSniParseLimit(int sniParseLimit) {
+ getEndpoint().setSniParseLimit(sniParseLimit);
}
}
diff --git a/java/org/apache/coyote/http11/AbstractHttp11Processor.java b/java/org/apache/coyote/http11/AbstractHttp11Processor.java
deleted file mode 100644
index d2adcdc..0000000
--- a/java/org/apache/coyote/http11/AbstractHttp11Processor.java
+++ /dev/null
@@ -1,1865 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.coyote.http11;
-
-import java.io.IOException;
-import java.io.InterruptedIOException;
-import java.nio.ByteBuffer;
-import java.util.Locale;
-import java.util.Set;
-import java.util.StringTokenizer;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.regex.Pattern;
-
-import javax.servlet.RequestDispatcher;
-import javax.servlet.http.HttpServletResponse;
-
-import org.apache.coyote.AbstractProcessor;
-import org.apache.coyote.ActionCode;
-import org.apache.coyote.AsyncContextCallback;
-import org.apache.coyote.ErrorState;
-import org.apache.coyote.RequestInfo;
-import org.apache.coyote.UpgradeToken;
-import org.apache.coyote.http11.filters.BufferedInputFilter;
-import org.apache.coyote.http11.filters.ChunkedInputFilter;
-import org.apache.coyote.http11.filters.ChunkedOutputFilter;
-import org.apache.coyote.http11.filters.GzipOutputFilter;
-import org.apache.coyote.http11.filters.IdentityInputFilter;
-import org.apache.coyote.http11.filters.IdentityOutputFilter;
-import org.apache.coyote.http11.filters.SavedRequestInputFilter;
-import org.apache.coyote.http11.filters.VoidInputFilter;
-import org.apache.coyote.http11.filters.VoidOutputFilter;
-import org.apache.tomcat.util.ExceptionUtils;
-import org.apache.tomcat.util.buf.Ascii;
-import org.apache.tomcat.util.buf.ByteChunk;
-import org.apache.tomcat.util.buf.HexUtils;
-import org.apache.tomcat.util.buf.MessageBytes;
-import org.apache.tomcat.util.http.FastHttpDateFormat;
-import org.apache.tomcat.util.http.MimeHeaders;
-import org.apache.tomcat.util.log.UserDataHelper;
-import org.apache.tomcat.util.net.AbstractEndpoint;
-import org.apache.tomcat.util.net.AbstractEndpoint.Handler.SocketState;
-import org.apache.tomcat.util.net.DispatchType;
-import org.apache.tomcat.util.net.SocketStatus;
-import org.apache.tomcat.util.net.SocketWrapper;
-import org.apache.tomcat.util.res.StringManager;
-
-public abstract class AbstractHttp11Processor<S> extends AbstractProcessor<S> {
-
- private final UserDataHelper userDataHelper;
-
-
- /**
- * The string manager for this package.
- */
- protected static final StringManager sm =
- StringManager.getManager(Constants.Package);
-
-
- /**
- * Input.
- */
- protected AbstractInputBuffer<S> inputBuffer ;
-
-
- /**
- * Output.
- */
- protected AbstractOutputBuffer<S> outputBuffer;
-
-
- /**
- * 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;
-
-
- /**
- * Keep-alive.
- */
- protected volatile boolean keepAlive = true;
-
-
- /**
- * Flag used to indicate that the socket should be kept open (e.g. for keep
- * alive or send file.
- */
- protected boolean openSocket = false;
-
-
- /**
- * Flag used to indicate that the socket should treat the next request
- * processed like a keep-alive connection - i.e. one where there may not be
- * any data to process. The initial value of this flag on entering the
- * process method is different for connectors that use polling (NIO / APR -
- * data is always expected) compared to those that use blocking (BIO - data
- * is only expected if the connection isn't in the keep-alive state).
- */
- protected boolean keptAlive;
-
-
- /**
- * Flag that indicates that send file processing is in progress and that the
- * socket should not be returned to the poller (where a poller is used).
- */
- protected boolean sendfileInProgress = false;
-
-
- /**
- * Flag that indicates if the request headers have been completely read.
- */
- protected boolean readComplete = true;
-
- /**
- * HTTP/1.1 flag.
- */
- protected boolean http11 = true;
-
-
- /**
- * HTTP/0.9 flag.
- */
- protected boolean http09 = false;
-
-
- /**
- * Content delimiter for the request (if false, the connection will
- * be closed at the end of the request).
- */
- protected boolean contentDelimitation = true;
-
-
- /**
- * Is there an expectation ?
- */
- protected boolean expectation = false;
-
-
- /**
- * Comet used.
- */
- protected boolean comet = false;
-
-
- /**
- * Regular expression that defines the restricted user agents.
- */
- protected Pattern restrictedUserAgents = null;
-
-
- /**
- * Maximum number of Keep-Alive requests to honor.
- */
- protected int maxKeepAliveRequests = -1;
-
- /**
- * The number of seconds Tomcat will wait for a subsequent request
- * before closing the connection.
- */
- protected int keepAliveTimeout = -1;
-
- /**
- * Maximum timeout on uploads. 5 minutes as in Apache HTTPD server.
- */
- protected int connectionUploadTimeout = 300000;
-
-
- /**
- * Flag to disable setting a different time-out on uploads.
- */
- protected boolean disableUploadTimeout = false;
-
-
- /**
- * Allowed compression level.
- */
- protected int compressionLevel = 0;
-
-
- /**
- * Minimum content size to make compression.
- */
- protected int compressionMinSize = 2048;
-
-
- /**
- * Max saved post size.
- */
- protected int maxSavePostSize = 4 * 1024;
-
-
- /**
- * Regular expression that defines the user agents to not use gzip with
- */
- protected Pattern noCompressionUserAgents = null;
-
- /**
- * List of MIMES which could be gzipped
- */
- protected String[] compressableMimeTypes =
- { "text/html", "text/xml", "text/plain" };
-
-
- /**
- * Host name (used to avoid useless B2C conversion on the host name).
- */
- protected char[] hostNameC = new char[0];
-
-
- /**
- * Allow a customized the server header for the tin-foil hat folks.
- */
- protected String server = null;
-
-
- /**
- * Instance of the new protocol to use after the HTTP connection has been
- * upgraded.
- */
- protected UpgradeToken upgradeToken = null;
-
-
- public AbstractHttp11Processor(AbstractEndpoint<S> endpoint) {
- super(endpoint);
- userDataHelper = new UserDataHelper(getLog());
- }
-
-
- /**
- * Set compression level.
- */
- public void setCompression(String compression) {
- if (compression.equals("on")) {
- this.compressionLevel = 1;
- } else if (compression.equals("force")) {
- this.compressionLevel = 2;
- } else if (compression.equals("off")) {
- this.compressionLevel = 0;
- } else {
- try {
- // Try to parse compression as an int, which would give the
- // minimum compression size
- compressionMinSize = Integer.parseInt(compression);
- this.compressionLevel = 1;
- } catch (Exception e) {
- this.compressionLevel = 0;
- }
- }
- }
-
- /**
- * Set Minimum size to trigger compression.
- */
- public void setCompressionMinSize(int compressionMinSize) {
- this.compressionMinSize = compressionMinSize;
- }
-
-
- /**
- * Set no compression user agent pattern. Regular expression as supported
- * by {@link Pattern}.
- *
- * ie: "gorilla|desesplorer|tigrus"
- */
- public void setNoCompressionUserAgents(String noCompressionUserAgents) {
- if (noCompressionUserAgents == null || noCompressionUserAgents.length() == 0) {
- this.noCompressionUserAgents = null;
- } else {
- this.noCompressionUserAgents =
- Pattern.compile(noCompressionUserAgents);
- }
- }
-
- /**
- * Add a mime-type which will be compressible
- * The mime-type String will be exactly matched
- * in the response mime-type header .
- *
- * @param mimeType mime-type string
- */
- public void addCompressableMimeType(String mimeType) {
- compressableMimeTypes =
- addStringArray(compressableMimeTypes, mimeType);
- }
-
-
- /**
- * Set compressible mime-type list (this method is best when used with
- * a large number of connectors, where it would be better to have all of
- * them referenced a single array).
- */
- public void setCompressableMimeTypes(String[] compressableMimeTypes) {
- this.compressableMimeTypes = compressableMimeTypes;
- }
-
-
- /**
- * Set compressable mime-type list
- * List contains users agents separated by ',' :
- *
- * ie: "text/html,text/xml,text/plain"
- */
- public void setCompressableMimeTypes(String compressableMimeTypes) {
- if (compressableMimeTypes != null) {
- this.compressableMimeTypes = null;
- StringTokenizer st = new StringTokenizer(compressableMimeTypes, ",");
-
- while (st.hasMoreTokens()) {
- addCompressableMimeType(st.nextToken().trim());
- }
- }
- }
-
-
- /**
- * Return compression level.
- */
- public String getCompression() {
- switch (compressionLevel) {
- case 0:
- return "off";
- case 1:
- return "on";
- case 2:
- return "force";
- }
- return "off";
- }
-
-
- /**
- * General use method
- *
- * @param sArray the StringArray
- * @param value string
- */
- private String[] addStringArray(String sArray[], String value) {
- String[] result = null;
- if (sArray == null) {
- result = new String[1];
- result[0] = value;
- }
- else {
- result = new String[sArray.length + 1];
- for (int i = 0; i < sArray.length; i++) {
- result[i] = sArray[i];
- }
- result[sArray.length] = value;
- }
- return result;
- }
-
-
- /**
- * Checks if any entry in the string array starts with the specified value
- *
- * @param sArray the StringArray
- * @param value string
- */
- private boolean startsWithStringArray(String sArray[], String value) {
- if (value == null) {
- return false;
- }
- for (int i = 0; i < sArray.length; i++) {
- if (value.startsWith(sArray[i])) {
- return true;
- }
- }
- return false;
- }
-
-
- /**
- * Set restricted user agent list (which will downgrade the connector
- * to HTTP/1.0 mode). Regular expression as supported by {@link Pattern}.
- *
- * ie: "gorilla|desesplorer|tigrus"
- */
- public void setRestrictedUserAgents(String restrictedUserAgents) {
- if (restrictedUserAgents == null ||
- restrictedUserAgents.length() == 0) {
- this.restrictedUserAgents = null;
- } else {
- this.restrictedUserAgents = Pattern.compile(restrictedUserAgents);
- }
- }
-
-
- /**
- * Set the maximum number of Keep-Alive requests to honor.
- * This is to safeguard from DoS attacks. Setting to a negative
- * value disables the check.
- */
- public void setMaxKeepAliveRequests(int mkar) {
- maxKeepAliveRequests = mkar;
- }
-
-
- /**
- * Return the number of Keep-Alive requests that we will honor.
- */
- public int getMaxKeepAliveRequests() {
- return maxKeepAliveRequests;
- }
-
- /**
- * Set the Keep-Alive timeout.
- */
- public void setKeepAliveTimeout(int timeout) {
- keepAliveTimeout = timeout;
- }
-
-
- /**
- * Return the number Keep-Alive timeout.
- */
- public int getKeepAliveTimeout() {
- return keepAliveTimeout;
- }
-
-
- /**
- * Set the maximum size of a POST which will be buffered in SSL mode.
- */
- public void setMaxSavePostSize(int msps) {
- maxSavePostSize = msps;
- }
-
-
- /**
- * Return the maximum size of a POST which will be buffered in SSL mode.
- */
- public int getMaxSavePostSize() {
- return maxSavePostSize;
- }
-
-
- /**
- * Set the flag to control upload time-outs.
- */
- public void setDisableUploadTimeout(boolean isDisabled) {
- disableUploadTimeout = isDisabled;
- }
-
- /**
- * Get the flag that controls upload time-outs.
- */
- public boolean getDisableUploadTimeout() {
- return disableUploadTimeout;
- }
-
- /**
- * Set the socket buffer flag.
- */
- public void setSocketBuffer(int socketBuffer) {
- outputBuffer.setSocketBuffer(socketBuffer);
- }
-
- /**
- * Get the socket buffer flag.
- */
- public int getSocketBuffer() {
- return outputBuffer.getSocketBuffer();
- }
-
- /**
- * Set the upload timeout.
- */
- public void setConnectionUploadTimeout(int timeout) {
- connectionUploadTimeout = timeout ;
- }
-
- /**
- * Get the upload timeout.
- */
- public int getConnectionUploadTimeout() {
- return connectionUploadTimeout;
- }
-
-
- /**
- * Set the server header name.
- */
- public void setServer( String server ) {
- if (server==null || server.equals("")) {
- this.server = null;
- } else {
- this.server = server;
- }
- }
-
- /**
- * Get the server header name.
- */
- public String getServer() {
- return server;
- }
-
-
- /**
- * Check if the resource could be compressed, if the client supports it.
- */
- private boolean isCompressable() {
-
- // Check if content is not already gzipped
- MessageBytes contentEncodingMB =
- response.getMimeHeaders().getValue("Content-Encoding");
-
- if ((contentEncodingMB != null)
- && (contentEncodingMB.indexOf("gzip") != -1)) {
- return false;
- }
-
- // If force mode, always compress (test purposes only)
- if (compressionLevel == 2) {
- return true;
- }
-
- // Check if sufficient length to trigger the compression
- long contentLength = response.getContentLengthLong();
- if ((contentLength == -1)
- || (contentLength > compressionMinSize)) {
- // Check for compatible MIME-TYPE
- if (compressableMimeTypes != null) {
- return (startsWithStringArray(compressableMimeTypes,
- response.getContentType()));
- }
- }
-
- return false;
- }
-
-
- /**
- * Check if compression should be used for this resource. Already checked
- * that the resource could be compressed if the client supports it.
- */
- private boolean useCompression() {
-
- // Check if browser support gzip encoding
- MessageBytes acceptEncodingMB =
- request.getMimeHeaders().getValue("accept-encoding");
-
- if ((acceptEncodingMB == null)
- || (acceptEncodingMB.indexOf("gzip") == -1)) {
- return false;
- }
-
- // If force mode, always compress (test purposes only)
- if (compressionLevel == 2) {
- return true;
- }
-
- // Check for incompatible Browser
- if (noCompressionUserAgents != null) {
- MessageBytes userAgentValueMB =
- request.getMimeHeaders().getValue("user-agent");
- if(userAgentValueMB != null) {
- String userAgentValue = userAgentValueMB.toString();
-
- if (noCompressionUserAgents.matcher(userAgentValue).matches()) {
- return false;
- }
- }
- }
-
- return true;
- }
-
-
- /**
- * Specialized utility method: find a sequence of lower case bytes inside
- * a ByteChunk.
- */
- protected int findBytes(ByteChunk bc, byte[] b) {
-
- byte first = b[0];
- byte[] buff = bc.getBuffer();
- int start = bc.getStart();
- int end = bc.getEnd();
-
- // Look for first char
- int srcEnd = b.length;
-
- for (int i = start; i <= (end - srcEnd); i++) {
- if (Ascii.toLower(buff[i]) != first) {
- continue;
- }
- // found first char, now look for a match
- int myPos = i+1;
- for (int srcPos = 1; srcPos < srcEnd;) {
- if (Ascii.toLower(buff[myPos++]) != b[srcPos++]) {
- break;
- }
- if (srcPos == srcEnd) {
- return i - start; // found it
- }
- }
- }
- return -1;
- }
-
-
- /**
- * Determine if we must drop the connection because of the HTTP status
- * code. Use the same list of codes as Apache/httpd.
- */
- protected boolean statusDropsConnection(int status) {
- return status == 400 /* SC_BAD_REQUEST */ ||
- status == 408 /* SC_REQUEST_TIMEOUT */ ||
- status == 411 /* SC_LENGTH_REQUIRED */ ||
- status == 413 /* SC_REQUEST_ENTITY_TOO_LARGE */ ||
- status == 414 /* SC_REQUEST_URI_TOO_LONG */ ||
- status == 500 /* SC_INTERNAL_SERVER_ERROR */ ||
- status == 503 /* SC_SERVICE_UNAVAILABLE */ ||
- status == 501 /* SC_NOT_IMPLEMENTED */;
- }
-
-
- /**
- * Exposes input buffer to super class to allow better code re-use.
- * @return The input buffer used by the processor.
- */
- protected abstract AbstractInputBuffer<S> getInputBuffer();
-
-
- /**
- * Exposes output buffer to super class to allow better code re-use.
- * @return The output buffer used by the processor.
- */
- protected abstract AbstractOutputBuffer<S> getOutputBuffer();
-
-
- /**
- * Initialize standard input and output filters.
- */
- protected void initializeFilters(int maxTrailerSize, Set<String> allowedTrailerHeaders,
- int maxExtensionSize, int maxSwallowSize) {
- // Create and add the identity filters.
- getInputBuffer().addFilter(new IdentityInputFilter(maxSwallowSize));
- getOutputBuffer().addFilter(new IdentityOutputFilter());
-
- // Create and add the chunked filters.
- getInputBuffer().addFilter( new ChunkedInputFilter(maxTrailerSize,allowedTrailerHeaders,
- maxExtensionSize, maxSwallowSize));
- getOutputBuffer().addFilter(new ChunkedOutputFilter());
-
- // Create and add the void filters
- getInputBuffer().addFilter(new VoidInputFilter());
- getOutputBuffer().addFilter(new VoidOutputFilter());
-
- // Create and add buffered input filter
- getInputBuffer().addFilter(new BufferedInputFilter());
-
- // Create and add the chunked filters.
- //getInputBuffer().addFilter(new GzipInputFilter());
- getOutputBuffer().addFilter(new GzipOutputFilter());
-
- pluggableFilterIndex = getInputBuffer().getFilters().length;
- }
-
-
- /**
- * Add an input filter to the current request. If the encoding is not
- * supported, a 501 response will be returned to the client.
- */
- private void addInputFilter(InputFilter[] inputFilters, String encodingName) {
-
- // Trim provided encoding name and convert to lower case since transfer
- // encoding names are case insensitive. (RFC2616, section 3.6)
- encodingName = encodingName.trim().toLowerCase(Locale.ENGLISH);
-
- if (encodingName.equals("identity")) {
- // Skip
- } else if (encodingName.equals("chunked")) {
- getInputBuffer().addActiveFilter
- (inputFilters[Constants.CHUNKED_FILTER]);
- contentDelimitation = true;
- } else {
- for (int i = pluggableFilterIndex; i < inputFilters.length; i++) {
- if (inputFilters[i].getEncodingName().toString().equals(encodingName)) {
- getInputBuffer().addActiveFilter(inputFilters[i]);
- return;
- }
- }
- // Unsupported transfer encoding
- // 501 - Unimplemented
- response.setStatus(501);
- setErrorState(ErrorState.CLOSE_CLEAN, null);
- if (getLog().isDebugEnabled()) {
- getLog().debug(sm.getString("http11processor.request.prepare") +
- " Unsupported transfer encoding [" + encodingName + "]");
- }
- }
- }
-
-
- /**
- * Send an action to the connector.
- *
- * @param actionCode Type of the action
- * @param param Action parameter
- */
- @Override
- public final void action(ActionCode actionCode, Object param) {
-
- switch (actionCode) {
- case CLOSE: {
- // End the processing of the current request
- try {
- getOutputBuffer().endRequest();
- } catch (IOException e) {
- setErrorState(ErrorState.CLOSE_NOW, e);
- }
- break;
- }
- case COMMIT: {
- // Commit current response
- if (response.isCommitted()) {
- return;
- }
-
- // Validate and write response headers
- try {
- prepareResponse();
- getOutputBuffer().commit();
- } catch (IOException e) {
- setErrorState(ErrorState.CLOSE_NOW, e);
- }
- break;
- }
- case ACK: {
- // Acknowledge request
- // Send a 100 status back if it makes sense (response not committed
- // yet, and client specified an expectation for 100-continue)
- if ((response.isCommitted()) || !expectation) {
- return;
- }
-
- getInputBuffer().setSwallowInput(true);
- try {
- getOutputBuffer().sendAck();
- } catch (IOException e) {
- setErrorState(ErrorState.CLOSE_NOW, e);
- }
- break;
- }
- case CLIENT_FLUSH: {
- try {
- getOutputBuffer().flush();
- } catch (IOException e) {
- setErrorState(ErrorState.CLOSE_NOW, e);
- response.setErrorException(e);
- }
- break;
- }
- case IS_ERROR: {
- ((AtomicBoolean) param).set(getErrorState().isError());
- break;
- }
- case DISABLE_SWALLOW_INPUT: {
- // Do not swallow request input and make sure we are closing the
- // connection
- setErrorState(ErrorState.CLOSE_CLEAN, null);
- getInputBuffer().setSwallowInput(false);
- break;
- }
- case RESET: {
- // Note: This must be called before the response is committed
- getOutputBuffer().reset();
- break;
- }
- case REQ_SET_BODY_REPLAY: {
- ByteChunk body = (ByteChunk) param;
-
- InputFilter savedBody = new SavedRequestInputFilter(body);
- savedBody.setRequest(request);
-
- @SuppressWarnings("unchecked")
- AbstractInputBuffer<S> internalBuffer = (AbstractInputBuffer<S>)
- request.getInputBuffer();
- internalBuffer.addActiveFilter(savedBody);
- break;
- }
- case ASYNC_START: {
- asyncStateMachine.asyncStart((AsyncContextCallback) param);
- // Async time out is based on SocketWrapper access time
- getSocketWrapper().access();
- break;
- }
- case ASYNC_DISPATCHED: {
- asyncStateMachine.asyncDispatched();
- break;
- }
- case ASYNC_TIMEOUT: {
- AtomicBoolean result = (AtomicBoolean) param;
- result.set(asyncStateMachine.asyncTimeout());
- break;
- }
- case ASYNC_RUN: {
- asyncStateMachine.asyncRun((Runnable) param);
- break;
- }
- case ASYNC_ERROR: {
- asyncStateMachine.asyncError();
- break;
- }
- case ASYNC_IS_STARTED: {
- ((AtomicBoolean) param).set(asyncStateMachine.isAsyncStarted());
- break;
- }
- case ASYNC_IS_COMPLETING: {
- ((AtomicBoolean) param).set(asyncStateMachine.isCompleting());
- break;
- }
- case ASYNC_IS_DISPATCHING: {
- ((AtomicBoolean) param).set(asyncStateMachine.isAsyncDispatching());
- break;
- }
- case ASYNC_IS_ASYNC: {
- ((AtomicBoolean) param).set(asyncStateMachine.isAsync());
- break;
- }
- case ASYNC_IS_TIMINGOUT: {
- ((AtomicBoolean) param).set(asyncStateMachine.isAsyncTimingOut());
- break;
- }
- case ASYNC_IS_ERROR: {
- ((AtomicBoolean) param).set(asyncStateMachine.isAsyncError());
- break;
- }
- case ASYNC_COMPLETE: {
- socketWrapper.clearDispatches();
- if (asyncStateMachine.asyncComplete()) {
- endpoint.processSocket(this.socketWrapper, SocketStatus.OPEN_READ, true);
- }
- break;
- }
- case ASYNC_SETTIMEOUT: {
- if (param == null || socketWrapper == null) {
- return;
- }
- long timeout = ((Long)param).longValue();
- // If we are not piggy backing on a worker thread, set the timeout
- socketWrapper.setTimeout(timeout);
- break;
- }
- case ASYNC_DISPATCH: {
- if (asyncStateMachine.asyncDispatch()) {
- endpoint.processSocket(this.socketWrapper, SocketStatus.OPEN_READ, true);
- }
- break;
- }
- case ASYNC_POST_PROCESS: {
- asyncStateMachine.asyncPostProcess();
- break;
- }
- case UPGRADE: {
- upgradeToken = (UpgradeToken) param;
- // Stop further HTTP output
- getOutputBuffer().finished = true;
- break;
- }
- case AVAILABLE: {
- request.setAvailable(inputBuffer.available(Boolean.TRUE.equals(param)));
- break;
- }
- case NB_WRITE_INTEREST: {
- AtomicBoolean isReady = (AtomicBoolean)param;
- try {
- isReady.set(getOutputBuffer().isReady());
- } catch (IOException e) {
- getLog().debug("isReady() failed", e);
- setErrorState(ErrorState.CLOSE_NOW, e);
- }
- break;
- }
- case NB_READ_INTEREST: {
- registerForEvent(true, false);
- break;
- }
- case REQUEST_BODY_FULLY_READ: {
- AtomicBoolean result = (AtomicBoolean) param;
- result.set(getInputBuffer().isFinished());
- break;
- }
- case DISPATCH_READ: {
- socketWrapper.addDispatch(DispatchType.NON_BLOCKING_READ);
- break;
- }
- case DISPATCH_WRITE: {
- socketWrapper.addDispatch(DispatchType.NON_BLOCKING_WRITE);
- break;
- }
- case DISPATCH_EXECUTE: {
- SocketWrapper<S> wrapper = socketWrapper;
- if (wrapper != null) {
- getEndpoint().executeNonBlockingDispatches(wrapper);
- }
- break;
- }
- case CLOSE_NOW: {
- // Block further output
- getOutputBuffer().finished = true;
- if (param instanceof Throwable) {
- setErrorState(ErrorState.CLOSE_NOW, (Throwable) param);
- } else {
- setErrorState(ErrorState.CLOSE_NOW, null);
- }
- break;
- }
- case END_REQUEST: {
- endRequest();
- break;
- }
- case IS_COMET: {
- AtomicBoolean result = (AtomicBoolean) param;
- result.set(isComet());
- break;
- }
- default: {
- actionInternal(actionCode, param);
- break;
- }
- }
- }
-
- protected abstract void actionInternal(ActionCode actionCode, Object param);
-
-
- /**
- * Processors (currently only HTTP BIO) may elect to disable HTTP keep-alive
- * in some circumstances. This method allows the processor implementation to
- * determine if keep-alive should be disabled or not.
- */
- protected abstract boolean disableKeepAlive();
-
-
- /**
- * Configures the timeout to be used for reading the request line.
- */
- protected abstract void setRequestLineReadTimeout() throws IOException;
-
-
- /**
- * Defines how a connector handles an incomplete request line read.
- *
- * @return <code>true</code> if the processor should break out of the
- * processing loop, otherwise <code>false</code>.
- */
- protected abstract boolean handleIncompleteRequestLineRead();
-
-
- /**
- * Set the socket timeout.
- */
- protected abstract void setSocketTimeout(int timeout) throws IOException;
-
-
- /**
- * Process pipelined HTTP requests using the specified input and output
- * streams.
- *
- * @param socketWrapper Socket from which the HTTP requests will be read
- * and the HTTP responses will be written.
- *
- * @throws IOException error during an I/O operation
- */
- @Override
- public SocketState process(SocketWrapper<S> socketWrapper)
- throws IOException {
- RequestInfo rp = request.getRequestProcessor();
- rp.setStage(org.apache.coyote.Constants.STAGE_PARSE);
-
- // Setting up the I/O
- setSocketWrapper(socketWrapper);
- getInputBuffer().init(socketWrapper, endpoint);
- getOutputBuffer().init(socketWrapper, endpoint);
-
- // Flags
- keepAlive = true;
- comet = false;
- openSocket = false;
- sendfileInProgress = false;
- readComplete = true;
- if (endpoint.getUsePolling()) {
- keptAlive = false;
- } else {
- keptAlive = socketWrapper.isKeptAlive();
- }
-
- if (disableKeepAlive()) {
- socketWrapper.setKeepAliveLeft(0);
- }
-
- while (!getErrorState().isError() && keepAlive && !comet && !isAsync() &&
- upgradeToken == null && !endpoint.isPaused()) {
-
- // Parsing the request header
- try {
- setRequestLineReadTimeout();
-
- if (!getInputBuffer().parseRequestLine(keptAlive)) {
- if (handleIncompleteRequestLineRead()) {
- break;
- }
- }
-
- if (endpoint.isPaused()) {
- // 503 - Service unavailable
- response.setStatus(503);
- setErrorState(ErrorState.CLOSE_CLEAN, null);
- } else {
- keptAlive = true;
- // Set this every time in case limit has been changed via JMX
- request.getMimeHeaders().setLimit(endpoint.getMaxHeaderCount());
- request.getCookies().setLimit(getMaxCookieCount());
- // Currently only NIO will ever return false here
- if (!getInputBuffer().parseHeaders()) {
- // We've read part of the request, don't recycle it
- // instead associate it with the socket
- openSocket = true;
- readComplete = false;
- break;
- }
- if (!disableUploadTimeout) {
- setSocketTimeout(connectionUploadTimeout);
- }
- }
- } catch (IOException e) {
- if (getLog().isDebugEnabled()) {
- getLog().debug(
- sm.getString("http11processor.header.parse"), e);
- }
- setErrorState(ErrorState.CLOSE_NOW, e);
- break;
- } catch (Throwable t) {
- ExceptionUtils.handleThrowable(t);
- UserDataHelper.Mode logMode = userDataHelper.getNextMode();
- if (logMode != null) {
- String message = sm.getString(
- "http11processor.header.parse");
- switch (logMode) {
- case INFO_THEN_DEBUG:
- message += sm.getString(
- "http11processor.fallToDebug");
- //$FALL-THROUGH$
- case INFO:
- getLog().info(message, t);
- break;
- case DEBUG:
- getLog().debug(message, t);
- }
- }
- // 400 - Bad Request
- response.setStatus(400);
- setErrorState(ErrorState.CLOSE_CLEAN, t);
- getAdapter().log(request, response, 0);
- }
-
- if (!getErrorState().isError()) {
- // Setting up filters, and parse some request headers
- rp.setStage(org.apache.coyote.Constants.STAGE_PREPARE);
- try {
- prepareRequest();
- } catch (Throwable t) {
- ExceptionUtils.handleThrowable(t);
- if (getLog().isDebugEnabled()) {
- getLog().debug(sm.getString(
- "http11processor.request.prepare"), t);
- }
- // 500 - Internal Server Error
- response.setStatus(500);
- setErrorState(ErrorState.CLOSE_CLEAN, t);
- getAdapter().log(request, response, 0);
- }
- }
-
- if (maxKeepAliveRequests == 1) {
- keepAlive = false;
- } else if (maxKeepAliveRequests > 0 &&
- socketWrapper.decrementKeepAlive() <= 0) {
- keepAlive = false;
- }
-
- // Process the request in the adapter
- if (!getErrorState().isError()) {
- try {
- rp.setStage(org.apache.coyote.Constants.STAGE_SERVICE);
- getAdapter().service(request, response);
- // Handle when the response was committed before a serious
- // error occurred. Throwing a ServletException should both
- // set the status to 500 and set the errorException.
- // If we fail here, then the response is likely already
- // committed, so we can't try and set headers.
- if(keepAlive && !getErrorState().isError() && !isAsync() &&
- statusDropsConnection(response.getStatus())) {
- setErrorState(ErrorState.CLOSE_CLEAN, null);
- }
- setCometTimeouts(socketWrapper);
- } catch (InterruptedIOException e) {
- setErrorState(ErrorState.CLOSE_NOW, e);
- } catch (HeadersTooLargeException e) {
- getLog().error(sm.getString("http11processor.request.process"), e);
- // The response should not have been committed but check it
- // anyway to be safe
- if (response.isCommitted()) {
- setErrorState(ErrorState.CLOSE_NOW, e);
- } else {
- response.reset();
- response.setStatus(500);
- setErrorState(ErrorState.CLOSE_CLEAN, e);
- response.setHeader("Connection", "close"); // TODO: Remove
- }
- } catch (Throwable t) {
- ExceptionUtils.handleThrowable(t);
- getLog().error(sm.getString("http11processor.request.process"), t);
- // 500 - Internal Server Error
- response.setStatus(500);
- setErrorState(ErrorState.CLOSE_CLEAN, t);
- getAdapter().log(request, response, 0);
- }
- }
-
- // Finish the handling of the request
- rp.setStage(org.apache.coyote.Constants.STAGE_ENDINPUT);
-
- if (!isAsync() && !comet) {
- if (getErrorState().isError()) {
- // If we know we are closing the connection, don't drain
- // input. This way uploading a 100GB file doesn't tie up the
- // thread if the servlet has rejected it.
- getInputBuffer().setSwallowInput(false);
- } else {
- // Need to check this again here in case the response was
- // committed before the error that requires the connection
- // to be closed occurred.
- checkExpectationAndResponseStatus();
- }
- endRequest();
- }
-
- rp.setStage(org.apache.coyote.Constants.STAGE_ENDOUTPUT);
-
- // If there was an error, make sure the request is counted as
- // and error, and update the statistics counter
- if (getErrorState().isError()) {
- response.setStatus(500);
- }
-
- if (!isAsync() && !comet || getErrorState().isError()) {
- request.updateCounters();
- if (getErrorState().isIoAllowed()) {
- getInputBuffer().nextRequest();
- getOutputBuffer().nextRequest();
- }
- }
-
- if (!disableUploadTimeout) {
- if(endpoint.getSoTimeout() > 0) {
- setSocketTimeout(endpoint.getSoTimeout());
- } else {
- setSocketTimeout(0);
- }
- }
-
- rp.setStage(org.apache.coyote.Constants.STAGE_KEEPALIVE);
-
- if (breakKeepAliveLoop(socketWrapper)) {
- break;
- }
- }
-
- rp.setStage(org.apache.coyote.Constants.STAGE_ENDED);
-
- if (getErrorState().isError() || endpoint.isPaused()) {
- return SocketState.CLOSED;
- } else if (isAsync() || comet) {
- return SocketState.LONG;
- } else if (isUpgrade()) {
- return SocketState.UPGRADING;
- } else {
- if (sendfileInProgress) {
- return SocketState.SENDFILE;
- } else {
- if (openSocket) {
- if (readComplete) {
- return SocketState.OPEN;
- } else {
- return SocketState.LONG;
- }
- } else {
- return SocketState.CLOSED;
- }
- }
- }
- }
-
-
- private void checkExpectationAndResponseStatus() {
- if (expectation && (response.getStatus() < 200 || response.getStatus() > 299)) {
- // Client sent Expect: 100-continue but received a
- // non-2xx final response. Disable keep-alive (if enabled)
- // to ensure that the connection is closed. Some clients may
- // still send the body, some may send the next request.
- // No way to differentiate, so close the connection to
- // force the client to send the next request.
- getInputBuffer().setSwallowInput(false);
- keepAlive = false;
- }
- }
-
-
- /**
- * After reading the request headers, we have to setup the request filters.
- */
- protected void prepareRequest() {
-
- http11 = true;
- http09 = false;
- contentDelimitation = false;
- expectation = false;
-
- prepareRequestInternal();
-
- if (endpoint.isSSLEnabled()) {
- request.scheme().setString("https");
- }
- MessageBytes protocolMB = request.protocol();
- if (protocolMB.equals(Constants.HTTP_11)) {
- http11 = true;
- protocolMB.setString(Constants.HTTP_11);
- } else if (protocolMB.equals(Constants.HTTP_10)) {
- http11 = false;
- keepAlive = false;
- protocolMB.setString(Constants.HTTP_10);
- } else if (protocolMB.equals("")) {
- // HTTP/0.9
- http09 = true;
- http11 = false;
- keepAlive = false;
- } else {
- // Unsupported protocol
- http11 = false;
- // Send 505; Unsupported HTTP version
- response.setStatus(505);
- setErrorState(ErrorState.CLOSE_CLEAN, null);
- if (getLog().isDebugEnabled()) {
- getLog().debug(sm.getString("http11processor.request.prepare")+
- " Unsupported HTTP version \""+protocolMB+"\"");
- }
- }
-
- MessageBytes methodMB = request.method();
- if (methodMB.equals(Constants.GET)) {
- methodMB.setString(Constants.GET);
- } else if (methodMB.equals(Constants.POST)) {
- methodMB.setString(Constants.POST);
- }
-
- MimeHeaders headers = request.getMimeHeaders();
-
- // Check connection header
- MessageBytes connectionValueMB = headers.getValue(Constants.CONNECTION);
- if (connectionValueMB != null) {
- ByteChunk connectionValueBC = connectionValueMB.getByteChunk();
- if (findBytes(connectionValueBC, Constants.CLOSE_BYTES) != -1) {
- keepAlive = false;
- } else if (findBytes(connectionValueBC,
- Constants.KEEPALIVE_BYTES) != -1) {
- keepAlive = true;
- }
- }
-
- MessageBytes expectMB = null;
- if (http11) {
- expectMB = headers.getValue("expect");
- }
- if (expectMB != null) {
- if (expectMB.indexOfIgnoreCase("100-continue", 0) != -1) {
- getInputBuffer().setSwallowInput(false);
- expectation = true;
- } else {
- response.setStatus(HttpServletResponse.SC_EXPECTATION_FAILED);
- setErrorState(ErrorState.CLOSE_CLEAN, null);
- }
- }
-
- // Check user-agent header
- if (restrictedUserAgents != null && (http11 || keepAlive)) {
- MessageBytes userAgentValueMB = headers.getValue("user-agent");
- // Check in the restricted list, and adjust the http11
- // and keepAlive flags accordingly
- if(userAgentValueMB != null) {
- String userAgentValue = userAgentValueMB.toString();
- if (restrictedUserAgents != null &&
- restrictedUserAgents.matcher(userAgentValue).matches()) {
- http11 = false;
- keepAlive = false;
- }
- }
- }
-
- // Check for a full URI (including protocol://host:port/)
- ByteChunk uriBC = request.requestURI().getByteChunk();
- if (uriBC.startsWithIgnoreCase("http", 0)) {
-
- 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);
- if (slashPos == -1) {
- slashPos = uriBC.getLength();
- // Set URI as "/"
- request.requestURI().setBytes
- (uriB, uriBCStart + pos + 1, 1);
- } else {
- request.requestURI().setBytes
- (uriB, uriBCStart + slashPos,
- uriBC.getLength() - slashPos);
- }
- MessageBytes hostMB = headers.setValue("host");
- hostMB.setBytes(uriB, uriBCStart + pos + 3,
- slashPos - pos - 3);
- }
- }
-
- // Input filter setup
- InputFilter[] inputFilters = getInputBuffer().getFilters();
-
- // Parse transfer-encoding header
- MessageBytes transferEncodingValueMB = null;
- if (http11) {
- transferEncodingValueMB = headers.getValue("transfer-encoding");
- }
- if (transferEncodingValueMB != null) {
- String transferEncodingValue = transferEncodingValueMB.toString();
- // Parse the comma separated list. "identity" codings are ignored
- int startPos = 0;
- int commaPos = transferEncodingValue.indexOf(',');
- String encodingName = null;
- while (commaPos != -1) {
- encodingName = transferEncodingValue.substring(startPos, commaPos);
- addInputFilter(inputFilters, encodingName);
- startPos = commaPos + 1;
- commaPos = transferEncodingValue.indexOf(',', startPos);
- }
- encodingName = transferEncodingValue.substring(startPos);
- addInputFilter(inputFilters, encodingName);
- }
-
- // Parse content-length header
- long contentLength = request.getContentLengthLong();
- if (contentLength >= 0) {
- if (contentDelimitation) {
- // contentDelimitation being true at this point indicates that
- // chunked encoding is being used but chunked encoding should
- // not be used with a content length. RFC 2616, section 4.4,
- // bullet 3 states Content-Length must be ignored in this case -
- // so remove it.
- headers.removeHeader("content-length");
- request.setContentLength(-1);
- } else {
- getInputBuffer().addActiveFilter
- (inputFilters[Constants.IDENTITY_FILTER]);
- contentDelimitation = true;
- }
- }
-
- MessageBytes valueMB = headers.getValue("host");
-
- // Check host header
- if (http11 && (valueMB == null)) {
- // 400 - Bad request
- response.setStatus(400);
- setErrorState(ErrorState.CLOSE_CLEAN, null);
- if (getLog().isDebugEnabled()) {
- getLog().debug(sm.getString("http11processor.request.prepare")+
- " host header missing");
- }
- }
-
- parseHost(valueMB);
-
- if (!contentDelimitation) {
- // If there's no content length
- // (broken HTTP/1.0 or HTTP/1.1), assume
- // the client is not broken and didn't send a body
- getInputBuffer().addActiveFilter
- (inputFilters[Constants.VOID_FILTER]);
- contentDelimitation = true;
- }
-
- if (getErrorState().isError()) {
- getAdapter().log(request, response, 0);
- }
- }
-
-
- /**
- * Connector implementation specific request preparation. Ideally, this will
- * go away in the future.
- */
- protected abstract void prepareRequestInternal();
-
- /**
- * When committing the response, we have to validate the set of headers, as
- * well as setup the response filters.
- */
- private void prepareResponse() {
-
- boolean entityBody = true;
- contentDelimitation = false;
-
- OutputFilter[] outputFilters = getOutputBuffer().getFilters();
-
- if (http09 == true) {
- // HTTP/0.9
- getOutputBuffer().addActiveFilter
- (outputFilters[Constants.IDENTITY_FILTER]);
- return;
- }
-
- int statusCode = response.getStatus();
- if (statusCode < 200 || statusCode == 204 || statusCode == 205 ||
- statusCode == 304) {
- // No entity body
- getOutputBuffer().addActiveFilter
- (outputFilters[Constants.VOID_FILTER]);
- entityBody = false;
- contentDelimitation = true;
- }
-
- MessageBytes methodMB = request.method();
- if (methodMB.equals("HEAD")) {
- // No entity body
- getOutputBuffer().addActiveFilter
- (outputFilters[Constants.VOID_FILTER]);
- contentDelimitation = true;
- }
-
- // Sendfile support
- boolean sendingWithSendfile = false;
- if (getEndpoint().getUseSendfile()) {
- sendingWithSendfile = prepareSendfile(outputFilters);
- }
-
- // Check for compression
- boolean isCompressable = false;
- boolean useCompression = false;
- if (entityBody && (compressionLevel > 0) && !sendingWithSendfile) {
- isCompressable = isCompressable();
- if (isCompressable) {
- useCompression = useCompression();
- }
- // Change content-length to -1 to force chunking
- if (useCompression) {
- response.setContentLength(-1);
- }
- }
-
- MimeHeaders headers = response.getMimeHeaders();
- if (!entityBody) {
- response.setContentLength(-1);
- }
- // A SC_NO_CONTENT response may include entity headers
- if (entityBody || statusCode == HttpServletResponse.SC_NO_CONTENT) {
- String contentType = response.getContentType();
- if (contentType != null) {
- headers.setValue("Content-Type").setString(contentType);
- }
- String contentLanguage = response.getContentLanguage();
- if (contentLanguage != null) {
- headers.setValue("Content-Language")
- .setString(contentLanguage);
- }
- }
-
- long contentLength = response.getContentLengthLong();
- boolean connectionClosePresent = false;
- if (contentLength != -1) {
- headers.setValue("Content-Length").setLong(contentLength);
- getOutputBuffer().addActiveFilter
- (outputFilters[Constants.IDENTITY_FILTER]);
- contentDelimitation = true;
- } else {
- // If the response code supports an entity body and we're on
- // HTTP 1.1 then we chunk unless we have a Connection: close header
- connectionClosePresent = isConnectionClose(headers);
- if (entityBody && http11 && !connectionClosePresent) {
- getOutputBuffer().addActiveFilter
- (outputFilters[Constants.CHUNKED_FILTER]);
- contentDelimitation = true;
- headers.addValue(Constants.TRANSFERENCODING).setString(Constants.CHUNKED);
- } else {
- getOutputBuffer().addActiveFilter
- (outputFilters[Constants.IDENTITY_FILTER]);
- }
- }
-
- if (useCompression) {
- getOutputBuffer().addActiveFilter(outputFilters[Constants.GZIP_FILTER]);
- headers.setValue("Content-Encoding").setString("gzip");
- }
- // If it might be compressed, set the Vary header
- if (isCompressable) {
- // Make Proxies happy via Vary (from mod_deflate)
- MessageBytes vary = headers.getValue("Vary");
- if (vary == null) {
- // Add a new Vary header
- headers.setValue("Vary").setString("Accept-Encoding");
- } else if (vary.equals("*")) {
- // No action required
- } else {
- // Merge into current header
- headers.setValue("Vary").setString(
- vary.getString() + ",Accept-Encoding");
- }
- }
-
- // Add date header unless application has already set one (e.g. in a
- // Caching Filter)
- if (headers.getValue("Date") == null) {
- headers.setValue("Date").setString(
- FastHttpDateFormat.getCurrentDate());
- }
-
- // FIXME: Add transfer encoding header
-
- if ((entityBody) && (!contentDelimitation)) {
- // Mark as close the connection after the request, and add the
- // connection: close header
- keepAlive = false;
- }
-
- // This may disabled keep-alive to check before working out the
- // Connection header.
- checkExpectationAndResponseStatus();
-
- // If we know that the request is bad this early, add the
- // Connection: close header.
- if (keepAlive && statusDropsConnection(statusCode)) {
- keepAlive = false;
- }
- if (!keepAlive) {
- // Avoid adding the close header twice
- if (!connectionClosePresent) {
- headers.addValue(Constants.CONNECTION).setString(
- Constants.CLOSE);
- }
- } else if (!http11 && !getErrorState().isError()) {
- headers.addValue(Constants.CONNECTION).setString(Constants.KEEPALIVE);
- }
-
- // Build the response header
- getOutputBuffer().sendStatus();
-
- // Add server header
- if (server != null) {
- // Always overrides anything the app might set
- headers.setValue("Server").setString(server);
- } else if (headers.getValue("Server") == null) {
- // If app didn't set the header, use the default
- getOutputBuffer().write(Constants.SERVER_BYTES);
- }
-
- int size = headers.size();
- for (int i = 0; i < size; i++) {
- getOutputBuffer().sendHeader(headers.getName(i), headers.getValue(i));
- }
- getOutputBuffer().endHeaders();
-
- }
-
- private boolean isConnectionClose(MimeHeaders headers) {
- MessageBytes connection = headers.getValue(Constants.CONNECTION);
- if (connection == null) {
- return false;
- }
- return connection.equals(Constants.CLOSE);
- }
-
- protected abstract boolean prepareSendfile(OutputFilter[] outputFilters);
-
- /**
- * Parse host.
- */
- protected void parseHost(MessageBytes valueMB) {
-
- if (valueMB == null || valueMB.isNull()) {
- // HTTP/1.0
- // If no host header, use the port info from the endpoint
- // The host will be obtained lazily from the socket if required
- // using ActionCode#REQ_LOCAL_NAME_ATTRIBUTE
- request.setServerPort(endpoint.getPort());
- return;
- }
-
- ByteChunk valueBC = valueMB.getByteChunk();
- byte[] valueB = valueBC.getBytes();
- int valueL = valueBC.getLength();
- int valueS = valueBC.getStart();
- int colonPos = -1;
- if (hostNameC.length < valueL) {
- hostNameC = new char[valueL];
- }
-
- boolean ipv6 = (valueB[valueS] == '[');
- boolean bracketClosed = false;
- for (int i = 0; i < valueL; i++) {
- char b = (char) valueB[i + valueS];
- hostNameC[i] = b;
- if (b == ']') {
- bracketClosed = true;
- } else if (b == ':') {
- if (!ipv6 || bracketClosed) {
- colonPos = i;
- break;
- }
- }
- }
-
- if (colonPos < 0) {
- if (!endpoint.isSSLEnabled()) {
- // 80 - Default HTTP port
- request.setServerPort(80);
- } else {
- // 443 - Default HTTPS port
- request.setServerPort(443);
- }
- request.serverName().setChars(hostNameC, 0, valueL);
- } else {
- request.serverName().setChars(hostNameC, 0, colonPos);
-
- int port = 0;
- int mult = 1;
- for (int i = valueL - 1; i > colonPos; i--) {
- int charValue = HexUtils.getDec(valueB[i + valueS]);
- if (charValue == -1 || charValue > 9) {
- // Invalid character
- // 400 - Bad request
- response.setStatus(400);
- setErrorState(ErrorState.CLOSE_CLEAN, null);
- break;
- }
- port = port + (charValue * mult);
- mult = 10 * mult;
- }
- request.setServerPort(port);
- }
-
- }
-
-
- @Override
- public SocketState asyncDispatch(SocketStatus status) {
-
- if (status == SocketStatus.OPEN_WRITE && response.getWriteListener() != null) {
- try {
- asyncStateMachine.asyncOperation();
-
- if (outputBuffer.hasDataToWrite()) {
- if (outputBuffer.flushBuffer(false)) {
- // The buffer wasn't fully flushed so re-register the
- // socket for write. Note this does not go via the
- // Response since the write registration state at
- // that level should remain unchanged. Once the buffer
- // has been emptied then the code below will call
- // Adaptor.asyncDispatch() which will enable the
- // Response to respond to this event.
- outputBuffer.registerWriteInterest();
- return SocketState.LONG;
- }
- }
- } catch (IOException | IllegalStateException x) {
- // IOE - Problem writing to socket
- // ISE - Request/Response not in correct state for async write
- if (getLog().isDebugEnabled()) {
- getLog().debug("Unable to write async data.",x);
- }
- status = SocketStatus.ERROR;
- request.setAttribute(RequestDispatcher.ERROR_EXCEPTION, x);
- }
- } else if (status == SocketStatus.OPEN_READ && request.getReadListener() != null) {
- try {
- // Check of asyncStateMachine.isAsyncStarted() is to avoid issue
- // with BIO. Because it can't do a non-blocking read, BIO always
- // returns available() == 1. This causes a problem here at the
- // end of a non-blocking read. See BZ 57481.
- if (asyncStateMachine.isAsyncStarted()) {
- asyncStateMachine.asyncOperation();
- }
- } catch (IllegalStateException x) {
- // ISE - Request/Response not in correct state for async read
- if (getLog().isDebugEnabled()) {
- getLog().debug("Unable to read async data.",x);
- }
- status = SocketStatus.ERROR;
- request.setAttribute(RequestDispatcher.ERROR_EXCEPTION, x);
- }
- }
-
- RequestInfo rp = request.getRequestProcessor();
- try {
- rp.setStage(org.apache.coyote.Constants.STAGE_SERVICE);
- if (!getAdapter().asyncDispatch(request, response, status)) {
- setErrorState(ErrorState.CLOSE_NOW, null);
- }
- resetTimeouts();
- } catch (InterruptedIOException e) {
- setErrorState(ErrorState.CLOSE_NOW, e);
- } catch (Throwable t) {
- ExceptionUtils.handleThrowable(t);
- setErrorState(ErrorState.CLOSE_NOW, t);
- getLog().error(sm.getString("http11processor.request.process"), t);
- }
-
- rp.setStage(org.apache.coyote.Constants.STAGE_ENDED);
-
- if (getErrorState().isError()) {
- request.updateCounters();
- return SocketState.CLOSED;
- } else if (isAsync()) {
- return SocketState.LONG;
- } else {
- request.updateCounters();
- if (!keepAlive) {
- return SocketState.CLOSED;
- } else {
- endRequest();
- getInputBuffer().nextRequest();
- getOutputBuffer().nextRequest();
- return SocketState.OPEN;
- }
- }
- }
-
-
- @Override
- public boolean isComet() {
- return comet;
- }
-
-
- @Override
- public boolean isUpgrade() {
- return upgradeToken != null;
- }
-
-
-
- @Override
- public SocketState upgradeDispatch(SocketStatus status) throws IOException {
- // Should never reach this code but in case we do...
- throw new IllegalStateException(
- sm.getString("http11Processor.upgrade"));
- }
-
-
- @Override
- public UpgradeToken getUpgradeToken() {
- return upgradeToken;
- }
-
-
- /**
- * Provides a mechanism for those connector implementations (currently only
- * NIO) that need to reset timeouts from Async timeouts to standard HTTP
- * timeouts once async processing completes.
- */
- protected abstract void resetTimeouts();
-
-
- /**
- * Provides a mechanism for those connectors (currently only NIO) that need
- * that need to set comet timeouts.
- */
- protected abstract void setCometTimeouts(SocketWrapper<S> socketWrapper);
-
- public void endRequest() {
-
- // Finish the handling of the request
- if (getErrorState().isIoAllowed()) {
- try {
- getInputBuffer().endRequest();
- } catch (IOException e) {
- setErrorState(ErrorState.CLOSE_NOW, e);
- } catch (Throwable t) {
- ExceptionUtils.handleThrowable(t);
- // 500 - Internal Server Error
- // Can't add a 500 to the access log since that has already been
- // written in the Adapter.service method.
- response.setStatus(500);
- setErrorState(ErrorState.CLOSE_NOW, t);
- getLog().error(sm.getString("http11processor.request.finish"), t);
- }
- }
- if (getErrorState().isIoAllowed()) {
- try {
- getOutputBuffer().endRequest();
- } catch (IOException e) {
- setErrorState(ErrorState.CLOSE_NOW, e);
- } catch (Throwable t) {
- ExceptionUtils.handleThrowable(t);
- setErrorState(ErrorState.CLOSE_NOW, t);
- getLog().error(sm.getString("http11processor.response.finish"), t);
- }
- }
- }
-
-
- /**
- * Checks to see if the keep-alive loop should be broken, performing any
- * processing (e.g. sendfile handling) that may have an impact on whether
- * or not the keep-alive loop should be broken.
- * @return true if the keep-alive loop should be broken
- */
- protected abstract boolean breakKeepAliveLoop(
- SocketWrapper<S> socketWrapper);
-
-
- @Override
- public final void recycle(boolean isSocketClosing) {
- getAdapter().checkRecycled(request, response);
-
- if (getInputBuffer() != null) {
- getInputBuffer().recycle();
- }
- if (getOutputBuffer() != null) {
- getOutputBuffer().recycle();
- }
- if (asyncStateMachine != null) {
- asyncStateMachine.recycle();
- }
- upgradeToken = null;
- comet = false;
- resetErrorState();
- recycleInternal();
- }
-
- protected abstract void recycleInternal();
-
-
- @Override
- public ByteBuffer getLeftoverInput() {
- return inputBuffer.getLeftover();
- }
-
-}
diff --git a/java/org/apache/coyote/http11/AbstractHttp11Protocol.java b/java/org/apache/coyote/http11/AbstractHttp11Protocol.java
index 4b744f2..5051ba0 100644
--- a/java/org/apache/coyote/http11/AbstractHttp11Protocol.java
+++ b/java/org/apache/coyote/http11/AbstractHttp11Protocol.java
@@ -18,32 +18,74 @@ package org.apache.coyote.http11;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
+import java.util.Map;
import java.util.Set;
+import java.util.StringTokenizer;
import java.util.concurrent.ConcurrentHashMap;
+import javax.servlet.http.HttpUpgradeHandler;
+
import org.apache.coyote.AbstractProtocol;
+import org.apache.coyote.Processor;
+import org.apache.coyote.UpgradeProtocol;
+import org.apache.coyote.UpgradeToken;
+import org.apache.coyote.http11.upgrade.InternalHttpUpgradeHandler;
+import org.apache.coyote.http11.upgrade.UpgradeProcessorExternal;
+import org.apache.coyote.http11.upgrade.UpgradeProcessorInternal;
+import org.apache.tomcat.util.net.AbstractEndpoint;
+import org.apache.tomcat.util.net.SSLHostConfig;
+import org.apache.tomcat.util.net.SocketWrapperBase;
+import org.apache.tomcat.util.res.StringManager;
public abstract class AbstractHttp11Protocol<S> extends AbstractProtocol<S> {
+ protected static final StringManager sm =
+ StringManager.getManager(AbstractHttp11Protocol.class);
+
+
+ public AbstractHttp11Protocol(AbstractEndpoint<S> endpoint) {
+ super(endpoint);
+ setSoTimeout(Constants.DEFAULT_CONNECTION_TIMEOUT);
+ ConnectionHandler<S> cHandler = new ConnectionHandler<>(this);
+ setHandler(cHandler);
+ getEndpoint().setHandler(cHandler);
+ }
+
+
+ @Override
+ public void init() throws Exception {
+ for (UpgradeProtocol upgradeProtocol : upgradeProtocols) {
+ configureUpgradeProtocol(upgradeProtocol);
+ }
+
+ super.init();
+ }
+
+
@Override
protected String getProtocolName() {
return "Http";
}
- // ------------------------------------------------ HTTP specific properties
- // ------------------------------------------ managed in the ProtocolHandler
-
- private int socketBuffer = 9000;
- public int getSocketBuffer() { return socketBuffer; }
- public void setSocketBuffer(int socketBuffer) {
- this.socketBuffer = socketBuffer;
+ /**
+ * {@inheritDoc}
+ * <p>
+ * Over-ridden here to make the method visible to nested classes.
+ */
+ @Override
+ protected AbstractEndpoint<S> getEndpoint() {
+ return super.getEndpoint();
}
+ // ------------------------------------------------ HTTP specific properties
+ // ------------------------------------------ managed in the ProtocolHandler
+
/**
* Maximum size of the post which will be saved when processing certain
* requests, such as a POST.
@@ -100,16 +142,29 @@ public abstract class AbstractHttp11Protocol<S> extends AbstractProtocol<S> {
}
- private String compressableMimeTypes = "text/html,text/xml,text/plain,text/css,text/javascript,application/javascript";
- public String getCompressableMimeType() { return compressableMimeTypes; }
+ private String compressableMimeType = "text/html,text/xml,text/plain,text/css,text/javascript,application/javascript";
+ private String[] compressableMimeTypes = null;
+ public String getCompressableMimeType() { return compressableMimeType; }
public void setCompressableMimeType(String valueS) {
- compressableMimeTypes = valueS;
- }
- public String getCompressableMimeTypes() {
- return getCompressableMimeType();
+ compressableMimeType = valueS;
+ compressableMimeTypes = null;
}
- public void setCompressableMimeTypes(String valueS) {
- setCompressableMimeType(valueS);
+ public String[] getCompressableMimeTypes() {
+ String[] result = compressableMimeTypes;
+ if (result != null) {
+ return result;
+ }
+ List<String> values = new ArrayList<>();
+ StringTokenizer tokens = new StringTokenizer(compressableMimeType, ",");
+ while (tokens.hasMoreTokens()) {
+ String token = tokens.nextToken().trim();
+ if (token.length() > 0) {
+ values.add(token);
+ }
+ }
+ result = values.toArray(new String[values.size()]);
+ compressableMimeTypes = result;
+ return result;
}
@@ -141,6 +196,13 @@ public abstract class AbstractHttp11Protocol<S> extends AbstractProtocol<S> {
}
+ private boolean serverRemoveAppProvidedValues = false;
+ public boolean getServerRemoveAppProvidedValues() { return serverRemoveAppProvidedValues; }
+ public void setServerRemoveAppProvidedValues(boolean serverRemoveAppProvidedValues) {
+ this.serverRemoveAppProvidedValues = serverRemoveAppProvidedValues;
+ }
+
+
/**
* Maximum size of trailing headers in bytes
*/
@@ -184,17 +246,6 @@ public abstract class AbstractHttp11Protocol<S> extends AbstractProtocol<S> {
/**
- * The size of the buffer used by the ServletOutputStream when performing
- * delayed asynchronous writes using HTTP upgraded connections.
- */
- private int upgradeAsyncWriteBufferSize = 8192;
- public int getUpgradeAsyncWriteBufferSize() { return upgradeAsyncWriteBufferSize; }
- public void setUpgradeAsyncWriteBufferSize(int upgradeAsyncWriteBufferSize) {
- this.upgradeAsyncWriteBufferSize = upgradeAsyncWriteBufferSize;
- }
-
-
- /**
* The names of headers that are allowed to be sent via a trailer when using
* chunked encoding. They are stored in lower case.
*/
@@ -245,39 +296,348 @@ public abstract class AbstractHttp11Protocol<S> extends AbstractProtocol<S> {
allowedTrailerHeaders.remove(header.trim().toLowerCase(Locale.ENGLISH));
}
}
- protected Set<String> getAllowedTrailerHeadersAsSet() {
- return allowedTrailerHeaders;
+
+
+ /**
+ * The upgrade protocol instances configured.
+ */
+ private final List<UpgradeProtocol> upgradeProtocols = new ArrayList<>();
+ @Override
+ public void addUpgradeProtocol(UpgradeProtocol upgradeProtocol) {
+ upgradeProtocols.add(upgradeProtocol);
+ }
+ @Override
+ public UpgradeProtocol[] findUpgradeProtocols() {
+ return upgradeProtocols.toArray(new UpgradeProtocol[0]);
+ }
+
+ /**
+ * The protocols that are available via internal Tomcat support for access
+ * via HTTP upgrade.
+ */
+ private final Map<String,UpgradeProtocol> httpUpgradeProtocols = new HashMap<>();
+ /**
+ * The protocols that are available via internal Tomcat support for access
+ * via ALPN negotiation.
+ */
+ private final Map<String,UpgradeProtocol> negotiatedProtocols = new HashMap<>();
+ private void configureUpgradeProtocol(UpgradeProtocol upgradeProtocol) {
+ boolean isSSLEnabled = getEndpoint().isSSLEnabled();
+ // HTTP Upgrade
+ String httpUpgradeName = upgradeProtocol.getHttpUpgradeName(isSSLEnabled);
+ boolean httpUpgradeConfigured = false;
+ if (httpUpgradeName != null && httpUpgradeName.length() > 0) {
+ httpUpgradeProtocols.put(httpUpgradeName, upgradeProtocol);
+ httpUpgradeConfigured = true;
+ getLog().info(sm.getString("abstractHttp11Protocol.httpUpgradeConfigured",
+ getName(), httpUpgradeName));
+ }
+
+ // ALPN
+ String alpnName = upgradeProtocol.getAlpnName();
+ if (alpnName != null && alpnName.length() > 0) {
+ // ALPN requires SSL
+ if (isSSLEnabled) {
+ negotiatedProtocols.put(alpnName, upgradeProtocol);
+ getEndpoint().addNegotiatedProtocol(alpnName);
+ getLog().info(sm.getString("abstractHttp11Protocol.alpnConfigured",
+ getName(), alpnName));
+ } else {
+ if (!httpUpgradeConfigured) {
+ // HTTP Upgrade is not available for this protocol so it
+ // requires ALPN. It has been configured on a non-secure
+ // connector where ALPN is not available.
+ getLog().error(sm.getString("abstractHttp11Protocol.alpnWithNoTls",
+ upgradeProtocol.getClass().getName(), alpnName, getName()));
+ }
+ }
+ }
+ }
+ @Override
+ public UpgradeProtocol getNegotiatedProtocol(String negotiatedName) {
+ return negotiatedProtocols.get(negotiatedName);
+ }
+ @Override
+ public UpgradeProtocol getUpgradeProtocol(String upgradedName) {
+ return httpUpgradeProtocols.get(upgradedName);
}
// ------------------------------------------------ HTTP specific properties
// ------------------------------------------ passed through to the EndPoint
- public boolean isSSLEnabled() { return endpoint.isSSLEnabled();}
+ public boolean isSSLEnabled() { return getEndpoint().isSSLEnabled();}
public void setSSLEnabled(boolean SSLEnabled) {
- endpoint.setSSLEnabled(SSLEnabled);
+ getEndpoint().setSSLEnabled(SSLEnabled);
}
+ public boolean getUseSendfile() { return getEndpoint().getUseSendfile(); }
+ public void setUseSendfile(boolean useSendfile) { getEndpoint().setUseSendfile(useSendfile); }
+
+
/**
- * Maximum number of requests which can be performed over a keepalive
- * connection. The default is the same as for Apache HTTP Server.
+ * @return The maximum number of requests which can be performed over a
+ * keep-alive connection. The default is the same as for Apache HTTP
+ * Server (100).
*/
public int getMaxKeepAliveRequests() {
- return endpoint.getMaxKeepAliveRequests();
+ return getEndpoint().getMaxKeepAliveRequests();
}
public void setMaxKeepAliveRequests(int mkar) {
- endpoint.setMaxKeepAliveRequests(mkar);
+ getEndpoint().setMaxKeepAliveRequests(mkar);
+ }
+
+
+ // ----------------------------------------------- HTTPS specific properties
+ // ------------------------------------------ passed through to the EndPoint
+
+ public String getDefaultSSLHostConfigName() {
+ return getEndpoint().getDefaultSSLHostConfigName();
+ }
+ public void setDefaultSSLHostConfigName(String defaultSSLHostConfigName) {
+ getEndpoint().setDefaultSSLHostConfigName(defaultSSLHostConfigName);
+ if (defaultSSLHostConfig != null) {
+ defaultSSLHostConfig.setHostName(defaultSSLHostConfigName);
+ }
+ }
+
+
+ @Override
+ public void addSslHostConfig(SSLHostConfig sslHostConfig) {
+ getEndpoint().addSslHostConfig(sslHostConfig);
+ }
+
+ @Override
+ public SSLHostConfig[] findSslHostConfigs() {
+ return getEndpoint().findSslHostConfigs();
+ }
+
+ // ----------------------------------------------- HTTPS specific properties
+ // -------------------------------------------- Handled via an SSLHostConfig
+
+ private SSLHostConfig defaultSSLHostConfig = null;
+ private void registerDefaultSSLHostConfig() {
+ if (defaultSSLHostConfig == null) {
+ defaultSSLHostConfig = new SSLHostConfig();
+ defaultSSLHostConfig.setHostName(getDefaultSSLHostConfigName());
+ getEndpoint().addSslHostConfig(defaultSSLHostConfig);
+ }
+ }
+
+
+ // TODO: All of these SSL setters can be removed once it is no longer
+ // necessary to support the old configuration attributes (Tomcat 10?).
+
+ public void setSslEnabledProtocols(String enabledProtocols) {
+ registerDefaultSSLHostConfig();
+ defaultSSLHostConfig.setProtocols(enabledProtocols);
+ }
+ public void setSSLProtocol(String sslProtocol) {
+ registerDefaultSSLHostConfig();
+ defaultSSLHostConfig.setProtocols(sslProtocol);
+ }
+
+
+ public void setKeystoreFile(String keystoreFile) {
+ registerDefaultSSLHostConfig();
+ defaultSSLHostConfig.setCertificateKeystoreFile(keystoreFile);
+ }
+ public void setSSLCertificateChainFile(String certificateChainFile) {
+ registerDefaultSSLHostConfig();
+ defaultSSLHostConfig.setCertificateChainFile(certificateChainFile);
+ }
+ public void setSSLCertificateFile(String certificateFile) {
+ registerDefaultSSLHostConfig();
+ defaultSSLHostConfig.setCertificateFile(certificateFile);
+ }
+ public void setSSLCertificateKeyFile(String certificateKeyFile) {
+ registerDefaultSSLHostConfig();
+ defaultSSLHostConfig.setCertificateKeyFile(certificateKeyFile);
+ }
+
+
+ public void setAlgorithm(String keyManagerAlgorithm) {
+ registerDefaultSSLHostConfig();
+ defaultSSLHostConfig.setKeyManagerAlgorithm(keyManagerAlgorithm);
+ }
+
+
+ public void setClientAuth(String certificateVerification) {
+ registerDefaultSSLHostConfig();
+ defaultSSLHostConfig.setCertificateVerification(certificateVerification);
+ }
+
+
+ public void setSSLVerifyClient(String certificateVerification) {
+ registerDefaultSSLHostConfig();
+ defaultSSLHostConfig.setCertificateVerification(certificateVerification);
+ }
+
+
+ public void setTrustMaxCertLength(int certificateVerificationDepth){
+ registerDefaultSSLHostConfig();
+ defaultSSLHostConfig.setCertificateVerificationDepth(certificateVerificationDepth);
+ }
+ public void setSSLVerifyDepth(int certificateVerificationDepth) {
+ registerDefaultSSLHostConfig();
+ defaultSSLHostConfig.setCertificateVerificationDepth(certificateVerificationDepth);
+ }
+
+
+ public void setUseServerCipherSuitesOrder(String honorCipherOrder) {
+ registerDefaultSSLHostConfig();
+ defaultSSLHostConfig.setHonorCipherOrder(honorCipherOrder);
+ }
+ public void setSSLHonorCipherOrder(String honorCipherOrder) {
+ registerDefaultSSLHostConfig();
+ defaultSSLHostConfig.setHonorCipherOrder(honorCipherOrder);
+ }
+
+
+ public void setCiphers(String ciphers) {
+ registerDefaultSSLHostConfig();
+ defaultSSLHostConfig.setCiphers(ciphers);
+ }
+ public void setSSLCipherSuite(String ciphers) {
+ registerDefaultSSLHostConfig();
+ defaultSSLHostConfig.setCiphers(ciphers);
+ }
+
+ public void setKeystorePass(String certificateKeystorePassword) {
+ registerDefaultSSLHostConfig();
+ defaultSSLHostConfig.setCertificateKeystorePassword(certificateKeystorePassword);
+ }
+
+ public void setKeyPass(String certificateKeyPassword) {
+ registerDefaultSSLHostConfig();
+ defaultSSLHostConfig.setCertificateKeyPassword(certificateKeyPassword);
+ }
+ public void setSSLPassword(String certificateKeyPassword) {
+ registerDefaultSSLHostConfig();
+ defaultSSLHostConfig.setCertificateKeyPassword(certificateKeyPassword);
+ }
+
+
+ public void setCrlFile(String certificateRevocationListFile){
+ registerDefaultSSLHostConfig();
+ defaultSSLHostConfig.setCertificateRevocationListFile(certificateRevocationListFile);
+ }
+ public void setSSLCARevocationFile(String certificateRevocationListFile) {
+ registerDefaultSSLHostConfig();
+ defaultSSLHostConfig.setCertificateRevocationListFile(certificateRevocationListFile);
+ }
+ public void setSSLCARevocationPath(String certificateRevocationListPath) {
+ registerDefaultSSLHostConfig();
+ defaultSSLHostConfig.setCertificateRevocationListPath(certificateRevocationListPath);
+ }
+
+
+ public void setKeystoreType(String certificateKeystoreType) {
+ registerDefaultSSLHostConfig();
+ defaultSSLHostConfig.setCertificateKeystoreType(certificateKeystoreType);
+ }
+
+
+ public void setKeystoreProvider(String certificateKeystoreProvider) {
+ registerDefaultSSLHostConfig();
+ defaultSSLHostConfig.setCertificateKeystoreProvider(certificateKeystoreProvider);
+ }
+
+
+ public void setKeyAlias(String certificateKeyAlias) {
+ registerDefaultSSLHostConfig();
+ defaultSSLHostConfig.setCertificateKeyAlias(certificateKeyAlias);
+ }
+
+
+ public void setTruststoreAlgorithm(String truststoreAlgorithm){
+ registerDefaultSSLHostConfig();
+ defaultSSLHostConfig.setTruststoreAlgorithm(truststoreAlgorithm);
+ }
+
+
+ public void setTruststoreFile(String truststoreFile){
+ registerDefaultSSLHostConfig();
+ defaultSSLHostConfig.setTruststoreFile(truststoreFile);
+ }
+
+
+ public void setTruststorePass(String truststorePassword){
+ registerDefaultSSLHostConfig();
+ defaultSSLHostConfig.setTruststorePassword(truststorePassword);
+ }
+
+
+ public void setTruststoreType(String truststoreType){
+ registerDefaultSSLHostConfig();
+ defaultSSLHostConfig.setTruststoreType(truststoreType);
+ }
+
+
+ public void setTruststoreProvider(String truststoreProvider){
+ registerDefaultSSLHostConfig();
+ defaultSSLHostConfig.setTruststoreProvider(truststoreProvider);
+ }
+
+
+ public void setSslProtocol(String sslProtocol) {
+ registerDefaultSSLHostConfig();
+ defaultSSLHostConfig.setSslProtocol(sslProtocol);
+ }
+
+
+ public void setSessionCacheSize(int sessionCacheSize){
+ registerDefaultSSLHostConfig();
+ defaultSSLHostConfig.setSessionCacheSize(sessionCacheSize);
+ }
+
+
+ public void setSessionTimeout(int sessionTimeout){
+ registerDefaultSSLHostConfig();
+ defaultSSLHostConfig.setSessionTimeout(sessionTimeout);
+ }
+
+
+ public void setSSLCACertificatePath(String caCertificatePath) {
+ registerDefaultSSLHostConfig();
+ defaultSSLHostConfig.setCaCertificatePath(caCertificatePath);
+ }
+
+
+ public void setSSLCACertificateFile(String caCertificateFile) {
+ registerDefaultSSLHostConfig();
+ defaultSSLHostConfig.setCaCertificateFile(caCertificateFile);
+ }
+
+
+ public void setSSLDisableCompression(boolean disableCompression) {
+ registerDefaultSSLHostConfig();
+ defaultSSLHostConfig.setDisableCompression(disableCompression);
+ }
+
+
+ public void setSSLDisableSessionTickets(boolean disableSessionTickets) {
+ registerDefaultSSLHostConfig();
+ defaultSSLHostConfig.setDisableSessionTickets(disableSessionTickets);
+ }
+
+
+ public void setTrustManagerClassName(String trustManagerClassName) {
+ registerDefaultSSLHostConfig();
+ defaultSSLHostConfig.setTrustManagerClassName(trustManagerClassName);
}
// ------------------------------------------------------------- Common code
- // Common configuration required for all new HTTP11 processors
- protected void configureProcessor(AbstractHttp11Processor<S> processor) {
+ @Override
+ protected Processor createProcessor() {
+ Http11Processor processor = new Http11Processor(getMaxHttpHeaderSize(), getEndpoint(),
+ getMaxTrailerSize(), allowedTrailerHeaders, getMaxExtensionSize(),
+ getMaxSwallowSize(), httpUpgradeProtocols);
processor.setAdapter(getAdapter());
processor.setMaxKeepAliveRequests(getMaxKeepAliveRequests());
- processor.setKeepAliveTimeout(getKeepAliveTimeout());
processor.setConnectionUploadTimeout(getConnectionUploadTimeout());
processor.setDisableUploadTimeout(getDisableUploadTimeout());
processor.setCompressionMinSize(getCompressionMinSize());
@@ -285,9 +645,22 @@ public abstract class AbstractHttp11Protocol<S> extends AbstractProtocol<S> {
processor.setNoCompressionUserAgents(getNoCompressionUserAgents());
processor.setCompressableMimeTypes(getCompressableMimeTypes());
processor.setRestrictedUserAgents(getRestrictedUserAgents());
- processor.setSocketBuffer(getSocketBuffer());
processor.setMaxSavePostSize(getMaxSavePostSize());
processor.setServer(getServer());
- processor.setMaxCookieCount(getMaxCookieCount());
+ processor.setServerRemoveAppProvidedValues(getServerRemoveAppProvidedValues());
+ return processor;
+ }
+
+
+ @Override
+ protected Processor createUpgradeProcessor(
+ SocketWrapperBase<?> socket,
+ UpgradeToken upgradeToken) {
+ HttpUpgradeHandler httpUpgradeHandler = upgradeToken.getHttpUpgradeHandler();
+ if (httpUpgradeHandler instanceof InternalHttpUpgradeHandler) {
+ return new UpgradeProcessorInternal(socket, upgradeToken);
+ } else {
+ return new UpgradeProcessorExternal(socket, upgradeToken);
+ }
}
}
diff --git a/java/org/apache/coyote/http11/AbstractInputBuffer.java b/java/org/apache/coyote/http11/AbstractInputBuffer.java
deleted file mode 100644
index dde588b..0000000
--- a/java/org/apache/coyote/http11/AbstractInputBuffer.java
+++ /dev/null
@@ -1,365 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.coyote.http11;
-
-import java.io.IOException;
-import java.nio.ByteBuffer;
-
-import org.apache.coyote.InputBuffer;
-import org.apache.coyote.Request;
-import org.apache.juli.logging.Log;
-import org.apache.tomcat.util.buf.ByteChunk;
-import org.apache.tomcat.util.http.MimeHeaders;
-import org.apache.tomcat.util.net.AbstractEndpoint;
-import org.apache.tomcat.util.net.SocketWrapper;
-import org.apache.tomcat.util.res.StringManager;
-
-public abstract class AbstractInputBuffer<S> implements InputBuffer{
-
- /**
- * The string manager for this package.
- */
- protected static final StringManager sm = StringManager.getManager(Constants.Package);
-
-
- /**
- * Associated Coyote request.
- */
- protected Request request;
-
-
- /**
- * Headers of the associated request.
- */
- protected MimeHeaders headers;
-
-
- /**
- * State.
- */
- protected boolean parsingHeader;
-
-
- /**
- * Swallow input ? (in the case of an expectation)
- */
- protected boolean swallowInput;
-
-
- /**
- * Pointer to the current read buffer.
- */
- protected byte[] buf;
-
-
- /**
- * Last valid byte.
- */
- protected int lastValid;
-
-
- /**
- * Position in the buffer.
- */
- protected int pos;
-
-
- /**
- * Pos of the end of the header in the buffer, which is also the
- * start of the body.
- */
- protected int end;
-
-
- /**
- * Underlying input buffer.
- */
- protected InputBuffer inputStreamInputBuffer;
-
-
- /**
- * Filter library.
- * Note: Filter[Constants.CHUNKED_FILTER] is always the "chunked" filter.
- */
- protected InputFilter[] filterLibrary;
-
-
- /**
- * Active filters (in order).
- */
- protected InputFilter[] activeFilters;
-
-
- /**
- * Index of the last active filter.
- */
- protected int lastActiveFilter;
-
-
- // ------------------------------------------------------------- Properties
-
- /**
- * Add an input filter to the filter library.
- *
- * @throws NullPointerException if the supplied filter is null
- */
- public void addFilter(InputFilter filter) {
-
- if (filter == null) {
- throw new NullPointerException(sm.getString("iib.filter.npe"));
- }
-
- InputFilter[] newFilterLibrary =
- new InputFilter[filterLibrary.length + 1];
- for (int i = 0; i < filterLibrary.length; i++) {
- newFilterLibrary[i] = filterLibrary[i];
- }
- newFilterLibrary[filterLibrary.length] = filter;
- filterLibrary = newFilterLibrary;
-
- activeFilters = new InputFilter[filterLibrary.length];
- }
-
-
- /**
- * Get filters.
- */
- public InputFilter[] getFilters() {
-
- return filterLibrary;
-
- }
-
-
- /**
- * Add an input filter to the filter library.
- */
- public void addActiveFilter(InputFilter filter) {
-
- if (lastActiveFilter == -1) {
- filter.setBuffer(inputStreamInputBuffer);
- } else {
- for (int i = 0; i <= lastActiveFilter; i++) {
- if (activeFilters[i] == filter)
- return;
- }
- filter.setBuffer(activeFilters[lastActiveFilter]);
- }
-
- activeFilters[++lastActiveFilter] = filter;
-
- filter.setRequest(request);
-
- }
-
-
- /**
- * Set the swallow input flag.
- */
- public void setSwallowInput(boolean swallowInput) {
- this.swallowInput = swallowInput;
- }
-
-
- /**
- * Implementations are expected to call {@link Request#setStartTime(long)}
- * as soon as the first byte is read from the request.
- */
- public abstract boolean parseRequestLine(boolean useAvailableDataOnly)
- throws IOException;
-
- public abstract boolean parseHeaders() throws IOException;
-
- /**
- * Attempts to read some data into the input buffer.
- *
- * @return <code>true</code> if more data was added to the input buffer
- * otherwise <code>false</code>
- */
- protected abstract boolean fill(boolean block) throws IOException;
-
- protected abstract void init(SocketWrapper<S> socketWrapper,
- AbstractEndpoint<S> endpoint) throws IOException;
-
- protected abstract Log getLog();
-
-
- // --------------------------------------------------------- Public Methods
-
- /**
- * Recycle the input buffer. This should be called when closing the
- * connection.
- */
- public void recycle() {
-
- // Recycle Request object
- request.recycle();
-
- // Recycle filters
- for (int i = 0; i <= lastActiveFilter; i++) {
- activeFilters[i].recycle();
- }
-
- lastValid = 0;
- pos = 0;
- lastActiveFilter = -1;
- parsingHeader = true;
- swallowInput = true;
-
- }
-
-
- /**
- * End processing of current HTTP request.
- * Note: All bytes of the current request should have been already
- * consumed. This method only resets all the pointers so that we are ready
- * to parse the next HTTP request.
- */
- public void nextRequest() {
-
- // Recycle Request object
- request.recycle();
-
- // Copy leftover bytes to the beginning of the buffer
- if (lastValid - pos > 0 && pos > 0) {
- System.arraycopy(buf, pos, buf, 0, lastValid - pos);
- }
- // Always reset pos to zero
- lastValid = lastValid - pos;
- pos = 0;
-
- // Recycle filters
- for (int i = 0; i <= lastActiveFilter; i++) {
- activeFilters[i].recycle();
- }
-
- // Reset pointers
- lastActiveFilter = -1;
- parsingHeader = true;
- swallowInput = true;
- }
-
-
- /**
- * End request (consumes leftover bytes).
- *
- * @throws IOException an underlying I/O error occurred
- */
- public void endRequest() throws IOException {
-
- if (swallowInput && (lastActiveFilter != -1)) {
- int extraBytes = (int) activeFilters[lastActiveFilter].end();
- pos = pos - extraBytes;
- }
- }
-
-
- /**
- * Available bytes in the buffers (note that due to encoding, this may not
- * correspond).
- */
- public int available(boolean read) {
- int available = lastValid - pos;
- if ((available == 0) && (lastActiveFilter >= 0)) {
- for (int i = 0; (available == 0) && (i <= lastActiveFilter); i++) {
- available = activeFilters[i].available();
- }
- }
- if (available > 0 || !read) {
- return available;
- }
-
- try {
- fill(false);
- available = lastValid - pos;
- } catch (IOException ioe) {
- if (getLog().isDebugEnabled()) {
- getLog().debug(sm.getString("iib.available.readFail"), ioe);
- }
- // 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.
- available = 1;
- }
- return available;
- }
-
-
- /**
- * Has all of the request body been read? There are subtle differences
- * between this and available() > 0 primarily because of having to handle
- * faking non-blocking reads with the blocking IO connector.
- */
- public boolean isFinished() {
- if (lastValid > pos) {
- // Data to read in the buffer so not finished
- return false;
- }
-
- /*
- * Don't use fill(false) here because in the following circumstances
- * BIO will block - possibly indefinitely
- * - client is using keep-alive and connection is still open
- * - client has sent the complete request
- * - client has not sent any of the next request (i.e. no pipelining)
- * - application has read the complete request
- */
-
- // Check the InputFilters
-
- if (lastActiveFilter >= 0) {
- return activeFilters[lastActiveFilter].isFinished();
- } else {
- // No filters. Assume request is not finished. EOF will signal end of
- // request.
- return false;
- }
- }
-
- ByteBuffer getLeftover() {
- int available = lastValid - pos;
- if (available > 0) {
- return ByteBuffer.wrap(buf, pos, available);
- } else {
- return null;
- }
- }
-
- /**
- * Is standard Servlet blocking IO being used for input?
- */
- protected final boolean isBlocking() {
- return request.getReadListener() == null;
- }
-
-
- // ---------------------------------------------------- InputBuffer Methods
-
- /**
- * Read some bytes.
- */
- @Override
- public int doRead(ByteChunk chunk, Request req)
- throws IOException {
-
- if (lastActiveFilter == -1)
- return inputStreamInputBuffer.doRead(chunk, req);
- else
- return activeFilters[lastActiveFilter].doRead(chunk,req);
-
- }
-}
diff --git a/java/org/apache/coyote/http11/AbstractNioInputBuffer.java b/java/org/apache/coyote/http11/AbstractNioInputBuffer.java
deleted file mode 100644
index 0e2a1cc..0000000
--- a/java/org/apache/coyote/http11/AbstractNioInputBuffer.java
+++ /dev/null
@@ -1,667 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.coyote.http11;
-
-import java.io.IOException;
-import java.nio.charset.StandardCharsets;
-
-import org.apache.coyote.Request;
-import org.apache.tomcat.util.buf.MessageBytes;
-import org.apache.tomcat.util.http.parser.HttpParser;
-
-public abstract class AbstractNioInputBuffer<S> extends AbstractInputBuffer<S> {
-
- // -------------------------------------------------------------- Constants
-
- enum HeaderParseStatus {
- DONE, HAVE_MORE_HEADERS, NEED_MORE_DATA
- }
-
- enum HeaderParsePosition {
- /**
- * Start of a new header. A CRLF here means that there are no more
- * headers. Any other character starts a header name.
- */
- HEADER_START,
- /**
- * Reading a header name. All characters of header are HTTP_TOKEN_CHAR.
- * Header name is followed by ':'. No whitespace is allowed.<br>
- * Any non-HTTP_TOKEN_CHAR (this includes any whitespace) encountered
- * before ':' will result in the whole line being ignored.
- */
- HEADER_NAME,
- /**
- * Skipping whitespace before text of header value starts, either on the
- * first line of header value (just after ':') or on subsequent lines
- * when it is known that subsequent line starts with SP or HT.
- */
- HEADER_VALUE_START,
- /**
- * Reading the header value. We are inside the value. Either on the
- * first line or on any subsequent line. We come into this state from
- * HEADER_VALUE_START after the first non-SP/non-HT byte is encountered
- * on the line.
- */
- HEADER_VALUE,
- /**
- * Before reading a new line of a header. Once the next byte is peeked,
- * the state changes without advancing our position. The state becomes
- * either HEADER_VALUE_START (if that first byte is SP or HT), or
- * HEADER_START (otherwise).
- */
- HEADER_MULTI_LINE,
- /**
- * Reading all bytes until the next CRLF. The line is being ignored.
- */
- HEADER_SKIPLINE
- }
-
- // ----------------------------------------------------------- Constructors
-
-
- /**
- * Alternate constructor.
- */
- public AbstractNioInputBuffer(Request request, int headerBufferSize) {
-
- this.request = request;
- headers = request.getMimeHeaders();
-
- this.headerBufferSize = headerBufferSize;
-
- filterLibrary = new InputFilter[0];
- activeFilters = new InputFilter[0];
- lastActiveFilter = -1;
-
- parsingHeader = true;
- parsingRequestLine = true;
- parsingRequestLinePhase = 0;
- parsingRequestLineEol = false;
- parsingRequestLineStart = 0;
- parsingRequestLineQPos = -1;
- headerParsePos = HeaderParsePosition.HEADER_START;
- headerData.recycle();
- swallowInput = true;
-
- }
-
- /**
- * Parsing state - used for non blocking parsing so that
- * when more data arrives, we can pick up where we left off.
- */
- private boolean parsingRequestLine;
- private int parsingRequestLinePhase = 0;
- private boolean parsingRequestLineEol = false;
- private int parsingRequestLineStart = 0;
- private int parsingRequestLineQPos = -1;
- private HeaderParsePosition headerParsePos;
-
- /**
- * Maximum allowed size of the HTTP request line plus headers plus any
- * leading blank lines.
- */
- protected final int headerBufferSize;
-
- /**
- * Known size of the NioChannel read buffer.
- */
- protected int socketReadBufferSize;
-
- // --------------------------------------------------------- Public Methods
-
- /**
- * Recycle the input buffer. This should be called when closing the
- * connection.
- */
- @Override
- public void recycle() {
- super.recycle();
- headerParsePos = HeaderParsePosition.HEADER_START;
- parsingRequestLine = true;
- parsingRequestLinePhase = 0;
- parsingRequestLineEol = false;
- parsingRequestLineStart = 0;
- parsingRequestLineQPos = -1;
- headerData.recycle();
- }
-
-
- /**
- * End processing of current HTTP request.
- * Note: All bytes of the current request should have been already
- * consumed. This method only resets all the pointers so that we are ready
- * to parse the next HTTP request.
- */
- @Override
- public void nextRequest() {
- super.nextRequest();
- headerParsePos = HeaderParsePosition.HEADER_START;
- parsingRequestLine = true;
- parsingRequestLinePhase = 0;
- parsingRequestLineEol = false;
- parsingRequestLineStart = 0;
- parsingRequestLineQPos = -1;
- headerData.recycle();
- }
-
- /**
- * Read the request line. This function is meant to be used during the
- * HTTP request header parsing. Do NOT attempt to read the request body
- * using it.
- *
- * @throws IOException If an exception occurs during the underlying socket
- * read operations, or if the given buffer is not big enough to accommodate
- * the whole line.
- * @return true if data is properly fed; false if no data is available
- * immediately and thread should be freed
- */
- @Override
- public boolean parseRequestLine(boolean useAvailableDataOnly)
- throws IOException {
-
- //check state
- if ( !parsingRequestLine ) return true;
- //
- // Skipping blank lines
- //
- if ( parsingRequestLinePhase < 2 ) {
- byte chr = 0;
- do {
-
- // Read new bytes if needed
- if (pos >= lastValid) {
- if (useAvailableDataOnly) {
- return false;
- }
- // Do a simple read with a short timeout
- if (!fill(false)) {
- // A read is pending, so no longer in initial state
- parsingRequestLinePhase = 1;
- return false;
- }
- }
- // Set the start time once we start reading data (even if it is
- // just skipping blank lines)
- if (request.getStartTime() < 0) {
- request.setStartTime(System.currentTimeMillis());
- }
- chr = buf[pos++];
- } while ((chr == Constants.CR) || (chr == Constants.LF));
- pos--;
-
- parsingRequestLineStart = pos;
- parsingRequestLinePhase = 2;
- if (getLog().isDebugEnabled()) {
- getLog().debug("Received ["
- + new String(buf, pos, lastValid - pos,
- StandardCharsets.ISO_8859_1)
- + "]");
- }
- }
- if ( parsingRequestLinePhase == 2 ) {
- //
- // Reading the method name
- // Method name is a token
- //
- boolean space = false;
- while (!space) {
- // Read new bytes if needed
- if (pos >= lastValid) {
- if (!fill(false)) //request line parsing
- return false;
- }
- // Spec says method name is a token followed by a single SP but
- // also be tolerant of multiple SP and/or HT.
- if (buf[pos] == Constants.SP || buf[pos] == Constants.HT) {
- space = true;
- request.method().setBytes(buf, parsingRequestLineStart, pos - parsingRequestLineStart);
- } else if (!HttpParser.isToken(buf[pos])) {
- throw new IllegalArgumentException(sm.getString("iib.invalidmethod"));
- }
- pos++;
- }
- parsingRequestLinePhase = 3;
- }
- if ( parsingRequestLinePhase == 3 ) {
- // Spec says single SP but also be tolerant of multiple SP and/or HT
- boolean space = true;
- while (space) {
- // Read new bytes if needed
- if (pos >= lastValid) {
- if (!fill(false)) //request line parsing
- return false;
- }
- if (buf[pos] == Constants.SP || buf[pos] == Constants.HT) {
- pos++;
- } else {
- space = false;
- }
- }
- parsingRequestLineStart = pos;
- parsingRequestLinePhase = 4;
- }
- if (parsingRequestLinePhase == 4) {
- // Mark the current buffer position
-
- int end = 0;
- //
- // Reading the URI
- //
- boolean space = false;
- while (!space) {
- // Read new bytes if needed
- if (pos >= lastValid) {
- if (!fill(false)) //request line parsing
- return false;
- }
- if (buf[pos] == Constants.SP || buf[pos] == Constants.HT) {
- space = true;
- end = pos;
- } else if ((buf[pos] == Constants.CR)
- || (buf[pos] == Constants.LF)) {
- // HTTP/0.9 style request
- parsingRequestLineEol = true;
- space = true;
- end = pos;
- } else if ((buf[pos] == Constants.QUESTION) && (parsingRequestLineQPos == -1)) {
- parsingRequestLineQPos = pos;
- } else if (HttpParser.isNotRequestTarget(buf[pos])) {
- throw new IllegalArgumentException(sm.getString("iib.invalidRequestTarget"));
- }
- pos++;
- }
- if (parsingRequestLineQPos >= 0) {
- request.queryString().setBytes(buf, parsingRequestLineQPos + 1,
- end - parsingRequestLineQPos - 1);
- request.requestURI().setBytes(buf, parsingRequestLineStart, parsingRequestLineQPos - parsingRequestLineStart);
- } else {
- request.requestURI().setBytes(buf, parsingRequestLineStart, end - parsingRequestLineStart);
- }
- parsingRequestLinePhase = 5;
- }
- if ( parsingRequestLinePhase == 5 ) {
- // Spec says single SP but also be tolerant of multiple and/or HT
- boolean space = true;
- while (space) {
- // Read new bytes if needed
- if (pos >= lastValid) {
- if (!fill(false)) //request line parsing
- return false;
- }
- if (buf[pos] == Constants.SP || buf[pos] == Constants.HT) {
- pos++;
- } else {
- space = false;
- }
- }
- parsingRequestLineStart = pos;
- parsingRequestLinePhase = 6;
-
- // Mark the current buffer position
- end = 0;
- }
- if (parsingRequestLinePhase == 6) {
- //
- // Reading the protocol
- // Protocol is always "HTTP/" DIGIT "." DIGIT
- //
- while (!parsingRequestLineEol) {
- // Read new bytes if needed
- if (pos >= lastValid) {
- if (!fill(false)) //request line parsing
- return false;
- }
-
- if (buf[pos] == Constants.CR) {
- end = pos;
- } else if (buf[pos] == Constants.LF) {
- if (end == 0)
- end = pos;
- parsingRequestLineEol = true;
- } else if (!HttpParser.isHttpProtocol(buf[pos])) {
- throw new IllegalArgumentException(sm.getString("iib.invalidHttpProtocol"));
- }
- pos++;
- }
-
- if ( (end - parsingRequestLineStart) > 0) {
- request.protocol().setBytes(buf, parsingRequestLineStart, end - parsingRequestLineStart);
- } else {
- request.protocol().setString("");
- }
- parsingRequestLine = false;
- parsingRequestLinePhase = 0;
- parsingRequestLineEol = false;
- parsingRequestLineStart = 0;
- return true;
- }
- throw new IllegalStateException("Invalid request line parse phase:"+parsingRequestLinePhase);
- }
-
- protected void expand(int newsize) {
- if ( newsize > buf.length ) {
- if (parsingHeader) {
- throw new IllegalArgumentException(
- sm.getString("iib.requestheadertoolarge.error"));
- }
- // Should not happen
- getLog().warn("Expanding buffer size. Old size: " + buf.length
- + ", new size: " + newsize, new Exception());
- byte[] tmp = new byte[newsize];
- System.arraycopy(buf,0,tmp,0,buf.length);
- buf = tmp;
- }
- }
-
- /**
- * Parse the HTTP headers.
- */
- @Override
- public boolean parseHeaders()
- throws IOException {
- if (!parsingHeader) {
- throw new IllegalStateException(
- sm.getString("iib.parseheaders.ise.error"));
- }
-
- HeaderParseStatus status = HeaderParseStatus.HAVE_MORE_HEADERS;
-
- 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 (pos > headerBufferSize
- || buf.length - pos < socketReadBufferSize) {
- throw new IllegalArgumentException(
- sm.getString("iib.requestheadertoolarge.error"));
- }
- } while ( status == HeaderParseStatus.HAVE_MORE_HEADERS );
- if (status == HeaderParseStatus.DONE) {
- parsingHeader = false;
- end = pos;
- return true;
- } else {
- return false;
- }
- }
-
- /**
- * Parse an HTTP header.
- *
- * @return false after reading a blank line (which indicates that the
- * HTTP header parsing is done
- */
- private HeaderParseStatus parseHeader()
- throws IOException {
-
- //
- // Check for blank line
- //
-
- byte chr = 0;
- while (headerParsePos == HeaderParsePosition.HEADER_START) {
-
- // Read new bytes if needed
- if (pos >= lastValid) {
- if (!fill(false)) {//parse header
- headerParsePos = HeaderParsePosition.HEADER_START;
- return HeaderParseStatus.NEED_MORE_DATA;
- }
- }
-
- chr = buf[pos];
-
- if (chr == Constants.CR) {
- // Skip
- } else if (chr == Constants.LF) {
- pos++;
- return HeaderParseStatus.DONE;
- } else {
- break;
- }
-
- pos++;
-
- }
-
- if ( headerParsePos == HeaderParsePosition.HEADER_START ) {
- // Mark the current buffer position
- headerData.start = pos;
- headerParsePos = HeaderParsePosition.HEADER_NAME;
- }
-
- //
- // Reading the header name
- // Header name is always US-ASCII
- //
-
- while (headerParsePos == HeaderParsePosition.HEADER_NAME) {
-
- // Read new bytes if needed
- if (pos >= lastValid) {
- if (!fill(false)) { //parse header
- return HeaderParseStatus.NEED_MORE_DATA;
- }
- }
-
- chr = buf[pos];
- if (chr == Constants.COLON) {
- headerParsePos = HeaderParsePosition.HEADER_VALUE_START;
- headerData.headerValue = headers.addValue(buf, headerData.start, pos - headerData.start);
- pos++;
- // Mark the current buffer position
- headerData.start = pos;
- headerData.realPos = pos;
- headerData.lastSignificantChar = pos;
- break;
- } else if (!HttpParser.isToken(chr)) {
- // If a non-token header is detected, skip the line and
- // ignore the header
- headerData.lastSignificantChar = pos;
- return skipLine();
- }
-
- // chr is next byte of header name. Convert to lowercase.
- if ((chr >= Constants.A) && (chr <= Constants.Z)) {
- buf[pos] = (byte) (chr - Constants.LC_OFFSET);
- }
- pos++;
- }
-
- // Skip the line and ignore the header
- if (headerParsePos == HeaderParsePosition.HEADER_SKIPLINE) {
- return skipLine();
- }
-
- //
- // Reading the header value (which can be spanned over multiple lines)
- //
-
- while (headerParsePos == HeaderParsePosition.HEADER_VALUE_START ||
- headerParsePos == HeaderParsePosition.HEADER_VALUE ||
- headerParsePos == HeaderParsePosition.HEADER_MULTI_LINE) {
-
- if ( headerParsePos == HeaderParsePosition.HEADER_VALUE_START ) {
- // Skipping spaces
- while (true) {
- // Read new bytes if needed
- if (pos >= lastValid) {
- if (!fill(false)) {//parse header
- //HEADER_VALUE_START
- return HeaderParseStatus.NEED_MORE_DATA;
- }
- }
-
- chr = buf[pos];
- if (chr == Constants.SP || chr == Constants.HT) {
- pos++;
- } else {
- headerParsePos = HeaderParsePosition.HEADER_VALUE;
- break;
- }
- }
- }
- if ( headerParsePos == HeaderParsePosition.HEADER_VALUE ) {
-
- // Reading bytes until the end of the line
- boolean eol = false;
- while (!eol) {
-
- // Read new bytes if needed
- if (pos >= lastValid) {
- if (!fill(false)) {//parse header
- //HEADER_VALUE
- return HeaderParseStatus.NEED_MORE_DATA;
- }
- }
-
- chr = buf[pos];
- if (chr == Constants.CR) {
- // Skip
- } else if (chr == Constants.LF) {
- eol = true;
- } else if (chr == Constants.SP || chr == Constants.HT) {
- buf[headerData.realPos] = chr;
- headerData.realPos++;
- } else {
- buf[headerData.realPos] = chr;
- headerData.realPos++;
- headerData.lastSignificantChar = headerData.realPos;
- }
-
- pos++;
- }
-
- // Ignore whitespaces at the end of the line
- headerData.realPos = headerData.lastSignificantChar;
-
- // Checking the first character of the new line. If the character
- // is a LWS, then it's a multiline header
- headerParsePos = HeaderParsePosition.HEADER_MULTI_LINE;
- }
- // Read new bytes if needed
- if (pos >= lastValid) {
- if (!fill(false)) {//parse header
- //HEADER_MULTI_LINE
- return HeaderParseStatus.NEED_MORE_DATA;
- }
- }
-
- chr = buf[pos];
- if ( headerParsePos == HeaderParsePosition.HEADER_MULTI_LINE ) {
- if ( (chr != Constants.SP) && (chr != Constants.HT)) {
- headerParsePos = HeaderParsePosition.HEADER_START;
- break;
- } else {
- // Copying one extra space in the buffer (since there must
- // be at least one space inserted between the lines)
- buf[headerData.realPos] = chr;
- headerData.realPos++;
- headerParsePos = HeaderParsePosition.HEADER_VALUE_START;
- }
- }
- }
- // Set the header value
- headerData.headerValue.setBytes(buf, headerData.start,
- headerData.lastSignificantChar - headerData.start);
- headerData.recycle();
- return HeaderParseStatus.HAVE_MORE_HEADERS;
- }
-
- public int getParsingRequestLinePhase() {
- return parsingRequestLinePhase;
- }
-
- private HeaderParseStatus skipLine() throws IOException {
- headerParsePos = HeaderParsePosition.HEADER_SKIPLINE;
- boolean eol = false;
-
- // Reading bytes until the end of the line
- while (!eol) {
-
- // Read new bytes if needed
- if (pos >= lastValid) {
- if (!fill(false)) {
- return HeaderParseStatus.NEED_MORE_DATA;
- }
- }
-
- if (buf[pos] == Constants.CR) {
- // Skip
- } else if (buf[pos] == Constants.LF) {
- eol = true;
- } else {
- headerData.lastSignificantChar = pos;
- }
-
- pos++;
- }
- if (getLog().isDebugEnabled()) {
- getLog().debug(sm.getString("iib.invalidheader", new String(buf,
- headerData.start,
- headerData.lastSignificantChar - headerData.start + 1,
- StandardCharsets.ISO_8859_1)));
- }
-
- headerParsePos = HeaderParsePosition.HEADER_START;
- return HeaderParseStatus.HAVE_MORE_HEADERS;
- }
-
- private final HeaderParseData headerData = new HeaderParseData();
- public static class HeaderParseData {
- /**
- * When parsing header name: first character of the header.<br>
- * When skipping broken header line: first character of the header.<br>
- * When parsing header value: first character after ':'.
- */
- int start = 0;
- /**
- * When parsing header name: not used (stays as 0).<br>
- * When skipping broken header line: not used (stays as 0).<br>
- * When parsing header value: starts as the first character after ':'.
- * Then is increased as far as more bytes of the header are harvested.
- * Bytes from buf[pos] are copied to buf[realPos]. Thus the string from
- * [start] to [realPos-1] is the prepared value of the header, with
- * whitespaces removed as needed.<br>
- */
- int realPos = 0;
- /**
- * When parsing header name: not used (stays as 0).<br>
- * When skipping broken header line: last non-CR/non-LF character.<br>
- * When parsing header value: position after the last not-LWS character.<br>
- */
- int lastSignificantChar = 0;
- /**
- * MB that will store the value of the header. It is null while parsing
- * header name and is created after the name has been parsed.
- */
- MessageBytes headerValue = null;
- public void recycle() {
- start = 0;
- realPos = 0;
- lastSignificantChar = 0;
- headerValue = null;
- }
- }
-
-}
diff --git a/java/org/apache/coyote/http11/AbstractOutputBuffer.java b/java/org/apache/coyote/http11/AbstractOutputBuffer.java
deleted file mode 100644
index f731752..0000000
--- a/java/org/apache/coyote/http11/AbstractOutputBuffer.java
+++ /dev/null
@@ -1,655 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.coyote.http11;
-
-import java.io.IOException;
-import java.security.AccessController;
-import java.security.PrivilegedAction;
-import java.util.Iterator;
-import java.util.concurrent.LinkedBlockingDeque;
-
-import org.apache.coyote.ActionCode;
-import org.apache.coyote.ByteBufferHolder;
-import org.apache.coyote.OutputBuffer;
-import org.apache.coyote.Response;
-import org.apache.coyote.http11.filters.GzipOutputFilter;
-import org.apache.tomcat.util.buf.ByteChunk;
-import org.apache.tomcat.util.buf.MessageBytes;
-import org.apache.tomcat.util.http.HttpMessages;
-import org.apache.tomcat.util.net.AbstractEndpoint;
-import org.apache.tomcat.util.net.SocketWrapper;
-import org.apache.tomcat.util.res.StringManager;
-
-public abstract class AbstractOutputBuffer<S> implements OutputBuffer {
-
- // ----------------------------------------------------- Instance Variables
-
-
- /**
- * Associated Coyote response.
- */
- protected Response response;
-
-
- /**
- * Committed flag.
- */
- protected boolean committed;
-
-
- /**
- * Finished flag.
- */
- protected boolean finished;
-
-
- /**
- * The buffer used for header composition.
- */
- protected byte[] headerBuffer;
-
-
- /**
- * Position in the buffer.
- */
- protected int pos;
-
-
- /**
- * Filter library.
- * Note: Filter[0] is always the "chunked" filter.
- */
- protected OutputFilter[] filterLibrary;
-
-
- /**
- * Active filter (which is actually the top of the pipeline).
- */
- protected OutputFilter[] activeFilters;
-
-
- /**
- * Index of the last active filter.
- */
- protected int lastActiveFilter;
-
- /**
- * Underlying output buffer.
- */
- protected OutputBuffer outputStreamOutputBuffer;
-
- /**
- * Bytes written to client for the current request
- */
- protected long byteCount = 0;
-
- /**
- * Socket buffering.
- */
- protected int socketBuffer = -1;
-
- /**
- * For "non-blocking" writes use an external set of buffers. Although the
- * API only allows one non-blocking write at a time, due to buffering and
- * the possible need to write HTTP headers, there may be more than one write
- * to the OutputBuffer.
- */
- protected final LinkedBlockingDeque<ByteBufferHolder> bufferedWrites =
- new LinkedBlockingDeque<>();
-
- /**
- * The max size of the buffered write buffer
- */
- protected int bufferedWriteSize = 64*1024; //64k default write buffer
-
-
- protected AbstractOutputBuffer(Response response, int headerBufferSize) {
-
- this.response = response;
-
- headerBuffer = new byte[headerBufferSize];
-
- filterLibrary = new OutputFilter[0];
- activeFilters = new OutputFilter[0];
- lastActiveFilter = -1;
-
- committed = false;
- finished = false;
-
- // Cause loading of HttpMessages
- HttpMessages.getInstance(response.getLocale()).getMessage(200);
- }
-
-
- // -------------------------------------------------------------- Variables
-
- /**
- * The string manager for this package.
- */
- protected static final StringManager sm =
- StringManager.getManager(Constants.Package);
-
- /**
- * Logger.
- */
- private static final org.apache.juli.logging.Log log
- = org.apache.juli.logging.LogFactory.getLog(AbstractOutputBuffer.class);
-
- // ------------------------------------------------------------- Properties
-
-
- /**
- * Add an output filter to the filter library.
- */
- public void addFilter(OutputFilter filter) {
-
- OutputFilter[] newFilterLibrary =
- new OutputFilter[filterLibrary.length + 1];
- for (int i = 0; i < filterLibrary.length; i++) {
- newFilterLibrary[i] = filterLibrary[i];
- }
- newFilterLibrary[filterLibrary.length] = filter;
- filterLibrary = newFilterLibrary;
-
- activeFilters = new OutputFilter[filterLibrary.length];
-
- }
-
-
- /**
- * Get filters.
- */
- public OutputFilter[] getFilters() {
-
- return filterLibrary;
-
- }
-
-
- /**
- * Add an output filter to the filter library.
- */
- public void addActiveFilter(OutputFilter filter) {
-
- if (lastActiveFilter == -1) {
- filter.setBuffer(outputStreamOutputBuffer);
- } else {
- for (int i = 0; i <= lastActiveFilter; i++) {
- if (activeFilters[i] == filter)
- return;
- }
- filter.setBuffer(activeFilters[lastActiveFilter]);
- }
-
- activeFilters[++lastActiveFilter] = filter;
-
- filter.setResponse(response);
-
- }
-
-
- /**
- * Set the socket buffer flag.
- */
- public void setSocketBuffer(int socketBuffer) {
- this.socketBuffer = socketBuffer;
- }
-
-
- /**
- * Get the socket buffer flag.
- */
- public int getSocketBuffer() {
- return socketBuffer;
- }
-
-
- public void setBufferedWriteSize(int bufferedWriteSize) {
- this.bufferedWriteSize = bufferedWriteSize;
- }
-
-
- public int getBufferedWriteSize() {
- return bufferedWriteSize;
- }
-
-
- // --------------------------------------------------- OutputBuffer Methods
-
- /**
- * Write the contents of a byte chunk.
- *
- * @param chunk byte chunk
- * @return number of bytes written
- * @throws IOException an underlying I/O error occurred
- */
- @Override
- public int doWrite(ByteChunk chunk, Response res)
- throws IOException {
-
- if (!committed) {
-
- // Send the connector a request for commit. The connector should
- // then validate the headers, send them (using sendHeaders) and
- // set the filters accordingly.
- response.action(ActionCode.COMMIT, null);
-
- }
-
- if (lastActiveFilter == -1)
- return outputStreamOutputBuffer.doWrite(chunk, res);
- else
- return activeFilters[lastActiveFilter].doWrite(chunk, res);
-
- }
-
-
- @Override
- public long getBytesWritten() {
- if (lastActiveFilter == -1) {
- return outputStreamOutputBuffer.getBytesWritten();
- } else {
- return activeFilters[lastActiveFilter].getBytesWritten();
- }
- }
-
-
- // --------------------------------------------------------- Public Methods
-
-
- /**
- * Flush the response.
- *
- * @throws IOException an underlying I/O error occurred
- */
- public void flush()
- throws IOException {
-
- if (!committed) {
-
- // Send the connector a request for commit. The connector should
- // then validate the headers, send them (using sendHeader) and
- // set the filters accordingly.
- response.action(ActionCode.COMMIT, null);
-
- }
-
- // go through the filters and if there is gzip filter
- // invoke it to flush
- for (int i = 0; i <= lastActiveFilter; i++) {
- if (activeFilters[i] instanceof GzipOutputFilter) {
- if (log.isDebugEnabled()) {
- log.debug("Flushing the gzip filter at position " + i +
- " of the filter chain...");
- }
- ((GzipOutputFilter) activeFilters[i]).flush();
- break;
- }
- }
-
- // Flush the current buffer(s)
- flushBuffer(isBlocking());
- }
-
-
- /**
- * Reset current response.
- *
- * @throws IllegalStateException if the response has already been committed
- */
- public void reset() {
-
- if (committed) {
- throw new IllegalStateException(sm.getString("iob.illegalreset"));
- }
-
- // These will need to be reset if the reset was triggered by the error
- // handling if the headers were too large
- pos = 0;
- byteCount = 0;
- }
-
- /**
- * Recycle the output buffer. This should be called when closing the
- * connection.
- */
- public void recycle() {
- // Sub-classes may wish to do more than this.
- nextRequest();
- bufferedWrites.clear();
- }
-
- /**
- * End processing of current HTTP request.
- * Note: All bytes of the current request should have been already
- * consumed. This method only resets all the pointers so that we are ready
- * to parse the next HTTP request.
- */
- public void nextRequest() {
- // Recycle filters
- for (int i = 0; i <= lastActiveFilter; i++) {
- activeFilters[i].recycle();
- }
- // Recycle response object
- response.recycle();
- // Reset pointers
- pos = 0;
- lastActiveFilter = -1;
- committed = false;
- finished = false;
- byteCount = 0;
- }
-
-
- /**
- * End request.
- *
- * @throws IOException an underlying I/O error occurred
- */
- public void endRequest() throws IOException {
-
- if (!committed) {
- // Send the connector a request for commit. The connector should
- // then validate the headers, send them (using sendHeader) and
- // set the filters accordingly.
- response.action(ActionCode.COMMIT, null);
- }
-
- if (finished)
- return;
-
- if (lastActiveFilter != -1)
- activeFilters[lastActiveFilter].end();
-
- flushBuffer(true);
-
- finished = true;
- }
-
-
- public abstract void init(SocketWrapper<S> socketWrapper,
- AbstractEndpoint<S> endpoint) throws IOException;
-
- public abstract void sendAck() throws IOException;
-
- protected abstract void commit() throws IOException;
-
-
- /**
- * Send the response status line.
- */
- public void sendStatus() {
-
- // Write protocol name
- write(Constants.HTTP_11_BYTES);
- headerBuffer[pos++] = Constants.SP;
-
- // Write status code
- int status = response.getStatus();
- switch (status) {
- case 200:
- write(Constants._200_BYTES);
- break;
- case 400:
- write(Constants._400_BYTES);
- break;
- case 404:
- write(Constants._404_BYTES);
- break;
- default:
- write(status);
- }
-
- headerBuffer[pos++] = Constants.SP;
-
- // Write message
- String message = null;
- if (org.apache.coyote.Constants.USE_CUSTOM_STATUS_MSG_IN_HEADER &&
- HttpMessages.isSafeInHttpHeader(response.getMessage())) {
- message = response.getMessage();
- }
- if (message == null) {
- write(HttpMessages.getInstance(
- response.getLocale()).getMessage(status));
- } else {
- write(message);
- }
-
- // End the response status line
- if (org.apache.coyote.Constants.IS_SECURITY_ENABLED){
- AccessController.doPrivileged(
- new PrivilegedAction<Void>(){
- @Override
- public Void run(){
- headerBuffer[pos++] = Constants.CR;
- headerBuffer[pos++] = Constants.LF;
- return null;
- }
- }
- );
- } else {
- headerBuffer[pos++] = Constants.CR;
- headerBuffer[pos++] = Constants.LF;
- }
-
- }
-
-
- /**
- * Send a header.
- *
- * @param name Header name
- * @param value Header value
- */
- public void sendHeader(MessageBytes name, MessageBytes value) {
-
- write(name);
- headerBuffer[pos++] = Constants.COLON;
- headerBuffer[pos++] = Constants.SP;
- write(value);
- headerBuffer[pos++] = Constants.CR;
- headerBuffer[pos++] = Constants.LF;
-
- }
-
-
- /**
- * End the header block.
- */
- public void endHeaders() {
-
- headerBuffer[pos++] = Constants.CR;
- headerBuffer[pos++] = Constants.LF;
-
- }
-
-
- /**
- * This method will write the contents of the specified message bytes
- * buffer to the output stream, without filtering. This method is meant to
- * be used to write the response header.
- *
- * @param mb data to be written
- */
- protected void write(MessageBytes mb) {
-
- if (mb.getType() != MessageBytes.T_BYTES) {
- mb.toBytes();
- ByteChunk bc = mb.getByteChunk();
- // Need to filter out CTLs excluding TAB. ISO-8859-1 and UTF-8
- // values will be OK. Strings using other encodings may be
- // corrupted.
- byte[] buffer = bc.getBuffer();
- for (int i = bc.getOffset(); i < bc.getLength(); i++) {
- // byte values are signed i.e. -128 to 127
- // The values are used unsigned. 0 to 31 are CTLs so they are
- // filtered (apart from TAB which is 9). 127 is a control (DEL).
- // The values 128 to 255 are all OK. Converting those to signed
- // gives -128 to -1.
- if ((buffer[i] > -1 && buffer[i] <= 31 && buffer[i] != 9) ||
- buffer[i] == 127) {
- buffer[i] = ' ';
- }
- }
- }
- write(mb.getByteChunk());
- }
-
-
- /**
- * This method will write the contents of the specified message bytes
- * buffer to the output stream, without filtering. This method is meant to
- * be used to write the response header.
- *
- * @param bc data to be written
- */
- protected void write(ByteChunk bc) {
-
- // Writing the byte chunk to the output buffer
- int length = bc.getLength();
- checkLengthBeforeWrite(length);
- System.arraycopy(bc.getBytes(), bc.getStart(), headerBuffer, pos, length);
- pos = pos + length;
-
- }
-
-
- /**
- * This method will write the contents of the specified byte
- * buffer to the output stream, without filtering. This method is meant to
- * be used to write the response header.
- *
- * @param b data to be written
- */
- public void write(byte[] b) {
- checkLengthBeforeWrite(b.length);
-
- // Writing the byte chunk to the output buffer
- System.arraycopy(b, 0, headerBuffer, pos, b.length);
- pos = pos + b.length;
-
- }
-
-
- /**
- * This method will write the contents of the specified String to the
- * output stream, without filtering. This method is meant to be used to
- * write the response header.
- *
- * @param s data to be written
- */
- protected void write(String s) {
-
- if (s == null)
- return;
-
- // From the Tomcat 3.3 HTTP/1.0 connector
- int len = s.length();
- checkLengthBeforeWrite(len);
- for (int i = 0; i < len; i++) {
- char c = s.charAt (i);
- // Note: This is clearly incorrect for many strings,
- // but is the only consistent approach within the current
- // servlet framework. It must suffice until servlet output
- // streams properly encode their output.
- if (((c <= 31) && (c != 9)) || c == 127 || c > 255) {
- c = ' ';
- }
- headerBuffer[pos++] = (byte) c;
- }
-
- }
-
-
- /**
- * This method will print the specified integer to the output stream,
- * without filtering. This method is meant to be used to write the
- * response header.
- *
- * @param i data to be written
- */
- protected void write(int i) {
-
- write(String.valueOf(i));
-
- }
-
-
- /**
- * Checks to see if there is enough space in the buffer to write the
- * requested number of bytes.
- */
- private void checkLengthBeforeWrite(int length) {
- // "+ 4": BZ 57509. Reserve space for CR/LF/COLON/SP characters that
- // are put directly into the buffer following this write operation.
- if (pos + length + 4 > headerBuffer.length) {
- throw new HeadersTooLargeException(
- sm.getString("iob.responseheadertoolarge.error"));
- }
- }
-
-
- //------------------------------------------------------ Non-blocking writes
-
- protected abstract boolean hasMoreDataToFlush();
- protected abstract void registerWriteInterest() throws IOException;
-
-
- /**
- * Writes any remaining buffered data.
- *
- * @param block Should this method block until the buffer is empty
- * @return <code>true</code> if data remains in the buffer (which can only
- * happen in non-blocking mode) else <code>false</code>.
- * @throws IOException
- */
- protected abstract boolean flushBuffer(boolean block) throws IOException;
-
-
- /**
- * Is standard Servlet blocking IO being used for output?
- */
- protected final boolean isBlocking() {
- return response.getWriteListener() == null;
- }
-
-
- protected final boolean isReady() throws IOException {
- boolean result = !hasDataToWrite();
- if (!result) {
- registerWriteInterest();
- }
- return result;
- }
-
-
- public boolean hasDataToWrite() {
- return hasMoreDataToFlush() || hasBufferedData();
- }
-
-
- protected boolean hasBufferedData() {
- boolean result = false;
- if (bufferedWrites!=null) {
- Iterator<ByteBufferHolder> iter = bufferedWrites.iterator();
- while (!result && iter.hasNext()) {
- result = iter.next().hasData();
- }
- }
- return result;
- }
-}
diff --git a/java/org/apache/coyote/http11/Constants.java b/java/org/apache/coyote/http11/Constants.java
index 15e26ea..cd8aae0 100644
--- a/java/org/apache/coyote/http11/Constants.java
+++ b/java/org/apache/coyote/http11/Constants.java
@@ -14,12 +14,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
package org.apache.coyote.http11;
import org.apache.tomcat.util.buf.ByteChunk;
-
/**
* Constants.
*
@@ -27,18 +25,7 @@ import org.apache.tomcat.util.buf.ByteChunk;
*/
public final class Constants {
-
- // -------------------------------------------------------------- Constants
-
-
- /**
- * Package name.
- */
- public static final String Package = "org.apache.coyote.http11";
-
- public static final int DEFAULT_CONNECTION_LINGER = -1;
public static final int DEFAULT_CONNECTION_TIMEOUT = 60000;
- public static final boolean DEFAULT_TCP_NO_DELAY = true;
/**
@@ -48,13 +35,6 @@ public final class Constants {
/**
- * Server string.
- */
- public static final byte[] SERVER_BYTES =
- ByteChunk.convertToBytes("Server: Apache-Coyote/1.1" + CRLF);
-
-
- /**
* CR.
*/
public static final byte CR = (byte) '\r';
@@ -83,13 +63,13 @@ public final class Constants {
*/
public static final byte COLON = (byte) ':';
+
/**
* SEMI_COLON.
*/
public static final byte SEMI_COLON = (byte) ';';
-
/**
* 'A'.
*/
@@ -123,21 +103,16 @@ public final class Constants {
/* Various constant "strings" */
public static final String CONNECTION = "Connection";
public static final String CLOSE = "close";
- public static final byte[] CLOSE_BYTES =
- ByteChunk.convertToBytes(CLOSE);
+ public static final byte[] CLOSE_BYTES = ByteChunk.convertToBytes(CLOSE);
public static final String KEEPALIVE = "keep-alive";
- public static final byte[] KEEPALIVE_BYTES =
- ByteChunk.convertToBytes(KEEPALIVE);
+ public static final byte[] KEEPALIVE_BYTES = ByteChunk.convertToBytes(KEEPALIVE);
public static final String CHUNKED = "chunked";
public static final byte[] ACK_BYTES =
- ByteChunk.convertToBytes("HTTP/1.1 100 Continue" + CRLF + CRLF);
+ ByteChunk.convertToBytes("HTTP/1.1 100 Continue" + CRLF + CRLF);
public static final String TRANSFERENCODING = "Transfer-Encoding";
- public static final byte[] _200_BYTES =
- ByteChunk.convertToBytes("200");
- public static final byte[] _400_BYTES =
- ByteChunk.convertToBytes("400");
- public static final byte[] _404_BYTES =
- ByteChunk.convertToBytes("404");
+ public static final byte[] _200_BYTES = ByteChunk.convertToBytes("200");
+ public static final byte[] _400_BYTES = ByteChunk.convertToBytes("400");
+ public static final byte[] _404_BYTES = ByteChunk.convertToBytes("404");
/**
@@ -180,24 +155,5 @@ public final class Constants {
* HTTP/1.1.
*/
public static final String HTTP_11 = "HTTP/1.1";
- public static final byte[] HTTP_11_BYTES =
- ByteChunk.convertToBytes(HTTP_11);
-
-
- /**
- * GET.
- */
- public static final String GET = "GET";
-
-
- /**
- * HEAD.
- */
- public static final String HEAD = "HEAD";
-
-
- /**
- * POST.
- */
- public static final String POST = "POST";
+ public static final byte[] HTTP_11_BYTES = ByteChunk.convertToBytes(HTTP_11);
}
diff --git a/java/org/apache/coyote/http11/Http11AprProcessor.java b/java/org/apache/coyote/http11/Http11AprProcessor.java
deleted file mode 100644
index 2831f2d..0000000
--- a/java/org/apache/coyote/http11/Http11AprProcessor.java
+++ /dev/null
@@ -1,520 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.coyote.http11;
-
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.InterruptedIOException;
-import java.security.cert.CertificateFactory;
-import java.security.cert.X509Certificate;
-import java.util.Set;
-
-import org.apache.coyote.ActionCode;
-import org.apache.coyote.ErrorState;
-import org.apache.coyote.RequestInfo;
-import org.apache.coyote.http11.filters.BufferedInputFilter;
-import org.apache.juli.logging.Log;
-import org.apache.juli.logging.LogFactory;
-import org.apache.tomcat.jni.Address;
-import org.apache.tomcat.jni.SSL;
-import org.apache.tomcat.jni.SSLSocket;
-import org.apache.tomcat.jni.Sockaddr;
-import org.apache.tomcat.jni.Socket;
-import org.apache.tomcat.util.ExceptionUtils;
-import org.apache.tomcat.util.net.AbstractEndpoint.Handler.SocketState;
-import org.apache.tomcat.util.net.AprEndpoint;
-import org.apache.tomcat.util.net.SSLSupport;
-import org.apache.tomcat.util.net.SocketStatus;
-import org.apache.tomcat.util.net.SocketWrapper;
-
-
-/**
- * Processes HTTP requests.
- *
- * @author Remy Maucherat
- */
-public class Http11AprProcessor extends AbstractHttp11Processor<Long> {
-
-
- private static final Log log = LogFactory.getLog(Http11AprProcessor.class);
- @Override
- protected Log getLog() {
- return log;
- }
-
- // ----------------------------------------------------------- Constructors
-
-
- public Http11AprProcessor(int headerBufferSize, AprEndpoint endpoint, int maxTrailerSize,
- Set<String> allowedTrailerHeaders, int maxExtensionSize, int maxSwallowSize) {
-
- super(endpoint);
-
- inputBuffer = new InternalAprInputBuffer(request, headerBufferSize);
- request.setInputBuffer(inputBuffer);
-
- outputBuffer = new InternalAprOutputBuffer(response, headerBufferSize);
- response.setOutputBuffer(outputBuffer);
-
- initializeFilters(maxTrailerSize, allowedTrailerHeaders, maxExtensionSize, maxSwallowSize);
- }
-
-
- // ----------------------------------------------------- Instance Variables
-
- /**
- * Sendfile data.
- */
- protected AprEndpoint.SendfileData sendfileData = null;
-
-
- /**
- * When client certificate information is presented in a form other than
- * instances of {@link java.security.cert.X509Certificate} it needs to be
- * converted before it can be used and this property controls which JSSE
- * provider is used to perform the conversion. For example it is used with
- * the AJP connectors, the HTTP APR connector and with the
- * {@link org.apache.catalina.valves.SSLValve}. If not specified, the
- * default provider will be used.
- */
- protected String clientCertProvider = null;
- public String getClientCertProvider() { return clientCertProvider; }
- public void setClientCertProvider(String s) { this.clientCertProvider = s; }
-
-
- // --------------------------------------------------------- Public Methods
-
-
- /**
- * Process pipelined HTTP requests using the specified input and output
- * streams.
- *
- * @throws IOException error during an I/O operation
- */
- @Override
- public SocketState event(SocketStatus status)
- throws IOException {
-
- RequestInfo rp = request.getRequestProcessor();
-
- try {
- rp.setStage(org.apache.coyote.Constants.STAGE_SERVICE);
- if (!getAdapter().event(request, response, status)) {
- setErrorState(ErrorState.CLOSE_NOW, null);
- }
- } catch (InterruptedIOException e) {
- setErrorState(ErrorState.CLOSE_NOW, e);
- } catch (Throwable t) {
- ExceptionUtils.handleThrowable(t);
- // 500 - Internal Server Error
- response.setStatus(500);
- setErrorState(ErrorState.CLOSE_NOW, t);
- getAdapter().log(request, response, 0);
- log.error(sm.getString("http11processor.request.process"), t);
- }
-
- rp.setStage(org.apache.coyote.Constants.STAGE_ENDED);
-
- if (getErrorState().isError() || status==SocketStatus.STOP) {
- return SocketState.CLOSED;
- } else if (!comet) {
- inputBuffer.nextRequest();
- outputBuffer.nextRequest();
- return SocketState.OPEN;
- } else {
- return SocketState.LONG;
- }
- }
-
- @Override
- protected boolean disableKeepAlive() {
- return false;
- }
-
-
- @Override
- protected void setRequestLineReadTimeout() throws IOException {
- // Timeouts while in the poller are handled entirely by the poller
- // Only need to be concerned with socket timeouts
-
- // APR uses simulated blocking so if some request line data is present
- // then it must all be presented (with the normal socket timeout).
-
- // When entering the processing loop for the first time there will
- // always be some data to read so the keep-alive timeout is not required
-
- // For the second and subsequent executions of the processing loop, if
- // there is no request line data present then no further data will be
- // read from the socket. If there is request line data present then it
- // must all be presented (with the normal socket timeout)
-
- // When the socket is created it is given the correct timeout.
- // sendfile may change the timeout but will restore it
- // This processor may change the timeout for uploads but will restore it
-
- // NO-OP
- }
-
-
- @Override
- protected boolean handleIncompleteRequestLineRead() {
- // This means that no data is available right now
- // (long keepalive), so that the processor should be recycled
- // and the method should return true
- openSocket = true;
- return true;
- }
-
-
- @Override
- protected void setSocketTimeout(int timeout) {
- Socket.timeoutSet(socketWrapper.getSocket().longValue(), timeout * 1000);
- }
-
-
- @Override
- protected void setCometTimeouts(SocketWrapper<Long> socketWrapper) {
- // NO-OP for APR/native
- }
-
-
- @Override
- protected boolean breakKeepAliveLoop(SocketWrapper<Long> socketWrapper) {
- openSocket = keepAlive;
- // Do sendfile as needed: add socket to sendfile and end
- if (sendfileData != null && !getErrorState().isError()) {
- sendfileData.socket = socketWrapper.getSocket().longValue();
- sendfileData.keepAlive = keepAlive;
- if (!((AprEndpoint)endpoint).getSendfile().add(sendfileData)) {
- // Didn't send all of the data to sendfile.
- if (sendfileData.socket == 0) {
- // The socket is no longer set. Something went wrong.
- // Close the connection. Too late to set status code.
- if (log.isDebugEnabled()) {
- log.debug(sm.getString(
- "http11processor.sendfile.error"));
- }
- setErrorState(ErrorState.CLOSE_NOW, null);
- } else {
- // The sendfile Poller will add the socket to the main
- // Poller once sendfile processing is complete
- sendfileInProgress = true;
- }
- return true;
- }
- }
- return false;
- }
-
-
- @Override
- protected void registerForEvent(boolean read, boolean write) {
- socketWrapper.registerforEvent(-1, read, write);
- }
-
-
- @Override
- protected void resetTimeouts() {
- // NO-OP for APR
- }
-
-
- @Override
- public void recycleInternal() {
- socketWrapper = null;
- sendfileData = null;
- }
-
-
- @Override
- public void setSslSupport(SSLSupport sslSupport) {
- // NOOP for APR
- }
-
- // ----------------------------------------------------- ActionHook Methods
-
-
- /**
- * Send an action to the connector.
- *
- * @param actionCode Type of the action
- * @param param Action parameter
- */
- @Override
- @SuppressWarnings("incomplete-switch") // Other cases are handled by action()
- public void actionInternal(ActionCode actionCode, Object param) {
-
- long socketRef = socketWrapper.getSocket().longValue();
-
- switch (actionCode) {
- case REQ_HOST_ADDR_ATTRIBUTE: {
- if (socketRef == 0) {
- request.remoteAddr().recycle();
- } else {
- if (socketWrapper.getRemoteAddr() == null) {
- try {
- long sa = Address.get(Socket.APR_REMOTE, socketRef);
- socketWrapper.setRemoteAddr(Address.getip(sa));
- } catch (Exception e) {
- log.warn(sm.getString("http11processor.socket.info"), e);
- }
- }
- request.remoteAddr().setString(socketWrapper.getRemoteAddr());
- }
- break;
- }
- case REQ_LOCAL_NAME_ATTRIBUTE: {
- if (socketRef == 0) {
- request.localName().recycle();
- } else {
- if (socketWrapper.getLocalName() == null) {
- try {
- long sa = Address.get(Socket.APR_LOCAL, socketRef);
- socketWrapper.setLocalName(Address.getnameinfo(sa, 0));
- } catch (Exception e) {
- log.warn(sm.getString("http11processor.socket.info"), e);
- }
- }
- request.localName().setString(socketWrapper.getLocalName());
- }
- break;
- }
- case REQ_HOST_ATTRIBUTE: {
- if (socketRef == 0) {
- request.remoteHost().recycle();
- } else {
- if (socketWrapper.getRemoteHost() == null) {
- try {
- long sa = Address.get(Socket.APR_REMOTE, socketRef);
- socketWrapper.setRemoteHost(Address.getnameinfo(sa, 0));
- if (socketWrapper.getRemoteHost() == null) {
- if (socketWrapper.getRemoteAddr() == null) {
- socketWrapper.setRemoteAddr(Address.getip(sa));
- }
- if (socketWrapper.getRemoteAddr() != null) {
- socketWrapper.setRemoteHost(socketWrapper.getRemoteAddr());
- }
- }
- } catch (Exception e) {
- log.warn(sm.getString("http11processor.socket.info"), e);
- }
- } else {
- request.remoteHost().setString(socketWrapper.getRemoteHost());
- }
- }
- break;
- }
- case REQ_LOCAL_ADDR_ATTRIBUTE: {
- if (socketRef == 0) {
- request.localAddr().recycle();
- } else {
- if (socketWrapper.getLocalAddr() == null) {
- try {
- long sa = Address.get(Socket.APR_LOCAL, socketRef);
- socketWrapper.setLocalAddr(Address.getip(sa));
- } catch (Exception e) {
- log.warn(sm.getString("http11processor.socket.info"), e);
- }
- }
- request.localAddr().setString(socketWrapper.getLocalAddr());
- }
- break;
- }
- case REQ_REMOTEPORT_ATTRIBUTE: {
- if (socketRef == 0) {
- request.setRemotePort(0);
- } else {
- if (socketWrapper.getRemotePort() == -1) {
- try {
- long sa = Address.get(Socket.APR_REMOTE, socketRef);
- Sockaddr addr = Address.getInfo(sa);
- socketWrapper.setRemotePort(addr.port);
- } catch (Exception e) {
- log.warn(sm.getString("http11processor.socket.info"), e);
- }
- }
- request.setRemotePort(socketWrapper.getRemotePort());
- }
- break;
- }
- case REQ_LOCALPORT_ATTRIBUTE: {
- if (socketRef == 0) {
- request.setLocalPort(0);
- } else {
- if (socketWrapper.getLocalPort() == -1) {
- try {
- long sa = Address.get(Socket.APR_LOCAL, socketRef);
- Sockaddr addr = Address.getInfo(sa);
- socketWrapper.setLocalPort(addr.port);
- } catch (Exception e) {
- log.warn(sm.getString("http11processor.socket.info"), e);
- }
- }
- request.setLocalPort(socketWrapper.getLocalPort());
- }
- break;
- }
- case REQ_SSL_ATTRIBUTE: {
- if (endpoint.isSSLEnabled() && (socketRef != 0)) {
- try {
- // Cipher suite
- Object sslO = SSLSocket.getInfoS(socketRef, SSL.SSL_INFO_CIPHER);
- if (sslO != null) {
- request.setAttribute(SSLSupport.CIPHER_SUITE_KEY, sslO);
- }
- // Get client certificate and the certificate chain if present
- // certLength == -1 indicates an error
- int certLength = SSLSocket.getInfoI(socketRef, SSL.SSL_INFO_CLIENT_CERT_CHAIN);
- byte[] clientCert = SSLSocket.getInfoB(socketRef, SSL.SSL_INFO_CLIENT_CERT);
- X509Certificate[] certs = null;
- if (clientCert != null && certLength > -1) {
- certs = new X509Certificate[certLength + 1];
- CertificateFactory cf;
- if (clientCertProvider == null) {
- cf = CertificateFactory.getInstance("X.509");
- } else {
- cf = CertificateFactory.getInstance("X.509",
- clientCertProvider);
- }
- certs[0] = (X509Certificate) cf.generateCertificate(new ByteArrayInputStream(clientCert));
- for (int i = 0; i < certLength; i++) {
- byte[] data = SSLSocket.getInfoB(socketRef, SSL.SSL_INFO_CLIENT_CERT_CHAIN + i);
- certs[i+1] = (X509Certificate) cf.generateCertificate(new ByteArrayInputStream(data));
- }
- }
- if (certs != null) {
- request.setAttribute(SSLSupport.CERTIFICATE_KEY, certs);
- }
- // User key size
- sslO = Integer.valueOf(SSLSocket.getInfoI(socketRef,
- SSL.SSL_INFO_CIPHER_USEKEYSIZE));
- request.setAttribute(SSLSupport.KEY_SIZE_KEY, sslO);
-
- // SSL session ID
- sslO = SSLSocket.getInfoS(socketRef, SSL.SSL_INFO_SESSION_ID);
- if (sslO != null) {
- request.setAttribute(SSLSupport.SESSION_ID_KEY, sslO);
- }
- sslO = SSLSocket.getInfoS(socketRef, SSL.SSL_INFO_PROTOCOL);
- if (sslO != null) {
- request.setAttribute
- (SSLSupport.PROTOCOL_VERSION_KEY, sslO);
- }
- //TODO provide a hook to enable the SSL session to be
- // invalidated. Set AprEndpoint.SESSION_MGR req attr
- } catch (Exception e) {
- log.warn(sm.getString("http11processor.socket.ssl"), e);
- }
- }
- break;
- }
- case REQ_SSL_CERTIFICATE: {
- if (endpoint.isSSLEnabled() && (socketRef != 0)) {
- // Consume and buffer the request body, so that it does not
- // interfere with the client's handshake messages
- InputFilter[] inputFilters = inputBuffer.getFilters();
- ((BufferedInputFilter) inputFilters[Constants.BUFFERED_FILTER]).setLimit(maxSavePostSize);
- inputBuffer.addActiveFilter(inputFilters[Constants.BUFFERED_FILTER]);
- try {
- // Configure connection to require a certificate
- SSLSocket.setVerify(socketRef, SSL.SSL_CVERIFY_REQUIRE,
- ((AprEndpoint)endpoint).getSSLVerifyDepth());
- // Renegotiate certificates
- if (SSLSocket.renegotiate(socketRef) == 0) {
- // Don't look for certs unless we know renegotiation worked.
- // Get client certificate and the certificate chain if present
- // certLength == -1 indicates an error
- int certLength = SSLSocket.getInfoI(socketRef,SSL.SSL_INFO_CLIENT_CERT_CHAIN);
- byte[] clientCert = SSLSocket.getInfoB(socketRef, SSL.SSL_INFO_CLIENT_CERT);
- X509Certificate[] certs = null;
- if (clientCert != null && certLength > -1) {
- certs = new X509Certificate[certLength + 1];
- CertificateFactory cf = CertificateFactory.getInstance("X.509");
- certs[0] = (X509Certificate) cf.generateCertificate(new ByteArrayInputStream(clientCert));
- for (int i = 0; i < certLength; i++) {
- byte[] data = SSLSocket.getInfoB(socketRef, SSL.SSL_INFO_CLIENT_CERT_CHAIN + i);
- certs[i+1] = (X509Certificate) cf.generateCertificate(new ByteArrayInputStream(data));
- }
- }
- if (certs != null) {
- request.setAttribute(SSLSupport.CERTIFICATE_KEY, certs);
- }
- }
- } catch (Exception e) {
- log.warn(sm.getString("http11processor.socket.ssl"), e);
- }
- }
- break;
- }
- case COMET_BEGIN: {
- comet = true;
- break;
- }
- case COMET_END: {
- comet = false;
- break;
- }
- case COMET_CLOSE: {
- ((AprEndpoint)endpoint).processSocket(this.socketWrapper,
- SocketStatus.OPEN_READ, true);
- break;
- }
- case COMET_SETTIMEOUT: {
- //no op
- break;
- }
- }
- }
-
-
- // ------------------------------------------------------ Protected Methods
-
-
- @Override
- protected void prepareRequestInternal() {
- sendfileData = null;
- }
-
- @Override
- protected boolean prepareSendfile(OutputFilter[] outputFilters) {
- String fileName = (String) request.getAttribute(
- org.apache.coyote.Constants.SENDFILE_FILENAME_ATTR);
- if (fileName != null) {
- // No entity body sent here
- outputBuffer.addActiveFilter(outputFilters[Constants.VOID_FILTER]);
- contentDelimitation = true;
- sendfileData = new AprEndpoint.SendfileData();
- sendfileData.fileName = fileName;
- sendfileData.start = ((Long) request.getAttribute(
- org.apache.coyote.Constants.SENDFILE_FILE_START_ATTR)).longValue();
- sendfileData.end = ((Long) request.getAttribute(
- org.apache.coyote.Constants.SENDFILE_FILE_END_ATTR)).longValue();
- return true;
- }
- return false;
- }
-
- @Override
- protected AbstractInputBuffer<Long> getInputBuffer() {
- return inputBuffer;
- }
-
- @Override
- protected AbstractOutputBuffer<Long> getOutputBuffer() {
- return outputBuffer;
- }
-}
diff --git a/java/org/apache/coyote/http11/Http11AprProtocol.java b/java/org/apache/coyote/http11/Http11AprProtocol.java
index 03950d4..fc45b17 100644
--- a/java/org/apache/coyote/http11/Http11AprProtocol.java
+++ b/java/org/apache/coyote/http11/Http11AprProtocol.java
@@ -16,21 +16,9 @@
*/
package org.apache.coyote.http11;
-import java.io.IOException;
-import java.nio.ByteBuffer;
-
-import org.apache.coyote.AbstractProtocol;
-import org.apache.coyote.Processor;
-import org.apache.coyote.UpgradeToken;
-import org.apache.coyote.http11.upgrade.AprProcessor;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
-import org.apache.tomcat.util.net.AbstractEndpoint;
import org.apache.tomcat.util.net.AprEndpoint;
-import org.apache.tomcat.util.net.AprEndpoint.Handler;
-import org.apache.tomcat.util.net.AprEndpoint.Poller;
-import org.apache.tomcat.util.net.SocketStatus;
-import org.apache.tomcat.util.net.SocketWrapper;
/**
@@ -45,15 +33,13 @@ public class Http11AprProtocol extends AbstractHttp11Protocol<Long> {
private static final Log log = LogFactory.getLog(Http11AprProtocol.class);
- @Override
- protected Log getLog() { return log; }
+ public Http11AprProtocol() {
+ super(new AprEndpoint());
+ }
@Override
- protected AbstractEndpoint.Handler getHandler() {
- return cHandler;
- }
-
+ protected Log getLog() { return log; }
@Override
public boolean isAprRequired() {
@@ -62,252 +48,24 @@ public class Http11AprProtocol extends AbstractHttp11Protocol<Long> {
return true;
}
+ public int getPollTime() { return ((AprEndpoint)getEndpoint()).getPollTime(); }
+ public void setPollTime(int pollTime) { ((AprEndpoint)getEndpoint()).setPollTime(pollTime); }
- public Http11AprProtocol() {
- endpoint = new AprEndpoint();
- cHandler = new Http11ConnectionHandler(this);
- ((AprEndpoint) endpoint).setHandler(cHandler);
- setSoLinger(Constants.DEFAULT_CONNECTION_LINGER);
- setSoTimeout(Constants.DEFAULT_CONNECTION_TIMEOUT);
- setTcpNoDelay(Constants.DEFAULT_TCP_NO_DELAY);
- }
-
- private final Http11ConnectionHandler cHandler;
-
- public boolean getUseSendfile() { return endpoint.getUseSendfile(); }
- public void setUseSendfile(boolean useSendfile) { ((AprEndpoint)endpoint).setUseSendfile(useSendfile); }
-
- public int getPollTime() { return ((AprEndpoint)endpoint).getPollTime(); }
- public void setPollTime(int pollTime) { ((AprEndpoint)endpoint).setPollTime(pollTime); }
-
- public void setPollerSize(int pollerSize) { endpoint.setMaxConnections(pollerSize); }
- public int getPollerSize() { return endpoint.getMaxConnections(); }
-
- public int getSendfileSize() { return ((AprEndpoint)endpoint).getSendfileSize(); }
- public void setSendfileSize(int sendfileSize) { ((AprEndpoint)endpoint).setSendfileSize(sendfileSize); }
-
- public void setSendfileThreadCount(int sendfileThreadCount) { ((AprEndpoint)endpoint).setSendfileThreadCount(sendfileThreadCount); }
- public int getSendfileThreadCount() { return ((AprEndpoint)endpoint).getSendfileThreadCount(); }
-
- public boolean getDeferAccept() { return ((AprEndpoint)endpoint).getDeferAccept(); }
- public void setDeferAccept(boolean deferAccept) { ((AprEndpoint)endpoint).setDeferAccept(deferAccept); }
-
- // -------------------- SSL related properties --------------------
-
- /**
- * SSL protocol.
- */
- public String getSSLProtocol() { return ((AprEndpoint)endpoint).getSSLProtocol(); }
- public void setSSLProtocol(String SSLProtocol) { ((AprEndpoint)endpoint).setSSLProtocol(SSLProtocol); }
-
-
- /**
- * SSL password (if a cert is encrypted, and no password has been provided, a callback
- * will ask for a password).
- */
- public String getSSLPassword() { return ((AprEndpoint)endpoint).getSSLPassword(); }
- public void setSSLPassword(String SSLPassword) { ((AprEndpoint)endpoint).setSSLPassword(SSLPassword); }
-
-
- /**
- * SSL cipher suite.
- */
- public String getSSLCipherSuite() { return ((AprEndpoint)endpoint).getSSLCipherSuite(); }
- public void setSSLCipherSuite(String SSLCipherSuite) { ((AprEndpoint)endpoint).setSSLCipherSuite(SSLCipherSuite); }
- public String[] getCiphersUsed() { return endpoint.getCiphersUsed();}
-
- /**
- * SSL honor cipher order.
- *
- * Set to <code>true</code> to enforce the <i>server's</i> cipher order
- * instead of the default which is to allow the client to choose a
- * preferred cipher.
- */
- public boolean getSSLHonorCipherOrder() { return ((AprEndpoint)endpoint).getSSLHonorCipherOrder(); }
- public void setSSLHonorCipherOrder(boolean SSLHonorCipherOrder) { ((AprEndpoint)endpoint).setSSLHonorCipherOrder(SSLHonorCipherOrder); }
-
-
- /**
- * SSL certificate file.
- */
- public String getSSLCertificateFile() { return ((AprEndpoint)endpoint).getSSLCertificateFile(); }
- public void setSSLCertificateFile(String SSLCertificateFile) { ((AprEndpoint)endpoint).setSSLCertificateFile(SSLCertificateFile); }
-
-
- /**
- * SSL certificate key file.
- */
- public String getSSLCertificateKeyFile() { return ((AprEndpoint)endpoint).getSSLCertificateKeyFile(); }
- public void setSSLCertificateKeyFile(String SSLCertificateKeyFile) { ((AprEndpoint)endpoint).setSSLCertificateKeyFile(SSLCertificateKeyFile); }
-
-
- /**
- * SSL certificate chain file.
- */
- public String getSSLCertificateChainFile() { return ((AprEndpoint)endpoint).getSSLCertificateChainFile(); }
- public void setSSLCertificateChainFile(String SSLCertificateChainFile) { ((AprEndpoint)endpoint).setSSLCertificateChainFile(SSLCertificateChainFile); }
-
-
- /**
- * SSL CA certificate path.
- */
- public String getSSLCACertificatePath() { return ((AprEndpoint)endpoint).getSSLCACertificatePath(); }
- public void setSSLCACertificatePath(String SSLCACertificatePath) { ((AprEndpoint)endpoint).setSSLCACertificatePath(SSLCACertificatePath); }
+ public int getSendfileSize() { return ((AprEndpoint)getEndpoint()).getSendfileSize(); }
+ public void setSendfileSize(int sendfileSize) { ((AprEndpoint)getEndpoint()).setSendfileSize(sendfileSize); }
-
- /**
- * SSL CA certificate file.
- */
- public String getSSLCACertificateFile() { return ((AprEndpoint)endpoint).getSSLCACertificateFile(); }
- public void setSSLCACertificateFile(String SSLCACertificateFile) { ((AprEndpoint)endpoint).setSSLCACertificateFile(SSLCACertificateFile); }
-
-
- /**
- * SSL CA revocation path.
- */
- public String getSSLCARevocationPath() { return ((AprEndpoint)endpoint).getSSLCARevocationPath(); }
- public void setSSLCARevocationPath(String SSLCARevocationPath) { ((AprEndpoint)endpoint).setSSLCARevocationPath(SSLCARevocationPath); }
-
-
- /**
- * SSL CA revocation file.
- */
- public String getSSLCARevocationFile() { return ((AprEndpoint)endpoint).getSSLCARevocationFile(); }
- public void setSSLCARevocationFile(String SSLCARevocationFile) { ((AprEndpoint)endpoint).setSSLCARevocationFile(SSLCARevocationFile); }
-
-
- /**
- * SSL verify client.
- */
- public String getSSLVerifyClient() { return ((AprEndpoint)endpoint).getSSLVerifyClient(); }
- public void setSSLVerifyClient(String SSLVerifyClient) { ((AprEndpoint)endpoint).setSSLVerifyClient(SSLVerifyClient); }
-
-
- /**
- * SSL verify depth.
- */
- public int getSSLVerifyDepth() { return ((AprEndpoint)endpoint).getSSLVerifyDepth(); }
- public void setSSLVerifyDepth(int SSLVerifyDepth) { ((AprEndpoint)endpoint).setSSLVerifyDepth(SSLVerifyDepth); }
-
- /**
- * Disable SSL compression.
- */
- public boolean getSSLDisableCompression() { return ((AprEndpoint)endpoint).getSSLDisableCompression(); }
- public void setSSLDisableCompression(boolean disable) { ((AprEndpoint)endpoint).setSSLDisableCompression(disable); }
-
- /**
- * Disable TLS Session Tickets (RFC 4507).
- */
- public boolean getSSLDisableSessionTickets() { return ((AprEndpoint)endpoint).getSSLDisableSessionTickets(); }
- public void setSSLDisableSessionTickets(boolean enable) { ((AprEndpoint)endpoint).setSSLDisableSessionTickets(enable); }
+ public boolean getDeferAccept() { return ((AprEndpoint)getEndpoint()).getDeferAccept(); }
+ public void setDeferAccept(boolean deferAccept) { ((AprEndpoint)getEndpoint()).setDeferAccept(deferAccept); }
// ----------------------------------------------------- JMX related methods
@Override
protected String getNamePrefix() {
- return ("http-apr");
- }
-
-
- // -------------------- Connection handler --------------------
-
- protected static class Http11ConnectionHandler
- extends AbstractConnectionHandler<Long,Http11AprProcessor> implements Handler {
-
- protected Http11AprProtocol proto;
-
- Http11ConnectionHandler(Http11AprProtocol proto) {
- this.proto = proto;
- }
-
- @Override
- protected AbstractProtocol<Long> getProtocol() {
- return proto;
- }
-
- @Override
- protected Log getLog() {
- return log;
- }
-
- /**
- * Expected to be used by the handler once the processor is no longer
- * required.
- *
- * @param socket
- * @param processor
- * @param isSocketClosing Not used in HTTP
- * @param addToPoller
- */
- @Override
- public void release(SocketWrapper<Long> socket,
- Processor<Long> processor, boolean isSocketClosing,
- boolean addToPoller) {
- processor.recycle(isSocketClosing);
- recycledProcessors.push(processor);
- if (addToPoller && proto.endpoint.isRunning()) {
- socket.registerforEvent(proto.endpoint.getKeepAliveTimeout(), true, false);
- }
- }
-
- @Override
- protected void initSsl(SocketWrapper<Long> socket,
- Processor<Long> processor) {
- // NOOP for APR
- }
-
- @Override
- protected void longPoll(SocketWrapper<Long> socket,
- Processor<Long> processor) {
-
- if (processor.isAsync()) {
- // Async
- socket.setAsync(true);
- } else if (processor.isComet()) {
- // Comet
- if (proto.endpoint.isRunning()) {
- socket.setComet(true);
- socket.registerforEvent(proto.endpoint.getSoTimeout(), true, false);
- } else {
- // Process a STOP directly
- ((AprEndpoint) proto.endpoint).processSocket(
- socket.getSocket().longValue(),
- SocketStatus.STOP);
- }
- } else {
- // Upgraded
- Poller p = ((AprEndpoint) proto.endpoint).getPoller();
- if (p == null) {
- // Connector has been stopped
- release(socket, processor, true, false);
- } else {
- socket.registerforEvent(-1, true, false);
- }
- }
- }
-
- @Override
- protected Http11AprProcessor createProcessor() {
- Http11AprProcessor processor = new Http11AprProcessor(
- proto.getMaxHttpHeaderSize(), (AprEndpoint)proto.endpoint,
- proto.getMaxTrailerSize(), proto.getAllowedTrailerHeadersAsSet(),
- proto.getMaxExtensionSize(), proto.getMaxSwallowSize());
- proto.configureProcessor(processor);
- // APR specific configuration
- processor.setClientCertProvider(proto.getClientCertProvider());
- register(processor);
- return processor;
- }
-
- @Override
- protected Processor<Long> createUpgradeProcessor(
- SocketWrapper<Long> socket, ByteBuffer leftoverInput,
- UpgradeToken upgradeToken)
- throws IOException {
- return new AprProcessor(socket, leftoverInput,
- upgradeToken, (AprEndpoint) proto.endpoint,
- proto.getUpgradeAsyncWriteBufferSize());
+ if (isSSLEnabled()) {
+ return ("https-openssl-apr");
+ } else {
+ return ("http-apr");
}
}
}
diff --git a/java/org/apache/coyote/http11/Http11InputBuffer.java b/java/org/apache/coyote/http11/Http11InputBuffer.java
new file mode 100644
index 0000000..cb9beb9
--- /dev/null
+++ b/java/org/apache/coyote/http11/Http11InputBuffer.java
@@ -0,0 +1,1093 @@
+/*
+ * 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.http11;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+
+import org.apache.coyote.InputBuffer;
+import org.apache.coyote.Request;
+import org.apache.juli.logging.Log;
+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.MimeHeaders;
+import org.apache.tomcat.util.http.parser.HttpParser;
+import org.apache.tomcat.util.net.ApplicationBufferHandler;
+import org.apache.tomcat.util.net.SocketWrapperBase;
+import org.apache.tomcat.util.res.StringManager;
+
+/**
+ * InputBuffer for HTTP that provides request header parsing as well as transfer
+ * encoding.
+ */
+public class Http11InputBuffer implements InputBuffer, ApplicationBufferHandler {
+
+ // -------------------------------------------------------------- Constants
+
+ private static final Log log = LogFactory.getLog(Http11InputBuffer.class);
+
+ /**
+ * The string manager for this package.
+ */
+ private static final StringManager sm = StringManager.getManager(Http11InputBuffer.class);
+
+
+ private static final byte[] CLIENT_PREFACE_START =
+ "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n".getBytes(StandardCharsets.ISO_8859_1);
+
+ /**
+ * Associated Coyote request.
+ */
+ private final Request request;
+
+
+ /**
+ * Headers of the associated request.
+ */
+ private final MimeHeaders headers;
+
+
+ /**
+ * State.
+ */
+ private boolean parsingHeader;
+
+
+ /**
+ * Swallow input ? (in the case of an expectation)
+ */
+ private boolean swallowInput;
+
+
+ /**
+ * The read buffer.
+ */
+ private ByteBuffer byteBuffer;
+
+
+ /**
+ * Pos of the end of the header in the buffer, which is also the
+ * start of the body.
+ */
+ private int end;
+
+
+ /**
+ * Wrapper that provides access to the underlying socket.
+ */
+ private SocketWrapperBase<?> wrapper;
+
+
+ /**
+ * Underlying input buffer.
+ */
+ private InputBuffer inputStreamInputBuffer;
+
+
+ /**
+ * Filter library.
+ * Note: Filter[Constants.CHUNKED_FILTER] is always the "chunked" filter.
+ */
+ private InputFilter[] filterLibrary;
+
+
+ /**
+ * Active filters (in order).
+ */
+ private InputFilter[] activeFilters;
+
+
+ /**
+ * Index of the last active filter.
+ */
+ private int lastActiveFilter;
+
+
+ /**
+ * Parsing state - used for non blocking parsing so that
+ * when more data arrives, we can pick up where we left off.
+ */
+ private boolean parsingRequestLine;
+ private int parsingRequestLinePhase = 0;
+ private boolean parsingRequestLineEol = false;
+ private int parsingRequestLineStart = 0;
+ private int parsingRequestLineQPos = -1;
+ private HeaderParsePosition headerParsePos;
+ private final HeaderParseData headerData = new HeaderParseData();
+
+ /**
+ * Maximum allowed size of the HTTP request line plus headers plus any
+ * leading blank lines.
+ */
+ private final int headerBufferSize;
+
+ /**
+ * Known size of the NioChannel read buffer.
+ */
+ private int socketReadBufferSize;
+
+
+ // ----------------------------------------------------------- Constructors
+
+ public Http11InputBuffer(Request request, int headerBufferSize) {
+
+ this.request = request;
+ headers = request.getMimeHeaders();
+
+ this.headerBufferSize = headerBufferSize;
+
+ filterLibrary = new InputFilter[0];
+ activeFilters = new InputFilter[0];
+ lastActiveFilter = -1;
+
+ parsingHeader = true;
+ parsingRequestLine = true;
+ parsingRequestLinePhase = 0;
+ parsingRequestLineEol = false;
+ parsingRequestLineStart = 0;
+ parsingRequestLineQPos = -1;
+ headerParsePos = HeaderParsePosition.HEADER_START;
+ swallowInput = true;
+
+ inputStreamInputBuffer = new SocketInputBuffer();
+ }
+
+
+ // ------------------------------------------------------------- Properties
+
+ /**
+ * Add an input filter to the filter library.
+ *
+ * @throws NullPointerException if the supplied filter is null
+ */
+ void addFilter(InputFilter filter) {
+
+ if (filter == null) {
+ throw new NullPointerException(sm.getString("iib.filter.npe"));
+ }
+
+ InputFilter[] newFilterLibrary = new InputFilter[filterLibrary.length + 1];
+ for (int i = 0; i < filterLibrary.length; i++) {
+ newFilterLibrary[i] = filterLibrary[i];
+ }
+ newFilterLibrary[filterLibrary.length] = filter;
+ filterLibrary = newFilterLibrary;
+
+ activeFilters = new InputFilter[filterLibrary.length];
+ }
+
+
+ /**
+ * Get filters.
+ */
+ InputFilter[] getFilters() {
+ return filterLibrary;
+ }
+
+
+ /**
+ * Add an input filter to the filter library.
+ */
+ void addActiveFilter(InputFilter filter) {
+
+ if (lastActiveFilter == -1) {
+ filter.setBuffer(inputStreamInputBuffer);
+ } else {
+ for (int i = 0; i <= lastActiveFilter; i++) {
+ if (activeFilters[i] == filter)
+ return;
+ }
+ filter.setBuffer(activeFilters[lastActiveFilter]);
+ }
+
+ activeFilters[++lastActiveFilter] = filter;
+
+ filter.setRequest(request);
+ }
+
+
+ /**
+ * Set the swallow input flag.
+ */
+ void setSwallowInput(boolean swallowInput) {
+ this.swallowInput = swallowInput;
+ }
+
+
+ // ---------------------------------------------------- InputBuffer Methods
+
+ /**
+ * @deprecated Unused. Will be removed in Tomcat 9. Use
+ * {@link #doRead(ApplicationBufferHandler)}
+ */
+ @Override
+ public int doRead(ByteChunk chunk) throws IOException {
+
+ if (lastActiveFilter == -1)
+ return inputStreamInputBuffer.doRead(chunk);
+ else
+ return activeFilters[lastActiveFilter].doRead(chunk);
+
+ }
+
+ @Override
+ public int doRead(ApplicationBufferHandler handler) throws IOException {
+
+ if (lastActiveFilter == -1)
+ return inputStreamInputBuffer.doRead(handler);
+ else
+ return activeFilters[lastActiveFilter].doRead(handler);
+
+ }
+
+
+ // ------------------------------------------------------- Protected Methods
+
+ /**
+ * Recycle the input buffer. This should be called when closing the
+ * connection.
+ */
+ void recycle() {
+ wrapper = null;
+ request.recycle();
+
+ for (int i = 0; i <= lastActiveFilter; i++) {
+ activeFilters[i].recycle();
+ }
+
+ byteBuffer.limit(0).position(0);
+ lastActiveFilter = -1;
+ parsingHeader = true;
+ swallowInput = true;
+
+ headerParsePos = HeaderParsePosition.HEADER_START;
+ parsingRequestLine = true;
+ parsingRequestLinePhase = 0;
+ parsingRequestLineEol = false;
+ parsingRequestLineStart = 0;
+ parsingRequestLineQPos = -1;
+ headerData.recycle();
+ }
+
+
+ /**
+ * End processing of current HTTP request.
+ * Note: All bytes of the current request should have been already
+ * consumed. This method only resets all the pointers so that we are ready
+ * to parse the next HTTP request.
+ */
+ void nextRequest() {
+ request.recycle();
+
+ // Copy leftover bytes to the beginning of the buffer
+ if (byteBuffer.remaining() > 0 && byteBuffer.position() > 0) {
+ byteBuffer.compact();
+ }
+ // Always reset pos to zero
+ byteBuffer.limit(byteBuffer.limit() - byteBuffer.position()).position(0);
+
+ // Recycle filters
+ for (int i = 0; i <= lastActiveFilter; i++) {
+ activeFilters[i].recycle();
+ }
+
+ // Reset pointers
+ lastActiveFilter = -1;
+ parsingHeader = true;
+ swallowInput = true;
+
+ headerParsePos = HeaderParsePosition.HEADER_START;
+ parsingRequestLine = true;
+ parsingRequestLinePhase = 0;
+ parsingRequestLineEol = false;
+ parsingRequestLineStart = 0;
+ parsingRequestLineQPos = -1;
+ headerData.recycle();
+ }
+
+
+ /**
+ * Read the request line. This function is meant to be used during the
+ * HTTP request header parsing. Do NOT attempt to read the request body
+ * using it.
+ *
+ * @throws IOException If an exception occurs during the underlying socket
+ * read operations, or if the given buffer is not big enough to accommodate
+ * the whole line.
+ * @return true if data is properly fed; false if no data is available
+ * immediately and thread should be freed
+ */
+ boolean parseRequestLine(boolean keptAlive) throws IOException {
+
+ // check state
+ if (!parsingRequestLine) {
+ return true;
+ }
+ //
+ // Skipping blank lines
+ //
+ if (parsingRequestLinePhase < 2) {
+ byte chr = 0;
+ do {
+
+ // Read new bytes if needed
+ if (byteBuffer.position() >= byteBuffer.limit()) {
+ if (keptAlive) {
+ // Haven't read any request data yet so use the keep-alive
+ // timeout.
+ wrapper.setReadTimeout(wrapper.getEndpoint().getKeepAliveTimeout());
+ }
+ if (!fill(false)) {
+ // A read is pending, so no longer in initial state
+ parsingRequestLinePhase = 1;
+ return false;
+ }
+ // At least one byte of the request has been received.
+ // Switch to the socket timeout.
+ wrapper.setReadTimeout(wrapper.getEndpoint().getSoTimeout());
+ }
+ if (!keptAlive && byteBuffer.position() == 0 && byteBuffer.limit() >= CLIENT_PREFACE_START.length - 1) {
+ boolean prefaceMatch = true;
+ for (int i = 0; i < CLIENT_PREFACE_START.length && prefaceMatch; i++) {
+ if (CLIENT_PREFACE_START[i] != byteBuffer.get(i)) {
+ prefaceMatch = false;
+ }
+ }
+ if (prefaceMatch) {
+ // HTTP/2 preface matched
+ parsingRequestLinePhase = -1;
+ return false;
+ }
+ }
+ // Set the start time once we start reading data (even if it is
+ // just skipping blank lines)
+ if (request.getStartTime() < 0) {
+ request.setStartTime(System.currentTimeMillis());
+ }
+ chr = byteBuffer.get();
+ } while ((chr == Constants.CR) || (chr == Constants.LF));
+ byteBuffer.position(byteBuffer.position() - 1);
+
+ parsingRequestLineStart = byteBuffer.position();
+ parsingRequestLinePhase = 2;
+ if (log.isDebugEnabled()) {
+ log.debug("Received ["
+ + new String(byteBuffer.array(), byteBuffer.position(), byteBuffer.remaining(), StandardCharsets.ISO_8859_1) + "]");
+ }
+ }
+ if (parsingRequestLinePhase == 2) {
+ //
+ // Reading the method name
+ // Method name is a token
+ //
+ boolean space = false;
+ while (!space) {
+ // Read new bytes if needed
+ if (byteBuffer.position() >= byteBuffer.limit()) {
+ if (!fill(false)) // request line parsing
+ return false;
+ }
+ // Spec says method name is a token followed by a single SP but
+ // also be tolerant of multiple SP and/or HT.
+ int pos = byteBuffer.position();
+ byte chr = byteBuffer.get();
+ if (chr == Constants.SP || chr == Constants.HT) {
+ space = true;
+ request.method().setBytes(byteBuffer.array(), parsingRequestLineStart,
+ pos - parsingRequestLineStart);
+ } else if (!HttpParser.isToken(chr)) {
+ byteBuffer.position(byteBuffer.position() - 1);
+ throw new IllegalArgumentException(sm.getString("iib.invalidmethod"));
+ }
+ }
+ parsingRequestLinePhase = 3;
+ }
+ if (parsingRequestLinePhase == 3) {
+ // Spec says single SP but also be tolerant of multiple SP and/or HT
+ boolean space = true;
+ while (space) {
+ // Read new bytes if needed
+ if (byteBuffer.position() >= byteBuffer.limit()) {
+ if (!fill(false)) // request line parsing
+ return false;
+ }
+ byte chr = byteBuffer.get();
+ if (!(chr == Constants.SP || chr == Constants.HT)) {
+ space = false;
+ byteBuffer.position(byteBuffer.position() - 1);
+ }
+ }
+ parsingRequestLineStart = byteBuffer.position();
+ parsingRequestLinePhase = 4;
+ }
+ if (parsingRequestLinePhase == 4) {
+ // Mark the current buffer position
+
+ int end = 0;
+ //
+ // Reading the URI
+ //
+ boolean space = false;
+ while (!space) {
+ // Read new bytes if needed
+ if (byteBuffer.position() >= byteBuffer.limit()) {
+ if (!fill(false)) // request line parsing
+ return false;
+ }
+ int pos = byteBuffer.position();
+ byte chr = byteBuffer.get();
+ if (chr == Constants.SP || chr == Constants.HT) {
+ space = true;
+ end = pos;
+ } else if (chr == Constants.CR || chr == Constants.LF) {
+ // HTTP/0.9 style request
+ parsingRequestLineEol = true;
+ space = true;
+ end = pos;
+ } else if (chr == Constants.QUESTION && parsingRequestLineQPos == -1) {
+ parsingRequestLineQPos = pos;
+ } else if (HttpParser.isNotRequestTarget(chr)) {
+ throw new IllegalArgumentException(sm.getString("iib.invalidRequestTarget"));
+ }
+ }
+ if (parsingRequestLineQPos >= 0) {
+ request.queryString().setBytes(byteBuffer.array(), parsingRequestLineQPos + 1,
+ end - parsingRequestLineQPos - 1);
+ request.requestURI().setBytes(byteBuffer.array(), parsingRequestLineStart,
+ parsingRequestLineQPos - parsingRequestLineStart);
+ } else {
+ request.requestURI().setBytes(byteBuffer.array(), parsingRequestLineStart,
+ end - parsingRequestLineStart);
+ }
+ parsingRequestLinePhase = 5;
+ }
+ if (parsingRequestLinePhase == 5) {
+ // Spec says single SP but also be tolerant of multiple and/or HT
+ boolean space = true;
+ while (space) {
+ // Read new bytes if needed
+ if (byteBuffer.position() >= byteBuffer.limit()) {
+ if (!fill(false)) // request line parsing
+ return false;
+ }
+ byte chr = byteBuffer.get();
+ if (!(chr == Constants.SP || chr == Constants.HT)) {
+ space = false;
+ byteBuffer.position(byteBuffer.position() - 1);
+ }
+ }
+ parsingRequestLineStart = byteBuffer.position();
+ parsingRequestLinePhase = 6;
+
+ // Mark the current buffer position
+ end = 0;
+ }
+ if (parsingRequestLinePhase == 6) {
+ //
+ // Reading the protocol
+ // Protocol is always "HTTP/" DIGIT "." DIGIT
+ //
+ while (!parsingRequestLineEol) {
+ // Read new bytes if needed
+ if (byteBuffer.position() >= byteBuffer.limit()) {
+ if (!fill(false)) // request line parsing
+ return false;
+ }
+
+ int pos = byteBuffer.position();
+ byte chr = byteBuffer.get();
+ if (chr == Constants.CR) {
+ end = pos;
+ } else if (chr == Constants.LF) {
+ if (end == 0) {
+ end = pos;
+ }
+ parsingRequestLineEol = true;
+ } else if (!HttpParser.isHttpProtocol(chr)) {
+ throw new IllegalArgumentException(sm.getString("iib.invalidHttpProtocol"));
+ }
+ }
+
+ if ((end - parsingRequestLineStart) > 0) {
+ request.protocol().setBytes(byteBuffer.array(), parsingRequestLineStart,
+ end - parsingRequestLineStart);
+ } else {
+ request.protocol().setString("");
+ }
+ parsingRequestLine = false;
+ parsingRequestLinePhase = 0;
+ parsingRequestLineEol = false;
+ parsingRequestLineStart = 0;
+ return true;
+ }
+ throw new IllegalStateException(
+ "Invalid request line parse phase:" + parsingRequestLinePhase);
+ }
+
+
+ /**
+ * Parse the HTTP headers.
+ */
+ boolean parseHeaders() throws IOException {
+ if (!parsingHeader) {
+ throw new IllegalStateException(sm.getString("iib.parseheaders.ise.error"));
+ }
+
+ HeaderParseStatus status = HeaderParseStatus.HAVE_MORE_HEADERS;
+
+ 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) {
+ throw new IllegalArgumentException(sm.getString("iib.requestheadertoolarge.error"));
+ }
+ } while (status == HeaderParseStatus.HAVE_MORE_HEADERS);
+ if (status == HeaderParseStatus.DONE) {
+ parsingHeader = false;
+ end = byteBuffer.position();
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+
+ int getParsingRequestLinePhase() {
+ return parsingRequestLinePhase;
+ }
+
+
+ /**
+ * End request (consumes leftover bytes).
+ *
+ * @throws IOException an underlying I/O error occurred
+ */
+ void endRequest() throws IOException {
+
+ if (swallowInput && (lastActiveFilter != -1)) {
+ int extraBytes = (int) activeFilters[lastActiveFilter].end();
+ byteBuffer.position(byteBuffer.position() - extraBytes);
+ }
+ }
+
+
+ /**
+ * Available bytes in the buffers (note that due to encoding, this may not
+ * correspond).
+ */
+ int available(boolean read) {
+ int available = byteBuffer.remaining();
+ if ((available == 0) && (lastActiveFilter >= 0)) {
+ for (int i = 0; (available == 0) && (i <= lastActiveFilter); i++) {
+ available = activeFilters[i].available();
+ }
+ }
+ if (available > 0 || !read) {
+ return available;
+ }
+
+ try {
+ fill(false);
+ available = byteBuffer.remaining();
+ } catch (IOException ioe) {
+ if (log.isDebugEnabled()) {
+ log.debug(sm.getString("iib.available.readFail"), ioe);
+ }
+ // 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.
+ available = 1;
+ }
+ return available;
+ }
+
+
+ /**
+ * Has all of the request body been read? There are subtle differences
+ * between this and available() > 0 primarily because of having to handle
+ * faking non-blocking reads with the blocking IO connector.
+ */
+ boolean isFinished() {
+ if (byteBuffer.limit() > byteBuffer.position()) {
+ // Data to read in the buffer so not finished
+ return false;
+ }
+
+ /*
+ * Don't use fill(false) here because in the following circumstances
+ * BIO will block - possibly indefinitely
+ * - client is using keep-alive and connection is still open
+ * - client has sent the complete request
+ * - client has not sent any of the next request (i.e. no pipelining)
+ * - application has read the complete request
+ */
+
+ // Check the InputFilters
+
+ if (lastActiveFilter >= 0) {
+ return activeFilters[lastActiveFilter].isFinished();
+ } else {
+ // No filters. Assume request is not finished. EOF will signal end of
+ // request.
+ return false;
+ }
+ }
+
+ ByteBuffer getLeftover() {
+ int available = byteBuffer.remaining();
+ if (available > 0) {
+ return ByteBuffer.wrap(byteBuffer.array(), byteBuffer.position(), available);
+ } else {
+ return null;
+ }
+ }
+
+
+ void init(SocketWrapperBase<?> socketWrapper) {
+
+ wrapper = socketWrapper;
+ wrapper.setAppReadBufHandler(this);
+
+ int bufLength = headerBufferSize +
+ wrapper.getSocketBufferHandler().getReadBuffer().capacity();
+ if (byteBuffer == null || byteBuffer.capacity() < bufLength) {
+ byteBuffer = ByteBuffer.allocate(bufLength);
+ byteBuffer.position(0).limit(0);
+ }
+ }
+
+
+
+ // --------------------------------------------------------- Private Methods
+
+ /**
+ * Attempts to read some data into the input buffer.
+ *
+ * @return <code>true</code> if more data was added to the input buffer
+ * otherwise <code>false</code>
+ */
+ private boolean fill(boolean block) throws IOException {
+
+ if (parsingHeader) {
+ if (byteBuffer.limit() >= headerBufferSize) {
+ throw new IllegalArgumentException(sm.getString("iib.requestheadertoolarge.error"));
+ }
+ } else {
+ byteBuffer.limit(end).position(end);
+ }
+
+ byteBuffer.mark();
+ if (byteBuffer.position() < byteBuffer.limit()) {
+ byteBuffer.position(byteBuffer.limit());
+ }
+ byteBuffer.limit(byteBuffer.capacity());
+ int nRead = wrapper.read(block, byteBuffer);
+ byteBuffer.limit(byteBuffer.position()).reset();
+ if (nRead > 0) {
+ return true;
+ } else if (nRead == -1) {
+ throw new EOFException(sm.getString("iib.eof.error"));
+ } else {
+ return false;
+ }
+
+ }
+
+
+ /**
+ * Parse an HTTP header.
+ *
+ * @return false after reading a blank line (which indicates that the
+ * HTTP header parsing is done
+ */
+ private HeaderParseStatus parseHeader() throws IOException {
+
+ //
+ // Check for blank line
+ //
+
+ byte chr = 0;
+ while (headerParsePos == HeaderParsePosition.HEADER_START) {
+
+ // Read new bytes if needed
+ if (byteBuffer.position() >= byteBuffer.limit()) {
+ if (!fill(false)) {// parse header
+ headerParsePos = HeaderParsePosition.HEADER_START;
+ return HeaderParseStatus.NEED_MORE_DATA;
+ }
+ }
+
+ chr = byteBuffer.get();
+
+ if (chr == Constants.CR) {
+ // Skip
+ } else if (chr == Constants.LF) {
+ return HeaderParseStatus.DONE;
+ } else {
+ byteBuffer.position(byteBuffer.position() - 1);
+ break;
+ }
+
+ }
+
+ if (headerParsePos == HeaderParsePosition.HEADER_START) {
+ // Mark the current buffer position
+ headerData.start = byteBuffer.position();
+ headerParsePos = HeaderParsePosition.HEADER_NAME;
+ }
+
+ //
+ // Reading the header name
+ // Header name is always US-ASCII
+ //
+
+ while (headerParsePos == HeaderParsePosition.HEADER_NAME) {
+
+ // Read new bytes if needed
+ if (byteBuffer.position() >= byteBuffer.limit()) {
+ if (!fill(false)) { // parse header
+ return HeaderParseStatus.NEED_MORE_DATA;
+ }
+ }
+
+ int pos = byteBuffer.position();
+ chr = byteBuffer.get();
+ if (chr == Constants.COLON) {
+ headerParsePos = HeaderParsePosition.HEADER_VALUE_START;
+ headerData.headerValue = headers.addValue(byteBuffer.array(), headerData.start,
+ pos - headerData.start);
+ pos = byteBuffer.position();
+ // Mark the current buffer position
+ headerData.start = pos;
+ headerData.realPos = pos;
+ headerData.lastSignificantChar = pos;
+ break;
+ } else if (!HttpParser.isToken(chr)) {
+ // If a non-token header is detected, skip the line and
+ // ignore the header
+ headerData.lastSignificantChar = pos;
+ byteBuffer.position(byteBuffer.position() - 1);
+ return skipLine();
+ }
+
+ // chr is next byte of header name. Convert to lowercase.
+ if ((chr >= Constants.A) && (chr <= Constants.Z)) {
+ byteBuffer.put(pos, (byte) (chr - Constants.LC_OFFSET));
+ }
+ }
+
+ // Skip the line and ignore the header
+ if (headerParsePos == HeaderParsePosition.HEADER_SKIPLINE) {
+ return skipLine();
+ }
+
+ //
+ // Reading the header value (which can be spanned over multiple lines)
+ //
+
+ while (headerParsePos == HeaderParsePosition.HEADER_VALUE_START ||
+ headerParsePos == HeaderParsePosition.HEADER_VALUE ||
+ headerParsePos == HeaderParsePosition.HEADER_MULTI_LINE) {
+
+ if (headerParsePos == HeaderParsePosition.HEADER_VALUE_START) {
+ // Skipping spaces
+ while (true) {
+ // Read new bytes if needed
+ if (byteBuffer.position() >= byteBuffer.limit()) {
+ if (!fill(false)) {// parse header
+ // HEADER_VALUE_START
+ return HeaderParseStatus.NEED_MORE_DATA;
+ }
+ }
+
+ chr = byteBuffer.get();
+ if (!(chr == Constants.SP || chr == Constants.HT)) {
+ headerParsePos = HeaderParsePosition.HEADER_VALUE;
+ byteBuffer.position(byteBuffer.position() - 1);
+ break;
+ }
+ }
+ }
+ if (headerParsePos == HeaderParsePosition.HEADER_VALUE) {
+
+ // Reading bytes until the end of the line
+ boolean eol = false;
+ while (!eol) {
+
+ // Read new bytes if needed
+ if (byteBuffer.position() >= byteBuffer.limit()) {
+ if (!fill(false)) {// parse header
+ // HEADER_VALUE
+ return HeaderParseStatus.NEED_MORE_DATA;
+ }
+ }
+
+ chr = byteBuffer.get();
+ if (chr == Constants.CR) {
+ // Skip
+ } else if (chr == Constants.LF) {
+ eol = true;
+ } else if (chr == Constants.SP || chr == Constants.HT) {
+ byteBuffer.put(headerData.realPos, chr);
+ headerData.realPos++;
+ } else {
+ byteBuffer.put(headerData.realPos, chr);
+ headerData.realPos++;
+ headerData.lastSignificantChar = headerData.realPos;
+ }
+ }
+
+ // Ignore whitespaces at the end of the line
+ headerData.realPos = headerData.lastSignificantChar;
+
+ // Checking the first character of the new line. If the character
+ // is a LWS, then it's a multiline header
+ headerParsePos = HeaderParsePosition.HEADER_MULTI_LINE;
+ }
+ // Read new bytes if needed
+ if (byteBuffer.position() >= byteBuffer.limit()) {
+ if (!fill(false)) {// parse header
+ // HEADER_MULTI_LINE
+ return HeaderParseStatus.NEED_MORE_DATA;
+ }
+ }
+
+ chr = byteBuffer.get(byteBuffer.position());
+ if (headerParsePos == HeaderParsePosition.HEADER_MULTI_LINE) {
+ if ((chr != Constants.SP) && (chr != Constants.HT)) {
+ headerParsePos = HeaderParsePosition.HEADER_START;
+ break;
+ } else {
+ // Copying one extra space in the buffer (since there must
+ // be at least one space inserted between the lines)
+ byteBuffer.put(headerData.realPos, chr);
+ headerData.realPos++;
+ headerParsePos = HeaderParsePosition.HEADER_VALUE_START;
+ }
+ }
+ }
+ // Set the header value
+ headerData.headerValue.setBytes(byteBuffer.array(), headerData.start,
+ headerData.lastSignificantChar - headerData.start);
+ headerData.recycle();
+ return HeaderParseStatus.HAVE_MORE_HEADERS;
+ }
+
+
+ private HeaderParseStatus skipLine() throws IOException {
+ headerParsePos = HeaderParsePosition.HEADER_SKIPLINE;
+ boolean eol = false;
+
+ // Reading bytes until the end of the line
+ while (!eol) {
+
+ // Read new bytes if needed
+ if (byteBuffer.position() >= byteBuffer.limit()) {
+ if (!fill(false)) {
+ return HeaderParseStatus.NEED_MORE_DATA;
+ }
+ }
+
+ int pos = byteBuffer.position();
+ byte chr = byteBuffer.get();
+ if (chr == Constants.CR) {
+ // Skip
+ } else if (chr == Constants.LF) {
+ eol = true;
+ } else {
+ headerData.lastSignificantChar = pos;
+ }
+ }
+ if (log.isDebugEnabled()) {
+ log.debug(sm.getString("iib.invalidheader",
+ new String(byteBuffer.array(), headerData.start,
+ headerData.lastSignificantChar - headerData.start + 1,
+ StandardCharsets.ISO_8859_1)));
+ }
+
+ headerParsePos = HeaderParsePosition.HEADER_START;
+ return HeaderParseStatus.HAVE_MORE_HEADERS;
+ }
+
+
+ // ----------------------------------------------------------- Inner classes
+
+ private static enum HeaderParseStatus {
+ DONE, HAVE_MORE_HEADERS, NEED_MORE_DATA
+ }
+
+
+ private static enum HeaderParsePosition {
+ /**
+ * Start of a new header. A CRLF here means that there are no more
+ * headers. Any other character starts a header name.
+ */
+ HEADER_START,
+ /**
+ * Reading a header name. All characters of header are HTTP_TOKEN_CHAR.
+ * Header name is followed by ':'. No whitespace is allowed.<br>
+ * Any non-HTTP_TOKEN_CHAR (this includes any whitespace) encountered
+ * before ':' will result in the whole line being ignored.
+ */
+ HEADER_NAME,
+ /**
+ * Skipping whitespace before text of header value starts, either on the
+ * first line of header value (just after ':') or on subsequent lines
+ * when it is known that subsequent line starts with SP or HT.
+ */
+ HEADER_VALUE_START,
+ /**
+ * Reading the header value. We are inside the value. Either on the
+ * first line or on any subsequent line. We come into this state from
+ * HEADER_VALUE_START after the first non-SP/non-HT byte is encountered
+ * on the line.
+ */
+ HEADER_VALUE,
+ /**
+ * Before reading a new line of a header. Once the next byte is peeked,
+ * the state changes without advancing our position. The state becomes
+ * either HEADER_VALUE_START (if that first byte is SP or HT), or
+ * HEADER_START (otherwise).
+ */
+ HEADER_MULTI_LINE,
+ /**
+ * Reading all bytes until the next CRLF. The line is being ignored.
+ */
+ HEADER_SKIPLINE
+ }
+
+
+ private static class HeaderParseData {
+ /**
+ * When parsing header name: first character of the header.<br>
+ * When skipping broken header line: first character of the header.<br>
+ * When parsing header value: first character after ':'.
+ */
+ int start = 0;
+ /**
+ * When parsing header name: not used (stays as 0).<br>
+ * When skipping broken header line: not used (stays as 0).<br>
+ * When parsing header value: starts as the first character after ':'.
+ * Then is increased as far as more bytes of the header are harvested.
+ * Bytes from buf[pos] are copied to buf[realPos]. Thus the string from
+ * [start] to [realPos-1] is the prepared value of the header, with
+ * whitespaces removed as needed.<br>
+ */
+ int realPos = 0;
+ /**
+ * When parsing header name: not used (stays as 0).<br>
+ * When skipping broken header line: last non-CR/non-LF character.<br>
+ * When parsing header value: position after the last not-LWS character.<br>
+ */
+ int lastSignificantChar = 0;
+ /**
+ * MB that will store the value of the header. It is null while parsing
+ * header name and is created after the name has been parsed.
+ */
+ MessageBytes headerValue = null;
+ public void recycle() {
+ start = 0;
+ realPos = 0;
+ lastSignificantChar = 0;
+ headerValue = null;
+ }
+ }
+
+
+ // ------------------------------------- InputStreamInputBuffer Inner Class
+
+ /**
+ * This class is an input buffer which will read its data from an input
+ * stream.
+ */
+ private class SocketInputBuffer implements InputBuffer {
+
+ /**
+ *
+ * @deprecated Unused. Will be removed in Tomcat 9. Use
+ * {@link #doRead(ApplicationBufferHandler)}
+ */
+ @Override
+ public int doRead(ByteChunk chunk) throws IOException {
+
+ if (byteBuffer.position() >= byteBuffer.limit()) {
+ // The application is reading the HTTP request body which is
+ // always a blocking operation.
+ if (!fill(true))
+ return -1;
+ }
+
+ int length = byteBuffer.remaining();
+ chunk.setBytes(byteBuffer.array(), byteBuffer.position(), length);
+ byteBuffer.position(byteBuffer.limit());
+
+ return length;
+ }
+
+ @Override
+ public int doRead(ApplicationBufferHandler handler) throws IOException {
+
+ if (byteBuffer.position() >= byteBuffer.limit()) {
+ // The application is reading the HTTP request body which is
+ // always a blocking operation.
+ if (!fill(true))
+ return -1;
+ }
+
+ int length = byteBuffer.remaining();
+ handler.setByteBuffer(byteBuffer.duplicate());
+ byteBuffer.position(byteBuffer.limit());
+
+ return length;
+ }
+ }
+
+
+ @Override
+ public void setByteBuffer(ByteBuffer buffer) {
+ byteBuffer = buffer;
+ }
+
+
+ @Override
+ public ByteBuffer getByteBuffer() {
+ return byteBuffer;
+ }
+
+
+ @Override
+ public void expand(int size) {
+ if (byteBuffer.capacity() >= size) {
+ byteBuffer.limit(size);
+ }
+ ByteBuffer temp = ByteBuffer.allocate(size);
+ temp.put(byteBuffer);
+ byteBuffer = temp;
+ byteBuffer.mark();
+ temp = null;
+ }
+}
diff --git a/java/org/apache/coyote/http11/Http11Nio2Processor.java b/java/org/apache/coyote/http11/Http11Nio2Processor.java
deleted file mode 100644
index 30cb52d..0000000
--- a/java/org/apache/coyote/http11/Http11Nio2Processor.java
+++ /dev/null
@@ -1,589 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.coyote.http11;
-
-import java.io.IOException;
-import java.io.InterruptedIOException;
-import java.net.InetAddress;
-import java.net.InetSocketAddress;
-import java.util.Set;
-
-import javax.net.ssl.SSLEngine;
-
-import org.apache.coyote.ActionCode;
-import org.apache.coyote.ContainerThreadMarker;
-import org.apache.coyote.ErrorState;
-import org.apache.coyote.RequestInfo;
-import org.apache.coyote.http11.filters.BufferedInputFilter;
-import org.apache.juli.logging.Log;
-import org.apache.juli.logging.LogFactory;
-import org.apache.tomcat.util.ExceptionUtils;
-import org.apache.tomcat.util.net.AbstractEndpoint.Handler.SocketState;
-import org.apache.tomcat.util.net.Nio2Channel;
-import org.apache.tomcat.util.net.Nio2Endpoint;
-import org.apache.tomcat.util.net.SSLSupport;
-import org.apache.tomcat.util.net.SecureNio2Channel;
-import org.apache.tomcat.util.net.SocketStatus;
-import org.apache.tomcat.util.net.SocketWrapper;
-
-
-/**
- * Processes HTTP requests.
- */
-public class Http11Nio2Processor extends AbstractHttp11Processor<Nio2Channel> {
-
- private static final Log log = LogFactory.getLog(Http11Nio2Processor.class);
- @Override
- protected Log getLog() {
- return log;
- }
-
-
- /**
- * SSL information.
- */
- protected SSLSupport sslSupport;
-
- // ----------------------------------------------------------- Constructors
-
-
- public Http11Nio2Processor(int maxHttpHeaderSize, Nio2Endpoint endpoint, int maxTrailerSize,
- Set<String> allowedTrailerHeaders, int maxExtensionSize, int maxSwallowSize) {
-
- super(endpoint);
-
- inputBuffer = new InternalNio2InputBuffer(request, maxHttpHeaderSize);
- request.setInputBuffer(inputBuffer);
-
- outputBuffer = new InternalNio2OutputBuffer(response, maxHttpHeaderSize);
- response.setOutputBuffer(outputBuffer);
-
- initializeFilters(maxTrailerSize, allowedTrailerHeaders, maxExtensionSize, maxSwallowSize);
- }
-
-
- // ----------------------------------------------------- Instance Variables
-
- /**
- * Sendfile data.
- */
- protected Nio2Endpoint.SendfileData sendfileData = null;
-
-
- // --------------------------------------------------------- Public Methods
-
- @Override
- public SocketState event(SocketStatus status)
- throws IOException {
-
- long soTimeout = endpoint.getSoTimeout();
-
- RequestInfo rp = request.getRequestProcessor();
- try {
- rp.setStage(org.apache.coyote.Constants.STAGE_SERVICE);
- if (!getAdapter().event(request, response, status)) {
- setErrorState(ErrorState.CLOSE_NOW, null);
- }
- if (!getErrorState().isError()) {
- if (socketWrapper != null) {
- socketWrapper.setComet(comet);
- if (comet) {
- Integer comettimeout = (Integer) request.getAttribute(
- org.apache.coyote.Constants.COMET_TIMEOUT_ATTR);
- if (comettimeout != null) {
- socketWrapper.setTimeout(comettimeout.longValue());
- }
- } else {
- //reset the timeout
- if (keepAlive) {
- socketWrapper.setTimeout(keepAliveTimeout);
- } else {
- socketWrapper.setTimeout(soTimeout);
- }
- }
-
- }
- }
- } catch (InterruptedIOException e) {
- setErrorState(ErrorState.CLOSE_NOW, e);
- } catch (Throwable t) {
- ExceptionUtils.handleThrowable(t);
- // 500 - Internal Server Error
- response.setStatus(500);
- setErrorState(ErrorState.CLOSE_NOW, t);
- getAdapter().log(request, response, 0);
- log.error(sm.getString("http11processor.request.process"), t);
- }
-
- rp.setStage(org.apache.coyote.Constants.STAGE_ENDED);
-
- if (getErrorState().isError() || status==SocketStatus.STOP) {
- return SocketState.CLOSED;
- } else if (!comet) {
- if (keepAlive) {
- inputBuffer.nextRequest();
- outputBuffer.nextRequest();
- if (((InternalNio2InputBuffer) inputBuffer).isPending()) {
- // Following comet processing, a read is still pending, so
- // keep the processor associated
- return SocketState.LONG;
- } else {
- return SocketState.OPEN;
- }
- } else {
- return SocketState.CLOSED;
- }
- } else {
- return SocketState.LONG;
- }
- }
-
- @Override
- public SocketState asyncDispatch(SocketStatus status) {
- SocketState state = super.asyncDispatch(status);
- if (state == SocketState.OPEN && ((InternalNio2InputBuffer) inputBuffer).isPending()) {
- // Following async processing, a read is still pending, so
- // keep the processor associated
- return SocketState.LONG;
- } else {
- return state;
- }
- }
-
- @Override
- protected void registerForEvent(boolean read, boolean write) {
- if (read) {
- ((InternalNio2InputBuffer) inputBuffer).registerReadInterest();
- }
- if (write) {
- ((InternalNio2OutputBuffer) outputBuffer).registerWriteInterest();
- }
- }
-
-
- @Override
- protected void resetTimeouts() {
- if (!getErrorState().isError() && socketWrapper != null &&
- asyncStateMachine.isAsyncDispatching()) {
- long soTimeout = endpoint.getSoTimeout();
-
- //reset the timeout
- if (keepAlive) {
- socketWrapper.setTimeout(keepAliveTimeout);
- } else {
- socketWrapper.setTimeout(soTimeout);
- }
- }
- }
-
-
- @Override
- protected boolean disableKeepAlive() {
- return false;
- }
-
-
- @Override
- protected void setRequestLineReadTimeout() throws IOException {
- // socket.setTimeout()
- // - timeout used by poller
- // socket.getSocket().getIOChannel().socket().setSoTimeout()
- // - timeout used for blocking reads
-
- // When entering the processing loop there will always be data to read
- // so no point changing timeouts at this point
-
- // For the second and subsequent executions of the processing loop, a
- // non-blocking read is used so again no need to set the timeouts
-
- // Because NIO supports non-blocking reading of the request line and
- // headers the timeouts need to be set when returning the socket to
- // the poller rather than here.
-
- // NO-OP
- }
-
-
- @Override
- protected boolean handleIncompleteRequestLineRead() {
- // Haven't finished reading the request so keep the socket
- // open
- openSocket = true;
- // Check to see if we have read any of the request line yet
- if (((InternalNio2InputBuffer)
- inputBuffer).getParsingRequestLinePhase() < 1) {
- if (socketWrapper.getLastAccess() > -1 || keptAlive) {
- // Haven't read the request line and have previously processed a
- // request. Must be keep-alive. Make sure poller uses keepAlive.
- socketWrapper.setTimeout(endpoint.getKeepAliveTimeout());
- }
- } else {
- // Started to read request line.
- if (request.getStartTime() < 0) {
- request.setStartTime(System.currentTimeMillis());
- }
- if (endpoint.isPaused()) {
- // Partially processed the request so need to respond
- response.setStatus(503);
- setErrorState(ErrorState.CLOSE_CLEAN, null);
- getAdapter().log(request, response, 0);
- return false;
- } else {
- // Need to keep processor associated with socket
- readComplete = false;
- // Make sure poller uses soTimeout from here onwards
- socketWrapper.setTimeout(endpoint.getSoTimeout());
- }
- }
- return true;
- }
-
-
- @Override
- protected void setSocketTimeout(int timeout) throws IOException {
- socketWrapper.setTimeout(timeout);
- }
-
-
- @Override
- protected void setCometTimeouts(SocketWrapper<Nio2Channel> socketWrapper) {
- if (socketWrapper != null) {
- socketWrapper.setComet(comet);
- if (comet) {
- Integer comettimeout = (Integer) request.getAttribute(
- org.apache.coyote.Constants.COMET_TIMEOUT_ATTR);
- if (comettimeout != null) {
- socketWrapper.setTimeout(comettimeout.longValue());
- }
- }
- }
- }
-
-
- @Override
- protected boolean breakKeepAliveLoop(SocketWrapper<Nio2Channel> socketWrapper) {
- openSocket = keepAlive;
- // Do sendfile as needed: add socket to sendfile and end
- if (sendfileData != null && !getErrorState().isError()) {
- ((Nio2Endpoint.Nio2SocketWrapper) socketWrapper).setSendfileData(sendfileData);
- sendfileData.keepAlive = keepAlive;
- switch (((Nio2Endpoint) endpoint).processSendfile(
- (Nio2Endpoint.Nio2SocketWrapper) socketWrapper)) {
- case DONE:
- return false;
- case PENDING:
- sendfileInProgress = true;
- return true;
- case ERROR:
- // Write failed
- if (log.isDebugEnabled()) {
- log.debug(sm.getString("http11processor.sendfile.error"));
- }
- setErrorState(ErrorState.CLOSE_NOW, null);
- return true;
- }
- }
- return false;
- }
-
-
- @Override
- public void recycleInternal() {
- socketWrapper = null;
- sendfileData = null;
- }
-
-
- // ----------------------------------------------------- ActionHook Methods
-
-
- /**
- * Send an action to the connector.
- *
- * @param actionCode Type of the action
- * @param param Action parameter
- */
- @Override
- @SuppressWarnings("incomplete-switch") // Other cases are handled by action()
- public void actionInternal(ActionCode actionCode, Object param) {
-
- switch (actionCode) {
- case REQ_HOST_ADDR_ATTRIBUTE: {
- if (socketWrapper == null || socketWrapper.getSocket() == null) {
- request.remoteAddr().recycle();
- } else {
- if (socketWrapper.getRemoteAddr() == null) {
- InetAddress inetAddr = null;
- try {
- inetAddr = ((InetSocketAddress) socketWrapper.getSocket().getIOChannel().getRemoteAddress()).getAddress();
- } catch (IOException e) {
- // Ignore
- }
- if (inetAddr != null) {
- socketWrapper.setRemoteAddr(inetAddr.getHostAddress());
- }
- }
- request.remoteAddr().setString(socketWrapper.getRemoteAddr());
- }
- break;
- }
- case REQ_LOCAL_NAME_ATTRIBUTE: {
- if (socketWrapper == null || socketWrapper.getSocket() == null) {
- request.localName().recycle();
- } else {
- if (socketWrapper.getLocalName() == null) {
- InetAddress inetAddr = null;
- try {
- inetAddr = ((InetSocketAddress) socketWrapper.getSocket().getIOChannel().getLocalAddress()).getAddress();
- } catch (IOException e) {
- // Ignore
- }
- if (inetAddr != null) {
- socketWrapper.setLocalName(inetAddr.getHostName());
- }
- }
- request.localName().setString(socketWrapper.getLocalName());
- }
- break;
- }
- case REQ_HOST_ATTRIBUTE: {
- if (socketWrapper == null || socketWrapper.getSocket() == null) {
- request.remoteHost().recycle();
- } else {
- if (socketWrapper.getRemoteHost() == null) {
- InetAddress inetAddr = null;
- try {
- inetAddr = ((InetSocketAddress) socketWrapper.getSocket().getIOChannel().getRemoteAddress()).getAddress();
- } catch (IOException e) {
- // Ignore
- }
- if (inetAddr != null) {
- socketWrapper.setRemoteHost(inetAddr.getHostName());
- }
- if (socketWrapper.getRemoteHost() == null) {
- if (socketWrapper.getRemoteAddr() == null &&
- inetAddr != null) {
- socketWrapper.setRemoteAddr(inetAddr.getHostAddress());
- }
- if (socketWrapper.getRemoteAddr() != null) {
- socketWrapper.setRemoteHost(socketWrapper.getRemoteAddr());
- }
- }
- }
- request.remoteHost().setString(socketWrapper.getRemoteHost());
- }
- break;
- }
- case REQ_LOCAL_ADDR_ATTRIBUTE: {
- if (socketWrapper == null || socketWrapper.getSocket() == null) {
- request.localAddr().recycle();
- } else {
- if (socketWrapper.getLocalAddr() == null) {
- try {
- socketWrapper.setLocalAddr(
- ((InetSocketAddress) socketWrapper.getSocket().getIOChannel().getLocalAddress()).getAddress().getHostAddress());
- } catch (IOException e) {
- // Ignore
- }
- }
- request.localAddr().setString(socketWrapper.getLocalAddr());
- }
- break;
- }
- case REQ_REMOTEPORT_ATTRIBUTE: {
- if (socketWrapper == null || socketWrapper.getSocket() == null) {
- request.setRemotePort(0);
- } else {
- if (socketWrapper.getRemotePort() == -1) {
- try {
- socketWrapper.setRemotePort(((InetSocketAddress) socketWrapper.getSocket().getIOChannel().getRemoteAddress()).getPort());
- } catch (IOException e) {
- // Ignore
- }
- }
- request.setRemotePort(socketWrapper.getRemotePort());
- }
- break;
- }
- case REQ_LOCALPORT_ATTRIBUTE: {
- if (socketWrapper == null || socketWrapper.getSocket() == null) {
- request.setLocalPort(0);
- } else {
- if (socketWrapper.getLocalPort() == -1) {
- try {
- socketWrapper.setLocalPort(((InetSocketAddress) socketWrapper.getSocket().getIOChannel().getLocalAddress()).getPort());
- } catch (IOException e) {
- // Ignore
- }
- }
- request.setLocalPort(socketWrapper.getLocalPort());
- }
- break;
- }
- case REQ_SSL_ATTRIBUTE: {
- try {
- if (sslSupport != null) {
- Object sslO = sslSupport.getCipherSuite();
- if (sslO != null) {
- request.setAttribute
- (SSLSupport.CIPHER_SUITE_KEY, sslO);
- }
- sslO = sslSupport.getPeerCertificateChain(false);
- if (sslO != null) {
- request.setAttribute
- (SSLSupport.CERTIFICATE_KEY, sslO);
- }
- sslO = sslSupport.getKeySize();
- if (sslO != null) {
- request.setAttribute
- (SSLSupport.KEY_SIZE_KEY, sslO);
- }
- sslO = sslSupport.getSessionId();
- if (sslO != null) {
- request.setAttribute
- (SSLSupport.SESSION_ID_KEY, sslO);
- }
- request.setAttribute(SSLSupport.SESSION_MGR, sslSupport);
- }
- } catch (Exception e) {
- log.warn(sm.getString("http11processor.socket.ssl"), e);
- }
- break;
- }
- case REQ_SSL_CERTIFICATE: {
- if (sslSupport != null && socketWrapper.getSocket() != null) {
- /*
- * Consume and buffer the request body, so that it does not
- * interfere with the client's handshake messages
- */
- InputFilter[] inputFilters = inputBuffer.getFilters();
- ((BufferedInputFilter) inputFilters[Constants.BUFFERED_FILTER])
- .setLimit(maxSavePostSize);
- inputBuffer.addActiveFilter
- (inputFilters[Constants.BUFFERED_FILTER]);
- SecureNio2Channel sslChannel = (SecureNio2Channel) socketWrapper.getSocket();
- SSLEngine engine = sslChannel.getSslEngine();
- if (!engine.getNeedClientAuth()) {
- // Need to re-negotiate SSL connection
- engine.setNeedClientAuth(true);
- try {
- sslChannel.rehandshake();
- sslSupport = ((Nio2Endpoint)endpoint).getHandler()
- .getSslImplementation().getSSLSupport(
- engine.getSession());
- } catch (IOException ioe) {
- log.warn(sm.getString("http11processor.socket.sslreneg"), ioe);
- }
- }
-
- try {
- // use force=false since re-negotiation is handled above
- // (and it is a NO-OP for NIO anyway)
- Object sslO = sslSupport.getPeerCertificateChain(false);
- if( sslO != null) {
- request.setAttribute
- (SSLSupport.CERTIFICATE_KEY, sslO);
- }
- } catch (Exception e) {
- log.warn(sm.getString("http11processor.socket.ssl"), e);
- }
- }
- break;
- }
- case COMET_BEGIN: {
- comet = true;
- break;
- }
- case COMET_END: {
- comet = false;
- break;
- }
- case COMET_CLOSE: {
- if (socketWrapper == null || socketWrapper.getSocket() == null) {
- return;
- }
- if (!ContainerThreadMarker.isContainerThread()) {
- // Close event for this processor triggered by request
- // processing in another processor, a non-Tomcat thread (i.e.
- // an application controlled thread) or similar.
- endpoint.processSocket(this.socketWrapper, SocketStatus.OPEN_READ, true);
- }
- break;
- }
- case COMET_SETTIMEOUT: {
- if (param == null) {
- return;
- }
- if (socketWrapper == null) {
- return;
- }
- // If we are not piggy backing on a worker thread, set the timeout
- if (!ContainerThreadMarker.isContainerThread()) {
- long timeout = ((Long)param).longValue();
- socketWrapper.setTimeout(timeout);
- }
- break;
- }
- }
- }
-
-
- // ------------------------------------------------------ Protected Methods
-
-
- @Override
- protected void prepareRequestInternal() {
- sendfileData = null;
- }
-
- @Override
- protected boolean prepareSendfile(OutputFilter[] outputFilters) {
- String fileName = (String) request.getAttribute(
- org.apache.coyote.Constants.SENDFILE_FILENAME_ATTR);
- if (fileName != null) {
- // No entity body sent here
- outputBuffer.addActiveFilter(outputFilters[Constants.VOID_FILTER]);
- contentDelimitation = true;
- sendfileData = new Nio2Endpoint.SendfileData();
- sendfileData.fileName = fileName;
- sendfileData.pos = ((Long) request.getAttribute(
- org.apache.coyote.Constants.SENDFILE_FILE_START_ATTR)).longValue();
- sendfileData.length = ((Long) request.getAttribute(
- org.apache.coyote.Constants.SENDFILE_FILE_END_ATTR)).longValue() - sendfileData.pos;
- return true;
- }
- return false;
- }
-
- @Override
- protected AbstractInputBuffer<Nio2Channel> getInputBuffer() {
- return inputBuffer;
- }
-
- @Override
- protected AbstractOutputBuffer<Nio2Channel> getOutputBuffer() {
- return outputBuffer;
- }
-
- /**
- * Set the SSL information for this HTTP connection.
- */
- @Override
- public void setSslSupport(SSLSupport sslSupport) {
- this.sslSupport = sslSupport;
- }
-}
diff --git a/java/org/apache/coyote/http11/Http11Nio2Protocol.java b/java/org/apache/coyote/http11/Http11Nio2Protocol.java
index 713999f..15c3b61 100644
--- a/java/org/apache/coyote/http11/Http11Nio2Protocol.java
+++ b/java/org/apache/coyote/http11/Http11Nio2Protocol.java
@@ -16,25 +16,10 @@
*/
package org.apache.coyote.http11;
-import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.nio.channels.ReadPendingException;
-
-import org.apache.coyote.AbstractProtocol;
-import org.apache.coyote.Processor;
-import org.apache.coyote.UpgradeToken;
-import org.apache.coyote.http11.upgrade.Nio2Processor;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
-import org.apache.tomcat.util.net.AbstractEndpoint;
import org.apache.tomcat.util.net.Nio2Channel;
import org.apache.tomcat.util.net.Nio2Endpoint;
-import org.apache.tomcat.util.net.Nio2Endpoint.Handler;
-import org.apache.tomcat.util.net.Nio2Endpoint.Nio2SocketWrapper;
-import org.apache.tomcat.util.net.SSLImplementation;
-import org.apache.tomcat.util.net.SecureNio2Channel;
-import org.apache.tomcat.util.net.SocketStatus;
-import org.apache.tomcat.util.net.SocketWrapper;
/**
@@ -45,212 +30,23 @@ public class Http11Nio2Protocol extends AbstractHttp11JsseProtocol<Nio2Channel>
private static final Log log = LogFactory.getLog(Http11Nio2Protocol.class);
- @Override
- protected Log getLog() { return log; }
-
-
- @Override
- protected AbstractEndpoint.Handler getHandler() {
- return cHandler;
- }
-
-
public Http11Nio2Protocol() {
- endpoint=new Nio2Endpoint();
- cHandler = new Http11ConnectionHandler(this);
- ((Nio2Endpoint) endpoint).setHandler(cHandler);
- setSoLinger(Constants.DEFAULT_CONNECTION_LINGER);
- setSoTimeout(Constants.DEFAULT_CONNECTION_TIMEOUT);
- setTcpNoDelay(Constants.DEFAULT_TCP_NO_DELAY);
- }
-
-
- public Nio2Endpoint getEndpoint() {
- return ((Nio2Endpoint)endpoint);
- }
-
-
- // -------------------- Properties--------------------
-
- private final Http11ConnectionHandler cHandler;
-
- // -------------------- Pool setup --------------------
-
- public void setAcceptorThreadPriority(int threadPriority) {
- ((Nio2Endpoint)endpoint).setAcceptorThreadPriority(threadPriority);
- }
-
- public void setPollerThreadPriority(int threadPriority) {
- ((Nio2Endpoint)endpoint).setPollerThreadPriority(threadPriority);
- }
-
- public int getAcceptorThreadPriority() {
- return ((Nio2Endpoint)endpoint).getAcceptorThreadPriority();
+ super(new Nio2Endpoint());
}
- public int getPollerThreadPriority() {
- return ((Nio2Endpoint)endpoint).getThreadPriority();
- }
- public boolean getUseSendfile() {
- return endpoint.getUseSendfile();
- }
-
- public void setUseSendfile(boolean useSendfile) {
- ((Nio2Endpoint)endpoint).setUseSendfile(useSendfile);
- }
-
- // -------------------- Tcp setup --------------------
+ @Override
+ protected Log getLog() { return log; }
- public void setOomParachute(int oomParachute) {
- ((Nio2Endpoint)endpoint).setOomParachute(oomParachute);
- }
// ----------------------------------------------------- JMX related methods
@Override
protected String getNamePrefix() {
- return ("http-nio2");
- }
-
-
- // -------------------- Connection handler --------------------
-
- protected static class Http11ConnectionHandler
- extends AbstractConnectionHandler<Nio2Channel,Http11Nio2Processor>
- implements Handler {
-
- protected Http11Nio2Protocol proto;
-
- Http11ConnectionHandler(Http11Nio2Protocol proto) {
- this.proto = proto;
- }
-
- @Override
- protected AbstractProtocol<Nio2Channel> getProtocol() {
- return proto;
- }
-
- @Override
- protected Log getLog() {
- return log;
- }
-
-
- @Override
- public SSLImplementation getSslImplementation() {
- return proto.sslImplementation;
- }
-
- /**
- * Expected to be used by the Poller to release resources on socket
- * close, errors etc.
- */
- @Override
- public void release(SocketWrapper<Nio2Channel> socket) {
- Processor<Nio2Channel> processor =
- connections.remove(socket.getSocket());
- if (processor != null) {
- processor.recycle(true);
- if (!processor.isUpgrade()) {
- recycledProcessors.push(processor);
- }
- }
- }
-
-
- /**
- * Expected to be used by the handler once the processor is no longer
- * required.
- *
- * @param socket
- * @param processor
- * @param isSocketClosing Not used in HTTP
- * @param addToPoller
- */
- @Override
- public void release(SocketWrapper<Nio2Channel> socket,
- Processor<Nio2Channel> processor, boolean isSocketClosing,
- boolean addToPoller) {
- processor.recycle(isSocketClosing);
- recycledProcessors.push(processor);
- if (socket.isAsync()) {
- ((Nio2Endpoint) proto.endpoint).removeTimeout(socket);
- }
- if (addToPoller) {
- ((Nio2Endpoint) proto.endpoint).awaitBytes(socket);
- }
- }
-
-
- @Override
- protected void initSsl(SocketWrapper<Nio2Channel> socket,
- Processor<Nio2Channel> processor) {
- if (proto.isSSLEnabled() &&
- (proto.sslImplementation != null)
- && (socket.getSocket() instanceof SecureNio2Channel)) {
- SecureNio2Channel ch = (SecureNio2Channel)socket.getSocket();
- processor.setSslSupport(
- proto.sslImplementation.getSSLSupport(
- ch.getSslEngine().getSession()));
- } else {
- processor.setSslSupport(null);
- }
-
- }
-
- @Override
- protected void longPoll(SocketWrapper<Nio2Channel> socket,
- Processor<Nio2Channel> processor) {
- if (processor.isAsync()) {
- socket.setAsync(true);
- ((Nio2Endpoint) proto.endpoint).addTimeout(socket);
- } else if (processor.isUpgrade()) {
- if (((Nio2SocketWrapper) socket).isUpgradeInit()) {
- try {
- ((Nio2Endpoint) proto.endpoint).awaitBytes(socket);
- } catch (ReadPendingException e) {
- // Ignore, the initial state after upgrade is
- // impossible to predict, and a read must be pending
- // to get a first notification
- }
- }
- } else {
- // Either:
- // - this is comet request
- // - this is an upgraded connection
- // - the request line/headers have not been completely
- // read
- // The completion handlers should be in place,
- // so nothing to do here
- }
- }
-
- @Override
- public Http11Nio2Processor createProcessor() {
- Http11Nio2Processor processor = new Http11Nio2Processor(
- proto.getMaxHttpHeaderSize(), (Nio2Endpoint) proto.endpoint,
- proto.getMaxTrailerSize(), proto.getAllowedTrailerHeadersAsSet(),
- proto.getMaxExtensionSize(), proto.getMaxSwallowSize());
- proto.configureProcessor(processor);
- register(processor);
- return processor;
- }
-
- @Override
- protected Processor<Nio2Channel> createUpgradeProcessor(
- SocketWrapper<Nio2Channel> socket, ByteBuffer leftoverInput,
- UpgradeToken upgradeToken)
- throws IOException {
- return new Nio2Processor(proto.endpoint, socket, leftoverInput,
- upgradeToken, proto.getUpgradeAsyncWriteBufferSize());
- }
-
- @Override
- public void closeAll() {
- for (Nio2Channel channel : connections.keySet()) {
- ((Nio2Endpoint) proto.endpoint).closeSocket(channel.getSocket(), SocketStatus.STOP);
- }
+ if (isSSLEnabled()) {
+ return ("https-" + getSslImplemenationShortName()+ "-nio2");
+ } else {
+ return ("http-nio2");
}
}
}
diff --git a/java/org/apache/coyote/http11/Http11NioProcessor.java b/java/org/apache/coyote/http11/Http11NioProcessor.java
deleted file mode 100644
index ea7d726..0000000
--- a/java/org/apache/coyote/http11/Http11NioProcessor.java
+++ /dev/null
@@ -1,573 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.coyote.http11;
-
-import java.io.IOException;
-import java.io.InterruptedIOException;
-import java.net.InetAddress;
-import java.nio.channels.SelectionKey;
-import java.util.Set;
-
-import javax.net.ssl.SSLEngine;
-
-import org.apache.coyote.ActionCode;
-import org.apache.coyote.ErrorState;
-import org.apache.coyote.RequestInfo;
-import org.apache.coyote.http11.filters.BufferedInputFilter;
-import org.apache.juli.logging.Log;
-import org.apache.juli.logging.LogFactory;
-import org.apache.tomcat.util.ExceptionUtils;
-import org.apache.tomcat.util.net.AbstractEndpoint.Handler.SocketState;
-import org.apache.tomcat.util.net.NioChannel;
-import org.apache.tomcat.util.net.NioEndpoint;
-import org.apache.tomcat.util.net.NioEndpoint.KeyAttachment;
-import org.apache.tomcat.util.net.SSLSupport;
-import org.apache.tomcat.util.net.SecureNioChannel;
-import org.apache.tomcat.util.net.SocketStatus;
-import org.apache.tomcat.util.net.SocketWrapper;
-
-
-/**
- * Processes HTTP requests.
- *
- * @author Remy Maucherat
- */
-public class Http11NioProcessor extends AbstractHttp11Processor<NioChannel> {
-
- private static final Log log = LogFactory.getLog(Http11NioProcessor.class);
- @Override
- protected Log getLog() {
- return log;
- }
-
-
- /**
- * SSL information.
- */
- protected SSLSupport sslSupport;
-
- // ----------------------------------------------------------- Constructors
-
-
- public Http11NioProcessor(int maxHttpHeaderSize, NioEndpoint endpoint, int maxTrailerSize,
- Set<String> allowedTrailerHeaders, int maxExtensionSize, int maxSwallowSize) {
-
- super(endpoint);
-
- inputBuffer = new InternalNioInputBuffer(request, maxHttpHeaderSize);
- request.setInputBuffer(inputBuffer);
-
- outputBuffer = new InternalNioOutputBuffer(response, maxHttpHeaderSize);
- response.setOutputBuffer(outputBuffer);
-
- initializeFilters(maxTrailerSize, allowedTrailerHeaders, maxExtensionSize, maxSwallowSize);
- }
-
-
- // ----------------------------------------------------- Instance Variables
-
- /**
- * Sendfile data.
- */
- protected NioEndpoint.SendfileData sendfileData = null;
-
-
- // --------------------------------------------------------- Public Methods
-
- /**
- * Process pipelined HTTP requests using the specified input and output
- * streams.
- *
- * @throws IOException error during an I/O operation
- */
- @Override
- public SocketState event(SocketStatus status) throws IOException {
-
- long soTimeout = endpoint.getSoTimeout();
-
- RequestInfo rp = request.getRequestProcessor();
- final NioEndpoint.KeyAttachment attach = (NioEndpoint.KeyAttachment)socketWrapper.getSocket().getAttachment();
- try {
- rp.setStage(org.apache.coyote.Constants.STAGE_SERVICE);
- if (!getAdapter().event(request, response, status)) {
- setErrorState(ErrorState.CLOSE_NOW, null);
- }
- if (!getErrorState().isError()) {
- if (attach != null) {
- attach.setComet(comet);
- if (comet) {
- Integer comettimeout = (Integer) request.getAttribute(
- org.apache.coyote.Constants.COMET_TIMEOUT_ATTR);
- if (comettimeout != null) {
- attach.setTimeout(comettimeout.longValue());
- }
- } else {
- //reset the timeout
- if (keepAlive) {
- attach.setTimeout(keepAliveTimeout);
- } else {
- attach.setTimeout(soTimeout);
- }
- }
-
- }
- }
- } catch (InterruptedIOException e) {
- setErrorState(ErrorState.CLOSE_NOW, e);
- } catch (Throwable t) {
- ExceptionUtils.handleThrowable(t);
- // 500 - Internal Server Error
- response.setStatus(500);
- setErrorState(ErrorState.CLOSE_NOW, t);
- log.error(sm.getString("http11processor.request.process"), t);
- getAdapter().log(request, response, 0);
- }
-
- rp.setStage(org.apache.coyote.Constants.STAGE_ENDED);
-
- if (getErrorState().isError() || status==SocketStatus.STOP) {
- return SocketState.CLOSED;
- } else if (!comet) {
- if (keepAlive) {
- inputBuffer.nextRequest();
- outputBuffer.nextRequest();
- return SocketState.OPEN;
- } else {
- return SocketState.CLOSED;
- }
- } else {
- return SocketState.LONG;
- }
- }
-
-
- @Override
- protected void registerForEvent(boolean read, boolean write) {
- final NioChannel socket = socketWrapper.getSocket();
-
- int interestOps = 0;
- if (read) {
- interestOps = SelectionKey.OP_READ;
- }
- if (write) {
- interestOps = interestOps | SelectionKey.OP_WRITE;
- }
- socket.getPoller().add(socket, interestOps);
- }
-
-
- @Override
- protected void resetTimeouts() {
- final NioEndpoint.KeyAttachment attach = (NioEndpoint.KeyAttachment)socketWrapper.getSocket().getAttachment();
- if (!getErrorState().isError() && attach != null &&
- asyncStateMachine.isAsyncDispatching()) {
- long soTimeout = endpoint.getSoTimeout();
-
- //reset the timeout
- if (keepAlive) {
- attach.setTimeout(keepAliveTimeout);
- } else {
- attach.setTimeout(soTimeout);
- }
- }
- }
-
-
- @Override
- protected boolean disableKeepAlive() {
- return false;
- }
-
-
- @Override
- protected void setRequestLineReadTimeout() throws IOException {
- // socket.setTimeout()
- // - timeout used by poller
- // socket.getSocket().getIOChannel().socket().setSoTimeout()
- // - timeout used for blocking reads
-
- // When entering the processing loop there will always be data to read
- // so no point changing timeouts at this point
-
- // For the second and subsequent executions of the processing loop, a
- // non-blocking read is used so again no need to set the timeouts
-
- // Because NIO supports non-blocking reading of the request line and
- // headers the timeouts need to be set when returning the socket to
- // the poller rather than here.
-
- // NO-OP
- }
-
-
- @Override
- protected boolean handleIncompleteRequestLineRead() {
- // Haven't finished reading the request so keep the socket
- // open
- openSocket = true;
- // Check to see if we have read any of the request line yet
- if (((InternalNioInputBuffer)
- inputBuffer).getParsingRequestLinePhase() < 2) {
- if (socketWrapper.getLastAccess() > -1 || keptAlive) {
- // Haven't read the request line and have previously processed a
- // request. Must be keep-alive. Make sure poller uses keepAlive.
- socketWrapper.setTimeout(endpoint.getKeepAliveTimeout());
- }
- } else {
- if (endpoint.isPaused()) {
- // Partially processed the request so need to respond
- response.setStatus(503);
- setErrorState(ErrorState.CLOSE_CLEAN, null);
- getAdapter().log(request, response, 0);
- return false;
- } else {
- // Need to keep processor associated with socket
- readComplete = false;
- // Make sure poller uses soTimeout from here onwards
- socketWrapper.setTimeout(endpoint.getSoTimeout());
- }
- }
- return true;
- }
-
-
- @Override
- protected void setSocketTimeout(int timeout) throws IOException {
- socketWrapper.getSocket().getIOChannel().socket().setSoTimeout(timeout);
- }
-
-
- @Override
- protected void setCometTimeouts(SocketWrapper<NioChannel> socketWrapper) {
- // Comet support
- SelectionKey key = socketWrapper.getSocket().getIOChannel().keyFor(
- socketWrapper.getSocket().getPoller().getSelector());
- if (key != null) {
- NioEndpoint.KeyAttachment attach = (NioEndpoint.KeyAttachment) key.attachment();
- if (attach != null) {
- attach.setComet(comet);
- if (comet) {
- Integer comettimeout = (Integer) request.getAttribute(
- org.apache.coyote.Constants.COMET_TIMEOUT_ATTR);
- if (comettimeout != null) {
- attach.setTimeout(comettimeout.longValue());
- }
- }
- }
- }
- }
-
-
- @Override
- protected boolean breakKeepAliveLoop(SocketWrapper<NioChannel> socketWrapper) {
- openSocket = keepAlive;
- // Do sendfile as needed: add socket to sendfile and end
- if (sendfileData != null && !getErrorState().isError()) {
- ((KeyAttachment) socketWrapper).setSendfileData(sendfileData);
- sendfileData.keepAlive = keepAlive;
- SelectionKey key = socketWrapper.getSocket().getIOChannel().keyFor(
- socketWrapper.getSocket().getPoller().getSelector());
- //do the first write on this thread, might as well
- switch (socketWrapper.getSocket().getPoller().processSendfile(
- key, (KeyAttachment) socketWrapper, true)) {
- case DONE:
- // If sendfile is complete, no need to break keep-alive loop
- sendfileData = null;
- return false;
- case PENDING:
- sendfileInProgress = true;
- return true;
- case ERROR:
- // Write failed
- if (log.isDebugEnabled()) {
- log.debug(sm.getString("http11processor.sendfile.error"));
- }
- setErrorState(ErrorState.CLOSE_NOW, null);
- return true;
- }
- }
- return false;
- }
-
-
- @Override
- public void recycleInternal() {
- socketWrapper = null;
- sendfileData = null;
- }
-
-
- // ----------------------------------------------------- ActionHook Methods
-
- /**
- * Send an action to the connector.
- *
- * @param actionCode Type of the action
- * @param param Action parameter
- */
- @Override
- @SuppressWarnings("incomplete-switch") // Other cases are handled by action()
- public void actionInternal(ActionCode actionCode, Object param) {
-
- switch (actionCode) {
- case REQ_HOST_ADDR_ATTRIBUTE: {
- if (socketWrapper == null) {
- request.remoteAddr().recycle();
- } else {
- if (socketWrapper.getRemoteAddr() == null) {
- InetAddress inetAddr = socketWrapper.getSocket().getIOChannel().socket().getInetAddress();
- if (inetAddr != null) {
- socketWrapper.setRemoteAddr(inetAddr.getHostAddress());
- }
- }
- request.remoteAddr().setString(socketWrapper.getRemoteAddr());
- }
- break;
- }
- case REQ_LOCAL_NAME_ATTRIBUTE: {
- if (socketWrapper == null) {
- request.localName().recycle();
- } else {
- if (socketWrapper.getLocalName() == null) {
- InetAddress inetAddr = socketWrapper.getSocket().getIOChannel().socket().getLocalAddress();
- if (inetAddr != null) {
- socketWrapper.setLocalName(inetAddr.getHostName());
- }
- }
- request.localName().setString(socketWrapper.getLocalName());
- }
- break;
- }
- case REQ_HOST_ATTRIBUTE: {
- if (socketWrapper == null) {
- request.remoteHost().recycle();
- } else {
- if (socketWrapper.getRemoteHost() == null) {
- InetAddress inetAddr = socketWrapper.getSocket().getIOChannel().socket().getInetAddress();
- if (inetAddr != null) {
- socketWrapper.setRemoteHost(inetAddr.getHostName());
- }
- if (socketWrapper.getRemoteHost() == null) {
- if (socketWrapper.getRemoteAddr() == null &&
- inetAddr != null) {
- socketWrapper.setRemoteAddr(inetAddr.getHostAddress());
- }
- if (socketWrapper.getRemoteAddr() != null) {
- socketWrapper.setRemoteHost(socketWrapper.getRemoteAddr());
- }
- }
- }
- request.remoteHost().setString(socketWrapper.getRemoteHost());
- }
- break;
- }
- case REQ_LOCAL_ADDR_ATTRIBUTE: {
- if (socketWrapper == null) {
- request.localAddr().recycle();
- } else {
- if (socketWrapper.getLocalAddr() == null) {
- socketWrapper.setLocalAddr(
- socketWrapper.getSocket().getIOChannel().socket().getLocalAddress().getHostAddress());
- }
- request.localAddr().setString(socketWrapper.getLocalAddr());
- }
- break;
- }
- case REQ_REMOTEPORT_ATTRIBUTE: {
- if (socketWrapper == null) {
- request.setRemotePort(0);
- } else {
- if (socketWrapper.getRemotePort() == -1) {
- socketWrapper.setRemotePort(socketWrapper.getSocket().getIOChannel().socket().getPort());
- }
- request.setRemotePort(socketWrapper.getRemotePort());
- }
- break;
- }
- case REQ_LOCALPORT_ATTRIBUTE: {
- if (socketWrapper == null) {
- request.setLocalPort(0);
- } else {
- if (socketWrapper.getLocalPort() == -1) {
- socketWrapper.setLocalPort(socketWrapper.getSocket().getIOChannel().socket().getLocalPort());
- }
- request.setLocalPort(socketWrapper.getLocalPort());
- }
- break;
- }
- case REQ_SSL_ATTRIBUTE: {
- try {
- if (sslSupport != null) {
- Object sslO = sslSupport.getCipherSuite();
- if (sslO != null) {
- request.setAttribute
- (SSLSupport.CIPHER_SUITE_KEY, sslO);
- }
- sslO = sslSupport.getPeerCertificateChain(false);
- if (sslO != null) {
- request.setAttribute
- (SSLSupport.CERTIFICATE_KEY, sslO);
- }
- sslO = sslSupport.getKeySize();
- if (sslO != null) {
- request.setAttribute
- (SSLSupport.KEY_SIZE_KEY, sslO);
- }
- sslO = sslSupport.getSessionId();
- if (sslO != null) {
- request.setAttribute
- (SSLSupport.SESSION_ID_KEY, sslO);
- }
- sslO = sslSupport.getProtocol();
- if (sslO != null) {
- request.setAttribute
- (SSLSupport.PROTOCOL_VERSION_KEY, sslO);
- }
- request.setAttribute(SSLSupport.SESSION_MGR, sslSupport);
- }
- } catch (Exception e) {
- log.warn(sm.getString("http11processor.socket.ssl"), e);
- }
- break;
- }
- case REQ_SSL_CERTIFICATE: {
- if (sslSupport != null) {
- /*
- * Consume and buffer the request body, so that it does not
- * interfere with the client's handshake messages
- */
- InputFilter[] inputFilters = inputBuffer.getFilters();
- ((BufferedInputFilter) inputFilters[Constants.BUFFERED_FILTER])
- .setLimit(maxSavePostSize);
- inputBuffer.addActiveFilter
- (inputFilters[Constants.BUFFERED_FILTER]);
- SecureNioChannel sslChannel = (SecureNioChannel) socketWrapper.getSocket();
- SSLEngine engine = sslChannel.getSslEngine();
- if (!engine.getNeedClientAuth()) {
- // Need to re-negotiate SSL connection
- engine.setNeedClientAuth(true);
- try {
- sslChannel.rehandshake(endpoint.getSoTimeout());
- sslSupport = ((NioEndpoint)endpoint).getHandler()
- .getSslImplementation().getSSLSupport(
- engine.getSession());
- } catch (IOException ioe) {
- log.warn(sm.getString("http11processor.socket.sslreneg",ioe));
- }
- }
-
- try {
- // use force=false since re-negotiation is handled above
- // (and it is a NO-OP for NIO anyway)
- Object sslO = sslSupport.getPeerCertificateChain(false);
- if( sslO != null) {
- request.setAttribute
- (SSLSupport.CERTIFICATE_KEY, sslO);
- }
- } catch (Exception e) {
- log.warn(sm.getString("http11processor.socket.ssl"), e);
- }
- }
- break;
- }
- case COMET_BEGIN: {
- comet = true;
- break;
- }
- case COMET_END: {
- comet = false;
- break;
- }
- case COMET_CLOSE: {
- if (socketWrapper==null || socketWrapper.getSocket().getAttachment()==null) {
- return;
- }
- RequestInfo rp = request.getRequestProcessor();
- if (rp.getStage() != org.apache.coyote.Constants.STAGE_SERVICE) {
- // Close event for this processor triggered by request
- // processing in another processor, a non-Tomcat thread (i.e.
- // an application controlled thread) or similar.
- socketWrapper.getSocket().getPoller().add(socketWrapper.getSocket());
- }
- break;
- }
- case COMET_SETTIMEOUT: {
- if (param==null) {
- return;
- }
- if (socketWrapper==null || socketWrapper.getSocket().getAttachment()==null) {
- return;
- }
- NioEndpoint.KeyAttachment attach = (NioEndpoint.KeyAttachment)socketWrapper.getSocket().getAttachment();
- long timeout = ((Long)param).longValue();
- //if we are not piggy backing on a worker thread, set the timeout
- RequestInfo rp = request.getRequestProcessor();
- if ( rp.getStage() != org.apache.coyote.Constants.STAGE_SERVICE ) {
- attach.setTimeout(timeout);
- }
- break;
- }
- }
- }
-
-
- // ------------------------------------------------------ Protected Methods
-
-
- @Override
- protected void prepareRequestInternal() {
- sendfileData = null;
- }
-
- @Override
- protected boolean prepareSendfile(OutputFilter[] outputFilters) {
- String fileName = (String) request.getAttribute(
- org.apache.coyote.Constants.SENDFILE_FILENAME_ATTR);
- if (fileName != null) {
- // No entity body sent here
- outputBuffer.addActiveFilter(outputFilters[Constants.VOID_FILTER]);
- contentDelimitation = true;
- sendfileData = new NioEndpoint.SendfileData();
- sendfileData.fileName = fileName;
- sendfileData.pos = ((Long) request.getAttribute(
- org.apache.coyote.Constants.SENDFILE_FILE_START_ATTR)).longValue();
- sendfileData.length = ((Long) request.getAttribute(
- org.apache.coyote.Constants.SENDFILE_FILE_END_ATTR)).longValue() - sendfileData.pos;
- return true;
- }
- return false;
- }
-
- @Override
- protected AbstractInputBuffer<NioChannel> getInputBuffer() {
- return inputBuffer;
- }
-
- @Override
- protected AbstractOutputBuffer<NioChannel> getOutputBuffer() {
- return outputBuffer;
- }
-
- /**
- * Set the SSL information for this HTTP connection.
- */
- @Override
- public void setSslSupport(SSLSupport sslSupport) {
- this.sslSupport = sslSupport;
- }
-
-}
diff --git a/java/org/apache/coyote/http11/Http11NioProtocol.java b/java/org/apache/coyote/http11/Http11NioProtocol.java
index f965d9a..58052d0 100644
--- a/java/org/apache/coyote/http11/Http11NioProtocol.java
+++ b/java/org/apache/coyote/http11/Http11NioProtocol.java
@@ -16,24 +16,10 @@
*/
package org.apache.coyote.http11;
-import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.nio.channels.SocketChannel;
-import java.util.Iterator;
-
-import org.apache.coyote.AbstractProtocol;
-import org.apache.coyote.Processor;
-import org.apache.coyote.UpgradeToken;
-import org.apache.coyote.http11.upgrade.NioProcessor;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
-import org.apache.tomcat.util.net.AbstractEndpoint;
import org.apache.tomcat.util.net.NioChannel;
import org.apache.tomcat.util.net.NioEndpoint;
-import org.apache.tomcat.util.net.NioEndpoint.Handler;
-import org.apache.tomcat.util.net.SSLImplementation;
-import org.apache.tomcat.util.net.SecureNioChannel;
-import org.apache.tomcat.util.net.SocketWrapper;
/**
@@ -49,237 +35,50 @@ public class Http11NioProtocol extends AbstractHttp11JsseProtocol<NioChannel> {
private static final Log log = LogFactory.getLog(Http11NioProtocol.class);
- @Override
- protected Log getLog() { return log; }
-
-
- @Override
- protected AbstractEndpoint.Handler getHandler() {
- return cHandler;
- }
-
-
public Http11NioProtocol() {
- endpoint=new NioEndpoint();
- cHandler = new Http11ConnectionHandler(this);
- ((NioEndpoint) endpoint).setHandler(cHandler);
- setSoLinger(Constants.DEFAULT_CONNECTION_LINGER);
- setSoTimeout(Constants.DEFAULT_CONNECTION_TIMEOUT);
- setTcpNoDelay(Constants.DEFAULT_TCP_NO_DELAY);
+ super(new NioEndpoint());
}
- public NioEndpoint getEndpoint() {
- return ((NioEndpoint)endpoint);
- }
-
-
- // -------------------- Properties--------------------
+ @Override
+ protected Log getLog() { return log; }
- private final Http11ConnectionHandler cHandler;
// -------------------- Pool setup --------------------
public void setPollerThreadCount(int count) {
- ((NioEndpoint)endpoint).setPollerThreadCount(count);
+ ((NioEndpoint)getEndpoint()).setPollerThreadCount(count);
}
public int getPollerThreadCount() {
- return ((NioEndpoint)endpoint).getPollerThreadCount();
+ return ((NioEndpoint)getEndpoint()).getPollerThreadCount();
}
public void setSelectorTimeout(long timeout) {
- ((NioEndpoint)endpoint).setSelectorTimeout(timeout);
+ ((NioEndpoint)getEndpoint()).setSelectorTimeout(timeout);
}
public long getSelectorTimeout() {
- return ((NioEndpoint)endpoint).getSelectorTimeout();
- }
-
- public void setAcceptorThreadPriority(int threadPriority) {
- ((NioEndpoint)endpoint).setAcceptorThreadPriority(threadPriority);
+ return ((NioEndpoint)getEndpoint()).getSelectorTimeout();
}
public void setPollerThreadPriority(int threadPriority) {
- ((NioEndpoint)endpoint).setPollerThreadPriority(threadPriority);
- }
-
- public int getAcceptorThreadPriority() {
- return ((NioEndpoint)endpoint).getAcceptorThreadPriority();
+ ((NioEndpoint)getEndpoint()).setPollerThreadPriority(threadPriority);
}
public int getPollerThreadPriority() {
- return ((NioEndpoint)endpoint).getThreadPriority();
- }
-
-
- public boolean getUseSendfile() {
- return endpoint.getUseSendfile();
+ return ((NioEndpoint)getEndpoint()).getPollerThreadPriority();
}
- public void setUseSendfile(boolean useSendfile) {
- ((NioEndpoint)endpoint).setUseSendfile(useSendfile);
- }
-
- // -------------------- Tcp setup --------------------
- public void setOomParachute(int oomParachute) {
- ((NioEndpoint)endpoint).setOomParachute(oomParachute);
- }
// ----------------------------------------------------- JMX related methods
@Override
protected String getNamePrefix() {
- return ("http-nio");
- }
-
-
- // -------------------- Connection handler --------------------
-
- protected static class Http11ConnectionHandler
- extends AbstractConnectionHandler<NioChannel,Http11NioProcessor>
- implements Handler {
-
- protected Http11NioProtocol proto;
-
- Http11ConnectionHandler(Http11NioProtocol proto) {
- this.proto = proto;
- }
-
- @Override
- protected AbstractProtocol<NioChannel> getProtocol() {
- return proto;
- }
-
- @Override
- protected Log getLog() {
- return log;
- }
-
-
- @Override
- public SSLImplementation getSslImplementation() {
- return proto.sslImplementation;
- }
-
- /**
- * Expected to be used by the Poller to release resources on socket
- * close, errors etc.
- */
- @Override
- public void release(SocketChannel socket) {
- if (log.isDebugEnabled())
- log.debug("Iterating through our connections to release a socket channel:"+socket);
- boolean released = false;
- Iterator<java.util.Map.Entry<NioChannel, Processor<NioChannel>>> it = connections.entrySet().iterator();
- while (it.hasNext()) {
- java.util.Map.Entry<NioChannel, Processor<NioChannel>> entry = it.next();
- if (entry.getKey().getIOChannel()==socket) {
- it.remove();
- Processor<NioChannel> result = entry.getValue();
- result.recycle(true);
- unregister(result);
- released = true;
- break;
- }
- }
- if (log.isDebugEnabled())
- log.debug("Done iterating through our connections to release a socket channel:"+socket +" released:"+released);
- }
-
- /**
- * Expected to be used by the Poller to release resources on socket
- * close, errors etc.
- */
- @Override
- public void release(SocketWrapper<NioChannel> socket) {
- Processor<NioChannel> processor =
- connections.remove(socket.getSocket());
- if (processor != null) {
- processor.recycle(true);
- if (!processor.isUpgrade()) {
- recycledProcessors.push(processor);
- }
- }
- }
-
-
- /**
- * Expected to be used by the handler once the processor is no longer
- * required.
- *
- * @param socket
- * @param processor
- * @param isSocketClosing Not used in HTTP
- * @param addToPoller
- */
- @Override
- public void release(SocketWrapper<NioChannel> socket,
- Processor<NioChannel> processor, boolean isSocketClosing,
- boolean addToPoller) {
- processor.recycle(isSocketClosing);
- recycledProcessors.push(processor);
- if (addToPoller) {
- // The only time this method is called with addToPoller == true
- // is when the socket is in keep-alive so set the appropriate
- // timeout.
- socket.setTimeout(getProtocol().getKeepAliveTimeout());
- socket.getSocket().getPoller().add(socket.getSocket());
- }
- }
-
-
- @Override
- protected void initSsl(SocketWrapper<NioChannel> socket,
- Processor<NioChannel> processor) {
- if (proto.isSSLEnabled() &&
- (proto.sslImplementation != null)
- && (socket.getSocket() instanceof SecureNioChannel)) {
- SecureNioChannel ch = (SecureNioChannel)socket.getSocket();
- processor.setSslSupport(
- proto.sslImplementation.getSSLSupport(
- ch.getSslEngine().getSession()));
- } else {
- processor.setSslSupport(null);
- }
-
- }
-
- @Override
- protected void longPoll(SocketWrapper<NioChannel> socket,
- Processor<NioChannel> processor) {
-
- if (processor.isAsync()) {
- socket.setAsync(true);
- } else {
- // Either:
- // - this is comet request
- // - this is an upgraded connection
- // - the request line/headers have not been completely
- // read
- socket.getSocket().getPoller().add(socket.getSocket());
- }
- }
-
- @Override
- public Http11NioProcessor createProcessor() {
- Http11NioProcessor processor = new Http11NioProcessor(
- proto.getMaxHttpHeaderSize(), (NioEndpoint)proto.endpoint,
- proto.getMaxTrailerSize(), proto.getAllowedTrailerHeadersAsSet(),
- proto.getMaxExtensionSize(), proto.getMaxSwallowSize());
- proto.configureProcessor(processor);
- register(processor);
- return processor;
- }
-
- @Override
- protected Processor<NioChannel> createUpgradeProcessor(
- SocketWrapper<NioChannel> socket, ByteBuffer leftoverInput,
- UpgradeToken upgradeToken)
- throws IOException {
- return new NioProcessor(socket, leftoverInput, upgradeToken,
- proto.getEndpoint().getSelectorPool(),
- proto.getUpgradeAsyncWriteBufferSize());
+ if (isSSLEnabled()) {
+ return ("https-" + getSslImplemenationShortName()+ "-nio");
+ } else {
+ return ("http-nio");
}
}
}
diff --git a/java/org/apache/coyote/http11/Http11OutputBuffer.java b/java/org/apache/coyote/http11/Http11OutputBuffer.java
new file mode 100644
index 0000000..29ab7f3
--- /dev/null
+++ b/java/org/apache/coyote/http11/Http11OutputBuffer.java
@@ -0,0 +1,594 @@
+/*
+ * 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.http11;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+import org.apache.coyote.ActionCode;
+import org.apache.coyote.OutputBuffer;
+import org.apache.coyote.Response;
+import org.apache.coyote.http11.filters.GzipOutputFilter;
+import org.apache.juli.logging.Log;
+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.net.SocketWrapperBase;
+import org.apache.tomcat.util.res.StringManager;
+
+/**
+ * Provides buffering for the HTTP headers (allowing responses to be reset
+ * before they have been committed) and the link to the Socket for writing the
+ * headers (once committed) and the response body. Note that buffering of the
+ * response body happens at a higher level.
+ */
+public class Http11OutputBuffer implements OutputBuffer {
+
+ // -------------------------------------------------------------- Variables
+
+ /**
+ * The string manager for this package.
+ */
+ protected static final StringManager sm = StringManager.getManager(Http11OutputBuffer.class);
+
+
+ /**
+ * Logger.
+ */
+ private static final Log log = LogFactory.getLog(Http11OutputBuffer.class);
+
+
+ // ----------------------------------------------------- Instance Variables
+
+ /**
+ * Associated Coyote response.
+ */
+ protected Response response;
+
+
+ /**
+ * Finished flag.
+ */
+ protected boolean responseFinished;
+
+
+ /**
+ * The buffer used for header composition.
+ */
+ protected final ByteBuffer headerBuffer;
+
+
+ /**
+ * Filter library for processing the response body.
+ */
+ protected OutputFilter[] filterLibrary;
+
+
+ /**
+ * Active filters for the current request.
+ */
+ protected OutputFilter[] activeFilters;
+
+
+ /**
+ * Index of the last active filter.
+ */
+ protected int lastActiveFilter;
+
+
+ /**
+ * Underlying output buffer.
+ */
+ protected OutputBuffer outputStreamOutputBuffer;
+
+
+ /**
+ * Wrapper for socket where data will be written to.
+ */
+ protected SocketWrapperBase<?> socketWrapper;
+
+
+ /**
+ * Bytes written to client for the current request
+ */
+ protected long byteCount = 0;
+
+
+ protected Http11OutputBuffer(Response response, int headerBufferSize) {
+
+ this.response = response;
+
+ headerBuffer = ByteBuffer.allocate(headerBufferSize);
+
+ filterLibrary = new OutputFilter[0];
+ activeFilters = new OutputFilter[0];
+ lastActiveFilter = -1;
+
+ responseFinished = false;
+
+ outputStreamOutputBuffer = new SocketOutputBuffer();
+ }
+
+
+ // ------------------------------------------------------------- Properties
+
+ /**
+ * Add an output filter to the filter library. Note that calling this method
+ * resets the currently active filters to none.
+ *
+ * @param filter The filter to add
+ */
+ public void addFilter(OutputFilter filter) {
+
+ OutputFilter[] newFilterLibrary = new OutputFilter[filterLibrary.length + 1];
+ for (int i = 0; i < filterLibrary.length; i++) {
+ newFilterLibrary[i] = filterLibrary[i];
+ }
+ newFilterLibrary[filterLibrary.length] = filter;
+ filterLibrary = newFilterLibrary;
+
+ activeFilters = new OutputFilter[filterLibrary.length];
+ }
+
+
+ /**
+ * Get filters.
+ *
+ * @return The current filter library containing all possible filters
+ */
+ public OutputFilter[] getFilters() {
+ return filterLibrary;
+ }
+
+
+ /**
+ * Add an output filter to the active filters for the current response.
+ * <p>
+ * The filter does not have to be present in {@link #getFilters()}.
+ * <p>
+ * A filter can only be added to a response once. If the filter has already
+ * been added to this response then this method will be a NO-OP.
+ *
+ * @param filter The filter to add
+ */
+ public void addActiveFilter(OutputFilter filter) {
+
+ if (lastActiveFilter == -1) {
+ filter.setBuffer(outputStreamOutputBuffer);
+ } else {
+ for (int i = 0; i <= lastActiveFilter; i++) {
+ if (activeFilters[i] == filter)
+ return;
+ }
+ filter.setBuffer(activeFilters[lastActiveFilter]);
+ }
+
+ activeFilters[++lastActiveFilter] = filter;
+
+ filter.setResponse(response);
+ }
+
+
+ // --------------------------------------------------- OutputBuffer Methods
+
+ /**
+ * @deprecated Unused. Will be removed in Tomcat 9. Use
+ * {@link #doWrite(ByteBuffer)}
+ */
+ @Override
+ public int doWrite(ByteChunk chunk) throws IOException {
+
+ if (!response.isCommitted()) {
+ // Send the connector a request for commit. The connector should
+ // then validate the headers, send them (using sendHeaders) and
+ // set the filters accordingly.
+ response.action(ActionCode.COMMIT, null);
+ }
+
+ if (lastActiveFilter == -1) {
+ return outputStreamOutputBuffer.doWrite(chunk);
+ } else {
+ return activeFilters[lastActiveFilter].doWrite(chunk);
+ }
+ }
+
+
+ @Override
+ public int doWrite(ByteBuffer chunk) throws IOException {
+
+ if (!response.isCommitted()) {
+ // Send the connector a request for commit. The connector should
+ // then validate the headers, send them (using sendHeaders) and
+ // set the filters accordingly.
+ response.action(ActionCode.COMMIT, null);
+ }
+
+ if (lastActiveFilter == -1) {
+ return outputStreamOutputBuffer.doWrite(chunk);
+ } else {
+ return activeFilters[lastActiveFilter].doWrite(chunk);
+ }
+ }
+
+
+ @Override
+ public long getBytesWritten() {
+ if (lastActiveFilter == -1) {
+ return outputStreamOutputBuffer.getBytesWritten();
+ } else {
+ return activeFilters[lastActiveFilter].getBytesWritten();
+ }
+ }
+
+
+ // --------------------------------------------------------- Public Methods
+
+ /**
+ * Flush the response.
+ *
+ * @throws IOException an underlying I/O error occurred
+ */
+ public void flush() throws IOException {
+ // go through the filters and if there is gzip filter
+ // invoke it to flush
+ for (int i = 0; i <= lastActiveFilter; i++) {
+ if (activeFilters[i] instanceof GzipOutputFilter) {
+ if (log.isDebugEnabled()) {
+ log.debug("Flushing the gzip filter at position " + i +
+ " of the filter chain...");
+ }
+ ((GzipOutputFilter) activeFilters[i]).flush();
+ break;
+ }
+ }
+
+ // Flush the current buffer(s)
+ flushBuffer(isBlocking());
+ }
+
+
+ /**
+ * Reset the header buffer if an error occurs during the writing of the
+ * headers so the error response can be written.
+ */
+ void resetHeaderBuffer() {
+ headerBuffer.position(0);
+ }
+
+
+ /**
+ * Recycle the output buffer. This should be called when closing the
+ * connection.
+ */
+ public void recycle() {
+ nextRequest();
+ socketWrapper = null;
+ }
+
+
+ /**
+ * End processing of current HTTP request.
+ * Note: All bytes of the current request should have been already
+ * consumed. This method only resets all the pointers so that we are ready
+ * to parse the next HTTP request.
+ */
+ public void nextRequest() {
+ // Recycle filters
+ for (int i = 0; i <= lastActiveFilter; i++) {
+ activeFilters[i].recycle();
+ }
+ // Recycle response object
+ response.recycle();
+ // Reset pointers
+ headerBuffer.position(0);
+ lastActiveFilter = -1;
+ responseFinished = false;
+ byteCount = 0;
+ }
+
+
+ /**
+ * Finish writing the response.
+ *
+ * @throws IOException an underlying I/O error occurred
+ */
+ public void finishResponse() throws IOException {
+ if (responseFinished) {
+ return;
+ }
+
+ if (lastActiveFilter != -1) {
+ activeFilters[lastActiveFilter].end();
+ }
+
+ flushBuffer(true);
+
+ responseFinished = true;
+ }
+
+
+ public void init(SocketWrapperBase<?> socketWrapper) {
+ this.socketWrapper = socketWrapper;
+ }
+
+
+ public void sendAck() throws IOException {
+ if (!response.isCommitted()) {
+ socketWrapper.write(isBlocking(), Constants.ACK_BYTES, 0, Constants.ACK_BYTES.length);
+ if (flushBuffer(true)) {
+ throw new IOException(sm.getString("iob.failedwrite.ack"));
+ }
+ }
+ }
+
+
+ /**
+ * Commit the response.
+ *
+ * @throws IOException an underlying I/O error occurred
+ */
+ protected void commit() throws IOException {
+ response.setCommitted(true);
+
+ if (headerBuffer.position() > 0) {
+ // Sending the response header buffer
+ headerBuffer.flip();
+ socketWrapper.write(isBlocking(), headerBuffer);
+ headerBuffer.position(0).limit(headerBuffer.capacity());
+ }
+ }
+
+
+ /**
+ * Send the response status line.
+ */
+ public void sendStatus() {
+ // Write protocol name
+ write(Constants.HTTP_11_BYTES);
+ headerBuffer.put(Constants.SP);
+
+ // Write status code
+ int status = response.getStatus();
+ switch (status) {
+ case 200:
+ write(Constants._200_BYTES);
+ break;
+ case 400:
+ write(Constants._400_BYTES);
+ break;
+ case 404:
+ write(Constants._404_BYTES);
+ break;
+ default:
+ write(status);
+ }
+
+ headerBuffer.put(Constants.SP);
+
+ // The reason phrase is optional but the space before it is not. Skip
+ // sending the reason phrase. Clients should ignore it (RFC 7230) and it
+ // just wastes bytes.
+
+ headerBuffer.put(Constants.CR).put(Constants.LF);
+ }
+
+
+ /**
+ * Send a header.
+ *
+ * @param name Header name
+ * @param value Header value
+ */
+ public void sendHeader(MessageBytes name, MessageBytes value) {
+ write(name);
+ headerBuffer.put(Constants.COLON).put(Constants.SP);
+ write(value);
+ headerBuffer.put(Constants.CR).put(Constants.LF);
+ }
+
+
+ /**
+ * End the header block.
+ */
+ public void endHeaders() {
+ headerBuffer.put(Constants.CR).put(Constants.LF);
+ }
+
+
+ /**
+ * This method will write the contents of the specified message bytes
+ * buffer to the output stream, without filtering. This method is meant to
+ * be used to write the response header.
+ *
+ * @param mb data to be written
+ */
+ private void write(MessageBytes mb) {
+ if (mb.getType() != MessageBytes.T_BYTES) {
+ mb.toBytes();
+ ByteChunk bc = mb.getByteChunk();
+ // Need to filter out CTLs excluding TAB. ISO-8859-1 and UTF-8
+ // values will be OK. Strings using other encodings may be
+ // corrupted.
+ byte[] buffer = bc.getBuffer();
+ for (int i = bc.getOffset(); i < bc.getLength(); i++) {
+ // byte values are signed i.e. -128 to 127
+ // The values are used unsigned. 0 to 31 are CTLs so they are
+ // filtered (apart from TAB which is 9). 127 is a control (DEL).
+ // The values 128 to 255 are all OK. Converting those to signed
+ // gives -128 to -1.
+ if ((buffer[i] > -1 && buffer[i] <= 31 && buffer[i] != 9) ||
+ buffer[i] == 127) {
+ buffer[i] = ' ';
+ }
+ }
+ }
+ write(mb.getByteChunk());
+ }
+
+
+ /**
+ * This method will write the contents of the specified byte chunk to the
+ * output stream, without filtering. This method is meant to be used to
+ * write the response header.
+ *
+ * @param bc data to be written
+ */
+ private void write(ByteChunk bc) {
+ // Writing the byte chunk to the output buffer
+ int length = bc.getLength();
+ checkLengthBeforeWrite(length);
+ headerBuffer.put(bc.getBytes(), bc.getStart(), length);
+ }
+
+
+ /**
+ * This method will write the contents of the specified byte
+ * buffer to the output stream, without filtering. This method is meant to
+ * be used to write the response header.
+ *
+ * @param b data to be written
+ */
+ public void write(byte[] b) {
+ checkLengthBeforeWrite(b.length);
+
+ // Writing the byte chunk to the output buffer
+ headerBuffer.put(b);
+ }
+
+
+ /**
+ * This method will write the specified integer to the output stream. This
+ * method is meant to be used to write the response header.
+ *
+ * @param value data to be written
+ */
+ private void write(int value) {
+ // From the Tomcat 3.3 HTTP/1.0 connector
+ String s = Integer.toString(value);
+ int len = s.length();
+ checkLengthBeforeWrite(len);
+ for (int i = 0; i < len; i++) {
+ char c = s.charAt (i);
+ headerBuffer.put((byte) c);
+ }
+ }
+
+
+ /**
+ * Checks to see if there is enough space in the buffer to write the
+ * requested number of bytes.
+ */
+ private void checkLengthBeforeWrite(int length) {
+ // "+ 4": BZ 57509. Reserve space for CR/LF/COLON/SP characters that
+ // are put directly into the buffer following this write operation.
+ if (headerBuffer.position() + length + 4 > headerBuffer.capacity()) {
+ throw new HeadersTooLargeException(
+ sm.getString("iob.responseheadertoolarge.error"));
+ }
+ }
+
+
+ //------------------------------------------------------ Non-blocking writes
+
+ /**
+ * Writes any remaining buffered data.
+ *
+ * @param block Should this method block until the buffer is empty
+ * @return <code>true</code> if data remains in the buffer (which can only
+ * happen in non-blocking mode) else <code>false</code>.
+ * @throws IOException Error writing data
+ */
+ protected boolean flushBuffer(boolean block) throws IOException {
+ return socketWrapper.flush(block);
+ }
+
+
+ /**
+ * Is standard Servlet blocking IO being used for output?
+ * @return <code>true</code> if this is blocking IO
+ */
+ protected final boolean isBlocking() {
+ return response.getWriteListener() == null;
+ }
+
+
+ protected final boolean isReady() {
+ boolean result = !hasDataToWrite();
+ if (!result) {
+ socketWrapper.registerWriteInterest();
+ }
+ return result;
+ }
+
+
+ public boolean hasDataToWrite() {
+ return socketWrapper.hasDataToWrite();
+ }
+
+
+ public void registerWriteInterest() {
+ socketWrapper.registerWriteInterest();
+ }
+
+
+ // ------------------------------------------ SocketOutputBuffer Inner Class
+
+ /**
+ * This class is an output buffer which will write data to a socket.
+ */
+ protected class SocketOutputBuffer implements OutputBuffer {
+
+ /**
+ * Write chunk.
+ *
+ * @deprecated Unused. Will be removed in Tomcat 9. Use
+ * {@link #doWrite(ByteBuffer)}
+ */
+ @Override
+ public int doWrite(ByteChunk chunk) throws IOException {
+ int len = chunk.getLength();
+ int start = chunk.getStart();
+ byte[] b = chunk.getBuffer();
+ socketWrapper.write(isBlocking(), b, start, len);
+ byteCount += len;
+ return len;
+ }
+
+ /**
+ * Write chunk.
+ */
+ @Override
+ public int doWrite(ByteBuffer chunk) throws IOException {
+ try {
+ int len = chunk.remaining();
+ socketWrapper.write(isBlocking(), chunk);
+ len -= chunk.remaining();
+ byteCount += len;
+ return len;
+ } catch (IOException ioe) {
+ response.action(ActionCode.CLOSE_NOW, ioe);
+ // Re-throw
+ throw ioe;
+ }
+ }
+
+ @Override
+ public long getBytesWritten() {
+ return byteCount;
+ }
+ }
+}
diff --git a/java/org/apache/coyote/http11/Http11Processor.java b/java/org/apache/coyote/http11/Http11Processor.java
index 3cb8c6c..c890d4b 100644
--- a/java/org/apache/coyote/http11/Http11Processor.java
+++ b/java/org/apache/coyote/http11/Http11Processor.java
@@ -16,403 +16,1632 @@
*/
package org.apache.coyote.http11;
-import java.io.EOFException;
import java.io.IOException;
-import java.net.InetAddress;
-import java.net.Socket;
+import java.io.InterruptedIOException;
+import java.nio.ByteBuffer;
+import java.util.Enumeration;
+import java.util.Locale;
+import java.util.Map;
import java.util.Set;
+import java.util.regex.Pattern;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.coyote.AbstractProcessor;
import org.apache.coyote.ActionCode;
+import org.apache.coyote.ErrorState;
+import org.apache.coyote.Request;
+import org.apache.coyote.RequestInfo;
+import org.apache.coyote.UpgradeProtocol;
+import org.apache.coyote.UpgradeToken;
import org.apache.coyote.http11.filters.BufferedInputFilter;
+import org.apache.coyote.http11.filters.ChunkedInputFilter;
+import org.apache.coyote.http11.filters.ChunkedOutputFilter;
+import org.apache.coyote.http11.filters.GzipOutputFilter;
+import org.apache.coyote.http11.filters.IdentityInputFilter;
+import org.apache.coyote.http11.filters.IdentityOutputFilter;
+import org.apache.coyote.http11.filters.SavedRequestInputFilter;
+import org.apache.coyote.http11.filters.VoidInputFilter;
+import org.apache.coyote.http11.filters.VoidOutputFilter;
+import org.apache.coyote.http11.upgrade.InternalHttpUpgradeHandler;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
+import org.apache.tomcat.util.ExceptionUtils;
+import org.apache.tomcat.util.buf.Ascii;
+import org.apache.tomcat.util.buf.ByteChunk;
+import org.apache.tomcat.util.buf.HexUtils;
+import org.apache.tomcat.util.buf.MessageBytes;
+import org.apache.tomcat.util.http.FastHttpDateFormat;
+import org.apache.tomcat.util.http.MimeHeaders;
+import org.apache.tomcat.util.log.UserDataHelper;
+import org.apache.tomcat.util.net.AbstractEndpoint;
import org.apache.tomcat.util.net.AbstractEndpoint.Handler.SocketState;
-import org.apache.tomcat.util.net.JIoEndpoint;
import org.apache.tomcat.util.net.SSLSupport;
-import org.apache.tomcat.util.net.SocketStatus;
-import org.apache.tomcat.util.net.SocketWrapper;
+import org.apache.tomcat.util.net.SendfileDataBase;
+import org.apache.tomcat.util.net.SocketWrapperBase;
+import org.apache.tomcat.util.res.StringManager;
+
+public class Http11Processor extends AbstractProcessor {
+
+ private static final Log log = LogFactory.getLog(Http11Processor.class);
+
+ /**
+ * The string manager for this package.
+ */
+ private static final StringManager sm = StringManager.getManager(Http11Processor.class);
+
+
+ private final UserDataHelper userDataHelper;
+
+ /**
+ * Input.
+ */
+ protected final Http11InputBuffer inputBuffer;
+
+
+ /**
+ * Output.
+ */
+ protected final Http11OutputBuffer outputBuffer;
+
+
+ /**
+ * 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;
+
+
+ /**
+ * Keep-alive.
+ */
+ protected volatile boolean keepAlive = true;
+
+
+ /**
+ * Flag used to indicate that the socket should be kept open (e.g. for keep
+ * alive or send file.
+ */
+ protected boolean openSocket = false;
+
+
+ /**
+ * Flag that indicates if the request headers have been completely read.
+ */
+ protected boolean readComplete = true;
+
+ /**
+ * HTTP/1.1 flag.
+ */
+ protected boolean http11 = true;
+
+
+ /**
+ * HTTP/0.9 flag.
+ */
+ protected boolean http09 = false;
+
+
+ /**
+ * Content delimiter for the request (if false, the connection will
+ * be closed at the end of the request).
+ */
+ protected boolean contentDelimitation = true;
+
+
+ /**
+ * Regular expression that defines the restricted user agents.
+ */
+ protected Pattern restrictedUserAgents = null;
+
+
+ /**
+ * Maximum number of Keep-Alive requests to honor.
+ */
+ protected int maxKeepAliveRequests = -1;
+
+
+ /**
+ * Maximum timeout on uploads. 5 minutes as in Apache HTTPD server.
+ */
+ protected int connectionUploadTimeout = 300000;
+
+
+ /**
+ * Flag to disable setting a different time-out on uploads.
+ */
+ protected boolean disableUploadTimeout = false;
+
+
+ /**
+ * Allowed compression level.
+ */
+ protected int compressionLevel = 0;
+
+
+ /**
+ * Minimum content size to make compression.
+ */
+ protected int compressionMinSize = 2048;
+
+
+ /**
+ * Max saved post size.
+ */
+ protected int maxSavePostSize = 4 * 1024;
+
+
+ /**
+ * Regular expression that defines the user agents to not use gzip with
+ */
+ protected Pattern noCompressionUserAgents = null;
+
+
+ /**
+ * List of MIMES for which compression may be enabled.
+ */
+ protected String[] compressableMimeTypes;
+
+
+ /**
+ * Host name (used to avoid useless B2C conversion on the host name).
+ */
+ protected char[] hostNameC = new char[0];
+
+
+ /**
+ * Allow a customized the server header for the tin-foil hat folks.
+ */
+ private String server = null;
+
+
+ /*
+ * Should application provider values for the HTTP Server header be removed.
+ * Note that if {@link #server} is set, any application provided value will
+ * be over-ridden.
+ */
+ private boolean serverRemoveAppProvidedValues = false;
+
+ /**
+ * Instance of the new protocol to use after the HTTP connection has been
+ * upgraded.
+ */
+ protected UpgradeToken upgradeToken = null;
+
+
+ /**
+ * Sendfile data.
+ */
+ protected SendfileDataBase sendfileData = null;
+
+
+ /**
+ * UpgradeProtocol information
+ */
+ private final Map<String,UpgradeProtocol> httpUpgradeProtocols;
+
+
+ public Http11Processor(int maxHttpHeaderSize, AbstractEndpoint<?> endpoint,int maxTrailerSize,
+ Set<String> allowedTrailerHeaders, int maxExtensionSize, int maxSwallowSize,
+ Map<String,UpgradeProtocol> httpUpgradeProtocols) {
+
+ super(endpoint);
+ userDataHelper = new UserDataHelper(log);
+
+ inputBuffer = new Http11InputBuffer(request, maxHttpHeaderSize);
+ request.setInputBuffer(inputBuffer);
+
+ outputBuffer = new Http11OutputBuffer(response, maxHttpHeaderSize);
+ response.setOutputBuffer(outputBuffer);
+
+ // Create and add the identity filters.
+ inputBuffer.addFilter(new IdentityInputFilter(maxSwallowSize));
+ outputBuffer.addFilter(new IdentityOutputFilter());
+
+ // Create and add the chunked filters.
+ inputBuffer.addFilter(new ChunkedInputFilter(maxTrailerSize, allowedTrailerHeaders,
+ maxExtensionSize, maxSwallowSize));
+ outputBuffer.addFilter(new ChunkedOutputFilter());
+
+ // Create and add the void filters.
+ inputBuffer.addFilter(new VoidInputFilter());
+ outputBuffer.addFilter(new VoidOutputFilter());
+
+ // Create and add buffered input filter
+ inputBuffer.addFilter(new BufferedInputFilter());
+
+ // Create and add the chunked filters.
+ //inputBuffer.addFilter(new GzipInputFilter());
+ outputBuffer.addFilter(new GzipOutputFilter());
+
+ pluggableFilterIndex = inputBuffer.getFilters().length;
+
+ this.httpUpgradeProtocols = httpUpgradeProtocols;
+ }
+
+
+ /**
+ * Set compression level.
+ *
+ * @param compression One of <code>on</code>, <code>force</code>,
+ * <code>off</code> or the minimum compression size in
+ * bytes which implies <code>on</code>
+ */
+ public void setCompression(String compression) {
+ if (compression.equals("on")) {
+ this.compressionLevel = 1;
+ } else if (compression.equals("force")) {
+ this.compressionLevel = 2;
+ } else if (compression.equals("off")) {
+ this.compressionLevel = 0;
+ } else {
+ try {
+ // Try to parse compression as an int, which would give the
+ // minimum compression size
+ compressionMinSize = Integer.parseInt(compression);
+ this.compressionLevel = 1;
+ } catch (Exception e) {
+ this.compressionLevel = 0;
+ }
+ }
+ }
+
+ /**
+ * Set Minimum size to trigger compression.
+ *
+ * @param compressionMinSize The minimum content length required for
+ * compression in bytes
+ */
+ public void setCompressionMinSize(int compressionMinSize) {
+ this.compressionMinSize = compressionMinSize;
+ }
+
+
+ /**
+ * Set no compression user agent pattern. Regular expression as supported
+ * by {@link Pattern}. e.g.: <code>gorilla|desesplorer|tigrus</code>.
+ *
+ * @param noCompressionUserAgents The regular expression for user agent
+ * strings for which compression should not
+ * be applied
+ */
+ public void setNoCompressionUserAgents(String noCompressionUserAgents) {
+ if (noCompressionUserAgents == null || noCompressionUserAgents.length() == 0) {
+ this.noCompressionUserAgents = null;
+ } else {
+ this.noCompressionUserAgents =
+ Pattern.compile(noCompressionUserAgents);
+ }
+ }
+
+
+ /**
+ * Set compressible mime-type list (this method is best when used with
+ * a large number of connectors, where it would be better to have all of
+ * them referenced a single array).
+ *
+ * @param compressableMimeTypes MIME types for which compression should be
+ * enabled
+ */
+ public void setCompressableMimeTypes(String[] compressableMimeTypes) {
+ this.compressableMimeTypes = compressableMimeTypes;
+ }
+
+
+ /**
+ * Return compression level.
+ *
+ * @return The current compression level in string form (off/on/force)
+ */
+ public String getCompression() {
+ switch (compressionLevel) {
+ case 0:
+ return "off";
+ case 1:
+ return "on";
+ case 2:
+ return "force";
+ }
+ return "off";
+ }
+
+
+ /**
+ * Checks if any entry in the string array starts with the specified value
+ *
+ * @param sArray the StringArray
+ * @param value string
+ */
+ private static boolean startsWithStringArray(String sArray[], String value) {
+ if (value == null) {
+ return false;
+ }
+ for (int i = 0; i < sArray.length; i++) {
+ if (value.startsWith(sArray[i])) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+
+ /**
+ * Set restricted user agent list (which will downgrade the connector
+ * to HTTP/1.0 mode). Regular expression as supported by {@link Pattern}.
+ *
+ * @param restrictedUserAgents The regular expression as supported by
+ * {@link Pattern} for the user agents e.g.
+ * "gorilla|desesplorer|tigrus"
+ */
+ public void setRestrictedUserAgents(String restrictedUserAgents) {
+ if (restrictedUserAgents == null ||
+ restrictedUserAgents.length() == 0) {
+ this.restrictedUserAgents = null;
+ } else {
+ this.restrictedUserAgents = Pattern.compile(restrictedUserAgents);
+ }
+ }
+
+
+ /**
+ * Set the maximum number of Keep-Alive requests to allow.
+ * This is to safeguard from DoS attacks. Setting to a negative
+ * value disables the limit.
+ *
+ * @param mkar The new maximum number of Keep-Alive requests allowed
+ */
+ public void setMaxKeepAliveRequests(int mkar) {
+ maxKeepAliveRequests = mkar;
+ }
+
+
+ /**
+ * Get the maximum number of Keep-Alive requests allowed. A negative value
+ * means there is no limit.
+ *
+ * @return the number of Keep-Alive requests that we will allow.
+ */
+ public int getMaxKeepAliveRequests() {
+ return maxKeepAliveRequests;
+ }
+
+
+ /**
+ * Set the maximum size of a POST which will be buffered in SSL mode.
+ * When a POST is received where the security constraints require a client
+ * certificate, the POST body needs to be buffered while an SSL handshake
+ * takes place to obtain the certificate.
+ *
+ * @param msps The maximum size POST body to buffer in bytes
+ */
+ public void setMaxSavePostSize(int msps) {
+ maxSavePostSize = msps;
+ }
+
+
+ /**
+ * Return the maximum size of a POST which will be buffered in SSL mode.
+ *
+ * @return The size in bytes
+ */
+ public int getMaxSavePostSize() {
+ return maxSavePostSize;
+ }
+
+
+ /**
+ * Set the flag to control whether a separate connection timeout is used
+ * during upload of a request body.
+ *
+ * @param isDisabled {@code true} if the separate upload timeout should be
+ * disabled
+ */
+ public void setDisableUploadTimeout(boolean isDisabled) {
+ disableUploadTimeout = isDisabled;
+ }
+
+ /**
+ * Get the flag that controls upload time-outs.
+ *
+ * @return {@code true} if the separate upload timeout is disabled
+ */
+ public boolean getDisableUploadTimeout() {
+ return disableUploadTimeout;
+ }
+
+ /**
+ * Set the upload timeout.
+ *
+ * @param timeout Upload timeout in milliseconds
+ */
+ public void setConnectionUploadTimeout(int timeout) {
+ connectionUploadTimeout = timeout ;
+ }
+
+ /**
+ * Get the upload timeout.
+ *
+ * @return Upload timeout in milliseconds
+ */
+ public int getConnectionUploadTimeout() {
+ return connectionUploadTimeout;
+ }
+
+
+ /**
+ * Set the server header name.
+ *
+ * @param server The new value to use for the server header
+ */
+ public void setServer(String server) {
+ if (server == null || server.equals("")) {
+ this.server = null;
+ } else {
+ this.server = server;
+ }
+ }
+
+
+ public void setServerRemoveAppProvidedValues(boolean serverRemoveAppProvidedValues) {
+ this.serverRemoveAppProvidedValues = serverRemoveAppProvidedValues;
+ }
+
+
+ /**
+ * Check if the resource could be compressed, if the client supports it.
+ */
+ private boolean isCompressable() {
+
+ // Check if content is not already gzipped
+ MessageBytes contentEncodingMB =
+ response.getMimeHeaders().getValue("Content-Encoding");
+
+ if ((contentEncodingMB != null)
+ && (contentEncodingMB.indexOf("gzip") != -1)) {
+ return false;
+ }
+
+ // If force mode, always compress (test purposes only)
+ if (compressionLevel == 2) {
+ return true;
+ }
+
+ // Check if sufficient length to trigger the compression
+ long contentLength = response.getContentLengthLong();
+ if ((contentLength == -1)
+ || (contentLength > compressionMinSize)) {
+ // Check for compatible MIME-TYPE
+ if (compressableMimeTypes != null) {
+ return (startsWithStringArray(compressableMimeTypes,
+ response.getContentType()));
+ }
+ }
+
+ return false;
+ }
+
+
+ /**
+ * Check if compression should be used for this resource. Already checked
+ * that the resource could be compressed if the client supports it.
+ */
+ private boolean useCompression() {
+
+ // Check if browser support gzip encoding
+ MessageBytes acceptEncodingMB =
+ request.getMimeHeaders().getValue("accept-encoding");
+
+ if ((acceptEncodingMB == null)
+ || (acceptEncodingMB.indexOf("gzip") == -1)) {
+ return false;
+ }
+
+ // If force mode, always compress (test purposes only)
+ if (compressionLevel == 2) {
+ return true;
+ }
+
+ // Check for incompatible Browser
+ if (noCompressionUserAgents != null) {
+ MessageBytes userAgentValueMB =
+ request.getMimeHeaders().getValue("user-agent");
+ if(userAgentValueMB != null) {
+ String userAgentValue = userAgentValueMB.toString();
+
+ if (noCompressionUserAgents.matcher(userAgentValue).matches()) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+
+
+ /**
+ * Specialized utility method: find a sequence of lower case bytes inside
+ * a ByteChunk.
+ */
+ private static int findBytes(ByteChunk bc, byte[] b) {
+
+ byte first = b[0];
+ byte[] buff = bc.getBuffer();
+ int start = bc.getStart();
+ int end = bc.getEnd();
+
+ // Look for first char
+ int srcEnd = b.length;
+
+ for (int i = start; i <= (end - srcEnd); i++) {
+ if (Ascii.toLower(buff[i]) != first) {
+ continue;
+ }
+ // found first char, now look for a match
+ int myPos = i+1;
+ for (int srcPos = 1; srcPos < srcEnd;) {
+ if (Ascii.toLower(buff[myPos++]) != b[srcPos++]) {
+ break;
+ }
+ if (srcPos == srcEnd) {
+ return i - start; // found it
+ }
+ }
+ }
+ return -1;
+ }
+
+
+ /**
+ * Determine if we must drop the connection because of the HTTP status
+ * code. Use the same list of codes as Apache/httpd.
+ */
+ private static boolean statusDropsConnection(int status) {
+ return status == 400 /* SC_BAD_REQUEST */ ||
+ status == 408 /* SC_REQUEST_TIMEOUT */ ||
+ status == 411 /* SC_LENGTH_REQUIRED */ ||
+ status == 413 /* SC_REQUEST_ENTITY_TOO_LARGE */ ||
+ status == 414 /* SC_REQUEST_URI_TOO_LONG */ ||
+ status == 500 /* SC_INTERNAL_SERVER_ERROR */ ||
+ status == 503 /* SC_SERVICE_UNAVAILABLE */ ||
+ status == 501 /* SC_NOT_IMPLEMENTED */;
+ }
+
+
+ /**
+ * Add an input filter to the current request. If the encoding is not
+ * supported, a 501 response will be returned to the client.
+ */
+ private void addInputFilter(InputFilter[] inputFilters, String encodingName) {
+
+ // Trim provided encoding name and convert to lower case since transfer
+ // encoding names are case insensitive. (RFC2616, section 3.6)
+ encodingName = encodingName.trim().toLowerCase(Locale.ENGLISH);
+
+ if (encodingName.equals("identity")) {
+ // Skip
+ } else if (encodingName.equals("chunked")) {
+ inputBuffer.addActiveFilter
+ (inputFilters[Constants.CHUNKED_FILTER]);
+ contentDelimitation = true;
+ } else {
+ for (int i = pluggableFilterIndex; i < inputFilters.length; i++) {
+ if (inputFilters[i].getEncodingName().toString().equals(encodingName)) {
+ inputBuffer.addActiveFilter(inputFilters[i]);
+ return;
+ }
+ }
+ // Unsupported transfer encoding
+ // 501 - Unimplemented
+ response.setStatus(501);
+ setErrorState(ErrorState.CLOSE_CLEAN, null);
+ if (log.isDebugEnabled()) {
+ log.debug(sm.getString("http11processor.request.prepare") +
+ " Unsupported transfer encoding [" + encodingName + "]");
+ }
+ }
+ }
+
+
+ @Override
+ public SocketState service(SocketWrapperBase<?> socketWrapper)
+ throws IOException {
+ RequestInfo rp = request.getRequestProcessor();
+ rp.setStage(org.apache.coyote.Constants.STAGE_PARSE);
+
+ // Setting up the I/O
+ setSocketWrapper(socketWrapper);
+ inputBuffer.init(socketWrapper);
+ outputBuffer.init(socketWrapper);
+
+ // Flags
+ keepAlive = true;
+ openSocket = false;
+ readComplete = true;
+ boolean keptAlive = false;
+
+ while (!getErrorState().isError() && keepAlive && !isAsync() &&
+ upgradeToken == null && !endpoint.isPaused()) {
+
+ // Parsing the request header
+ try {
+ if (!inputBuffer.parseRequestLine(keptAlive)) {
+ if (inputBuffer.getParsingRequestLinePhase() == -1) {
+ return SocketState.UPGRADING;
+ } else if (handleIncompleteRequestLineRead()) {
+ break;
+ }
+ }
+
+ if (endpoint.isPaused()) {
+ // 503 - Service unavailable
+ response.setStatus(503);
+ setErrorState(ErrorState.CLOSE_CLEAN, null);
+ } else {
+ keptAlive = true;
+ // Set this every time in case limit has been changed via JMX
+ request.getMimeHeaders().setLimit(endpoint.getMaxHeaderCount());
+ if (!inputBuffer.parseHeaders()) {
+ // We've read part of the request, don't recycle it
+ // instead associate it with the socket
+ openSocket = true;
+ readComplete = false;
+ break;
+ }
+ if (!disableUploadTimeout) {
+ socketWrapper.setReadTimeout(connectionUploadTimeout);
+ }
+ }
+ } catch (IOException e) {
+ if (log.isDebugEnabled()) {
+ log.debug(sm.getString("http11processor.header.parse"), e);
+ }
+ setErrorState(ErrorState.CLOSE_CONNECTION_NOW, e);
+ break;
+ } catch (Throwable t) {
+ ExceptionUtils.handleThrowable(t);
+ UserDataHelper.Mode logMode = userDataHelper.getNextMode();
+ if (logMode != null) {
+ String message = sm.getString("http11processor.header.parse");
+ switch (logMode) {
+ case INFO_THEN_DEBUG:
+ message += sm.getString("http11processor.fallToDebug");
+ //$FALL-THROUGH$
+ case INFO:
+ log.info(message, t);
+ break;
+ case DEBUG:
+ log.debug(message, t);
+ }
+ }
+ // 400 - Bad Request
+ response.setStatus(400);
+ setErrorState(ErrorState.CLOSE_CLEAN, t);
+ getAdapter().log(request, response, 0);
+ }
+
+ // Has an upgrade been requested?
+ Enumeration<String> connectionValues = request.getMimeHeaders().values("Connection");
+ boolean foundUpgrade = false;
+ while (connectionValues.hasMoreElements() && !foundUpgrade) {
+ foundUpgrade = connectionValues.nextElement().toLowerCase(
+ Locale.ENGLISH).contains("upgrade");
+ }
+
+ if (foundUpgrade) {
+ // Check the protocol
+ String requestedProtocol = request.getHeader("Upgrade");
+
+ UpgradeProtocol upgradeProtocol = httpUpgradeProtocols.get(requestedProtocol);
+ if (upgradeProtocol != null) {
+ if (upgradeProtocol.accept(request)) {
+ // TODO Figure out how to handle request bodies at this
+ // point.
+ response.setStatus(HttpServletResponse.SC_SWITCHING_PROTOCOLS);
+ response.setHeader("Connection", "Upgrade");
+ response.setHeader("Upgrade", requestedProtocol);
+ action(ActionCode.CLOSE, null);
+ getAdapter().log(request, response, 0);
+
+ InternalHttpUpgradeHandler upgradeHandler =
+ upgradeProtocol.getInternalUpgradeHandler(
+ getAdapter(), cloneRequest(request));
+ UpgradeToken upgradeToken = new UpgradeToken(upgradeHandler, null, null);
+ action(ActionCode.UPGRADE, upgradeToken);
+ return SocketState.UPGRADING;
+ }
+ }
+ }
+
+ if (!getErrorState().isError()) {
+ // Setting up filters, and parse some request headers
+ rp.setStage(org.apache.coyote.Constants.STAGE_PREPARE);
+ try {
+ prepareRequest();
+ } catch (Throwable t) {
+ ExceptionUtils.handleThrowable(t);
+ if (log.isDebugEnabled()) {
+ log.debug(sm.getString("http11processor.request.prepare"), t);
+ }
+ // 500 - Internal Server Error
+ response.setStatus(500);
+ setErrorState(ErrorState.CLOSE_CLEAN, t);
+ getAdapter().log(request, response, 0);
+ }
+ }
+ if (maxKeepAliveRequests == 1) {
+ keepAlive = false;
+ } else if (maxKeepAliveRequests > 0 &&
+ socketWrapper.decrementKeepAlive() <= 0) {
+ keepAlive = false;
+ }
-/**
- * Processes HTTP requests.
- *
- * @author Remy Maucherat
- */
-public class Http11Processor extends AbstractHttp11Processor<Socket> {
+ // Process the request in the adapter
+ if (!getErrorState().isError()) {
+ try {
+ rp.setStage(org.apache.coyote.Constants.STAGE_SERVICE);
+ getAdapter().service(request, response);
+ // Handle when the response was committed before a serious
+ // error occurred. Throwing a ServletException should both
+ // set the status to 500 and set the errorException.
+ // If we fail here, then the response is likely already
+ // committed, so we can't try and set headers.
+ if(keepAlive && !getErrorState().isError() && !isAsync() &&
+ statusDropsConnection(response.getStatus())) {
+ setErrorState(ErrorState.CLOSE_CLEAN, null);
+ }
+ } catch (InterruptedIOException e) {
+ setErrorState(ErrorState.CLOSE_CONNECTION_NOW, e);
+ } catch (HeadersTooLargeException e) {
+ log.error(sm.getString("http11processor.request.process"), e);
+ // The response should not have been committed but check it
+ // anyway to be safe
+ if (response.isCommitted()) {
+ setErrorState(ErrorState.CLOSE_NOW, e);
+ } else {
+ response.reset();
+ response.setStatus(500);
+ setErrorState(ErrorState.CLOSE_CLEAN, e);
+ response.setHeader("Connection", "close"); // TODO: Remove
+ }
+ } catch (Throwable t) {
+ ExceptionUtils.handleThrowable(t);
+ log.error(sm.getString("http11processor.request.process"), t);
+ // 500 - Internal Server Error
+ response.setStatus(500);
+ setErrorState(ErrorState.CLOSE_CLEAN, t);
+ getAdapter().log(request, response, 0);
+ }
+ }
- private static final Log log = LogFactory.getLog(Http11Processor.class);
- @Override
- protected Log getLog() {
- return log;
- }
+ // Finish the handling of the request
+ rp.setStage(org.apache.coyote.Constants.STAGE_ENDINPUT);
+ if (!isAsync()) {
+ // If this is an async request then the request ends when it has
+ // been completed. The AsyncContext is responsible for calling
+ // endRequest() in that case.
+ endRequest();
+ }
+ rp.setStage(org.apache.coyote.Constants.STAGE_ENDOUTPUT);
- // ------------------------------------------------------------ Constructor
+ // If there was an error, make sure the request is counted as
+ // and error, and update the statistics counter
+ if (getErrorState().isError()) {
+ response.setStatus(500);
+ }
+ if (!isAsync() || getErrorState().isError()) {
+ request.updateCounters();
+ if (getErrorState().isIoAllowed()) {
+ inputBuffer.nextRequest();
+ outputBuffer.nextRequest();
+ }
+ }
- public Http11Processor(int headerBufferSize, JIoEndpoint endpoint, int maxTrailerSize,
- Set<String> allowedTrailerHeaders, int maxExtensionSize, int maxSwallowSize) {
+ if (!disableUploadTimeout) {
+ int soTimeout = endpoint.getSoTimeout();
+ if(soTimeout > 0) {
+ socketWrapper.setReadTimeout(soTimeout);
+ } else {
+ socketWrapper.setReadTimeout(0);
+ }
+ }
- super(endpoint);
+ rp.setStage(org.apache.coyote.Constants.STAGE_KEEPALIVE);
- inputBuffer = new InternalInputBuffer(request, headerBufferSize);
- request.setInputBuffer(inputBuffer);
+ if (breakKeepAliveLoop(socketWrapper)) {
+ break;
+ }
+ }
- outputBuffer = new InternalOutputBuffer(response, headerBufferSize);
- response.setOutputBuffer(outputBuffer);
+ rp.setStage(org.apache.coyote.Constants.STAGE_ENDED);
+
+ if (getErrorState().isError() || endpoint.isPaused()) {
+ return SocketState.CLOSED;
+ } else if (isAsync()) {
+ return SocketState.LONG;
+ } else if (isUpgrade()) {
+ return SocketState.UPGRADING;
+ } else {
+ if (sendfileData != null) {
+ return SocketState.SENDFILE;
+ } else {
+ if (openSocket) {
+ if (readComplete) {
+ return SocketState.OPEN;
+ } else {
+ return SocketState.LONG;
+ }
+ } else {
+ return SocketState.CLOSED;
+ }
+ }
+ }
+ }
+
+
+ private Request cloneRequest(Request source) throws IOException {
+ Request dest = new Request();
+
+ // 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.getMimeHeaders().duplicate(source.getMimeHeaders());
+ dest.requestURI().duplicate(source.requestURI());
+
+ return dest;
- initializeFilters(maxTrailerSize, allowedTrailerHeaders, maxExtensionSize, maxSwallowSize);
+ }
+ private boolean handleIncompleteRequestLineRead() {
+ // Haven't finished reading the request so keep the socket
+ // open
+ openSocket = true;
+ // Check to see if we have read any of the request line yet
+ if (inputBuffer.getParsingRequestLinePhase() > 1) {
+ // Started to read request line.
+ if (endpoint.isPaused()) {
+ // Partially processed the request so need to respond
+ response.setStatus(503);
+ setErrorState(ErrorState.CLOSE_CLEAN, null);
+ getAdapter().log(request, response, 0);
+ return false;
+ } else {
+ // Need to keep processor associated with socket
+ readComplete = false;
+ }
+ }
+ return true;
}
- // ----------------------------------------------------- Instance Variables
+ private void checkExpectationAndResponseStatus() {
+ if (request.hasExpectation() &&
+ (response.getStatus() < 200 || response.getStatus() > 299)) {
+ // Client sent Expect: 100-continue but received a
+ // non-2xx final response. Disable keep-alive (if enabled)
+ // to ensure that the connection is closed. Some clients may
+ // still send the body, some may send the next request.
+ // No way to differentiate, so close the connection to
+ // force the client to send the next request.
+ inputBuffer.setSwallowInput(false);
+ keepAlive = false;
+ }
+ }
+
/**
- * SSL information.
+ * After reading the request headers, we have to setup the request filters.
*/
- protected SSLSupport sslSupport;
+ private void prepareRequest() {
+ http11 = true;
+ http09 = false;
+ contentDelimitation = false;
+ sendfileData = null;
- /**
- * The percentage of threads that have to be in use before keep-alive is
- * disabled to aid scalability.
- */
- private int disableKeepAlivePercentage = 75;
+ if (endpoint.isSSLEnabled()) {
+ request.scheme().setString("https");
+ }
+ MessageBytes protocolMB = request.protocol();
+ if (protocolMB.equals(Constants.HTTP_11)) {
+ http11 = true;
+ protocolMB.setString(Constants.HTTP_11);
+ } else if (protocolMB.equals(Constants.HTTP_10)) {
+ http11 = false;
+ keepAlive = false;
+ protocolMB.setString(Constants.HTTP_10);
+ } else if (protocolMB.equals("")) {
+ // HTTP/0.9
+ http09 = true;
+ http11 = false;
+ keepAlive = false;
+ } else {
+ // Unsupported protocol
+ http11 = false;
+ // Send 505; Unsupported HTTP version
+ response.setStatus(505);
+ setErrorState(ErrorState.CLOSE_CLEAN, null);
+ if (log.isDebugEnabled()) {
+ log.debug(sm.getString("http11processor.request.prepare")+
+ " Unsupported HTTP version \""+protocolMB+"\"");
+ }
+ }
+
+ MimeHeaders headers = request.getMimeHeaders();
+
+ // Check connection header
+ MessageBytes connectionValueMB = headers.getValue(Constants.CONNECTION);
+ if (connectionValueMB != null) {
+ ByteChunk connectionValueBC = connectionValueMB.getByteChunk();
+ if (findBytes(connectionValueBC, Constants.CLOSE_BYTES) != -1) {
+ keepAlive = false;
+ } else if (findBytes(connectionValueBC,
+ Constants.KEEPALIVE_BYTES) != -1) {
+ keepAlive = true;
+ }
+ }
+
+ if (http11) {
+ MessageBytes expectMB = headers.getValue("expect");
+ if (expectMB != null) {
+ if (expectMB.indexOfIgnoreCase("100-continue", 0) != -1) {
+ inputBuffer.setSwallowInput(false);
+ request.setExpectation(true);
+ } else {
+ response.setStatus(HttpServletResponse.SC_EXPECTATION_FAILED);
+ setErrorState(ErrorState.CLOSE_CLEAN, null);
+ }
+ }
+ }
+
+ // Check user-agent header
+ if (restrictedUserAgents != null && (http11 || keepAlive)) {
+ MessageBytes userAgentValueMB = headers.getValue("user-agent");
+ // Check in the restricted list, and adjust the http11
+ // and keepAlive flags accordingly
+ if(userAgentValueMB != null) {
+ String userAgentValue = userAgentValueMB.toString();
+ if (restrictedUserAgents != null &&
+ restrictedUserAgents.matcher(userAgentValue).matches()) {
+ http11 = false;
+ keepAlive = false;
+ }
+ }
+ }
+
+ // Check for a full URI (including protocol://host:port/)
+ ByteChunk uriBC = request.requestURI().getByteChunk();
+ if (uriBC.startsWithIgnoreCase("http", 0)) {
+
+ 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);
+ if (slashPos == -1) {
+ slashPos = uriBC.getLength();
+ // Set URI as "/"
+ request.requestURI().setBytes
+ (uriB, uriBCStart + pos + 1, 1);
+ } else {
+ request.requestURI().setBytes
+ (uriB, uriBCStart + slashPos,
+ uriBC.getLength() - slashPos);
+ }
+ MessageBytes hostMB = headers.setValue("host");
+ hostMB.setBytes(uriB, uriBCStart + pos + 3,
+ slashPos - pos - 3);
+ }
+ }
+
+ // Input filter setup
+ InputFilter[] inputFilters = inputBuffer.getFilters();
+
+ // Parse transfer-encoding header
+ if (http11) {
+ MessageBytes transferEncodingValueMB = headers.getValue("transfer-encoding");
+ if (transferEncodingValueMB != null) {
+ String transferEncodingValue = transferEncodingValueMB.toString();
+ // Parse the comma separated list. "identity" codings are ignored
+ int startPos = 0;
+ int commaPos = transferEncodingValue.indexOf(',');
+ String encodingName = null;
+ while (commaPos != -1) {
+ encodingName = transferEncodingValue.substring(startPos, commaPos);
+ addInputFilter(inputFilters, encodingName);
+ startPos = commaPos + 1;
+ commaPos = transferEncodingValue.indexOf(',', startPos);
+ }
+ encodingName = transferEncodingValue.substring(startPos);
+ addInputFilter(inputFilters, encodingName);
+ }
+ }
+
+ // Parse content-length header
+ long contentLength = request.getContentLengthLong();
+ if (contentLength >= 0) {
+ if (contentDelimitation) {
+ // contentDelimitation being true at this point indicates that
+ // chunked encoding is being used but chunked encoding should
+ // not be used with a content length. RFC 2616, section 4.4,
+ // bullet 3 states Content-Length must be ignored in this case -
+ // so remove it.
+ headers.removeHeader("content-length");
+ request.setContentLength(-1);
+ } else {
+ inputBuffer.addActiveFilter
+ (inputFilters[Constants.IDENTITY_FILTER]);
+ contentDelimitation = true;
+ }
+ }
+
+ MessageBytes valueMB = headers.getValue("host");
+
+ // Check host header
+ if (http11 && (valueMB == null)) {
+ // 400 - Bad request
+ response.setStatus(400);
+ setErrorState(ErrorState.CLOSE_CLEAN, null);
+ if (log.isDebugEnabled()) {
+ log.debug(sm.getString("http11processor.request.prepare")+
+ " host header missing");
+ }
+ }
+
+ parseHost(valueMB);
+
+ if (!contentDelimitation) {
+ // If there's no content length
+ // (broken HTTP/1.0 or HTTP/1.1), assume
+ // the client is not broken and didn't send a body
+ inputBuffer.addActiveFilter
+ (inputFilters[Constants.VOID_FILTER]);
+ contentDelimitation = true;
+ }
- // --------------------------------------------------------- Public Methods
+ if (getErrorState().isError()) {
+ getAdapter().log(request, response, 0);
+ }
+ }
/**
- * Set the SSL information for this HTTP connection.
+ * When committing the response, we have to validate the set of headers, as
+ * well as setup the response filters.
*/
@Override
- public void setSslSupport(SSLSupport sslSupport) {
- this.sslSupport = sslSupport;
- }
+ protected final void prepareResponse() throws IOException {
+ boolean entityBody = true;
+ contentDelimitation = false;
- public int getDisableKeepAlivePercentage() {
- return disableKeepAlivePercentage;
- }
+ OutputFilter[] outputFilters = outputBuffer.getFilters();
+
+ if (http09 == true) {
+ // HTTP/0.9
+ outputBuffer.addActiveFilter(outputFilters[Constants.IDENTITY_FILTER]);
+ outputBuffer.commit();
+ return;
+ }
+
+ int statusCode = response.getStatus();
+ if (statusCode < 200 || statusCode == 204 || statusCode == 205 ||
+ statusCode == 304) {
+ // No entity body
+ outputBuffer.addActiveFilter
+ (outputFilters[Constants.VOID_FILTER]);
+ entityBody = false;
+ contentDelimitation = true;
+ }
+
+ MessageBytes methodMB = request.method();
+ if (methodMB.equals("HEAD")) {
+ // No entity body
+ outputBuffer.addActiveFilter
+ (outputFilters[Constants.VOID_FILTER]);
+ contentDelimitation = true;
+ }
+
+ // Sendfile support
+ boolean sendingWithSendfile = false;
+ if (endpoint.getUseSendfile()) {
+ sendingWithSendfile = prepareSendfile(outputFilters);
+ }
+
+ // Check for compression
+ boolean isCompressable = false;
+ boolean useCompression = false;
+ if (entityBody && (compressionLevel > 0) && !sendingWithSendfile) {
+ isCompressable = isCompressable();
+ if (isCompressable) {
+ useCompression = useCompression();
+ }
+ // Change content-length to -1 to force chunking
+ if (useCompression) {
+ response.setContentLength(-1);
+ }
+ }
+
+ MimeHeaders headers = response.getMimeHeaders();
+ if (!entityBody) {
+ response.setContentLength(-1);
+ }
+ // A SC_NO_CONTENT response may include entity headers
+ if (entityBody || statusCode == HttpServletResponse.SC_NO_CONTENT) {
+ String contentType = response.getContentType();
+ if (contentType != null) {
+ headers.setValue("Content-Type").setString(contentType);
+ }
+ String contentLanguage = response.getContentLanguage();
+ if (contentLanguage != null) {
+ headers.setValue("Content-Language")
+ .setString(contentLanguage);
+ }
+ }
+
+ long contentLength = response.getContentLengthLong();
+ boolean connectionClosePresent = false;
+ if (contentLength != -1) {
+ headers.setValue("Content-Length").setLong(contentLength);
+ outputBuffer.addActiveFilter
+ (outputFilters[Constants.IDENTITY_FILTER]);
+ contentDelimitation = true;
+ } else {
+ // If the response code supports an entity body and we're on
+ // HTTP 1.1 then we chunk unless we have a Connection: close header
+ connectionClosePresent = isConnectionClose(headers);
+ if (entityBody && http11 && !connectionClosePresent) {
+ outputBuffer.addActiveFilter
+ (outputFilters[Constants.CHUNKED_FILTER]);
+ contentDelimitation = true;
+ headers.addValue(Constants.TRANSFERENCODING).setString(Constants.CHUNKED);
+ } else {
+ outputBuffer.addActiveFilter
+ (outputFilters[Constants.IDENTITY_FILTER]);
+ }
+ }
+
+ if (useCompression) {
+ outputBuffer.addActiveFilter(outputFilters[Constants.GZIP_FILTER]);
+ headers.setValue("Content-Encoding").setString("gzip");
+ }
+ // If it might be compressed, set the Vary header
+ if (isCompressable) {
+ // Make Proxies happy via Vary (from mod_deflate)
+ MessageBytes vary = headers.getValue("Vary");
+ if (vary == null) {
+ // Add a new Vary header
+ headers.setValue("Vary").setString("Accept-Encoding");
+ } else if (vary.equals("*")) {
+ // No action required
+ } else {
+ // Merge into current header
+ headers.setValue("Vary").setString(
+ vary.getString() + ",Accept-Encoding");
+ }
+ }
+
+ // Add date header unless application has already set one (e.g. in a
+ // Caching Filter)
+ if (headers.getValue("Date") == null) {
+ headers.addValue("Date").setString(
+ FastHttpDateFormat.getCurrentDate());
+ }
+
+ // FIXME: Add transfer encoding header
+
+ if ((entityBody) && (!contentDelimitation)) {
+ // Mark as close the connection after the request, and add the
+ // connection: close header
+ keepAlive = false;
+ }
+
+ // This may disabled keep-alive to check before working out the
+ // Connection header.
+ checkExpectationAndResponseStatus();
+
+ // If we know that the request is bad this early, add the
+ // Connection: close header.
+ if (keepAlive && statusDropsConnection(statusCode)) {
+ keepAlive = false;
+ }
+ if (!keepAlive) {
+ // Avoid adding the close header twice
+ if (!connectionClosePresent) {
+ headers.addValue(Constants.CONNECTION).setString(
+ Constants.CLOSE);
+ }
+ } else if (!http11 && !getErrorState().isError()) {
+ headers.addValue(Constants.CONNECTION).setString(Constants.KEEPALIVE);
+ }
+ // Add server header
+ if (server == null) {
+ if (serverRemoveAppProvidedValues) {
+ headers.removeHeader("server");
+ }
+ } else {
+ // server always overrides anything the app might set
+ headers.setValue("Server").setString(server);
+ }
+
+ // Build the response header
+ try {
+ outputBuffer.sendStatus();
+
+ int size = headers.size();
+ for (int i = 0; i < size; i++) {
+ outputBuffer.sendHeader(headers.getName(i), headers.getValue(i));
+ }
+ outputBuffer.endHeaders();
+ } catch (Throwable t) {
+ ExceptionUtils.handleThrowable(t);
+ // If something goes wrong, reset the header buffer so the error
+ // response can be written instead.
+ outputBuffer.resetHeaderBuffer();
+ throw t;
+ }
- public void setDisableKeepAlivePercentage(int disableKeepAlivePercentage) {
- this.disableKeepAlivePercentage = disableKeepAlivePercentage;
+ outputBuffer.commit();
}
+ private static boolean isConnectionClose(MimeHeaders headers) {
+ MessageBytes connection = headers.getValue(Constants.CONNECTION);
+ if (connection == null) {
+ return false;
+ }
+ return connection.equals(Constants.CLOSE);
+ }
- @Override
- protected boolean disableKeepAlive() {
- int threadRatio = -1;
- // These may return zero or negative values
- // Only calculate a thread ratio when both are >0 to ensure we get a
- // sensible result
- int maxThreads, threadsBusy;
- if ((maxThreads = endpoint.getMaxThreads()) > 0
- && (threadsBusy = endpoint.getCurrentThreadsBusy()) > 0) {
- threadRatio = (threadsBusy * 100) / maxThreads;
- }
- // Disable keep-alive if we are running low on threads
- if (threadRatio > getDisableKeepAlivePercentage()) {
+ private boolean prepareSendfile(OutputFilter[] outputFilters) {
+ String fileName = (String) request.getAttribute(
+ org.apache.coyote.Constants.SENDFILE_FILENAME_ATTR);
+ if (fileName != null) {
+ // No entity body sent here
+ outputBuffer.addActiveFilter(outputFilters[Constants.VOID_FILTER]);
+ contentDelimitation = true;
+ long pos = ((Long) request.getAttribute(
+ org.apache.coyote.Constants.SENDFILE_FILE_START_ATTR)).longValue();
+ long end = ((Long) request.getAttribute(
+ org.apache.coyote.Constants.SENDFILE_FILE_END_ATTR)).longValue();
+ sendfileData = socketWrapper.createSendfileData(fileName, pos, end - pos);
return true;
}
-
return false;
}
+ /**
+ * Parse host.
+ */
+ private void parseHost(MessageBytes valueMB) {
- @Override
- protected void setRequestLineReadTimeout() throws IOException {
-
- /*
- * When there is no data in the buffer and this is not the first
- * request on this connection and timeouts are being used the
- * first read for this request may need a different timeout to
- * take account of time spent waiting for a processing thread.
- *
- * This is a little hacky but better than exposing the socket
- * and the timeout info to the InputBuffer
- */
- if (inputBuffer.lastValid == 0 && socketWrapper.getLastAccess() > -1) {
- int firstReadTimeout;
- if (keepAliveTimeout == -1) {
- firstReadTimeout = 0;
- } else {
- long queueTime =
- System.currentTimeMillis() - socketWrapper.getLastAccess();
+ if (valueMB == null || valueMB.isNull()) {
+ // HTTP/1.0
+ // If no host header, use the port info from the endpoint
+ // The host will be obtained lazily from the socket if required
+ // using ActionCode#REQ_LOCAL_NAME_ATTRIBUTE
+ request.setServerPort(endpoint.getPort());
+ return;
+ }
- if (queueTime >= keepAliveTimeout) {
- // Queued for longer than timeout but there might be
- // data so use shortest possible timeout
- firstReadTimeout = 1;
- } else {
- // Cast is safe since queueTime must be less than
- // keepAliveTimeout which is an int
- firstReadTimeout = keepAliveTimeout - (int) queueTime;
+ ByteChunk valueBC = valueMB.getByteChunk();
+ byte[] valueB = valueBC.getBytes();
+ int valueL = valueBC.getLength();
+ int valueS = valueBC.getStart();
+ int colonPos = -1;
+ if (hostNameC.length < valueL) {
+ hostNameC = new char[valueL];
+ }
+
+ boolean ipv6 = (valueB[valueS] == '[');
+ boolean bracketClosed = false;
+ for (int i = 0; i < valueL; i++) {
+ char b = (char) valueB[i + valueS];
+ hostNameC[i] = b;
+ if (b == ']') {
+ bracketClosed = true;
+ } else if (b == ':') {
+ if (!ipv6 || bracketClosed) {
+ colonPos = i;
+ break;
}
}
- socketWrapper.getSocket().setSoTimeout(firstReadTimeout);
- // Blocking IO so fill() always blocks
- if (!inputBuffer.fill(true)) {
- throw new EOFException(sm.getString("iib.eof.error"));
- }
- // Once the first byte has been read, the standard timeout should be
- // used so restore it here.
- if (endpoint.getSoTimeout()> 0) {
- setSocketTimeout(endpoint.getSoTimeout());
- } else {
- setSocketTimeout(0);
+ }
+
+ if (colonPos < 0) {
+ request.serverName().setChars(hostNameC, 0, valueL);
+ } else {
+ request.serverName().setChars(hostNameC, 0, colonPos);
+
+ int port = 0;
+ int mult = 1;
+ for (int i = valueL - 1; i > colonPos; i--) {
+ int charValue = HexUtils.getDec(valueB[i + valueS]);
+ if (charValue == -1 || charValue > 9) {
+ // Invalid character
+ // 400 - Bad request
+ response.setStatus(400);
+ setErrorState(ErrorState.CLOSE_CLEAN, null);
+ break;
+ }
+ port = port + (charValue * mult);
+ mult = 10 * mult;
}
+ request.setServerPort(port);
}
+
}
@Override
- protected boolean handleIncompleteRequestLineRead() {
- // Not used with BIO since it uses blocking reads
+ protected boolean flushBufferedWrite() throws IOException {
+ if (outputBuffer.hasDataToWrite()) {
+ if (outputBuffer.flushBuffer(false)) {
+ // The buffer wasn't fully flushed so re-register the
+ // socket for write. Note this does not go via the
+ // Response since the write registration state at
+ // that level should remain unchanged. Once the buffer
+ // has been emptied then the code below will call
+ // Adaptor.asyncDispatch() which will enable the
+ // Response to respond to this event.
+ outputBuffer.registerWriteInterest();
+ return true;
+ }
+ }
return false;
}
@Override
- protected void setSocketTimeout(int timeout) throws IOException {
- socketWrapper.getSocket().setSoTimeout(timeout);
+ protected SocketState dispatchEndRequest() {
+ if (!keepAlive) {
+ return SocketState.CLOSED;
+ } else {
+ endRequest();
+ inputBuffer.nextRequest();
+ outputBuffer.nextRequest();
+ if (socketWrapper.isReadPending()) {
+ return SocketState.LONG;
+ } else {
+ return SocketState.OPEN;
+ }
+ }
+ }
+
+
+ @Override
+ protected Log getLog() {
+ return log;
+ }
+
+
+ /*
+ * No more input will be passed to the application. Remaining input will be
+ * swallowed or the connection dropped depending on the error and
+ * expectation status.
+ */
+ private void endRequest() {
+ if (getErrorState().isError()) {
+ // If we know we are closing the connection, don't drain
+ // input. This way uploading a 100GB file doesn't tie up the
+ // thread if the servlet has rejected it.
+ inputBuffer.setSwallowInput(false);
+ } else {
+ // Need to check this again here in case the response was
+ // committed before the error that requires the connection
+ // to be closed occurred.
+ checkExpectationAndResponseStatus();
+ }
+
+ // Finish the handling of the request
+ if (getErrorState().isIoAllowed()) {
+ try {
+ inputBuffer.endRequest();
+ } catch (IOException e) {
+ setErrorState(ErrorState.CLOSE_CONNECTION_NOW, e);
+ } catch (Throwable t) {
+ ExceptionUtils.handleThrowable(t);
+ // 500 - Internal Server Error
+ // Can't add a 500 to the access log since that has already been
+ // written in the Adapter.service method.
+ response.setStatus(500);
+ setErrorState(ErrorState.CLOSE_NOW, t);
+ log.error(sm.getString("http11processor.request.finish"), t);
+ }
+ }
+ if (getErrorState().isIoAllowed()) {
+ try {
+ action(ActionCode.COMMIT, null);
+ outputBuffer.finishResponse();
+ } catch (IOException e) {
+ setErrorState(ErrorState.CLOSE_CONNECTION_NOW, e);
+ } catch (Throwable t) {
+ ExceptionUtils.handleThrowable(t);
+ setErrorState(ErrorState.CLOSE_NOW, t);
+ log.error(sm.getString("http11processor.response.finish"), t);
+ }
+ }
}
@Override
- protected void setCometTimeouts(SocketWrapper<Socket> socketWrapper) {
- // NO-OP for BIO
+ protected final void finishResponse() throws IOException {
+ outputBuffer.finishResponse();
}
@Override
- protected boolean breakKeepAliveLoop(SocketWrapper<Socket> socketWrapper) {
- openSocket = keepAlive;
- // If we don't have a pipe-lined request allow this thread to be
- // used by another connection
- if (inputBuffer.lastValid == 0) {
- return true;
+ protected final void ack() {
+ // Acknowledge request
+ // Send a 100 status back if it makes sense (response not committed
+ // yet, and client specified an expectation for 100-continue)
+ if (!response.isCommitted() && request.hasExpectation()) {
+ inputBuffer.setSwallowInput(true);
+ try {
+ outputBuffer.sendAck();
+ } catch (IOException e) {
+ setErrorState(ErrorState.CLOSE_CONNECTION_NOW, e);
+ }
}
- return false;
}
@Override
- protected void registerForEvent(boolean read, boolean write) {
- // NO-OP for BIO
+ protected final void flush() throws IOException {
+ outputBuffer.flush();
}
+
@Override
- protected void resetTimeouts() {
- // NO-OP for BIO
+ protected final int available(boolean doRead) {
+ return inputBuffer.available(doRead);
}
@Override
- protected void recycleInternal() {
- // Recycle
- this.socketWrapper = null;
- // Recycle ssl info
- sslSupport = null;
+ protected final void setRequestBody(ByteChunk body) {
+ InputFilter savedBody = new SavedRequestInputFilter(body);
+ savedBody.setRequest(request);
+
+ Http11InputBuffer internalBuffer = (Http11InputBuffer) request.getInputBuffer();
+ internalBuffer.addActiveFilter(savedBody);
}
@Override
- public SocketState event(SocketStatus status) throws IOException {
- // Should never reach this code but in case we do...
- throw new IOException(
- sm.getString("http11processor.comet.notsupported"));
+ protected final void setSwallowResponse() {
+ outputBuffer.responseFinished = true;
}
- // ----------------------------------------------------- ActionHook Methods
+
+ @Override
+ protected final void disableSwallowRequest() {
+ inputBuffer.setSwallowInput(false);
+ }
- /**
- * Send an action to the connector.
- *
- * @param actionCode Type of the action
- * @param param Action parameter
- */
- @SuppressWarnings("incomplete-switch") // Other cases are handled by action()
@Override
- public void actionInternal(ActionCode actionCode, Object param) {
+ protected final void sslReHandShake() {
+ if (sslSupport != null) {
+ // Consume and buffer the request body, so that it does not
+ // interfere with the client's handshake messages
+ InputFilter[] inputFilters = inputBuffer.getFilters();
+ ((BufferedInputFilter) inputFilters[Constants.BUFFERED_FILTER]).setLimit(
+ maxSavePostSize);
+ inputBuffer.addActiveFilter(inputFilters[Constants.BUFFERED_FILTER]);
- switch (actionCode) {
- case REQ_SSL_ATTRIBUTE: {
try {
- if (sslSupport != null) {
- Object sslO = sslSupport.getCipherSuite();
- if (sslO != null)
- request.setAttribute
- (SSLSupport.CIPHER_SUITE_KEY, sslO);
- sslO = sslSupport.getPeerCertificateChain(false);
- if (sslO != null)
- request.setAttribute
- (SSLSupport.CERTIFICATE_KEY, sslO);
- sslO = sslSupport.getKeySize();
- if (sslO != null)
- request.setAttribute
- (SSLSupport.KEY_SIZE_KEY, sslO);
- sslO = sslSupport.getSessionId();
- if (sslO != null)
- request.setAttribute
- (SSLSupport.SESSION_ID_KEY, sslO);
- sslO = sslSupport.getProtocol();
- if (sslO != null) {
- request.setAttribute
- (SSLSupport.PROTOCOL_VERSION_KEY, sslO);
- }
- request.setAttribute(SSLSupport.SESSION_MGR, sslSupport);
- }
- } catch (Exception e) {
- log.warn(sm.getString("http11processor.socket.ssl"), e);
- }
- break;
- }
- case REQ_HOST_ADDR_ATTRIBUTE: {
- if (socketWrapper == null) {
- request.remoteAddr().recycle();
- } else {
- if (socketWrapper.getRemoteAddr() == null) {
- InetAddress inetAddr = socketWrapper.getSocket().getInetAddress();
- if (inetAddr != null) {
- socketWrapper.setRemoteAddr(inetAddr.getHostAddress());
- }
- }
- request.remoteAddr().setString(socketWrapper.getRemoteAddr());
- }
- break;
- }
- case REQ_LOCAL_NAME_ATTRIBUTE: {
- if (socketWrapper == null) {
- request.localName().recycle();
- } else {
- if (socketWrapper.getLocalName() == null) {
- InetAddress inetAddr = socketWrapper.getSocket().getLocalAddress();
- if (inetAddr != null) {
- socketWrapper.setLocalName(inetAddr.getHostName());
- }
- }
- request.localName().setString(socketWrapper.getLocalName());
- }
- break;
- }
- case REQ_HOST_ATTRIBUTE: {
- if (socketWrapper == null) {
- request.remoteHost().recycle();
- } else {
- if (socketWrapper.getRemoteHost() == null) {
- InetAddress inetAddr = socketWrapper.getSocket().getInetAddress();
- if (inetAddr != null) {
- socketWrapper.setRemoteHost(inetAddr.getHostName());
- }
- if (socketWrapper.getRemoteHost() == null) {
- if (socketWrapper.getRemoteAddr() == null &&
- inetAddr != null) {
- socketWrapper.setRemoteAddr(inetAddr.getHostAddress());
- }
- if (socketWrapper.getRemoteAddr() != null) {
- socketWrapper.setRemoteHost(socketWrapper.getRemoteAddr());
- }
- }
- }
- request.remoteHost().setString(socketWrapper.getRemoteHost());
- }
- break;
- }
- case REQ_LOCAL_ADDR_ATTRIBUTE: {
- if (socketWrapper == null) {
- request.localAddr().recycle();
- } else {
- if (socketWrapper.getLocalAddr() == null) {
- socketWrapper.setLocalAddr(
- socketWrapper.getSocket().getLocalAddress().getHostAddress());
- }
- request.localAddr().setString(socketWrapper.getLocalAddr());
- }
- break;
- }
- case REQ_REMOTEPORT_ATTRIBUTE: {
- if (socketWrapper == null) {
- request.setRemotePort(0);
- } else {
- if (socketWrapper.getRemotePort() == -1) {
- socketWrapper.setRemotePort(socketWrapper.getSocket().getPort());
- }
- request.setRemotePort(socketWrapper.getRemotePort());
- }
- break;
- }
- case REQ_LOCALPORT_ATTRIBUTE: {
- if (socketWrapper == null) {
- request.setLocalPort(0);
- } else {
- if (socketWrapper.getLocalPort() == -1) {
- socketWrapper.setLocalPort(socketWrapper.getSocket().getLocalPort());
- }
- request.setLocalPort(socketWrapper.getLocalPort());
- }
- break;
- }
- case REQ_SSL_CERTIFICATE: {
- if (sslSupport != null) {
- /*
- * Consume and buffer the request body, so that it does not
- * interfere with the client's handshake messages
- */
- InputFilter[] inputFilters = inputBuffer.getFilters();
- ((BufferedInputFilter) inputFilters[Constants.BUFFERED_FILTER])
- .setLimit(maxSavePostSize);
- inputBuffer.addActiveFilter
- (inputFilters[Constants.BUFFERED_FILTER]);
- try {
- Object sslO = sslSupport.getPeerCertificateChain(true);
- if( sslO != null) {
- request.setAttribute
- (SSLSupport.CERTIFICATE_KEY, sslO);
- }
- } catch (Exception e) {
- log.warn(sm.getString("http11processor.socket.ssl"), e);
+ socketWrapper.doClientAuth(sslSupport);
+ Object sslO = sslSupport.getPeerCertificateChain();
+ if (sslO != null) {
+ request.setAttribute(SSLSupport.CERTIFICATE_KEY, sslO);
}
+ } catch (IOException ioe) {
+ log.warn(sm.getString("http11processor.socket.ssl"), ioe);
}
- break;
- }
}
}
- // ------------------------------------------------------ Protected Methods
+ @Override
+ protected final boolean isRequestBodyFullyRead() {
+ return inputBuffer.isFinished();
+ }
@Override
- protected void prepareRequestInternal() {
- // NOOP for BIO
+ protected final void registerReadInterest() {
+ socketWrapper.registerReadInterest();
}
+
@Override
- protected boolean prepareSendfile(OutputFilter[] outputFilters) {
- // Should never, ever call this code
- Exception e = new Exception();
- log.error(sm.getString("http11processor.neverused"), e);
- return false;
+ protected final boolean isReady() {
+ return outputBuffer.isReady();
+ }
+
+
+ @Override
+ protected final void executeDispatches(SocketWrapperBase<?> wrapper) {
+ wrapper.executeNonBlockingDispatches(getIteratorAndClearDispatches());
+ }
+
+
+ @Override
+ public UpgradeToken getUpgradeToken() {
+ return upgradeToken;
+ }
+
+
+ @Override
+ protected final void doHttpUpgrade(UpgradeToken upgradeToken) {
+ this.upgradeToken = upgradeToken;
+ // Stop further HTTP output
+ outputBuffer.responseFinished = true;
}
+
@Override
- protected AbstractInputBuffer<Socket> getInputBuffer() {
- return inputBuffer;
+ public ByteBuffer getLeftoverInput() {
+ return inputBuffer.getLeftover();
}
+
@Override
- protected AbstractOutputBuffer<Socket> getOutputBuffer() {
- return outputBuffer;
+ public boolean isUpgrade() {
+ return upgradeToken != null;
}
+
/**
- * Set the socket buffer flag.
+ * Checks to see if the keep-alive loop should be broken, performing any
+ * processing (e.g. sendfile handling) that may have an impact on whether
+ * or not the keep-alive loop should be broken.
+ *
+ * @return true if the keep-alive loop should be broken
*/
+ private boolean breakKeepAliveLoop(SocketWrapperBase<?> socketWrapper) {
+ openSocket = keepAlive;
+ // Do sendfile as needed: add socket to sendfile and end
+ if (sendfileData != null && !getErrorState().isError()) {
+ sendfileData.keepAlive = keepAlive;
+ switch (socketWrapper.processSendfile(sendfileData)) {
+ case DONE:
+ // If sendfile is complete, no need to break keep-alive loop
+ sendfileData = null;
+ return false;
+ case PENDING:
+ return true;
+ case ERROR:
+ // Write failed
+ if (log.isDebugEnabled()) {
+ log.debug(sm.getString("http11processor.sendfile.error"));
+ }
+ setErrorState(ErrorState.CLOSE_CONNECTION_NOW, null);
+ return true;
+ }
+ }
+ return false;
+ }
+
+
+ @Override
+ public final void recycle() {
+ getAdapter().checkRecycled(request, response);
+ super.recycle();
+ inputBuffer.recycle();
+ outputBuffer.recycle();
+ upgradeToken = null;
+ socketWrapper = null;
+ sendfileData = null;
+ }
+
+
@Override
- public void setSocketBuffer(int socketBuffer) {
- super.setSocketBuffer(socketBuffer);
- outputBuffer.setSocketBuffer(socketBuffer);
+ public void pause() {
+ // NOOP for HTTP
}
}
diff --git a/java/org/apache/coyote/http11/Http11Protocol.java b/java/org/apache/coyote/http11/Http11Protocol.java
deleted file mode 100644
index b9db7f9..0000000
--- a/java/org/apache/coyote/http11/Http11Protocol.java
+++ /dev/null
@@ -1,191 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.coyote.http11;
-
-import java.io.IOException;
-import java.net.Socket;
-import java.nio.ByteBuffer;
-
-import org.apache.coyote.AbstractProtocol;
-import org.apache.coyote.Processor;
-import org.apache.coyote.UpgradeToken;
-import org.apache.coyote.http11.upgrade.BioProcessor;
-import org.apache.juli.logging.Log;
-import org.apache.tomcat.util.net.AbstractEndpoint;
-import org.apache.tomcat.util.net.JIoEndpoint;
-import org.apache.tomcat.util.net.JIoEndpoint.Handler;
-import org.apache.tomcat.util.net.SSLImplementation;
-import org.apache.tomcat.util.net.SocketWrapper;
-
-
-/**
- * Abstract the protocol implementation, including threading, etc.
- * Processor is single threaded and specific to stream-based protocols,
- * will not fit Jk protocols like JNI.
- *
- * @author Remy Maucherat
- * @author Costin Manolache
- */
-public class Http11Protocol extends AbstractHttp11JsseProtocol<Socket> {
-
-
- private static final org.apache.juli.logging.Log log
- = org.apache.juli.logging.LogFactory.getLog(Http11Protocol.class);
-
- @Override
- protected Log getLog() { return log; }
-
-
- @Override
- protected AbstractEndpoint.Handler getHandler() {
- return cHandler;
- }
-
-
- // ------------------------------------------------------------ Constructor
-
-
- public Http11Protocol() {
- endpoint = new JIoEndpoint();
- cHandler = new Http11ConnectionHandler(this);
- ((JIoEndpoint) endpoint).setHandler(cHandler);
- setSoLinger(Constants.DEFAULT_CONNECTION_LINGER);
- setSoTimeout(Constants.DEFAULT_CONNECTION_TIMEOUT);
- setTcpNoDelay(Constants.DEFAULT_TCP_NO_DELAY);
- }
-
-
- // ----------------------------------------------------------------- Fields
-
- private final Http11ConnectionHandler cHandler;
-
-
- // ------------------------------------------------ HTTP specific properties
- // ------------------------------------------ managed in the ProtocolHandler
-
- private int disableKeepAlivePercentage = 75;
- public int getDisableKeepAlivePercentage() {
- return disableKeepAlivePercentage;
- }
- public void setDisableKeepAlivePercentage(int disableKeepAlivePercentage) {
- if (disableKeepAlivePercentage < 0) {
- this.disableKeepAlivePercentage = 0;
- } else if (disableKeepAlivePercentage > 100) {
- this.disableKeepAlivePercentage = 100;
- } else {
- this.disableKeepAlivePercentage = disableKeepAlivePercentage;
- }
- }
-
-
- // ----------------------------------------------------- JMX related methods
-
- @Override
- protected String getNamePrefix() {
- return ("http-bio");
- }
-
-
- // ----------------------------------- Http11ConnectionHandler Inner Class
-
- protected static class Http11ConnectionHandler
- extends AbstractConnectionHandler<Socket, Http11Processor> implements Handler {
-
- protected Http11Protocol proto;
-
- Http11ConnectionHandler(Http11Protocol proto) {
- this.proto = proto;
- }
-
- @Override
- protected AbstractProtocol<Socket> getProtocol() {
- return proto;
- }
-
- @Override
- protected Log getLog() {
- return log;
- }
-
- @Override
- public SSLImplementation getSslImplementation() {
- return proto.sslImplementation;
- }
-
- /**
- * Expected to be used by the handler once the processor is no longer
- * required.
- *
- * @param socket Not used in BIO
- * @param processor
- * @param isSocketClosing Not used in HTTP
- * @param addToPoller Not used in BIO
- */
- @Override
- public void release(SocketWrapper<Socket> socket,
- Processor<Socket> processor, boolean isSocketClosing,
- boolean addToPoller) {
- processor.recycle(isSocketClosing);
- recycledProcessors.push(processor);
- }
-
- @Override
- protected void initSsl(SocketWrapper<Socket> socket,
- Processor<Socket> processor) {
- if (proto.isSSLEnabled() && (proto.sslImplementation != null)) {
- processor.setSslSupport(
- proto.sslImplementation.getSSLSupport(
- socket.getSocket()));
- } else {
- processor.setSslSupport(null);
- }
-
- }
-
- @Override
- protected void longPoll(SocketWrapper<Socket> socket,
- Processor<Socket> processor) {
- // NO-OP
- }
-
- @Override
- protected Http11Processor createProcessor() {
- Http11Processor processor = new Http11Processor(
- proto.getMaxHttpHeaderSize(), (JIoEndpoint)proto.endpoint,
- proto.getMaxTrailerSize(), proto.getAllowedTrailerHeadersAsSet(),
- proto.getMaxExtensionSize(), proto.getMaxSwallowSize());
- proto.configureProcessor(processor);
- // BIO specific configuration
- processor.setDisableKeepAlivePercentage(proto.getDisableKeepAlivePercentage());
- register(processor);
- return processor;
- }
-
- @Override
- protected Processor<Socket> createUpgradeProcessor(
- SocketWrapper<Socket> socket, ByteBuffer leftoverInput,
- UpgradeToken upgradeToken)
- throws IOException {
- return new BioProcessor(socket, leftoverInput, upgradeToken,
- proto.getUpgradeAsyncWriteBufferSize());
- }
-
- @Override
- public void beforeHandshake(SocketWrapper<Socket> socket) {
- }
- }
-}
diff --git a/java/org/apache/coyote/http11/InputFilter.java b/java/org/apache/coyote/http11/InputFilter.java
index 2fc2f7a..0d15490 100644
--- a/java/org/apache/coyote/http11/InputFilter.java
+++ b/java/org/apache/coyote/http11/InputFilter.java
@@ -30,21 +30,10 @@ import org.apache.tomcat.util.buf.ByteChunk;
*/
public interface InputFilter extends InputBuffer {
-
/**
- * Read bytes.
+ * Some filters need additional parameters from the request.
*
- * @return Number of bytes read.
- */
- @Override
- public int doRead(ByteChunk chunk, Request unused)
- throws IOException;
-
-
- /**
- * Some filters need additional parameters from the request. All the
- * necessary reading can occur in that method, as this method is called
- * after the request header processing is complete.
+ * @param request The request to be associated with this filter
*/
public void setRequest(Request request);
@@ -57,12 +46,18 @@ public interface InputFilter extends InputBuffer {
/**
* Get the name of the encoding handled by this filter.
+ *
+ * @return The encoding name as a byte chunk to facilitate comparison with
+ * the value read from the HTTP headers which will also be a
+ * ByteChunk
*/
public ByteChunk getEncodingName();
/**
* Set the next buffer in the filter pipeline.
+ *
+ * @param buffer The next buffer
*/
public void setBuffer(InputBuffer buffer);
@@ -74,19 +69,25 @@ public interface InputFilter extends InputBuffer {
* too many bytes were read. This method is allowed to use buffer.doRead
* to consume extra bytes. The result of this method can't be negative (if
* an error happens, an IOException should be thrown instead).
+ *
+ * @throws IOException If an error happens
*/
- public long end()
- throws IOException;
+ public long end() throws IOException;
/**
* Amount of bytes still available in a buffer.
+ *
+ * @return The number of bytes in the buffer
*/
public int available();
/**
* Has the request body been read fully?
+ *
+ * @return {@code true} if the request body has been fully read, otherwise
+ * {@code false}
*/
public boolean isFinished();
}
diff --git a/java/org/apache/coyote/http11/InternalAprInputBuffer.java b/java/org/apache/coyote/http11/InternalAprInputBuffer.java
deleted file mode 100644
index d1155da..0000000
--- a/java/org/apache/coyote/http11/InternalAprInputBuffer.java
+++ /dev/null
@@ -1,709 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.coyote.http11;
-
-import java.io.EOFException;
-import java.io.IOException;
-import java.net.SocketTimeoutException;
-import java.nio.ByteBuffer;
-import java.nio.charset.StandardCharsets;
-import java.util.concurrent.locks.Lock;
-import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
-
-import org.apache.coyote.InputBuffer;
-import org.apache.coyote.Request;
-import org.apache.juli.logging.Log;
-import org.apache.juli.logging.LogFactory;
-import org.apache.tomcat.jni.Socket;
-import org.apache.tomcat.jni.Status;
-import org.apache.tomcat.util.buf.ByteChunk;
-import org.apache.tomcat.util.buf.MessageBytes;
-import org.apache.tomcat.util.http.parser.HttpParser;
-import org.apache.tomcat.util.net.AbstractEndpoint;
-import org.apache.tomcat.util.net.SocketWrapper;
-
-/**
- * Implementation of InputBuffer which provides HTTP request header parsing as
- * well as transfer decoding.
- *
- * @author <a href="mailto:remm at apache.org">Remy Maucherat</a>
- */
-public class InternalAprInputBuffer extends AbstractInputBuffer<Long> {
-
- private static final Log log =
- LogFactory.getLog(InternalAprInputBuffer.class);
-
- // ----------------------------------------------------------- Constructors
-
-
- /**
- * Alternate constructor.
- */
- public InternalAprInputBuffer(Request request, int headerBufferSize) {
-
- this.request = request;
- headers = request.getMimeHeaders();
-
- buf = new byte[headerBufferSize];
- if (headerBufferSize < (8 * 1024)) {
- bbuf = ByteBuffer.allocateDirect(6 * 1500);
- } else {
- bbuf = ByteBuffer.allocateDirect((headerBufferSize / 1500 + 1) * 1500);
- }
-
- inputStreamInputBuffer = new SocketInputBuffer();
-
- filterLibrary = new InputFilter[0];
- activeFilters = new InputFilter[0];
- lastActiveFilter = -1;
-
- parsingHeader = true;
- swallowInput = true;
-
- }
-
-
- // ----------------------------------------------------- Instance Variables
-
-
- /**
- * Direct byte buffer used to perform actual reading.
- */
- private final ByteBuffer bbuf;
-
-
- /**
- * Underlying socket.
- */
- private long socket;
-
-
- private SocketWrapper<Long> wrapper;
-
-
- // --------------------------------------------------------- Public Methods
-
- /**
- * Recycle the input buffer. This should be called when closing the
- * connection.
- */
- @Override
- public void recycle() {
- socket = 0;
- wrapper = null;
- super.recycle();
- }
-
-
- /**
- * Read the request line. This function is meant to be used during the
- * HTTP request header parsing. Do NOT attempt to read the request body
- * using it.
- *
- * @throws IOException If an exception occurs during the underlying socket
- * read operations, or if the given buffer is not big enough to accommodate
- * the whole line.
- * @return true if data is properly fed; false if no data is available
- * immediately and thread should be freed
- */
- @Override
- public boolean parseRequestLine(boolean useAvailableData)
- throws IOException {
-
- int start = 0;
-
- //
- // Skipping blank lines
- //
-
- byte chr = 0;
- do {
-
- // Read new bytes if needed
- if (pos >= lastValid) {
- if (useAvailableData) {
- return false;
- }
- if (!fill(true))
- throw new EOFException(sm.getString("iib.eof.error"));
- }
- // Set the start time once we start reading data (even if it is
- // just skipping blank lines)
- if (request.getStartTime() < 0) {
- request.setStartTime(System.currentTimeMillis());
- }
- chr = buf[pos++];
- } while ((chr == Constants.CR) || (chr == Constants.LF));
-
- pos--;
-
- // Mark the current buffer position
- start = pos;
-
- if (pos >= lastValid) {
- if (useAvailableData) {
- return false;
- }
- if (!fill(true))
- throw new EOFException(sm.getString("iib.eof.error"));
- }
-
- //
- // Reading the method name
- // Method name is a token
- //
-
- boolean space = false;
-
- while (!space) {
-
- // Read new bytes if needed
- if (pos >= lastValid) {
- if (!fill(true))
- throw new EOFException(sm.getString("iib.eof.error"));
- }
-
- // Spec says method name is a token followed by a single SP but
- // also be tolerant of multiple SP and/or HT.
- if (buf[pos] == Constants.SP || buf[pos] == Constants.HT) {
- space = true;
- request.method().setBytes(buf, start, pos - start);
- } else if (!HttpParser.isToken(buf[pos])) {
- throw new IllegalArgumentException(sm.getString("iib.invalidmethod"));
- }
-
- pos++;
-
- }
-
- // Spec says single SP but also says be tolerant of multiple SP and/or HT
- while (space) {
- // Read new bytes if needed
- if (pos >= lastValid) {
- if (!fill(true))
- throw new EOFException(sm.getString("iib.eof.error"));
- }
- if (buf[pos] == Constants.SP || buf[pos] == Constants.HT) {
- pos++;
- } else {
- space = false;
- }
- }
-
- // Mark the current buffer position
- start = pos;
- int end = 0;
- int questionPos = -1;
-
- //
- // Reading the URI
- //
-
- boolean eol = false;
-
- while (!space) {
-
- // Read new bytes if needed
- if (pos >= lastValid) {
- if (!fill(true))
- throw new EOFException(sm.getString("iib.eof.error"));
- }
-
- // Spec says single SP but it also says be tolerant of HT
- if (buf[pos] == Constants.SP || buf[pos] == Constants.HT) {
- space = true;
- end = pos;
- } else if ((buf[pos] == Constants.CR)
- || (buf[pos] == Constants.LF)) {
- // HTTP/0.9 style request
- eol = true;
- space = true;
- end = pos;
- } else if ((buf[pos] == Constants.QUESTION) && (questionPos == -1)) {
- questionPos = pos;
- } else if (HttpParser.isNotRequestTarget(buf[pos])) {
- throw new IllegalArgumentException(sm.getString("iib.invalidRequestTarget"));
- }
-
- pos++;
-
- }
-
- if (questionPos >= 0) {
- request.queryString().setBytes(buf, questionPos + 1,
- end - questionPos - 1);
- request.requestURI().setBytes(buf, start, questionPos - start);
- } else {
- request.requestURI().setBytes(buf, start, end - start);
- }
-
- // Spec says single SP but also says be tolerant of multiple and/or HT
- while (space) {
- // Read new bytes if needed
- if (pos >= lastValid) {
- if (!fill(true))
- throw new EOFException(sm.getString("iib.eof.error"));
- }
- if (buf[pos] == Constants.SP || buf[pos] == Constants.HT) {
- pos++;
- } else {
- space = false;
- }
- }
-
-
- // Mark the current buffer position
- start = pos;
- end = 0;
-
- //
- // Reading the protocol
- // Protocol is always "HTTP/" DIGIT "." DIGIT
- //
-
- while (!eol) {
-
- // Read new bytes if needed
- if (pos >= lastValid) {
- if (!fill(true))
- throw new EOFException(sm.getString("iib.eof.error"));
- }
-
- if (buf[pos] == Constants.CR) {
- end = pos;
- } else if (buf[pos] == Constants.LF) {
- if (end == 0)
- end = pos;
- eol = true;
- } else if (!HttpParser.isHttpProtocol(buf[pos])) {
- throw new IllegalArgumentException(sm.getString("iib.invalidHttpProtocol"));
- }
-
- pos++;
-
- }
-
- if ((end - start) > 0) {
- request.protocol().setBytes(buf, start, end - start);
- } else {
- request.protocol().setString("");
- }
-
- return true;
-
- }
-
-
- /**
- * Parse the HTTP headers.
- */
- @Override
- public boolean parseHeaders()
- throws IOException {
- if (!parsingHeader) {
- throw new IllegalStateException(
- sm.getString("iib.parseheaders.ise.error"));
- }
-
- while (parseHeader()) {
- // Loop until there are no more headers
- }
-
- parsingHeader = false;
- end = pos;
- return true;
- }
-
-
- /**
- * Parse an HTTP header.
- *
- * @return false after reading a blank line (which indicates that the
- * HTTP header parsing is done
- */
- @SuppressWarnings("null") // headerValue cannot be null
- private boolean parseHeader()
- throws IOException {
-
- //
- // Check for blank line
- //
-
- byte chr = 0;
- while (true) {
-
- // Read new bytes if needed
- if (pos >= lastValid) {
- if (!fill(true))
- throw new EOFException(sm.getString("iib.eof.error"));
- }
-
- chr = buf[pos];
-
- if (chr == Constants.CR) {
- // Skip
- } else if (chr == Constants.LF) {
- pos++;
- return false;
- } else {
- break;
- }
-
- pos++;
-
- }
-
- // Mark the current buffer position
- int start = pos;
-
- //
- // Reading the header name
- // Header name is always US-ASCII
- //
-
- boolean colon = false;
- MessageBytes headerValue = null;
-
- while (!colon) {
-
- // Read new bytes if needed
- if (pos >= lastValid) {
- if (!fill(true))
- throw new EOFException(sm.getString("iib.eof.error"));
- }
-
- if (buf[pos] == Constants.COLON) {
- colon = true;
- headerValue = headers.addValue(buf, start, pos - start);
- } else if (!HttpParser.isToken(buf[pos])) {
- // If a non-token header is detected, skip the line and
- // ignore the header
- skipLine(start);
- return true;
- }
- chr = buf[pos];
- if ((chr >= Constants.A) && (chr <= Constants.Z)) {
- buf[pos] = (byte) (chr - Constants.LC_OFFSET);
- }
-
- pos++;
-
- }
-
- // Mark the current buffer position
- start = pos;
- int realPos = pos;
-
- //
- // Reading the header value (which can be spanned over multiple lines)
- //
-
- boolean eol = false;
- boolean validLine = true;
-
- while (validLine) {
-
- boolean space = true;
-
- // Skipping spaces
- while (space) {
-
- // Read new bytes if needed
- if (pos >= lastValid) {
- if (!fill(true))
- throw new EOFException(sm.getString("iib.eof.error"));
- }
-
- if ((buf[pos] == Constants.SP) || (buf[pos] == Constants.HT)) {
- pos++;
- } else {
- space = false;
- }
-
- }
-
- int lastSignificantChar = realPos;
-
- // Reading bytes until the end of the line
- while (!eol) {
-
- // Read new bytes if needed
- if (pos >= lastValid) {
- if (!fill(true))
- throw new EOFException(sm.getString("iib.eof.error"));
- }
-
- if (buf[pos] == Constants.CR) {
- // Skip
- } else if (buf[pos] == Constants.LF) {
- eol = true;
- } else if (buf[pos] == Constants.SP) {
- buf[realPos] = buf[pos];
- realPos++;
- } else {
- buf[realPos] = buf[pos];
- realPos++;
- lastSignificantChar = realPos;
- }
-
- pos++;
-
- }
-
- realPos = lastSignificantChar;
-
- // Checking the first character of the new line. If the character
- // is a LWS, then it's a multiline header
-
- // Read new bytes if needed
- if (pos >= lastValid) {
- if (!fill(true))
- throw new EOFException(sm.getString("iib.eof.error"));
- }
-
- chr = buf[pos];
- if ((chr != Constants.SP) && (chr != Constants.HT)) {
- validLine = false;
- } else {
- eol = false;
- // Copying one extra space in the buffer (since there must
- // be at least one space inserted between the lines)
- buf[realPos] = chr;
- realPos++;
- }
-
- }
-
- // Set the header value
- headerValue.setBytes(buf, start, realPos - start);
-
- return true;
-
- }
-
-
- private void skipLine(int start) throws IOException {
- boolean eol = false;
- int lastRealByte = start;
- if (pos - 1 > start) {
- lastRealByte = pos - 1;
- }
-
- while (!eol) {
-
- // Read new bytes if needed
- if (pos >= lastValid) {
- if (!fill(true))
- throw new EOFException(sm.getString("iib.eof.error"));
- }
-
- if (buf[pos] == Constants.CR) {
- // Skip
- } else if (buf[pos] == Constants.LF) {
- eol = true;
- } else {
- lastRealByte = pos;
- }
- pos++;
- }
-
- if (log.isDebugEnabled()) {
- log.debug(sm.getString("iib.invalidheader", new String(buf, start,
- lastRealByte - start + 1, StandardCharsets.ISO_8859_1)));
- }
- }
-
-
- // ---------------------------------------------------- InputBuffer Methods
-
-
- /**
- * Read some bytes.
- */
- @Override
- public int doRead(ByteChunk chunk, Request req)
- throws IOException {
-
- if (lastActiveFilter == -1)
- return inputStreamInputBuffer.doRead(chunk, req);
- else
- return activeFilters[lastActiveFilter].doRead(chunk,req);
-
- }
-
-
- // ------------------------------------------------------ Protected Methods
-
- @Override
- protected void init(SocketWrapper<Long> socketWrapper,
- AbstractEndpoint<Long> endpoint) throws IOException {
-
- socket = socketWrapper.getSocket().longValue();
- wrapper = socketWrapper;
- Socket.setrbb(this.socket, bbuf);
- }
-
-
- @Override
- protected boolean fill(boolean block) throws IOException {
-
- int nRead = 0;
-
- if (parsingHeader) {
- if (lastValid == buf.length) {
- throw new IllegalArgumentException
- (sm.getString("iib.requestheadertoolarge.error"));
- }
- } else {
- if (buf.length - end < 4500) {
- // In this case, the request header was really large, so we allocate a
- // brand new one; the old one will get GCed when subsequent requests
- // clear all references
- buf = new byte[buf.length];
- end = 0;
- }
- pos = end;
- lastValid = pos;
- }
-
- bbuf.clear();
-
- nRead = doReadSocket(block);
- if (nRead > 0) {
- bbuf.limit(nRead);
- bbuf.get(buf, pos, nRead);
- lastValid = pos + nRead;
- return true;
- } else if (-nRead == Status.EAGAIN) {
- return false;
- } else if (-nRead == Status.APR_EGENERAL && wrapper.isSecure()) {
- // Not entirely sure why this is necessary. Testing to date has not
- // identified any issues with this but log it so it can be tracked
- // if it is suspected of causing issues in the future.
- if (log.isDebugEnabled()) {
- log.debug(sm.getString("iib.apr.sslGeneralError",
- Long.valueOf(socket), wrapper));
- }
- return false;
- } else if ((-nRead) == Status.ETIMEDOUT || (-nRead) == Status.TIMEUP) {
- if (block) {
- throw new SocketTimeoutException(
- sm.getString("iib.readtimeout"));
- } else {
- // Attempting to read from the socket when the poller
- // has not signalled that there is data to read appears
- // to behave like a blocking read with a short timeout
- // on OSX rather than like a non-blocking read. If no
- // data is read, treat the resulting timeout like a
- // non-blocking read that returned no data.
- return false;
- }
- } else if (nRead == 0) {
- // APR_STATUS_IS_EOF, since native 1.1.22
- return false;
- } else {
- throw new IOException(sm.getString("iib.failedread.apr",
- Integer.valueOf(-nRead)));
- }
- }
-
-
- @Override
- protected final Log getLog() {
- return log;
- }
-
-
- private int doReadSocket(boolean block) {
-
- Lock readLock = wrapper.getBlockingStatusReadLock();
- WriteLock writeLock = wrapper.getBlockingStatusWriteLock();
-
- boolean readDone = false;
- int result = 0;
- readLock.lock();
- try {
- if (wrapper.getBlockingStatus() == block) {
- result = Socket.recvbb(socket, 0, buf.length - lastValid);
- readDone = true;
- }
- } finally {
- readLock.unlock();
- }
-
- if (!readDone) {
- writeLock.lock();
- try {
- wrapper.setBlockingStatus(block);
- // Set the current settings for this socket
- if (block) {
- Socket.optSet(socket, Socket.APR_SO_NONBLOCK, 0);
- } else {
- Socket.optSet(socket, Socket.APR_SO_NONBLOCK, 1);
- Socket.timeoutSet(socket, 0);
- }
- // Downgrade the lock
- readLock.lock();
- try {
- writeLock.unlock();
- result = Socket.recvbb(socket, 0, buf.length - lastValid);
- } finally {
- readLock.unlock();
- }
- } finally {
- // Should have been released above but may not have been on some
- // exception paths
- if (writeLock.isHeldByCurrentThread()) {
- writeLock.unlock();
- }
- }
- }
-
- return result;
- }
-
-
- // ------------------------------------- InputStreamInputBuffer Inner Class
-
- /**
- * This class is an input buffer which will read its data from an input
- * stream.
- */
- protected class SocketInputBuffer
- implements InputBuffer {
-
-
- /**
- * Read bytes into the specified chunk.
- */
- @Override
- public int doRead(ByteChunk chunk, Request req )
- throws IOException {
-
- if (pos >= lastValid) {
- if (!fill(true))
- return -1;
- }
-
- int length = lastValid - pos;
- chunk.setBytes(buf, pos, length);
- pos = lastValid;
-
- return (length);
- }
- }
-}
diff --git a/java/org/apache/coyote/http11/InternalAprOutputBuffer.java b/java/org/apache/coyote/http11/InternalAprOutputBuffer.java
deleted file mode 100644
index 9c4478d..0000000
--- a/java/org/apache/coyote/http11/InternalAprOutputBuffer.java
+++ /dev/null
@@ -1,365 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.coyote.http11;
-
-import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.util.Iterator;
-import java.util.concurrent.locks.Lock;
-import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
-
-import org.apache.coyote.ActionCode;
-import org.apache.coyote.ByteBufferHolder;
-import org.apache.coyote.OutputBuffer;
-import org.apache.coyote.Response;
-import org.apache.tomcat.jni.Socket;
-import org.apache.tomcat.jni.Status;
-import org.apache.tomcat.util.buf.ByteChunk;
-import org.apache.tomcat.util.net.AbstractEndpoint;
-import org.apache.tomcat.util.net.SocketWrapper;
-
-/**
- * Output buffer.
- *
- * @author <a href="mailto:remm at apache.org">Remy Maucherat</a>
- */
-public class InternalAprOutputBuffer extends AbstractOutputBuffer<Long> {
-
- // ----------------------------------------------------------- Constructors
-
- /**
- * Default constructor.
- */
- public InternalAprOutputBuffer(Response response, int headerBufferSize) {
-
- super(response, headerBufferSize);
-
- if (headerBufferSize < (8 * 1024)) {
- bbuf = ByteBuffer.allocateDirect(6 * 1500);
- } else {
- bbuf = ByteBuffer.allocateDirect((headerBufferSize / 1500 + 1) * 1500);
- }
-
- outputStreamOutputBuffer = new SocketOutputBuffer();
- }
-
-
- // ----------------------------------------------------- Instance Variables
-
-
- /**
- * Underlying socket.
- */
- private long socket;
-
-
- private SocketWrapper<Long> wrapper;
-
-
- /**
- * Direct byte buffer used for writing.
- */
- private final ByteBuffer bbuf;
-
-
- /**
- * <code>false</code> if bbuf is ready to be written to and
- * <code>true</code> is ready to be read from.
- */
- private volatile boolean flipped = false;
-
-
- private AbstractEndpoint<Long> endpoint;
-
-
- // --------------------------------------------------------- Public Methods
-
- @Override
- public void init(SocketWrapper<Long> socketWrapper,
- AbstractEndpoint<Long> endpoint) throws IOException {
-
- wrapper = socketWrapper;
- socket = socketWrapper.getSocket().longValue();
- this.endpoint = endpoint;
-
- Socket.setsbb(this.socket, bbuf);
- }
-
-
- /**
- * Recycle the output buffer. This should be called when closing the
- * connection.
- */
- @Override
- public void recycle() {
-
- super.recycle();
-
- bbuf.clear();
- flipped = false;
-
- socket = 0;
- wrapper = null;
- }
-
-
- // ------------------------------------------------ HTTP/1.1 Output Methods
-
- /**
- * Send an acknowledgment.
- */
- @Override
- public void sendAck() throws IOException {
- if (!committed) {
- if (Socket.send(socket, Constants.ACK_BYTES, 0, Constants.ACK_BYTES.length) < 0)
- throw new IOException(sm.getString("iob.failedwrite.ack"));
- }
- }
-
-
- // ------------------------------------------------------ Protected Methods
-
-
- /**
- * Commit the response.
- *
- * @throws IOException an underlying I/O error occurred
- */
- @Override
- protected void commit() throws IOException {
-
- // The response is now committed
- committed = true;
- response.setCommitted(true);
-
- if (pos > 0) {
- // Sending the response header buffer
- bbuf.put(headerBuffer, 0, pos);
- }
-
- }
-
-
- private synchronized void addToBB(byte[] buf, int offset, int length)
- throws IOException {
-
- if (length == 0) return;
-
- // If bbuf is currently being used for writes, add this data to the
- // write buffer
- if (flipped) {
- addToBuffers(buf, offset, length);
- return;
- }
-
- // Keep writing until all the data is written or a non-blocking write
- // leaves data in the buffer
- while (length > 0) {
- int thisTime = length;
- if (bbuf.position() == bbuf.capacity()) {
- if (flushBuffer(isBlocking())) {
- break;
- }
- }
- if (thisTime > bbuf.capacity() - bbuf.position()) {
- thisTime = bbuf.capacity() - bbuf.position();
- }
- bbuf.put(buf, offset, thisTime);
- length = length - thisTime;
- offset = offset + thisTime;
- }
-
- wrapper.access();
-
- if (!isBlocking() && length>0) {
- // Buffer the remaining data
- addToBuffers(buf, offset, length);
- }
- }
-
-
- private void addToBuffers(byte[] buf, int offset, int length) {
- ByteBufferHolder holder = bufferedWrites.peekLast();
- if (holder==null || holder.isFlipped() || holder.getBuf().remaining()<length) {
- ByteBuffer buffer = ByteBuffer.allocate(Math.max(bufferedWriteSize,length));
- holder = new ByteBufferHolder(buffer,false);
- bufferedWrites.add(holder);
- }
- holder.getBuf().put(buf,offset,length);
- }
-
-
- @Override
- protected synchronized boolean flushBuffer(boolean block)
- throws IOException {
-
- wrapper.access();
-
- if (hasMoreDataToFlush()) {
- writeToSocket(block);
- }
-
- if (bufferedWrites.size() > 0) {
- Iterator<ByteBufferHolder> bufIter = bufferedWrites.iterator();
- while (!hasMoreDataToFlush() && bufIter.hasNext()) {
- ByteBufferHolder buffer = bufIter.next();
- buffer.flip();
- while (!hasMoreDataToFlush() && buffer.getBuf().remaining()>0) {
- transfer(buffer.getBuf(), bbuf);
- if (buffer.getBuf().remaining() == 0) {
- bufIter.remove();
- }
- writeToSocket(block);
- //here we must break if we didn't finish the write
- }
- }
- }
-
- return hasMoreDataToFlush();
- }
-
-
- private synchronized void writeToSocket(boolean block) throws IOException {
-
- Lock readLock = wrapper.getBlockingStatusReadLock();
- WriteLock writeLock = wrapper.getBlockingStatusWriteLock();
-
- readLock.lock();
- try {
- if (wrapper.getBlockingStatus() == block) {
- writeToSocket();
- return;
- }
- } finally {
- readLock.unlock();
- }
-
- writeLock.lock();
- try {
- // Set the current settings for this socket
- wrapper.setBlockingStatus(block);
- if (block) {
- Socket.timeoutSet(socket, endpoint.getSoTimeout() * 1000);
- } else {
- Socket.timeoutSet(socket, 0);
- }
-
- // Downgrade the lock
- readLock.lock();
- try {
- writeLock.unlock();
- writeToSocket();
- } finally {
- readLock.unlock();
- }
- } finally {
- // Should have been released above but may not have been on some
- // exception paths
- if (writeLock.isHeldByCurrentThread()) {
- writeLock.unlock();
- }
- }
- }
-
- private synchronized void writeToSocket() throws IOException {
- if (!flipped) {
- flipped = true;
- bbuf.flip();
- }
-
- int written;
-
- do {
- written = Socket.sendbb(socket, bbuf.position(), bbuf.remaining());
- if (Status.APR_STATUS_IS_EAGAIN(-written)) {
- written = 0;
- } else if (written < 0) {
- throw new IOException("APR error: " + written);
- }
- bbuf.position(bbuf.position() + written);
- } while (written > 0 && bbuf.hasRemaining());
-
- if (bbuf.remaining() == 0) {
- bbuf.clear();
- flipped = false;
- }
- // If there is data left in the buffer the socket will be registered for
- // write further up the stack. This is to ensure the socket is only
- // registered for write once as both container and user code can trigger
- // write registration.
- }
-
-
- private void transfer(ByteBuffer from, ByteBuffer to) {
- int max = Math.min(from.remaining(), to.remaining());
- int fromLimit = from.limit();
- from.limit(from.position() + max);
- to.put(from);
- from.limit(fromLimit);
- }
-
-
- //-------------------------------------------------- Non-blocking IO methods
-
- @Override
- protected synchronized boolean hasMoreDataToFlush() {
- return (flipped && bbuf.remaining() > 0) ||
- (!flipped && bbuf.position() > 0);
- }
-
-
- @Override
- protected void registerWriteInterest() {
- wrapper.registerforEvent(-1, false, true);
- }
-
-
- // ----------------------------------- OutputStreamOutputBuffer Inner Class
-
- /**
- * This class is an output buffer which will write data to an output
- * stream.
- */
- protected class SocketOutputBuffer implements OutputBuffer {
-
-
- /**
- * Write chunk.
- */
- @Override
- public int doWrite(ByteChunk chunk, Response res) throws IOException {
- try {
- int len = chunk.getLength();
- int start = chunk.getStart();
- byte[] b = chunk.getBuffer();
- addToBB(b, start, len);
- byteCount += chunk.getLength();
- return chunk.getLength();
- } catch (IOException ioe) {
- response.action(ActionCode.CLOSE_NOW, ioe);
- // Re-throw
- throw ioe;
- }
- }
-
- @Override
- public long getBytesWritten() {
- return byteCount;
- }
- }
-}
diff --git a/java/org/apache/coyote/http11/InternalInputBuffer.java b/java/org/apache/coyote/http11/InternalInputBuffer.java
deleted file mode 100644
index f55517d..0000000
--- a/java/org/apache/coyote/http11/InternalInputBuffer.java
+++ /dev/null
@@ -1,592 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.coyote.http11;
-
-import java.io.EOFException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.Socket;
-import java.nio.charset.StandardCharsets;
-
-import org.apache.coyote.InputBuffer;
-import org.apache.coyote.Request;
-import org.apache.juli.logging.Log;
-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.parser.HttpParser;
-import org.apache.tomcat.util.net.AbstractEndpoint;
-import org.apache.tomcat.util.net.SocketWrapper;
-
-/**
- * Implementation of InputBuffer which provides HTTP request header parsing as
- * well as transfer decoding.
- *
- * @author <a href="mailto:remm at apache.org">Remy Maucherat</a>
- */
-public class InternalInputBuffer extends AbstractInputBuffer<Socket> {
-
- private static final Log log = LogFactory.getLog(InternalInputBuffer.class);
-
-
- /**
- * Underlying input stream.
- */
- private InputStream inputStream;
-
-
- /**
- * Default constructor.
- */
- public InternalInputBuffer(Request request, int headerBufferSize) {
-
- this.request = request;
- headers = request.getMimeHeaders();
-
- buf = new byte[headerBufferSize];
-
- inputStreamInputBuffer = new InputStreamInputBuffer();
-
- filterLibrary = new InputFilter[0];
- activeFilters = new InputFilter[0];
- lastActiveFilter = -1;
-
- parsingHeader = true;
- swallowInput = true;
-
- }
-
-
- /**
- * Data is always available for blocking IO (if you wait long enough) so
- * return a value of 1. Note that the actual value is never used it is only
- * tested for == 0 or > 0.
- */
- @Override
- public int available(boolean read) {
- return 1;
- }
-
-
- /**
- * Read the request line. This function is meant to be used during the
- * HTTP request header parsing. Do NOT attempt to read the request body
- * using it.
- *
- * @throws IOException If an exception occurs during the underlying socket
- * read operations, or if the given buffer is not big enough to accommodate
- * the whole line.
- */
- @Override
- public boolean parseRequestLine(boolean useAvailableDataOnly)
-
- throws IOException {
-
- int start = 0;
-
- //
- // Skipping blank lines
- //
-
- byte chr = 0;
- do {
-
- // Read new bytes if needed
- if (pos >= lastValid) {
- if (!fill())
- throw new EOFException(sm.getString("iib.eof.error"));
- }
- // Set the start time once we start reading data (even if it is
- // just skipping blank lines)
- if (request.getStartTime() < 0) {
- request.setStartTime(System.currentTimeMillis());
- }
- chr = buf[pos++];
- } while ((chr == Constants.CR) || (chr == Constants.LF));
-
- pos--;
-
- // Mark the current buffer position
- start = pos;
-
- //
- // Reading the method name
- // Method name is a token
- //
-
- boolean space = false;
-
- while (!space) {
-
- // Read new bytes if needed
- if (pos >= lastValid) {
- if (!fill())
- throw new EOFException(sm.getString("iib.eof.error"));
- }
-
- // Spec says method name is a token followed by a single SP but
- // also be tolerant of multiple SP and/or HT.
- if (buf[pos] == Constants.SP || buf[pos] == Constants.HT) {
- space = true;
- request.method().setBytes(buf, start, pos - start);
- } else if (!HttpParser.isToken(buf[pos])) {
- throw new IllegalArgumentException(sm.getString("iib.invalidmethod"));
- }
-
- pos++;
-
- }
-
- // Spec says single SP but also be tolerant of multiple SP and/or HT
- while (space) {
- // Read new bytes if needed
- if (pos >= lastValid) {
- if (!fill())
- throw new EOFException(sm.getString("iib.eof.error"));
- }
- if (buf[pos] == Constants.SP || buf[pos] == Constants.HT) {
- pos++;
- } else {
- space = false;
- }
- }
-
- // Mark the current buffer position
- start = pos;
- int end = 0;
- int questionPos = -1;
-
- //
- // Reading the URI
- //
-
- boolean eol = false;
-
- while (!space) {
-
- // Read new bytes if needed
- if (pos >= lastValid) {
- if (!fill())
- throw new EOFException(sm.getString("iib.eof.error"));
- }
-
- // Spec says single SP but it also says be tolerant of HT
- if (buf[pos] == Constants.SP || buf[pos] == Constants.HT) {
- space = true;
- end = pos;
- } else if ((buf[pos] == Constants.CR)
- || (buf[pos] == Constants.LF)) {
- // HTTP/0.9 style request
- eol = true;
- space = true;
- end = pos;
- } else if ((buf[pos] == Constants.QUESTION) && (questionPos == -1)) {
- questionPos = pos;
- } else if (HttpParser.isNotRequestTarget(buf[pos])) {
- throw new IllegalArgumentException(sm.getString("iib.invalidRequestTarget"));
- }
-
- pos++;
-
- }
-
- if (questionPos >= 0) {
- request.queryString().setBytes(buf, questionPos + 1,
- end - questionPos - 1);
- request.requestURI().setBytes(buf, start, questionPos - start);
- } else {
- request.requestURI().setBytes(buf, start, end - start);
- }
-
- // Spec says single SP but also says be tolerant of multiple SP and/or HT
- while (space) {
- // Read new bytes if needed
- if (pos >= lastValid) {
- if (!fill())
- throw new EOFException(sm.getString("iib.eof.error"));
- }
- if (buf[pos] == Constants.SP || buf[pos] == Constants.HT) {
- pos++;
- } else {
- space = false;
- }
- }
-
- // Mark the current buffer position
- start = pos;
- end = 0;
-
- //
- // Reading the protocol
- // Protocol is always "HTTP/" DIGIT "." DIGIT
- //
- while (!eol) {
-
- // Read new bytes if needed
- if (pos >= lastValid) {
- if (!fill())
- throw new EOFException(sm.getString("iib.eof.error"));
- }
-
- if (buf[pos] == Constants.CR) {
- end = pos;
- } else if (buf[pos] == Constants.LF) {
- if (end == 0)
- end = pos;
- eol = true;
- } else if (!HttpParser.isHttpProtocol(buf[pos])) {
- throw new IllegalArgumentException(sm.getString("iib.invalidHttpProtocol"));
- }
-
- pos++;
-
- }
-
- if ((end - start) > 0) {
- request.protocol().setBytes(buf, start, end - start);
- } else {
- request.protocol().setString("");
- }
-
- return true;
-
- }
-
-
- /**
- * Parse the HTTP headers.
- */
- @Override
- public boolean parseHeaders()
- throws IOException {
- if (!parsingHeader) {
- throw new IllegalStateException(
- sm.getString("iib.parseheaders.ise.error"));
- }
-
- while (parseHeader()) {
- // Loop until we run out of headers
- }
-
- parsingHeader = false;
- end = pos;
- return true;
- }
-
-
- /**
- * Parse an HTTP header.
- *
- * @return false after reading a blank line (which indicates that the
- * HTTP header parsing is done
- */
- @SuppressWarnings("null") // headerValue cannot be null
- private boolean parseHeader()
- throws IOException {
-
- //
- // Check for blank line
- //
-
- byte chr = 0;
- while (true) {
-
- // Read new bytes if needed
- if (pos >= lastValid) {
- if (!fill())
- throw new EOFException(sm.getString("iib.eof.error"));
- }
-
- chr = buf[pos];
-
- if (chr == Constants.CR) {
- // Skip
- } else if (chr == Constants.LF) {
- pos++;
- return false;
- } else {
- break;
- }
-
- pos++;
-
- }
-
- // Mark the current buffer position
- int start = pos;
-
- //
- // Reading the header name
- // Header name is always US-ASCII
- //
-
- boolean colon = false;
- MessageBytes headerValue = null;
-
- while (!colon) {
-
- // Read new bytes if needed
- if (pos >= lastValid) {
- if (!fill())
- throw new EOFException(sm.getString("iib.eof.error"));
- }
-
- if (buf[pos] == Constants.COLON) {
- colon = true;
- headerValue = headers.addValue(buf, start, pos - start);
- } else if (!HttpParser.isToken(buf[pos])) {
- // If a non-token header is detected, skip the line and
- // ignore the header
- skipLine(start);
- return true;
- }
-
- chr = buf[pos];
- if ((chr >= Constants.A) && (chr <= Constants.Z)) {
- buf[pos] = (byte) (chr - Constants.LC_OFFSET);
- }
-
- pos++;
-
- }
-
- // Mark the current buffer position
- start = pos;
- int realPos = pos;
-
- //
- // Reading the header value (which can be spanned over multiple lines)
- //
-
- boolean eol = false;
- boolean validLine = true;
-
- while (validLine) {
-
- boolean space = true;
-
- // Skipping spaces
- while (space) {
-
- // Read new bytes if needed
- if (pos >= lastValid) {
- if (!fill())
- throw new EOFException(sm.getString("iib.eof.error"));
- }
-
- if ((buf[pos] == Constants.SP) || (buf[pos] == Constants.HT)) {
- pos++;
- } else {
- space = false;
- }
-
- }
-
- int lastSignificantChar = realPos;
-
- // Reading bytes until the end of the line
- while (!eol) {
-
- // Read new bytes if needed
- if (pos >= lastValid) {
- if (!fill())
- throw new EOFException(sm.getString("iib.eof.error"));
- }
-
- if (buf[pos] == Constants.CR) {
- // Skip
- } else if (buf[pos] == Constants.LF) {
- eol = true;
- } else if (buf[pos] == Constants.SP) {
- buf[realPos] = buf[pos];
- realPos++;
- } else {
- buf[realPos] = buf[pos];
- realPos++;
- lastSignificantChar = realPos;
- }
-
- pos++;
-
- }
-
- realPos = lastSignificantChar;
-
- // Checking the first character of the new line. If the character
- // is a LWS, then it's a multiline header
-
- // Read new bytes if needed
- if (pos >= lastValid) {
- if (!fill())
- throw new EOFException(sm.getString("iib.eof.error"));
- }
-
- chr = buf[pos];
- if ((chr != Constants.SP) && (chr != Constants.HT)) {
- validLine = false;
- } else {
- eol = false;
- // Copying one extra space in the buffer (since there must
- // be at least one space inserted between the lines)
- buf[realPos] = chr;
- realPos++;
- }
-
- }
-
- // Set the header value
- headerValue.setBytes(buf, start, realPos - start);
-
- return true;
-
- }
-
-
- @Override
- public void recycle() {
- super.recycle();
- inputStream = null;
- }
-
-
- // ------------------------------------------------------ Protected Methods
-
-
- @Override
- protected void init(SocketWrapper<Socket> socketWrapper,
- AbstractEndpoint<Socket> endpoint) throws IOException {
- inputStream = socketWrapper.getSocket().getInputStream();
- }
-
-
-
- private void skipLine(int start) throws IOException {
- boolean eol = false;
- int lastRealByte = start;
- if (pos - 1 > start) {
- lastRealByte = pos - 1;
- }
-
- while (!eol) {
-
- // Read new bytes if needed
- if (pos >= lastValid) {
- if (!fill())
- throw new EOFException(sm.getString("iib.eof.error"));
- }
-
- if (buf[pos] == Constants.CR) {
- // Skip
- } else if (buf[pos] == Constants.LF) {
- eol = true;
- } else {
- lastRealByte = pos;
- }
- pos++;
- }
-
- if (log.isDebugEnabled()) {
- log.debug(sm.getString("iib.invalidheader", new String(buf, start,
- lastRealByte - start + 1, StandardCharsets.ISO_8859_1)));
- }
- }
-
- /**
- * Fill the internal buffer using data from the underlying input stream.
- *
- * @return false if at end of stream
- */
- protected boolean fill() throws IOException {
- return fill(true);
- }
-
- @Override
- protected boolean fill(boolean block) throws IOException {
-
- int nRead = 0;
-
- if (parsingHeader) {
-
- if (lastValid == buf.length) {
- throw new IllegalArgumentException
- (sm.getString("iib.requestheadertoolarge.error"));
- }
-
- nRead = inputStream.read(buf, pos, buf.length - lastValid);
- if (nRead > 0) {
- lastValid = pos + nRead;
- }
-
- } else {
-
- if (buf.length - end < 4500) {
- // In this case, the request header was really large, so we allocate a
- // brand new one; the old one will get GCed when subsequent requests
- // clear all references
- buf = new byte[buf.length];
- end = 0;
- }
- pos = end;
- lastValid = pos;
- nRead = inputStream.read(buf, pos, buf.length - lastValid);
- if (nRead > 0) {
- lastValid = pos + nRead;
- }
-
- }
-
- return (nRead > 0);
-
- }
-
-
- @Override
- protected final Log getLog() {
- return log;
- }
-
-
- // ------------------------------------- InputStreamInputBuffer Inner Class
-
- /**
- * This class is an input buffer which will read its data from an input
- * stream.
- */
- protected class InputStreamInputBuffer
- implements InputBuffer {
-
-
- /**
- * Read bytes into the specified chunk.
- */
- @Override
- public int doRead(ByteChunk chunk, Request req )
- throws IOException {
-
- if (pos >= lastValid) {
- if (!fill())
- return -1;
- }
-
- int length = lastValid - pos;
- chunk.setBytes(buf, pos, length);
- pos = lastValid;
-
- return (length);
- }
- }
-}
diff --git a/java/org/apache/coyote/http11/InternalNio2InputBuffer.java b/java/org/apache/coyote/http11/InternalNio2InputBuffer.java
deleted file mode 100644
index a022942..0000000
--- a/java/org/apache/coyote/http11/InternalNio2InputBuffer.java
+++ /dev/null
@@ -1,352 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.coyote.http11;
-
-import java.io.EOFException;
-import java.io.IOException;
-import java.net.SocketTimeoutException;
-import java.nio.ByteBuffer;
-import java.nio.channels.CompletionHandler;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.Future;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-
-import javax.servlet.RequestDispatcher;
-
-import org.apache.coyote.InputBuffer;
-import org.apache.coyote.Request;
-import org.apache.juli.logging.Log;
-import org.apache.juli.logging.LogFactory;
-import org.apache.tomcat.util.buf.ByteChunk;
-import org.apache.tomcat.util.net.AbstractEndpoint;
-import org.apache.tomcat.util.net.Nio2Channel;
-import org.apache.tomcat.util.net.Nio2Endpoint;
-import org.apache.tomcat.util.net.SocketStatus;
-import org.apache.tomcat.util.net.SocketWrapper;
-
-/**
- * Output buffer implementation for NIO2.
- */
-public class InternalNio2InputBuffer extends AbstractNioInputBuffer<Nio2Channel> {
-
- private static final Log log =
- LogFactory.getLog(InternalNio2InputBuffer.class);
-
- // ----------------------------------------------------------- Constructors
-
-
- public InternalNio2InputBuffer(Request request, int headerBufferSize) {
- super(request, headerBufferSize);
- inputStreamInputBuffer = new SocketInputBuffer();
- }
-
- /**
- * Underlying socket.
- */
- private SocketWrapper<Nio2Channel> socket;
-
- /**
- * Track write interest
- */
- protected volatile boolean interest = false;
-
- /**
- * The completion handler used for asynchronous read operations
- */
- private CompletionHandler<Integer, SocketWrapper<Nio2Channel>> completionHandler;
-
- /**
- * The associated endpoint.
- */
- protected AbstractEndpoint<Nio2Channel> endpoint = null;
-
- /**
- * Read pending flag.
- */
- protected volatile boolean readPending = false;
-
- /**
- * Exception that occurred during writing.
- */
- protected IOException e = null;
-
- /**
- * Track if the byte buffer is flipped
- */
- protected volatile boolean flipped = false;
-
- // --------------------------------------------------------- Public Methods
-
- @Override
- protected final Log getLog() {
- return log;
- }
-
-
- /**
- * Recycle the input buffer. This should be called when closing the
- * connection.
- */
- @Override
- public void recycle() {
- super.recycle();
- socket = null;
- readPending = false;
- flipped = false;
- interest = false;
- e = null;
- }
-
-
- /**
- * End processing of current HTTP request.
- * Note: All bytes of the current request should have been already
- * consumed. This method only resets all the pointers so that we are ready
- * to parse the next HTTP request.
- */
- @Override
- public void nextRequest() {
- super.nextRequest();
- interest = false;
- }
-
- public boolean isPending() {
- return readPending;
- }
-
- // ------------------------------------------------------ Protected Methods
-
- @Override
- protected void init(SocketWrapper<Nio2Channel> socketWrapper,
- AbstractEndpoint<Nio2Channel> associatedEndpoint) throws IOException {
-
- endpoint = associatedEndpoint;
- socket = socketWrapper;
- if (socket == null) {
- // Socket has been closed in another thread
- throw new IOException(sm.getString("iib.socketClosed"));
- }
- socketReadBufferSize =
- socket.getSocket().getBufHandler().getReadBuffer().capacity();
-
- int bufLength = headerBufferSize + socketReadBufferSize;
- if (buf == null || buf.length < bufLength) {
- buf = new byte[bufLength];
- }
-
- // Initialize the completion handler
- this.completionHandler = new CompletionHandler<Integer, SocketWrapper<Nio2Channel>>() {
-
- @Override
- public void completed(Integer nBytes, SocketWrapper<Nio2Channel> attachment) {
- boolean notify = false;
- synchronized (completionHandler) {
- if (nBytes.intValue() < 0) {
- failed(new EOFException(sm.getString("iib.eof.error")), attachment);
- } else {
- readPending = false;
- if ((request.getReadListener() == null || interest) && !Nio2Endpoint.isInline()) {
- interest = false;
- notify = true;
- }
- }
- }
- if (notify) {
- endpoint.processSocket(attachment, SocketStatus.OPEN_READ, false);
- }
- }
-
- @Override
- public void failed(Throwable exc, SocketWrapper<Nio2Channel> attachment) {
- attachment.setError(true);
- if (exc instanceof IOException) {
- e = (IOException) exc;
- } else {
- e = new IOException(exc);
- }
- request.setAttribute(RequestDispatcher.ERROR_EXCEPTION, e);
- readPending = false;
- endpoint.processSocket(attachment, SocketStatus.OPEN_READ, true);
- }
- };
- }
-
- @Override
- protected boolean fill(boolean block) throws IOException, EOFException {
- if (e != null) {
- throw e;
- }
- if (parsingHeader) {
- if (lastValid > headerBufferSize) {
- throw new IllegalArgumentException
- (sm.getString("iib.requestheadertoolarge.error"));
- }
- } else {
- lastValid = pos = end;
- }
- // Now fill the internal buffer
- int nRead = 0;
- ByteBuffer byteBuffer = socket.getSocket().getBufHandler().getReadBuffer();
- if (block) {
- if (!flipped) {
- byteBuffer.flip();
- flipped = true;
- }
- int nBytes = byteBuffer.remaining();
- // This case can happen when a blocking read follows a non blocking
- // fill that completed asynchronously
- if (nBytes > 0) {
- expand(nBytes + pos);
- byteBuffer.get(buf, pos, nBytes);
- lastValid = pos + nBytes;
- byteBuffer.clear();
- flipped = false;
- return true;
- } else {
- byteBuffer.clear();
- flipped = false;
- Future<Integer> future = null;
- try {
- future = socket.getSocket().read(byteBuffer);
- nRead = future.get(socket.getTimeout(), TimeUnit.MILLISECONDS).intValue();
- } catch (ExecutionException e) {
- if (e.getCause() instanceof IOException) {
- throw (IOException) e.getCause();
- } else {
- throw new IOException(e);
- }
- } catch (InterruptedException e) {
- throw new IOException(e);
- } catch (TimeoutException e) {
- future.cancel(true);
- throw new SocketTimeoutException();
- }
- if (nRead > 0) {
- if (!flipped) {
- byteBuffer.flip();
- flipped = true;
- }
- expand(nRead + pos);
- byteBuffer.get(buf, pos, nRead);
- lastValid = pos + nRead;
- return true;
- } else if (nRead == -1) {
- //return false;
- throw new EOFException(sm.getString("iib.eof.error"));
- } else {
- return false;
- }
- }
- } else {
- synchronized (completionHandler) {
- if (!readPending) {
- if (!flipped) {
- byteBuffer.flip();
- flipped = true;
- }
- int nBytes = byteBuffer.remaining();
- if (nBytes > 0) {
- expand(nBytes + pos);
- byteBuffer.get(buf, pos, nBytes);
- lastValid = pos + nBytes;
- byteBuffer.clear();
- flipped = false;
- } else {
- byteBuffer.clear();
- flipped = false;
- readPending = true;
- Nio2Endpoint.startInline();
- socket.getSocket().read(byteBuffer, socket.getTimeout(),
- TimeUnit.MILLISECONDS, socket, completionHandler);
- Nio2Endpoint.endInline();
- // Return the number of bytes that have been placed into the buffer
- if (!readPending) {
- // If the completion handler completed immediately
- if (!flipped) {
- byteBuffer.flip();
- flipped = true;
- }
- nBytes = byteBuffer.remaining();
- if (nBytes > 0) {
- expand(nBytes + pos);
- byteBuffer.get(buf, pos, nBytes);
- lastValid = pos + nBytes;
- }
- byteBuffer.clear();
- flipped = false;
- }
- }
- return (lastValid - pos) > 0;
- } else {
- return false;
- }
- }
- }
- }
-
-
- public void registerReadInterest() {
- synchronized (completionHandler) {
- if (readPending) {
- interest = true;
- } else {
- // If no read is pending, notify
- endpoint.processSocket(socket, SocketStatus.OPEN_READ, true);
- }
- }
- }
-
-
- // ------------------------------------- InputStreamInputBuffer Inner Class
-
-
- /**
- * This class is an input buffer which will read its data from an input
- * stream.
- */
- protected class SocketInputBuffer
- implements InputBuffer {
-
-
- /**
- * Read bytes into the specified chunk.
- */
- @Override
- public int doRead(ByteChunk chunk, Request req )
- throws IOException {
-
- if (pos >= lastValid) {
- if (!fill(true)) //read body, must be blocking, as the thread is inside the app
- return -1;
- }
- if (isBlocking()) {
- int length = lastValid - pos;
- chunk.setBytes(buf, pos, length);
- pos = lastValid;
- return (length);
- } else {
- synchronized (completionHandler) {
- int length = lastValid - pos;
- chunk.setBytes(buf, pos, length);
- pos = lastValid;
- return (length);
- }
- }
- }
- }
-}
diff --git a/java/org/apache/coyote/http11/InternalNio2OutputBuffer.java b/java/org/apache/coyote/http11/InternalNio2OutputBuffer.java
deleted file mode 100644
index 1db1227..0000000
--- a/java/org/apache/coyote/http11/InternalNio2OutputBuffer.java
+++ /dev/null
@@ -1,546 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.coyote.http11;
-
-import java.io.EOFException;
-import java.io.IOException;
-import java.net.SocketTimeoutException;
-import java.nio.ByteBuffer;
-import java.nio.channels.CompletionHandler;
-import java.util.ArrayList;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.Future;
-import java.util.concurrent.Semaphore;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-
-import javax.servlet.RequestDispatcher;
-
-import org.apache.coyote.ActionCode;
-import org.apache.coyote.OutputBuffer;
-import org.apache.coyote.Response;
-import org.apache.juli.logging.Log;
-import org.apache.juli.logging.LogFactory;
-import org.apache.tomcat.util.buf.ByteChunk;
-import org.apache.tomcat.util.net.AbstractEndpoint;
-import org.apache.tomcat.util.net.Nio2Channel;
-import org.apache.tomcat.util.net.Nio2Endpoint;
-import org.apache.tomcat.util.net.SocketStatus;
-import org.apache.tomcat.util.net.SocketWrapper;
-
-/**
- * Output buffer implementation for NIO2.
- */
-public class InternalNio2OutputBuffer extends AbstractOutputBuffer<Nio2Channel> {
-
- private static final Log log = LogFactory.getLog(InternalNio2OutputBuffer.class);
-
- // ----------------------------------------------------------- Constructors
-
- /**
- * Default constructor.
- */
- public InternalNio2OutputBuffer(Response response, int headerBufferSize) {
- super(response, headerBufferSize);
- outputStreamOutputBuffer = new SocketOutputBuffer();
- }
-
- private static final ByteBuffer[] EMPTY_BUF_ARRAY = new ByteBuffer[0];
-
- /**
- * Underlying socket.
- */
- private SocketWrapper<Nio2Channel> socket;
-
- /**
- * Track write interest
- */
- protected volatile boolean interest = false;
-
- /**
- * Track if the byte buffer is flipped
- */
- protected volatile boolean flipped = false;
-
- /**
- * The completion handler used for asynchronous write operations
- */
- protected CompletionHandler<Integer, ByteBuffer> completionHandler;
-
- /**
- * The completion handler used for asynchronous write operations
- */
- protected CompletionHandler<Long, ByteBuffer[]> gatherCompletionHandler;
-
- /**
- * Write pending flag.
- */
- protected Semaphore writePending = new Semaphore(1);
-
- /**
- * The associated endpoint.
- */
- protected AbstractEndpoint<Nio2Channel> endpoint = null;
-
- /**
- * Used instead of the deque since it looks equivalent and simpler.
- */
- protected ArrayList<ByteBuffer> bufferedWrites = new ArrayList<>();
-
- /**
- * Exception that occurred during writing.
- */
- protected IOException e = null;
-
- // --------------------------------------------------------- Public Methods
-
- @Override
- public void init(SocketWrapper<Nio2Channel> socketWrapper,
- AbstractEndpoint<Nio2Channel> associatedEndpoint) throws IOException {
- this.socket = socketWrapper;
- this.endpoint = associatedEndpoint;
-
- this.completionHandler = new CompletionHandler<Integer, ByteBuffer>() {
- @Override
- public void completed(Integer nBytes, ByteBuffer attachment) {
- boolean notify = false;
- synchronized (completionHandler) {
- if (nBytes.intValue() < 0) {
- failed(new EOFException(sm.getString("iob.failedwrite")), attachment);
- } else if (bufferedWrites.size() > 0) {
- // Continue writing data using a gathering write
- ArrayList<ByteBuffer> arrayList = new ArrayList<>();
- if (attachment.hasRemaining()) {
- arrayList.add(attachment);
- }
- for (ByteBuffer buffer : bufferedWrites) {
- buffer.flip();
- arrayList.add(buffer);
- }
- bufferedWrites.clear();
- ByteBuffer[] array = arrayList.toArray(EMPTY_BUF_ARRAY);
- socket.getSocket().write(array, 0, array.length,
- socket.getTimeout(), TimeUnit.MILLISECONDS,
- array, gatherCompletionHandler);
- } else if (attachment.hasRemaining()) {
- // Regular write
- socket.getSocket().write(attachment, socket.getTimeout(),
- TimeUnit.MILLISECONDS, attachment, completionHandler);
- } else {
- // All data has been written
- if (interest && !Nio2Endpoint.isInline()) {
- interest = false;
- notify = true;
- }
- writePending.release();
- }
- }
- if (notify) {
- endpoint.processSocket(socket, SocketStatus.OPEN_WRITE, false);
- }
- }
-
- @Override
- public void failed(Throwable exc, ByteBuffer attachment) {
- if (socket == null) {
- log.warn(sm.getString("iob.nio2.nullSocket"), exc);
- // Can't do anything else with a null socket
- return;
- }
- socket.setError(true);
- if (exc instanceof IOException) {
- e = (IOException) exc;
- } else {
- e = new IOException(exc);
- }
- response.getRequest().setAttribute(RequestDispatcher.ERROR_EXCEPTION, e);
- writePending.release();
- endpoint.processSocket(socket, SocketStatus.OPEN_WRITE, true);
- }
- };
- this.gatherCompletionHandler = new CompletionHandler<Long, ByteBuffer[]>() {
- @Override
- public void completed(Long nBytes, ByteBuffer[] attachment) {
- boolean notify = false;
- synchronized (completionHandler) {
- if (nBytes.longValue() < 0) {
- failed(new EOFException(sm.getString("iob.failedwrite")), attachment);
- } else if (bufferedWrites.size() > 0 || arrayHasData(attachment)) {
- // Continue writing data
- ArrayList<ByteBuffer> arrayList = new ArrayList<>();
- for (ByteBuffer buffer : attachment) {
- if (buffer.hasRemaining()) {
- arrayList.add(buffer);
- }
- }
- for (ByteBuffer buffer : bufferedWrites) {
- buffer.flip();
- arrayList.add(buffer);
- }
- bufferedWrites.clear();
- ByteBuffer[] array = arrayList.toArray(EMPTY_BUF_ARRAY);
- socket.getSocket().write(array, 0, array.length,
- socket.getTimeout(), TimeUnit.MILLISECONDS,
- array, gatherCompletionHandler);
- } else {
- // All data has been written
- if (interest && !Nio2Endpoint.isInline()) {
- interest = false;
- notify = true;
- }
- writePending.release();
- }
- }
- if (notify) {
- endpoint.processSocket(socket, SocketStatus.OPEN_WRITE, false);
- }
- }
-
- @Override
- public void failed(Throwable exc, ByteBuffer[] attachment) {
- if (socket == null) {
- log.debug(sm.getString("iob.nio2.nullSocket"), exc);
- // Can't do anything else with a null socket
- return;
- }
- socket.setError(true);
- if (exc instanceof IOException) {
- e = (IOException) exc;
- } else {
- e = new IOException(exc);
- }
- response.getRequest().setAttribute(RequestDispatcher.ERROR_EXCEPTION, e);
- writePending.release();
- endpoint.processSocket(socket, SocketStatus.OPEN_WRITE, true);
- }
- };
- }
-
-
- /**
- * Recycle the output buffer. This should be called when closing the
- * connection.
- */
- @Override
- public void recycle() {
- super.recycle();
- socket = null;
- e = null;
- flipped = false;
- interest = false;
- if (writePending.availablePermits() != 1) {
- writePending.drainPermits();
- writePending.release();
- }
- bufferedWrites.clear();
- }
-
-
- @Override
- public void nextRequest() {
- super.nextRequest();
- flipped = false;
- interest = false;
- }
-
- // ------------------------------------------------ HTTP/1.1 Output Methods
-
- /**
- * Send an acknowledgment.
- */
- @Override
- public void sendAck() throws IOException {
- if (!committed) {
- addToBB(Constants.ACK_BYTES, 0, Constants.ACK_BYTES.length);
- flushBuffer(true);
- }
- }
-
- // ------------------------------------------------------ Protected Methods
-
- /**
- * Commit the response.
- *
- * @throws IOException an underlying I/O error occurred
- */
- @Override
- protected void commit() throws IOException {
-
- // The response is now committed
- committed = true;
- response.setCommitted(true);
-
- if (pos > 0) {
- // Sending the response header buffer
- addToBB(headerBuffer, 0, pos);
- }
-
- }
-
- private static boolean arrayHasData(ByteBuffer[] byteBuffers) {
- for (ByteBuffer byteBuffer : byteBuffers) {
- if (byteBuffer.hasRemaining()) {
- return true;
- }
- }
- return false;
- }
-
- private void addToBB(byte[] buf, int offset, int length)
- throws IOException {
-
- if (length == 0)
- return;
- if (socket == null || socket.getSocket() == null)
- return;
-
- ByteBuffer writeByteBuffer = socket.getSocket().getBufHandler().getWriteBuffer();
-
- socket.access();
-
- if (isBlocking()) {
- while (length > 0) {
- int thisTime = transfer(buf, offset, length, writeByteBuffer);
- length = length - thisTime;
- offset = offset + thisTime;
- if (writeByteBuffer.remaining() == 0) {
- flushBuffer(true);
- }
- }
- } else {
- // FIXME: Possible new 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
- // done rather than continue buffering
- // Also allows doing autoblocking
- // Could be "smart" with coordination with the main CoyoteOutputStream to
- // indicate the end of a write
- // Uses: if (writePending.tryAcquire(socket.getTimeout(), TimeUnit.MILLISECONDS))
- if (writePending.tryAcquire()) {
- synchronized (completionHandler) {
- // No pending completion handler, so writing to the main buffer
- // is possible
- int thisTime = transfer(buf, offset, length, writeByteBuffer);
- length = length - thisTime;
- offset = offset + thisTime;
- if (length > 0) {
- // Remaining data must be buffered
- addToBuffers(buf, offset, length);
- }
- flushBufferInternal(false, true);
- }
- } else {
- synchronized (completionHandler) {
- addToBuffers(buf, offset, length);
- }
- }
- }
- }
-
-
- private void addToBuffers(byte[] buf, int offset, int length) {
- ByteBuffer buffer = ByteBuffer.allocate(Math.max(bufferedWriteSize, length));
- buffer.put(buf, offset, length);
- bufferedWrites.add(buffer);
- }
-
-
- private int transfer(byte[] from, int offset, int length, ByteBuffer to) {
- int max = Math.min(length, to.remaining());
- to.put(from, offset, max);
- return max;
- }
-
-
- /**
- * Callback to write data from the buffer.
- */
- @Override
- protected boolean flushBuffer(boolean block) throws IOException {
- if (e != null) {
- throw e;
- }
- return flushBufferInternal(block, false);
- }
-
- private boolean flushBufferInternal(boolean block, boolean hasPermit) throws IOException {
- if (socket == null || socket.getSocket() == null)
- return false;
-
- ByteBuffer byteBuffer = socket.getSocket().getBufHandler().getWriteBuffer();
- synchronized (completionHandler) {
- if (block) {
- if (!isBlocking()) {
- // The final flush is blocking, but the processing was using
- // non blocking so wait until an async write is done
- try {
- if (writePending.tryAcquire(socket.getTimeout(), TimeUnit.MILLISECONDS)) {
- writePending.release();
- }
- } catch (InterruptedException e) {
- // Ignore timeout
- }
- }
- Future<Integer> future = null;
- try {
- if (bufferedWrites.size() > 0) {
- for (ByteBuffer buffer : bufferedWrites) {
- buffer.flip();
- while (buffer.hasRemaining()) {
- future = socket.getSocket().write(buffer);
- if (future.get(socket.getTimeout(), TimeUnit.MILLISECONDS).intValue() < 0) {
- throw new EOFException(sm.getString("iob.failedwrite"));
- }
- }
- }
- bufferedWrites.clear();
- }
- if (!flipped) {
- byteBuffer.flip();
- flipped = true;
- }
- while (byteBuffer.hasRemaining()) {
- future = socket.getSocket().write(byteBuffer);
- if (future.get(socket.getTimeout(), TimeUnit.MILLISECONDS).intValue() < 0) {
- throw new EOFException(sm.getString("iob.failedwrite"));
- }
- }
- } catch (ExecutionException e) {
- if (e.getCause() instanceof IOException) {
- throw (IOException) e.getCause();
- } else {
- throw new IOException(e);
- }
- } catch (InterruptedException e) {
- throw new IOException(e);
- } catch (TimeoutException e) {
- future.cancel(true);
- throw new SocketTimeoutException();
- }
- byteBuffer.clear();
- flipped = false;
- return false;
- } else {
- if (hasPermit || writePending.tryAcquire()) {
- //prevent timeout for async
- socket.access();
- if (!flipped) {
- byteBuffer.flip();
- flipped = true;
- }
- Nio2Endpoint.startInline();
- if (bufferedWrites.size() > 0) {
- // Gathering write of the main buffer plus all leftovers
- ArrayList<ByteBuffer> arrayList = new ArrayList<>();
- if (byteBuffer.hasRemaining()) {
- arrayList.add(byteBuffer);
- }
- for (ByteBuffer buffer : bufferedWrites) {
- buffer.flip();
- arrayList.add(buffer);
- }
- bufferedWrites.clear();
- ByteBuffer[] array = arrayList.toArray(EMPTY_BUF_ARRAY);
- socket.getSocket().write(array, 0, array.length, socket.getTimeout(),
- TimeUnit.MILLISECONDS, array, gatherCompletionHandler);
- } else if (byteBuffer.hasRemaining()) {
- // Regular write
- socket.getSocket().write(byteBuffer, socket.getTimeout(),
- TimeUnit.MILLISECONDS, byteBuffer, completionHandler);
- } else {
- // Nothing was written
- writePending.release();
- }
- Nio2Endpoint.endInline();
- if (writePending.availablePermits() > 0) {
- if (byteBuffer.remaining() == 0) {
- byteBuffer.clear();
- flipped = false;
- }
- }
- }
- return hasMoreDataToFlush() || hasBufferedData() || e != null;
- }
- }
- }
-
-
- @Override
- public boolean hasDataToWrite() {
- synchronized (completionHandler) {
- return hasMoreDataToFlush() || hasBufferedData() || e != null;
- }
- }
-
- @Override
- protected boolean hasMoreDataToFlush() {
- return (flipped && socket.getSocket().getBufHandler().getWriteBuffer().remaining() > 0) ||
- (!flipped && socket.getSocket().getBufHandler().getWriteBuffer().position() > 0);
- }
-
- @Override
- protected boolean hasBufferedData() {
- return bufferedWrites.size() > 0;
- }
-
- @Override
- public void registerWriteInterest() {
- synchronized (completionHandler) {
- if (writePending.availablePermits() == 0) {
- interest = true;
- } else {
- // If no write is pending, notify
- endpoint.processSocket(socket, SocketStatus.OPEN_WRITE, true);
- }
- }
- }
-
-
- // ----------------------------------- OutputStreamOutputBuffer Inner Class
-
- /**
- * This class is an output buffer which will write data to an output
- * stream.
- */
- protected class SocketOutputBuffer implements OutputBuffer {
-
- /**
- * Write chunk.
- */
- @Override
- public int doWrite(ByteChunk chunk, Response res) throws IOException {
- try {
- int len = chunk.getLength();
- int start = chunk.getStart();
- byte[] b = chunk.getBuffer();
- addToBB(b, start, len);
- byteCount += len;
- return len;
- } catch (IOException ioe) {
- response.action(ActionCode.CLOSE_NOW, ioe);
- // Re-throw
- throw ioe;
- }
- }
-
- @Override
- public long getBytesWritten() {
- return byteCount;
- }
- }
-}
diff --git a/java/org/apache/coyote/http11/InternalNioInputBuffer.java b/java/org/apache/coyote/http11/InternalNioInputBuffer.java
deleted file mode 100644
index f802589..0000000
--- a/java/org/apache/coyote/http11/InternalNioInputBuffer.java
+++ /dev/null
@@ -1,188 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.coyote.http11;
-
-import java.io.EOFException;
-import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.nio.channels.Selector;
-
-import org.apache.coyote.InputBuffer;
-import org.apache.coyote.Request;
-import org.apache.juli.logging.Log;
-import org.apache.juli.logging.LogFactory;
-import org.apache.tomcat.util.buf.ByteChunk;
-import org.apache.tomcat.util.net.AbstractEndpoint;
-import org.apache.tomcat.util.net.NioChannel;
-import org.apache.tomcat.util.net.NioEndpoint;
-import org.apache.tomcat.util.net.NioSelectorPool;
-import org.apache.tomcat.util.net.SocketWrapper;
-
-/**
- * Implementation of InputBuffer which provides HTTP request header parsing as
- * well as transfer decoding.
- */
-public class InternalNioInputBuffer extends AbstractNioInputBuffer<NioChannel> {
-
- private static final Log log =
- LogFactory.getLog(InternalNioInputBuffer.class);
-
- // ----------------------------------------------------------- Constructors
-
-
- /**
- * Alternate constructor.
- */
- public InternalNioInputBuffer(Request request, int headerBufferSize) {
- super(request, headerBufferSize);
- inputStreamInputBuffer = new SocketInputBuffer();
- }
-
- /**
- * Underlying socket.
- */
- private NioChannel socket;
-
- /**
- * Selector pool, for blocking reads and blocking writes
- */
- private NioSelectorPool pool;
-
-
- // --------------------------------------------------------- Public Methods
-
- @Override
- protected final Log getLog() {
- return log;
- }
-
-
- /**
- * Recycle the input buffer. This should be called when closing the
- * connection.
- */
- @Override
- public void recycle() {
- super.recycle();
- socket = null;
- }
-
- // ------------------------------------------------------ Protected Methods
-
- @Override
- protected void init(SocketWrapper<NioChannel> socketWrapper,
- AbstractEndpoint<NioChannel> endpoint) throws IOException {
-
- socket = socketWrapper.getSocket();
- if (socket == null) {
- // Socket has been closed in another thread
- throw new IOException(sm.getString("iib.socketClosed"));
- }
- socketReadBufferSize =
- socket.getBufHandler().getReadBuffer().capacity();
-
- int bufLength = headerBufferSize + socketReadBufferSize;
- if (buf == null || buf.length < bufLength) {
- buf = new byte[bufLength];
- }
-
- pool = ((NioEndpoint)endpoint).getSelectorPool();
- }
-
-
- @Override
- protected boolean fill(boolean block) throws IOException, EOFException {
- if (parsingHeader) {
- if (lastValid > headerBufferSize) {
- throw new IllegalArgumentException
- (sm.getString("iib.requestheadertoolarge.error"));
- }
- } else {
- lastValid = pos = end;
- }
- int nRead = 0;
- ByteBuffer readBuffer = socket.getBufHandler().getReadBuffer();
- readBuffer.clear();
- if ( block ) {
- Selector selector = null;
- try {
- selector = pool.get();
- } catch ( IOException x ) {
- // Ignore
- }
- try {
- NioEndpoint.KeyAttachment att =
- (NioEndpoint.KeyAttachment) socket.getAttachment();
- if (att == null) {
- throw new IOException("Key must be cancelled.");
- }
- nRead = pool.read(readBuffer,
- socket, selector,
- socket.getIOChannel().socket().getSoTimeout());
- } catch ( EOFException eof ) {
- nRead = -1;
- } finally {
- if ( selector != null ) pool.put(selector);
- }
- } else {
- nRead = socket.read(readBuffer);
- }
- if (nRead > 0) {
- readBuffer.flip();
- readBuffer.limit(nRead);
- expand(nRead + pos);
- readBuffer.get(buf, pos, nRead);
- lastValid = pos + nRead;
- } else if (nRead == -1) {
- //return false;
- throw new EOFException(sm.getString("iib.eof.error"));
- }
- return nRead > 0;
- }
-
-
- // ------------------------------------- InputStreamInputBuffer Inner Class
-
-
- /**
- * This class is an input buffer which will read its data from an input
- * stream.
- */
- protected class SocketInputBuffer
- implements InputBuffer {
-
-
- /**
- * Read bytes into the specified chunk.
- */
- @Override
- public int doRead(ByteChunk chunk, Request req )
- throws IOException {
-
- if (pos >= lastValid) {
- if (!fill(true)) //read body, must be blocking, as the thread is inside the app
- return -1;
- }
-
- int length = lastValid - pos;
- chunk.setBytes(buf, pos, length);
- pos = lastValid;
-
- return (length);
- }
- }
-}
diff --git a/java/org/apache/coyote/http11/InternalNioOutputBuffer.java b/java/org/apache/coyote/http11/InternalNioOutputBuffer.java
deleted file mode 100644
index 027fa5f..0000000
--- a/java/org/apache/coyote/http11/InternalNioOutputBuffer.java
+++ /dev/null
@@ -1,336 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.coyote.http11;
-
-import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.nio.channels.SelectionKey;
-import java.nio.channels.Selector;
-import java.util.Iterator;
-
-import org.apache.coyote.ActionCode;
-import org.apache.coyote.ByteBufferHolder;
-import org.apache.coyote.OutputBuffer;
-import org.apache.coyote.Response;
-import org.apache.tomcat.util.buf.ByteChunk;
-import org.apache.tomcat.util.net.AbstractEndpoint;
-import org.apache.tomcat.util.net.NioChannel;
-import org.apache.tomcat.util.net.NioEndpoint;
-import org.apache.tomcat.util.net.NioSelectorPool;
-import org.apache.tomcat.util.net.SocketWrapper;
-
-/**
- * Output buffer.
- *
- * @author <a href="mailto:remm at apache.org">Remy Maucherat</a>
- */
-public class InternalNioOutputBuffer extends AbstractOutputBuffer<NioChannel> {
-
- // ----------------------------------------------------------- Constructors
-
- /**
- * Default constructor.
- */
- public InternalNioOutputBuffer(Response response, int headerBufferSize) {
-
- super(response, headerBufferSize);
-
- outputStreamOutputBuffer = new SocketOutputBuffer();
- }
-
-
- /**
- * Underlying socket.
- */
- private NioChannel socket;
-
- /**
- * Selector pool, for blocking reads and blocking writes
- */
- private NioSelectorPool pool;
-
- /**
- * Track if the byte buffer is flipped
- */
- protected volatile boolean flipped = false;
-
-
- // --------------------------------------------------------- Public Methods
-
- @Override
- public void init(SocketWrapper<NioChannel> socketWrapper,
- AbstractEndpoint<NioChannel> endpoint) throws IOException {
-
- socket = socketWrapper.getSocket();
- pool = ((NioEndpoint)endpoint).getSelectorPool();
- }
-
-
- /**
- * Recycle the output buffer. This should be called when closing the
- * connection.
- */
- @Override
- public void recycle() {
- super.recycle();
- if (socket != null) {
- socket.getBufHandler().getWriteBuffer().clear();
- socket = null;
- }
- flipped = false;
- }
-
-
- // ------------------------------------------------ HTTP/1.1 Output Methods
-
- /**
- * Send an acknowledgment.
- */
- @Override
- public void sendAck() throws IOException {
- if (!committed) {
- socket.getBufHandler().getWriteBuffer().put(
- Constants.ACK_BYTES, 0, Constants.ACK_BYTES.length);
- int result = writeToSocket(socket.getBufHandler().getWriteBuffer(), true, true);
- if (result < 0) {
- throw new IOException(sm.getString("iob.failedwrite.ack"));
- }
- }
- }
-
- /**
- *
- * @param bytebuffer ByteBuffer
- * @param flip boolean
- * @return int
- * @throws IOException
- */
- private synchronized int writeToSocket(ByteBuffer bytebuffer, boolean block, boolean flip) throws IOException {
- if ( flip ) {
- bytebuffer.flip();
- flipped = true;
- }
-
- int written = 0;
- NioEndpoint.KeyAttachment att = (NioEndpoint.KeyAttachment)socket.getAttachment();
- if ( att == null ) throw new IOException("Key must be cancelled");
- long writeTimeout = att.getWriteTimeout();
- Selector selector = null;
- try {
- selector = pool.get();
- } catch ( IOException x ) {
- //ignore
- }
- try {
- written = pool.write(bytebuffer, socket, selector, writeTimeout, block);
- //make sure we are flushed
- do {
- if (socket.flush(true,selector,writeTimeout)) break;
- }while ( true );
- } finally {
- if ( selector != null ) pool.put(selector);
- }
- if ( block || bytebuffer.remaining()==0) {
- //blocking writes must empty the buffer
- //and if remaining==0 then we did empty it
- bytebuffer.clear();
- flipped = false;
- }
- // If there is data left in the buffer the socket will be registered for
- // write further up the stack. This is to ensure the socket is only
- // registered for write once as both container and user code can trigger
- // write registration.
- return written;
- }
-
-
- // ------------------------------------------------------ Protected Methods
-
- /**
- * Commit the response.
- *
- * @throws IOException an underlying I/O error occurred
- */
- @Override
- protected void commit() throws IOException {
-
- // The response is now committed
- committed = true;
- response.setCommitted(true);
-
- if (pos > 0) {
- // Sending the response header buffer
- addToBB(headerBuffer, 0, pos);
- }
-
- }
-
-
- private synchronized void addToBB(byte[] buf, int offset, int length)
- throws IOException {
-
- if (length == 0) return;
-
- // Try to flush any data in the socket's write buffer first
- boolean dataLeft = flushBuffer(isBlocking());
-
- // Keep writing until all the data is written or a non-blocking write
- // leaves data in the buffer
- while (!dataLeft && length > 0) {
- int thisTime = transfer(buf,offset,length,socket.getBufHandler().getWriteBuffer());
- length = length - thisTime;
- offset = offset + thisTime;
- int written = writeToSocket(socket.getBufHandler().getWriteBuffer(),
- isBlocking(), true);
- if (written == 0) {
- dataLeft = true;
- } else {
- dataLeft = flushBuffer(isBlocking());
- }
- }
-
- NioEndpoint.KeyAttachment ka = (NioEndpoint.KeyAttachment)socket.getAttachment();
- if (ka != null) ka.access();//prevent timeouts for just doing client writes
-
- if (!isBlocking() && length > 0) {
- // Remaining data must be buffered
- addToBuffers(buf, offset, length);
- }
- }
-
-
- private void addToBuffers(byte[] buf, int offset, int length) {
- ByteBufferHolder holder = bufferedWrites.peekLast();
- if (holder==null || holder.isFlipped() || holder.getBuf().remaining()<length) {
- ByteBuffer buffer = ByteBuffer.allocate(Math.max(bufferedWriteSize,length));
- holder = new ByteBufferHolder(buffer,false);
- bufferedWrites.add(holder);
- }
- holder.getBuf().put(buf,offset,length);
- }
-
-
- /**
- * Callback to write data from the buffer.
- */
- @Override
- protected boolean flushBuffer(boolean block) throws IOException {
-
- //prevent timeout for async,
- SelectionKey key = socket.getIOChannel().keyFor(socket.getPoller().getSelector());
- if (key != null) {
- NioEndpoint.KeyAttachment attach = (NioEndpoint.KeyAttachment) key.attachment();
- attach.access();
- }
-
- boolean dataLeft = hasMoreDataToFlush();
-
- //write to the socket, if there is anything to write
- if (dataLeft) {
- writeToSocket(socket.getBufHandler().getWriteBuffer(),block, !flipped);
- }
-
- dataLeft = hasMoreDataToFlush();
-
- if (!dataLeft && bufferedWrites.size() > 0) {
- Iterator<ByteBufferHolder> bufIter = bufferedWrites.iterator();
- while (!hasMoreDataToFlush() && bufIter.hasNext()) {
- ByteBufferHolder buffer = bufIter.next();
- buffer.flip();
- while (!hasMoreDataToFlush() && buffer.getBuf().remaining()>0) {
- transfer(buffer.getBuf(), socket.getBufHandler().getWriteBuffer());
- if (buffer.getBuf().remaining() == 0) {
- bufIter.remove();
- }
- writeToSocket(socket.getBufHandler().getWriteBuffer(),block, true);
- //here we must break if we didn't finish the write
- }
- }
- }
-
- return hasMoreDataToFlush();
- }
-
-
- @Override
- protected boolean hasMoreDataToFlush() {
- return (flipped && socket.getBufHandler().getWriteBuffer().remaining()>0) ||
- (!flipped && socket.getBufHandler().getWriteBuffer().position() > 0);
- }
-
-
- @Override
- protected void registerWriteInterest() throws IOException {
- NioEndpoint.KeyAttachment att = (NioEndpoint.KeyAttachment)socket.getAttachment();
- if (att == null) {
- throw new IOException("Key must be cancelled");
- }
- att.getPoller().add(socket, SelectionKey.OP_WRITE);
- }
-
-
- private int transfer(byte[] from, int offset, int length, ByteBuffer to) {
- int max = Math.min(length, to.remaining());
- to.put(from, offset, max);
- return max;
- }
-
-
- private void transfer(ByteBuffer from, ByteBuffer to) {
- int max = Math.min(from.remaining(), to.remaining());
- ByteBuffer tmp = from.duplicate ();
- tmp.limit (tmp.position() + max);
- to.put (tmp);
- from.position(from.position() + max);
- }
-
-
- // ----------------------------------- OutputStreamOutputBuffer Inner Class
-
- /**
- * This class is an output buffer which will write data to an output
- * stream.
- */
- protected class SocketOutputBuffer implements OutputBuffer {
-
-
- /**
- * Write chunk.
- */
- @Override
- public int doWrite(ByteChunk chunk, Response res) throws IOException {
- try {
- int len = chunk.getLength();
- int start = chunk.getStart();
- byte[] b = chunk.getBuffer();
- addToBB(b, start, len);
- byteCount += chunk.getLength();
- return chunk.getLength();
- } catch (IOException ioe) {
- response.action(ActionCode.CLOSE_NOW, ioe);
- // Re-throw
- throw ioe;
- }
- }
-
- @Override
- public long getBytesWritten() {
- return byteCount;
- }
- }
-}
diff --git a/java/org/apache/coyote/http11/InternalOutputBuffer.java b/java/org/apache/coyote/http11/InternalOutputBuffer.java
deleted file mode 100644
index 3d53aad..0000000
--- a/java/org/apache/coyote/http11/InternalOutputBuffer.java
+++ /dev/null
@@ -1,243 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.coyote.http11;
-
-import java.io.IOException;
-import java.io.OutputStream;
-import java.net.Socket;
-
-import org.apache.coyote.ActionCode;
-import org.apache.coyote.OutputBuffer;
-import org.apache.coyote.Response;
-import org.apache.tomcat.util.buf.ByteChunk;
-import org.apache.tomcat.util.net.AbstractEndpoint;
-import org.apache.tomcat.util.net.SocketWrapper;
-
-/**
- * Output buffer.
- *
- * @author <a href="mailto:remm at apache.org">Remy Maucherat</a>
- */
-public class InternalOutputBuffer extends AbstractOutputBuffer<Socket>
- implements ByteChunk.ByteOutputChannel {
-
- // ----------------------------------------------------------- Constructors
-
- /**
- * Default constructor.
- */
- public InternalOutputBuffer(Response response, int headerBufferSize) {
-
- super(response, headerBufferSize);
-
- outputStreamOutputBuffer = new OutputStreamOutputBuffer();
-
- socketBuffer = new ByteChunk();
- socketBuffer.setByteOutputChannel(this);
- }
-
- /**
- * Underlying output stream. Note: protected to assist with unit testing
- */
- protected OutputStream outputStream;
-
-
- /**
- * Socket buffer.
- */
- private final ByteChunk socketBuffer;
-
-
- /**
- * Socket buffer (extra buffering to reduce number of packets sent).
- */
- private boolean useSocketBuffer = false;
-
-
- /**
- * Set the socket buffer size.
- */
- @Override
- public void setSocketBuffer(int socketBufferSize) {
-
- if (socketBufferSize > 500) {
- useSocketBuffer = true;
- socketBuffer.allocate(socketBufferSize, socketBufferSize);
- } else {
- useSocketBuffer = false;
- }
- }
-
-
- // --------------------------------------------------------- Public Methods
-
- @Override
- public void init(SocketWrapper<Socket> socketWrapper,
- AbstractEndpoint<Socket> endpoint) throws IOException {
-
- outputStream = socketWrapper.getSocket().getOutputStream();
- }
-
-
- /**
- * Recycle the output buffer. This should be called when closing the
- * connection.
- */
- @Override
- public void recycle() {
- super.recycle();
- outputStream = null;
- }
-
-
- /**
- * End processing of current HTTP request.
- * Note: All bytes of the current request should have been already
- * consumed. This method only resets all the pointers so that we are ready
- * to parse the next HTTP request.
- */
- @Override
- public void nextRequest() {
- super.nextRequest();
- socketBuffer.recycle();
- }
-
-
- // ------------------------------------------------ HTTP/1.1 Output Methods
-
- /**
- * Send an acknowledgment.
- */
- @Override
- public void sendAck()
- throws IOException {
-
- if (!committed)
- outputStream.write(Constants.ACK_BYTES);
-
- }
-
-
- // ------------------------------------------------------ Protected Methods
-
-
- /**
- * Commit the response.
- *
- * @throws IOException an underlying I/O error occurred
- */
- @Override
- protected void commit()
- throws IOException {
-
- // The response is now committed
- committed = true;
- response.setCommitted(true);
-
- if (pos > 0) {
- // Sending the response header buffer
- if (useSocketBuffer) {
- socketBuffer.append(headerBuffer, 0, pos);
- } else {
- outputStream.write(headerBuffer, 0, pos);
- }
- }
-
- }
-
-
- /**
- * Callback to write data from the buffer.
- */
- @Override
- public void realWriteBytes(byte cbuf[], int off, int len)
- throws IOException {
- if (len > 0) {
- outputStream.write(cbuf, off, len);
- }
- }
-
-
- //-------------------------------------------------- Non-blocking IO methods
-
- @Override
- protected boolean hasMoreDataToFlush() {
- // The blocking connector always blocks until the previous write is
- // complete so there is never data remaining to flush. This effectively
- // allows non-blocking code to work with the blocking connector but -
- // obviously - every write will always block.
- return false;
- }
-
-
- @Override
- protected void registerWriteInterest() {
- // NO-OP for non-blocking connector
- }
-
-
- @Override
- protected boolean flushBuffer(boolean block) throws IOException {
- // Blocking connector so ignore block parameter as this will always use
- // blocking IO.
- if (useSocketBuffer) {
- socketBuffer.flushBuffer();
- }
- // Always blocks so never any data left over.
- return false;
- }
-
-
- // ----------------------------------- OutputStreamOutputBuffer Inner Class
-
- /**
- * This class is an output buffer which will write data to an output
- * stream.
- */
- protected class OutputStreamOutputBuffer implements OutputBuffer {
-
-
- /**
- * Write chunk.
- */
- @Override
- public int doWrite(ByteChunk chunk, Response res) throws IOException {
- try {
- int length = chunk.getLength();
- if (useSocketBuffer) {
- socketBuffer.append(chunk.getBuffer(), chunk.getStart(),
- length);
- } else {
- outputStream.write(chunk.getBuffer(), chunk.getStart(),
- length);
- }
- byteCount += chunk.getLength();
- return chunk.getLength();
- } catch (IOException ioe) {
- response.action(ActionCode.CLOSE_NOW, ioe);
- // Re-throw
- throw ioe;
- }
- }
-
- @Override
- public long getBytesWritten() {
- return byteCount;
- }
- }
-}
diff --git a/java/org/apache/coyote/http11/LocalStrings.properties b/java/org/apache/coyote/http11/LocalStrings.properties
index d89fe2d..2a79c02 100644
--- a/java/org/apache/coyote/http11/LocalStrings.properties
+++ b/java/org/apache/coyote/http11/LocalStrings.properties
@@ -13,6 +13,10 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+abstractHttp11Protocol.alpnConfigured=The [{0}] connector has been configured to support negotiation to [{1}] via ALPN
+abstractHttp11Protocol.alpnWithNoTls=The upgrade handler [{0}] for [{1}] only supports upgrade via ALPN but has been configured for the [{2}] connector that is not enabled for TLS.
+abstractHttp11Protocol.httpUpgradeConfigured=The [{0}] connector has been configured to support HTTP upgrade to [{1}]
+
http11processor.fallToDebug=\n Note: further occurrences of HTTP header parsing errors will be logged at DEBUG level.
http11processor.header.parse=Error parsing HTTP request header
http11processor.neverused=This method should never be used
@@ -21,13 +25,7 @@ http11processor.request.process=Error processing request
http11processor.request.finish=Error finishing request
http11processor.response.finish=Error finishing response
http11processor.socket.info=Exception getting socket information
-http11processor.socket.ssl=Exception getting SSL attributes
-http11processor.socket.sslreneg=Exception re-negotiating SSL connection
-http11processor.comet.notsupported=The Comet protocol is not supported by this connector
http11processor.sendfile.error=Error sending data using sendfile. May be caused by invalid request attributes for start/end points
-http11Processor.upgrade=An internal error has occurred as upgraded connections should only be processed by the dedicated upgrade processor implementations
-
-iib.apr.sslGeneralError=An APR general error was returned by the SSL read operation on APR/native socket [{0}] with wrapper [{1}]. It will be treated as EAGAIN and the socket returned to the poller.
iib.available.readFail=A non-blocking read failed while attempting to determine if data was available
iib.eof.error=Unexpected EOF read on the socket
@@ -46,5 +44,3 @@ iob.failedwrite=Failed write
iob.failedwrite.ack=Failed to send HTTP 100 continue response
iob.illegalreset=The response may not be reset once it has been committed
iob.responseheadertoolarge.error=An attempt was made to write more data to the response headers than there was room available in the buffer. Increase maxHttpHeaderSize on the connector or write less data into the response headers.
-
-iob.nio2.nullSocket=Socket was null while trying to process exception. See Bug 57749
\ No newline at end of file
diff --git a/java/org/apache/coyote/http11/LocalStrings_es.properties b/java/org/apache/coyote/http11/LocalStrings_es.properties
index db0396f..48c0cb9 100644
--- a/java/org/apache/coyote/http11/LocalStrings_es.properties
+++ b/java/org/apache/coyote/http11/LocalStrings_es.properties
@@ -19,7 +19,6 @@ http11processor.request.process = Error procesando requerimiento
http11processor.request.finish = Error acabando requerimiento
http11processor.response.finish = Error acabando respuesta
http11processor.socket.info = Excepci\u00F3n obteniendo informaci\u00F3n de conector
-http11processor.socket.ssl = Excepci\u00F3n obteniendo atributos SSL
http11processor.socket.sslreneg = Excepci\u00F3n renegociando la conexi\u00F3n SSL
iib.eof.error = Inesperado Fin De Archivo (EOF) le\u00EDdo en el enchufe (socket)
iib.requestheadertoolarge.error = La cabecera del requerimiento es demasido grande
diff --git a/java/org/apache/coyote/http11/OutputFilter.java b/java/org/apache/coyote/http11/OutputFilter.java
index 044c2e0..f2549e5 100644
--- a/java/org/apache/coyote/http11/OutputFilter.java
+++ b/java/org/apache/coyote/http11/OutputFilter.java
@@ -21,7 +21,6 @@ import java.io.IOException;
import org.apache.coyote.OutputBuffer;
import org.apache.coyote.Response;
-import org.apache.tomcat.util.buf.ByteChunk;
/**
* Output filter.
@@ -32,19 +31,11 @@ public interface OutputFilter extends OutputBuffer {
/**
- * Write some bytes.
- *
- * @return number of bytes written by the filter
- */
- @Override
- public int doWrite(ByteChunk chunk, Response unused)
- throws IOException;
-
-
- /**
* Some filters need additional parameters from the response. All the
* necessary reading can occur in that method, as this method is called
* after the response header processing is complete.
+ *
+ * @param response The response to associate with this OutputFilter
*/
public void setResponse(Response response);
@@ -57,6 +48,8 @@ public interface OutputFilter extends OutputBuffer {
/**
* Set the next buffer in the filter pipeline.
+ *
+ * @param buffer The next buffer instance
*/
public void setBuffer(OutputBuffer buffer);
@@ -69,9 +62,8 @@ public interface OutputFilter extends OutputBuffer {
* delimitation, in which case the number is the amount of extra bytes or
* missing bytes, which would indicate an error.
* Note: It is recommended that extra bytes be swallowed by the filter.
+ *
+ * @throws IOException If an I/O error occurs while writing to the client
*/
- public long end()
- throws IOException;
-
-
+ public long end() throws IOException;
}
diff --git a/java/org/apache/coyote/http11/filters/BufferedInputFilter.java b/java/org/apache/coyote/http11/filters/BufferedInputFilter.java
index ebdfb92..927e881 100644
--- a/java/org/apache/coyote/http11/filters/BufferedInputFilter.java
+++ b/java/org/apache/coyote/http11/filters/BufferedInputFilter.java
@@ -18,18 +18,21 @@
package org.apache.coyote.http11.filters;
import java.io.IOException;
+import java.nio.BufferOverflowException;
+import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import org.apache.coyote.InputBuffer;
import org.apache.coyote.Request;
import org.apache.coyote.http11.InputFilter;
import org.apache.tomcat.util.buf.ByteChunk;
+import org.apache.tomcat.util.net.ApplicationBufferHandler;
/**
* Input filter responsible for reading and buffering the request body, so that
* it does not interfere with client SSL handshake messages.
*/
-public class BufferedInputFilter implements InputFilter {
+public class BufferedInputFilter implements InputFilter, ApplicationBufferHandler {
// -------------------------------------------------------------- Constants
@@ -39,8 +42,8 @@ public class BufferedInputFilter implements InputFilter {
// ----------------------------------------------------- Instance Variables
- private ByteChunk buffered = null;
- private final ByteChunk tempRead = new ByteChunk(1024);
+ private ByteBuffer buffered;
+ private ByteBuffer tempRead;
private InputBuffer buffer;
private boolean hasRead = false;
@@ -64,8 +67,8 @@ public class BufferedInputFilter implements InputFilter {
*/
public void setLimit(int limit) {
if (buffered == null) {
- buffered = new ByteChunk(4048);
- buffered.setLimit(limit);
+ buffered = ByteBuffer.allocate(limit);
+ buffered.flip();
}
}
@@ -80,11 +83,13 @@ public class BufferedInputFilter implements InputFilter {
public void setRequest(Request request) {
// save off the Request body
try {
- while (buffer.doRead(tempRead, request) >= 0) {
- buffered.append(tempRead);
- tempRead.recycle();
+ while (buffer.doRead(this) >= 0) {
+ buffered.mark().position(buffered.limit()).limit(buffered.capacity());
+ buffered.put(tempRead);
+ buffered.limit(buffered.position()).reset();
+ tempRead = null;
}
- } catch(IOException ioe) {
+ } catch(IOException | BufferOverflowException ioe) {
// No need for i18n - this isn't going to get logged anywhere
throw new IllegalStateException(
"Request body too large for buffer");
@@ -93,19 +98,36 @@ public class BufferedInputFilter implements InputFilter {
/**
* Fills the given ByteChunk with the buffered request body.
+ *
+ * @deprecated Unused. Will be removed in Tomcat 9. Use
+ * {@link #doRead(ApplicationBufferHandler)}
*/
@Override
- public int doRead(ByteChunk chunk, Request request) throws IOException {
- if (hasRead || buffered.getLength() <= 0) {
+ public int doRead(ByteChunk chunk) throws IOException {
+ if (isFinished()) {
return -1;
}
- chunk.setBytes(buffered.getBytes(), buffered.getStart(),
- buffered.getLength());
+ chunk.setBytes(buffered.array(), buffered.arrayOffset() + buffered.position(),
+ buffered.remaining());
hasRead = true;
return chunk.getLength();
}
+ /**
+ * Fills the given ByteBuffer with the buffered request body.
+ */
+ @Override
+ public int doRead(ApplicationBufferHandler handler) throws IOException {
+ if (isFinished()) {
+ return -1;
+ }
+
+ handler.setByteBuffer(buffered);
+ hasRead = true;
+ return buffered.remaining();
+ }
+
@Override
public void setBuffer(InputBuffer buffer) {
this.buffer = buffer;
@@ -114,13 +136,12 @@ public class BufferedInputFilter implements InputFilter {
@Override
public void recycle() {
if (buffered != null) {
- if (buffered.getBuffer().length > 65536) {
+ if (buffered.capacity() > 65536) {
buffered = null;
} else {
- buffered.recycle();
+ buffered.position(0).limit(0);
}
}
- tempRead.recycle();
hasRead = false;
buffer = null;
}
@@ -137,12 +158,30 @@ public class BufferedInputFilter implements InputFilter {
@Override
public int available() {
- return buffered.getLength();
+ return buffered.remaining();
}
@Override
public boolean isFinished() {
- return hasRead || buffered.getLength() <= 0;
+ return hasRead || buffered.remaining() <= 0;
+ }
+
+
+ @Override
+ public void setByteBuffer(ByteBuffer buffer) {
+ tempRead = buffer;
+ }
+
+
+ @Override
+ public ByteBuffer getByteBuffer() {
+ return tempRead;
+ }
+
+
+ @Override
+ public void expand(int size) {
+ // no-op
}
}
diff --git a/java/org/apache/coyote/http11/filters/ChunkedInputFilter.java b/java/org/apache/coyote/http11/filters/ChunkedInputFilter.java
index 6e92240..80b32fe 100644
--- a/java/org/apache/coyote/http11/filters/ChunkedInputFilter.java
+++ b/java/org/apache/coyote/http11/filters/ChunkedInputFilter.java
@@ -18,6 +18,7 @@ package org.apache.coyote.http11.filters;
import java.io.EOFException;
import java.io.IOException;
+import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.Locale;
import java.util.Set;
@@ -30,6 +31,7 @@ import org.apache.tomcat.util.buf.ByteChunk;
import org.apache.tomcat.util.buf.HexUtils;
import org.apache.tomcat.util.buf.MessageBytes;
import org.apache.tomcat.util.http.MimeHeaders;
+import org.apache.tomcat.util.net.ApplicationBufferHandler;
import org.apache.tomcat.util.res.StringManager;
/**
@@ -38,7 +40,7 @@ import org.apache.tomcat.util.res.StringManager;
*
* @author Remy Maucherat
*/
-public class ChunkedInputFilter implements InputFilter {
+public class ChunkedInputFilter implements InputFilter, ApplicationBufferHandler {
private static final StringManager sm = StringManager.getManager(
ChunkedInputFilter.class.getPackage().getName());
@@ -73,27 +75,9 @@ public class ChunkedInputFilter implements InputFilter {
/**
- * Position in the buffer.
- */
- protected int pos = 0;
-
-
- /**
- * Last valid byte in the buffer.
- */
- protected int lastValid = 0;
-
-
- /**
- * Read bytes buffer.
- */
- protected byte[] buf = null;
-
-
- /**
* Byte chunk used to read bytes.
*/
- protected final ByteChunk readChunk = new ByteChunk();
+ protected ByteBuffer readChunk;
/**
@@ -165,16 +149,11 @@ public class ChunkedInputFilter implements InputFilter {
// ---------------------------------------------------- InputBuffer Methods
/**
- * Read bytes.
- *
- * @return If the filter does request length control, this value is
- * significant; it should be the number of bytes consumed from the buffer,
- * up until the end of the current request body, or the buffer length,
- * whichever is greater. If the filter does not do request body length
- * control, the returned value should be -1.
+ * @deprecated Unused. Will be removed in Tomcat 9. Use
+ * {@link #doRead(ApplicationBufferHandler)}
*/
@Override
- public int doRead(ByteChunk chunk, Request req) throws IOException {
+ public int doRead(ByteChunk chunk) throws IOException {
if (endChunk) {
return -1;
}
@@ -198,24 +177,83 @@ public class ChunkedInputFilter implements InputFilter {
int result = 0;
- if (pos >= lastValid) {
+ if (readChunk == null || readChunk.position() >= readChunk.limit()) {
if (readBytes() < 0) {
throwIOException(sm.getString("chunkedInputFilter.eos"));
}
}
- if (remaining > (lastValid - pos)) {
- result = lastValid - pos;
+ if (remaining > readChunk.remaining()) {
+ result = readChunk.remaining();
remaining = remaining - result;
- chunk.setBytes(buf, pos, result);
- pos = lastValid;
+ chunk.setBytes(readChunk.array(), readChunk.arrayOffset() + readChunk.position(), result);
+ readChunk.position(readChunk.limit());
} else {
result = remaining;
- chunk.setBytes(buf, pos, remaining);
- pos = pos + remaining;
+ chunk.setBytes(readChunk.array(), readChunk.arrayOffset() + readChunk.position(), remaining);
+ readChunk.position(readChunk.position() + remaining);
remaining = 0;
//we need a CRLF
- if ((pos+1) >= lastValid) {
+ if ((readChunk.position() + 1) >= readChunk.limit()) {
+ //if we call parseCRLF we overrun the buffer here
+ //so we defer it to the next call BZ 11117
+ needCRLFParse = true;
+ } else {
+ parseCRLF(false); //parse the CRLF immediately
+ }
+ }
+
+ return result;
+ }
+
+ @Override
+ public int doRead(ApplicationBufferHandler handler) throws IOException {
+ if (endChunk) {
+ return -1;
+ }
+
+ checkError();
+
+ if(needCRLFParse) {
+ needCRLFParse = false;
+ parseCRLF(false);
+ }
+
+ if (remaining <= 0) {
+ if (!parseChunkHeader()) {
+ throwIOException(sm.getString("chunkedInputFilter.invalidHeader"));
+ }
+ if (endChunk) {
+ parseEndChunk();
+ return -1;
+ }
+ }
+
+ int result = 0;
+
+ if (readChunk == null || readChunk.position() >= readChunk.limit()) {
+ if (readBytes() < 0) {
+ throwIOException(sm.getString("chunkedInputFilter.eos"));
+ }
+ }
+
+ if (remaining > readChunk.remaining()) {
+ result = readChunk.remaining();
+ remaining = remaining - result;
+ if (readChunk != handler.getByteBuffer()) {
+ handler.setByteBuffer(readChunk.duplicate());
+ }
+ readChunk.position(readChunk.limit());
+ } else {
+ result = remaining;
+ if (readChunk != handler.getByteBuffer()) {
+ handler.setByteBuffer(readChunk.duplicate());
+ handler.getByteBuffer().limit(readChunk.position() + remaining);
+ }
+ readChunk.position(readChunk.position() + remaining);
+ remaining = 0;
+ //we need a CRLF
+ if ((readChunk.position() + 1) >= readChunk.limit()) {
//if we call parseCRLF we overrun the buffer here
//so we defer it to the next call BZ 11117
needCRLFParse = true;
@@ -247,7 +285,7 @@ public class ChunkedInputFilter implements InputFilter {
long swallowed = 0;
int read = 0;
// Consume extra bytes : parse the stream until the end chunk is found
- while ((read = doRead(readChunk, null)) >= 0) {
+ while ((read = doRead(this)) >= 0) {
swallowed += read;
if (maxSwallowSize > -1 && swallowed > maxSwallowSize) {
throwIOException(sm.getString("inputFilter.maxSwallow"));
@@ -255,7 +293,7 @@ public class ChunkedInputFilter implements InputFilter {
}
// Return the number of extra bytes which were consumed
- return lastValid - pos;
+ return readChunk.remaining();
}
@@ -264,7 +302,7 @@ public class ChunkedInputFilter implements InputFilter {
*/
@Override
public int available() {
- return lastValid - pos;
+ return readChunk != null ? readChunk.remaining() : 0;
}
@@ -283,8 +321,9 @@ public class ChunkedInputFilter implements InputFilter {
@Override
public void recycle() {
remaining = 0;
- pos = 0;
- lastValid = 0;
+ if (readChunk != null) {
+ readChunk.position(0).limit(0);
+ }
endChunk = false;
needCRLFParse = false;
trailingHeaders.recycle();
@@ -314,15 +353,11 @@ public class ChunkedInputFilter implements InputFilter {
/**
* Read bytes from the previous buffer.
+ * @return The byte count which has been read
+ * @throws IOException Read error
*/
protected int readBytes() throws IOException {
-
- int nRead = buffer.doRead(readChunk, null);
- pos = readChunk.getStart();
- lastValid = pos + nRead;
- buf = readChunk.getBytes();
-
- return nRead;
+ return buffer.doRead(this);
}
@@ -336,6 +371,9 @@ public class ChunkedInputFilter implements InputFilter {
* The letters before CRLF or ';' (whatever comes first) must be valid hex
* digits. We should not parse F23IAMGONNAMESSTHISUP34CRLF as a valid
* header according to the spec.
+ * @return <code>true</code> if the chunk header has been
+ * successfully parsed
+ * @throws IOException Read error
*/
protected boolean parseChunkHeader() throws IOException {
@@ -346,15 +384,16 @@ public class ChunkedInputFilter implements InputFilter {
while (!eol) {
- if (pos >= lastValid) {
+ if (readChunk == null || readChunk.position() >= readChunk.limit()) {
if (readBytes() <= 0)
return false;
}
- if (buf[pos] == Constants.CR || buf[pos] == Constants.LF) {
+ byte chr = readChunk.get(readChunk.position());
+ if (chr == Constants.CR || chr == Constants.LF) {
parseCRLF(false);
eol = true;
- } else if (buf[pos] == Constants.SEMI_COLON && !extension) {
+ } else if (chr == Constants.SEMI_COLON && !extension) {
// First semi-colon marks the start of the extension. Further
// semi-colons may appear to separate multiple chunk-extensions.
// These need to be processed as part of parsing the extensions.
@@ -362,7 +401,7 @@ public class ChunkedInputFilter implements InputFilter {
extensionSize++;
} else if (!extension) {
//don't read data after the trailer
- int charValue = HexUtils.getDec(buf[pos]);
+ int charValue = HexUtils.getDec(chr);
if (charValue != -1 && readDigit < 8) {
readDigit++;
result = (result << 4) | charValue;
@@ -383,7 +422,7 @@ public class ChunkedInputFilter implements InputFilter {
// Parsing the CRLF increments pos
if (!eol) {
- pos++;
+ readChunk.position(readChunk.position() + 1);
}
}
@@ -406,6 +445,7 @@ public class ChunkedInputFilter implements InputFilter {
* @param tolerant Should tolerant parsing (LF and CRLF) be used? This
* is recommended (RFC2616, section 19.3) for message
* headers.
+ * @throws IOException An error occurred parsing CRLF
*/
protected void parseCRLF(boolean tolerant) throws IOException {
@@ -413,18 +453,19 @@ public class ChunkedInputFilter implements InputFilter {
boolean crfound = false;
while (!eol) {
- if (pos >= lastValid) {
+ if (readChunk == null || readChunk.position() >= readChunk.limit()) {
if (readBytes() <= 0) {
throwIOException(sm.getString("chunkedInputFilter.invalidCrlfNoData"));
}
}
- if (buf[pos] == Constants.CR) {
+ byte chr = readChunk.get(readChunk.position());
+ if (chr == Constants.CR) {
if (crfound) {
throwIOException(sm.getString("chunkedInputFilter.invalidCrlfCRCR"));
}
crfound = true;
- } else if (buf[pos] == Constants.LF) {
+ } else if (chr == Constants.LF) {
if (!tolerant && !crfound) {
throwIOException(sm.getString("chunkedInputFilter.invalidCrlfNoCR"));
}
@@ -433,13 +474,14 @@ public class ChunkedInputFilter implements InputFilter {
throwIOException(sm.getString("chunkedInputFilter.invalidCrlf"));
}
- pos++;
+ readChunk.position(readChunk.position() + 1);
}
}
/**
* Parse end chunk data.
+ * @throws IOException Error propagation
*/
protected void parseEndChunk() throws IOException {
// Handle optional trailer headers
@@ -456,13 +498,13 @@ public class ChunkedInputFilter implements InputFilter {
byte chr = 0;
// Read new bytes if needed
- if (pos >= lastValid) {
+ if (readChunk == null || readChunk.position() >= readChunk.limit()) {
if (readBytes() <0) {
throwEOFException(sm.getString("chunkedInputFilter.eosTrailer"));
}
}
- chr = buf[pos];
+ chr = readChunk.get(readChunk.position());
// CRLF terminates the request
if (chr == Constants.CR || chr == Constants.LF) {
@@ -482,13 +524,13 @@ public class ChunkedInputFilter implements InputFilter {
while (!colon) {
// Read new bytes if needed
- if (pos >= lastValid) {
+ if (readChunk == null || readChunk.position() >= readChunk.limit()) {
if (readBytes() <0) {
throwEOFException(sm.getString("chunkedInputFilter.eosTrailer"));
}
}
- chr = buf[pos];
+ chr = readChunk.get(readChunk.position());
if ((chr >= Constants.A) && (chr <= Constants.Z)) {
chr = (byte) (chr - Constants.LC_OFFSET);
}
@@ -499,7 +541,7 @@ public class ChunkedInputFilter implements InputFilter {
trailingHeaders.append(chr);
}
- pos++;
+ readChunk.position(readChunk.position() + 1);
}
int colonPos = trailingHeaders.getEnd();
@@ -520,15 +562,15 @@ public class ChunkedInputFilter implements InputFilter {
while (space) {
// Read new bytes if needed
- if (pos >= lastValid) {
+ if (readChunk == null || readChunk.position() >= readChunk.limit()) {
if (readBytes() <0) {
throwEOFException(sm.getString("chunkedInputFilter.eosTrailer"));
}
}
- chr = buf[pos];
+ chr = readChunk.get(readChunk.position());
if ((chr == Constants.SP) || (chr == Constants.HT)) {
- pos++;
+ readChunk.position(readChunk.position() + 1);
// If we swallow whitespace, make sure it counts towards the
// limit placed on trailing header size
int newlimit = trailingHeaders.getLimit() -1;
@@ -546,13 +588,13 @@ public class ChunkedInputFilter implements InputFilter {
while (!eol) {
// Read new bytes if needed
- if (pos >= lastValid) {
+ if (readChunk == null || readChunk.position() >= readChunk.limit()) {
if (readBytes() <0) {
throwEOFException(sm.getString("chunkedInputFilter.eosTrailer"));
}
}
- chr = buf[pos];
+ chr = readChunk.get(readChunk.position());
if (chr == Constants.CR || chr == Constants.LF) {
parseCRLF(true);
eol = true;
@@ -564,7 +606,7 @@ public class ChunkedInputFilter implements InputFilter {
}
if (!eol) {
- pos++;
+ readChunk.position(readChunk.position() + 1);
}
}
@@ -572,13 +614,13 @@ public class ChunkedInputFilter implements InputFilter {
// is a LWS, then it's a multiline header
// Read new bytes if needed
- if (pos >= lastValid) {
+ if (readChunk == null || readChunk.position() >= readChunk.limit()) {
if (readBytes() <0) {
throwEOFException(sm.getString("chunkedInputFilter.eosTrailer"));
}
}
- chr = buf[pos];
+ chr = readChunk.get(readChunk.position());
if ((chr != Constants.SP) && (chr != Constants.HT)) {
validLine = false;
} else {
@@ -622,4 +664,22 @@ public class ChunkedInputFilter implements InputFilter {
throw new IOException(sm.getString("chunkedInputFilter.error"));
}
}
+
+
+ @Override
+ public void setByteBuffer(ByteBuffer buffer) {
+ readChunk = buffer;
+ }
+
+
+ @Override
+ public ByteBuffer getByteBuffer() {
+ return readChunk;
+ }
+
+
+ @Override
+ public void expand(int size) {
+ // no-op
+ }
}
diff --git a/java/org/apache/coyote/http11/filters/ChunkedOutputFilter.java b/java/org/apache/coyote/http11/filters/ChunkedOutputFilter.java
index 24fc4b1..8fc1d62 100644
--- a/java/org/apache/coyote/http11/filters/ChunkedOutputFilter.java
+++ b/java/org/apache/coyote/http11/filters/ChunkedOutputFilter.java
@@ -18,6 +18,7 @@
package org.apache.coyote.http11.filters;
import java.io.IOException;
+import java.nio.ByteBuffer;
import org.apache.coyote.OutputBuffer;
import org.apache.coyote.Response;
@@ -34,20 +35,8 @@ public class ChunkedOutputFilter implements OutputFilter {
// -------------------------------------------------------------- Constants
- /**
- * End chunk.
- */
- protected static final ByteChunk END_CHUNK = new ByteChunk();
-
-
- // ----------------------------------------------------- Static Initializer
-
-
- static {
- byte[] END_CHUNK_BYTES = {(byte) '0', (byte) '\r', (byte) '\n',
- (byte) '\r', (byte) '\n'};
- END_CHUNK.setBytes(END_CHUNK_BYTES, 0, END_CHUNK_BYTES.length);
- }
+ private static final byte[] END_CHUNK_BYTES = {(byte) '0', (byte) '\r', (byte) '\n',
+ (byte) '\r', (byte) '\n'};
// ------------------------------------------------------------ Constructor
@@ -57,8 +46,8 @@ public class ChunkedOutputFilter implements OutputFilter {
* Default constructor.
*/
public ChunkedOutputFilter() {
- chunkLength[8] = (byte) '\r';
- chunkLength[9] = (byte) '\n';
+ chunkHeader.put(8, (byte) '\r');
+ chunkHeader.put(9, (byte) '\n');
}
@@ -72,15 +61,15 @@ public class ChunkedOutputFilter implements OutputFilter {
/**
- * Buffer used for chunk length conversion.
+ * Chunk header.
*/
- protected final byte[] chunkLength = new byte[10];
+ protected final ByteBuffer chunkHeader = ByteBuffer.allocate(10);
/**
- * Chunk header.
+ * End chunk.
*/
- protected final ByteChunk chunkHeader = new ByteChunk();
+ protected final ByteBuffer endChunk = ByteBuffer.wrap(END_CHUNK_BYTES);
// ------------------------------------------------------------- Properties
@@ -88,15 +77,12 @@ public class ChunkedOutputFilter implements OutputFilter {
// --------------------------------------------------- OutputBuffer Methods
-
/**
- * Write some bytes.
- *
- * @return number of bytes written by the filter
+ * @deprecated Unused. Will be removed in Tomcat 9. Use
+ * {@link #doWrite(ByteBuffer)}
*/
@Override
- public int doWrite(ByteChunk chunk, Response res)
- throws IOException {
+ public int doWrite(ByteChunk chunk) throws IOException {
int result = chunk.getLength();
@@ -104,27 +90,58 @@ public class ChunkedOutputFilter implements OutputFilter {
return 0;
}
- // Calculate chunk header
- int pos = 7;
- int current = result;
- while (current > 0) {
- int digit = current % 16;
- current = current / 16;
- chunkLength[pos--] = HexUtils.getHex(digit);
+ int pos = calculateChunkHeader(result);
+
+ chunkHeader.position(pos + 1).limit(chunkHeader.position() + 9 - pos);
+ buffer.doWrite(chunkHeader);
+
+ buffer.doWrite(chunk);
+
+ chunkHeader.position(8).limit(10);
+ buffer.doWrite(chunkHeader);
+
+ return result;
+
+ }
+
+
+ @Override
+ public int doWrite(ByteBuffer chunk) throws IOException {
+
+ int result = chunk.remaining();
+
+ if (result <= 0) {
+ return 0;
}
- chunkHeader.setBytes(chunkLength, pos + 1, 9 - pos);
- buffer.doWrite(chunkHeader, res);
- buffer.doWrite(chunk, res);
+ int pos = calculateChunkHeader(result);
+
+ chunkHeader.position(pos + 1).limit(chunkHeader.position() + 9 - pos);
+ buffer.doWrite(chunkHeader);
- chunkHeader.setBytes(chunkLength, 8, 2);
- buffer.doWrite(chunkHeader, res);
+ buffer.doWrite(chunk);
+
+ chunkHeader.position(8).limit(10);
+ buffer.doWrite(chunkHeader);
return result;
}
+ private int calculateChunkHeader(int len) {
+ // Calculate chunk header
+ int pos = 7;
+ int current = len;
+ while (current > 0) {
+ int digit = current % 16;
+ current = current / 16;
+ chunkHeader.put(pos--, HexUtils.getHex(digit));
+ }
+ return pos;
+ }
+
+
@Override
public long getBytesWritten() {
return buffer.getBytesWritten();
@@ -163,7 +180,8 @@ public class ChunkedOutputFilter implements OutputFilter {
throws IOException {
// Write end chunk
- buffer.doWrite(END_CHUNK, null);
+ buffer.doWrite(endChunk);
+ endChunk.position(0).limit(endChunk.capacity());
return 0;
diff --git a/java/org/apache/coyote/http11/filters/GzipOutputFilter.java b/java/org/apache/coyote/http11/filters/GzipOutputFilter.java
index 0e2efe9..5b841ff 100644
--- a/java/org/apache/coyote/http11/filters/GzipOutputFilter.java
+++ b/java/org/apache/coyote/http11/filters/GzipOutputFilter.java
@@ -19,11 +19,14 @@ package org.apache.coyote.http11.filters;
import java.io.IOException;
import java.io.OutputStream;
+import java.nio.ByteBuffer;
import java.util.zip.GZIPOutputStream;
import org.apache.coyote.OutputBuffer;
import org.apache.coyote.Response;
import org.apache.coyote.http11.OutputFilter;
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.buf.ByteChunk;
/**
@@ -34,11 +37,7 @@ import org.apache.tomcat.util.buf.ByteChunk;
public class GzipOutputFilter implements OutputFilter {
- /**
- * Logger.
- */
- protected static final org.apache.juli.logging.Log log =
- org.apache.juli.logging.LogFactory.getLog(GzipOutputFilter.class);
+ protected static final Log log = LogFactory.getLog(GzipOutputFilter.class);
// ----------------------------------------------------- Instance Variables
@@ -64,15 +63,12 @@ public class GzipOutputFilter implements OutputFilter {
// --------------------------------------------------- OutputBuffer Methods
-
/**
- * Write some bytes.
- *
- * @return number of bytes written by the filter
+ * @deprecated Unused. Will be removed in Tomcat 9. Use
+ * {@link #doWrite(ByteBuffer)}
*/
@Override
- public int doWrite(ByteChunk chunk, Response res)
- throws IOException {
+ public int doWrite(ByteChunk chunk) throws IOException {
if (compressionStream == null) {
compressionStream = new GZIPOutputStream(fakeOutputStream, true);
}
@@ -83,6 +79,23 @@ public class GzipOutputFilter implements OutputFilter {
@Override
+ public int doWrite(ByteBuffer chunk) throws IOException {
+ if (compressionStream == null) {
+ compressionStream = new GZIPOutputStream(fakeOutputStream, true);
+ }
+ int len = chunk.remaining();
+ if (chunk.hasArray()) {
+ compressionStream.write(chunk.array(), chunk.arrayOffset() + chunk.position(), len);
+ } else {
+ byte[] bytes = new byte[len];
+ chunk.put(bytes);
+ compressionStream.write(bytes, 0, len);
+ }
+ return len;
+ }
+
+
+ @Override
public long getBytesWritten() {
return buffer.getBytesWritten();
}
@@ -159,22 +172,19 @@ public class GzipOutputFilter implements OutputFilter {
protected class FakeOutputStream
extends OutputStream {
- protected final ByteChunk outputChunk = new ByteChunk();
- protected final byte[] singleByteBuffer = new byte[1];
+ protected final ByteBuffer outputChunk = ByteBuffer.allocate(1);
@Override
public void write(int b)
throws IOException {
// Shouldn't get used for good performance, but is needed for
// compatibility with Sun JDK 1.4.0
- singleByteBuffer[0] = (byte) (b & 0xff);
- outputChunk.setBytes(singleByteBuffer, 0, 1);
- buffer.doWrite(outputChunk, null);
+ outputChunk.put(0, (byte) (b & 0xff));
+ buffer.doWrite(outputChunk);
}
@Override
public void write(byte[] b, int off, int len)
throws IOException {
- outputChunk.setBytes(b, off, len);
- buffer.doWrite(outputChunk, null);
+ buffer.doWrite(ByteBuffer.wrap(b, off, len));
}
@Override
public void flush() throws IOException {/*NOOP*/}
diff --git a/java/org/apache/coyote/http11/filters/IdentityInputFilter.java b/java/org/apache/coyote/http11/filters/IdentityInputFilter.java
index 68be923..b52a531 100644
--- a/java/org/apache/coyote/http11/filters/IdentityInputFilter.java
+++ b/java/org/apache/coyote/http11/filters/IdentityInputFilter.java
@@ -18,12 +18,14 @@
package org.apache.coyote.http11.filters;
import java.io.IOException;
+import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import org.apache.coyote.InputBuffer;
import org.apache.coyote.Request;
import org.apache.coyote.http11.InputFilter;
import org.apache.tomcat.util.buf.ByteChunk;
+import org.apache.tomcat.util.net.ApplicationBufferHandler;
import org.apache.tomcat.util.res.StringManager;
/**
@@ -31,7 +33,7 @@ import org.apache.tomcat.util.res.StringManager;
*
* @author Remy Maucherat
*/
-public class IdentityInputFilter implements InputFilter {
+public class IdentityInputFilter implements InputFilter, ApplicationBufferHandler {
private static final StringManager sm = StringManager.getManager(
IdentityInputFilter.class.getPackage().getName());
@@ -75,9 +77,9 @@ public class IdentityInputFilter implements InputFilter {
/**
- * Chunk used to read leftover bytes.
+ * ByteBuffer used to read leftover bytes.
*/
- protected final ByteChunk endChunk = new ByteChunk();
+ protected ByteBuffer tempRead;
private final int maxSwallowSize;
@@ -91,23 +93,17 @@ public class IdentityInputFilter implements InputFilter {
// ---------------------------------------------------- InputBuffer Methods
/**
- * Read bytes.
- *
- * @return If the filter does request length control, this value is
- * significant; it should be the number of bytes consumed from the buffer,
- * up until the end of the current request body, or the buffer length,
- * whichever is greater. If the filter does not do request body length
- * control, the returned value should be -1.
+ * @deprecated Unused. Will be removed in Tomcat 9. Use
+ * {@link #doRead(ApplicationBufferHandler)}
*/
@Override
- public int doRead(ByteChunk chunk, Request req)
- throws IOException {
+ public int doRead(ByteChunk chunk) throws IOException {
int result = -1;
if (contentLength >= 0) {
if (remaining > 0) {
- int nRead = buffer.doRead(chunk, req);
+ int nRead = buffer.doRead(chunk);
if (nRead > remaining) {
// The chunk is longer than the number of bytes remaining
// in the body; changing the chunk length to the number
@@ -133,6 +129,40 @@ public class IdentityInputFilter implements InputFilter {
}
+ @Override
+ public int doRead(ApplicationBufferHandler handler) throws IOException {
+
+ int result = -1;
+
+ if (contentLength >= 0) {
+ if (remaining > 0) {
+ int nRead = buffer.doRead(handler);
+ if (nRead > remaining) {
+ // The chunk is longer than the number of bytes remaining
+ // in the body; changing the chunk length to the number
+ // of bytes remaining
+ handler.getByteBuffer().limit(handler.getByteBuffer().position() + (int) remaining);
+ result = (int) remaining;
+ } else {
+ result = nRead;
+ }
+ if (nRead > 0) {
+ remaining = remaining - nRead;
+ }
+ } else {
+ // No more bytes left to be read : return -1 and clear the
+ // buffer
+ if (handler.getByteBuffer() != null) {
+ handler.getByteBuffer().position(0).limit(0);
+ }
+ result = -1;
+ }
+ }
+
+ return result;
+
+ }
+
// ---------------------------------------------------- InputFilter Methods
@@ -156,7 +186,8 @@ public class IdentityInputFilter implements InputFilter {
// Consume extra bytes.
while (remaining > 0) {
- int nread = buffer.doRead(endChunk, null);
+ int nread = buffer.doRead(this);
+ tempRead = null;
if (nread > 0 ) {
swallowed += nread;
remaining = remaining - nread;
@@ -202,7 +233,6 @@ public class IdentityInputFilter implements InputFilter {
public void recycle() {
contentLength = -1;
remaining = 0;
- endChunk.recycle();
}
@@ -222,4 +252,22 @@ public class IdentityInputFilter implements InputFilter {
// remaining
return contentLength > -1 && remaining <= 0;
}
+
+
+ @Override
+ public void setByteBuffer(ByteBuffer buffer) {
+ tempRead = buffer;
+ }
+
+
+ @Override
+ public ByteBuffer getByteBuffer() {
+ return tempRead;
+ }
+
+
+ @Override
+ public void expand(int size) {
+ // no-op
+ }
}
diff --git a/java/org/apache/coyote/http11/filters/IdentityOutputFilter.java b/java/org/apache/coyote/http11/filters/IdentityOutputFilter.java
index 0f78231..0b764f0 100644
--- a/java/org/apache/coyote/http11/filters/IdentityOutputFilter.java
+++ b/java/org/apache/coyote/http11/filters/IdentityOutputFilter.java
@@ -18,6 +18,7 @@
package org.apache.coyote.http11.filters;
import java.io.IOException;
+import java.nio.ByteBuffer;
import org.apache.coyote.OutputBuffer;
import org.apache.coyote.Response;
@@ -55,15 +56,12 @@ public class IdentityOutputFilter implements OutputFilter {
// --------------------------------------------------- OutputBuffer Methods
-
/**
- * Write some bytes.
- *
- * @return number of bytes written by the filter
+ * @deprecated Unused. Will be removed in Tomcat 9. Use
+ * {@link #doWrite(ByteBuffer)}
*/
@Override
- public int doWrite(ByteChunk chunk, Response res)
- throws IOException {
+ public int doWrite(ByteChunk chunk) throws IOException {
int result = -1;
@@ -81,7 +79,7 @@ public class IdentityOutputFilter implements OutputFilter {
} else {
remaining = remaining - result;
}
- buffer.doWrite(chunk, res);
+ buffer.doWrite(chunk);
} else {
// No more bytes left to be written : return -1 and clear the
// buffer
@@ -90,7 +88,7 @@ public class IdentityOutputFilter implements OutputFilter {
}
} else {
// If no content length was set, just write the bytes
- buffer.doWrite(chunk, res);
+ buffer.doWrite(chunk);
result = chunk.getLength();
}
@@ -100,6 +98,44 @@ public class IdentityOutputFilter implements OutputFilter {
@Override
+ public int doWrite(ByteBuffer chunk) throws IOException {
+
+ int result = -1;
+
+ if (contentLength >= 0) {
+ if (remaining > 0) {
+ result = chunk.remaining();
+ if (result > remaining) {
+ // The chunk is longer than the number of bytes remaining
+ // in the body; changing the chunk length to the number
+ // of bytes remaining
+ chunk.limit(chunk.position() + (int) remaining);
+ result = (int) remaining;
+ remaining = 0;
+ } else {
+ remaining = remaining - result;
+ }
+ buffer.doWrite(chunk);
+ } else {
+ // No more bytes left to be written : return -1 and clear the
+ // buffer
+ chunk.position(0);
+ chunk.limit(0);
+ result = -1;
+ }
+ } else {
+ // If no content length was set, just write the bytes
+ result = chunk.remaining();
+ buffer.doWrite(chunk);
+ result -= chunk.remaining();
+ }
+
+ return result;
+
+ }
+
+
+ @Override
public long getBytesWritten() {
return buffer.getBytesWritten();
}
diff --git a/java/org/apache/coyote/http11/filters/SavedRequestInputFilter.java b/java/org/apache/coyote/http11/filters/SavedRequestInputFilter.java
index 814dbe3..ec5d40a 100644
--- a/java/org/apache/coyote/http11/filters/SavedRequestInputFilter.java
+++ b/java/org/apache/coyote/http11/filters/SavedRequestInputFilter.java
@@ -18,10 +18,12 @@
package org.apache.coyote.http11.filters;
import java.io.IOException;
+import java.nio.ByteBuffer;
import org.apache.coyote.InputBuffer;
import org.apache.coyote.http11.InputFilter;
import org.apache.tomcat.util.buf.ByteChunk;
+import org.apache.tomcat.util.net.ApplicationBufferHandler;
/**
* Input filter responsible for replaying the request body when restoring the
@@ -44,11 +46,11 @@ public class SavedRequestInputFilter implements InputFilter {
}
/**
- * Read bytes.
+ * @deprecated Unused. Will be removed in Tomcat 9. Use
+ * {@link #doRead(ApplicationBufferHandler)}
*/
@Override
- public int doRead(ByteChunk chunk, org.apache.coyote.Request request)
- throws IOException {
+ public int doRead(ByteChunk chunk) throws IOException {
if(input.getOffset()>= input.getEnd())
return -1;
@@ -67,6 +69,18 @@ public class SavedRequestInputFilter implements InputFilter {
return writeLength;
}
+ @Override
+ public int doRead(ApplicationBufferHandler handler) throws IOException {
+ if(input.getOffset()>= input.getEnd())
+ return -1;
+
+ ByteBuffer byteBuffer = handler.getByteBuffer();
+ byteBuffer.position(byteBuffer.limit()).limit(byteBuffer.capacity());
+ input.substract(byteBuffer);
+
+ return byteBuffer.remaining();
+ }
+
/**
* Set the content length on the request.
*/
diff --git a/java/org/apache/coyote/http11/filters/VoidInputFilter.java b/java/org/apache/coyote/http11/filters/VoidInputFilter.java
index 9889ade..057145a 100644
--- a/java/org/apache/coyote/http11/filters/VoidInputFilter.java
+++ b/java/org/apache/coyote/http11/filters/VoidInputFilter.java
@@ -23,6 +23,7 @@ import org.apache.coyote.InputBuffer;
import org.apache.coyote.Request;
import org.apache.coyote.http11.InputFilter;
import org.apache.tomcat.util.buf.ByteChunk;
+import org.apache.tomcat.util.net.ApplicationBufferHandler;
/**
* Void input filter, which returns -1 when attempting a read. Used with a GET,
@@ -50,12 +51,16 @@ public class VoidInputFilter implements InputFilter {
// ---------------------------------------------------- InputBuffer Methods
/**
- * Write some bytes.
- *
- * @return number of bytes written by the filter
+ * @deprecated Unused. Will be removed in Tomcat 9. Use
+ * {@link #doRead(ApplicationBufferHandler)}
*/
@Override
- public int doRead(ByteChunk chunk, Request req) throws IOException {
+ public int doRead(ByteChunk chunk) throws IOException {
+ return -1;
+ }
+
+ @Override
+ public int doRead(ApplicationBufferHandler handler) throws IOException {
return -1;
}
diff --git a/java/org/apache/coyote/http11/filters/VoidOutputFilter.java b/java/org/apache/coyote/http11/filters/VoidOutputFilter.java
index c1e819f..a3f58a3 100644
--- a/java/org/apache/coyote/http11/filters/VoidOutputFilter.java
+++ b/java/org/apache/coyote/http11/filters/VoidOutputFilter.java
@@ -18,6 +18,7 @@
package org.apache.coyote.http11.filters;
import java.io.IOException;
+import java.nio.ByteBuffer;
import org.apache.coyote.OutputBuffer;
import org.apache.coyote.Response;
@@ -35,18 +36,19 @@ public class VoidOutputFilter implements OutputFilter {
// --------------------------------------------------- OutputBuffer Methods
-
/**
- * Write some bytes.
- *
- * @return number of bytes written by the filter
+ * @deprecated Unused. Will be removed in Tomcat 9. Use
+ * {@link #doWrite(ByteBuffer)}
*/
@Override
- public int doWrite(ByteChunk chunk, Response res)
- throws IOException {
-
+ public int doWrite(ByteChunk chunk) throws IOException {
return chunk.getLength();
+ }
+
+ @Override
+ public int doWrite(ByteBuffer chunk) throws IOException {
+ return chunk.remaining();
}
diff --git a/java/org/apache/coyote/http11/upgrade/AbstractProcessor.java b/java/org/apache/coyote/http11/upgrade/AbstractProcessor.java
deleted file mode 100644
index 5cb1bdc..0000000
--- a/java/org/apache/coyote/http11/upgrade/AbstractProcessor.java
+++ /dev/null
@@ -1,190 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.coyote.http11.upgrade;
-
-import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.util.concurrent.Executor;
-
-import javax.servlet.ServletInputStream;
-import javax.servlet.ServletOutputStream;
-import javax.servlet.http.WebConnection;
-
-import org.apache.coyote.Processor;
-import org.apache.coyote.Request;
-import org.apache.coyote.UpgradeToken;
-import org.apache.juli.logging.Log;
-import org.apache.tomcat.util.net.AbstractEndpoint.Handler.SocketState;
-import org.apache.tomcat.util.net.SSLSupport;
-import org.apache.tomcat.util.net.SocketStatus;
-import org.apache.tomcat.util.net.SocketWrapper;
-import org.apache.tomcat.util.res.StringManager;
-
-public abstract class AbstractProcessor<S>
- implements Processor<S>, WebConnection {
-
- protected static final StringManager sm =
- StringManager.getManager(Constants.Package);
- protected abstract Log getLog();
-
- private final UpgradeToken upgradeToken;
- private final AbstractServletInputStream upgradeServletInputStream;
- private final AbstractServletOutputStream<S> upgradeServletOutputStream;
-
- protected AbstractProcessor(UpgradeToken upgradeToken,
- AbstractServletInputStream upgradeServletInputStream,
- AbstractServletOutputStream<S> upgradeServletOutputStream) {
- this.upgradeToken = upgradeToken;
- this.upgradeServletInputStream = upgradeServletInputStream;
- this.upgradeServletOutputStream = upgradeServletOutputStream;
- }
-
-
- // --------------------------------------------------- AutoCloseable methods
-
- @Override
- public void close() throws Exception {
- upgradeServletInputStream.close();
- upgradeServletOutputStream.close();
- }
-
-
- // --------------------------------------------------- WebConnection methods
-
- @Override
- public ServletInputStream getInputStream() throws IOException {
- return upgradeServletInputStream;
- }
-
- @Override
- public ServletOutputStream getOutputStream() throws IOException {
- return upgradeServletOutputStream;
- }
-
-
- // ------------------------------------------- Implemented Processor methods
-
- @Override
- public final boolean isUpgrade() {
- return true;
- }
-
- @Override
- public UpgradeToken getUpgradeToken() {
- return upgradeToken;
- }
-
- @Override
- public final SocketState upgradeDispatch(SocketStatus status)
- throws IOException {
-
- if (status == SocketStatus.OPEN_READ) {
- upgradeServletInputStream.onDataAvailable();
- } else if (status == SocketStatus.OPEN_WRITE) {
- upgradeServletOutputStream.onWritePossible();
- } else if (status == SocketStatus.STOP) {
- try {
- upgradeServletInputStream.close();
- } catch (IOException ioe) {
- getLog().debug(sm.getString(
- "abstractProcessor.isCloseFail", ioe));
- }
- try {
- upgradeServletOutputStream.close();
- } catch (IOException ioe) {
- getLog().debug(sm.getString(
- "abstractProcessor.osCloseFail", ioe));
- }
- return SocketState.CLOSED;
- } else if (status == SocketStatus.DISCONNECT) {
- upgradeServletInputStream.onError(
- new IOException(sm.getString("abstractProcessor.clientAbort")));
- } else {
- // Unexpected state
- return SocketState.CLOSED;
- }
- if (upgradeServletInputStream.isCloseRequired() ||
- upgradeServletOutputStream.isCloseRequired()) {
- return SocketState.CLOSED;
- }
- return SocketState.UPGRADED;
- }
-
- @Override
- public final void recycle(boolean socketClosing) {
- // Currently a NO-OP as upgrade processors are not recycled.
- }
-
-
- // ---------------------------- Processor methods that are NO-OP for upgrade
-
- @Override
- public final Executor getExecutor() {
- return null;
- }
-
- @Override
- public final SocketState process(SocketWrapper<S> socketWrapper)
- throws IOException {
- return null;
- }
-
- @Override
- public final SocketState event(SocketStatus status) throws IOException {
- return null;
- }
-
- @Override
- public final SocketState asyncDispatch(SocketStatus status) {
- return null;
- }
-
- @Override
- public void errorDispatch() {
- // NO-OP
- }
-
- @Override
- public final SocketState asyncPostProcess() {
- return null;
- }
-
- @Override
- public final boolean isComet() {
- return false;
- }
-
- @Override
- public final boolean isAsync() {
- return false;
- }
-
- @Override
- public final Request getRequest() {
- return null;
- }
-
- @Override
- public final void setSslSupport(SSLSupport sslSupport) {
- // NOOP
- }
-
- @Override
- public ByteBuffer getLeftoverInput() {
- return null;
- }
-}
diff --git a/java/org/apache/coyote/http11/upgrade/AbstractServletInputStream.java b/java/org/apache/coyote/http11/upgrade/AbstractServletInputStream.java
deleted file mode 100644
index 37b3041..0000000
--- a/java/org/apache/coyote/http11/upgrade/AbstractServletInputStream.java
+++ /dev/null
@@ -1,250 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.coyote.http11.upgrade;
-
-import java.io.IOException;
-
-import javax.servlet.ReadListener;
-import javax.servlet.ServletInputStream;
-
-import org.apache.juli.logging.Log;
-import org.apache.juli.logging.LogFactory;
-import org.apache.tomcat.util.ExceptionUtils;
-import org.apache.tomcat.util.res.StringManager;
-
-public abstract class AbstractServletInputStream extends ServletInputStream {
-
- private static final Log log = LogFactory.getLog(AbstractServletInputStream.class);
- protected static final StringManager sm =
- StringManager.getManager(Constants.Package);
-
- private volatile boolean closeRequired = false;
- // Start in blocking-mode
- private volatile Boolean ready = Boolean.TRUE;
- private volatile ReadListener listener = null;
- private volatile ClassLoader applicationLoader = null;
-
- @Override
- public final boolean isFinished() {
- if (listener == null) {
- throw new IllegalStateException(
- sm.getString("upgrade.sis.isFinished.ise"));
- }
- // The only way to finish an HTTP Upgrade connection is to close the
- // socket.
- return false;
- }
-
-
- @Override
- public final boolean isReady() {
- if (listener == null) {
- throw new IllegalStateException(
- sm.getString("upgrade.sis.isReady.ise"));
- }
-
- // If we already know the current state, return it.
- if (ready != null) {
- return ready.booleanValue();
- }
-
- try {
- ready = Boolean.valueOf(doIsReady());
- } catch (IOException e) {
- onError(e);
- }
- return ready.booleanValue();
- }
-
-
- @Override
- public final void setReadListener(ReadListener listener) {
- if (listener == null) {
- throw new IllegalArgumentException(
- sm.getString("upgrade.sis.readListener.null"));
- }
- if (this.listener != null) {
- throw new IllegalArgumentException(
- sm.getString("upgrade.sis.readListener.set"));
- }
- this.listener = listener;
- this.applicationLoader = Thread.currentThread().getContextClassLoader();
- // Switching to non-blocking. Don't know if data is available.
- ready = null;
- }
-
-
- @Override
- public final int read() throws IOException {
- preReadChecks();
-
- return readInternal();
- }
-
-
- @Override
- public final int readLine(byte[] b, int off, int len) throws IOException {
- preReadChecks();
-
- if (len <= 0) {
- return 0;
- }
- int count = 0, c;
-
- while ((c = readInternal()) != -1) {
- b[off++] = (byte) c;
- count++;
- if (c == '\n' || count == len) {
- break;
- }
- }
- return count > 0 ? count : -1;
- }
-
-
- @Override
- public final int read(byte[] b, int off, int len) throws IOException {
- preReadChecks();
-
- try {
- return doRead(listener == null, b, off, len);
- } catch (IOException ioe) {
- closeRequired = true;
- throw ioe;
- }
- }
-
-
-
- @Override
- public void close() throws IOException {
- closeRequired = true;
- doClose();
- }
-
-
- private void preReadChecks() {
- if (listener != null && (ready == null || !ready.booleanValue())) {
- throw new IllegalStateException(
- sm.getString("upgrade.sis.read.ise"));
- }
- // No longer know if data is available
- ready = null;
- }
-
-
- private int readInternal() throws IOException {
- // Single byte reads for non-blocking need special handling so all
- // single byte reads run through this method.
- byte[] b = new byte[1];
- int result;
- try {
- result = doRead(listener == null, b, 0, 1);
- } catch (IOException ioe) {
- closeRequired = true;
- throw ioe;
- }
- if (result == 0) {
- return -1;
- } else if (result == -1) {
- // Will never happen with a network socket. An IOException will be
- // thrown when the client closes the connection.
- // Echo back the -1 to be safe.
- return -1;
- } else {
- return b[0] & 0xFF;
- }
- }
-
-
- protected final void onAllDataRead() throws IOException {
- if (listener == null) {
- return;
- }
- Thread thread = Thread.currentThread();
- ClassLoader originalClassLoader = thread.getContextClassLoader();
- try {
- thread.setContextClassLoader(applicationLoader);
- listener.onAllDataRead();
- } finally {
- thread.setContextClassLoader(originalClassLoader);
- }
- }
-
-
- final void onDataAvailable() {
- if (listener == null) {
- return;
- }
- ready = Boolean.TRUE;
- Thread thread = Thread.currentThread();
- ClassLoader originalClassLoader = thread.getContextClassLoader();
- try {
- thread.setContextClassLoader(applicationLoader);
- listener.onDataAvailable();
- } catch (Throwable t) {
- ExceptionUtils.handleThrowable(t);
- onError(t);
- } finally {
- thread.setContextClassLoader(originalClassLoader);
- }
- }
-
-
- protected final void onError(Throwable t) {
- if (listener == null) {
- return;
- }
- Thread thread = Thread.currentThread();
- ClassLoader originalClassLoader = thread.getContextClassLoader();
- try {
- thread.setContextClassLoader(applicationLoader);
- listener.onError(t);
- } catch (Throwable t2) {
- ExceptionUtils.handleThrowable(t2);
- log.warn(sm.getString("upgrade.sis.onErrorFail"), t2);
- } finally {
- thread.setContextClassLoader(originalClassLoader);
- }
- try {
- close();
- } catch (IOException ioe) {
- if (log.isDebugEnabled()) {
- log.debug(sm.getString("upgrade.sis.errorCloseFail"), ioe);
- }
- }
- ready = Boolean.FALSE;
- }
-
-
- final boolean isCloseRequired() {
- return closeRequired;
- }
-
-
- protected abstract boolean doIsReady() throws IOException;
-
- /**
- * Abstract method to be overridden by concrete implementations. The base
- * class will ensure that there are no concurrent calls to this method for
- * the same socket.
- */
- protected abstract int doRead(boolean block, byte[] b, int off, int len)
- throws IOException;
-
- protected abstract void doClose() throws IOException;
-}
diff --git a/java/org/apache/coyote/http11/upgrade/AbstractServletOutputStream.java b/java/org/apache/coyote/http11/upgrade/AbstractServletOutputStream.java
deleted file mode 100644
index 2b6ab08..0000000
--- a/java/org/apache/coyote/http11/upgrade/AbstractServletOutputStream.java
+++ /dev/null
@@ -1,268 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.coyote.http11.upgrade;
-
-import java.io.IOException;
-
-import javax.servlet.ServletOutputStream;
-import javax.servlet.WriteListener;
-
-import org.apache.juli.logging.Log;
-import org.apache.juli.logging.LogFactory;
-import org.apache.tomcat.util.ExceptionUtils;
-import org.apache.tomcat.util.net.DispatchType;
-import org.apache.tomcat.util.net.SocketWrapper;
-import org.apache.tomcat.util.res.StringManager;
-
-public abstract class AbstractServletOutputStream<S> extends ServletOutputStream {
-
- private static final Log log = LogFactory.getLog(AbstractServletOutputStream.class);
- protected static final StringManager sm =
- StringManager.getManager(Constants.Package);
-
- protected final SocketWrapper<S> socketWrapper;
-
- // Used to ensure that isReady() and onWritePossible() have a consistent
- // view of buffer and fireListener when determining if the listener should
- // fire.
- private final Object fireListenerLock = new Object();
-
- // Used to ensure that only one thread writes to the socket at a time and
- // that buffer is consistently updated with any unwritten data after the
- // write. Note it is not necessary to hold this lock when checking if buffer
- // contains data but, depending on how the result is used, some form of
- // synchronization may be required (see fireListenerLock for an example).
- private final Object writeLock = new Object();
-
- private volatile boolean closeRequired = false;
-
- // Start in blocking-mode
- private volatile WriteListener listener = null;
-
- // Guarded by fireListenerLock
- private volatile boolean fireListener = false;
-
- private volatile ClassLoader applicationLoader = null;
-
- // Writes guarded by writeLock
- private volatile byte[] buffer;
- private volatile int bufferPos;
- private volatile int bufferLimit;
- private final int asyncWriteBufferSize;
-
-
- public AbstractServletOutputStream(SocketWrapper<S> socketWrapper,
- int asyncWriteBufferSize) {
- this.socketWrapper = socketWrapper;
- this.asyncWriteBufferSize = asyncWriteBufferSize;
- buffer = new byte[asyncWriteBufferSize];
- }
-
-
- @Override
- public final boolean isReady() {
- if (listener == null) {
- throw new IllegalStateException(
- sm.getString("upgrade.sos.canWrite.ise"));
- }
-
- // Make sure isReady() and onWritePossible() have a consistent view of
- // buffer and fireListener when determining if the listener should fire
- synchronized (fireListenerLock) {
- boolean result = (bufferLimit == 0);
- fireListener = !result;
- return result;
- }
- }
-
-
- @Override
- public final void setWriteListener(WriteListener listener) {
- if (listener == null) {
- throw new IllegalArgumentException(
- sm.getString("upgrade.sos.writeListener.null"));
- }
- if (this.listener != null) {
- throw new IllegalArgumentException(
- sm.getString("upgrade.sos.writeListener.set"));
- }
- // Container is responsible for first call to onWritePossible() but only
- // need to do this if setting the listener for the first time.
- synchronized (fireListenerLock) {
- fireListener = true;
- }
- socketWrapper.addDispatch(DispatchType.NON_BLOCKING_WRITE);
- this.listener = listener;
- this.applicationLoader = Thread.currentThread().getContextClassLoader();
- }
-
-
- final boolean isCloseRequired() {
- return closeRequired;
- }
-
-
- @Override
- public void write(int b) throws IOException {
- synchronized (writeLock) {
- preWriteChecks();
- writeInternal(new byte[] { (byte) b }, 0, 1);
- }
- }
-
-
- @Override
- public void write(byte[] b, int off, int len) throws IOException {
- synchronized (writeLock) {
- preWriteChecks();
- writeInternal(b, off, len);
- }
- }
-
-
- @Override
- public void close() throws IOException {
- closeRequired = true;
- doClose();
- }
-
-
- private void preWriteChecks() {
- if (bufferLimit != 0) {
- throw new IllegalStateException(sm.getString("upgrade.sis.write.ise"));
- }
- }
-
-
- /**
- * 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
- doWrite(true, b, off, len);
- } else {
- // Non-blocking IO
- // If the non-blocking read does not complete, doWrite() will add
- // the socket back into the poller. The poller may trigger a new
- // write event before this method has finished updating buffer. The
- // writeLock sync makes sure that buffer is updated before the next
- // write executes.
- int written = doWrite(false, b, off, len);
- if (written < len) {
- if (b == buffer) {
- // This is a partial write of the existing buffer. Just
- // increment the current position
- bufferPos += written;
- } else {
- // This is a new partial write
- int bytesLeft = len - written;
- if (bytesLeft > buffer.length) {
- buffer = new byte[bytesLeft];
- } else if (bytesLeft < asyncWriteBufferSize &&
- buffer.length > asyncWriteBufferSize) {
- buffer = new byte[asyncWriteBufferSize];
- }
- bufferPos = 0;
- bufferLimit = bytesLeft;
- System.arraycopy(b, off + written, buffer, bufferPos, bufferLimit);
- }
- } else {
- bufferLimit = 0;
- }
- }
- }
-
-
- final void onWritePossible() {
- try {
- synchronized (writeLock) {
- if (bufferLimit > 0) {
- writeInternal(buffer, bufferPos, bufferLimit - bufferPos);
- }
- }
- } catch (Throwable t) {
- ExceptionUtils.handleThrowable(t);
- onError(t);
- return;
- }
-
- // Make sure isReady() and onWritePossible() have a consistent view
- // of buffer and fireListener when determining if the listener
- // should fire
- boolean fire = false;
- synchronized (fireListenerLock) {
- if (bufferLimit == 0 && fireListener) {
- fireListener = false;
- fire = true;
- }
- }
-
- if (fire) {
- Thread thread = Thread.currentThread();
- ClassLoader originalClassLoader = thread.getContextClassLoader();
- try {
- thread.setContextClassLoader(applicationLoader);
- listener.onWritePossible();
- } catch (Throwable t) {
- ExceptionUtils.handleThrowable(t);
- onError(t);
- } finally {
- thread.setContextClassLoader(originalClassLoader);
- }
- }
- }
-
-
- protected final void onError(Throwable t) {
- if (listener == null) {
- return;
- }
- Thread thread = Thread.currentThread();
- ClassLoader originalClassLoader = thread.getContextClassLoader();
- try {
- thread.setContextClassLoader(applicationLoader);
- listener.onError(t);
- } catch (Throwable t2) {
- ExceptionUtils.handleThrowable(t2);
- log.warn(sm.getString("upgrade.sos.onErrorFail"), t2);
- } finally {
- thread.setContextClassLoader(originalClassLoader);
- }
- try {
- close();
- } catch (IOException ioe) {
- if (log.isDebugEnabled()) {
- log.debug(sm.getString("upgrade.sos.errorCloseFail"), ioe);
- }
- }
- }
-
-
- /**
- * Abstract method to be overridden by concrete implementations. The base
- * class will ensure that there are no concurrent calls to this method for
- * the same socket by ensuring that the writeLock is held when making any
- * calls to this method.
- */
- protected abstract int doWrite(boolean block, byte[] b, int off, int len)
- throws IOException;
-
- protected abstract void doFlush() throws IOException;
-
- protected abstract void doClose() throws IOException;
-}
diff --git a/java/org/apache/coyote/http11/upgrade/AprProcessor.java b/java/org/apache/coyote/http11/upgrade/AprProcessor.java
deleted file mode 100644
index 1b0c4c6..0000000
--- a/java/org/apache/coyote/http11/upgrade/AprProcessor.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.coyote.http11.upgrade;
-
-import java.nio.ByteBuffer;
-
-import org.apache.coyote.UpgradeToken;
-import org.apache.juli.logging.Log;
-import org.apache.juli.logging.LogFactory;
-import org.apache.tomcat.jni.Socket;
-import org.apache.tomcat.util.net.AprEndpoint;
-import org.apache.tomcat.util.net.SocketWrapper;
-
-public class AprProcessor extends AbstractProcessor<Long> {
-
- private static final Log log = LogFactory.getLog(AprProcessor.class);
- @Override
- protected Log getLog() {return log;}
-
- private static final int INFINITE_TIMEOUT = -1;
-
- public AprProcessor(SocketWrapper<Long> wrapper, ByteBuffer leftoverInput,
- UpgradeToken upgradeToken, AprEndpoint endpoint,
- int asyncWriteBufferSize) {
- super(upgradeToken,
- new AprServletInputStream(wrapper, leftoverInput),
- new AprServletOutputStream(wrapper, asyncWriteBufferSize, endpoint));
-
- Socket.timeoutSet(wrapper.getSocket().longValue(), INFINITE_TIMEOUT);
- }
-}
diff --git a/java/org/apache/coyote/http11/upgrade/AprServletInputStream.java b/java/org/apache/coyote/http11/upgrade/AprServletInputStream.java
deleted file mode 100644
index add50ef..0000000
--- a/java/org/apache/coyote/http11/upgrade/AprServletInputStream.java
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.coyote.http11.upgrade;
-
-import java.io.EOFException;
-import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.util.concurrent.locks.Lock;
-import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
-
-import org.apache.juli.logging.Log;
-import org.apache.juli.logging.LogFactory;
-import org.apache.tomcat.jni.OS;
-import org.apache.tomcat.jni.Socket;
-import org.apache.tomcat.jni.Status;
-import org.apache.tomcat.util.net.SocketWrapper;
-
-public class AprServletInputStream extends AbstractServletInputStream {
-
- private static final Log log = LogFactory.getLog(AprServletInputStream.class);
-
- private final SocketWrapper<Long> wrapper;
- private final long socket;
- private ByteBuffer leftoverInput;
- private volatile boolean eagain = false;
- private volatile boolean closed = false;
-
-
- public AprServletInputStream(SocketWrapper<Long> wrapper, ByteBuffer leftoverInput) {
- this.wrapper = wrapper;
- this.socket = wrapper.getSocket().longValue();
- if (leftoverInput != null) {
- this.leftoverInput = ByteBuffer.allocate(leftoverInput.remaining());
- this.leftoverInput.put(leftoverInput);
- }
- }
-
-
- @Override
- protected int doRead(boolean block, byte[] b, int off, int len)
- throws IOException {
-
- if (closed) {
- throw new IOException(sm.getString("apr.closed", Long.valueOf(socket)));
- }
-
- if (leftoverInput != null) {
- if (leftoverInput.remaining() < len) {
- len = leftoverInput.remaining();
- }
- leftoverInput.get(b, off, len);
- if (leftoverInput.remaining() == 0) {
- leftoverInput = null;
- }
- return len;
- }
-
- Lock readLock = wrapper.getBlockingStatusReadLock();
- WriteLock writeLock = wrapper.getBlockingStatusWriteLock();
-
- boolean readDone = false;
- int result = 0;
- readLock.lock();
- try {
- if (wrapper.getBlockingStatus() == block) {
- result = Socket.recv(socket, b, off, len);
- readDone = true;
- }
- } finally {
- readLock.unlock();
- }
-
- if (!readDone) {
- writeLock.lock();
- try {
- wrapper.setBlockingStatus(block);
- // Set the current settings for this socket
- Socket.optSet(socket, Socket.APR_SO_NONBLOCK, (block ? 0 : 1));
- // Downgrade the lock
- readLock.lock();
- try {
- writeLock.unlock();
- result = Socket.recv(socket, b, off, len);
- } finally {
- readLock.unlock();
- }
- } finally {
- // Should have been released above but may not have been on some
- // exception paths
- if (writeLock.isHeldByCurrentThread()) {
- writeLock.unlock();
- }
- }
- }
-
- if (result > 0) {
- eagain = false;
- return result;
- } else if (-result == Status.EAGAIN) {
- eagain = true;
- return 0;
- } else if (-result == Status.APR_EGENERAL && wrapper.isSecure()) {
- // Not entirely sure why this is necessary. Testing to date has not
- // identified any issues with this but log it so it can be tracked
- // if it is suspected of causing issues in the future.
- if (log.isDebugEnabled()) {
- log.debug(sm.getString("apr.read.sslGeneralError",
- Long.valueOf(socket), wrapper));
- }
- eagain = true;
- return 0;
- } else if (-result == Status.APR_EOF) {
- throw new EOFException(sm.getString("apr.clientAbort"));
- } else if ((OS.IS_WIN32 || OS.IS_WIN64) &&
- (-result == Status.APR_OS_START_SYSERR + 10053)) {
- // 10053 on Windows is connection aborted
- throw new EOFException(sm.getString("apr.clientAbort"));
- } else {
- throw new IOException(sm.getString("apr.read.error",
- Integer.valueOf(-result), Long.valueOf(socket), wrapper));
- }
- }
-
-
- @Override
- protected boolean doIsReady() {
- return !eagain;
- }
-
-
- @Override
- protected void doClose() throws IOException {
- closed = true;
- // AbstractProcessor needs to trigger the close as multiple closes for
- // APR/native sockets will cause problems.
- }
-}
diff --git a/java/org/apache/coyote/http11/upgrade/AprServletOutputStream.java b/java/org/apache/coyote/http11/upgrade/AprServletOutputStream.java
deleted file mode 100644
index 0b6bc62..0000000
--- a/java/org/apache/coyote/http11/upgrade/AprServletOutputStream.java
+++ /dev/null
@@ -1,168 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.coyote.http11.upgrade;
-
-import java.io.EOFException;
-import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.util.concurrent.locks.Lock;
-import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
-
-import org.apache.tomcat.jni.OS;
-import org.apache.tomcat.jni.Socket;
-import org.apache.tomcat.jni.Status;
-import org.apache.tomcat.util.net.AprEndpoint;
-import org.apache.tomcat.util.net.SocketWrapper;
-
-public class AprServletOutputStream extends AbstractServletOutputStream<Long> {
-
- private static final int SSL_OUTPUT_BUFFER_SIZE = 8192;
-
- private final AprEndpoint endpoint;
- private final long socket;
- private volatile boolean closed = false;
- private final ByteBuffer sslOutputBuffer;
-
- public AprServletOutputStream(SocketWrapper<Long> socketWrapper,
- int asyncWriteBufferSize, AprEndpoint endpoint) {
- super(socketWrapper, asyncWriteBufferSize);
- this.endpoint = endpoint;
- this.socket = socketWrapper.getSocket().longValue();
- if (endpoint.isSSLEnabled()) {
- sslOutputBuffer = ByteBuffer.allocateDirect(SSL_OUTPUT_BUFFER_SIZE);
- sslOutputBuffer.position(SSL_OUTPUT_BUFFER_SIZE);
- } else {
- sslOutputBuffer = null;
- }
- }
-
-
- @Override
- protected int doWrite(boolean block, byte[] b, int off, int len)
- throws IOException {
-
- if (closed) {
- throw new IOException(sm.getString("apr.closed", Long.valueOf(socket)));
- }
-
- Lock readLock = socketWrapper.getBlockingStatusReadLock();
- WriteLock writeLock = socketWrapper.getBlockingStatusWriteLock();
-
- readLock.lock();
- try {
- if (socketWrapper.getBlockingStatus() == block) {
- return doWriteInternal(b, off, len);
- }
- } finally {
- readLock.unlock();
- }
-
- writeLock.lock();
- try {
- // Set the current settings for this socket
- socketWrapper.setBlockingStatus(block);
- if (block) {
- Socket.timeoutSet(socket, endpoint.getSoTimeout() * 1000);
- } else {
- Socket.timeoutSet(socket, 0);
- }
-
- // Downgrade the lock
- readLock.lock();
- try {
- writeLock.unlock();
- return doWriteInternal(b, off, len);
- } finally {
- readLock.unlock();
- }
- } finally {
- // Should have been released above but may not have been on some
- // exception paths
- if (writeLock.isHeldByCurrentThread()) {
- writeLock.unlock();
- }
- }
- }
-
-
- private int doWriteInternal(byte[] b, int off, int len) throws IOException {
-
- int start = off;
- int left = len;
- int written;
-
- do {
- if (endpoint.isSSLEnabled()) {
- if (sslOutputBuffer.remaining() == 0) {
- // Buffer was fully written last time around
- sslOutputBuffer.clear();
- if (left < SSL_OUTPUT_BUFFER_SIZE) {
- sslOutputBuffer.put(b, start, left);
- } else {
- sslOutputBuffer.put(b, start, SSL_OUTPUT_BUFFER_SIZE);
- }
- sslOutputBuffer.flip();
- } else {
- // Buffer still has data from previous attempt to write
- // APR + SSL requires that exactly the same parameters are
- // passed when re-attempting the write
- }
- written = Socket.sendb(socket, sslOutputBuffer,
- sslOutputBuffer.position(), sslOutputBuffer.limit());
- if (written > 0) {
- sslOutputBuffer.position(
- sslOutputBuffer.position() + written);
- }
- } else {
- written = Socket.send(socket, b, start, left);
- }
- if (Status.APR_STATUS_IS_EAGAIN(-written)) {
- written = 0;
- } else if (-written == Status.APR_EOF) {
- throw new EOFException(sm.getString("apr.clientAbort"));
- } else if ((OS.IS_WIN32 || OS.IS_WIN64) &&
- (-written == Status.APR_OS_START_SYSERR + 10053)) {
- // 10053 on Windows is connection aborted
- throw new EOFException(sm.getString("apr.clientAbort"));
- } else if (written < 0) {
- throw new IOException(sm.getString("apr.write.error",
- Integer.valueOf(-written), Long.valueOf(socket), socketWrapper));
- }
- start += written;
- left -= written;
- } while (written > 0 && left > 0);
-
- if (left > 0) {
- socketWrapper.registerforEvent(-1, false, true);
- }
- return len - left;
- }
-
-
- @Override
- protected void doFlush() throws IOException {
- // TODO Auto-generated method stub
- }
-
-
- @Override
- protected void doClose() throws IOException {
- closed = true;
- // AbstractProcessor needs to trigger the close as multiple closes for
- // APR/native sockets will cause problems.
- }
-}
diff --git a/java/org/apache/coyote/http11/upgrade/BioProcessor.java b/java/org/apache/coyote/http11/upgrade/BioProcessor.java
deleted file mode 100644
index c09d36c..0000000
--- a/java/org/apache/coyote/http11/upgrade/BioProcessor.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.coyote.http11.upgrade;
-
-import java.io.IOException;
-import java.net.Socket;
-import java.nio.ByteBuffer;
-
-import org.apache.coyote.UpgradeToken;
-import org.apache.juli.logging.Log;
-import org.apache.juli.logging.LogFactory;
-import org.apache.tomcat.util.net.SocketWrapper;
-
-public class BioProcessor extends AbstractProcessor<Socket> {
-
- private static final Log log = LogFactory.getLog(BioProcessor.class);
- @Override
- protected Log getLog() {return log;}
-
- private static final int INFINITE_TIMEOUT = 0;
-
- public BioProcessor(SocketWrapper<Socket> wrapper, ByteBuffer leftoverInput,
- UpgradeToken upgradeToken,
- int asyncWriteBufferSize) throws IOException {
- super(upgradeToken, new BioServletInputStream(wrapper, leftoverInput),
- new BioServletOutputStream(wrapper, asyncWriteBufferSize));
-
- wrapper.getSocket().setSoTimeout(INFINITE_TIMEOUT);
- }
-}
diff --git a/java/org/apache/coyote/http11/upgrade/BioServletInputStream.java b/java/org/apache/coyote/http11/upgrade/BioServletInputStream.java
deleted file mode 100644
index 0d2ec83..0000000
--- a/java/org/apache/coyote/http11/upgrade/BioServletInputStream.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.coyote.http11.upgrade;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.Socket;
-import java.nio.ByteBuffer;
-
-import org.apache.tomcat.util.net.SocketWrapper;
-
-public class BioServletInputStream extends AbstractServletInputStream {
-
- private final InputStream inputStream;
- private ByteBuffer leftoverInput;
-
- public BioServletInputStream(SocketWrapper<Socket> wrapper, ByteBuffer leftoverInput)
- throws IOException {
- inputStream = wrapper.getSocket().getInputStream();
- if (leftoverInput != null) {
- this.leftoverInput = ByteBuffer.allocate(leftoverInput.remaining());
- this.leftoverInput.put(leftoverInput);
- }
- }
-
- @Override
- protected int doRead(boolean block, byte[] b, int off, int len)
- throws IOException {
- if (leftoverInput != null) {
- if (leftoverInput.remaining() < len) {
- len = leftoverInput.remaining();
- }
- leftoverInput.get(b, off, len);
- if (leftoverInput.remaining() == 0) {
- leftoverInput = null;
- }
- return len;
- } else {
- return inputStream.read(b, off, len);
- }
- }
-
- @Override
- protected boolean doIsReady() {
- // Always returns true for BIO
- return true;
- }
-
- @Override
- protected void doClose() throws IOException {
- inputStream.close();
- }
-}
diff --git a/java/org/apache/coyote/http11/upgrade/BioServletOutputStream.java b/java/org/apache/coyote/http11/upgrade/BioServletOutputStream.java
deleted file mode 100644
index 233f636..0000000
--- a/java/org/apache/coyote/http11/upgrade/BioServletOutputStream.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.coyote.http11.upgrade;
-
-import java.io.IOException;
-import java.io.OutputStream;
-import java.net.Socket;
-
-import org.apache.tomcat.util.net.SocketWrapper;
-
-public class BioServletOutputStream extends AbstractServletOutputStream<Socket> {
-
- private final OutputStream os;
-
- public BioServletOutputStream(SocketWrapper<Socket> socketWrapper,
- int asyncWriteBufferSize) throws IOException {
- super(socketWrapper, asyncWriteBufferSize);
- os = socketWrapper.getSocket().getOutputStream();
- }
-
- @Override
- protected int doWrite(boolean block, byte[] b, int off, int len)
- throws IOException {
- os.write(b, off, len);
- return len;
- }
-
- @Override
- protected void doFlush() throws IOException {
- os.flush();
- }
-
- @Override
- protected void doClose() throws IOException {
- os.close();
- }
-}
diff --git a/java/org/apache/coyote/http11/upgrade/Constants.java b/java/org/apache/coyote/http11/upgrade/Constants.java
deleted file mode 100644
index d3764a7..0000000
--- a/java/org/apache/coyote/http11/upgrade/Constants.java
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.coyote.http11.upgrade;
-
-public class Constants {
-
- public static final String Package = "org.apache.coyote.http11.upgrade";
-}
diff --git a/java/org/apache/coyote/http11/upgrade/InternalHttpUpgradeHandler.java b/java/org/apache/coyote/http11/upgrade/InternalHttpUpgradeHandler.java
new file mode 100644
index 0000000..936784e
--- /dev/null
+++ b/java/org/apache/coyote/http11/upgrade/InternalHttpUpgradeHandler.java
@@ -0,0 +1,40 @@
+/*
+ * 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.http11.upgrade;
+
+import javax.servlet.http.HttpUpgradeHandler;
+
+import org.apache.tomcat.util.net.AbstractEndpoint.Handler.SocketState;
+import org.apache.tomcat.util.net.SSLSupport;
+import org.apache.tomcat.util.net.SocketEvent;
+import org.apache.tomcat.util.net.SocketWrapperBase;
+
+
+/**
+ * This Tomcat specific interface is implemented by handlers that require direct
+ * access to Tomcat's I/O layer rather than going through the Servlet API.
+ */
+public interface InternalHttpUpgradeHandler extends HttpUpgradeHandler {
+
+ SocketState upgradeDispatch(SocketEvent status);
+
+ void setSocketWrapper(SocketWrapperBase<?> wrapper);
+
+ void setSslSupport(SSLSupport sslSupport);
+
+ void pause();
+}
\ No newline at end of file
diff --git a/java/org/apache/coyote/http11/upgrade/LocalStrings.properties b/java/org/apache/coyote/http11/upgrade/LocalStrings.properties
index 071a35b..c9595bd 100644
--- a/java/org/apache/coyote/http11/upgrade/LocalStrings.properties
+++ b/java/org/apache/coyote/http11/upgrade/LocalStrings.properties
@@ -13,9 +13,11 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-abstractProcessor.clientAbort=Client abort
-abstractProcessor.isCloseFail=Failed to close input stream associated with upgraded connection
-abstractProcessor.osCloseFail=Failed to close output stream associated with upgraded connection
+upgradeProcessor.isCloseFail=Failed to close input stream associated with upgraded connection
+upgradeProcessor.osCloseFail=Failed to close output stream associated with upgraded connection
+upgradeProcessor.requiredClose=Closing upgraded connection due to closeRequired state of streams: Input [{0}], Output [{1}]
+upgradeProcessor.stop=Closing upgraded connection as incoming socket status was STOP
+upgradeProcessor.unexpectedState=Closing upgraded connection unexpectedly as incoming socket status was [{0}]
upgrade.sis.errorCloseFail=Failed to close InputStream cleanly after a previous error
upgrade.sis.isFinished.ise=It is illegal to call isFinished() when the ServletInputStream is not in non-blocking mode (i.e. setReadListener() must be called first)
@@ -23,17 +25,13 @@ upgrade.sis.isReady.ise=It is illegal to call isReady() when the ServletInputStr
upgrade.sis.onErrorFail=onError processing for the registered ReadListener triggered this further error which was swallowed
upgrade.sis.readListener.null=It is illegal to pass null to setReadListener()
upgrade.sis.readListener.set=It is illegal to call setReadListener() more than once for the same upgraded connection
+upgrade.sis.read.closed=The InputStream has been closed
upgrade.sis.read.ise=It is illegal to call any of the read() methods in non-blocking mode without first checking that there is data available by calling isReady()
upgrade.sos.errorCloseFail=Failed to close OutputStream cleanly after a previous error
upgrade.sos.canWrite.ise=It is illegal to call canWrite() when the ServletOutputStream is not in non-blocking mode (i.e. setWriteListener() must be called first)
upgrade.sos.onErrorFail=onError processing for the registered WriteListener triggered this further error which was swallowed
upgrade.sos.writeListener.null=It is illegal to pass null to setWriteListener()
upgrade.sos.writeListener.set=It is illegal to call setWriteListener() more than once for the same upgraded connection
-upgrade.sis.write.ise=It is illegal to call any of the write() methods in non-blocking mode without first checking that there is space available by calling isReady()
-
-apr.clientAbort=The client aborted the connection.
-apr.read.error=Unexpected error [{0}] reading data from the APR/native socket [{1}] with wrapper [{2}].
-apr.read.sslGeneralError=An APR general error was returned by the SSL read operation on APR/native socket [{0}] with wrapper [{1}]. It will be treated as EAGAIN and the socket returned to the poller.
-apr.write.error=Unexpected error [{0}] writing data to the APR/native socket [{1}] with wrapper [{2}].
-apr.closed=The socket [{0}] associated with this connection has been closed.
+upgrade.sos.write.closed=The OutputStream has been closed
+upgrade.sos.write.ise=It is illegal to call any of the write() methods in non-blocking mode without first checking that there is space available by calling isReady()
diff --git a/java/org/apache/coyote/http11/upgrade/Nio2Processor.java b/java/org/apache/coyote/http11/upgrade/Nio2Processor.java
deleted file mode 100644
index dd32515..0000000
--- a/java/org/apache/coyote/http11/upgrade/Nio2Processor.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.coyote.http11.upgrade;
-
-import java.nio.ByteBuffer;
-
-import org.apache.coyote.UpgradeToken;
-import org.apache.juli.logging.Log;
-import org.apache.juli.logging.LogFactory;
-import org.apache.tomcat.util.net.AbstractEndpoint;
-import org.apache.tomcat.util.net.Nio2Channel;
-import org.apache.tomcat.util.net.SocketWrapper;
-
-public class Nio2Processor extends AbstractProcessor<Nio2Channel> {
-
- private static final Log log = LogFactory.getLog(Nio2Processor.class);
- @Override
- protected Log getLog() {return log;}
-
- private static final int INFINITE_TIMEOUT = -1;
-
- public Nio2Processor(AbstractEndpoint<Nio2Channel> endpoint,
- SocketWrapper<Nio2Channel> wrapper, ByteBuffer leftoverInput,
- UpgradeToken upgradeToken,
- int asyncWriteBufferSize) {
- super(upgradeToken,
- new Nio2ServletInputStream(wrapper, endpoint),
- new Nio2ServletOutputStream(wrapper, asyncWriteBufferSize, endpoint));
-
- wrapper.setTimeout(INFINITE_TIMEOUT);
- if (leftoverInput != null) {
- wrapper.getSocket().getBufHandler().getReadBuffer().put(leftoverInput);
- }
- }
-}
diff --git a/java/org/apache/coyote/http11/upgrade/Nio2ServletInputStream.java b/java/org/apache/coyote/http11/upgrade/Nio2ServletInputStream.java
deleted file mode 100644
index d79f4e1..0000000
--- a/java/org/apache/coyote/http11/upgrade/Nio2ServletInputStream.java
+++ /dev/null
@@ -1,235 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.coyote.http11.upgrade;
-
-import java.io.EOFException;
-import java.io.IOException;
-import java.net.SocketTimeoutException;
-import java.nio.ByteBuffer;
-import java.nio.channels.AsynchronousCloseException;
-import java.nio.channels.CompletionHandler;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.Future;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-
-import org.apache.tomcat.util.net.AbstractEndpoint;
-import org.apache.tomcat.util.net.Nio2Channel;
-import org.apache.tomcat.util.net.Nio2Endpoint;
-import org.apache.tomcat.util.net.SocketStatus;
-import org.apache.tomcat.util.net.SocketWrapper;
-
-public class Nio2ServletInputStream extends AbstractServletInputStream {
-
- private final AbstractEndpoint<Nio2Channel> endpoint;
- private final SocketWrapper<Nio2Channel> wrapper;
- private final Nio2Channel channel;
- private final CompletionHandler<Integer, SocketWrapper<Nio2Channel>> completionHandler;
- private boolean flipped = false;
- private volatile boolean readPending = false;
- private volatile boolean interest = true;
- private volatile boolean closed = false;
-
- public Nio2ServletInputStream(SocketWrapper<Nio2Channel> wrapper, AbstractEndpoint<Nio2Channel> endpoint0) {
- this.endpoint = endpoint0;
- this.wrapper = wrapper;
- this.channel = wrapper.getSocket();
- this.completionHandler = new CompletionHandler<Integer, SocketWrapper<Nio2Channel>>() {
- @Override
- public void completed(Integer nBytes, SocketWrapper<Nio2Channel> attachment) {
- boolean notify = false;
- synchronized (completionHandler) {
- if (nBytes.intValue() < 0) {
- if (closed) {
- readPending = false;
- } else {
- failed(new EOFException(), attachment);
- }
- } else {
- readPending = false;
- if (interest && !Nio2Endpoint.isInline()) {
- interest = false;
- notify = true;
- }
- }
- }
- if (notify) {
- endpoint.processSocket(attachment, SocketStatus.OPEN_READ, false);
- }
- }
- @Override
- public void failed(Throwable exc, SocketWrapper<Nio2Channel> attachment) {
- attachment.setError(true);
- readPending = false;
- if (exc instanceof AsynchronousCloseException) {
- // If already closed, don't call onError and close again
- return;
- }
- onError(exc);
- endpoint.processSocket(attachment, SocketStatus.ERROR, true);
- }
- };
- }
-
- @Override
- protected boolean doIsReady() throws IOException {
- synchronized (completionHandler) {
- if (readPending) {
- interest = true;
- return false;
- }
- ByteBuffer readBuffer = channel.getBufHandler().getReadBuffer();
- if (!flipped) {
- readBuffer.flip();
- flipped = true;
- }
- if (readBuffer.remaining() > 0) {
- return true;
- }
-
- readBuffer.clear();
- flipped = false;
- int nRead = fillReadBuffer(false);
-
- boolean isReady = nRead > 0;
- if (isReady) {
- if (!flipped) {
- readBuffer.flip();
- flipped = true;
- }
- } else {
- interest = true;
- }
- return isReady;
- }
- }
-
- @Override
- protected int doRead(boolean block, byte[] b, int off, int len)
- throws IOException {
-
- synchronized (completionHandler) {
- if (readPending) {
- return 0;
- }
-
- ByteBuffer readBuffer = channel.getBufHandler().getReadBuffer();
-
- if (!flipped) {
- readBuffer.flip();
- flipped = true;
- }
- int remaining = readBuffer.remaining();
- // Is there enough data in the read buffer to satisfy this request?
- if (remaining >= len) {
- readBuffer.get(b, off, len);
- return len;
- }
-
- // Copy what data there is in the read buffer to the byte array
- int leftToWrite = len;
- int newOffset = off;
- if (remaining > 0) {
- readBuffer.get(b, off, remaining);
- leftToWrite -= remaining;
- newOffset += remaining;
- }
-
- // Fill the read buffer as best we can
- readBuffer.clear();
- flipped = false;
- int nRead = fillReadBuffer(block);
-
- // Full as much of the remaining byte array as possible with the data
- // that was just read
- if (nRead > 0) {
- if (!flipped) {
- readBuffer.flip();
- flipped = true;
- }
- if (nRead > leftToWrite) {
- readBuffer.get(b, newOffset, leftToWrite);
- leftToWrite = 0;
- } else {
- readBuffer.get(b, newOffset, nRead);
- leftToWrite -= nRead;
- }
- } else if (nRead == 0) {
- if (block) {
- if (!flipped) {
- readBuffer.flip();
- flipped = true;
- }
- }
- } else if (nRead == -1) {
- throw new EOFException();
- }
-
- return len - leftToWrite;
- }
- }
-
- @Override
- protected void doClose() throws IOException {
- closed = true;
- channel.close();
- }
-
- private int fillReadBuffer(boolean block) throws IOException {
- ByteBuffer readBuffer = channel.getBufHandler().getReadBuffer();
- int nRead = 0;
- if (block) {
- readPending = true;
- readBuffer.clear();
- flipped = false;
- Future<Integer> future = null;
- try {
- future = channel.read(readBuffer);
- nRead = future.get(wrapper.getTimeout(), TimeUnit.MILLISECONDS).intValue();
- readPending = false;
- } catch (ExecutionException e) {
- if (e.getCause() instanceof IOException) {
- onError(e.getCause());
- throw (IOException) e.getCause();
- } else {
- onError(e);
- throw new IOException(e);
- }
- } catch (InterruptedException e) {
- onError(e);
- throw new IOException(e);
- } catch (TimeoutException e) {
- future.cancel(true);
- SocketTimeoutException ex = new SocketTimeoutException();
- onError(ex);
- throw ex;
- }
- } else {
- readPending = true;
- readBuffer.clear();
- flipped = false;
- Nio2Endpoint.startInline();
- channel.read(readBuffer,
- wrapper.getTimeout(), TimeUnit.MILLISECONDS, wrapper, completionHandler);
- Nio2Endpoint.endInline();
- if (!readPending) {
- nRead = readBuffer.position();
- }
- }
- return nRead;
- }
-}
diff --git a/java/org/apache/coyote/http11/upgrade/Nio2ServletOutputStream.java b/java/org/apache/coyote/http11/upgrade/Nio2ServletOutputStream.java
deleted file mode 100644
index 7d8a756..0000000
--- a/java/org/apache/coyote/http11/upgrade/Nio2ServletOutputStream.java
+++ /dev/null
@@ -1,199 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.coyote.http11.upgrade;
-
-import java.io.EOFException;
-import java.io.IOException;
-import java.net.SocketTimeoutException;
-import java.nio.ByteBuffer;
-import java.nio.channels.AsynchronousCloseException;
-import java.nio.channels.CompletionHandler;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.Future;
-import java.util.concurrent.Semaphore;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-
-import org.apache.tomcat.util.net.AbstractEndpoint;
-import org.apache.tomcat.util.net.Nio2Channel;
-import org.apache.tomcat.util.net.Nio2Endpoint;
-import org.apache.tomcat.util.net.SocketStatus;
-import org.apache.tomcat.util.net.SocketWrapper;
-
-public class Nio2ServletOutputStream extends AbstractServletOutputStream<Nio2Channel> {
-
- private final AbstractEndpoint<Nio2Channel> endpoint;
- private final Nio2Channel channel;
- private final int maxWrite;
- private final CompletionHandler<Integer, ByteBuffer> completionHandler;
- private final Semaphore writePending = new Semaphore(1);
-
- public Nio2ServletOutputStream(SocketWrapper<Nio2Channel> socketWrapper0,
- int asyncWriteBufferSize, AbstractEndpoint<Nio2Channel> endpoint0) {
- super(socketWrapper0, asyncWriteBufferSize);
- this.endpoint = endpoint0;
- channel = socketWrapper0.getSocket();
- maxWrite = channel.getBufHandler().getWriteBuffer().capacity();
- this.completionHandler = new CompletionHandler<Integer, ByteBuffer>() {
- @Override
- public void completed(Integer nBytes, ByteBuffer attachment) {
- if (nBytes.intValue() < 0) {
- failed(new EOFException(), attachment);
- } else if (attachment.hasRemaining()) {
- channel.write(attachment, socketWrapper.getTimeout(),
- TimeUnit.MILLISECONDS, attachment, completionHandler);
- } else {
- writePending.release();
- if (!Nio2Endpoint.isInline()) {
- endpoint.processSocket(socketWrapper, SocketStatus.OPEN_WRITE, false);
- }
- }
- }
- @Override
- public void failed(Throwable exc, ByteBuffer attachment) {
- socketWrapper.setError(true);
- writePending.release();
- if (exc instanceof AsynchronousCloseException) {
- // If already closed, don't call onError and close again
- return;
- }
- onError(exc);
- endpoint.processSocket(socketWrapper, SocketStatus.ERROR, true);
- }
- };
- }
-
- @Override
- protected int doWrite(boolean block, byte[] b, int off, int len)
- throws IOException {
- int leftToWrite = len;
- int count = 0;
- int offset = off;
-
- while (leftToWrite > 0) {
- int writeThisLoop;
- int writtenThisLoop;
-
- if (leftToWrite > maxWrite) {
- writeThisLoop = maxWrite;
- } else {
- writeThisLoop = leftToWrite;
- }
-
- writtenThisLoop = doWriteInternal(block, b, offset, writeThisLoop);
- if (writtenThisLoop < 0) {
- throw new EOFException();
- }
- count += writtenThisLoop;
- if (!block && writePending.availablePermits() == 0) {
- // Prevent concurrent writes in non blocking mode,
- // leftover data has to be buffered
- return count;
- }
- offset += writtenThisLoop;
- leftToWrite -= writtenThisLoop;
-
- if (writtenThisLoop < writeThisLoop) {
- break;
- }
- }
-
- return count;
- }
-
- private int doWriteInternal(boolean block, byte[] b, int off, int len)
- throws IOException {
- ByteBuffer buffer = channel.getBufHandler().getWriteBuffer();
- int written = 0;
- if (block) {
- buffer.clear();
- buffer.put(b, off, len);
- buffer.flip();
- Future<Integer> future = null;
- try {
- future = channel.write(buffer);
- written = future.get(socketWrapper.getTimeout(), TimeUnit.MILLISECONDS).intValue();
- } catch (ExecutionException e) {
- if (e.getCause() instanceof IOException) {
- onError(e.getCause());
- throw (IOException) e.getCause();
- } else {
- onError(e);
- throw new IOException(e);
- }
- } catch (InterruptedException e) {
- onError(e);
- throw new IOException(e);
- } catch (TimeoutException e) {
- future.cancel(true);
- SocketTimeoutException ex = new SocketTimeoutException();
- onError(ex);
- throw ex;
- }
- } else {
- if (writePending.tryAcquire()) {
- buffer.clear();
- buffer.put(b, off, len);
- buffer.flip();
- Nio2Endpoint.startInline();
- channel.write(buffer, socketWrapper.getTimeout(), TimeUnit.MILLISECONDS, buffer, completionHandler);
- Nio2Endpoint.endInline();
- written = len;
- }
- }
- return written;
- }
-
- @Override
- protected void doFlush() throws IOException {
- Future<Boolean> future = null;
- try {
- // Block until a possible non blocking write is done
- if (writePending.tryAcquire(socketWrapper.getTimeout(), TimeUnit.MILLISECONDS)) {
- writePending.release();
- future = channel.flush();
- future.get(socketWrapper.getTimeout(), TimeUnit.MILLISECONDS);
- } else {
- throw new TimeoutException();
- }
- } catch (ExecutionException e) {
- if (e.getCause() instanceof IOException) {
- onError(e.getCause());
- throw (IOException) e.getCause();
- } else {
- onError(e);
- throw new IOException(e);
- }
- } catch (InterruptedException e) {
- onError(e);
- throw new IOException(e);
- } catch (TimeoutException e) {
- if (future != null) {
- future.cancel(true);
- }
- SocketTimeoutException ex = new SocketTimeoutException();
- onError(ex);
- throw ex;
- }
- }
-
- @Override
- protected void doClose() throws IOException {
- channel.close(true);
- }
-
-}
diff --git a/java/org/apache/coyote/http11/upgrade/NioProcessor.java b/java/org/apache/coyote/http11/upgrade/NioProcessor.java
deleted file mode 100644
index a03d2a9..0000000
--- a/java/org/apache/coyote/http11/upgrade/NioProcessor.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.coyote.http11.upgrade;
-
-import java.nio.ByteBuffer;
-
-import org.apache.coyote.UpgradeToken;
-import org.apache.juli.logging.Log;
-import org.apache.juli.logging.LogFactory;
-import org.apache.tomcat.util.net.NioChannel;
-import org.apache.tomcat.util.net.NioSelectorPool;
-import org.apache.tomcat.util.net.SocketWrapper;
-
-public class NioProcessor extends AbstractProcessor<NioChannel> {
-
- private static final Log log = LogFactory.getLog(NioProcessor.class);
- @Override
- protected Log getLog() {return log;}
-
- private static final int INFINITE_TIMEOUT = -1;
-
- public NioProcessor(SocketWrapper<NioChannel> wrapper, ByteBuffer leftoverInput,
- UpgradeToken upgradeToken, NioSelectorPool pool,
- int asyncWriteBufferSize) {
- super(upgradeToken,
- new NioServletInputStream(wrapper, pool),
- new NioServletOutputStream(wrapper, asyncWriteBufferSize, pool));
-
- wrapper.setTimeout(INFINITE_TIMEOUT);
- if (leftoverInput != null) {
- ByteBuffer readBuffer = wrapper.getSocket().getBufHandler().getReadBuffer();
- if (readBuffer.remaining() > 0) {
- readBuffer.flip();
- } else {
- readBuffer.clear();
- }
- readBuffer.put(leftoverInput);
- readBuffer.flip();
- }
- }
-}
diff --git a/java/org/apache/coyote/http11/upgrade/NioServletInputStream.java b/java/org/apache/coyote/http11/upgrade/NioServletInputStream.java
deleted file mode 100644
index 5ad99f6..0000000
--- a/java/org/apache/coyote/http11/upgrade/NioServletInputStream.java
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.coyote.http11.upgrade;
-
-import java.io.EOFException;
-import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.nio.channels.Selector;
-
-import org.apache.tomcat.util.net.NioChannel;
-import org.apache.tomcat.util.net.NioEndpoint;
-import org.apache.tomcat.util.net.NioSelectorPool;
-import org.apache.tomcat.util.net.SocketWrapper;
-
-public class NioServletInputStream extends AbstractServletInputStream {
-
- private final NioChannel channel;
- private final NioSelectorPool pool;
-
- public NioServletInputStream(SocketWrapper<NioChannel> wrapper,
- NioSelectorPool pool) {
- this.channel = wrapper.getSocket();
- this.pool = pool;
- }
-
- @Override
- protected boolean doIsReady() throws IOException {
- ByteBuffer readBuffer = channel.getBufHandler().getReadBuffer();
-
- if (readBuffer.remaining() > 0) {
- return true;
- }
-
- readBuffer.clear();
- fillReadBuffer(false);
-
- boolean isReady = readBuffer.position() > 0;
- readBuffer.flip();
- return isReady;
- }
-
- @Override
- protected int doRead(boolean block, byte[] b, int off, int len)
- throws IOException {
-
- ByteBuffer readBuffer = channel.getBufHandler().getReadBuffer();
- int remaining = readBuffer.remaining();
-
- // Is there enough data in the read buffer to satisfy this request?
- if (remaining >= len) {
- readBuffer.get(b, off, len);
- return len;
- }
-
- // Copy what data there is in the read buffer to the byte array
- int leftToWrite = len;
- int newOffset = off;
- if (remaining > 0) {
- readBuffer.get(b, off, remaining);
- leftToWrite -= remaining;
- newOffset += remaining;
- }
-
- // Fill the read buffer as best we can
- readBuffer.clear();
- int nRead = fillReadBuffer(block);
-
- // Full as much of the remaining byte array as possible with the data
- // that was just read
- if (nRead > 0) {
- readBuffer.flip();
- if (nRead > leftToWrite) {
- readBuffer.get(b, newOffset, leftToWrite);
- leftToWrite = 0;
- } else {
- readBuffer.get(b, newOffset, nRead);
- leftToWrite -= nRead;
- }
- } else if (nRead == 0) {
- readBuffer.flip();
- } else if (nRead == -1) {
- // TODO i18n
- throw new EOFException();
- }
-
- return len - leftToWrite;
- }
-
-
-
- @Override
- protected void doClose() throws IOException {
- channel.close();
- }
-
-
- private int fillReadBuffer(boolean block) throws IOException {
- int nRead;
- if (block) {
- Selector selector = null;
- try {
- selector = pool.get();
- } catch ( IOException x ) {
- // Ignore
- }
- try {
- NioEndpoint.KeyAttachment att =
- (NioEndpoint.KeyAttachment) channel.getAttachment();
- if (att == null) {
- throw new IOException("Key must be cancelled.");
- }
- nRead = pool.read(channel.getBufHandler().getReadBuffer(),
- channel, selector, att.getTimeout());
- } catch (EOFException eof) {
- nRead = -1;
- } finally {
- if (selector != null) {
- pool.put(selector);
- }
- }
- } else {
- nRead = channel.read(channel.getBufHandler().getReadBuffer());
- }
- return nRead;
- }
-}
diff --git a/java/org/apache/coyote/http11/upgrade/NioServletOutputStream.java b/java/org/apache/coyote/http11/upgrade/NioServletOutputStream.java
deleted file mode 100644
index 410ee10..0000000
--- a/java/org/apache/coyote/http11/upgrade/NioServletOutputStream.java
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.coyote.http11.upgrade;
-
-import java.io.IOException;
-import java.nio.channels.SelectionKey;
-import java.nio.channels.Selector;
-
-import org.apache.tomcat.util.net.NioChannel;
-import org.apache.tomcat.util.net.NioEndpoint;
-import org.apache.tomcat.util.net.NioSelectorPool;
-import org.apache.tomcat.util.net.SocketWrapper;
-
-public class NioServletOutputStream extends AbstractServletOutputStream<NioChannel> {
-
- private final NioChannel channel;
- private final NioSelectorPool pool;
- private final int maxWrite;
-
-
- public NioServletOutputStream(SocketWrapper<NioChannel> socketWrapper,
- int asyncWriteBufferSize, NioSelectorPool pool) {
- super(socketWrapper, asyncWriteBufferSize);
- channel = socketWrapper.getSocket();
- this.pool = pool;
- maxWrite = channel.getBufHandler().getWriteBuffer().capacity();
- }
-
-
- @Override
- protected int doWrite(boolean block, byte[] b, int off, int len)
- throws IOException {
- int leftToWrite = len;
- int count = 0;
- int offset = off;
-
- while (leftToWrite > 0) {
- int writeThisLoop;
- int writtenThisLoop;
-
- if (leftToWrite > maxWrite) {
- writeThisLoop = maxWrite;
- } else {
- writeThisLoop = leftToWrite;
- }
-
- writtenThisLoop = doWriteInternal(block, b, offset, writeThisLoop);
- count += writtenThisLoop;
- offset += writtenThisLoop;
- leftToWrite -= writtenThisLoop;
-
- if (writtenThisLoop < writeThisLoop) {
- break;
- }
- }
-
- return count;
- }
-
- private int doWriteInternal (boolean block, byte[] b, int off, int len)
- throws IOException {
- channel.getBufHandler().getWriteBuffer().clear();
- channel.getBufHandler().getWriteBuffer().put(b, off, len);
- channel.getBufHandler().getWriteBuffer().flip();
-
- int written = 0;
- NioEndpoint.KeyAttachment att =
- (NioEndpoint.KeyAttachment) channel.getAttachment();
- if (att == null) {
- throw new IOException("Key must be cancelled");
- }
- long writeTimeout = att.getWriteTimeout();
- Selector selector = null;
- try {
- selector = pool.get();
- } catch ( IOException x ) {
- //ignore
- }
- try {
- written = pool.write(channel.getBufHandler().getWriteBuffer(),
- channel, selector, writeTimeout, block);
- } finally {
- if (selector != null) {
- pool.put(selector);
- }
- }
- if (written < len) {
- channel.getPoller().add(channel, SelectionKey.OP_WRITE);
- }
- return written;
- }
-
-
- @Override
- protected void doFlush() throws IOException {
- NioEndpoint.KeyAttachment att =
- (NioEndpoint.KeyAttachment) channel.getAttachment();
- if (att == null) {
- throw new IOException("Key must be cancelled");
- }
- long writeTimeout = att.getWriteTimeout();
- Selector selector = null;
- try {
- selector = pool.get();
- } catch ( IOException x ) {
- //ignore
- }
- try {
- do {
- if (channel.flush(true, selector, writeTimeout)) {
- break;
- }
- } while (true);
- } finally {
- if (selector != null) {
- pool.put(selector);
- }
- }
- }
-
-
- @Override
- protected void doClose() throws IOException {
- channel.close(true);
- }
-}
diff --git a/java/org/apache/coyote/http11/upgrade/UpgradeProcessorBase.java b/java/org/apache/coyote/http11/upgrade/UpgradeProcessorBase.java
new file mode 100644
index 0000000..a8f9abb
--- /dev/null
+++ b/java/org/apache/coyote/http11/upgrade/UpgradeProcessorBase.java
@@ -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.coyote.http11.upgrade;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+import javax.servlet.http.WebConnection;
+
+import org.apache.coyote.AbstractProcessorLight;
+import org.apache.coyote.Request;
+import org.apache.coyote.UpgradeToken;
+import org.apache.tomcat.util.net.AbstractEndpoint.Handler.SocketState;
+import org.apache.tomcat.util.net.SocketWrapperBase;
+
+public abstract class UpgradeProcessorBase extends AbstractProcessorLight implements WebConnection {
+
+ protected static final int INFINITE_TIMEOUT = -1;
+
+ private final UpgradeToken upgradeToken;
+
+ public UpgradeProcessorBase(UpgradeToken upgradeToken) {
+ this.upgradeToken = upgradeToken;
+ }
+
+
+ // ------------------------------------------- Implemented Processor methods
+
+ @Override
+ public final boolean isUpgrade() {
+ return true;
+ }
+
+
+ @Override
+ public UpgradeToken getUpgradeToken() {
+ return upgradeToken;
+ }
+
+
+ @Override
+ public final void recycle() {
+ // Currently a NO-OP as upgrade processors are not recycled.
+ }
+
+
+ // ---------------------------- Processor methods that are NO-OP for upgrade
+
+ @Override
+ public final SocketState service(SocketWrapperBase<?> socketWrapper) throws IOException {
+ return null;
+ }
+
+
+ @Override
+ public final SocketState asyncPostProcess() {
+ return null;
+ }
+
+
+ @Override
+ public final boolean isAsync() {
+ return false;
+ }
+
+
+ @Override
+ public final Request getRequest() {
+ return null;
+ }
+
+
+ @Override
+ public ByteBuffer getLeftoverInput() {
+ return null;
+ }
+
+
+ @Override
+ public void timeoutAsync(long now) {
+ // NO-OP
+ }
+}
diff --git a/java/org/apache/coyote/http11/upgrade/UpgradeProcessorExternal.java b/java/org/apache/coyote/http11/upgrade/UpgradeProcessorExternal.java
new file mode 100644
index 0000000..75d074e
--- /dev/null
+++ b/java/org/apache/coyote/http11/upgrade/UpgradeProcessorExternal.java
@@ -0,0 +1,139 @@
+/*
+ * 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.http11.upgrade;
+
+import java.io.IOException;
+
+import javax.servlet.ServletInputStream;
+import javax.servlet.ServletOutputStream;
+
+import org.apache.coyote.UpgradeToken;
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
+import org.apache.tomcat.util.net.AbstractEndpoint.Handler.SocketState;
+import org.apache.tomcat.util.net.SSLSupport;
+import org.apache.tomcat.util.net.SocketEvent;
+import org.apache.tomcat.util.net.SocketWrapperBase;
+import org.apache.tomcat.util.res.StringManager;
+
+public class UpgradeProcessorExternal extends UpgradeProcessorBase {
+
+ private static final Log log = LogFactory.getLog(UpgradeProcessorExternal.class);
+ private static final StringManager sm = StringManager.getManager(UpgradeProcessorExternal.class);
+
+ private final UpgradeServletInputStream upgradeServletInputStream;
+ private final UpgradeServletOutputStream upgradeServletOutputStream;
+
+
+ public UpgradeProcessorExternal(SocketWrapperBase<?> wrapper,
+ UpgradeToken upgradeToken) {
+ super(upgradeToken);
+ this.upgradeServletInputStream = new UpgradeServletInputStream(this, wrapper);
+ this.upgradeServletOutputStream = new UpgradeServletOutputStream(this, wrapper);
+
+ /*
+ * Leave timeouts in the hands of the upgraded protocol.
+ */
+ wrapper.setReadTimeout(INFINITE_TIMEOUT);
+ wrapper.setWriteTimeout(INFINITE_TIMEOUT);
+ }
+
+
+ @Override
+ protected Log getLog() {
+ return log;
+ }
+
+
+ // --------------------------------------------------- AutoCloseable methods
+
+ @Override
+ public void close() throws Exception {
+ upgradeServletInputStream.close();
+ upgradeServletOutputStream.close();
+ }
+
+
+ // --------------------------------------------------- WebConnection methods
+
+ @Override
+ public ServletInputStream getInputStream() throws IOException {
+ return upgradeServletInputStream;
+ }
+
+ @Override
+ public ServletOutputStream getOutputStream() throws IOException {
+ return upgradeServletOutputStream;
+ }
+
+
+ // ------------------------------------------- Implemented Processor methods
+
+ @Override
+ public final SocketState dispatch(SocketEvent status) {
+ if (status == SocketEvent.OPEN_READ) {
+ upgradeServletInputStream.onDataAvailable();
+ } else if (status == SocketEvent.OPEN_WRITE) {
+ upgradeServletOutputStream.onWritePossible();
+ } else if (status == SocketEvent.STOP) {
+ if (log.isDebugEnabled()) {
+ log.debug(sm.getString("upgradeProcessor.stop"));
+ }
+ try {
+ upgradeServletInputStream.close();
+ } catch (IOException ioe) {
+ log.debug(sm.getString("upgradeProcessor.isCloseFail", ioe));
+ }
+ try {
+ upgradeServletOutputStream.close();
+ } catch (IOException ioe) {
+ log.debug(sm.getString("upgradeProcessor.osCloseFail", ioe));
+ }
+ return SocketState.CLOSED;
+ } else {
+ // Unexpected state
+ if (log.isDebugEnabled()) {
+ log.debug(sm.getString("upgradeProcessor.unexpectedState"));
+ }
+ return SocketState.CLOSED;
+ }
+ if (upgradeServletInputStream.isClosed() &&
+ upgradeServletOutputStream.isClosed()) {
+ if (log.isDebugEnabled()) {
+ log.debug(sm.getString("upgradeProcessor.requiredClose",
+ Boolean.valueOf(upgradeServletInputStream.isClosed()),
+ Boolean.valueOf(upgradeServletOutputStream.isClosed())));
+ }
+ return SocketState.CLOSED;
+ }
+ return SocketState.UPGRADED;
+ }
+
+
+ // ----------------------------------------- Unimplemented Processor methods
+
+ @Override
+ public final void setSslSupport(SSLSupport sslSupport) {
+ // NO-OP
+ }
+
+
+ @Override
+ public void pause() {
+ // NOOP for AJP
+ }
+}
diff --git a/java/org/apache/coyote/http11/upgrade/UpgradeProcessorInternal.java b/java/org/apache/coyote/http11/upgrade/UpgradeProcessorInternal.java
new file mode 100644
index 0000000..6397a72
--- /dev/null
+++ b/java/org/apache/coyote/http11/upgrade/UpgradeProcessorInternal.java
@@ -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.coyote.http11.upgrade;
+
+import java.io.IOException;
+
+import javax.servlet.ServletInputStream;
+import javax.servlet.ServletOutputStream;
+
+import org.apache.coyote.UpgradeToken;
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
+import org.apache.tomcat.util.net.AbstractEndpoint.Handler.SocketState;
+import org.apache.tomcat.util.net.SSLSupport;
+import org.apache.tomcat.util.net.SocketEvent;
+import org.apache.tomcat.util.net.SocketWrapperBase;
+
+public class UpgradeProcessorInternal extends UpgradeProcessorBase {
+
+ private static final Log log = LogFactory.getLog(UpgradeProcessorInternal.class);
+
+ private final InternalHttpUpgradeHandler internalHttpUpgradeHandler;
+
+ public UpgradeProcessorInternal(SocketWrapperBase<?> wrapper,
+ UpgradeToken upgradeToken) {
+ super(upgradeToken);
+ this.internalHttpUpgradeHandler = (InternalHttpUpgradeHandler) upgradeToken.getHttpUpgradeHandler();
+ /*
+ * Leave timeouts in the hands of the upgraded protocol.
+ */
+ wrapper.setReadTimeout(INFINITE_TIMEOUT);
+ wrapper.setWriteTimeout(INFINITE_TIMEOUT);
+
+ internalHttpUpgradeHandler.setSocketWrapper(wrapper);
+ }
+
+
+ @Override
+ public SocketState dispatch(SocketEvent status) {
+ return internalHttpUpgradeHandler.upgradeDispatch(status);
+ }
+
+
+ @Override
+ public final void setSslSupport(SSLSupport sslSupport) {
+ internalHttpUpgradeHandler.setSslSupport(sslSupport);
+ }
+
+
+ @Override
+ public void pause() {
+ internalHttpUpgradeHandler.pause();
+ }
+
+
+ @Override
+ protected Log getLog() {
+ return log;
+ }
+
+
+ // --------------------------------------------------- AutoCloseable methods
+
+ @Override
+ public void close() throws Exception {
+ internalHttpUpgradeHandler.destroy();
+ }
+
+
+ // --------------------------------------------------- WebConnection methods
+
+ @Override
+ public ServletInputStream getInputStream() throws IOException {
+ return null;
+ }
+
+ @Override
+ public ServletOutputStream getOutputStream() throws IOException {
+ return null;
+ }
+}
diff --git a/java/org/apache/coyote/http11/upgrade/UpgradeServletInputStream.java b/java/org/apache/coyote/http11/upgrade/UpgradeServletInputStream.java
new file mode 100644
index 0000000..25b808e
--- /dev/null
+++ b/java/org/apache/coyote/http11/upgrade/UpgradeServletInputStream.java
@@ -0,0 +1,253 @@
+/*
+ * 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.http11.upgrade;
+
+import java.io.IOException;
+
+import javax.servlet.ReadListener;
+import javax.servlet.ServletInputStream;
+
+import org.apache.coyote.ContainerThreadMarker;
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
+import org.apache.tomcat.util.ExceptionUtils;
+import org.apache.tomcat.util.net.DispatchType;
+import org.apache.tomcat.util.net.SocketWrapperBase;
+import org.apache.tomcat.util.res.StringManager;
+
+public class UpgradeServletInputStream extends ServletInputStream {
+
+ private static final Log log = LogFactory.getLog(UpgradeServletInputStream.class);
+ private static final StringManager sm =
+ StringManager.getManager(UpgradeServletInputStream.class);
+
+ private final UpgradeProcessorBase processor;
+ private final SocketWrapperBase<?> socketWrapper;
+
+ private volatile boolean closed = false;
+ private volatile boolean eof = false;
+ // Start in blocking-mode
+ private volatile Boolean ready = Boolean.TRUE;
+ private volatile ReadListener listener = null;
+
+
+ public UpgradeServletInputStream(UpgradeProcessorBase processor,
+ SocketWrapperBase<?> socketWrapper) {
+ this.processor = processor;
+ this.socketWrapper = socketWrapper;
+ }
+
+
+ @Override
+ public final boolean isFinished() {
+ if (listener == null) {
+ throw new IllegalStateException(
+ sm.getString("upgrade.sis.isFinished.ise"));
+ }
+ return eof;
+ }
+
+
+ @Override
+ public final boolean isReady() {
+ if (listener == null) {
+ throw new IllegalStateException(
+ sm.getString("upgrade.sis.isReady.ise"));
+ }
+
+ if (eof || closed) {
+ return false;
+ }
+
+ // If we already know the current state, return it.
+ if (ready != null) {
+ return ready.booleanValue();
+ }
+
+ try {
+ ready = Boolean.valueOf(socketWrapper.isReadyForRead());
+ } catch (IOException e) {
+ onError(e);
+ }
+ return ready.booleanValue();
+ }
+
+
+ @Override
+ public final void setReadListener(ReadListener listener) {
+ if (listener == null) {
+ throw new IllegalArgumentException(
+ sm.getString("upgrade.sis.readListener.null"));
+ }
+ if (this.listener != null) {
+ throw new IllegalArgumentException(
+ sm.getString("upgrade.sis.readListener.set"));
+ }
+ if (closed) {
+ throw new IllegalStateException(sm.getString("upgrade.sis.read.closed"));
+ }
+
+ // Container is responsible for first call to onDataAvailable().
+ if (ContainerThreadMarker.isContainerThread()) {
+ processor.addDispatch(DispatchType.NON_BLOCKING_READ);
+ } else {
+ socketWrapper.registerReadInterest();
+ }
+
+ this.listener = listener;
+ // Switching to non-blocking. Don't know if data is available.
+ ready = null;
+ }
+
+
+ @Override
+ public final int read() throws IOException {
+ preReadChecks();
+
+ return readInternal();
+ }
+
+
+ @Override
+ public final int readLine(byte[] b, int off, int len) throws IOException {
+ preReadChecks();
+
+ if (len <= 0) {
+ return 0;
+ }
+ int count = 0, c;
+
+ while ((c = readInternal()) != -1) {
+ b[off++] = (byte) c;
+ count++;
+ if (c == '\n' || count == len) {
+ break;
+ }
+ }
+ return count > 0 ? count : -1;
+ }
+
+
+ @Override
+ public final int read(byte[] b, int off, int len) throws IOException {
+ preReadChecks();
+
+ try {
+ int result = socketWrapper.read(listener == null, b, off, len);
+ if (result == -1) {
+ eof = true;
+ }
+ return result;
+ } catch (IOException ioe) {
+ close();
+ throw ioe;
+ }
+ }
+
+
+
+ @Override
+ public void close() throws IOException {
+ eof = true;
+ closed = true;
+ }
+
+
+ private void preReadChecks() {
+ if (listener != null && (ready == null || !ready.booleanValue())) {
+ throw new IllegalStateException(sm.getString("upgrade.sis.read.ise"));
+ }
+ if (closed) {
+ throw new IllegalStateException(sm.getString("upgrade.sis.read.closed"));
+ }
+ // No longer know if data is available
+ ready = null;
+ }
+
+
+ private int readInternal() throws IOException {
+ // Single byte reads for non-blocking need special handling so all
+ // single byte reads run through this method.
+ byte[] b = new byte[1];
+ int result;
+ try {
+ result = socketWrapper.read(listener == null, b, 0, 1);
+ } catch (IOException ioe) {
+ close();
+ throw ioe;
+ }
+ if (result == 0) {
+ return -1;
+ } else if (result == -1) {
+ eof = true;
+ return -1;
+ } else {
+ return b[0] & 0xFF;
+ }
+ }
+
+
+ final void onDataAvailable() {
+ if (listener == null) {
+ return;
+ }
+ ready = Boolean.TRUE;
+ ClassLoader oldCL = processor.getUpgradeToken().getContextBind().bind(false, null);
+ try {
+ if (!eof) {
+ listener.onDataAvailable();
+ }
+ if (eof) {
+ listener.onAllDataRead();
+ }
+ } catch (Throwable t) {
+ ExceptionUtils.handleThrowable(t);
+ onError(t);
+ } finally {
+ processor.getUpgradeToken().getContextBind().unbind(false, oldCL);
+ }
+ }
+
+
+ private final void onError(Throwable t) {
+ if (listener == null) {
+ return;
+ }
+ ClassLoader oldCL = processor.getUpgradeToken().getContextBind().bind(false, null);
+ try {
+ listener.onError(t);
+ } catch (Throwable t2) {
+ ExceptionUtils.handleThrowable(t2);
+ log.warn(sm.getString("upgrade.sis.onErrorFail"), t2);
+ } finally {
+ processor.getUpgradeToken().getContextBind().unbind(false, oldCL);
+ }
+ try {
+ close();
+ } catch (IOException ioe) {
+ if (log.isDebugEnabled()) {
+ log.debug(sm.getString("upgrade.sis.errorCloseFail"), ioe);
+ }
+ }
+ ready = Boolean.FALSE;
+ }
+
+
+ final boolean isClosed() {
+ return closed;
+ }
+}
diff --git a/java/org/apache/coyote/http11/upgrade/UpgradeServletOutputStream.java b/java/org/apache/coyote/http11/upgrade/UpgradeServletOutputStream.java
new file mode 100644
index 0000000..81f0f78
--- /dev/null
+++ b/java/org/apache/coyote/http11/upgrade/UpgradeServletOutputStream.java
@@ -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.coyote.http11.upgrade;
+
+import java.io.IOException;
+
+import javax.servlet.ServletOutputStream;
+import javax.servlet.WriteListener;
+
+import org.apache.coyote.ContainerThreadMarker;
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
+import org.apache.tomcat.util.ExceptionUtils;
+import org.apache.tomcat.util.net.DispatchType;
+import org.apache.tomcat.util.net.SocketWrapperBase;
+import org.apache.tomcat.util.res.StringManager;
+
+public class UpgradeServletOutputStream extends ServletOutputStream {
+
+ private static final Log log = LogFactory.getLog(UpgradeServletOutputStream.class);
+ private static final StringManager sm =
+ StringManager.getManager(UpgradeServletOutputStream.class);
+
+ private final UpgradeProcessorBase processor;
+ private final SocketWrapperBase<?> socketWrapper;
+
+ // Used to ensure that isReady() and onWritePossible() have a consistent
+ // view of buffer and registered.
+ private final Object registeredLock = new Object();
+
+ // Used to ensure that only one thread writes to the socket at a time and
+ // that buffer is consistently updated with any unwritten data after the
+ // write. Note it is not necessary to hold this lock when checking if buffer
+ // contains data but, depending on how the result is used, some form of
+ // synchronization may be required (see fireListenerLock for an example).
+ private final Object writeLock = new Object();
+
+ private volatile boolean flushing = false;
+
+ private volatile boolean closed = false;
+
+ // Start in blocking-mode
+ private volatile WriteListener listener = null;
+
+ // Guarded by registeredLock
+ private boolean registered = false;
+
+
+
+ public UpgradeServletOutputStream(UpgradeProcessorBase processor,
+ SocketWrapperBase<?> socketWrapper) {
+ this.processor = processor;
+ this.socketWrapper = socketWrapper;
+ }
+
+
+ @Override
+ public final boolean isReady() {
+ if (listener == null) {
+ throw new IllegalStateException(
+ sm.getString("upgrade.sos.canWrite.ise"));
+ }
+ if (closed) {
+ return false;
+ }
+
+ // Make sure isReady() and onWritePossible() have a consistent view of
+ // fireListener when determining if the listener should fire
+ synchronized (registeredLock) {
+ if (flushing) {
+ // Since flushing is true the socket must already be registered
+ // for write and multiple registrations will cause problems.
+ registered = true;
+ return false;
+ } else if (registered){
+ // The socket is already registered for write and multiple
+ // registrations will cause problems.
+ return false;
+ } else {
+ boolean result = socketWrapper.isReadyForWrite();
+ registered = !result;
+ return result;
+ }
+ }
+ }
+
+
+ @Override
+ public final void setWriteListener(WriteListener listener) {
+ if (listener == null) {
+ throw new IllegalArgumentException(
+ sm.getString("upgrade.sos.writeListener.null"));
+ }
+ if (this.listener != null) {
+ throw new IllegalArgumentException(
+ sm.getString("upgrade.sos.writeListener.set"));
+ }
+ if (closed) {
+ throw new IllegalStateException(sm.getString("upgrade.sos.write.closed"));
+ }
+ // Container is responsible for first call to onWritePossible().
+ synchronized (registeredLock) {
+ registered = true;
+ // Container is responsible for first call to onDataAvailable().
+ if (ContainerThreadMarker.isContainerThread()) {
+ processor.addDispatch(DispatchType.NON_BLOCKING_WRITE);
+ } else {
+ socketWrapper.registerWriteInterest();
+ }
+ }
+
+ this.listener = listener;
+ }
+
+
+ final boolean isClosed() {
+ return closed;
+ }
+
+
+ @Override
+ public void write(int b) throws IOException {
+ synchronized (writeLock) {
+ preWriteChecks();
+ writeInternal(new byte[] { (byte) b }, 0, 1);
+ }
+ }
+
+
+ @Override
+ public void write(byte[] b, int off, int len) throws IOException {
+ synchronized (writeLock) {
+ preWriteChecks();
+ writeInternal(b, off, len);
+ }
+ }
+
+
+ @Override
+ public void flush() throws IOException {
+ preWriteChecks();
+ flushInternal(listener == null, true);
+ }
+
+
+ private void flushInternal(boolean block, boolean updateFlushing) throws IOException {
+ try {
+ synchronized (writeLock) {
+ if (updateFlushing) {
+ flushing = socketWrapper.flush(block);
+ if (flushing) {
+ socketWrapper.registerWriteInterest();
+ }
+ } else {
+ socketWrapper.flush(block);
+ }
+ }
+ } catch (Throwable t) {
+ ExceptionUtils.handleThrowable(t);
+ onError(t);
+ if (t instanceof IOException) {
+ throw (IOException) t;
+ } else {
+ throw new IOException(t);
+ }
+ }
+ }
+
+ @Override
+ public void close() throws IOException {
+ if (closed) {
+ return;
+ }
+ closed = true;
+ flushInternal((listener == null), false);
+ }
+
+
+ private void preWriteChecks() {
+ if (listener != null && !socketWrapper.canWrite()) {
+ throw new IllegalStateException(sm.getString("upgrade.sos.write.ise"));
+ }
+ if (closed) {
+ throw new IllegalStateException(sm.getString("upgrade.sos.write.closed"));
+ }
+ }
+
+
+ /**
+ * 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);
+ }
+ }
+
+
+ final void onWritePossible() {
+ try {
+ if (flushing) {
+ flushInternal(false, true);
+ if (flushing) {
+ return;
+ }
+ } else {
+ // This may fill the write buffer in which case the
+ // isReadyForWrite() call below will re-register the socket for
+ // write
+ flushInternal(false, false);
+ }
+ } catch (IOException ioe) {
+ onError(ioe);
+ return;
+ }
+
+ // Make sure isReady() and onWritePossible() have a consistent view
+ // of buffer and fireListener when determining if the listener
+ // should fire
+ boolean fire = false;
+ synchronized (registeredLock) {
+ if (socketWrapper.isReadyForWrite()) {
+ registered = false;
+ fire = true;
+ } else {
+ registered = true;
+ }
+ }
+
+ if (fire) {
+ ClassLoader oldCL = processor.getUpgradeToken().getContextBind().bind(false, null);
+ try {
+ listener.onWritePossible();
+ } catch (Throwable t) {
+ ExceptionUtils.handleThrowable(t);
+ onError(t);
+ } finally {
+ processor.getUpgradeToken().getContextBind().unbind(false, oldCL);
+ }
+ }
+ }
+
+
+ private final void onError(Throwable t) {
+ if (listener == null) {
+ return;
+ }
+ ClassLoader oldCL = processor.getUpgradeToken().getContextBind().bind(false, null);
+ try {
+ listener.onError(t);
+ } catch (Throwable t2) {
+ ExceptionUtils.handleThrowable(t2);
+ log.warn(sm.getString("upgrade.sos.onErrorFail"), t2);
+ } finally {
+ processor.getUpgradeToken().getContextBind().unbind(false, oldCL);
+ }
+ try {
+ close();
+ } catch (IOException ioe) {
+ if (log.isDebugEnabled()) {
+ log.debug(sm.getString("upgrade.sos.errorCloseFail"), ioe);
+ }
+ }
+ }
+}
diff --git a/java/org/apache/coyote/http2/AbstractStream.java b/java/org/apache/coyote/http2/AbstractStream.java
new file mode 100644
index 0000000..a36d7c9
--- /dev/null
+++ b/java/org/apache/coyote/http2/AbstractStream.java
@@ -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.coyote.http2;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
+import org.apache.tomcat.util.res.StringManager;
+
+/**
+ * Used to managed prioritisation.
+ */
+abstract class AbstractStream {
+
+ private static final Log log = LogFactory.getLog(AbstractStream.class);
+ private static final StringManager sm = StringManager.getManager(AbstractStream.class);
+
+ private final Integer identifier;
+
+ private volatile AbstractStream parentStream = null;
+ private final Set<AbstractStream> childStreams = new HashSet<>();
+ private long windowSize = ConnectionSettingsBase.DEFAULT_INITIAL_WINDOW_SIZE;
+
+ public Integer getIdentifier() {
+ return identifier;
+ }
+
+
+ public AbstractStream(Integer identifier) {
+ this.identifier = identifier;
+ }
+
+
+ void detachFromParent() {
+ if (parentStream != null) {
+ parentStream.getChildStreams().remove(this);
+ parentStream = null;
+ }
+ }
+
+
+ void addChild(AbstractStream child) {
+ child.setParentStream(this);
+ childStreams.add(child);
+ }
+
+
+ boolean isDescendant(AbstractStream stream) {
+ if (childStreams.contains(stream)) {
+ return true;
+ }
+ for (AbstractStream child : childStreams) {
+ if (child.isDescendant(stream)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+
+ AbstractStream getParentStream() {
+ return parentStream;
+ }
+
+
+ void setParentStream(AbstractStream parentStream) {
+ this.parentStream = parentStream;
+ }
+
+
+ Set<AbstractStream> getChildStreams() {
+ return childStreams;
+ }
+
+
+ protected synchronized void setWindowSize(long windowSize) {
+ this.windowSize = windowSize;
+ }
+
+
+ protected synchronized long getWindowSize() {
+ return windowSize;
+ }
+
+
+ /**
+ * Increment window size.
+ * @param increment The amount of the incrementation
+ * @throws Http2Exception If the window size is now higher than
+ * the maximum allowed
+ */
+ protected synchronized void incrementWindowSize(int increment) throws Http2Exception {
+ // No need for overflow protection here.
+ // Increment can't be more than Integer.MAX_VALUE and once windowSize
+ // goes beyond 2^31-1 an error is triggered.
+ windowSize += increment;
+
+ if (log.isDebugEnabled()) {
+ log.debug(sm.getString("abstractStream.windowSizeInc", getConnectionId(),
+ getIdentifier(), Integer.toString(increment), Long.toString(windowSize)));
+ }
+
+ if (windowSize > ConnectionSettingsBase.MAX_WINDOW_SIZE) {
+ String msg = sm.getString("abstractStream.windowSizeTooBig", getConnectionId(), identifier,
+ Integer.toString(increment), Long.toString(windowSize));
+ if (identifier.intValue() == 0) {
+ throw new ConnectionException(msg, Http2Error.FLOW_CONTROL_ERROR);
+ } else {
+ throw new StreamException(
+ msg, Http2Error.FLOW_CONTROL_ERROR, identifier.intValue());
+ }
+ }
+ }
+
+
+ protected synchronized void decrementWindowSize(int decrement) {
+ // No need for overflow protection here. Decrement can never be larger
+ // the Integer.MAX_VALUE and once windowSize goes negative no further
+ // decrements are permitted
+ windowSize -= decrement;
+ if (log.isDebugEnabled()) {
+ log.debug(sm.getString("abstractStream.windowSizeDec", getConnectionId(),
+ getIdentifier(), Integer.toString(decrement), Long.toString(windowSize)));
+ }
+ }
+
+
+ protected abstract String getConnectionId();
+
+ protected abstract int getWeight();
+
+ protected abstract void doNotifyAll();
+}
diff --git a/java/org/apache/coyote/http2/ByteUtil.java b/java/org/apache/coyote/http2/ByteUtil.java
new file mode 100644
index 0000000..5b92ce9
--- /dev/null
+++ b/java/org/apache/coyote/http2/ByteUtil.java
@@ -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.coyote.http2;
+
+/**
+ * Utility class for extracting values from byte arrays.
+ */
+public class ByteUtil {
+
+ private ByteUtil() {
+ // Hide default constructor
+ }
+
+
+ public static boolean isBit7Set(byte input) {
+ return (input & 0x80) > 0;
+ }
+
+
+ public static int get31Bits(byte[] input, int firstByte) {
+ return ((input[firstByte] & 0x7F) << 24) + ((input[firstByte + 1] & 0xFF) << 16) +
+ ((input[firstByte + 2] & 0xFF) << 8) + (input[firstByte + 3] & 0xFF);
+ }
+
+
+ public static void set31Bits(byte[] output, int firstByte, int value) {
+ output[firstByte] = (byte) ((value & 0x7F000000) >> 24);
+ output[firstByte + 1] = (byte) ((value & 0xFF0000) >> 16);
+ output[firstByte + 2] = (byte) ((value & 0xFF00) >> 8);
+ output[firstByte + 3] = (byte) (value & 0xFF);
+ }
+
+
+ public static int getOneByte(byte[] input, int pos) {
+ return (input[pos] & 0xFF);
+ }
+
+
+ public static int getTwoBytes(byte[] input, int firstByte) {
+ return ((input[firstByte] & 0xFF) << 8) + (input[firstByte + 1] & 0xFF);
+ }
+
+
+ public static int getThreeBytes(byte[] input, int firstByte) {
+ return ((input[firstByte] & 0xFF) << 16) + ((input[firstByte + 1] & 0xFF) << 8) +
+ (input[firstByte + 2] & 0xFF);
+ }
+
+
+ public static void setOneBytes(byte[] output, int firstByte, int value) {
+ output[firstByte] = (byte) (value & 0xFF);
+ }
+
+
+ public static void setTwoBytes(byte[] output, int firstByte, int value) {
+ output[firstByte] = (byte) ((value & 0xFF00) >> 8);
+ output[firstByte + 1] = (byte) (value & 0xFF);
+ }
+
+
+ public static void setThreeBytes(byte[] output, int firstByte, int value) {
+ output[firstByte] = (byte) ((value & 0xFF0000) >> 16);
+ output[firstByte + 1] = (byte) ((value & 0xFF00) >> 8);
+ output[firstByte + 2] = (byte) (value & 0xFF);
+ }
+
+
+ public static long getFourBytes(byte[] input, int firstByte) {
+ return ((long)(input[firstByte] & 0xFF) << 24) + ((input[firstByte + 1] & 0xFF) << 16) +
+ ((input[firstByte + 2] & 0xFF) << 8) + (input[firstByte + 3] & 0xFF);
+ }
+
+
+ public static void setFourBytes(byte[] output, int firstByte, long value) {
+ output[firstByte] = (byte) ((value & 0xFF000000) >> 24);
+ output[firstByte + 1] = (byte) ((value & 0xFF0000) >> 16);
+ output[firstByte + 2] = (byte) ((value & 0xFF00) >> 8);
+ output[firstByte + 3] = (byte) (value & 0xFF);
+ }
+}
diff --git a/java/org/apache/coyote/http2/ConnectionException.java b/java/org/apache/coyote/http2/ConnectionException.java
new file mode 100644
index 0000000..5957302
--- /dev/null
+++ b/java/org/apache/coyote/http2/ConnectionException.java
@@ -0,0 +1,29 @@
+/*
+ * 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;
+
+/**
+ * Thrown when an HTTP/2 connection error occurs.
+ */
+public class ConnectionException extends Http2Exception {
+
+ private static final long serialVersionUID = 1L;
+
+ public ConnectionException(String msg, Http2Error error) {
+ super(msg, error);
+ }
+}
diff --git a/java/org/apache/coyote/http2/ConnectionSettingsBase.java b/java/org/apache/coyote/http2/ConnectionSettingsBase.java
new file mode 100644
index 0000000..691740c
--- /dev/null
+++ b/java/org/apache/coyote/http2/ConnectionSettingsBase.java
@@ -0,0 +1,218 @@
+/*
+ * 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.util.HashMap;
+import java.util.Map;
+
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
+import org.apache.tomcat.util.res.StringManager;
+
+public abstract class ConnectionSettingsBase<T extends Throwable> {
+
+ private final Log log = LogFactory.getLog(ConnectionSettingsBase.class);
+ private final StringManager sm = StringManager.getManager(ConnectionSettingsBase.class);
+
+ private final String connectionId;
+
+ // Limits
+ protected static final int MAX_WINDOW_SIZE = (1 << 31) - 1;
+ protected static final int MIN_MAX_FRAME_SIZE = 1 << 14;
+ protected static final int MAX_MAX_FRAME_SIZE = (1 << 24) - 1;
+ protected static final long UNLIMITED = ((long)1 << 32); // Use the maximum possible
+ protected static final int MAX_HEADER_TABLE_SIZE = 1 << 16;
+
+ // Defaults
+ protected static final int DEFAULT_HEADER_TABLE_SIZE = 4096;
+ protected static final boolean DEFAULT_ENABLE_PUSH = true;
+ protected static final long DEFAULT_MAX_CONCURRENT_STREAMS = UNLIMITED;
+ protected static final int DEFAULT_INITIAL_WINDOW_SIZE = (1 << 16) - 1;
+ protected static final int DEFAULT_MAX_FRAME_SIZE = MIN_MAX_FRAME_SIZE;
+ protected static final long DEFAULT_MAX_HEADER_LIST_SIZE = UNLIMITED;
+
+ protected Map<Setting,Long> current = new HashMap<>();
+ protected Map<Setting,Long> pending = new HashMap<>();
+
+
+ public ConnectionSettingsBase(String connectionId) {
+ this.connectionId = connectionId;
+ // Set up the defaults
+ current.put(Setting.HEADER_TABLE_SIZE, Long.valueOf(DEFAULT_HEADER_TABLE_SIZE));
+ current.put(Setting.ENABLE_PUSH, Long.valueOf(DEFAULT_ENABLE_PUSH ? 1 : 0));
+ current.put(Setting.MAX_CONCURRENT_STREAMS, Long.valueOf(DEFAULT_MAX_CONCURRENT_STREAMS));
+ current.put(Setting.INITIAL_WINDOW_SIZE, Long.valueOf(DEFAULT_INITIAL_WINDOW_SIZE));
+ current.put(Setting.MAX_FRAME_SIZE, Long.valueOf(DEFAULT_MAX_FRAME_SIZE));
+ current.put(Setting.MAX_HEADER_LIST_SIZE, Long.valueOf(DEFAULT_MAX_HEADER_LIST_SIZE));
+ }
+
+
+ public void set(Setting setting, long value) throws T {
+ if (log.isDebugEnabled()) {
+ log.debug(sm.getString("connectionSettings.debug",
+ connectionId, setting, Long.toString(value)));
+ }
+
+ switch(setting) {
+ case HEADER_TABLE_SIZE:
+ validateHeaderTableSize(value);
+ break;
+ case ENABLE_PUSH:
+ validateEnablePush(value);
+ break;
+ case MAX_CONCURRENT_STREAMS:
+ // No further validation required
+ break;
+ case INITIAL_WINDOW_SIZE:
+ validateInitialWindowSize(value);
+ break;
+ case MAX_FRAME_SIZE:
+ validateMaxFrameSize(value);
+ break;
+ case MAX_HEADER_LIST_SIZE:
+ // No further validation required
+ break;
+ case UNKNOWN:
+ // Unrecognised. Ignore it.
+ log.warn(sm.getString("connectionSettings.unknown",
+ connectionId, setting, Long.toString(value)));
+ return;
+ }
+
+ set(setting, Long.valueOf(value));
+ }
+
+
+ synchronized void set(Setting setting, Long value) {
+ current.put(setting, value);
+ }
+
+
+ public int getHeaderTableSize() {
+ return getMinInt(Setting.HEADER_TABLE_SIZE);
+ }
+
+
+ public boolean getEnablePush() {
+ long result = getMin(Setting.ENABLE_PUSH);
+ return result != 0;
+ }
+
+
+ public long getMaxConcurrentStreams() {
+ return getMax(Setting.MAX_CONCURRENT_STREAMS);
+ }
+
+
+ public int getInitialWindowSize() {
+ return getMaxInt(Setting.INITIAL_WINDOW_SIZE);
+ }
+
+
+ public int getMaxFrameSize() {
+ return getMaxInt(Setting.MAX_FRAME_SIZE);
+ }
+
+
+ public long getMaxHeaderListSize() {
+ return getMax(Setting.MAX_HEADER_LIST_SIZE);
+ }
+
+
+ private synchronized long getMin(Setting setting) {
+ Long pendingValue = pending.get(setting);
+ long currentValue = current.get(setting).longValue();
+ if (pendingValue == null) {
+ return currentValue;
+ } else {
+ return Math.min(pendingValue.longValue(), currentValue);
+ }
+ }
+
+
+ private synchronized int getMinInt(Setting setting) {
+ long result = getMin(setting);
+ if (result > Integer.MAX_VALUE) {
+ return Integer.MAX_VALUE;
+ } else {
+ return (int) result;
+ }
+ }
+
+
+ private synchronized long getMax(Setting setting) {
+ Long pendingValue = pending.get(setting);
+ long currentValue = current.get(setting).longValue();
+ if (pendingValue == null) {
+ return currentValue;
+ } else {
+ return Math.max(pendingValue.longValue(), currentValue);
+ }
+ }
+
+
+ private synchronized int getMaxInt(Setting setting) {
+ long result = getMax(setting);
+ if (result > Integer.MAX_VALUE) {
+ return Integer.MAX_VALUE;
+ } else {
+ return (int) result;
+ }
+ }
+
+
+ private void validateHeaderTableSize(long headerTableSize) throws T {
+ if (headerTableSize > MAX_HEADER_TABLE_SIZE) {
+ String msg = sm.getString("connectionSettings.headerTableSizeLimit",
+ connectionId, Long.toString(headerTableSize));
+ throwException(msg, Http2Error.PROTOCOL_ERROR);
+ }
+ }
+
+
+ private void validateEnablePush(long enablePush) throws T {
+ // Can't be less than zero since the result of the byte->long conversion
+ // will never be negative
+ if (enablePush > 1) {
+ String msg = sm.getString("connectionSettings.enablePushInvalid",
+ connectionId, Long.toString(enablePush));
+ throwException(msg, Http2Error.PROTOCOL_ERROR);
+ }
+ }
+
+
+ private void validateInitialWindowSize(long initialWindowSize) throws T {
+ if (initialWindowSize > MAX_WINDOW_SIZE) {
+ String msg = sm.getString("connectionSettings.windowSizeTooBig",
+ connectionId, Long.toString(initialWindowSize), Long.toString(MAX_WINDOW_SIZE));
+ throwException(msg, Http2Error.FLOW_CONTROL_ERROR);
+ }
+ }
+
+
+ private void validateMaxFrameSize(long maxFrameSize) throws T {
+ if (maxFrameSize < MIN_MAX_FRAME_SIZE || maxFrameSize > MAX_MAX_FRAME_SIZE) {
+ String msg = sm.getString("connectionSettings.maxFrameSizeInvalid",
+ connectionId, Long.toString(maxFrameSize), Integer.toString(MIN_MAX_FRAME_SIZE),
+ Integer.toString(MAX_MAX_FRAME_SIZE));
+ throwException(msg, Http2Error.PROTOCOL_ERROR);
+ }
+ }
+
+
+ abstract void throwException(String msg, Http2Error error) throws T;
+}
diff --git a/java/org/apache/coyote/http2/ConnectionSettingsLocal.java b/java/org/apache/coyote/http2/ConnectionSettingsLocal.java
new file mode 100644
index 0000000..54e2aa1
--- /dev/null
+++ b/java/org/apache/coyote/http2/ConnectionSettingsLocal.java
@@ -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.coyote.http2;
+
+import java.util.Map;
+
+/**
+ * Represents the local connection settings i.e. the settings the client is
+ * expected to use when communicating with the server. There will be a delay
+ * between calling a setter and the setting taking effect at the client. When a
+ * setter is called, the new value is added to the set of pending settings. Once
+ * the ACK is received, the new value is moved to the current settings. While
+ * waiting for the ACK, the getters will return the most lenient / generous /
+ * relaxed of the current setting and the pending setting. This class does not
+ * validate the values passed to the setters. If an invalid value is used the
+ * client will respond (almost certainly by closing the connection) as defined
+ * in the HTTP/2 specification.
+ */
+public class ConnectionSettingsLocal extends ConnectionSettingsBase<IllegalArgumentException> {
+
+ private boolean sendInProgress = false;
+
+
+ public ConnectionSettingsLocal(String connectionId) {
+ super(connectionId);
+ }
+
+
+ @Override
+ protected synchronized void set(Setting setting, Long value) {
+ checkSend();
+ if (current.get(setting).longValue() == value.longValue()) {
+ pending.remove(setting);
+ } else {
+ pending.put(setting, value);
+ }
+ }
+
+
+ synchronized byte[] getSettingsFrameForPending() {
+ checkSend();
+ int payloadSize = pending.size() * 6;
+ byte[] result = new byte[9 + payloadSize];
+
+ ByteUtil.setThreeBytes(result, 0, payloadSize);
+ result[3] = FrameType.SETTINGS.getIdByte();
+ // No flags
+ // Stream is zero
+ // Payload
+ int pos = 9;
+ for (Map.Entry<Setting,Long> setting : pending.entrySet()) {
+ ByteUtil.setTwoBytes(result, pos, setting.getKey().getId());
+ pos += 2;
+ ByteUtil.setFourBytes(result, pos, setting.getValue().longValue());
+ pos += 4;
+ }
+ sendInProgress = true;
+ return result;
+ }
+
+
+ synchronized boolean ack() {
+ if (sendInProgress) {
+ sendInProgress = false;
+ current.putAll(pending);
+ pending.clear();
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+
+ private void checkSend() {
+ if (sendInProgress) {
+ // Coding error. No need for i18n
+ throw new IllegalStateException();
+ }
+ }
+
+
+ @Override
+ void throwException(String msg, Http2Error error) throws IllegalArgumentException {
+ throw new IllegalArgumentException(msg);
+ }
+}
diff --git a/java/org/apache/coyote/http2/ConnectionSettingsRemote.java b/java/org/apache/coyote/http2/ConnectionSettingsRemote.java
new file mode 100644
index 0000000..914d7fe
--- /dev/null
+++ b/java/org/apache/coyote/http2/ConnectionSettingsRemote.java
@@ -0,0 +1,34 @@
+/*
+ * 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;
+
+/**
+ * Represents the remote connection settings: i.e. the settings the server must
+ * use when communicating with the client.
+ */
+public class ConnectionSettingsRemote extends ConnectionSettingsBase<ConnectionException> {
+
+ public ConnectionSettingsRemote(String connectionId) {
+ super(connectionId);
+ }
+
+
+ @Override
+ void throwException(String msg, Http2Error error) throws ConnectionException {
+ throw new ConnectionException(msg, error);
+ }
+}
diff --git a/java/org/apache/coyote/http2/Constants.java b/java/org/apache/coyote/http2/Constants.java
new file mode 100644
index 0000000..2da5d68
--- /dev/null
+++ b/java/org/apache/coyote/http2/Constants.java
@@ -0,0 +1,33 @@
+/*
+ * 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;
+
+public class Constants {
+
+ // Prioritisation
+ public static final int DEFAULT_WEIGHT = 16;
+
+ // Parsing
+ static final int DEFAULT_HEADER_READ_BUFFER_SIZE = 1024;
+
+ // Limits
+ static final int DEFAULT_MAX_COOKIE_COUNT = 200;
+ static final int DEFAULT_MAX_HEADER_COUNT = 100;
+ static final int DEFAULT_MAX_HEADER_SIZE = 8 * 1024;
+ static final int DEFAULT_MAX_TRAILER_COUNT = 100;
+ static final int DEFAULT_MAX_TRAILER_SIZE = 8 * 1024;
+}
diff --git a/java/org/apache/coyote/http2/Flags.java b/java/org/apache/coyote/http2/Flags.java
new file mode 100644
index 0000000..d520951
--- /dev/null
+++ b/java/org/apache/coyote/http2/Flags.java
@@ -0,0 +1,49 @@
+/*
+ * 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;
+
+public class Flags {
+
+ private Flags() {
+ // Utility class. Hide default constructor
+ }
+
+
+ public static boolean isEndOfStream(int flags) {
+ return (flags & 0x01) > 0;
+ }
+
+
+ public static boolean isAck(int flags) {
+ return (flags & 0x01) > 0;
+ }
+
+
+ public static boolean isEndOfHeaders(int flags) {
+ return (flags & 0x04) > 0;
+ }
+
+
+ public static boolean hasPadding(int flags) {
+ return (flags & 0x08) > 0;
+ }
+
+
+ public static boolean hasPriority(int flags) {
+ return (flags & 0x20) > 0;
+ }
+}
diff --git a/java/org/apache/coyote/http2/FrameType.java b/java/org/apache/coyote/http2/FrameType.java
new file mode 100644
index 0000000..76f655d
--- /dev/null
+++ b/java/org/apache/coyote/http2/FrameType.java
@@ -0,0 +1,138 @@
+/*
+ * 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.apache.tomcat.util.res.StringManager;
+
+public enum FrameType {
+
+ DATA (0, false, true, null, false),
+ HEADERS (1, false, true, null, true),
+ PRIORITY (2, false, true, equals(5), false),
+ RST (3, false, true, equals(4), false),
+ SETTINGS (4, true, false, dividableBy(6), true),
+ PUSH_PROMISE (5, false, true, greaterOrEquals(4), true),
+ PING (6, true, false, equals(8), false),
+ GOAWAY (7, true, false, greaterOrEquals(8), false),
+ WINDOW_UPDATE (8, true, true, equals(4), true),
+ CONTINUATION (9, false, true, null, true),
+ UNKNOWN (256, true, true, null, false);
+
+ private static final StringManager sm = StringManager.getManager(FrameType.class);
+
+ private final int id;
+ private final boolean streamZero;
+ private final boolean streamNonZero;
+ private final IntPredicate payloadSizeValidator;
+ private final boolean payloadErrorFatal;
+
+
+ private FrameType(int id, boolean streamZero, boolean streamNonZero,
+ IntPredicate payloadSizeValidator, boolean payloadErrorFatal) {
+ this.id = id;
+ this.streamZero = streamZero;
+ this.streamNonZero = streamNonZero;
+ this.payloadSizeValidator = payloadSizeValidator;
+ this.payloadErrorFatal = payloadErrorFatal;
+ }
+
+
+ public byte getIdByte() {
+ return (byte) id;
+ }
+
+
+ public void check(int streamId, int payloadSize) throws Http2Exception {
+ // Is FrameType valid for the given stream?
+ if (streamId == 0 && !streamZero || streamId != 0 && !streamNonZero) {
+ throw new ConnectionException(sm.getString("frameType.checkStream", this),
+ Http2Error.PROTOCOL_ERROR);
+ }
+
+ // Is the payload size valid for the given FrameType
+ if (payloadSizeValidator != null && !payloadSizeValidator.test(payloadSize)) {
+ if (payloadErrorFatal || streamId == 0) {
+ throw new ConnectionException(sm.getString("frameType.checkPayloadSize",
+ Integer.toString(payloadSize), this),
+ Http2Error.FRAME_SIZE_ERROR);
+ } else {
+ throw new StreamException(sm.getString("frameType.checkPayloadSize",
+ Integer.toString(payloadSize), this),
+ Http2Error.FRAME_SIZE_ERROR, streamId);
+ }
+ }
+ }
+
+
+ public static FrameType valueOf(int i) {
+ switch(i) {
+ case 0:
+ return DATA;
+ case 1:
+ return HEADERS;
+ case 2:
+ return PRIORITY;
+ case 3:
+ return RST;
+ case 4:
+ return SETTINGS;
+ case 5:
+ return PUSH_PROMISE;
+ case 6:
+ return PING;
+ case 7:
+ return GOAWAY;
+ case 8:
+ return WINDOW_UPDATE;
+ case 9:
+ return CONTINUATION;
+ default:
+ return UNKNOWN;
+ }
+ }
+
+ private interface IntPredicate {
+ boolean test(int x);
+ }
+
+ private static IntPredicate greaterOrEquals(final int y) {
+ return new IntPredicate() {
+ @Override
+ public boolean test(int x) {
+ return x >= y;
+ }
+ };
+ }
+
+ private static IntPredicate equals(final int y) {
+ return new IntPredicate() {
+ @Override
+ public boolean test(int x) {
+ return x == y;
+ }
+ };
+ }
+
+ private static IntPredicate dividableBy(final int y) {
+ return new IntPredicate() {
+ @Override
+ public boolean test(int x) {
+ return x % y == 0;
+ }
+ };
+ }
+}
diff --git a/java/org/apache/coyote/http2/HPackHuffman.java b/java/org/apache/coyote/http2/HPackHuffman.java
new file mode 100644
index 0000000..38ff624
--- /dev/null
+++ b/java/org/apache/coyote/http2/HPackHuffman.java
@@ -0,0 +1,567 @@
+/*
+ * 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.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.apache.tomcat.util.res.StringManager;
+
+public class HPackHuffman {
+
+ protected static final StringManager sm = StringManager.getManager(HPackHuffman.class);
+
+ private static final HuffmanCode[] HUFFMAN_CODES;
+
+ /**
+ * array based tree representation of a huffman code.
+ * <p/>
+ * the high two bytes corresponds to the tree node if the bit is set, and the low two bytes for if it is clear
+ * if the high bit is set it is a terminal node, otherwise it contains the next node position.
+ */
+ private static final int[] DECODING_TABLE;
+
+ private static final int LOW_TERMINAL_BIT = (0b10000000) << 8;
+ private static final int HIGH_TERMINAL_BIT = (0b10000000) << 24;
+ private static final int LOW_MASK = 0b0111111111111111;
+
+
+ static {
+
+ HuffmanCode[] codes = new HuffmanCode[257];
+
+ codes[0] = new HuffmanCode(0x1ff8, 13);
+ codes[1] = new HuffmanCode(0x7fffd8, 23);
+ codes[2] = new HuffmanCode(0xfffffe2, 28);
+ codes[3] = new HuffmanCode(0xfffffe3, 28);
+ codes[4] = new HuffmanCode(0xfffffe4, 28);
+ codes[5] = new HuffmanCode(0xfffffe5, 28);
+ codes[6] = new HuffmanCode(0xfffffe6, 28);
+ codes[7] = new HuffmanCode(0xfffffe7, 28);
+ codes[8] = new HuffmanCode(0xfffffe8, 28);
+ codes[9] = new HuffmanCode(0xffffea, 24);
+ codes[10] = new HuffmanCode(0x3ffffffc, 30);
+ codes[11] = new HuffmanCode(0xfffffe9, 28);
+ codes[12] = new HuffmanCode(0xfffffea, 28);
+ codes[13] = new HuffmanCode(0x3ffffffd, 30);
+ codes[14] = new HuffmanCode(0xfffffeb, 28);
+ codes[15] = new HuffmanCode(0xfffffec, 28);
+ codes[16] = new HuffmanCode(0xfffffed, 28);
+ codes[17] = new HuffmanCode(0xfffffee, 28);
+ codes[18] = new HuffmanCode(0xfffffef, 28);
+ codes[19] = new HuffmanCode(0xffffff0, 28);
+ codes[20] = new HuffmanCode(0xffffff1, 28);
+ codes[21] = new HuffmanCode(0xffffff2, 28);
+ codes[22] = new HuffmanCode(0x3ffffffe, 30);
+ codes[23] = new HuffmanCode(0xffffff3, 28);
+ codes[24] = new HuffmanCode(0xffffff4, 28);
+ codes[25] = new HuffmanCode(0xffffff5, 28);
+ codes[26] = new HuffmanCode(0xffffff6, 28);
+ codes[27] = new HuffmanCode(0xffffff7, 28);
+ codes[28] = new HuffmanCode(0xffffff8, 28);
+ codes[29] = new HuffmanCode(0xffffff9, 28);
+ codes[30] = new HuffmanCode(0xffffffa, 28);
+ codes[31] = new HuffmanCode(0xffffffb, 28);
+ codes[32] = new HuffmanCode(0x14, 6);
+ codes[33] = new HuffmanCode(0x3f8, 10);
+ codes[34] = new HuffmanCode(0x3f9, 10);
+ codes[35] = new HuffmanCode(0xffa, 12);
+ codes[36] = new HuffmanCode(0x1ff9, 13);
+ codes[37] = new HuffmanCode(0x15, 6);
+ codes[38] = new HuffmanCode(0xf8, 8);
+ codes[39] = new HuffmanCode(0x7fa, 11);
+ codes[40] = new HuffmanCode(0x3fa, 10);
+ codes[41] = new HuffmanCode(0x3fb, 10);
+ codes[42] = new HuffmanCode(0xf9, 8);
+ codes[43] = new HuffmanCode(0x7fb, 11);
+ codes[44] = new HuffmanCode(0xfa, 8);
+ codes[45] = new HuffmanCode(0x16, 6);
+ codes[46] = new HuffmanCode(0x17, 6);
+ codes[47] = new HuffmanCode(0x18, 6);
+ codes[48] = new HuffmanCode(0x0, 5);
+ codes[49] = new HuffmanCode(0x1, 5);
+ codes[50] = new HuffmanCode(0x2, 5);
+ codes[51] = new HuffmanCode(0x19, 6);
+ codes[52] = new HuffmanCode(0x1a, 6);
+ codes[53] = new HuffmanCode(0x1b, 6);
+ codes[54] = new HuffmanCode(0x1c, 6);
+ codes[55] = new HuffmanCode(0x1d, 6);
+ codes[56] = new HuffmanCode(0x1e, 6);
+ codes[57] = new HuffmanCode(0x1f, 6);
+ codes[58] = new HuffmanCode(0x5c, 7);
+ codes[59] = new HuffmanCode(0xfb, 8);
+ codes[60] = new HuffmanCode(0x7ffc, 15);
+ codes[61] = new HuffmanCode(0x20, 6);
+ codes[62] = new HuffmanCode(0xffb, 12);
+ codes[63] = new HuffmanCode(0x3fc, 10);
+ codes[64] = new HuffmanCode(0x1ffa, 13);
+ codes[65] = new HuffmanCode(0x21, 6);
+ codes[66] = new HuffmanCode(0x5d, 7);
+ codes[67] = new HuffmanCode(0x5e, 7);
+ codes[68] = new HuffmanCode(0x5f, 7);
+ codes[69] = new HuffmanCode(0x60, 7);
+ codes[70] = new HuffmanCode(0x61, 7);
+ codes[71] = new HuffmanCode(0x62, 7);
+ codes[72] = new HuffmanCode(0x63, 7);
+ codes[73] = new HuffmanCode(0x64, 7);
+ codes[74] = new HuffmanCode(0x65, 7);
+ codes[75] = new HuffmanCode(0x66, 7);
+ codes[76] = new HuffmanCode(0x67, 7);
+ codes[77] = new HuffmanCode(0x68, 7);
+ codes[78] = new HuffmanCode(0x69, 7);
+ codes[79] = new HuffmanCode(0x6a, 7);
+ codes[80] = new HuffmanCode(0x6b, 7);
+ codes[81] = new HuffmanCode(0x6c, 7);
+ codes[82] = new HuffmanCode(0x6d, 7);
+ codes[83] = new HuffmanCode(0x6e, 7);
+ codes[84] = new HuffmanCode(0x6f, 7);
+ codes[85] = new HuffmanCode(0x70, 7);
+ codes[86] = new HuffmanCode(0x71, 7);
+ codes[87] = new HuffmanCode(0x72, 7);
+ codes[88] = new HuffmanCode(0xfc, 8);
+ codes[89] = new HuffmanCode(0x73, 7);
+ codes[90] = new HuffmanCode(0xfd, 8);
+ codes[91] = new HuffmanCode(0x1ffb, 13);
+ codes[92] = new HuffmanCode(0x7fff0, 19);
+ codes[93] = new HuffmanCode(0x1ffc, 13);
+ codes[94] = new HuffmanCode(0x3ffc, 14);
+ codes[95] = new HuffmanCode(0x22, 6);
+ codes[96] = new HuffmanCode(0x7ffd, 15);
+ codes[97] = new HuffmanCode(0x3, 5);
+ codes[98] = new HuffmanCode(0x23, 6);
+ codes[99] = new HuffmanCode(0x4, 5);
+ codes[100] = new HuffmanCode(0x24, 6);
+ codes[101] = new HuffmanCode(0x5, 5);
+ codes[102] = new HuffmanCode(0x25, 6);
+ codes[103] = new HuffmanCode(0x26, 6);
+ codes[104] = new HuffmanCode(0x27, 6);
+ codes[105] = new HuffmanCode(0x6, 5);
+ codes[106] = new HuffmanCode(0x74, 7);
+ codes[107] = new HuffmanCode(0x75, 7);
+ codes[108] = new HuffmanCode(0x28, 6);
+ codes[109] = new HuffmanCode(0x29, 6);
+ codes[110] = new HuffmanCode(0x2a, 6);
+ codes[111] = new HuffmanCode(0x7, 5);
+ codes[112] = new HuffmanCode(0x2b, 6);
+ codes[113] = new HuffmanCode(0x76, 7);
+ codes[114] = new HuffmanCode(0x2c, 6);
+ codes[115] = new HuffmanCode(0x8, 5);
+ codes[116] = new HuffmanCode(0x9, 5);
+ codes[117] = new HuffmanCode(0x2d, 6);
+ codes[118] = new HuffmanCode(0x77, 7);
+ codes[119] = new HuffmanCode(0x78, 7);
+ codes[120] = new HuffmanCode(0x79, 7);
+ codes[121] = new HuffmanCode(0x7a, 7);
+ codes[122] = new HuffmanCode(0x7b, 7);
+ codes[123] = new HuffmanCode(0x7ffe, 15);
+ codes[124] = new HuffmanCode(0x7fc, 11);
+ codes[125] = new HuffmanCode(0x3ffd, 14);
+ codes[126] = new HuffmanCode(0x1ffd, 13);
+ codes[127] = new HuffmanCode(0xffffffc, 28);
+ codes[128] = new HuffmanCode(0xfffe6, 20);
+ codes[129] = new HuffmanCode(0x3fffd2, 22);
+ codes[130] = new HuffmanCode(0xfffe7, 20);
+ codes[131] = new HuffmanCode(0xfffe8, 20);
+ codes[132] = new HuffmanCode(0x3fffd3, 22);
+ codes[133] = new HuffmanCode(0x3fffd4, 22);
+ codes[134] = new HuffmanCode(0x3fffd5, 22);
+ codes[135] = new HuffmanCode(0x7fffd9, 23);
+ codes[136] = new HuffmanCode(0x3fffd6, 22);
+ codes[137] = new HuffmanCode(0x7fffda, 23);
+ codes[138] = new HuffmanCode(0x7fffdb, 23);
+ codes[139] = new HuffmanCode(0x7fffdc, 23);
+ codes[140] = new HuffmanCode(0x7fffdd, 23);
+ codes[141] = new HuffmanCode(0x7fffde, 23);
+ codes[142] = new HuffmanCode(0xffffeb, 24);
+ codes[143] = new HuffmanCode(0x7fffdf, 23);
+ codes[144] = new HuffmanCode(0xffffec, 24);
+ codes[145] = new HuffmanCode(0xffffed, 24);
+ codes[146] = new HuffmanCode(0x3fffd7, 22);
+ codes[147] = new HuffmanCode(0x7fffe0, 23);
+ codes[148] = new HuffmanCode(0xffffee, 24);
+ codes[149] = new HuffmanCode(0x7fffe1, 23);
+ codes[150] = new HuffmanCode(0x7fffe2, 23);
+ codes[151] = new HuffmanCode(0x7fffe3, 23);
+ codes[152] = new HuffmanCode(0x7fffe4, 23);
+ codes[153] = new HuffmanCode(0x1fffdc, 21);
+ codes[154] = new HuffmanCode(0x3fffd8, 22);
+ codes[155] = new HuffmanCode(0x7fffe5, 23);
+ codes[156] = new HuffmanCode(0x3fffd9, 22);
+ codes[157] = new HuffmanCode(0x7fffe6, 23);
+ codes[158] = new HuffmanCode(0x7fffe7, 23);
+ codes[159] = new HuffmanCode(0xffffef, 24);
+ codes[160] = new HuffmanCode(0x3fffda, 22);
+ codes[161] = new HuffmanCode(0x1fffdd, 21);
+ codes[162] = new HuffmanCode(0xfffe9, 20);
+ codes[163] = new HuffmanCode(0x3fffdb, 22);
+ codes[164] = new HuffmanCode(0x3fffdc, 22);
+ codes[165] = new HuffmanCode(0x7fffe8, 23);
+ codes[166] = new HuffmanCode(0x7fffe9, 23);
+ codes[167] = new HuffmanCode(0x1fffde, 21);
+ codes[168] = new HuffmanCode(0x7fffea, 23);
+ codes[169] = new HuffmanCode(0x3fffdd, 22);
+ codes[170] = new HuffmanCode(0x3fffde, 22);
+ codes[171] = new HuffmanCode(0xfffff0, 24);
+ codes[172] = new HuffmanCode(0x1fffdf, 21);
+ codes[173] = new HuffmanCode(0x3fffdf, 22);
+ codes[174] = new HuffmanCode(0x7fffeb, 23);
+ codes[175] = new HuffmanCode(0x7fffec, 23);
+ codes[176] = new HuffmanCode(0x1fffe0, 21);
+ codes[177] = new HuffmanCode(0x1fffe1, 21);
+ codes[178] = new HuffmanCode(0x3fffe0, 22);
+ codes[179] = new HuffmanCode(0x1fffe2, 21);
+ codes[180] = new HuffmanCode(0x7fffed, 23);
+ codes[181] = new HuffmanCode(0x3fffe1, 22);
+ codes[182] = new HuffmanCode(0x7fffee, 23);
+ codes[183] = new HuffmanCode(0x7fffef, 23);
+ codes[184] = new HuffmanCode(0xfffea, 20);
+ codes[185] = new HuffmanCode(0x3fffe2, 22);
+ codes[186] = new HuffmanCode(0x3fffe3, 22);
+ codes[187] = new HuffmanCode(0x3fffe4, 22);
+ codes[188] = new HuffmanCode(0x7ffff0, 23);
+ codes[189] = new HuffmanCode(0x3fffe5, 22);
+ codes[190] = new HuffmanCode(0x3fffe6, 22);
+ codes[191] = new HuffmanCode(0x7ffff1, 23);
+ codes[192] = new HuffmanCode(0x3ffffe0, 26);
+ codes[193] = new HuffmanCode(0x3ffffe1, 26);
+ codes[194] = new HuffmanCode(0xfffeb, 20);
+ codes[195] = new HuffmanCode(0x7fff1, 19);
+ codes[196] = new HuffmanCode(0x3fffe7, 22);
+ codes[197] = new HuffmanCode(0x7ffff2, 23);
+ codes[198] = new HuffmanCode(0x3fffe8, 22);
+ codes[199] = new HuffmanCode(0x1ffffec, 25);
+ codes[200] = new HuffmanCode(0x3ffffe2, 26);
+ codes[201] = new HuffmanCode(0x3ffffe3, 26);
+ codes[202] = new HuffmanCode(0x3ffffe4, 26);
+ codes[203] = new HuffmanCode(0x7ffffde, 27);
+ codes[204] = new HuffmanCode(0x7ffffdf, 27);
+ codes[205] = new HuffmanCode(0x3ffffe5, 26);
+ codes[206] = new HuffmanCode(0xfffff1, 24);
+ codes[207] = new HuffmanCode(0x1ffffed, 25);
+ codes[208] = new HuffmanCode(0x7fff2, 19);
+ codes[209] = new HuffmanCode(0x1fffe3, 21);
+ codes[210] = new HuffmanCode(0x3ffffe6, 26);
+ codes[211] = new HuffmanCode(0x7ffffe0, 27);
+ codes[212] = new HuffmanCode(0x7ffffe1, 27);
+ codes[213] = new HuffmanCode(0x3ffffe7, 26);
+ codes[214] = new HuffmanCode(0x7ffffe2, 27);
+ codes[215] = new HuffmanCode(0xfffff2, 24);
+ codes[216] = new HuffmanCode(0x1fffe4, 21);
+ codes[217] = new HuffmanCode(0x1fffe5, 21);
+ codes[218] = new HuffmanCode(0x3ffffe8, 26);
+ codes[219] = new HuffmanCode(0x3ffffe9, 26);
+ codes[220] = new HuffmanCode(0xffffffd, 28);
+ codes[221] = new HuffmanCode(0x7ffffe3, 27);
+ codes[222] = new HuffmanCode(0x7ffffe4, 27);
+ codes[223] = new HuffmanCode(0x7ffffe5, 27);
+ codes[224] = new HuffmanCode(0xfffec, 20);
+ codes[225] = new HuffmanCode(0xfffff3, 24);
+ codes[226] = new HuffmanCode(0xfffed, 20);
+ codes[227] = new HuffmanCode(0x1fffe6, 21);
+ codes[228] = new HuffmanCode(0x3fffe9, 22);
+ codes[229] = new HuffmanCode(0x1fffe7, 21);
+ codes[230] = new HuffmanCode(0x1fffe8, 21);
+ codes[231] = new HuffmanCode(0x7ffff3, 23);
+ codes[232] = new HuffmanCode(0x3fffea, 22);
+ codes[233] = new HuffmanCode(0x3fffeb, 22);
+ codes[234] = new HuffmanCode(0x1ffffee, 25);
+ codes[235] = new HuffmanCode(0x1ffffef, 25);
+ codes[236] = new HuffmanCode(0xfffff4, 24);
+ codes[237] = new HuffmanCode(0xfffff5, 24);
+ codes[238] = new HuffmanCode(0x3ffffea, 26);
+ codes[239] = new HuffmanCode(0x7ffff4, 23);
+ codes[240] = new HuffmanCode(0x3ffffeb, 26);
+ codes[241] = new HuffmanCode(0x7ffffe6, 27);
+ codes[242] = new HuffmanCode(0x3ffffec, 26);
+ codes[243] = new HuffmanCode(0x3ffffed, 26);
+ codes[244] = new HuffmanCode(0x7ffffe7, 27);
+ codes[245] = new HuffmanCode(0x7ffffe8, 27);
+ codes[246] = new HuffmanCode(0x7ffffe9, 27);
+ codes[247] = new HuffmanCode(0x7ffffea, 27);
+ codes[248] = new HuffmanCode(0x7ffffeb, 27);
+ codes[249] = new HuffmanCode(0xffffffe, 28);
+ codes[250] = new HuffmanCode(0x7ffffec, 27);
+ codes[251] = new HuffmanCode(0x7ffffed, 27);
+ codes[252] = new HuffmanCode(0x7ffffee, 27);
+ codes[253] = new HuffmanCode(0x7ffffef, 27);
+ codes[254] = new HuffmanCode(0x7fffff0, 27);
+ codes[255] = new HuffmanCode(0x3ffffee, 26);
+ codes[256] = new HuffmanCode(0x3fffffff, 30);
+ HUFFMAN_CODES = codes;
+
+ //lengths determined by experimentation, just set it to something large then see how large it actually ends up
+ int[] codingTree = new int[256];
+ //the current position in the tree
+ int pos = 0;
+ int allocated = 1; //the next position to allocate to
+ //map of the current state at a given position
+ //only used while building the tree
+ HuffmanCode[] currentCode = new HuffmanCode[256];
+ currentCode[0] = new HuffmanCode(0, 0);
+
+ final Set<HuffmanCode> allCodes = new HashSet<>();
+ allCodes.addAll(Arrays.asList(HUFFMAN_CODES));
+
+ while (!allCodes.isEmpty()) {
+ int length = currentCode[pos].length;
+ int code = currentCode[pos].value;
+
+ int newLength = length + 1;
+ HuffmanCode high = new HuffmanCode(code << 1 | 1, newLength);
+ HuffmanCode low = new HuffmanCode(code << 1, newLength);
+ int newVal = 0;
+ boolean highTerminal = allCodes.remove(high);
+ if (highTerminal) {
+ //bah, linear search
+ int i = 0;
+ for (i = 0; i < codes.length; ++i) {
+ if (codes[i].equals(high)) {
+ break;
+ }
+ }
+ newVal = LOW_TERMINAL_BIT | i;
+ } else {
+ int highPos = allocated++;
+ currentCode[highPos] = high;
+ newVal = highPos;
+ }
+ newVal <<= 16;
+ boolean lowTerminal = allCodes.remove(low);
+ if (lowTerminal) {
+ //bah, linear search
+ int i = 0;
+ for (i = 0; i < codes.length; ++i) {
+ if (codes[i].equals(low)) {
+ break;
+ }
+ }
+ newVal |= LOW_TERMINAL_BIT | i;
+ } else {
+ int lowPos = allocated++;
+ currentCode[lowPos] = low;
+ newVal |= lowPos;
+ }
+ codingTree[pos] = newVal;
+ pos++;
+ }
+ DECODING_TABLE = codingTree;
+ }
+
+ /**
+ * Decodes a huffman encoded string into the target StringBuilder. There
+ * must be enough space left in the buffer for this method to succeed.
+ *
+ * @param data The byte buffer
+ * @param length The length of data from the buffer to decode
+ * @param target The target for the decompressed data
+ *
+ * @throws HpackException If the Huffman encoded value in HPACK headers did
+ * not end with EOS padding
+ */
+ public static void decode(ByteBuffer data, int length, StringBuilder target)
+ throws HpackException {
+ assert data.remaining() >= length;
+ int treePos = 0;
+ boolean eosBits = true;
+ for (int i = 0; i < length; ++i) {
+ byte b = data.get();
+ int bitPos = 7;
+ while (bitPos >= 0) {
+ int val = DECODING_TABLE[treePos];
+ if (((1 << bitPos) & b) == 0) {
+ eosBits = false;
+ //bit not set, we want the lower part of the tree
+ if ((val & LOW_TERMINAL_BIT) == 0) {
+ treePos = val & LOW_MASK;
+ } else {
+ target.append((char) (val & LOW_MASK));
+ treePos = 0;
+ eosBits = true;
+ }
+ } else {
+ //bit not set, we want the lower part of the tree
+ if ((val & HIGH_TERMINAL_BIT) == 0) {
+ treePos = (val >> 16) & LOW_MASK;
+ } else {
+ target.append((char) ((val >> 16) & LOW_MASK));
+ treePos = 0;
+ eosBits = true;
+ }
+ }
+ bitPos--;
+ }
+ }
+ if (!eosBits) {
+ throw new HpackException(sm.getString(
+ "hpackhuffman.huffmanEncodedHpackValueDidNotEndWithEOS"));
+ }
+ }
+
+
+ /**
+ * Encodes the given string into the buffer. If there is not enough space in
+ * the buffer, or the encoded version is bigger than the original it will
+ * return false and not modify the buffers position.
+ *
+ * @param buffer The buffer to encode into
+ * @param toEncode The string to encode
+ * @param forceLowercase If the string should be encoded in lower case
+ * @return true if encoding succeeded
+ */
+ public static boolean encode(ByteBuffer buffer, String toEncode, boolean forceLowercase) {
+ if (buffer.remaining() <= toEncode.length()) {
+ return false;
+ }
+ int start = buffer.position();
+ //this sucks, but we need to put the length first
+ //and we don't really have any option but to calculate it in advance to make sure we have left enough room
+ //so we end up iterating twice
+ int length = 0;
+ for (int i = 0; i < toEncode.length(); ++i) {
+ byte c = (byte) toEncode.charAt(i);
+ if(forceLowercase) {
+ c = Hpack.toLower(c);
+ }
+ HuffmanCode code = HUFFMAN_CODES[c];
+ length += code.length;
+ }
+ int byteLength = length / 8 + (length % 8 == 0 ? 0 : 1);
+
+ buffer.put((byte) (1 << 7));
+ Hpack.encodeInteger(buffer, byteLength, 7);
+
+
+ int bytePos = 0;
+ byte currentBufferByte = 0;
+ for (int i = 0; i < toEncode.length(); ++i) {
+ byte c = (byte) toEncode.charAt(i);
+ if(forceLowercase) {
+ c = Hpack.toLower(c);
+ }
+ HuffmanCode code = HUFFMAN_CODES[c];
+ if (code.length + bytePos <= 8) {
+ //it fits in the current byte
+ currentBufferByte |= ((code.value & 0xFF) << 8 - (code.length + bytePos));
+ bytePos += code.length;
+ } else {
+ //it does not fit, it may need up to 4 bytes
+ int val = code.value;
+ int rem = code.length;
+ while (rem > 0) {
+ if (!buffer.hasRemaining()) {
+ buffer.position(start);
+ return false;
+ }
+ int remainingInByte = 8 - bytePos;
+ if (rem > remainingInByte) {
+ currentBufferByte |= (val >> (rem - remainingInByte));
+ } else {
+ currentBufferByte |= (val << (remainingInByte - rem));
+ }
+ if (rem > remainingInByte) {
+ buffer.put(currentBufferByte);
+ currentBufferByte = 0;
+ bytePos = 0;
+ } else {
+ bytePos = rem;
+ }
+ rem -= remainingInByte;
+ }
+ }
+ if (bytePos == 8) {
+ if (!buffer.hasRemaining()) {
+ buffer.position(start);
+ return false;
+ }
+ buffer.put(currentBufferByte);
+ currentBufferByte = 0;
+ bytePos = 0;
+ }
+ if (buffer.position() - start > toEncode.length()) {
+ //the encoded version is longer than the original
+ //just return false
+ buffer.position(start);
+ return false;
+ }
+ }
+ if (bytePos > 0) {
+ //add the EOS bytes if we have not finished on a single byte
+ if (!buffer.hasRemaining()) {
+ buffer.position(start);
+ return false;
+ }
+ buffer.put((byte) (currentBufferByte | ((0xFF) >> bytePos)));
+ }
+ return true;
+ }
+
+ protected static class HuffmanCode {
+ /**
+ * The value of the least significan't bits of the code
+ */
+ int value;
+ /**
+ * length of the code, in bits
+ */
+ int length;
+
+ public HuffmanCode(int value, int length) {
+ this.value = value;
+ this.length = length;
+ }
+
+ public int getValue() {
+ return value;
+ }
+
+ public int getLength() {
+ return length;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+
+
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ HuffmanCode that = (HuffmanCode) o;
+
+ if (length != that.length) return false;
+ if (value != that.value) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = value;
+ result = 31 * result + length;
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "HuffmanCode{" +
+ "value=" + value +
+ ", length=" + length +
+ '}';
+ }
+ }
+}
diff --git a/java/org/apache/coyote/http2/HeaderSink.java b/java/org/apache/coyote/http2/HeaderSink.java
new file mode 100644
index 0000000..341bca9
--- /dev/null
+++ b/java/org/apache/coyote/http2/HeaderSink.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.coyote.http2;
+
+import org.apache.coyote.http2.HpackDecoder.HeaderEmitter;
+
+/**
+ * Purpose of this class is to silently swallow any headers. It is used once
+ * the connection close process has started if headers for new streams are
+ * received.
+ */
+public class HeaderSink implements HeaderEmitter {
+
+ @Override
+ public void emitHeader(String name, String value) {
+ // NO-OP
+ }
+
+ @Override
+ public void validateHeaders() throws StreamException {
+ // NO-OP
+ }
+}
diff --git a/java/org/apache/coyote/http2/Hpack.java b/java/org/apache/coyote/http2/Hpack.java
new file mode 100644
index 0000000..e60c9d5
--- /dev/null
+++ b/java/org/apache/coyote/http2/Hpack.java
@@ -0,0 +1,216 @@
+/*
+ * 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.nio.ByteBuffer;
+
+import org.apache.tomcat.util.res.StringManager;
+
+final class Hpack {
+
+ private static final StringManager sm = StringManager.getManager(Hpack.class);
+
+ 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
+
+ /**
+ * table that contains powers of two,
+ * used as both bitmask and to quickly calculate 2^n
+ */
+ private static final int[] PREFIX_TABLE;
+
+
+ static final HeaderField[] STATIC_TABLE;
+ static final int STATIC_TABLE_LENGTH;
+
+ static {
+ PREFIX_TABLE = new int[32];
+ for (int i = 0; i < 32; ++i) {
+ int n = 0;
+ for (int j = 0; j < i; ++j) {
+ n = n << 1;
+ n |= 1;
+ }
+ PREFIX_TABLE[i] = n;
+ }
+
+ 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[4] = new HeaderField(":path", "/");
+ fields[5] = new HeaderField(":path", "/index.html");
+ fields[6] = new HeaderField(":scheme", "http");
+ fields[7] = new HeaderField(":scheme", "https");
+ fields[8] = new HeaderField(":status", "200");
+ fields[9] = new HeaderField(":status", "204");
+ fields[10] = new HeaderField(":status", "206");
+ fields[11] = new HeaderField(":status", "304");
+ fields[12] = new HeaderField(":status", "400");
+ fields[13] = new HeaderField(":status", "404");
+ fields[14] = new HeaderField(":status", "500");
+ fields[15] = new HeaderField("accept-charset", null);
+ fields[16] = new HeaderField("accept-encoding", "gzip, deflate");
+ fields[17] = new HeaderField("accept-language", null);
+ fields[18] = new HeaderField("accept-ranges", null);
+ fields[19] = new HeaderField("accept", null);
+ fields[20] = new HeaderField("access-control-allow-origin", null);
+ fields[21] = new HeaderField("age", null);
+ fields[22] = new HeaderField("allow", null);
+ fields[23] = new HeaderField("authorization", null);
+ fields[24] = new HeaderField("cache-control", null);
+ fields[25] = new HeaderField("content-disposition", null);
+ fields[26] = new HeaderField("content-encoding", null);
+ fields[27] = new HeaderField("content-language", null);
+ fields[28] = new HeaderField("content-length", null);
+ fields[29] = new HeaderField("content-location", null);
+ fields[30] = new HeaderField("content-range", null);
+ fields[31] = new HeaderField("content-type", null);
+ fields[32] = new HeaderField("cookie", null);
+ fields[33] = new HeaderField("date", null);
+ fields[34] = new HeaderField("etag", null);
+ fields[35] = new HeaderField("expect", null);
+ fields[36] = new HeaderField("expires", null);
+ fields[37] = new HeaderField("from", null);
+ fields[38] = new HeaderField("host", null);
+ fields[39] = new HeaderField("if-match", null);
+ fields[40] = new HeaderField("if-modified-since", null);
+ fields[41] = new HeaderField("if-none-match", null);
+ fields[42] = new HeaderField("if-range", null);
+ fields[43] = new HeaderField("if-unmodified-since", null);
+ fields[44] = new HeaderField("last-modified", null);
+ fields[45] = new HeaderField("link", null);
+ fields[46] = new HeaderField("location", null);
+ fields[47] = new HeaderField("max-forwards", null);
+ fields[48] = new HeaderField("proxy-authenticate", null);
+ fields[49] = new HeaderField("proxy-authorization", null);
+ fields[50] = new HeaderField("range", null);
+ fields[51] = new HeaderField("referer", null);
+ fields[52] = new HeaderField("refresh", null);
+ fields[53] = new HeaderField("retry-after", null);
+ fields[54] = new HeaderField("server", null);
+ fields[55] = new HeaderField("set-cookie", null);
+ fields[56] = new HeaderField("strict-transport-security", null);
+ fields[57] = new HeaderField("transfer-encoding", null);
+ fields[58] = new HeaderField("user-agent", null);
+ fields[59] = new HeaderField("vary", null);
+ fields[60] = new HeaderField("via", null);
+ fields[61] = new HeaderField("www-authenticate", null);
+ STATIC_TABLE = fields;
+ STATIC_TABLE_LENGTH = STATIC_TABLE.length - 1;
+ }
+
+ static class HeaderField {
+ final String name;
+ final String value;
+ final int size;
+
+ HeaderField(String name, String value) {
+ this.name = name;
+ this.value = value;
+ if (value != null) {
+ this.size = 32 + name.length() + value.length();
+ } else {
+ this.size = -1;
+ }
+ }
+ }
+
+ /**
+ * Decodes an integer in the HPACK prefex format. If the return value is -1
+ * it means that there was not enough data in the buffer to complete the decoding
+ * sequence.
+ * <p/>
+ * If this method returns -1 then the source buffer will not have been modified.
+ *
+ * @param source The buffer that contains the integer
+ * @param n The encoding prefix length
+ * @return The encoded integer, or -1 if there was not enough data
+ */
+ static int decodeInteger(ByteBuffer source, int n) throws HpackException {
+ if (source.remaining() == 0) {
+ return -1;
+ }
+ int count = 1;
+ int sp = source.position();
+ int mask = PREFIX_TABLE[n];
+
+ int i = mask & source.get();
+ int b;
+ if (i < PREFIX_TABLE[n]) {
+ return i;
+ } else {
+ int m = 0;
+ do {
+ if(count++ > MAX_INTEGER_OCTETS) {
+ throw new HpackException(sm.getString("hpack.integerEncodedOverTooManyOctets",
+ Integer.valueOf(MAX_INTEGER_OCTETS)));
+ }
+ if (source.remaining() == 0) {
+ //we have run out of data
+ //reset
+ source.position(sp);
+ return -1;
+ }
+ b = source.get();
+ i = i + (b & 127) * (PREFIX_TABLE[m] + 1);
+ m += 7;
+ } while ((b & 128) == 128);
+ }
+ return i;
+ }
+
+ /**
+ * Encodes an integer in the HPACK prefix format.
+ * <p/>
+ * This method assumes that the buffer has already had the first 8-n bits filled.
+ * As such it will modify the last byte that is already present in the buffer, and
+ * potentially add more if required
+ *
+ * @param source The buffer that contains the integer
+ * @param value The integer to encode
+ * @param n The encoding prefix length
+ */
+ static void encodeInteger(ByteBuffer source, int value, int n) {
+ int twoNminus1 = PREFIX_TABLE[n];
+ int pos = source.position() - 1;
+ if (value < twoNminus1) {
+ source.put(pos, (byte) (source.get(pos) | value));
+ } else {
+ source.put(pos, (byte) (source.get(pos) | twoNminus1));
+ value = value - twoNminus1;
+ while (value >= 128) {
+ source.put((byte) (value % 128 + 128));
+ value = value / 128;
+ }
+ source.put((byte) value);
+ }
+ }
+
+
+ static byte toLower(byte b) {
+ if (b >= 'A' && b <= 'Z') {
+ return (byte) (b + LOWER_DIFF);
+ }
+ return b;
+ }
+
+ private Hpack() {}
+
+}
diff --git a/java/org/apache/coyote/http2/HpackDecoder.java b/java/org/apache/coyote/http2/HpackDecoder.java
new file mode 100644
index 0000000..d75d641
--- /dev/null
+++ b/java/org/apache/coyote/http2/HpackDecoder.java
@@ -0,0 +1,453 @@
+/*
+ * 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.nio.ByteBuffer;
+
+import org.apache.tomcat.util.res.StringManager;
+
+/**
+ * A decoder for HPACK.
+ */
+public class HpackDecoder {
+
+ protected static final StringManager sm = StringManager.getManager(HpackDecoder.class);
+
+ private static final int DEFAULT_RING_BUFFER_SIZE = 10;
+
+ /**
+ * The object that receives the headers that are emitted from this decoder
+ */
+ private HeaderEmitter headerEmitter;
+
+ /**
+ * The header table
+ */
+ private Hpack.HeaderField[] headerTable;
+
+ /**
+ * The current HEAD position of the header table. We use a ring buffer type
+ * construct as it would be silly to actually shuffle the items around in the
+ * array.
+ */
+ private int firstSlotPosition = 0;
+
+ /**
+ * The current table size by index (aka the number of index positions that are filled up)
+ */
+ private int filledTableSlots = 0;
+
+ /**
+ * the current calculates memory size, as per the HPACK algorithm
+ */
+ private int currentMemorySize = 0;
+
+ /**
+ * The maximum allowed memory size
+ */
+ private int maxMemorySize;
+
+ private int maxHeaderCount = Constants.DEFAULT_MAX_HEADER_COUNT;
+ private int maxHeaderSize = Constants.DEFAULT_MAX_HEADER_SIZE;
+
+ private volatile int headerCount = 0;
+ private volatile boolean countedCookie;
+ private volatile int headerSize = 0;
+
+ private final StringBuilder stringBuilder = new StringBuilder();
+
+ public HpackDecoder(int maxMemorySize) {
+ this.maxMemorySize = maxMemorySize;
+ headerTable = new Hpack.HeaderField[DEFAULT_RING_BUFFER_SIZE];
+ }
+
+ public HpackDecoder() {
+ this(Hpack.DEFAULT_TABLE_SIZE);
+ }
+
+ /**
+ * Decodes the provided frame data. If this method leaves data in the buffer
+ * then this buffer should be compacted so this data is preserved, unless
+ * there is no more data in which case this should be considered a protocol error.
+ *
+ * @param buffer The buffer
+ *
+ * @throws HpackException If the packed data is not valid
+ */
+ public void decode(ByteBuffer buffer) throws HpackException {
+ while (buffer.hasRemaining()) {
+ int originalPos = buffer.position();
+ byte b = buffer.get();
+ if ((b & 0b10000000) != 0) {
+ //if the first bit is set it is an indexed header field
+ buffer.position(buffer.position() - 1); //unget the byte
+ int index = Hpack.decodeInteger(buffer, 7); //prefix is 7
+ if (index == -1) {
+ buffer.position(originalPos);
+ return;
+ } else if(index == 0) {
+ throw new HpackException(
+ sm.getString("hpackdecoder.zeroNotValidHeaderTableIndex"));
+ }
+ handleIndex(index);
+ } else if ((b & 0b01000000) != 0) {
+ //Literal Header Field with Incremental Indexing
+ String headerName = readHeaderName(buffer, 6);
+ if (headerName == null) {
+ buffer.position(originalPos);
+ return;
+ }
+ String headerValue = readHpackString(buffer);
+ if (headerValue == null) {
+ buffer.position(originalPos);
+ return;
+ }
+ emitHeader(headerName, headerValue);
+ addEntryToHeaderTable(new Hpack.HeaderField(headerName, headerValue));
+ } else if ((b & 0b11110000) == 0) {
+ //Literal Header Field without Indexing
+ String headerName = readHeaderName(buffer, 4);
+ if (headerName == null) {
+ buffer.position(originalPos);
+ return;
+ }
+ String headerValue = readHpackString(buffer);
+ if (headerValue == null) {
+ buffer.position(originalPos);
+ return;
+ }
+ emitHeader(headerName, headerValue);
+ } else if ((b & 0b11110000) == 0b00010000) {
+ //Literal Header Field never indexed
+ String headerName = readHeaderName(buffer, 4);
+ if (headerName == null) {
+ buffer.position(originalPos);
+ return;
+ }
+ String headerValue = readHpackString(buffer);
+ if (headerValue == null) {
+ buffer.position(originalPos);
+ return;
+ }
+ emitHeader(headerName, headerValue);
+ } else if ((b & 0b11100000) == 0b00100000) {
+ //context update max table size change
+ if (!handleMaxMemorySizeChange(buffer, originalPos)) {
+ return;
+ }
+ } else {
+ throw new RuntimeException("Not yet implemented");
+ }
+ }
+ }
+
+ private boolean handleMaxMemorySizeChange(ByteBuffer buffer, int originalPos) throws HpackException {
+ buffer.position(buffer.position() - 1); //unget the byte
+ int size = Hpack.decodeInteger(buffer, 5);
+ if (size == -1) {
+ buffer.position(originalPos);
+ return false;
+ }
+ maxMemorySize = size;
+ if (currentMemorySize > maxMemorySize) {
+ int newTableSlots = filledTableSlots;
+ int tableLength = headerTable.length;
+ int newSize = currentMemorySize;
+ while (newSize > maxMemorySize) {
+ int clearIndex = firstSlotPosition;
+ firstSlotPosition++;
+ if (firstSlotPosition == tableLength) {
+ firstSlotPosition = 0;
+ }
+ Hpack.HeaderField oldData = headerTable[clearIndex];
+ headerTable[clearIndex] = null;
+ newSize -= oldData.size;
+ newTableSlots--;
+ }
+ this.filledTableSlots = newTableSlots;
+ currentMemorySize = newSize;
+ }
+ return true;
+ }
+
+ private String readHeaderName(ByteBuffer buffer, int prefixLength) throws HpackException {
+ buffer.position(buffer.position() - 1); //unget the byte
+ int index = Hpack.decodeInteger(buffer, prefixLength);
+ if (index == -1) {
+ return null;
+ } else if (index != 0) {
+ return handleIndexedHeaderName(index);
+ } else {
+ return readHpackString(buffer);
+ }
+ }
+
+ private String readHpackString(ByteBuffer buffer) throws HpackException {
+ if (!buffer.hasRemaining()) {
+ return null;
+ }
+ byte data = buffer.get(buffer.position());
+
+ int length = Hpack.decodeInteger(buffer, 7);
+ if (buffer.remaining() < length) {
+ return null;
+ }
+ boolean huffman = (data & 0b10000000) != 0;
+ if (huffman) {
+ return readHuffmanString(length, buffer);
+ }
+ for (int i = 0; i < length; ++i) {
+ stringBuilder.append((char) buffer.get());
+ }
+ String ret = stringBuilder.toString();
+ stringBuilder.setLength(0);
+ return ret;
+ }
+
+ private String readHuffmanString(int length, ByteBuffer buffer) throws HpackException {
+ HPackHuffman.decode(buffer, length, stringBuilder);
+ String ret = stringBuilder.toString();
+ stringBuilder.setLength(0);
+ return ret;
+ }
+
+ private String handleIndexedHeaderName(int index) throws HpackException {
+ if (index <= Hpack.STATIC_TABLE_LENGTH) {
+ return Hpack.STATIC_TABLE[index].name;
+ } else {
+ if (index >= Hpack.STATIC_TABLE_LENGTH + filledTableSlots) {
+ throw new HpackException();
+ }
+ int adjustedIndex = getRealIndex(index - Hpack.STATIC_TABLE_LENGTH);
+ Hpack.HeaderField res = headerTable[adjustedIndex];
+ if (res == null) {
+ throw new HpackException();
+ }
+ return res.name;
+ }
+ }
+
+ /**
+ * Handle an indexed header representation
+ *
+ * @param index The index
+ * @throws HpackException
+ */
+ private void handleIndex(int index) throws HpackException {
+ if (index <= Hpack.STATIC_TABLE_LENGTH) {
+ addStaticTableEntry(index);
+ } else {
+ int adjustedIndex = getRealIndex(index - Hpack.STATIC_TABLE_LENGTH);
+ Hpack.HeaderField headerField = headerTable[adjustedIndex];
+ emitHeader(headerField.name, headerField.value);
+ }
+ }
+
+ /**
+ * because we use a ring buffer type construct, and don't actually shuffle
+ * items in the array, we need to figure out the real index to use.
+ * <p/>
+ * package private for unit tests
+ *
+ * @param index The index from the hpack
+ * @return the real index into the array
+ */
+ int getRealIndex(int index) {
+ //the index is one based, but our table is zero based, hence -1
+ //also because of our ring buffer setup the indexes are reversed
+ //index = 1 is at position firstSlotPosition + filledSlots
+ return (firstSlotPosition + (filledTableSlots - index)) % headerTable.length;
+ }
+
+ private void addStaticTableEntry(int index) throws HpackException {
+ //adds an entry from the static table.
+ //this must be an entry with a value as far as I can determine
+ Hpack.HeaderField entry = Hpack.STATIC_TABLE[index];
+ if (entry.value == null) {
+ throw new HpackException();
+ }
+ emitHeader(entry.name, entry.value);
+ }
+
+ private void addEntryToHeaderTable(Hpack.HeaderField entry) {
+ if (entry.size > maxMemorySize) {
+ //it is to big to fit, so we just completely clear the table.
+ while (filledTableSlots > 0) {
+ headerTable[firstSlotPosition] = null;
+ firstSlotPosition++;
+ if (firstSlotPosition == headerTable.length) {
+ firstSlotPosition = 0;
+ }
+ filledTableSlots--;
+ }
+ currentMemorySize = 0;
+ return;
+ }
+ resizeIfRequired();
+ int newTableSlots = filledTableSlots + 1;
+ int tableLength = headerTable.length;
+ int index = (firstSlotPosition + filledTableSlots) % tableLength;
+ headerTable[index] = entry;
+ int newSize = currentMemorySize + entry.size;
+ while (newSize > maxMemorySize) {
+ int clearIndex = firstSlotPosition;
+ firstSlotPosition++;
+ if (firstSlotPosition == tableLength) {
+ firstSlotPosition = 0;
+ }
+ Hpack.HeaderField oldData = headerTable[clearIndex];
+ headerTable[clearIndex] = null;
+ newSize -= oldData.size;
+ newTableSlots--;
+ }
+ this.filledTableSlots = newTableSlots;
+ currentMemorySize = newSize;
+ }
+
+ private void resizeIfRequired() {
+ if(filledTableSlots == headerTable.length) {
+ Hpack.HeaderField[] newArray = new Hpack.HeaderField[headerTable.length + 10]; //we only grow slowly
+ for(int i = 0; i < headerTable.length; ++i) {
+ newArray[i] = headerTable[(firstSlotPosition + i) % headerTable.length];
+ }
+ firstSlotPosition = 0;
+ headerTable = newArray;
+ }
+ }
+
+
+ /**
+ * Interface implemented by the intended recipient of the headers.
+ */
+ interface HeaderEmitter {
+ /**
+ * Pass a single header to the recipient.
+ *
+ * @param name Header name
+ * @param value Header value
+ */
+ void emitHeader(String name, String value);
+
+ /**
+ * 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.
+ *
+ * @throws StreamException If the headers received to date are not valid
+ */
+ void validateHeaders() throws StreamException;
+ }
+
+
+ public HeaderEmitter getHeaderEmitter() {
+ return headerEmitter;
+ }
+
+
+ void setHeaderEmitter(HeaderEmitter headerEmitter) {
+ this.headerEmitter = headerEmitter;
+ // Reset limit tracking
+ headerCount = 0;
+ countedCookie = false;
+ headerSize = 0;
+ }
+
+
+ void setMaxHeaderCount(int maxHeaderCount) {
+ this.maxHeaderCount = maxHeaderCount;
+ }
+
+
+ void setMaxHeaderSize(int maxHeaderSize) {
+ this.maxHeaderSize = maxHeaderSize;
+ }
+
+
+ private void emitHeader(String name, String value) {
+ // Header names are forced to lower case
+ if ("cookie".equals(name)) {
+ // Only count the cookie header once since HTTP/2 splits it into
+ // multiple headers to aid compression
+ if (!countedCookie) {
+ headerCount ++;
+ countedCookie = true;
+ }
+ } else {
+ headerCount ++;
+ }
+ // Overhead will vary. The main concern is that lots of small headers
+ // trigger the limiting mechanism correctly. Therefore, use an overhead
+ // estimate of 3 which is the worst case for small headers.
+ int inc = 3 + name.length() + value.length();
+ headerSize += inc;
+ if (!isHeaderCountExceeded() && !isHeaderSizeExceeded(0)) {
+ headerEmitter.emitHeader(name, value);
+ }
+ }
+
+
+ boolean isHeaderCountExceeded() {
+ if (maxHeaderCount < 0) {
+ return false;
+ }
+ return headerCount > maxHeaderCount;
+ }
+
+
+ boolean isHeaderSizeExceeded(int unreadSize) {
+ if (maxHeaderSize < 0) {
+ return false;
+ }
+ return (headerSize + unreadSize) > maxHeaderSize;
+ }
+
+
+ boolean isHeaderSwallowSizeExceeded(int unreadSize) {
+ if (maxHeaderSize < 0) {
+ return false;
+ }
+ // Swallow the same again before closing the connection.
+ return (headerSize + unreadSize) > (2 * maxHeaderSize);
+ }
+
+
+ //package private fields for unit tests
+
+ int getFirstSlotPosition() {
+ return firstSlotPosition;
+ }
+
+ Hpack.HeaderField[] getHeaderTable() {
+ return headerTable;
+ }
+
+ int getFilledTableSlots() {
+ return filledTableSlots;
+ }
+
+ int getCurrentMemorySize() {
+ return currentMemorySize;
+ }
+
+ int getMaxMemorySize() {
+ return maxMemorySize;
+ }
+}
diff --git a/java/org/apache/coyote/http2/HpackEncoder.java b/java/org/apache/coyote/http2/HpackEncoder.java
new file mode 100644
index 0000000..5731faf
--- /dev/null
+++ b/java/org/apache/coyote/http2/HpackEncoder.java
@@ -0,0 +1,398 @@
+/*
+ * 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.nio.ByteBuffer;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Deque;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
+import org.apache.tomcat.util.http.MimeHeaders;
+import org.apache.tomcat.util.res.StringManager;
+
+/**
+ * Encoder for HPACK frames.
+ */
+public class HpackEncoder {
+
+ private static final Log log = LogFactory.getLog(HpackEncoder.class);
+ private static final StringManager sm = StringManager.getManager(HpackEncoder.class);
+
+ public static final HpackHeaderFunction DEFAULT_HEADER_FUNCTION = new HpackHeaderFunction() {
+ @Override
+ public boolean shouldUseIndexing(String headerName, String value) {
+ //content length and date change all the time
+ //no need to index them, or they will churn the table
+ return !headerName.equals("content-length") && !headerName.equals("date");
+ }
+
+ @Override
+ public boolean shouldUseHuffman(String header, String value) {
+ return value.length() > 5; //TODO: figure out a good value for this
+ }
+
+ @Override
+ public boolean shouldUseHuffman(String header) {
+ return header.length() > 5; //TODO: figure out a good value for this
+ }
+
+
+ };
+
+ private int headersIterator = -1;
+ private boolean firstPass = true;
+
+ private MimeHeaders currentHeaders;
+
+ private int entryPositionCounter;
+
+ private int newMaxHeaderSize = -1; //if the max header size has been changed
+ private int minNewMaxHeaderSize = -1; //records the smallest value of newMaxHeaderSize, as per section 4.1
+
+ private static final Map<String, TableEntry[]> ENCODING_STATIC_TABLE;
+
+ private final Deque<TableEntry> evictionQueue = new ArrayDeque<>();
+ private final Map<String, List<TableEntry>> dynamicTable = new HashMap<>(); //TODO: use a custom data structure to reduce allocations
+
+ static {
+ Map<String, TableEntry[]> map = new HashMap<>();
+ for (int i = 1; i < Hpack.STATIC_TABLE.length; ++i) {
+ Hpack.HeaderField m = Hpack.STATIC_TABLE[i];
+ TableEntry[] existing = map.get(m.name);
+ if (existing == null) {
+ map.put(m.name, new TableEntry[]{new TableEntry(m.name, m.value, i)});
+ } else {
+ TableEntry[] newEntry = new TableEntry[existing.length + 1];
+ System.arraycopy(existing, 0, newEntry, 0, existing.length);
+ newEntry[existing.length] = new TableEntry(m.name, m.value, i);
+ map.put(m.name, newEntry);
+ }
+ }
+ ENCODING_STATIC_TABLE = Collections.unmodifiableMap(map);
+ }
+
+ /**
+ * The maximum table size
+ */
+ private int maxTableSize;
+
+ /**
+ * The current table size
+ */
+ private int currentTableSize;
+
+ private final HpackHeaderFunction hpackHeaderFunction;
+
+ public HpackEncoder(int maxTableSize, HpackHeaderFunction headerFunction) {
+ this.maxTableSize = maxTableSize;
+ this.hpackHeaderFunction = headerFunction;
+ }
+
+ public HpackEncoder(int maxTableSize) {
+ this(maxTableSize, DEFAULT_HEADER_FUNCTION);
+ }
+
+ /**
+ * Encodes the headers into a buffer.
+ *
+ * @param headers The headers to encode
+ * @param target The buffer to which to write the encoded headers
+ *
+ * @return The state of the encoding process
+ */
+ public State encode(MimeHeaders headers, ByteBuffer target) {
+ int it = headersIterator;
+ if (headersIterator == -1) {
+ handleTableSizeChange(target);
+ //new headers map
+ it = 0;
+ currentHeaders = headers;
+ } else {
+ if (headers != currentHeaders) {
+ throw new IllegalStateException();
+ }
+ }
+ while (it < currentHeaders.size()) {
+ // FIXME: Review lowercase policy
+ String headerName = headers.getName(it).toString().toLowerCase(Locale.US);
+ boolean skip = false;
+ if (firstPass) {
+ if (headerName.charAt(0) != ':') {
+ skip = true;
+ }
+ } else {
+ if (headerName.charAt(0) == ':') {
+ skip = true;
+ }
+ }
+ if (!skip) {
+ String val = headers.getValue(it).toString();
+
+ if (log.isDebugEnabled()) {
+ log.debug(sm.getString("hpackEncoder.encodeHeader", headerName, val));
+ }
+ TableEntry tableEntry = findInTable(headerName, val);
+
+ // We use 11 to make sure we have enough room for the
+ // variable length integers
+ int required = 11 + headerName.length() + 1 + val.length();
+
+ if (target.remaining() < required) {
+ this.headersIterator = it;
+ return State.UNDERFLOW;
+ }
+ // Only index if it will fit
+ boolean canIndex = hpackHeaderFunction.shouldUseIndexing(headerName, val) &&
+ (headerName.length() + val.length() + 32) < maxTableSize;
+ if (tableEntry == null && canIndex) {
+ //add the entry to the dynamic table
+ target.put((byte) (1 << 6));
+ writeHuffmanEncodableName(target, headerName);
+ writeHuffmanEncodableValue(target, headerName, val);
+ addToDynamicTable(headerName, val);
+ } else if (tableEntry == null) {
+ //literal never indexed
+ target.put((byte) (1 << 4));
+ writeHuffmanEncodableName(target, headerName);
+ writeHuffmanEncodableValue(target, headerName, val);
+ } else {
+ //so we know something is already in the table
+ if (val.equals(tableEntry.value)) {
+ //the whole thing is in the table
+ target.put((byte) (1 << 7));
+ Hpack.encodeInteger(target, tableEntry.getPosition(), 7);
+ } else {
+ if (canIndex) {
+ //add the entry to the dynamic table
+ target.put((byte) (1 << 6));
+ Hpack.encodeInteger(target, tableEntry.getPosition(), 6);
+ writeHuffmanEncodableValue(target, headerName, val);
+ addToDynamicTable(headerName, val);
+
+ } else {
+ target.put((byte) (1 << 4));
+ Hpack.encodeInteger(target, tableEntry.getPosition(), 4);
+ writeHuffmanEncodableValue(target, headerName, val);
+ }
+ }
+ }
+
+ }
+ if (++it == currentHeaders.size() && firstPass) {
+ firstPass = false;
+ it = 0;
+ }
+ }
+ headersIterator = -1;
+ firstPass = true;
+ return State.COMPLETE;
+ }
+
+ private void writeHuffmanEncodableName(ByteBuffer target, String headerName) {
+ if (hpackHeaderFunction.shouldUseHuffman(headerName)) {
+ if(HPackHuffman.encode(target, headerName, true)) {
+ return;
+ }
+ }
+ target.put((byte) 0); //to use encodeInteger we need to place the first byte in the buffer.
+ Hpack.encodeInteger(target, headerName.length(), 7);
+ for (int j = 0; j < headerName.length(); ++j) {
+ target.put(Hpack.toLower((byte) headerName.charAt(j)));
+ }
+
+ }
+
+ private void writeHuffmanEncodableValue(ByteBuffer target, String headerName, String val) {
+ if (hpackHeaderFunction.shouldUseHuffman(headerName, val)) {
+ if (!HPackHuffman.encode(target, val, false)) {
+ writeValueString(target, val);
+ }
+ } else {
+ writeValueString(target, val);
+ }
+ }
+
+ private void writeValueString(ByteBuffer target, String val) {
+ target.put((byte) 0); //to use encodeInteger we need to place the first byte in the buffer.
+ Hpack.encodeInteger(target, val.length(), 7);
+ for (int j = 0; j < val.length(); ++j) {
+ target.put((byte) val.charAt(j));
+ }
+ }
+
+ private void addToDynamicTable(String headerName, String val) {
+ int pos = entryPositionCounter++;
+ DynamicTableEntry d = new DynamicTableEntry(headerName, val, -pos);
+ List<TableEntry> existing = dynamicTable.get(headerName);
+ if (existing == null) {
+ dynamicTable.put(headerName, existing = new ArrayList<>(1));
+ }
+ existing.add(d);
+ evictionQueue.add(d);
+ currentTableSize += d.size;
+ runEvictionIfRequired();
+ if (entryPositionCounter == Integer.MAX_VALUE) {
+ //prevent rollover
+ preventPositionRollover();
+ }
+
+ }
+
+
+ private void preventPositionRollover() {
+ //if the position counter is about to roll over we iterate all the table entries
+ //and set their position to their actual position
+ for (Map.Entry<String, List<TableEntry>> entry : dynamicTable.entrySet()) {
+ for (TableEntry t : entry.getValue()) {
+ t.position = t.getPosition();
+ }
+ }
+ entryPositionCounter = 0;
+ }
+
+ private void runEvictionIfRequired() {
+
+ while (currentTableSize > maxTableSize) {
+ TableEntry next = evictionQueue.poll();
+ if (next == null) {
+ return;
+ }
+ currentTableSize -= next.size;
+ List<TableEntry> list = dynamicTable.get(next.name);
+ list.remove(next);
+ if (list.isEmpty()) {
+ dynamicTable.remove(next.name);
+ }
+ }
+ }
+
+ private TableEntry findInTable(String headerName, String value) {
+ TableEntry[] staticTable = ENCODING_STATIC_TABLE.get(headerName);
+ if (staticTable != null) {
+ for (TableEntry st : staticTable) {
+ if (st.value != null && st.value.equals(value)) { //todo: some form of lookup?
+ return st;
+ }
+ }
+ }
+ List<TableEntry> dynamic = dynamicTable.get(headerName);
+ if (dynamic != null) {
+ for (TableEntry st : dynamic) {
+ if (st.value.equals(value)) { //todo: some form of lookup?
+ return st;
+ }
+ }
+ }
+ if (staticTable != null) {
+ return staticTable[0];
+ }
+ return null;
+ }
+
+ public void setMaxTableSize(int newSize) {
+ this.newMaxHeaderSize = newSize;
+ if (minNewMaxHeaderSize == -1) {
+ minNewMaxHeaderSize = newSize;
+ } else {
+ minNewMaxHeaderSize = Math.min(newSize, minNewMaxHeaderSize);
+ }
+ }
+
+ private void handleTableSizeChange(ByteBuffer target) {
+ if (newMaxHeaderSize == -1) {
+ return;
+ }
+ if (minNewMaxHeaderSize != newMaxHeaderSize) {
+ target.put((byte) (1 << 5));
+ Hpack.encodeInteger(target, minNewMaxHeaderSize, 5);
+ }
+ target.put((byte) (1 << 5));
+ Hpack.encodeInteger(target, newMaxHeaderSize, 5);
+ maxTableSize = newMaxHeaderSize;
+ runEvictionIfRequired();
+ newMaxHeaderSize = -1;
+ minNewMaxHeaderSize = -1;
+ }
+
+ public enum State {
+ COMPLETE,
+ UNDERFLOW,
+
+ }
+
+ static class TableEntry {
+ final String name;
+ final String value;
+ final int size;
+ int position;
+
+ TableEntry(String name, String value, int position) {
+ this.name = name;
+ this.value = value;
+ this.position = position;
+ if (value != null) {
+ this.size = 32 + name.length() + value.length();
+ } else {
+ this.size = -1;
+ }
+ }
+
+ public int getPosition() {
+ return position;
+ }
+ }
+
+ class DynamicTableEntry extends TableEntry {
+
+ DynamicTableEntry(String name, String value, int position) {
+ super(name, value, position);
+ }
+
+ @Override
+ public int getPosition() {
+ return super.getPosition() + entryPositionCounter + Hpack.STATIC_TABLE_LENGTH;
+ }
+ }
+
+ public interface HpackHeaderFunction {
+ boolean shouldUseIndexing(String header, String value);
+
+ /**
+ * Returns true if huffman encoding should be used on the header value
+ *
+ * @param header The header name
+ * @param value The header value to be encoded
+ * @return <code>true</code> if the value should be encoded
+ */
+ boolean shouldUseHuffman(String header, String value);
+
+ /**
+ * Returns true if huffman encoding should be used on the header name
+ *
+ * @param header The header name to be encoded
+ * @return <code>true</code> if the value should be encoded
+ */
+ boolean shouldUseHuffman(String header);
+ }
+}
diff --git a/java/org/apache/coyote/http2/HpackException.java b/java/org/apache/coyote/http2/HpackException.java
new file mode 100644
index 0000000..1dc3f7c
--- /dev/null
+++ b/java/org/apache/coyote/http2/HpackException.java
@@ -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.coyote.http2;
+
+/**
+ * Exception that is thrown when the HPACK compress context is broken. In this
+ * case the connection must be closed.
+ */
+public class HpackException extends Exception {
+
+ private static final long serialVersionUID = 1L;
+
+ public HpackException(String message, Throwable cause) {
+ super(message, cause);
+ }
+ public HpackException(String message) {
+ super(message);
+ }
+ public HpackException() {
+ super();
+ }
+}
diff --git a/java/org/apache/coyote/http2/Http2Error.java b/java/org/apache/coyote/http2/Http2Error.java
new file mode 100644
index 0000000..9db65ac
--- /dev/null
+++ b/java/org/apache/coyote/http2/Http2Error.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.coyote.http2;
+
+public enum Http2Error {
+
+ NO_ERROR (0x00),
+ PROTOCOL_ERROR (0x01),
+ INTERNAL_ERROR (0x02),
+ FLOW_CONTROL_ERROR (0x03),
+ SETTINGS_TIMEOUT (0x04),
+ STREAM_CLOSED (0x05),
+ FRAME_SIZE_ERROR (0x06),
+ REFUSED_STREAM (0x07),
+ CANCEL (0x08),
+ COMPRESSION_ERROR (0x09),
+ CONNECT_ERROR (0x0a),
+ ENHANCE_YOUR_CALM (0x0b),
+ INADEQUATE_SECURITY (0x0c),
+ HTTP_1_1_REQUIRED (0x0d);
+
+ private final long code;
+
+ private Http2Error(long code) {
+ this.code = code;
+ }
+
+
+ public long getCode() {
+ return code;
+ }
+
+
+ public byte[] getCodeBytes() {
+ byte[] codeByte = new byte[4];
+ ByteUtil.setFourBytes(codeByte, 0, code);
+ return codeByte;
+ }
+}
diff --git a/java/org/apache/coyote/http2/Http2Exception.java b/java/org/apache/coyote/http2/Http2Exception.java
new file mode 100644
index 0000000..65f7502
--- /dev/null
+++ b/java/org/apache/coyote/http2/Http2Exception.java
@@ -0,0 +1,35 @@
+/*
+ * 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;
+
+public abstract class Http2Exception extends Exception {
+
+ private static final long serialVersionUID = 1L;
+
+ private final Http2Error error;
+
+
+ public Http2Exception(String msg, Http2Error error) {
+ super(msg);
+ this.error = error;
+ }
+
+
+ public Http2Error getError() {
+ return error;
+ }
+}
diff --git a/java/org/apache/coyote/http2/Http2Parser.java b/java/org/apache/coyote/http2/Http2Parser.java
new file mode 100644
index 0000000..1074915
--- /dev/null
+++ b/java/org/apache/coyote/http2/Http2Parser.java
@@ -0,0 +1,659 @@
+/*
+ * 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.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+
+import org.apache.coyote.ProtocolException;
+import org.apache.coyote.http2.HpackDecoder.HeaderEmitter;
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
+import org.apache.tomcat.util.buf.ByteBufferUtils;
+import org.apache.tomcat.util.res.StringManager;
+
+class Http2Parser {
+
+ private static final Log log = LogFactory.getLog(Http2Parser.class);
+ private static final StringManager sm = StringManager.getManager(Http2Parser.class);
+
+ static final byte[] CLIENT_PREFACE_START =
+ "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n".getBytes(StandardCharsets.ISO_8859_1);
+
+ private final String connectionId;
+ private final Input input;
+ private final Output output;
+ private final byte[] frameHeaderBuffer = new byte[9];
+
+ private volatile HpackDecoder hpackDecoder;
+ private volatile ByteBuffer headerReadBuffer =
+ ByteBuffer.allocate(Constants.DEFAULT_HEADER_READ_BUFFER_SIZE);
+ private volatile int headersCurrentStream = -1;
+ private volatile boolean headersEndStream = false;
+ private volatile boolean streamReset = false;
+
+ Http2Parser(String connectionId, Input input, Output output) {
+ this.connectionId = connectionId;
+ this.input = input;
+ this.output = output;
+ }
+
+
+ /**
+ * Read and process a single frame. Once the start of a frame is read, the
+ * remainder will be read using blocking IO.
+ *
+ * @param block Should this method block until a frame is available if no
+ * frame is available immediately?
+ *
+ * @return <code>true</code> if a frame was read otherwise
+ * <code>false</code>
+ *
+ * @throws IOException If an IO error occurs while trying to read a frame
+ */
+ boolean readFrame(boolean block) throws Http2Exception, IOException {
+ return readFrame(block, null);
+ }
+
+
+ private boolean readFrame(boolean block, FrameType expected)
+ throws IOException, Http2Exception {
+
+ if (!input.fill(block, frameHeaderBuffer)) {
+ return false;
+ }
+
+ int payloadSize = ByteUtil.getThreeBytes(frameHeaderBuffer, 0);
+ FrameType frameType = FrameType.valueOf(ByteUtil.getOneByte(frameHeaderBuffer, 3));
+ int flags = ByteUtil.getOneByte(frameHeaderBuffer, 4);
+ int streamId = ByteUtil.get31Bits(frameHeaderBuffer, 5);
+
+ try {
+ validateFrame(expected, frameType, streamId, flags, payloadSize);
+ } catch (StreamException se) {
+ swallow(streamId, payloadSize, false);
+ throw se;
+ }
+
+ switch (frameType) {
+ case DATA:
+ readDataFrame(streamId, flags, payloadSize);
+ break;
+ case HEADERS:
+ readHeadersFrame(streamId, flags, payloadSize);
+ break;
+ case PRIORITY:
+ readPriorityFrame(streamId);
+ break;
+ case RST:
+ readRstFrame(streamId);
+ break;
+ case SETTINGS:
+ readSettingsFrame(flags, payloadSize);
+ break;
+ case PUSH_PROMISE:
+ readPushPromiseFrame(streamId);
+ break;
+ case PING:
+ readPingFrame(flags);
+ break;
+ case GOAWAY:
+ readGoawayFrame(payloadSize);
+ break;
+ case WINDOW_UPDATE:
+ readWindowUpdateFrame(streamId);
+ break;
+ case CONTINUATION:
+ readContinuationFrame(streamId, flags, payloadSize);
+ break;
+ case UNKNOWN:
+ readUnknownFrame(streamId, frameType, flags, payloadSize);
+ }
+
+ return true;
+ }
+
+
+ private void readDataFrame(int streamId, int flags, int payloadSize)
+ throws Http2Exception, IOException {
+ // Process the Stream
+ int padLength = 0;
+
+ boolean endOfStream = Flags.isEndOfStream(flags);
+
+ int dataLength;
+ if (Flags.hasPadding(flags)) {
+ byte[] b = new byte[1];
+ input.fill(true, b);
+ padLength = b[0] & 0xFF;
+
+ if (padLength >= payloadSize) {
+ throw new ConnectionException(
+ sm.getString("http2Parser.processFrame.tooMuchPadding", connectionId,
+ Integer.toString(streamId), Integer.toString(padLength),
+ Integer.toString(payloadSize)), Http2Error.PROTOCOL_ERROR);
+ }
+ // +1 is for the padding length byte we just read above
+ dataLength = payloadSize - (padLength + 1);
+ } else {
+ dataLength = payloadSize;
+ }
+
+ if (log.isDebugEnabled()) {
+ String padding;
+ if (Flags.hasPadding(flags)) {
+ padding = Integer.toString(padLength);
+ } else {
+ padding = "none";
+ }
+ log.debug(sm.getString("http2Parser.processFrameData.lengths", connectionId,
+ Integer.toString(streamId), Integer.toString(dataLength), padding));
+ }
+
+ ByteBuffer dest = output.startRequestBodyFrame(streamId, payloadSize);
+ if (dest == null) {
+ swallow(streamId, dataLength, false);
+ // Process padding before sending any notifications in case padding
+ // is invalid.
+ if (padLength > 0) {
+ swallow(streamId, padLength, true);
+ }
+ if (endOfStream) {
+ output.receivedEndOfStream(streamId);
+ }
+ } else {
+ synchronized (dest) {
+ input.fill(true, dest, dataLength);
+ // Process padding before sending any notifications in case
+ // padding is invalid.
+ if (padLength > 0) {
+ swallow(streamId, padLength, true);
+ }
+ if (endOfStream) {
+ output.receivedEndOfStream(streamId);
+ }
+ output.endRequestBodyFrame(streamId);
+ }
+ }
+ if (padLength > 0) {
+ output.swallowedPadding(streamId, padLength);
+ }
+ }
+
+
+ private void readHeadersFrame(int streamId, int flags, int payloadSize)
+ throws Http2Exception, IOException {
+
+ headersEndStream = Flags.isEndOfStream(flags);
+
+ if (hpackDecoder == null) {
+ hpackDecoder = output.getHpackDecoder();
+ }
+ try {
+ hpackDecoder.setHeaderEmitter(output.headersStart(streamId, headersEndStream));
+ } catch (StreamException se) {
+ swallow(streamId, payloadSize, false);
+ throw se;
+ }
+
+ int padLength = 0;
+ boolean padding = Flags.hasPadding(flags);
+ boolean priority = Flags.hasPriority(flags);
+ int optionalLen = 0;
+ if (padding) {
+ optionalLen = 1;
+ }
+ if (priority) {
+ optionalLen += 5;
+ }
+ if (optionalLen > 0) {
+ byte[] optional = new byte[optionalLen];
+ input.fill(true, optional);
+ int optionalPos = 0;
+ if (padding) {
+ padLength = ByteUtil.getOneByte(optional, optionalPos++);
+ if (padLength >= payloadSize) {
+ throw new ConnectionException(
+ sm.getString("http2Parser.processFrame.tooMuchPadding", connectionId,
+ Integer.toString(streamId), Integer.toString(padLength),
+ Integer.toString(payloadSize)), Http2Error.PROTOCOL_ERROR);
+ }
+ }
+ if (priority) {
+ boolean exclusive = ByteUtil.isBit7Set(optional[optionalPos]);
+ int parentStreamId = ByteUtil.get31Bits(optional, optionalPos);
+ int weight = ByteUtil.getOneByte(optional, optionalPos + 4) + 1;
+ output.reprioritise(streamId, parentStreamId, exclusive, weight);
+ }
+
+ payloadSize -= optionalLen;
+ payloadSize -= padLength;
+ }
+
+ readHeaderPayload(streamId, payloadSize);
+
+ swallow(streamId, padLength, true);
+
+ if (Flags.isEndOfHeaders(flags)) {
+ onHeadersComplete(streamId);
+ } else {
+ headersCurrentStream = streamId;
+ }
+ }
+
+
+ private void readPriorityFrame(int streamId) throws Http2Exception, IOException {
+ byte[] payload = new byte[5];
+ input.fill(true, payload);
+
+ boolean exclusive = ByteUtil.isBit7Set(payload[0]);
+ int parentStreamId = ByteUtil.get31Bits(payload, 0);
+ int weight = ByteUtil.getOneByte(payload, 4) + 1;
+
+ if (streamId == parentStreamId) {
+ throw new StreamException(sm.getString("http2Parser.processFramePriority.invalidParent",
+ connectionId, Integer.valueOf(streamId)), Http2Error.PROTOCOL_ERROR, streamId);
+ }
+
+ output.reprioritise(streamId, parentStreamId, exclusive, weight);
+ }
+
+
+ private void readRstFrame(int streamId) throws Http2Exception, IOException {
+ byte[] payload = new byte[4];
+ input.fill(true, payload);
+
+ long errorCode = ByteUtil.getFourBytes(payload, 0);
+ output.reset(streamId, errorCode);
+ headersCurrentStream = -1;
+ headersEndStream = false;
+ }
+
+
+ private void readSettingsFrame(int flags, int payloadSize) throws Http2Exception, IOException {
+ boolean ack = Flags.isAck(flags);
+ if (payloadSize > 0 && ack) {
+ throw new ConnectionException(sm.getString(
+ "http2Parser.processFrameSettings.ackWithNonZeroPayload"),
+ Http2Error.FRAME_SIZE_ERROR);
+ }
+
+ if (payloadSize != 0) {
+ // Process the settings
+ byte[] setting = new byte[6];
+ for (int i = 0; i < payloadSize / 6; i++) {
+ input.fill(true, setting);
+ int id = ByteUtil.getTwoBytes(setting, 0);
+ long value = ByteUtil.getFourBytes(setting, 2);
+ output.setting(Setting.valueOf(id), value);
+ }
+ }
+ output.settingsEnd(ack);
+ }
+
+
+ private void readPushPromiseFrame(int streamId) throws Http2Exception {
+ throw new ConnectionException(sm.getString("http2Parser.processFramePushPromise",
+ connectionId, Integer.valueOf(streamId)), Http2Error.PROTOCOL_ERROR);
+ }
+
+
+ private void readPingFrame(int flags) throws IOException {
+ // Read the payload
+ byte[] payload = new byte[8];
+ input.fill(true, payload);
+ output.pingReceive(payload, Flags.isAck(flags));
+ }
+
+
+ private void readGoawayFrame(int payloadSize) throws IOException {
+ byte[] payload = new byte[payloadSize];
+ input.fill(true, payload);
+
+ int lastStreamId = ByteUtil.get31Bits(payload, 0);
+ long errorCode = ByteUtil.getFourBytes(payload, 4);
+ String debugData = null;
+ if (payloadSize > 8) {
+ debugData = new String(payload, 8, payloadSize - 8, StandardCharsets.UTF_8);
+ }
+ output.goaway(lastStreamId, errorCode, debugData);
+ }
+
+
+ private void readWindowUpdateFrame(int streamId) throws Http2Exception, IOException {
+ byte[] payload = new byte[4];
+ input.fill(true, payload);
+ int windowSizeIncrement = ByteUtil.get31Bits(payload, 0);
+
+ if (log.isDebugEnabled()) {
+ log.debug(sm.getString("http2Parser.processFrameWindowUpdate.debug", connectionId,
+ Integer.toString(streamId), Integer.toString(windowSizeIncrement)));
+ }
+
+ // Validate the data
+ if (windowSizeIncrement == 0) {
+ if (streamId == 0) {
+ throw new ConnectionException(
+ sm.getString("http2Parser.processFrameWindowUpdate.invalidIncrement"),
+ Http2Error.PROTOCOL_ERROR);
+ } else {
+ throw new StreamException(
+ sm.getString("http2Parser.processFrameWindowUpdate.invalidIncrement"),
+ Http2Error.PROTOCOL_ERROR, streamId);
+ }
+ }
+
+ output.incrementWindowSize(streamId, windowSizeIncrement);
+ }
+
+
+ private void readContinuationFrame(int streamId, int flags, int payloadSize)
+ throws Http2Exception, IOException {
+ if (headersCurrentStream == -1) {
+ // No headers to continue
+ throw new ConnectionException(sm.getString(
+ "http2Parser.processFrameContinuation.notExpected", connectionId,
+ Integer.toString(streamId)), Http2Error.PROTOCOL_ERROR);
+ }
+
+ readHeaderPayload(streamId, payloadSize);
+
+ if (Flags.isEndOfHeaders(flags)) {
+ onHeadersComplete(streamId);
+ headersCurrentStream = -1;
+ }
+ }
+
+
+ private void readHeaderPayload(int streamId, int payloadSize)
+ throws Http2Exception, IOException {
+
+ if (log.isDebugEnabled()) {
+ log.debug(sm.getString("http2Parser.processFrameHeaders.payload", connectionId,
+ Integer.valueOf(streamId), Integer.valueOf(payloadSize)));
+ }
+
+ int remaining = payloadSize;
+
+ while (remaining > 0) {
+ if (headerReadBuffer.remaining() == 0) {
+ // Buffer needs expansion
+ int newSize;
+ if (headerReadBuffer.capacity() < payloadSize) {
+ // First step, expand to the current payload. That should
+ // cover most cases.
+ newSize = payloadSize;
+ } else {
+ // Header must be spread over multiple frames. Keep doubling
+ // buffer size until the header can be read.
+ newSize = headerReadBuffer.capacity() * 2;
+ }
+ headerReadBuffer = ByteBufferUtils.expand(headerReadBuffer, newSize);
+ }
+ int toRead = Math.min(headerReadBuffer.remaining(), remaining);
+ // headerReadBuffer in write mode
+ input.fill(true, headerReadBuffer, toRead);
+ // switch to read mode
+ headerReadBuffer.flip();
+ try {
+ hpackDecoder.decode(headerReadBuffer);
+ } catch (HpackException hpe) {
+ throw new ConnectionException(
+ sm.getString("http2Parser.processFrameHeaders.decodingFailed"),
+ Http2Error.COMPRESSION_ERROR);
+ }
+
+ // switches to write mode
+ headerReadBuffer.compact();
+ remaining -= toRead;
+
+ if (hpackDecoder.isHeaderCountExceeded() && !streamReset) {
+ streamReset = true;
+ throw new StreamException(sm.getString("http2Parser.headerLimitCount", connectionId,
+ Integer.valueOf(streamId)), Http2Error.ENHANCE_YOUR_CALM, streamId);
+ }
+
+ if (hpackDecoder.isHeaderSizeExceeded(headerReadBuffer.position()) && !streamReset) {
+ streamReset = true;
+ throw new StreamException(sm.getString("http2Parser.headerLimitSize", connectionId,
+ Integer.valueOf(streamId)), Http2Error.ENHANCE_YOUR_CALM, streamId);
+ }
+
+ if (hpackDecoder.isHeaderSwallowSizeExceeded(headerReadBuffer.position())) {
+ throw new ConnectionException(sm.getString("http2Parser.headerLimitSize",
+ connectionId, Integer.valueOf(streamId)), Http2Error.ENHANCE_YOUR_CALM);
+ }
+ }
+
+ hpackDecoder.getHeaderEmitter().validateHeaders();
+ }
+
+
+ private void onHeadersComplete(int streamId) throws Http2Exception {
+ // Any left over data is a compression error
+ if (headerReadBuffer.position() > 0) {
+ throw new ConnectionException(
+ sm.getString("http2Parser.processFrameHeaders.decodingDataLeft"),
+ Http2Error.COMPRESSION_ERROR);
+ }
+
+ output.headersEnd(streamId);
+
+ if (headersEndStream) {
+ output.receivedEndOfStream(streamId);
+ headersEndStream = false;
+ }
+
+ // Reset size for new request if the buffer was previously expanded
+ if (headerReadBuffer.capacity() > Constants.DEFAULT_HEADER_READ_BUFFER_SIZE) {
+ headerReadBuffer = ByteBuffer.allocate(Constants.DEFAULT_HEADER_READ_BUFFER_SIZE);
+ }
+
+ // Clear the 'stream has been reset' flag, if set
+ if (streamReset) {
+ streamReset = false;
+ }
+ }
+
+
+ private void readUnknownFrame(int streamId, FrameType frameType, int flags, int payloadSize)
+ throws IOException {
+ try {
+ swallow(streamId, payloadSize, false);
+ } catch (ConnectionException e) {
+ // Will never happen because swallow() is called with mustBeZero set
+ // to false
+ }
+ output.swallowed(streamId, frameType, flags, payloadSize);
+ }
+
+
+ private void swallow(int streamId, int len, boolean mustBeZero)
+ throws IOException, ConnectionException {
+ if (log.isDebugEnabled()) {
+ log.debug(sm.getString("http2Parser.swallow.debug", connectionId,
+ Integer.toString(streamId), Integer.toString(len)));
+ }
+ if (len == 0) {
+ return;
+ }
+ int read = 0;
+ byte[] buffer = new byte[1024];
+ while (read < len) {
+ int thisTime = Math.min(buffer.length, len - read);
+ input.fill(true, buffer, 0, thisTime);
+ if (mustBeZero) {
+ // Validate the padding is zero since receiving non-zero padding
+ // is a strong indication of either a faulty client or a server
+ // side bug.
+ for (int i = 0; i < thisTime; i++) {
+ if (buffer[i] != 0) {
+ throw new ConnectionException(sm.getString("http2Parser.nonZeroPadding",
+ connectionId, Integer.toString(streamId)), Http2Error.PROTOCOL_ERROR);
+ }
+ }
+ }
+ read += thisTime;
+ }
+ }
+
+
+ /*
+ * Implementation note:
+ * Validation applicable to all incoming frames should be implemented here.
+ * Frame type specific validation should be performed in the appropriate
+ * readXxxFrame() method.
+ * For validation applicable to some but not all frame types, use your
+ * judgement.
+ */
+ private void validateFrame(FrameType expected, FrameType frameType, int streamId, int flags,
+ int payloadSize) throws Http2Exception {
+
+ if (log.isDebugEnabled()) {
+ log.debug(sm.getString("http2Parser.processFrame", connectionId,
+ Integer.toString(streamId), frameType, Integer.toString(flags),
+ Integer.toString(payloadSize)));
+ }
+
+ if (expected != null && frameType != expected) {
+ throw new StreamException(sm.getString("http2Parser.processFrame.unexpectedType",
+ expected, frameType), Http2Error.PROTOCOL_ERROR, streamId);
+ }
+
+ int maxFrameSize = input.getMaxFrameSize();
+ if (payloadSize > maxFrameSize) {
+ throw new ConnectionException(sm.getString("http2Parser.payloadTooBig",
+ Integer.toString(payloadSize), Integer.toString(maxFrameSize)),
+ Http2Error.FRAME_SIZE_ERROR);
+ }
+
+ if (headersCurrentStream != -1) {
+ if (headersCurrentStream != streamId) {
+ throw new ConnectionException(sm.getString("http2Parser.headers.wrongStream",
+ connectionId, Integer.toString(headersCurrentStream),
+ Integer.toString(streamId)), Http2Error.COMPRESSION_ERROR);
+ }
+ if (frameType == FrameType.RST) {
+ // NO-OP: RST is OK here
+ } else if (frameType != FrameType.CONTINUATION) {
+ throw new ConnectionException(sm.getString("http2Parser.headers.wrongFrameType",
+ connectionId, Integer.toString(headersCurrentStream),
+ frameType), Http2Error.COMPRESSION_ERROR);
+ }
+ }
+
+ frameType.check(streamId, payloadSize);
+ }
+
+
+ /**
+ * Read and validate the connection preface from input using blocking IO.
+ */
+ void readConnectionPreface() throws Http2Exception {
+ byte[] data = new byte[CLIENT_PREFACE_START.length];
+ try {
+ input.fill(true, data);
+
+ for (int i = 0; i < CLIENT_PREFACE_START.length; i++) {
+ if (CLIENT_PREFACE_START[i] != data[i]) {
+ throw new ProtocolException(sm.getString("http2Parser.preface.invalid"));
+ }
+ }
+
+ // Must always be followed by a settings frame
+ readFrame(true, FrameType.SETTINGS);
+ } catch (IOException ioe) {
+ throw new ProtocolException(sm.getString("http2Parser.preface.io"), ioe);
+ }
+ }
+
+
+ /**
+ * Interface that must be implemented by the source of data for the parser.
+ */
+ static interface Input {
+
+ /**
+ * Fill the given array with data unless non-blocking is requested and
+ * no data is available. If any data is available then the buffer will
+ * be filled using blocking I/O.
+ *
+ * @param block Should the first read into the provided buffer be a
+ * blocking read or not.
+ * @param data Buffer to fill
+ * @param offset Position in buffer to start writing
+ * @param length Number of bytes to read
+ *
+ * @return <code>true</code> if the buffer was filled otherwise
+ * <code>false</code>
+ *
+ * @throws IOException If an I/O occurred while obtaining data with
+ * which to fill the buffer
+ */
+ boolean fill(boolean block, byte[] data, int offset, int length) throws IOException;
+
+ boolean fill(boolean block, byte[] data) throws IOException;
+
+ boolean fill(boolean block, ByteBuffer data, int len) throws IOException;
+
+ int getMaxFrameSize();
+ }
+
+
+ /**
+ * Interface that must be implemented to receive notifications from the
+ * parser as it processes incoming frames.
+ */
+ static interface Output {
+
+ HpackDecoder getHpackDecoder();
+
+ // Data frames
+ ByteBuffer startRequestBodyFrame(int streamId, int payloadSize) throws Http2Exception;
+ void endRequestBodyFrame(int streamId) throws Http2Exception;
+ void receivedEndOfStream(int streamId) throws ConnectionException;
+ void swallowedPadding(int streamId, int paddingLength) throws ConnectionException, IOException;
+
+ // Header frames
+ HeaderEmitter headersStart(int streamId, boolean headersEndStream) throws Http2Exception;
+ void headersEnd(int streamId) throws ConnectionException;
+
+ // Priority frames (also headers)
+ void reprioritise(int streamId, int parentStreamId, boolean exclusive, int weight)
+ throws Http2Exception;
+
+ // Reset frames
+ void reset(int streamId, long errorCode) throws Http2Exception;
+
+ // Settings frames
+ void setting(Setting setting, long value) throws ConnectionException;
+ void settingsEnd(boolean ack) throws IOException;
+
+ // Ping frames
+ void pingReceive(byte[] payload, boolean ack) throws IOException;
+
+ // Goaway
+ void goaway(int lastStreamId, long errorCode, String debugData);
+
+ // Window size
+ void incrementWindowSize(int streamId, int increment) throws Http2Exception;
+
+ // Testing
+ void swallowed(int streamId, FrameType frameType, int flags, int size) throws IOException;
+ }
+}
diff --git a/java/org/apache/coyote/http2/Http2Protocol.java b/java/org/apache/coyote/http2/Http2Protocol.java
new file mode 100644
index 0000000..988b2d1
--- /dev/null
+++ b/java/org/apache/coyote/http2/Http2Protocol.java
@@ -0,0 +1,279 @@
+/*
+ * 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.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Locale;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.apache.coyote.Adapter;
+import org.apache.coyote.Processor;
+import org.apache.coyote.Request;
+import org.apache.coyote.UpgradeProtocol;
+import org.apache.coyote.UpgradeToken;
+import org.apache.coyote.http11.upgrade.InternalHttpUpgradeHandler;
+import org.apache.coyote.http11.upgrade.UpgradeProcessorInternal;
+import org.apache.tomcat.util.net.SocketWrapperBase;
+
+public class Http2Protocol implements UpgradeProtocol {
+
+ static final long DEFAULT_READ_TIMEOUT = 10000;
+ static final long DEFAULT_KEEP_ALIVE_TIMEOUT = -1;
+ static final long DEFAULT_WRITE_TIMEOUT = 10000;
+ // The HTTP/2 specification recommends a minimum default of 100
+ static final long DEFAULT_MAX_CONCURRENT_STREAMS = 200;
+ // Maximum amount of streams which can be concurrently executed over
+ // a single connection
+ static final int DEFAULT_MAX_CONCURRENT_STREAM_EXECUTION = 200;
+ // This default is defined by the HTTP/2 specification
+ static final int DEFAULT_INITIAL_WINDOW_SIZE = (1 << 16) - 1;
+
+ private static final String HTTP_UPGRADE_NAME = "h2c";
+ private static final String ALPN_NAME = "h2";
+ private static final byte[] ALPN_IDENTIFIER = ALPN_NAME.getBytes(StandardCharsets.UTF_8);
+
+ // All timeouts in milliseconds
+ private long readTimeout = DEFAULT_READ_TIMEOUT;
+ private long keepAliveTimeout = DEFAULT_KEEP_ALIVE_TIMEOUT;
+ private long writeTimeout = DEFAULT_WRITE_TIMEOUT;
+ private long maxConcurrentStreams = DEFAULT_MAX_CONCURRENT_STREAMS;
+ private int maxConcurrentStreamExecution = DEFAULT_MAX_CONCURRENT_STREAM_EXECUTION;
+ // If a lower initial value is required, set it here but DO NOT change the
+ // default defined above.
+ private int initialWindowSize = DEFAULT_INITIAL_WINDOW_SIZE;
+ // Limits
+ private Set<String> allowedTrailerHeaders =
+ Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>());
+ private int maxHeaderCount = Constants.DEFAULT_MAX_HEADER_COUNT;
+ private int maxHeaderSize = Constants.DEFAULT_MAX_HEADER_SIZE;
+ private int maxTrailerCount = Constants.DEFAULT_MAX_TRAILER_COUNT;
+ private int maxTrailerSize = Constants.DEFAULT_MAX_TRAILER_SIZE;
+
+
+ @Override
+ public String getHttpUpgradeName(boolean isSSLEnabled) {
+ if (isSSLEnabled) {
+ return null;
+ } else {
+ return HTTP_UPGRADE_NAME;
+ }
+ }
+
+ @Override
+ public byte[] getAlpnIdentifier() {
+ return ALPN_IDENTIFIER;
+ }
+
+ @Override
+ public String getAlpnName() {
+ return ALPN_NAME;
+ }
+
+ @Override
+ public Processor getProcessor(SocketWrapperBase<?> socketWrapper, Adapter adapter) {
+ UpgradeProcessorInternal processor = new UpgradeProcessorInternal(socketWrapper,
+ new UpgradeToken(getInternalUpgradeHandler(adapter, null), null, null));
+ return processor;
+ }
+
+
+ @Override
+ public InternalHttpUpgradeHandler getInternalUpgradeHandler(Adapter adapter,
+ Request coyoteRequest) {
+ Http2UpgradeHandler result = new Http2UpgradeHandler(adapter, coyoteRequest);
+
+ result.setReadTimeout(getReadTimeout());
+ result.setKeepAliveTimeout(getKeepAliveTimeout());
+ result.setWriteTimeout(getWriteTimeout());
+ result.setMaxConcurrentStreams(getMaxConcurrentStreams());
+ result.setMaxConcurrentStreamExecution(getMaxConcurrentStreamExecution());
+ result.setInitialWindowSize(getInitialWindowSize());
+ result.setAllowedTrailerHeaders(allowedTrailerHeaders);
+ result.setMaxHeaderCount(getMaxHeaderCount());
+ result.setMaxHeaderSize(getMaxHeaderSize());
+ result.setMaxTrailerCount(getMaxTrailerCount());
+ result.setMaxTrailerSize(getMaxTrailerSize());
+ return result;
+ }
+
+
+ @Override
+ public boolean accept(Request request) {
+ // Should only be one HTTP2-Settings header
+ Enumeration<String> settings = request.getMimeHeaders().values("HTTP2-Settings");
+ int count = 0;
+ while (settings.hasMoreElements()) {
+ count++;
+ settings.nextElement();
+ }
+ if (count != 1) {
+ return false;
+ }
+
+ Enumeration<String> connection = request.getMimeHeaders().values("Connection");
+ boolean found = false;
+ while (connection.hasMoreElements() && !found) {
+ found = connection.nextElement().contains("HTTP2-Settings");
+ }
+ return found;
+ }
+
+
+ public long getReadTimeout() {
+ return readTimeout;
+ }
+
+
+ public void setReadTimeout(long readTimeout) {
+ this.readTimeout = readTimeout;
+ }
+
+
+ public long getKeepAliveTimeout() {
+ return keepAliveTimeout;
+ }
+
+
+ public void setKeepAliveTimeout(long keepAliveTimeout) {
+ this.keepAliveTimeout = keepAliveTimeout;
+ }
+
+
+ public long getWriteTimeout() {
+ return writeTimeout;
+ }
+
+
+ public void setWriteTimeout(long writeTimeout) {
+ this.writeTimeout = writeTimeout;
+ }
+
+
+ public long getMaxConcurrentStreams() {
+ return maxConcurrentStreams;
+ }
+
+
+ public void setMaxConcurrentStreams(long maxConcurrentStreams) {
+ this.maxConcurrentStreams = maxConcurrentStreams;
+ }
+
+
+ public int getMaxConcurrentStreamExecution() {
+ return maxConcurrentStreamExecution;
+ }
+
+
+ public void setMaxConcurrentStreamExecution(int maxConcurrentStreamExecution) {
+ this.maxConcurrentStreamExecution = maxConcurrentStreamExecution;
+ }
+
+
+ public int getInitialWindowSize() {
+ return initialWindowSize;
+ }
+
+
+ public void setInitialWindowSize(int initialWindowSize) {
+ this.initialWindowSize = initialWindowSize;
+ }
+
+
+ public void setAllowedTrailerHeaders(String commaSeparatedHeaders) {
+ // Jump through some hoops so we don't end up with an empty set while
+ // doing updates.
+ Set<String> toRemove = new HashSet<>();
+ toRemove.addAll(allowedTrailerHeaders);
+ if (commaSeparatedHeaders != null) {
+ String[] headers = commaSeparatedHeaders.split(",");
+ for (String header : headers) {
+ String trimmedHeader = header.trim().toLowerCase(Locale.ENGLISH);
+ if (toRemove.contains(trimmedHeader)) {
+ toRemove.remove(trimmedHeader);
+ } else {
+ allowedTrailerHeaders.add(trimmedHeader);
+ }
+ }
+ allowedTrailerHeaders.removeAll(toRemove);
+ }
+ }
+
+
+ public String getAllowedTrailerHeaders() {
+ // Chances of a size change between these lines are small enough that a
+ // sync is unnecessary.
+ List<String> copy = new ArrayList<>(allowedTrailerHeaders.size());
+ copy.addAll(allowedTrailerHeaders);
+ StringBuilder result = new StringBuilder();
+ boolean first = true;
+ for (String header : copy) {
+ if (first) {
+ first = false;
+ } else {
+ result.append(',');
+ }
+ result.append(header);
+ }
+ return result.toString();
+ }
+
+
+ public void setMaxHeaderCount(int maxHeaderCount) {
+ this.maxHeaderCount = maxHeaderCount;
+ }
+
+
+ public int getMaxHeaderCount() {
+ return maxHeaderCount;
+ }
+
+
+ public void setMaxHeaderSize(int maxHeaderSize) {
+ this.maxHeaderSize = maxHeaderSize;
+ }
+
+
+ public int getMaxHeaderSize() {
+ return maxHeaderSize;
+ }
+
+
+ public void setMaxTrailerCount(int maxTrailerCount) {
+ this.maxTrailerCount = maxTrailerCount;
+ }
+
+
+ public int getMaxTrailerCount() {
+ return maxTrailerCount;
+ }
+
+
+ public void setMaxTrailerSize(int maxTrailerSize) {
+ this.maxTrailerSize = maxTrailerSize;
+ }
+
+
+ public int getMaxTrailerSize() {
+ return maxTrailerSize;
+ }
+}
diff --git a/java/org/apache/coyote/http2/Http2UpgradeHandler.java b/java/org/apache/coyote/http2/Http2UpgradeHandler.java
new file mode 100644
index 0000000..2cc25fe
--- /dev/null
+++ b/java/org/apache/coyote/http2/Http2UpgradeHandler.java
@@ -0,0 +1,1560 @@
+/*
+ * 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.EOFException;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Queue;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+
+import javax.servlet.http.WebConnection;
+
+import org.apache.coyote.Adapter;
+import org.apache.coyote.CloseNowException;
+import org.apache.coyote.ProtocolException;
+import org.apache.coyote.Request;
+import org.apache.coyote.Response;
+import org.apache.coyote.http11.upgrade.InternalHttpUpgradeHandler;
+import org.apache.coyote.http2.HpackDecoder.HeaderEmitter;
+import org.apache.coyote.http2.HpackEncoder.State;
+import org.apache.coyote.http2.Http2Parser.Input;
+import org.apache.coyote.http2.Http2Parser.Output;
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
+import org.apache.tomcat.util.codec.binary.Base64;
+import org.apache.tomcat.util.http.FastHttpDateFormat;
+import org.apache.tomcat.util.http.MimeHeaders;
+import org.apache.tomcat.util.net.AbstractEndpoint.Handler.SocketState;
+import org.apache.tomcat.util.net.SSLSupport;
+import org.apache.tomcat.util.net.SocketEvent;
+import org.apache.tomcat.util.net.SocketWrapperBase;
+import org.apache.tomcat.util.res.StringManager;
+
+/**
+ * This represents an HTTP/2 connection from a client to Tomcat. It is designed
+ * on the basis that there will never be more than one thread performing I/O at
+ * a time.
+ * <br>
+ * For reading, this implementation is blocking within frames and non-blocking
+ * between frames.
+ * <br>
+ * Note:
+ * <ul>
+ * <li>You will need to nest an <UpgradeProtocol
+ * className="org.apache.coyote.http2.Http2Protocol" /> element inside
+ * a TLS enabled Connector element in server.xml to enable HTTP/2 support.
+ * </li>
+ * </ul>
+ */
+public class Http2UpgradeHandler extends AbstractStream implements InternalHttpUpgradeHandler,
+ Input, Output {
+
+ private static final Log log = LogFactory.getLog(Http2UpgradeHandler.class);
+ private static final StringManager sm = StringManager.getManager(Http2UpgradeHandler.class);
+
+ private static final AtomicInteger connectionIdGenerator = new AtomicInteger(0);
+ private static final Integer STREAM_ID_ZERO = Integer.valueOf(0);
+
+ private static final int FLAG_END_OF_STREAM = 1;
+ private static final int FLAG_END_OF_HEADERS = 4;
+
+ private static final byte[] PING = { 0x00, 0x00, 0x08, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00};
+ private static final byte[] PING_ACK = { 0x00, 0x00, 0x08, 0x06, 0x01, 0x00, 0x00, 0x00, 0x00 };
+
+ private static final byte[] SETTINGS_ACK = { 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00 };
+
+ private static final byte[] GOAWAY = { 0x07, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+ private static final String HTTP2_SETTINGS_HEADER = "HTTP2-Settings";
+
+ private static final HeaderSink HEADER_SINK = new HeaderSink();
+
+ private final String connectionId;
+
+ private final Adapter adapter;
+ private volatile SocketWrapperBase<?> socketWrapper;
+ private volatile SSLSupport sslSupport;
+
+ private volatile Http2Parser parser;
+
+ // Simple state machine (sequence of states)
+ private AtomicReference<ConnectionState> connectionState =
+ new AtomicReference<>(ConnectionState.NEW);
+ private volatile long pausedNanoTime = Long.MAX_VALUE;
+
+ /**
+ * Remote settings are settings defined by the client and sent to Tomcat
+ * that Tomcat must use when communicating with the client.
+ */
+ private final ConnectionSettingsRemote remoteSettings;
+ /**
+ * Local settings are settings defined by Tomcat and sent to the client that
+ * the client must use when communicating with Tomcat.
+ */
+ private final ConnectionSettingsLocal localSettings;
+
+ private HpackDecoder hpackDecoder;
+ private HpackEncoder hpackEncoder;
+
+ // All timeouts in milliseconds
+ private long readTimeout = Http2Protocol.DEFAULT_READ_TIMEOUT;
+ private long keepAliveTimeout = Http2Protocol.DEFAULT_KEEP_ALIVE_TIMEOUT;
+ private long writeTimeout = Http2Protocol.DEFAULT_WRITE_TIMEOUT;
+
+ private final Map<Integer,Stream> streams = new HashMap<>();
+ private final AtomicInteger activeRemoteStreamCount = new AtomicInteger(0);
+ private volatile int maxRemoteStreamId = 0;
+ // Start at -1 so the 'add 2' logic in closeIdleStreams() works
+ private volatile int maxActiveRemoteStreamId = -1;
+ private volatile int maxProcessedStreamId;
+ private final AtomicInteger nextLocalStreamId = new AtomicInteger(2);
+ private final PingManager pingManager = new PingManager();
+ private volatile int newStreamsSinceLastPrune = 0;
+ // Tracking for when the connection is blocked (windowSize < 1)
+ private final ConcurrentMap<AbstractStream,int[]> backLogStreams = new ConcurrentHashMap<>();
+ private long backLogSize = 0;
+
+ // Stream concurrency control
+ private int maxConcurrentStreamExecution = Http2Protocol.DEFAULT_MAX_CONCURRENT_STREAM_EXECUTION;
+ private AtomicInteger streamConcurrency = null;
+ private Queue<StreamProcessor> queuedProcessors = null;
+
+ // Limits
+ private Set<String> allowedTrailerHeaders = Collections.emptySet();
+ private int maxHeaderCount = Constants.DEFAULT_MAX_HEADER_COUNT;
+ private int maxHeaderSize = Constants.DEFAULT_MAX_HEADER_SIZE;
+ private int maxTrailerCount = Constants.DEFAULT_MAX_TRAILER_COUNT;
+ private int maxTrailerSize = Constants.DEFAULT_MAX_TRAILER_SIZE;
+
+
+ public Http2UpgradeHandler(Adapter adapter, Request coyoteRequest) {
+ super (STREAM_ID_ZERO);
+ this.adapter = adapter;
+ this.connectionId = Integer.toString(connectionIdGenerator.getAndIncrement());
+
+ remoteSettings = new ConnectionSettingsRemote(connectionId);
+ localSettings = new ConnectionSettingsLocal(connectionId);
+
+ // Initial HTTP request becomes stream 1.
+ if (coyoteRequest != null) {
+ if (log.isDebugEnabled()) {
+ log.debug(sm.getString("upgradeHandler.upgrade", connectionId));
+ }
+ Integer key = Integer.valueOf(1);
+ Stream stream = new Stream(key, this, coyoteRequest);
+ streams.put(key, stream);
+ maxRemoteStreamId = 1;
+ maxActiveRemoteStreamId = 1;
+ activeRemoteStreamCount.set(1);
+ maxProcessedStreamId = 1;
+ }
+ }
+
+
+ @Override
+ public void init(WebConnection webConnection) {
+ if (log.isDebugEnabled()) {
+ log.debug(sm.getString("upgradeHandler.init", connectionId, connectionState.get()));
+ }
+
+ if (!connectionState.compareAndSet(ConnectionState.NEW, ConnectionState.CONNECTED)) {
+ return;
+ }
+
+ // Init concurrency control if needed
+ if (maxConcurrentStreamExecution < localSettings.getMaxConcurrentStreams()) {
+ streamConcurrency = new AtomicInteger(0);
+ queuedProcessors = new ConcurrentLinkedQueue<>();
+ }
+
+ parser = new Http2Parser(connectionId, this, this);
+
+ Stream stream = null;
+
+ socketWrapper.setReadTimeout(getReadTimeout());
+ socketWrapper.setWriteTimeout(getWriteTimeout());
+
+ if (webConnection != null) {
+ // HTTP/2 started via HTTP upgrade.
+ // The initial HTTP/1.1 request is available as Stream 1.
+
+ try {
+ // Process the initial settings frame
+ stream = getStream(1, true);
+ String base64Settings = stream.getCoyoteRequest().getHeader(HTTP2_SETTINGS_HEADER);
+ byte[] settings = Base64.decodeBase64(base64Settings);
+
+ // Settings are only valid on stream 0
+ FrameType.SETTINGS.check(0, settings.length);
+
+ for (int i = 0; i < settings.length % 6; i++) {
+ int id = ByteUtil.getTwoBytes(settings, i * 6);
+ long value = ByteUtil.getFourBytes(settings, (i * 6) + 2);
+ remoteSettings.set(Setting.valueOf(id), value);
+ }
+ } catch (Http2Exception e) {
+ throw new ProtocolException(
+ sm.getString("upgradeHandler.upgrade.fail", connectionId));
+ }
+ }
+
+ // Send the initial settings frame
+ try {
+ byte[] settings = localSettings.getSettingsFrameForPending();
+ socketWrapper.write(true, settings, 0, settings.length);
+ socketWrapper.flush(true);
+ } catch (IOException ioe) {
+ String msg = sm.getString("upgradeHandler.sendPrefaceFail", connectionId);
+ if (log.isDebugEnabled()) {
+ log.debug(msg);
+ }
+ throw new ProtocolException(msg, ioe);
+ }
+
+ // Make sure the client has sent a valid connection preface before we
+ // send the response to the original request over HTTP/2.
+ try {
+ parser.readConnectionPreface();
+ } catch (Http2Exception e) {
+ String msg = sm.getString("upgradeHandler.invalidPreface", connectionId);
+ if (log.isDebugEnabled()) {
+ log.debug(msg);
+ }
+ throw new ProtocolException(msg);
+ }
+ if (log.isDebugEnabled()) {
+ log.debug(sm.getString("upgradeHandler.prefaceReceived", connectionId));
+ }
+
+ // Send a ping to get an idea of round trip time as early as possible
+ try {
+ pingManager.sendPing(true);
+ } catch (IOException ioe) {
+ throw new ProtocolException(sm.getString("upgradeHandler.pingFailed"), ioe);
+ }
+
+ if (webConnection != null) {
+ processStreamOnContainerThread(stream);
+ }
+ }
+
+
+ private void processStreamOnContainerThread(Stream stream) {
+ StreamProcessor streamProcessor = new StreamProcessor(this, stream, adapter, socketWrapper);
+ streamProcessor.setSslSupport(sslSupport);
+ if (streamConcurrency == null) {
+ socketWrapper.getEndpoint().getExecutor().execute(streamProcessor);
+ } else {
+ if (getStreamConcurrency() < maxConcurrentStreamExecution) {
+ increaseStreamConcurrency();
+ socketWrapper.getEndpoint().getExecutor().execute(streamProcessor);
+ } else {
+ queuedProcessors.offer(streamProcessor);
+ }
+ }
+ }
+
+
+ @Override
+ public void setSocketWrapper(SocketWrapperBase<?> wrapper) {
+ this.socketWrapper = wrapper;
+ }
+
+
+ @Override
+ public void setSslSupport(SSLSupport sslSupport) {
+ this.sslSupport = sslSupport;
+ }
+
+
+ @Override
+ public SocketState upgradeDispatch(SocketEvent status) {
+ if (log.isDebugEnabled()) {
+ log.debug(sm.getString("upgradeHandler.upgradeDispatch.entry", connectionId, status));
+ }
+
+ // WebConnection is not used so passing null here is fine
+ // Might not be necessary. init() will handle that.
+ init(null);
+
+
+ SocketState result = SocketState.CLOSED;
+
+ try {
+ pingManager.sendPing(false);
+
+ checkPauseState();
+
+ switch(status) {
+ case OPEN_READ:
+ try {
+ // There is data to read so use the read timeout while
+ // reading frames.
+ socketWrapper.setReadTimeout(getReadTimeout());
+ while (true) {
+ try {
+ if (!parser.readFrame(false)) {
+ break;
+ }
+ } catch (StreamException se) {
+ // Stream errors are not fatal to the connection so
+ // continue reading frames
+ Stream stream = getStream(se.getStreamId(), false);
+ if (stream == null) {
+ sendStreamReset(se);
+ } else {
+ stream.close(se);
+ }
+ }
+ }
+ // No more frames to read so switch to the keep-alive
+ // timeout.
+ socketWrapper.setReadTimeout(getKeepAliveTimeout());
+ } catch (Http2Exception ce) {
+ // Really ConnectionException
+ if (log.isDebugEnabled()) {
+ log.debug(sm.getString("upgradeHandler.connectionError"), ce);
+ }
+ closeConnection(ce);
+ break;
+ }
+
+ result = SocketState.UPGRADED;
+ break;
+
+ case OPEN_WRITE:
+ processWrites();
+
+ result = SocketState.UPGRADED;
+ break;
+
+ case DISCONNECT:
+ case ERROR:
+ case TIMEOUT:
+ case STOP:
+ close();
+ break;
+ }
+ } catch (IOException ioe) {
+ if (log.isDebugEnabled()) {
+ log.debug(sm.getString("upgradeHandler.ioerror", connectionId), ioe);
+ }
+ close();
+ }
+
+ if (log.isDebugEnabled()) {
+ log.debug(sm.getString("upgradeHandler.upgradeDispatch.exit", connectionId, result));
+ }
+ return result;
+ }
+
+
+ ConnectionSettingsRemote getRemoteSettings() {
+ return remoteSettings;
+ }
+
+
+ ConnectionSettingsLocal getLocalSettings() {
+ return localSettings;
+ }
+
+
+ @Override
+ public void pause() {
+ if (log.isDebugEnabled()) {
+ log.debug(sm.getString("upgradeHandler.pause.entry", connectionId));
+ }
+
+ if (connectionState.compareAndSet(ConnectionState.CONNECTED, ConnectionState.PAUSING)) {
+ pausedNanoTime = System.nanoTime();
+
+ try {
+ writeGoAwayFrame((1 << 31) - 1, Http2Error.NO_ERROR.getCode(), null);
+ } catch (IOException ioe) {
+ // 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.
+ }
+ }
+ }
+
+
+ @Override
+ public void destroy() {
+ // NO-OP
+ }
+
+
+ private void checkPauseState() throws IOException {
+ if (connectionState.get() == ConnectionState.PAUSING) {
+ if (pausedNanoTime + pingManager.getRoundTripTimeNano() < System.nanoTime()) {
+ connectionState.compareAndSet(ConnectionState.PAUSING, ConnectionState.PAUSED);
+ writeGoAwayFrame(maxProcessedStreamId, Http2Error.NO_ERROR.getCode(), null);
+ }
+ }
+ }
+
+
+ private int increaseStreamConcurrency() {
+ return streamConcurrency.incrementAndGet();
+ }
+
+ private int decreaseStreamConcurrency() {
+ return streamConcurrency.decrementAndGet();
+ }
+
+ private int getStreamConcurrency() {
+ return streamConcurrency.get();
+ }
+
+ void executeQueuedStream() {
+ if (streamConcurrency == null) {
+ return;
+ }
+ decreaseStreamConcurrency();
+ if (getStreamConcurrency() < maxConcurrentStreamExecution) {
+ StreamProcessor streamProcessor = queuedProcessors.poll();
+ if (streamProcessor != null) {
+ increaseStreamConcurrency();
+ socketWrapper.getEndpoint().getExecutor().execute(streamProcessor);
+ }
+ }
+ }
+
+
+ void sendStreamReset(StreamException se) throws IOException {
+
+ if (log.isDebugEnabled()) {
+ log.debug(sm.getString("upgradeHandler.rst.debug", connectionId,
+ Integer.toString(se.getStreamId()), se.getError()));
+ }
+
+ // Write a RST frame
+ byte[] rstFrame = new byte[13];
+ // Length
+ ByteUtil.setThreeBytes(rstFrame, 0, 4);
+ // Type
+ rstFrame[3] = FrameType.RST.getIdByte();
+ // No flags
+ // Stream ID
+ ByteUtil.set31Bits(rstFrame, 5, se.getStreamId());
+ // Payload
+ ByteUtil.setFourBytes(rstFrame, 9, se.getError().getCode());
+
+ synchronized (socketWrapper) {
+ socketWrapper.write(true, rstFrame, 0, rstFrame.length);
+ socketWrapper.flush(true);
+ }
+ }
+
+
+ void closeConnection(Http2Exception ce) {
+ try {
+ writeGoAwayFrame(maxProcessedStreamId, ce.getError().getCode(),
+ ce.getMessage().getBytes(StandardCharsets.UTF_8));
+ } catch (IOException ioe) {
+ // Ignore. GOAWAY is sent on a best efforts basis and the original
+ // error has already been logged.
+ }
+ close();
+ }
+
+
+ private void writeGoAwayFrame(int maxStreamId, long errorCode, byte[] debugMsg)
+ throws IOException {
+ byte[] fixedPayload = new byte[8];
+ ByteUtil.set31Bits(fixedPayload, 0, maxStreamId);
+ ByteUtil.setFourBytes(fixedPayload, 4, errorCode);
+ int len = 8;
+ if (debugMsg != null) {
+ len += debugMsg.length;
+ }
+ byte[] payloadLength = new byte[3];
+ ByteUtil.setThreeBytes(payloadLength, 0, len);
+
+ synchronized (socketWrapper) {
+ socketWrapper.write(true, payloadLength, 0, payloadLength.length);
+ socketWrapper.write(true, GOAWAY, 0, GOAWAY.length);
+ socketWrapper.write(true, fixedPayload, 0, 8);
+ if (debugMsg != null) {
+ socketWrapper.write(true, debugMsg, 0, debugMsg.length);
+ }
+ socketWrapper.flush(true);
+ }
+ }
+
+ void writeHeaders(Stream stream, Response coyoteResponse, int payloadSize)
+ throws IOException {
+ if (log.isDebugEnabled()) {
+ log.debug(sm.getString("upgradeHandler.writeHeaders", connectionId,
+ stream.getIdentifier()));
+ }
+
+ if (!stream.canWrite()) {
+ return;
+ }
+
+ prepareHeaders(coyoteResponse);
+
+ byte[] header = new byte[9];
+ ByteBuffer target = ByteBuffer.allocate(payloadSize);
+ boolean first = true;
+ State state = null;
+ // This ensures the Stream processing thread has control of the socket.
+ synchronized (socketWrapper) {
+ while (state != State.COMPLETE) {
+ state = getHpackEncoder().encode(coyoteResponse.getMimeHeaders(), target);
+ target.flip();
+ ByteUtil.setThreeBytes(header, 0, target.limit());
+ if (first) {
+ first = false;
+ header[3] = FrameType.HEADERS.getIdByte();
+ if (stream.getOutputBuffer().hasNoBody()) {
+ header[4] = FLAG_END_OF_STREAM;
+ }
+ } else {
+ header[3] = FrameType.CONTINUATION.getIdByte();
+ }
+ if (state == State.COMPLETE) {
+ header[4] += FLAG_END_OF_HEADERS;
+ }
+ if (log.isDebugEnabled()) {
+ log.debug(target.limit() + " bytes");
+ }
+ ByteUtil.set31Bits(header, 5, stream.getIdentifier().intValue());
+ try {
+ socketWrapper.write(true, header, 0, header.length);
+ socketWrapper.write(true, target);
+ socketWrapper.flush(true);
+ } catch (IOException ioe) {
+ handleAppInitiatedIOException(ioe);
+ }
+ }
+ }
+ }
+
+
+ private void prepareHeaders(Response coyoteResponse) {
+ MimeHeaders headers = coyoteResponse.getMimeHeaders();
+ int statusCode = coyoteResponse.getStatus();
+
+ // Add the pseudo header for status
+ headers.addValue(":status").setString(Integer.toString(statusCode));
+
+ // Check to see if a response body is present
+ if (!(statusCode < 200 || statusCode == 205 || statusCode == 304)) {
+ String contentType = coyoteResponse.getContentType();
+ if (contentType != null) {
+ headers.setValue("content-type").setString(contentType);
+ }
+ String contentLanguage = coyoteResponse.getContentLanguage();
+ if (contentLanguage != null) {
+ headers.setValue("content-language").setString(contentLanguage);
+ }
+ }
+
+ // Add date header unless the application has already set one
+ if (headers.getValue("date") == null) {
+ headers.addValue("date").setString(FastHttpDateFormat.getCurrentDate());
+ }
+ }
+
+
+ void writePushHeaders(Stream stream, int pushedStreamId, Request coyoteRequest, int payloadSize)
+ throws IOException {
+ if (log.isDebugEnabled()) {
+ log.debug(sm.getString("upgradeHandler.writePushHeaders", connectionId,
+ stream.getIdentifier(), Integer.toString(pushedStreamId)));
+ }
+ // This ensures the Stream processing thread has control of the socket.
+ synchronized (socketWrapper) {
+ byte[] header = new byte[9];
+ ByteBuffer target = ByteBuffer.allocate(payloadSize);
+ boolean first = true;
+ State state = null;
+ byte[] pushedStreamIdBytes = new byte[4];
+ ByteUtil.set31Bits(pushedStreamIdBytes, 0, pushedStreamId);
+ target.put(pushedStreamIdBytes);
+ while (state != State.COMPLETE) {
+ state = getHpackEncoder().encode(coyoteRequest.getMimeHeaders(), target);
+ target.flip();
+ ByteUtil.setThreeBytes(header, 0, target.limit());
+ if (first) {
+ first = false;
+ header[3] = FrameType.PUSH_PROMISE.getIdByte();
+ } else {
+ header[3] = FrameType.CONTINUATION.getIdByte();
+ }
+ if (state == State.COMPLETE) {
+ header[4] += FLAG_END_OF_HEADERS;
+ }
+ if (log.isDebugEnabled()) {
+ log.debug(target.limit() + " bytes");
+ }
+ ByteUtil.set31Bits(header, 5, stream.getIdentifier().intValue());
+ socketWrapper.write(true, header, 0, header.length);
+ socketWrapper.write(true, target);
+ socketWrapper.flush(true);
+ }
+ }
+ }
+
+
+ private HpackEncoder getHpackEncoder() {
+ if (hpackEncoder == null) {
+ hpackEncoder = new HpackEncoder(remoteSettings.getHeaderTableSize());
+ }
+ return hpackEncoder;
+ }
+
+
+ void writeBody(Stream stream, ByteBuffer data, int len, boolean finished) throws IOException {
+ if (log.isDebugEnabled()) {
+ log.debug(sm.getString("upgradeHandler.writeBody", connectionId, stream.getIdentifier(),
+ Integer.toString(len)));
+ }
+ // Need to check this now since sending end of stream will change this.
+ boolean writeable = stream.canWrite();
+ byte[] header = new byte[9];
+ ByteUtil.setThreeBytes(header, 0, len);
+ header[3] = FrameType.DATA.getIdByte();
+ if (finished) {
+ header[4] = FLAG_END_OF_STREAM;
+ stream.sentEndOfStream();
+ if (!stream.isActive()) {
+ activeRemoteStreamCount.decrementAndGet();
+ }
+ }
+ if (writeable) {
+ ByteUtil.set31Bits(header, 5, stream.getIdentifier().intValue());
+ synchronized (socketWrapper) {
+ try {
+ socketWrapper.write(true, header, 0, header.length);
+ int orgLimit = data.limit();
+ data.limit(data.position() + len);
+ socketWrapper.write(true, data);
+ data.limit(orgLimit);
+ socketWrapper.flush(true);
+ } catch (IOException ioe) {
+ handleAppInitiatedIOException(ioe);
+ }
+ }
+ }
+ }
+
+
+ /*
+ * Handles an I/O error on the socket underlying the HTTP/2 connection when
+ * it is triggered by application code (usually reading the request or
+ * writing the response). Such I/O errors are fatal so the connection is
+ * closed. The exception is re-thrown to make the client code aware of the
+ * problem.
+ *
+ * Note: We can not rely on this exception reaching the socket processor
+ * since the application code may swallow it.
+ */
+ private void handleAppInitiatedIOException(IOException ioe) throws IOException {
+ close();
+ throw ioe;
+ }
+
+
+ /*
+ * Needs to know if this was application initiated since that affects the
+ * error handling.
+ */
+ void writeWindowUpdate(Stream stream, int increment, boolean applicationInitiated)
+ throws IOException {
+ if (!stream.canWrite()) {
+ return;
+ }
+ synchronized (socketWrapper) {
+ // Build window update frame for stream 0
+ byte[] frame = new byte[13];
+ ByteUtil.setThreeBytes(frame, 0, 4);
+ frame[3] = FrameType.WINDOW_UPDATE.getIdByte();
+ ByteUtil.set31Bits(frame, 9, increment);
+ socketWrapper.write(true, frame, 0, frame.length);
+ // Change stream Id and re-use
+ ByteUtil.set31Bits(frame, 5, stream.getIdentifier().intValue());
+ try {
+ socketWrapper.write(true, frame, 0, frame.length);
+ socketWrapper.flush(true);
+ } catch (IOException ioe) {
+ if (applicationInitiated) {
+ handleAppInitiatedIOException(ioe);
+ } else {
+ throw ioe;
+ }
+ }
+ }
+ }
+
+
+ private void processWrites() throws IOException {
+ synchronized (socketWrapper) {
+ if (socketWrapper.flush(false)) {
+ socketWrapper.registerWriteInterest();
+ return;
+ }
+ }
+ }
+
+
+ int reserveWindowSize(Stream stream, int reservation) throws IOException {
+ // Need to be holding the stream lock so releaseBacklog() can't notify
+ // this thread until after this thread enters wait()
+ int allocation = 0;
+ synchronized (stream) {
+ do {
+ synchronized (this) {
+ if (!stream.canWrite()) {
+ throw new CloseNowException(
+ sm.getString("upgradeHandler.stream.notWritable",
+ stream.getConnectionId(), stream.getIdentifier()));
+ }
+ long windowSize = getWindowSize();
+ if (windowSize < 1 || backLogSize > 0) {
+ // Has this stream been granted an allocation
+ int[] value = backLogStreams.get(stream);
+ if (value == null) {
+ value = new int[] { reservation, 0 };
+ backLogStreams.put(stream, value);
+ backLogSize += reservation;
+ // Add the parents as well
+ AbstractStream parent = stream.getParentStream();
+ while (parent != null && backLogStreams.putIfAbsent(parent, new int[2]) == null) {
+ parent = parent.getParentStream();
+ }
+ } else {
+ if (value[1] > 0) {
+ allocation = value[1];
+ decrementWindowSize(allocation);
+ if (value[0] == 0) {
+ // The reservation has been fully allocated
+ // so this stream can be removed from the
+ // backlog.
+ backLogStreams.remove(stream);
+ } else {
+ // This allocation has been used. Reset the
+ // allocation to zero. Leave the stream on
+ // the backlog as it still has more bytes to
+ // write.
+ value[1] = 0;
+ }
+ }
+ }
+ } else if (windowSize < reservation) {
+ allocation = (int) windowSize;
+ decrementWindowSize(allocation);
+ } else {
+ allocation = reservation;
+ decrementWindowSize(allocation);
+ }
+ }
+ if (allocation == 0) {
+ try {
+ stream.wait();
+ } catch (InterruptedException e) {
+ throw new IOException(sm.getString(
+ "upgradeHandler.windowSizeReservationInterrupted", connectionId,
+ stream.getIdentifier(), Integer.toString(reservation)), e);
+ }
+ }
+ } while (allocation == 0);
+ }
+ return allocation;
+ }
+
+
+
+ @SuppressWarnings("sync-override") // notifyAll() needs to be outside sync
+ // to avoid deadlock
+ @Override
+ protected void incrementWindowSize(int increment) throws Http2Exception {
+ Set<AbstractStream> streamsToNotify = null;
+
+ synchronized (this) {
+ long windowSize = getWindowSize();
+ if (windowSize < 1 && windowSize + increment > 0) {
+ streamsToNotify = releaseBackLog((int) (windowSize +increment));
+ }
+ super.incrementWindowSize(increment);
+ }
+
+ if (streamsToNotify != null) {
+ for (AbstractStream stream : streamsToNotify) {
+ synchronized (stream) {
+ stream.notifyAll();
+ }
+ }
+ }
+ }
+
+
+ private synchronized Set<AbstractStream> releaseBackLog(int increment) {
+ Set<AbstractStream> result = new HashSet<>();
+ if (backLogSize < increment) {
+ // Can clear the whole backlog
+ result.addAll(backLogStreams.keySet());
+ backLogStreams.clear();
+ backLogSize = 0;
+ } else {
+ int leftToAllocate = increment;
+ while (leftToAllocate > 0) {
+ leftToAllocate = allocate(this, leftToAllocate);
+ }
+ for (Entry<AbstractStream,int[]> entry : backLogStreams.entrySet()) {
+ int allocation = entry.getValue()[1];
+ if (allocation > 0) {
+ backLogSize -= allocation;
+ result.add(entry.getKey());
+ }
+ }
+ }
+ return result;
+ }
+
+
+
+ @Override
+ protected synchronized void doNotifyAll() {
+ this.notifyAll();
+ }
+
+
+ private int allocate(AbstractStream stream, int allocation) {
+ if (log.isDebugEnabled()) {
+ log.debug(sm.getString("upgradeHandler.allocate.debug", getConnectionId(),
+ stream.getIdentifier(), Integer.toString(allocation)));
+ }
+ // Allocate to the specified stream
+ int[] value = backLogStreams.get(stream);
+ if (value[0] >= allocation) {
+ value[0] -= allocation;
+ value[1] += allocation;
+ return 0;
+ }
+
+ // There was some left over so allocate that to the children of the
+ // stream.
+ int leftToAllocate = allocation;
+ value[1] = value[0];
+ value[0] = 0;
+ leftToAllocate -= value[1];
+
+ if (log.isDebugEnabled()) {
+ log.debug(sm.getString("upgradeHandler.allocate.left",
+ getConnectionId(), stream.getIdentifier(), Integer.toString(leftToAllocate)));
+ }
+
+ // Recipients are children of the current stream that are in the
+ // backlog.
+ Set<AbstractStream> recipients = new HashSet<>();
+ recipients.addAll(stream.getChildStreams());
+ recipients.retainAll(backLogStreams.keySet());
+
+ // Loop until we run out of allocation or recipients
+ while (leftToAllocate > 0) {
+ if (recipients.size() == 0) {
+ backLogStreams.remove(stream);
+ return leftToAllocate;
+ }
+
+ int totalWeight = 0;
+ for (AbstractStream recipient : recipients) {
+ if (log.isDebugEnabled()) {
+ log.debug(sm.getString("upgradeHandler.allocate.recipient",
+ getConnectionId(), stream.getIdentifier(), recipient.getIdentifier(),
+ Integer.toString(recipient.getWeight())));
+ }
+ totalWeight += recipient.getWeight();
+ }
+
+ // Use an Iterator so fully allocated children/recipients can be
+ // removed.
+ Iterator<AbstractStream> iter = recipients.iterator();
+ int allocated = 0;
+ while (iter.hasNext()) {
+ AbstractStream recipient = iter.next();
+ int share = leftToAllocate * recipient.getWeight() / totalWeight;
+ if (share == 0) {
+ // This is to avoid rounding issues triggering an infinite
+ // loop. It will cause a very slight over allocation but
+ // HTTP/2 should cope with that.
+ share = 1;
+ }
+ int remainder = allocate(recipient, share);
+ // Remove recipients that receive their full allocation so that
+ // they are excluded from the next allocation round.
+ if (remainder > 0) {
+ iter.remove();
+ }
+ allocated += (share - remainder);
+ }
+ leftToAllocate -= allocated;
+ }
+
+ return 0;
+ }
+
+
+ private Stream getStream(int streamId, boolean unknownIsError) throws ConnectionException {
+ Integer key = Integer.valueOf(streamId);
+ Stream result = streams.get(key);
+ if (result == null && unknownIsError) {
+ // Stream has been closed and removed from the map
+ throw new ConnectionException(sm.getString("upgradeHandler.stream.closed", key),
+ Http2Error.PROTOCOL_ERROR);
+ }
+ return result;
+ }
+
+
+ private Stream createRemoteStream(int streamId) throws ConnectionException {
+ Integer key = Integer.valueOf(streamId);
+
+ if (streamId %2 != 1) {
+ throw new ConnectionException(
+ sm.getString("upgradeHandler.stream.even", key), Http2Error.PROTOCOL_ERROR);
+ }
+
+ if (streamId <= maxRemoteStreamId) {
+ throw new ConnectionException(sm.getString("upgradeHandler.stream.old", key,
+ Integer.valueOf(maxRemoteStreamId)), Http2Error.PROTOCOL_ERROR);
+ }
+
+ pruneClosedStreams();
+
+ Stream result = new Stream(key, this);
+ streams.put(key, result);
+ maxRemoteStreamId = streamId;
+ return result;
+ }
+
+
+ private Stream createLocalStream(Request request) {
+ int streamId = nextLocalStreamId.getAndAdd(2);
+
+ Integer key = Integer.valueOf(streamId);
+
+ Stream result = new Stream(key, this, request);
+ streams.put(key, result);
+ maxRemoteStreamId = streamId;
+ return result;
+ }
+
+
+ private void close() {
+ connectionState.set(ConnectionState.CLOSED);
+ try {
+ socketWrapper.close();
+ } catch (IOException ioe) {
+ log.debug(sm.getString("upgradeHandler.socketCloseFailed"), ioe);
+ }
+ }
+
+
+ private void pruneClosedStreams() {
+ // Only prune every 10 new streams
+ if (newStreamsSinceLastPrune < 9) {
+ // Not atomic. Increments may be lost. Not a problem.
+ newStreamsSinceLastPrune++;
+ return;
+ }
+ // Reset counter
+ newStreamsSinceLastPrune = 0;
+
+ // RFC 7540, 5.3.4 endpoints should maintain state for at least the
+ // maximum number of concurrent streams
+ long max = localSettings.getMaxConcurrentStreams();
+
+ if (log.isDebugEnabled()) {
+ log.debug(sm.getString("upgradeHandler.pruneStart", connectionId,
+ Long.toString(max), Integer.toString(streams.size())));
+ }
+
+ // Allow an additional 10% for closed streams that are used in the
+ // priority tree
+ max = max + max / 10;
+ if (max > Integer.MAX_VALUE) {
+ max = Integer.MAX_VALUE;
+ }
+
+ int toClose = streams.size() - (int) max;
+ if (toClose < 1) {
+ return;
+ }
+
+ // Need to try and close some streams.
+ // Use this Set to keep track of streams that might be part of the
+ // priority tree. Only remove these if we absolutely have to.
+ TreeSet<Integer> additionalCandidates = new TreeSet<>();
+
+ Iterator<Entry<Integer,Stream>> entryIter = streams.entrySet().iterator();
+ while (entryIter.hasNext() && toClose > 0) {
+ Entry<Integer,Stream> entry = entryIter.next();
+ Stream stream = entry.getValue();
+ // Never remove active streams or streams with children
+ if (stream.isActive() || stream.getChildStreams().size() > 0) {
+ continue;
+ }
+ if (stream.isClosedFinal()) {
+ // This stream went from IDLE to CLOSED and is likely to have
+ // been created by the client as part of the priority tree. Keep
+ // it if possible.
+ additionalCandidates.add(entry.getKey());
+ } else {
+ if (log.isDebugEnabled()) {
+ log.debug(sm.getString("upgradeHandler.pruned", connectionId, entry.getKey()));
+ }
+ entryIter.remove();
+ toClose--;
+ }
+ }
+
+ while (toClose > 0 && additionalCandidates.size() > 0) {
+ Integer pruned = additionalCandidates.pollLast();
+ if (log.isDebugEnabled()) {
+ log.debug(sm.getString("upgradeHandler.prunedPriority", connectionId, pruned));
+ }
+ toClose++;
+ }
+
+ if (toClose > 0) {
+ log.warn(sm.getString("upgradeHandler.pruneIncomplete", connectionId,
+ Integer.toString(toClose)));
+ }
+ }
+
+
+ void push(Request request, Stream associatedStream) throws IOException {
+ Stream pushStream = createLocalStream(request);
+
+ // TODO: Is 1k the optimal value?
+ writePushHeaders(associatedStream, pushStream.getIdentifier().intValue(), request, 1024);
+
+ pushStream.sentPushPromise();
+
+ processStreamOnContainerThread(pushStream);
+ }
+
+
+ @Override
+ protected final String getConnectionId() {
+ return connectionId;
+ }
+
+
+ @Override
+ protected final int getWeight() {
+ return 0;
+ }
+
+
+ boolean isTrailerHeaderAllowed(String headerName) {
+ return allowedTrailerHeaders.contains(headerName);
+ }
+
+
+ // ------------------------------------------- Configuration getters/setters
+
+ public long getReadTimeout() {
+ return readTimeout;
+ }
+
+
+ public void setReadTimeout(long readTimeout) {
+ this.readTimeout = readTimeout;
+ }
+
+
+ public long getKeepAliveTimeout() {
+ return keepAliveTimeout;
+ }
+
+
+ public void setKeepAliveTimeout(long keepAliveTimeout) {
+ this.keepAliveTimeout = keepAliveTimeout;
+ }
+
+
+ public long getWriteTimeout() {
+ return writeTimeout;
+ }
+
+
+ public void setWriteTimeout(long writeTimeout) {
+ this.writeTimeout = writeTimeout;
+ }
+
+
+ public void setMaxConcurrentStreams(long maxConcurrentStreams) {
+ localSettings.set(Setting.MAX_CONCURRENT_STREAMS, maxConcurrentStreams);
+ }
+
+
+ public void setMaxConcurrentStreamExecution(int maxConcurrentStreamExecution) {
+ this.maxConcurrentStreamExecution = maxConcurrentStreamExecution;
+ }
+
+
+ public void setInitialWindowSize(int initialWindowSize) {
+ localSettings.set(Setting.INITIAL_WINDOW_SIZE, initialWindowSize);
+ }
+
+
+ public void setAllowedTrailerHeaders(Set<String> allowedTrailerHeaders) {
+ this.allowedTrailerHeaders = allowedTrailerHeaders;
+ }
+
+
+ public void setMaxHeaderCount(int maxHeaderCount) {
+ this.maxHeaderCount = maxHeaderCount;
+ }
+
+
+ public int getMaxHeaderCount() {
+ return maxHeaderCount;
+ }
+
+
+ public void setMaxHeaderSize(int maxHeaderSize) {
+ this.maxHeaderSize = maxHeaderSize;
+ }
+
+
+ public int getMaxHeaderSize() {
+ return maxHeaderSize;
+ }
+
+
+ public void setMaxTrailerCount(int maxTrailerCount) {
+ this.maxTrailerCount = maxTrailerCount;
+ }
+
+
+ public int getMaxTrailerCount() {
+ return maxTrailerCount;
+ }
+
+
+ public void setMaxTrailerSize(int maxTrailerSize) {
+ this.maxTrailerSize = maxTrailerSize;
+ }
+
+
+ public int getMaxTrailerSize() {
+ return maxTrailerSize;
+ }
+
+
+ // ----------------------------------------------- Http2Parser.Input methods
+
+ @Override
+ public boolean fill(boolean block, byte[] data) throws IOException {
+ return fill(block, data, 0, data.length);
+ }
+
+ @Override
+ public boolean fill(boolean block, ByteBuffer data, int len) throws IOException {
+ boolean result = fill(block, data.array(), data.arrayOffset() + data.position(), len);
+ if (result) {
+ data.position(data.position() + len);
+ }
+ return result;
+ }
+
+ @Override
+ public boolean fill(boolean block, byte[] data, int offset, int length) throws IOException {
+ int len = length;
+ int pos = offset;
+ boolean nextReadBlock = block;
+ int thisRead = 0;
+
+ while (len > 0) {
+ thisRead = socketWrapper.read(nextReadBlock, data, pos, len);
+ if (thisRead == 0) {
+ if (nextReadBlock) {
+ // Should never happen
+ throw new IllegalStateException();
+ } else {
+ return false;
+ }
+ } else if (thisRead == -1) {
+ if (connectionState.get().isNewStreamAllowed()) {
+ throw new EOFException();
+ } else {
+ return false;
+ }
+ } else {
+ pos += thisRead;
+ len -= thisRead;
+ nextReadBlock = true;
+ }
+ }
+
+ return true;
+ }
+
+
+ @Override
+ public int getMaxFrameSize() {
+ return localSettings.getMaxFrameSize();
+ }
+
+
+ // ---------------------------------------------- Http2Parser.Output methods
+
+ @Override
+ public HpackDecoder getHpackDecoder() {
+ if (hpackDecoder == null) {
+ hpackDecoder = new HpackDecoder(localSettings.getHeaderTableSize());
+ }
+ return hpackDecoder;
+ }
+
+
+ @Override
+ public ByteBuffer startRequestBodyFrame(int streamId, int payloadSize) throws Http2Exception {
+ Stream stream = getStream(streamId, true);
+ stream.checkState(FrameType.DATA);
+ return stream.getInputByteBuffer();
+ }
+
+
+
+ @Override
+ public void endRequestBodyFrame(int streamId) throws Http2Exception {
+ Stream stream = getStream(streamId, true);
+ stream.getInputBuffer().onDataAvailable();
+ }
+
+
+ @Override
+ public void receivedEndOfStream(int streamId) throws ConnectionException {
+ Stream stream = getStream(streamId, connectionState.get().isNewStreamAllowed());
+ if (stream != null) {
+ stream.receivedEndOfStream();
+ if (!stream.isActive()) {
+ activeRemoteStreamCount.decrementAndGet();
+ }
+ }
+ }
+
+
+ @Override
+ public void swallowedPadding(int streamId, int paddingLength) throws
+ ConnectionException, IOException {
+ Stream stream = getStream(streamId, true);
+ // +1 is for the payload byte used to define the padding length
+ writeWindowUpdate(stream, paddingLength + 1, false);
+ }
+
+
+ @Override
+ public HeaderEmitter headersStart(int streamId, boolean headersEndStream) throws Http2Exception {
+ if (connectionState.get().isNewStreamAllowed()) {
+ Stream stream = getStream(streamId, false);
+ if (stream == null) {
+ stream = createRemoteStream(streamId);
+ }
+ stream.checkState(FrameType.HEADERS);
+ stream.receivedStartOfHeaders(headersEndStream);
+ closeIdleStreams(streamId);
+ if (localSettings.getMaxConcurrentStreams() < activeRemoteStreamCount.incrementAndGet()) {
+ activeRemoteStreamCount.decrementAndGet();
+ throw new StreamException(sm.getString("upgradeHandler.tooManyRemoteStreams",
+ Long.toString(localSettings.getMaxConcurrentStreams())),
+ Http2Error.REFUSED_STREAM, streamId);
+ }
+ return stream;
+ } else {
+ if (log.isDebugEnabled()) {
+ log.debug(sm.getString("upgradeHandler.noNewStreams",
+ connectionId, Integer.toString(streamId)));
+ }
+ // Stateless so a static can be used to save on GC
+ return HEADER_SINK;
+ }
+ }
+
+
+ private void closeIdleStreams(int newMaxActiveRemoteStreamId) throws Http2Exception {
+ for (int i = maxActiveRemoteStreamId + 2; i < newMaxActiveRemoteStreamId; i += 2) {
+ Stream stream = getStream(i, false);
+ if (stream != null) {
+ stream.closeIfIdle();
+ }
+ }
+ maxActiveRemoteStreamId = newMaxActiveRemoteStreamId;
+ }
+
+
+ @Override
+ public void reprioritise(int streamId, int parentStreamId,
+ boolean exclusive, int weight) throws Http2Exception {
+ Stream stream = getStream(streamId, false);
+ if (stream == null) {
+ stream = createRemoteStream(streamId);
+ }
+ stream.checkState(FrameType.PRIORITY);
+ AbstractStream parentStream = getStream(parentStreamId, false);
+ if (parentStream == null) {
+ parentStream = this;
+ }
+ stream.rePrioritise(parentStream, exclusive, weight);
+ }
+
+
+ @Override
+ public void headersEnd(int streamId) throws ConnectionException {
+ setMaxProcessedStream(streamId);
+ Stream stream = getStream(streamId, connectionState.get().isNewStreamAllowed());
+ if (stream != null && stream.isActive()) {
+ if (stream.receivedEndOfHeaders()) {
+ processStreamOnContainerThread(stream);
+ }
+ }
+ }
+
+
+ private void setMaxProcessedStream(int streamId) {
+ if (maxProcessedStreamId < streamId) {
+ maxProcessedStreamId = streamId;
+ }
+ }
+
+
+ @Override
+ public void reset(int streamId, long errorCode) throws Http2Exception {
+ Stream stream = getStream(streamId, true);
+ stream.checkState(FrameType.RST);
+ stream.receiveReset(errorCode);
+ }
+
+
+ @Override
+ public void setting(Setting setting, long value) throws ConnectionException {
+ // Special handling required
+ if (setting == Setting.INITIAL_WINDOW_SIZE) {
+ long oldValue = remoteSettings.getInitialWindowSize();
+ // Do this first in case new value is invalid
+ remoteSettings.set(setting, value);
+ int diff = (int) (value - oldValue);
+ for (Stream stream : streams.values()) {
+ try {
+ stream.incrementWindowSize(diff);
+ } catch (Http2Exception h2e) {
+ stream.close(new StreamException(sm.getString(
+ "upgradeHandler.windowSizeTooBig", connectionId,
+ stream.getIdentifier()),
+ h2e.getError(), stream.getIdentifier().intValue()));
+ }
+ }
+ } else {
+ remoteSettings.set(setting, value);
+ }
+ }
+
+
+ @Override
+ public void settingsEnd(boolean ack) throws IOException {
+ if (ack) {
+ if (!localSettings.ack()) {
+ // Ack was unexpected
+ log.warn(sm.getString(
+ "upgradeHandler.unexpectedAck", connectionId, getIdentifier()));
+ }
+ } else {
+ synchronized (socketWrapper) {
+ socketWrapper.write(true, SETTINGS_ACK, 0, SETTINGS_ACK.length);
+ socketWrapper.flush(true);
+ }
+ }
+ }
+
+
+ @Override
+ public void pingReceive(byte[] payload, boolean ack) throws IOException {
+ pingManager.receivePing(payload, ack);
+ }
+
+
+ @Override
+ public void goaway(int lastStreamId, long errorCode, String debugData) {
+ if (log.isDebugEnabled()) {
+ log.debug(sm.getString("upgradeHandler.goaway.debug", connectionId,
+ Integer.toString(lastStreamId), Long.toHexString(errorCode), debugData));
+ }
+ }
+
+
+ @Override
+ public void incrementWindowSize(int streamId, int increment) throws Http2Exception {
+ if (streamId == 0) {
+ incrementWindowSize(increment);
+ } else {
+ Stream stream = getStream(streamId, true);
+ stream.checkState(FrameType.WINDOW_UPDATE);
+ stream.incrementWindowSize(increment);
+ }
+ }
+
+
+ @Override
+ public void swallowed(int streamId, FrameType frameType, int flags, int size)
+ throws IOException {
+ // NO-OP.
+ }
+
+
+ private class PingManager {
+
+ // 10 seconds
+ private final long pingIntervalNano = 10000000000L;
+
+ private int sequence = 0;
+ private long lastPingNanoTime = Long.MIN_VALUE;
+
+ private Queue<PingRecord> inflightPings = new ConcurrentLinkedQueue<>();
+ private Queue<Long> roundTripTimes = new ConcurrentLinkedQueue<>();
+
+ /**
+ * Check to see if a ping was sent recently and, if not, send one.
+ *
+ * @param force Send a ping, even if one was sent recently
+ *
+ * @throws IOException If an I/O issue prevents the ping from being sent
+ */
+ public void sendPing(boolean force) throws IOException {
+ long now = System.nanoTime();
+ if (force || now - lastPingNanoTime > pingIntervalNano) {
+ lastPingNanoTime = now;
+ byte[] payload = new byte[8];
+ synchronized (socketWrapper) {
+ 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);
+ }
+ }
+ }
+
+ public void receivePing(byte[] payload, boolean ack) throws IOException {
+ if (ack) {
+ // Extract the sequence from the payload
+ int receivedSequence = ByteUtil.get31Bits(payload, 4);
+ PingRecord pingRecord = inflightPings.poll();
+ while (pingRecord != null && pingRecord.getSequence() < receivedSequence) {
+ pingRecord = inflightPings.poll();
+ }
+ if (pingRecord == null) {
+ // Unexpected ACK. Log it.
+ } else {
+ long roundTripTime = System.nanoTime() - pingRecord.getSentNanoTime();
+ roundTripTimes.add(Long.valueOf(roundTripTime));
+ while (roundTripTimes.size() > 3) {
+ roundTripTimes.poll();
+ }
+ if (log.isDebugEnabled()) {
+ log.debug(sm.getString("pingManager.roundTripTime",
+ connectionId, Long.valueOf(roundTripTime)));
+ }
+ }
+
+ } else {
+ // Client originated ping. Echo it back.
+ synchronized (socketWrapper) {
+ socketWrapper.write(true, PING_ACK, 0, PING_ACK.length);
+ socketWrapper.write(true, payload, 0, payload.length);
+ socketWrapper.flush(true);
+ }
+ }
+ }
+
+ public long getRoundTripTimeNano() {
+ long sum = 0;
+ long count = 0;
+ for (Long roundTripTime: roundTripTimes) {
+ sum += roundTripTime.longValue();
+ count++;
+ }
+ if (count > 0) {
+ return sum / count;
+ }
+ return 0;
+ }
+ }
+
+
+ private static class PingRecord {
+
+ private final int sequence;
+ private final long sentNanoTime;
+
+ public PingRecord(int sequence, long sentNanoTime) {
+ this.sequence = sequence;
+ this.sentNanoTime = sentNanoTime;
+ }
+
+ public int getSequence() {
+ return sequence;
+ }
+
+ public long getSentNanoTime() {
+ return sentNanoTime;
+ }
+ }
+
+
+ private enum ConnectionState {
+
+ NEW(true),
+ CONNECTED(true),
+ PAUSING(true),
+ PAUSED(false),
+ CLOSED(false);
+
+ private final boolean newStreamsAllowed;
+
+ private ConnectionState(boolean newStreamsAllowed) {
+ this.newStreamsAllowed = newStreamsAllowed;
+ }
+
+ public boolean isNewStreamAllowed() {
+ return newStreamsAllowed;
+ }
+ }
+}
diff --git a/java/org/apache/coyote/http2/LocalStrings.properties b/java/org/apache/coyote/http2/LocalStrings.properties
new file mode 100644
index 0000000..af53eb3
--- /dev/null
+++ b/java/org/apache/coyote/http2/LocalStrings.properties
@@ -0,0 +1,137 @@
+# 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.
+
+abstractStream.windowSizeDec=Connection [{0}], Stream [{1}], reduce flow control window by [{2}] to [{3}]
+abstractStream.windowSizeInc=Connection [{0}], Stream [{1}], increase flow control window by [{2}] to [{3}]
+abstractStream.windowSizeTooBig=Connection [{0}], Stream [{1}], increase in window size of [{2}] to [{3}] exceeded permitted maximum
+
+connectionPrefaceParser.eos=Unexpected end of stream while reading opening client preface byte sequence. Only [{0}] bytes read.
+connectionPrefaceParser.ioError=Failed to read opening client preface byte sequence
+connectionPrefaceParser.mismatch=An unexpected byte sequence was received at the start of the client preface [{0}]
+
+connectionSettings.debug=Connection [{0}], Parameter type [{1}] set to [{2}]
+connectionSettings.enablePushInvalid=Connection [{0}], The requested value for enable push [{1}] is not one of the permitted values (zero or one)
+connectionSettings.headerTableSizeLimit=Connection [{0}], Attempted to set a header table size of [{1}] but the limit is 16k
+connectionSettings.maxFrameSizeInvalid=Connection [{0}], The requested maximum frame size of [{1}] is outside the permitted range of [{2}] to [{3}]
+connectionSettings.unknown=Connection [{0}], An unknown setting with identifier [{1}] and value [{2}] was ignored
+connectionSettings.windowSizeTooBig=Connection [{0}], The requested window size of [{1}] is bigger than the maximum permitted value of [{2}]
+
+frameType.checkPayloadSize=Payload size of [{0}] is not valid for frame type [{1}]
+frameType.checkStream=Invalid frame type [{0}]
+
+hpack.integerEncodedOverTooManyOctets=HPACK variable length integer encoded over too many octets, max is {0}
+
+hpackdecoder.zeroNotValidHeaderTableIndex=Zero is not a valid header table index
+
+hpackEncoder.encodeHeader=Encoding header [{0}] with value [{1}]
+
+hpackhuffman.huffmanEncodedHpackValueDidNotEndWithEOS=Huffman encoded value in HPACK headers did not end with EOS padding
+
+http2Parser.headerLimitCount=Connection [{0}], Stream [{1}], Too many headers
+http2Parser.headerLimitSize=Connection [{0}], Stream [{1}], Total header size too big
+http2Parser.headers.wrongFrameType=Connection [{0}], headers in progress for stream [{1}] but a frame of type [{2}] was received
+http2Parser.headers.wrongStream=Connection [{0}], headers in progress for stream [{1}] but a frame for stream [{2}] was received
+http2Parser.nonZeroPadding=Connection [{0}], Stream [{1}], Non-zero padding received
+http2Parser.payloadTooBig=The payload is [{0}] bytes long but the maximum frame size is [{1}]
+http2Parser.preface.invalid=Invalid connection preface [{0}] presented
+http2Parser.preface.io=Unable to read connection preface
+http2Parser.processFrame=Connection [{0}], Stream [{1}], Frame type [{2}], Flags [{3}], Payload size [{4}]
+http2Parser.processFrame.tooMuchPadding=Connection [{0}], Stream [{1}], The padding length [{2}] was too big for the payload [{3}]
+http2Parser.processFrame.unexpectedType=Expected frame type [{0}] but received frame type [{1}]
+http2Parser.processFrameContinuation.notExpected=Connection [{0}], Continuation frame received for stream [{1}] when no headers were in progress
+http2Parser.processFrameData.lengths=Connection [{0}], Stream [{1}], Data length, [{2}], Padding length [{3}]
+http2Parser.processFrameGoaway.payloadTooSmall=Connection [{0}]: Goaway payload size was [{1}] which is less than the minimum 8
+http2Parser.processFrameHeaders.decodingFailed=There was an error during the HPACK decoding of HTTP headers
+http2Parser.processFrameHeaders.decodingDataLeft=Data left over after HPACK decoding - it should have been consumed
+http2Parser.processFrameHeaders.payload=Connection [{0}], Stream [{1}], Processing headers payload size [{2}]
+http2Parser.processFramePing.invalidPayloadSize=Settings frame received with an invalid payload size of [{0}] (should be 8)
+http2Parser.processFramePriority.invalidParent=Connection [{0}], Stream [{1}], A stream may not depend on itself
+http2Parser.processFramePriority.invalidPayloadSize=Priority frame received with an invalid payload size of [{0}] (should be 5)
+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
+http2Parser.processFrameSettings.invalidPayloadSize=Settings frame received with a payload size of [{0}] which is not a multiple of 6
+http2Parser.processFrameWindowUpdate.debug=Connection [{0}], Stream [{1}], Window size increment [{2}]
+http2Parser.processFrameWindowUpdate.invalidIncrement=Window update frame received with an invalid increment size of [0]
+http2Parser.processFrameWindowUpdate.invalidPayloadSize=Window update frame received with an invalid payload size of [{0}]
+http2Parser.swallow.debug=Connection [{0}], Stream [{1}], Swallowed [{2}] bytes
+
+pingManager.roundTripTime=Connection [{0}] Round trip time measured as [{1}]ns
+
+stream.closed=Connection [{0}], Stream [{1}], Unable to write to stream once it has been closed
+stream.header.debug=Connection [{0}], Stream [{1}], HTTP header [{2}], Value [{3}]
+stream.header.unexpectedPseudoHeader=Connection [{0}], Stream [{1}], Pseduo header [{2}] received after a regular header
+stream.header.unknownPseudoHeader=Connection [{0}], Stream [{1}], Unknown pseduo header [{2}] received
+stream.notWritable=Connection [{0}], Stream [{1}], This stream is not writable
+stream.reprioritisation.debug=Connection [{0}], Stream [{1}], Exclusive [{2}], Parent [{3}], Weight [{4}]
+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}]
+stream.trialerHeader.noEndOfStream=Connection [{0}], Stream [{1}], The trailer headers did not include the end of stream flag
+stream.write=Connection [{0}], Stream [{1}]
+
+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
+stream.inputBuffer.signal=Data added to inBuffer when read thread is waiting. Signalling that thread to continue
+
+stream.outputBuffer.flush.debug=Connection [{0}], Stream [{1}], flushing output with buffer at position [{2}], writeInProgress [{3}] and closed [{4}]
+
+streamProcessor.error.connection=Connection [{0}], Stream [{1}], An error occurred during processing that was fatal to the connection
+streamProcessor.error.stream=Connection [{0}], Stream [{1}], An error occurred during processing that was fatal to the stream
+streamProcessor.service.error=Error during request processing
+
+streamStateMachine.debug.change=Connection [{0}], Stream [{1}], State changed from [{2}] to [{3}]
+streamStateMachine.invalidFrame=Connection [{0}], Stream [{1}], State [{2}], Frame type [{3}]
+streamStateMachine.invalidReset=Connection [{0}], Stream [{1}], State [{2}], Reset is not permitted in this state
+
+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.allocate.recipient=Connection [{0}], Stream [{1}], potential recipient [{2}] with weight [{3}]
+upgradeHandler.connectionError=Connection error
+upgradeHandler.goaway.debug=Connection [{0}], Goaway, Last stream [{1}], Error code [{2}], Debug data [{3}]
+upgradeHandler.init=Connection [{0}], State [{1}]
+upgradeHandler.initialWindowSize.invalid=Connection [{0}], Illegal value of [{1}] ignored for initial window size
+upgradeHandler.invalidPreface=Connection [{0}], Invalid connection preface
+upgradeHandler.ioerror=Connection [{0}]
+upgradeHandler.noNewStreams=Connection [{0}], Stream [{1}], Stream ignored as no new streams are permitted on this connection
+upgradeHandler.pause.entry=Connection [{0}] Pausing
+upgradeHandler.prefaceReceived=Connection [{0}], Connection preface received from client
+upgradeHandler.pingFailed=Connection [{0}] Failed to send ping to client
+upgradeHandler.pruneIncomplete=Connection [{0}] Failed to fully prune the connection because streams were active / used in the priority tree. There are [{1}] too many streams
+upgradeHandler.pruneStart=Connection [{0}] Starting pruning of old streams. Limit is [{1}] + 10% and there are currently [{2}] streams.
+upgradeHandler.pruned=Connection [{0}] Pruned completed stream [{1}]
+upgradeHandler.prunedPriority=Connection [{0}] Pruned unused stream [{1}] that may have been part of the priority tree
+upgradeHandler.rst.debug=Connection [{0}], Stream [{1}], Error [{2}], RST (closing stream)
+upgradeHandler.sendPrefaceFail=Connection [{0}], Failed to send preface to client
+upgradeHandler.socketCloseFailed=Error closing socket
+upgradeHandler.stream.closed=Stream [{0}] has been closed for some time
+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 not writable
+upgradeHandler.stream.old=A new remote stream ID of [{0}] was requested but the most recent stream was [{1}]
+upgradeHandler.tooManyRemoteStreams=The client attempted to use more than [{0}] active streams
+upgradeHandler.unexpectedAck=Connection [{0}], Stream [{1}], A settings acknowledgement was received when not expected
+upgradeHandler.unexpectedEos=Unexpected end of stream
+upgradeHandler.upgrade=Connection [{0}], HTTP/1.1 upgrade to stream [1]
+upgradeHandler.upgrade.fail=Connection [{0}], HTTP/1.1 upgrade failed
+upgradeHandler.upgradeDispatch.entry=Entry, Connection [{0}], SocketStatus [{1}]
+upgradeHandler.upgradeDispatch.exit=Exit, Connection [{0}], SocketState [{1}]
+upgradeHandler.windowSizeTooBig=Connection [{0}], Stream [{1}], Window size too big
+upgradeHandler.windowSizeReservationInterrupted=Connection [{0}], Stream [{1}], reservation for [{2}] bytes
+upgradeHandler.writeBody=Connection [{0}], Stream [{1}], Data length [{2}]
+upgradeHandler.writeHeaders=Connection [{0}], Stream [{1}]
+upgradeHandler.writePushHeaders=Connection [{0}], Stream [{1}], Pushed stream [{2}]
+
+writeStateMachine.endWrite.ise=It is illegal to specify [{0}] for the new state once a write has completed
+writeStateMachine.ise=It is illegal to call [{0}()] in state [{1}]
\ No newline at end of file
diff --git a/java/org/apache/coyote/http2/Setting.java b/java/org/apache/coyote/http2/Setting.java
new file mode 100644
index 0000000..edfde16
--- /dev/null
+++ b/java/org/apache/coyote/http2/Setting.java
@@ -0,0 +1,68 @@
+/*
+ * 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;
+
+public enum Setting {
+ HEADER_TABLE_SIZE(1),
+ ENABLE_PUSH(2),
+ MAX_CONCURRENT_STREAMS(3),
+ INITIAL_WINDOW_SIZE(4),
+ MAX_FRAME_SIZE(5),
+ MAX_HEADER_LIST_SIZE(6),
+ UNKNOWN(Integer.MAX_VALUE);
+
+ private final int id;
+
+ private Setting (int id) {
+ this.id = id;
+ }
+
+ public int getId() {
+ return id;
+ }
+
+ @Override
+ public String toString() {
+ return Integer.toString(id);
+ }
+
+ public static Setting valueOf(int i) {
+ switch(i) {
+ case 1: {
+ return HEADER_TABLE_SIZE;
+ }
+ case 2: {
+ return ENABLE_PUSH;
+ }
+ case 3: {
+ return MAX_CONCURRENT_STREAMS;
+ }
+ case 4: {
+ return INITIAL_WINDOW_SIZE;
+ }
+ case 5: {
+ return MAX_FRAME_SIZE;
+ }
+ case 6: {
+ return MAX_HEADER_LIST_SIZE;
+ }
+ default: {
+ return Setting.UNKNOWN;
+ }
+ }
+ }
+}
diff --git a/java/org/apache/coyote/http2/Stream.java b/java/org/apache/coyote/http2/Stream.java
new file mode 100644
index 0000000..070b86b
--- /dev/null
+++ b/java/org/apache/coyote/http2/Stream.java
@@ -0,0 +1,918 @@
+/*
+ * 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.nio.ByteBuffer;
+import java.security.AccessController;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.util.Iterator;
+
+import org.apache.coyote.ActionCode;
+import org.apache.coyote.CloseNowException;
+import org.apache.coyote.InputBuffer;
+import org.apache.coyote.OutputBuffer;
+import org.apache.coyote.Request;
+import org.apache.coyote.Response;
+import org.apache.coyote.http2.HpackDecoder.HeaderEmitter;
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
+import org.apache.tomcat.util.buf.ByteChunk;
+import org.apache.tomcat.util.net.ApplicationBufferHandler;
+import org.apache.tomcat.util.res.StringManager;
+
+public class Stream extends AbstractStream implements HeaderEmitter {
+
+ private static final Log log = LogFactory.getLog(Stream.class);
+ private static final StringManager sm = StringManager.getManager(Stream.class);
+
+ private static final int HEADER_STATE_START = 0;
+ private static final int HEADER_STATE_PSEUDO = 1;
+ private static final int HEADER_STATE_REGULAR = 2;
+ private static final int HEADER_STATE_TRAILER = 3;
+
+ private static final Response ACK_RESPONSE = new Response();
+
+ static {
+ ACK_RESPONSE.setStatus(100);
+ }
+
+ private volatile int weight = Constants.DEFAULT_WEIGHT;
+
+ private final Http2UpgradeHandler handler;
+ private final StreamStateMachine state;
+ // State machine would be too much overhead
+ private int headerState = HEADER_STATE_START;
+ private String headerStateErrorMsg = null;
+ // TODO: null these when finished to reduce memory used by closed stream
+ private final Request coyoteRequest;
+ private StringBuilder cookieHeader = null;
+ private final Response coyoteResponse = new Response();
+ private final StreamInputBuffer inputBuffer;
+ private final StreamOutputBuffer outputBuffer = new StreamOutputBuffer();
+
+
+ public Stream(Integer identifier, Http2UpgradeHandler handler) {
+ this(identifier, handler, null);
+ }
+
+
+ public Stream(Integer identifier, Http2UpgradeHandler handler, Request coyoteRequest) {
+ super(identifier);
+ this.handler = handler;
+ setParentStream(handler);
+ setWindowSize(handler.getRemoteSettings().getInitialWindowSize());
+ state = new StreamStateMachine(this);
+ if (coyoteRequest == null) {
+ // HTTP/2 new request
+ this.coyoteRequest = new Request();
+ this.inputBuffer = new StreamInputBuffer();
+ this.coyoteRequest.setInputBuffer(inputBuffer);
+ } else {
+ // HTTP/1.1 upgrade
+ this.coyoteRequest = coyoteRequest;
+ this.inputBuffer = null;
+ // Headers have been populated by this point
+ state.receivedStartOfHeaders();
+ // TODO Assuming the body has been read at this point is not valid
+ state.recievedEndOfStream();
+ }
+ // No sendfile for HTTP/2 (it is enabled by default in the request)
+ this.coyoteRequest.setSendfile(false);
+ this.coyoteResponse.setOutputBuffer(outputBuffer);
+ this.coyoteRequest.setResponse(coyoteResponse);
+ this.coyoteRequest.protocol().setString("HTTP/2.0");
+ }
+
+
+ void rePrioritise(AbstractStream parent, boolean exclusive, int weight) {
+ if (log.isDebugEnabled()) {
+ log.debug(sm.getString("stream.reprioritisation.debug",
+ getConnectionId(), getIdentifier(), Boolean.toString(exclusive),
+ parent.getIdentifier(), Integer.toString(weight)));
+ }
+
+ // Check if new parent is a descendant of this stream
+ if (isDescendant(parent)) {
+ parent.detachFromParent();
+ getParentStream().addChild(parent);
+ }
+
+ if (exclusive) {
+ // Need to move children of the new parent to be children of this
+ // stream. Slightly convoluted to avoid concurrent modification.
+ Iterator<AbstractStream> parentsChildren = parent.getChildStreams().iterator();
+ while (parentsChildren.hasNext()) {
+ AbstractStream parentsChild = parentsChildren.next();
+ parentsChildren.remove();
+ this.addChild(parentsChild);
+ }
+ }
+ parent.addChild(this);
+ this.weight = weight;
+ }
+
+
+ void receiveReset(long errorCode) {
+ if (log.isDebugEnabled()) {
+ log.debug(sm.getString("stream.reset.debug", getConnectionId(), getIdentifier(),
+ Long.toString(errorCode)));
+ }
+ // Set the new state first since read and write both check this
+ state.receivedReset();
+ // Reads wait internally so need to call a method to break the wait()
+ if (inputBuffer != null) {
+ inputBuffer.receiveReset();
+ }
+ // Writes wait on Stream so we can notify directly
+ synchronized (this) {
+ this.notifyAll();
+ }
+ }
+
+
+ void checkState(FrameType frameType) throws Http2Exception {
+ state.checkFrameType(frameType);
+ }
+
+
+ @Override
+ protected synchronized void incrementWindowSize(int windowSizeIncrement) throws Http2Exception {
+ // If this is zero then any thread that has been trying to write for
+ // this stream will be waiting. Notify that thread it can continue. Use
+ // notify all even though only one thread is waiting to be on the safe
+ // side.
+ boolean notify = getWindowSize() < 1;
+ super.incrementWindowSize(windowSizeIncrement);
+ if (notify && getWindowSize() > 0) {
+ notifyAll();
+ }
+ }
+
+
+ private synchronized int reserveWindowSize(int reservation, boolean block) throws IOException {
+ long windowSize = getWindowSize();
+ while (windowSize < 1) {
+ if (!canWrite()) {
+ throw new CloseNowException(sm.getString("stream.notWritable",
+ getConnectionId(), getIdentifier()));
+ }
+ try {
+ if (block) {
+ wait();
+ } else {
+ return 0;
+ }
+ } catch (InterruptedException e) {
+ // Possible shutdown / rst or similar. Use an IOException to
+ // signal to the client that further I/O isn't possible for this
+ // Stream.
+ throw new IOException(e);
+ }
+ windowSize = getWindowSize();
+ }
+ int allocation;
+ if (windowSize < reservation) {
+ allocation = (int) windowSize;
+ } else {
+ allocation = reservation;
+ }
+ decrementWindowSize(allocation);
+ return allocation;
+ }
+
+
+ @Override
+ protected synchronized void doNotifyAll() {
+ if (coyoteResponse.getWriteListener() == null) {
+ // Blocking IO so thread will be waiting. Release it.
+ // Use notifyAll() to be safe (should be unnecessary)
+ this.notifyAll();
+ } else {
+ if (outputBuffer.isRegisteredForWrite()) {
+ coyoteResponse.action(ActionCode.DISPATCH_WRITE, null);
+ }
+ }
+ }
+
+
+ @Override
+ public void emitHeader(String name, String value) {
+ if (log.isDebugEnabled()) {
+ log.debug(sm.getString("stream.header.debug", getConnectionId(), getIdentifier(),
+ name, value));
+ }
+
+ if (headerStateErrorMsg != null) {
+ // Don't bother processing the header since the stream is going to
+ // be reset anyway
+ return;
+ }
+
+ boolean pseudoHeader = name.charAt(0) == ':';
+
+ if (pseudoHeader && headerState != HEADER_STATE_PSEUDO) {
+ headerStateErrorMsg = sm.getString("stream.header.unexpectedPseudoHeader",
+ getConnectionId(), getIdentifier(), name);
+ // No need for further processing. The stream will be reset.
+ return;
+ }
+
+ if (headerState == HEADER_STATE_PSEUDO && !pseudoHeader) {
+ headerState = HEADER_STATE_REGULAR;
+ }
+
+ switch(name) {
+ case ":method": {
+ coyoteRequest.method().setString(value);
+ break;
+ }
+ case ":scheme": {
+ coyoteRequest.scheme().setString(value);
+ break;
+ }
+ case ":path": {
+ int queryStart = value.indexOf('?');
+ if (queryStart == -1) {
+ coyoteRequest.requestURI().setString(value);
+ coyoteRequest.decodedURI().setString(coyoteRequest.getURLDecoder().convert(value, false));
+ } else {
+ String uri = value.substring(0, queryStart);
+ String query = value.substring(queryStart + 1);
+ coyoteRequest.requestURI().setString(uri);
+ coyoteRequest.decodedURI().setString(coyoteRequest.getURLDecoder().convert(uri, false));
+ coyoteRequest.queryString().setString(coyoteRequest.getURLDecoder().convert(query, true));
+ }
+ break;
+ }
+ case ":authority": {
+ int i = value.lastIndexOf(':');
+ if (i > -1) {
+ coyoteRequest.serverName().setString(value.substring(0, i));
+ coyoteRequest.setServerPort(Integer.parseInt(value.substring(i + 1)));
+ } else {
+ coyoteRequest.serverName().setString(value);
+ }
+ break;
+ }
+ case "cookie": {
+ // Cookie headers need to be concatenated into a single header
+ // See RFC 7540 8.1.2.5
+ if (cookieHeader == null) {
+ cookieHeader = new StringBuilder();
+ } else {
+ cookieHeader.append("; ");
+ }
+ cookieHeader.append(value);
+ break;
+ }
+ default: {
+ if (headerState == HEADER_STATE_TRAILER && !handler.isTrailerHeaderAllowed(name)) {
+ break;
+ }
+ if ("expect".equals(name) && "100-continue".equals(value)) {
+ coyoteRequest.setExpectation(true);
+ }
+ if (pseudoHeader) {
+ headerStateErrorMsg = sm.getString("stream.header.unknownPseudoHeader",
+ getConnectionId(), getIdentifier(), name);
+ }
+ // Assume other HTTP header
+ coyoteRequest.getMimeHeaders().addValue(name).setString(value);
+ }
+ }
+ }
+
+
+ @Override
+ public void validateHeaders() throws StreamException {
+ if (headerStateErrorMsg == null) {
+ return;
+ }
+
+ throw new StreamException(headerStateErrorMsg, Http2Error.PROTOCOL_ERROR,
+ getIdentifier().intValue());
+ }
+
+
+ final boolean receivedEndOfHeaders() {
+ // Cookie headers need to be concatenated into a single header
+ // See RFC 7540 8.1.2.5
+ // Can only do this once the headers are fully received
+ if (cookieHeader != null) {
+ coyoteRequest.getMimeHeaders().addValue("cookie").setString(cookieHeader.toString());
+ }
+ return headerState == HEADER_STATE_REGULAR || headerState == HEADER_STATE_PSEUDO;
+ }
+
+
+ void writeHeaders() throws IOException {
+ // TODO: Is 1k the optimal value?
+ handler.writeHeaders(this, coyoteResponse, 1024);
+ }
+
+ void writeAck() throws IOException {
+ // TODO: Is 64 too big? Just the status header with compression
+ handler.writeHeaders(this, ACK_RESPONSE, 64);
+ }
+
+
+
+ void flushData() throws IOException {
+ if (log.isDebugEnabled()) {
+ log.debug(sm.getString("stream.write", getConnectionId(), getIdentifier()));
+ }
+ outputBuffer.flush(true);
+ }
+
+
+ @Override
+ protected final String getConnectionId() {
+ return getParentStream().getConnectionId();
+ }
+
+
+ @Override
+ protected int getWeight() {
+ return weight;
+ }
+
+
+ Request getCoyoteRequest() {
+ return coyoteRequest;
+ }
+
+
+ Response getCoyoteResponse() {
+ return coyoteResponse;
+ }
+
+
+ ByteBuffer getInputByteBuffer() {
+ return inputBuffer.getInBuffer();
+ }
+
+
+ final void receivedStartOfHeaders(boolean headersEndStream) throws Http2Exception {
+ if (headerState == HEADER_STATE_START) {
+ headerState = HEADER_STATE_PSEUDO;
+ handler.getHpackDecoder().setMaxHeaderCount(handler.getMaxHeaderCount());
+ handler.getHpackDecoder().setMaxHeaderSize(handler.getMaxHeaderSize());
+ } else if (headerState == HEADER_STATE_PSEUDO || headerState == HEADER_STATE_REGULAR) {
+ // Trailer headers MUST include the end of stream flag
+ if (headersEndStream) {
+ headerState = HEADER_STATE_TRAILER;
+ handler.getHpackDecoder().setMaxHeaderCount(handler.getMaxTrailerCount());
+ handler.getHpackDecoder().setMaxHeaderSize(handler.getMaxTrailerSize());
+ } else {
+ throw new ConnectionException(sm.getString("stream.trialerHeader.noEndOfStream",
+ getConnectionId(), getIdentifier()), Http2Error.PROTOCOL_ERROR);
+ }
+ }
+ // Parser will catch attempt to send a headers frame after the stream
+ // has closed.
+ state.receivedStartOfHeaders();
+ }
+
+
+ void receivedEndOfStream() {
+ synchronized (inputBuffer) {
+ inputBuffer.notifyAll();
+ }
+ state.recievedEndOfStream();
+ }
+
+
+ void sentEndOfStream() {
+ outputBuffer.endOfStreamSent = true;
+ state.sentEndOfStream();
+ }
+
+
+ StreamInputBuffer getInputBuffer() {
+ return inputBuffer;
+ }
+
+
+ StreamOutputBuffer getOutputBuffer() {
+ return outputBuffer;
+ }
+
+
+ void sentPushPromise() {
+ state.sentPushPromise();
+ }
+
+
+ boolean isActive() {
+ return state.isActive();
+ }
+
+
+ boolean canWrite() {
+ return state.canWrite();
+ }
+
+
+ boolean isClosedFinal() {
+ return state.isClosedFinal();
+ }
+
+
+ void closeIfIdle() {
+ state.closeIfIdle();
+ }
+
+
+ boolean isInputFinished() {
+ return !state.isFrameTypePermitted(FrameType.DATA);
+ }
+
+
+ void close(Http2Exception http2Exception) {
+ if (http2Exception instanceof StreamException) {
+ try {
+ StreamException se = (StreamException) http2Exception;
+ if (log.isDebugEnabled()) {
+ log.debug(sm.getString("stream.reset.send", getConnectionId(), getIdentifier(),
+ Long.toString(se.getError().getCode())));
+ }
+ state.sendReset();
+ handler.sendStreamReset(se);
+ } catch (IOException ioe) {
+ ConnectionException ce = new ConnectionException(
+ sm.getString("stream.reset.fail"), Http2Error.PROTOCOL_ERROR);
+ ce.initCause(ioe);
+ handler.closeConnection(ce);
+ }
+ } else {
+ handler.closeConnection(http2Exception);
+ }
+ }
+
+
+ boolean isPushSupported() {
+ return handler.getRemoteSettings().getEnablePush();
+ }
+
+
+ boolean push(Request request) throws IOException {
+ if (!isPushSupported()) {
+ return false;
+ }
+ // Set the special HTTP/2 headers
+ request.getMimeHeaders().addValue(":method").duplicate(request.method());
+ request.getMimeHeaders().addValue(":scheme").duplicate(request.scheme());
+ StringBuilder path = new StringBuilder(request.requestURI().toString());
+ if (!request.queryString().isNull()) {
+ path.append('?');
+ path.append(request.queryString().toString());
+ }
+ request.getMimeHeaders().addValue(":path").setString(path.toString());
+
+ // Authority needs to include the port only if a non-standard port is
+ // being used.
+ if (!(request.scheme().equals("http") && request.getServerPort() == 80) &&
+ !(request.scheme().equals("https") && request.getServerPort() == 443)) {
+ request.getMimeHeaders().addValue(":authority").setString(
+ request.serverName().getString() + ":" + request.getServerPort());
+ } else {
+ request.getMimeHeaders().addValue(":authority").duplicate(request.serverName());
+ }
+
+ push(handler, request, this);
+
+ return true;
+ }
+
+
+ private static void push(final Http2UpgradeHandler handler, final Request request, final Stream stream)
+ throws IOException {
+ if (org.apache.coyote.Constants.IS_SECURITY_ENABLED) {
+ try {
+ AccessController.doPrivileged(
+ new PrivilegedExceptionAction<Void>() {
+ @Override
+ public Void run() throws IOException {
+ handler.push(request, stream);
+ return null;
+ }
+ });
+ } catch (PrivilegedActionException ex) {
+ Exception e = ex.getException();
+ if (e instanceof IOException) {
+ throw (IOException) e;
+ } else {
+ throw new IOException(ex);
+ }
+ }
+
+ } else {
+ handler.push(request, stream);
+ }
+ }
+
+ class StreamOutputBuffer implements OutputBuffer {
+
+ private final ByteBuffer buffer = ByteBuffer.allocate(8 * 1024);
+ private volatile long written = 0;
+ private volatile boolean closed = false;
+ private volatile boolean endOfStreamSent = false;
+ private volatile boolean writeInterest = false;
+
+ /* The write methods are synchronized to ensure that only one thread at
+ * a time is able to access the buffer. Without this protection, a
+ * client that performed concurrent writes could corrupt the buffer.
+ */
+
+ /**
+ * @deprecated Unused. Will be removed in Tomcat 9. Use
+ * {@link #doWrite(ByteBuffer)}
+ */
+ @Override
+ public synchronized int doWrite(ByteChunk chunk) throws IOException {
+ if (closed) {
+ throw new IllegalStateException(
+ sm.getString("stream.closed", getConnectionId(), getIdentifier()));
+ }
+ if (!coyoteResponse.isCommitted()) {
+ coyoteResponse.sendHeaders();
+ }
+ int len = chunk.getLength();
+ int offset = 0;
+ while (len > 0) {
+ int thisTime = Math.min(buffer.remaining(), len);
+ buffer.put(chunk.getBytes(), chunk.getOffset() + offset, thisTime);
+ offset += thisTime;
+ len -= thisTime;
+ if (len > 0 && !buffer.hasRemaining()) {
+ // Only flush if we have more data to write and the buffer
+ // is full
+ if (flush(true, coyoteResponse.getWriteListener() == null)) {
+ break;
+ }
+ }
+ }
+ written += offset;
+ return offset;
+ }
+
+ @Override
+ public synchronized int doWrite(ByteBuffer chunk) throws IOException {
+ if (closed) {
+ throw new IllegalStateException(
+ sm.getString("stream.closed", getConnectionId(), getIdentifier()));
+ }
+ if (!coyoteResponse.isCommitted()) {
+ coyoteResponse.sendHeaders();
+ }
+ int chunkLimit = chunk.limit();
+ int offset = 0;
+ while (chunk.remaining() > 0) {
+ int thisTime = Math.min(buffer.remaining(), chunk.remaining());
+ chunk.limit(chunk.position() + thisTime);
+ buffer.put(chunk);
+ chunk.limit(chunkLimit);
+ offset += thisTime;
+ if (chunk.remaining() > 0 && !buffer.hasRemaining()) {
+ // Only flush if we have more data to write and the buffer
+ // is full
+ if (flush(true, coyoteResponse.getWriteListener() == null)) {
+ break;
+ }
+ }
+ }
+ written += offset;
+ return offset;
+ }
+
+ public synchronized boolean flush(boolean block) throws IOException {
+ return flush(false, block);
+ }
+
+ private synchronized boolean flush(boolean writeInProgress, boolean block)
+ throws IOException {
+ if (log.isDebugEnabled()) {
+ log.debug(sm.getString("stream.outputBuffer.flush.debug", getConnectionId(),
+ getIdentifier(), Integer.toString(buffer.position()),
+ Boolean.toString(writeInProgress), Boolean.toString(closed)));
+ }
+ if (buffer.position() == 0) {
+ if (closed && !endOfStreamSent) {
+ // Handling this special case here is simpler than trying
+ // to modify the following code to handle it.
+ handler.writeBody(Stream.this, buffer, 0, true);
+ }
+ // Buffer is empty. Nothing to do.
+ return false;
+ }
+ buffer.flip();
+ int left = buffer.remaining();
+ while (left > 0) {
+ int streamReservation = reserveWindowSize(left, block);
+ if (streamReservation == 0) {
+ // Must be non-blocking
+ buffer.compact();
+ return true;
+ }
+ while (streamReservation > 0) {
+ int connectionReservation =
+ handler.reserveWindowSize(Stream.this, streamReservation);
+ // Do the write
+ handler.writeBody(Stream.this, buffer, connectionReservation,
+ !writeInProgress && closed && left == connectionReservation);
+ streamReservation -= connectionReservation;
+ left -= connectionReservation;
+ }
+ }
+ buffer.clear();
+ return false;
+ }
+
+ synchronized boolean isReady() {
+ if (getWindowSize() > 0 && handler.getWindowSize() > 0) {
+ return true;
+ } else {
+ writeInterest = true;
+ return false;
+ }
+ }
+
+ synchronized boolean isRegisteredForWrite() {
+ if (writeInterest) {
+ writeInterest = false;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public long getBytesWritten() {
+ return written;
+ }
+
+ public void close() throws IOException {
+ closed = true;
+ flushData();
+ }
+
+ public boolean isClosed() {
+ return closed;
+ }
+
+ /**
+ * @return <code>true</code> if it is certain that the associated
+ * response has no body.
+ */
+ public boolean hasNoBody() {
+ return ((written == 0) && closed);
+ }
+ }
+
+
+ class StreamInputBuffer implements InputBuffer {
+
+ /* Two buffers are required to avoid various multi-threading issues.
+ * These issues arise from the fact that the Stream (or the
+ * Request/Response) used by the application is processed in one thread
+ * but the connection is processed in another. Therefore it is possible
+ * that a request body frame could be received before the application
+ * is ready to read it. If it isn't buffered, processing of the
+ * connection (and hence all streams) would block until the application
+ * read the data. Hence the incoming data has to be buffered.
+ * If only one buffer was used then it could become corrupted if the
+ * connection thread is trying to add to it at the same time as the
+ * application is read it. While it should be possible to avoid this
+ * corruption by careful use of the buffer it would still require the
+ * same copies as using two buffers and the behaviour would be less
+ * clear.
+ *
+ * The buffers are created lazily because they quickly add up to a lot
+ * of memory and most requests do not have bodies.
+ */
+ // This buffer is used to populate the ByteChunk passed in to the read
+ // method
+ private byte[] outBuffer;
+ // This buffer is the destination for incoming data. It is normally is
+ // 'write mode'.
+ private volatile ByteBuffer inBuffer;
+ private volatile boolean readInterest;
+ private boolean reset = false;
+
+ /**
+ * @deprecated Unused. Will be removed in Tomcat 9. Use
+ * {@link #doRead(ApplicationBufferHandler)}
+ */
+ @Override
+ public int doRead(ByteChunk chunk) throws IOException {
+
+ ensureBuffersExist();
+
+ int written = -1;
+
+ // Ensure that only one thread accesses inBuffer at a time
+ synchronized (inBuffer) {
+ while (inBuffer.position() == 0 && !isInputFinished()) {
+ // Need to block until some data is written
+ try {
+ if (log.isDebugEnabled()) {
+ log.debug(sm.getString("stream.inputBuffer.empty"));
+ }
+ inBuffer.wait();
+ if (reset) {
+ // TODO: i18n
+ throw new IOException("HTTP/2 Stream reset");
+ }
+ } catch (InterruptedException e) {
+ // Possible shutdown / rst or similar. Use an
+ // IOException to signal to the client that further I/O
+ // isn't possible for this Stream.
+ throw new IOException(e);
+ }
+ }
+
+ if (inBuffer.position() > 0) {
+ // Data is available in the inBuffer. Copy it to the
+ // outBuffer.
+ inBuffer.flip();
+ written = inBuffer.remaining();
+ if (log.isDebugEnabled()) {
+ log.debug(sm.getString("stream.inputBuffer.copy",
+ Integer.toString(written)));
+ }
+ inBuffer.get(outBuffer, 0, written);
+ inBuffer.clear();
+ } else if (isInputFinished()) {
+ return -1;
+ } else {
+ // Should never happen
+ throw new IllegalStateException();
+ }
+ }
+
+ chunk.setBytes(outBuffer, 0, written);
+
+ // Increment client-side flow control windows by the number of bytes
+ // read
+ handler.writeWindowUpdate(Stream.this, written, true);
+
+ return written;
+ }
+
+ @Override
+ public int doRead(ApplicationBufferHandler applicationBufferHandler) throws IOException {
+
+ ensureBuffersExist();
+
+ int written = -1;
+
+ // Ensure that only one thread accesses inBuffer at a time
+ synchronized (inBuffer) {
+ while (inBuffer.position() == 0 && !isInputFinished()) {
+ // Need to block until some data is written
+ try {
+ if (log.isDebugEnabled()) {
+ log.debug(sm.getString("stream.inputBuffer.empty"));
+ }
+ inBuffer.wait();
+ if (reset) {
+ // TODO: i18n
+ throw new IOException("HTTP/2 Stream reset");
+ }
+ } catch (InterruptedException e) {
+ // Possible shutdown / rst or similar. Use an
+ // IOException to signal to the client that further I/O
+ // isn't possible for this Stream.
+ throw new IOException(e);
+ }
+ }
+
+ if (inBuffer.position() > 0) {
+ // Data is available in the inBuffer. Copy it to the
+ // outBuffer.
+ inBuffer.flip();
+ written = inBuffer.remaining();
+ if (log.isDebugEnabled()) {
+ log.debug(sm.getString("stream.inputBuffer.copy",
+ Integer.toString(written)));
+ }
+ inBuffer.get(outBuffer, 0, written);
+ inBuffer.clear();
+ } else if (isInputFinished()) {
+ return -1;
+ } else {
+ // Should never happen
+ throw new IllegalStateException();
+ }
+ }
+
+ applicationBufferHandler.setByteBuffer(ByteBuffer.wrap(outBuffer, 0, written));
+
+ // Increment client-side flow control windows by the number of bytes
+ // read
+ handler.writeWindowUpdate(Stream.this, written, true);
+
+ return written;
+ }
+
+
+ void registerReadInterest() {
+ synchronized (inBuffer) {
+ readInterest = true;
+ }
+ }
+
+
+ synchronized boolean isRequestBodyFullyRead() {
+ return (inBuffer == null || inBuffer.position() == 0) && isInputFinished();
+ }
+
+
+ synchronized int available() {
+ if (inBuffer == null) {
+ return 0;
+ }
+ return inBuffer.position();
+ }
+
+
+ /*
+ * Called after placing some data in the inBuffer.
+ */
+ synchronized boolean onDataAvailable() {
+ if (readInterest) {
+ if (log.isDebugEnabled()) {
+ log.debug(sm.getString("stream.inputBuffer.dispatch"));
+ }
+ readInterest = false;
+ coyoteRequest.action(ActionCode.DISPATCH_READ, null);
+ // Always need to dispatch since this thread is processing
+ // the incoming connection and streams are processed on their
+ // own.
+ coyoteRequest.action(ActionCode.DISPATCH_EXECUTE, null);
+ return true;
+ } else {
+ if (log.isDebugEnabled()) {
+ log.debug(sm.getString("stream.inputBuffer.signal"));
+ }
+ synchronized (inBuffer) {
+ inBuffer.notifyAll();
+ }
+ return false;
+ }
+ }
+
+
+ public ByteBuffer getInBuffer() {
+ ensureBuffersExist();
+ return inBuffer;
+ }
+
+
+ protected synchronized void insertReplayedBody(ByteChunk body) {
+ inBuffer = ByteBuffer.wrap(body.getBytes(), body.getOffset(), body.getLength());
+ }
+
+
+ private void ensureBuffersExist() {
+ if (inBuffer == null) {
+ // The client must obey Tomcat's window size when sending so
+ // this is the initial window size set by Tomcat that the client
+ // uses (i.e. the local setting is required here).
+ int size = handler.getLocalSettings().getInitialWindowSize();
+ synchronized (this) {
+ if (inBuffer == null) {
+ inBuffer = ByteBuffer.allocate(size);
+ outBuffer = new byte[size];
+ }
+ }
+ }
+ }
+
+
+ protected void receiveReset() {
+ if (inBuffer != null) {
+ synchronized (inBuffer) {
+ reset = true;
+ inBuffer.notifyAll();
+ }
+ }
+ }
+ }
+}
diff --git a/java/org/apache/coyote/http2/StreamException.java b/java/org/apache/coyote/http2/StreamException.java
new file mode 100644
index 0000000..efb3748
--- /dev/null
+++ b/java/org/apache/coyote/http2/StreamException.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.coyote.http2;
+
+/**
+ * Thrown when an HTTP/2 stream error occurs.
+ */
+public class StreamException extends Http2Exception {
+
+ private static final long serialVersionUID = 1L;
+
+ private final int streamId;
+
+ public StreamException(String msg, Http2Error error, int streamId) {
+ super(msg, error);
+ this.streamId = streamId;
+ }
+
+
+ public int getStreamId() {
+ return streamId;
+ }
+}
diff --git a/java/org/apache/coyote/http2/StreamProcessor.java b/java/org/apache/coyote/http2/StreamProcessor.java
new file mode 100644
index 0000000..5d8c3a9
--- /dev/null
+++ b/java/org/apache/coyote/http2/StreamProcessor.java
@@ -0,0 +1,266 @@
+/*
+ * 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 org.apache.coyote.AbstractProcessor;
+import org.apache.coyote.ActionCode;
+import org.apache.coyote.Adapter;
+import org.apache.coyote.ContainerThreadMarker;
+import org.apache.coyote.ErrorState;
+import org.apache.coyote.PushToken;
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
+import org.apache.tomcat.util.buf.ByteChunk;
+import org.apache.tomcat.util.net.AbstractEndpoint.Handler.SocketState;
+import org.apache.tomcat.util.net.SocketEvent;
+import org.apache.tomcat.util.net.SocketWrapperBase;
+import org.apache.tomcat.util.res.StringManager;
+
+public class StreamProcessor extends AbstractProcessor implements Runnable {
+
+ private static final Log log = LogFactory.getLog(StreamProcessor.class);
+ private static final StringManager sm = StringManager.getManager(StreamProcessor.class);
+
+ private final Http2UpgradeHandler handler;
+ private final Stream stream;
+
+
+ public StreamProcessor(Http2UpgradeHandler handler, Stream stream, Adapter adapter, SocketWrapperBase<?> socketWrapper) {
+ super(stream.getCoyoteRequest(), stream.getCoyoteResponse());
+ this.handler = handler;
+ this.stream = stream;
+ setAdapter(adapter);
+ setSocketWrapper(socketWrapper);
+ }
+
+
+ @Override
+ public void run() {
+ try {
+ // FIXME: the regular processor syncs on socketWrapper, but here this deadlocks
+ synchronized (this) {
+ // HTTP/2 equivalent of AbstractConnectionHandler#process() without the
+ // socket <-> processor mapping
+ ContainerThreadMarker.set();
+ SocketState state = SocketState.CLOSED;
+ try {
+ state = process(socketWrapper, SocketEvent.OPEN_READ);
+
+ if (state == SocketState.CLOSED) {
+ if (!getErrorState().isConnectionIoAllowed()) {
+ ConnectionException ce = new ConnectionException(sm.getString(
+ "streamProcessor.error.connection", stream.getConnectionId(),
+ stream.getIdentifier()), Http2Error.INTERNAL_ERROR);
+ stream.close(ce);
+ } else if (!getErrorState().isIoAllowed()) {
+ StreamException se = new StreamException(sm.getString(
+ "streamProcessor.error.stream", stream.getConnectionId(),
+ stream.getIdentifier()), Http2Error.INTERNAL_ERROR,
+ stream.getIdentifier().intValue());
+ stream.close(se);
+ }
+ }
+ } catch (Exception e) {
+ ConnectionException ce = new ConnectionException(sm.getString(
+ "streamProcessor.error.connection", stream.getConnectionId(),
+ stream.getIdentifier()), Http2Error.INTERNAL_ERROR);
+ ce.initCause(e);
+ stream.close(ce);
+ } finally {
+ ContainerThreadMarker.clear();
+ }
+ }
+ } finally {
+ handler.executeQueuedStream();
+ }
+ }
+
+
+ @Override
+ protected final void prepareResponse() throws IOException {
+ response.setCommitted(true);
+ stream.writeHeaders();
+ }
+
+
+ @Override
+ protected final void finishResponse() throws IOException {
+ stream.getOutputBuffer().close();
+ }
+
+
+ @Override
+ protected final void ack() {
+ if (!response.isCommitted() && request.hasExpectation()) {
+ try {
+ stream.writeAck();
+ } catch (IOException ioe) {
+ setErrorState(ErrorState.CLOSE_CONNECTION_NOW, ioe);
+ }
+ }
+ }
+
+
+ @Override
+ protected final void flush() throws IOException {
+ stream.flushData();
+ }
+
+
+ @Override
+ protected final int available(boolean doRead) {
+ return stream.getInputBuffer().available();
+ }
+
+
+ @Override
+ protected final void setRequestBody(ByteChunk body) {
+ stream.getInputBuffer().insertReplayedBody(body);
+ stream.receivedEndOfStream();
+ }
+
+
+ @Override
+ protected final void setSwallowResponse() {
+ // NO-OP
+ }
+
+
+ @Override
+ protected final void disableSwallowRequest() {
+ // NO-OP
+ // HTTP/2 has to swallow any input received to ensure that the flow
+ // control windows are correctly tracked.
+ }
+
+
+ @Override
+ protected final boolean isRequestBodyFullyRead() {
+ return stream.getInputBuffer().isRequestBodyFullyRead();
+ }
+
+
+ @Override
+ protected final void registerReadInterest() {
+ stream.getInputBuffer().registerReadInterest();
+ }
+
+
+ @Override
+ protected final boolean isReady() {
+ return stream.getOutputBuffer().isReady();
+ }
+
+
+ @Override
+ protected final void executeDispatches(SocketWrapperBase<?> wrapper) {
+ wrapper.getEndpoint().getExecutor().execute(this);
+ }
+
+
+ @Override
+ protected final boolean isPushSupported() {
+ return stream.isPushSupported();
+ }
+
+
+ @Override
+ protected final void doPush(PushToken pushToken) {
+ try {
+ pushToken.setResult(stream.push(pushToken.getPushTarget()));
+ } catch (IOException ioe) {
+ setErrorState(ErrorState.CLOSE_CONNECTION_NOW, ioe);
+ response.setErrorException(ioe);
+ }
+ }
+
+
+ @Override
+ public void recycle() {
+ // StreamProcessor instances are not re-used.
+ // Clear fields that can be cleared to aid GC and trigger NPEs if this
+ // is reused
+ setSocketWrapper(null);
+ setAdapter(null);
+ }
+
+
+ @Override
+ protected Log getLog() {
+ return log;
+ }
+
+
+ @Override
+ public void pause() {
+ // NO-OP. Handled by the Http2UpgradeHandler
+ }
+
+
+ @Override
+ public SocketState service(SocketWrapperBase<?> socket) throws IOException {
+ try {
+ adapter.service(request, response);
+ } catch (Exception e) {
+ if (log.isDebugEnabled()) {
+ log.debug(sm.getString("streamProcessor.service.error"), e);
+ }
+ response.setStatus(500);
+ setErrorState(ErrorState.CLOSE_NOW, e);
+ }
+
+ if (getErrorState().isError()) {
+ action(ActionCode.CLOSE, null);
+ request.updateCounters();
+ return SocketState.CLOSED;
+ } else if (isAsync()) {
+ return SocketState.LONG;
+ } else {
+ action(ActionCode.CLOSE, null);
+ request.updateCounters();
+ return SocketState.CLOSED;
+ }
+ }
+
+
+ @Override
+ protected boolean flushBufferedWrite() throws IOException {
+ if (stream.getOutputBuffer().flush(false)) {
+ // The buffer wasn't fully flushed so re-register the
+ // stream for write. Note this does not go via the
+ // Response since the write registration state at
+ // that level should remain unchanged. Once the buffer
+ // has been emptied then the code below will call
+ // dispatch() which will enable the
+ // Response to respond to this event.
+ if (stream.getOutputBuffer().isReady()) {
+ // Unexpected
+ throw new IllegalStateException();
+ }
+ return true;
+ }
+ return false;
+ }
+
+
+ @Override
+ protected SocketState dispatchEndRequest() {
+ return SocketState.CLOSED;
+ }
+}
diff --git a/java/org/apache/coyote/http2/StreamStateMachine.java b/java/org/apache/coyote/http2/StreamStateMachine.java
new file mode 100644
index 0000000..ae28ebb
--- /dev/null
+++ b/java/org/apache/coyote/http2/StreamStateMachine.java
@@ -0,0 +1,257 @@
+/*
+ * 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.util.HashSet;
+import java.util.Set;
+
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
+import org.apache.tomcat.util.res.StringManager;
+
+/**
+ * See <a href="https://tools.ietf.org/html/rfc7540#section-5.1">state
+ * diagram</a> in RFC 7540.
+ * <br>
+ * The following additions are supported by this state machine:
+ * <ul>
+ * <li>differentiate between closed (normal) and closed caused by reset</li>
+ * </ul>
+ *
+ */
+public class StreamStateMachine {
+
+ private static final Log log = LogFactory.getLog(StreamStateMachine.class);
+ private static final StringManager sm = StringManager.getManager(StreamStateMachine.class);
+
+ private final Stream stream;
+ private State state;
+
+
+ public StreamStateMachine(Stream stream) {
+ this.stream = stream;
+ stateChange(null, State.IDLE);
+ }
+
+
+ public synchronized void sentPushPromise() {
+ stateChange(State.IDLE, State.RESERVED_LOCAL);
+ }
+
+
+ public synchronized void receivedPushPromise() {
+ stateChange(State.IDLE, State.RESERVED_REMOTE);
+ }
+
+
+ public synchronized void sentStartOfHeaders() {
+ stateChange(State.IDLE, State.OPEN);
+ stateChange(State.RESERVED_LOCAL, State.HALF_CLOSED_REMOTE);
+ }
+
+
+ public synchronized void receivedStartOfHeaders() {
+ stateChange(State.IDLE, State.OPEN);
+ stateChange(State.RESERVED_REMOTE, State.HALF_CLOSED_LOCAL);
+ }
+
+
+ public synchronized void sentEndOfStream() {
+ stateChange(State.OPEN, State.HALF_CLOSED_LOCAL);
+ stateChange(State.HALF_CLOSED_REMOTE, State.CLOSED_TX);
+ }
+
+
+ public synchronized void recievedEndOfStream() {
+ stateChange(State.OPEN, State.HALF_CLOSED_REMOTE);
+ stateChange(State.HALF_CLOSED_LOCAL, State.CLOSED_RX);
+ }
+
+
+ /**
+ * Marks the stream as reset. This method will not change the stream state
+ * if:
+ * <ul>
+ * <li>The stream is already reset</li>
+ * <li>The stream is already closed</li>
+ * </ul>
+ *
+ * @throws IllegalStateException If the stream is in a state that does not
+ * permit resets
+ */
+ public synchronized void sendReset() {
+ if (state == State.IDLE) {
+ throw new IllegalStateException(sm.getString("streamStateMachine.debug.change",
+ stream.getConnectionId(), stream.getIdentifier(), state));
+ }
+ if (state.canReset()) {
+ stateChange(state, State.CLOSED_RST_TX);
+ }
+ }
+
+
+ final synchronized void receivedReset() {
+ stateChange(state, State.CLOSED_RST_RX);
+ }
+
+
+ private void stateChange(State oldState, State newState) {
+ if (state == oldState) {
+ state = newState;
+ if (log.isDebugEnabled()) {
+ log.debug(sm.getString("streamStateMachine.debug.change", stream.getConnectionId(),
+ stream.getIdentifier(), oldState, newState));
+ }
+ }
+ }
+
+
+ public synchronized void checkFrameType(FrameType frameType) throws Http2Exception {
+ // No state change. Checks that receiving the frame type is valid for
+ // the current state of this stream.
+ if (!isFrameTypePermitted(frameType)) {
+ if (state.connectionErrorForInvalidFrame) {
+ throw new ConnectionException(sm.getString("streamStateMachine.invalidFrame",
+ stream.getConnectionId(), stream.getIdentifier(), state, frameType),
+ state.errorCodeForInvalidFrame);
+ } else {
+ throw new StreamException(sm.getString("streamStateMachine.invalidFrame",
+ stream.getConnectionId(), stream.getIdentifier(), state, frameType),
+ state.errorCodeForInvalidFrame, stream.getIdentifier().intValue());
+ }
+ }
+ }
+
+
+ public synchronized boolean isFrameTypePermitted(FrameType frameType) {
+ return state.isFrameTypePermitted(frameType);
+ }
+
+
+ public synchronized boolean isActive() {
+ return state.isActive();
+ }
+
+
+ public synchronized boolean canRead() {
+ return state.canRead();
+ }
+
+
+ public synchronized boolean canWrite() {
+ return state.canWrite();
+ }
+
+
+ public synchronized boolean isClosedFinal() {
+ return state == State.CLOSED_FINAL;
+ }
+
+ public synchronized void closeIfIdle() {
+ stateChange(State.IDLE, State.CLOSED_FINAL);
+ }
+
+
+ private enum State {
+ IDLE (false, false, false, true,
+ Http2Error.PROTOCOL_ERROR, FrameType.HEADERS,
+ FrameType.PRIORITY),
+ OPEN (true, true, true, true,
+ Http2Error.PROTOCOL_ERROR, FrameType.DATA,
+ FrameType.HEADERS,
+ FrameType.PRIORITY,
+ FrameType.RST,
+ FrameType.PUSH_PROMISE,
+ FrameType.WINDOW_UPDATE),
+ RESERVED_LOCAL (false, false, true, true,
+ Http2Error.PROTOCOL_ERROR, FrameType.PRIORITY,
+ FrameType.RST,
+ FrameType.WINDOW_UPDATE),
+ RESERVED_REMOTE (false, false, true, true,
+ Http2Error.PROTOCOL_ERROR, FrameType.HEADERS,
+ FrameType.PRIORITY,
+ FrameType.RST),
+ HALF_CLOSED_LOCAL (true, false, true, true,
+ Http2Error.PROTOCOL_ERROR, FrameType.DATA,
+ FrameType.HEADERS,
+ FrameType.PRIORITY,
+ FrameType.RST,
+ FrameType.PUSH_PROMISE,
+ FrameType.WINDOW_UPDATE),
+ HALF_CLOSED_REMOTE (false, true, true, true,
+ Http2Error.STREAM_CLOSED, FrameType.PRIORITY,
+ FrameType.RST,
+ FrameType.WINDOW_UPDATE),
+ CLOSED_RX (false, false, false, true,
+ Http2Error.STREAM_CLOSED, FrameType.PRIORITY),
+ CLOSED_TX (false, false, false, true,
+ Http2Error.STREAM_CLOSED, FrameType.PRIORITY,
+ FrameType.RST,
+ FrameType.WINDOW_UPDATE),
+ CLOSED_RST_RX (false, false, false, false,
+ Http2Error.STREAM_CLOSED, FrameType.PRIORITY),
+ CLOSED_RST_TX (false, false, false, false,
+ Http2Error.STREAM_CLOSED, FrameType.DATA,
+ FrameType.HEADERS,
+ FrameType.PRIORITY,
+ FrameType.RST,
+ FrameType.PUSH_PROMISE,
+ FrameType.WINDOW_UPDATE),
+ CLOSED_FINAL (false, false, false, true,
+ Http2Error.PROTOCOL_ERROR, FrameType.PRIORITY);
+
+ private final boolean canRead;
+ private final boolean canWrite;
+ private final boolean canReset;
+ private final boolean connectionErrorForInvalidFrame;
+ private final Http2Error errorCodeForInvalidFrame;
+ private final Set<FrameType> frameTypesPermitted = new HashSet<>();
+
+ private State(boolean canRead, boolean canWrite, boolean canReset,
+ boolean connectionErrorForInvalidFrame, Http2Error errorCode,
+ FrameType... frameTypes) {
+ this.canRead = canRead;
+ this.canWrite = canWrite;
+ this.canReset = canReset;
+ this.connectionErrorForInvalidFrame = connectionErrorForInvalidFrame;
+ this.errorCodeForInvalidFrame = errorCode;
+ for (FrameType frameType : frameTypes) {
+ frameTypesPermitted.add(frameType);
+ }
+ }
+
+ public boolean isActive() {
+ return canWrite || canRead;
+ }
+
+ public boolean canRead() {
+ return canRead;
+ }
+
+ public boolean canWrite() {
+ return canWrite;
+ }
+
+ public boolean canReset() {
+ return canReset;
+ }
+
+ public boolean isFrameTypePermitted(FrameType frameType) {
+ return frameTypesPermitted.contains(frameType);
+ }
+ }
+}
diff --git a/java/org/apache/el/lang/ExpressionBuilder.java b/java/org/apache/el/lang/ExpressionBuilder.java
index 1f07f5c..cde34a6 100644
--- a/java/org/apache/el/lang/ExpressionBuilder.java
+++ b/java/org/apache/el/lang/ExpressionBuilder.java
@@ -14,7 +14,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
package org.apache.el.lang;
import java.io.StringReader;
@@ -49,6 +48,8 @@ import org.apache.el.util.MessageFactory;
*/
public final class ExpressionBuilder implements NodeVisitor {
+ private static final SynchronizedStack<ELParser> parserCache = new SynchronizedStack<>();
+
private static final int CACHE_SIZE;
private static final String CACHE_SIZE_PROP =
"org.apache.el.ExpressionBuilder.CACHE_SIZE";
@@ -70,7 +71,7 @@ public final class ExpressionBuilder implements NodeVisitor {
CACHE_SIZE = Integer.parseInt(cacheSizeStr);
}
- private static final ConcurrentCache<String, Node> cache =
+ private static final ConcurrentCache<String, Node> expressionCache =
new ConcurrentCache<>(CACHE_SIZE);
private FunctionMapper fnMapper;
@@ -105,11 +106,16 @@ public final class ExpressionBuilder implements NodeVisitor {
throw new ELException(MessageFactory.get("error.null"));
}
- Node n = cache.get(expr);
+ Node n = expressionCache.get(expr);
if (n == null) {
+ ELParser parser = parserCache.pop();
try {
- n = (new ELParser(new StringReader(expr)))
- .CompositeExpression();
+ if (parser == null) {
+ parser = new ELParser(new StringReader(expr));
+ } else {
+ parser.ReInit(new StringReader(expr));
+ }
+ n = parser.CompositeExpression();
// validate composite expression
int numChildren = n.jjtGetNumChildren();
@@ -137,10 +143,14 @@ public final class ExpressionBuilder implements NodeVisitor {
|| n instanceof AstDynamicExpression) {
n = n.jjtGetChild(0);
}
- cache.put(expr, n);
+ expressionCache.put(expr, n);
} catch (Exception e) {
throw new ELException(
MessageFactory.get("error.parseFail", expr), e);
+ } finally {
+ if (parser != null) {
+ parserCache.push(parser);
+ }
}
}
return n;
@@ -252,4 +262,76 @@ public final class ExpressionBuilder implements NodeVisitor {
+ expression);
}
}
+
+ /*
+ * Copied from org.apache.tomcat.util.collections.SynchronizedStack since
+ * we don't want the EL implementation to depend on the JAR where that
+ * class resides.
+ */
+ private static class SynchronizedStack<T> {
+
+ public static final int DEFAULT_SIZE = 128;
+ private static final int DEFAULT_LIMIT = -1;
+
+ private int size;
+ private final int limit;
+
+ /*
+ * Points to the next available object in the stack
+ */
+ private int index = -1;
+
+ private Object[] stack;
+
+
+ public SynchronizedStack() {
+ this(DEFAULT_SIZE, DEFAULT_LIMIT);
+ }
+
+ public SynchronizedStack(int size, int limit) {
+ this.size = size;
+ this.limit = limit;
+ stack = new Object[size];
+ }
+
+
+ public synchronized boolean push(T obj) {
+ index++;
+ if (index == size) {
+ if (limit == -1 || size < limit) {
+ expand();
+ } else {
+ index--;
+ return false;
+ }
+ }
+ stack[index] = obj;
+ return true;
+ }
+
+ @SuppressWarnings("unchecked")
+ public synchronized T pop() {
+ if (index == -1) {
+ return null;
+ }
+ T result = (T) stack[index];
+ stack[index--] = null;
+ return result;
+ }
+
+ private void expand() {
+ int newSize = size * 2;
+ if (limit != -1 && newSize > limit) {
+ newSize = limit;
+ }
+ Object[] newStack = new Object[newSize];
+ System.arraycopy(stack, 0, newStack, 0, size);
+ // This is the only point where garbage is created by throwing away the
+ // old array. Note it is only the array, not the contents, that becomes
+ // garbage.
+ stack = newStack;
+ size = newSize;
+ }
+ }
+
}
diff --git a/java/org/apache/el/util/ReflectionUtil.java b/java/org/apache/el/util/ReflectionUtil.java
index 7792b0b..0b0ff5b 100644
--- a/java/org/apache/el/util/ReflectionUtil.java
+++ b/java/org/apache/el/util/ReflectionUtil.java
@@ -118,7 +118,7 @@ public class ReflectionUtil {
* @param paramTypes the parameter types to use
* @param paramValues the parameter values
* @return the method specified
- * @throws MethodNotFoundException If a method can not be found that matches
+ * @throws MethodNotFoundException If a method cannot be found that matches
* the given criteria
*/
/*
diff --git a/java/org/apache/jasper/Constants.java b/java/org/apache/jasper/Constants.java
index 3e3d00a..ebb5fa0 100644
--- a/java/org/apache/jasper/Constants.java
+++ b/java/org/apache/jasper/Constants.java
@@ -65,19 +65,6 @@ public class Constants {
System.getProperty("org.apache.jasper.Constants.SERVLET_CLASSPATH", "org.apache.catalina.jsp_classpath");
/**
- * Request attribute for <code><jsp-file></code> element of a
- * servlet definition. If present on a request, this overrides the
- * value returned by <code>request.getServletPath()</code> to select
- * the JSP page to be executed.
- * @deprecated This will be removed in Tomcat 8.5.x onwards. It is replaced
- * by the use of the jspFile servlet initialisation parameter
- */
- @Deprecated
- public static final String JSP_FILE =
- System.getProperty("org.apache.jasper.Constants.JSP_FILE", "org.apache.catalina.jsp_file");
-
-
- /**
* Default size of the JSP buffer.
*/
public static final int DEFAULT_BUFFER_SIZE = 8 * 1024;
diff --git a/java/org/apache/jasper/EmbeddedServletOptions.java b/java/org/apache/jasper/EmbeddedServletOptions.java
index 9dfbd1f..a553199 100644
--- a/java/org/apache/jasper/EmbeddedServletOptions.java
+++ b/java/org/apache/jasper/EmbeddedServletOptions.java
@@ -200,18 +200,10 @@ public final class EmbeddedServletOptions implements Options {
private int jspIdleTimeout = -1;
/**
- * System property that controls if the strict quoting rules are applied
- * when parsing attribute values that use scriptlet expressions (<%=...%>).
- */
- private static final boolean STRICT_QUOTE_ESCAPING_DEFAULT= Boolean.parseBoolean(
- System.getProperty(
- "org.apache.jasper.compiler.Parser.STRICT_QUOTE_ESCAPING",
- "true"));
- /**
* Should JSP.1.6 be applied strictly to attributes defined using scriptlet
* expressions?
*/
- private boolean strictQuoteEscaping = STRICT_QUOTE_ESCAPING_DEFAULT;
+ private boolean strictQuoteEscaping = true;
/**
* When EL is used in JSP attribute values, should the rules for quoting of
@@ -479,6 +471,8 @@ public final class EmbeddedServletOptions implements Options {
/**
* 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) {
@@ -787,8 +781,7 @@ public final class EmbeddedServletOptions implements Options {
this.strictQuoteEscaping = false;
} else {
if (log.isWarnEnabled()) {
- log.warn(Localizer.getMessage("jsp.warning.strictQuoteEscaping",
- Boolean.toString(STRICT_QUOTE_ESCAPING_DEFAULT)));
+ log.warn(Localizer.getMessage("jsp.warning.strictQuoteEscaping"));
}
}
}
diff --git a/java/org/apache/jasper/JasperException.java b/java/org/apache/jasper/JasperException.java
index 6fdefce..2d37eb3 100644
--- a/java/org/apache/jasper/JasperException.java
+++ b/java/org/apache/jasper/JasperException.java
@@ -33,16 +33,19 @@ public class JasperException extends javax.servlet.ServletException {
/**
* Creates a JasperException with the embedded exception and the reason for
- * throwing a JasperException
+ * throwing a JasperException.
+ * @param reason The exception message
+ * @param exception The root cause
*/
- public JasperException (String reason, Throwable exception) {
+ public JasperException(String reason, Throwable exception) {
super(reason, exception);
}
/**
- * Creates a JasperException with the embedded exception
+ * Creates a JasperException with the embedded exception.
+ * @param exception The root cause
*/
- public JasperException (Throwable exception) {
+ public JasperException(Throwable exception) {
super(exception);
}
}
diff --git a/java/org/apache/jasper/JspC.java b/java/org/apache/jasper/JspC.java
index 773b161..b24edb8 100644
--- a/java/org/apache/jasper/JspC.java
+++ b/java/org/apache/jasper/JspC.java
@@ -107,15 +107,6 @@ public class JspC extends Task implements Options {
// Logger
private static final Log log = LogFactory.getLog(JspC.class);
- /**
- * System property that controls if the strict quoting rules are applied
- * when parsing attribute values that use scriptlet expressions (<%=...%>).
- */
- private static final boolean STRICT_QUOTE_ESCAPING_DEFAULT= Boolean.parseBoolean(
- System.getProperty(
- "org.apache.jasper.compiler.Parser.STRICT_QUOTE_ESCAPING",
- "true"));
-
protected static final String SWITCH_VERBOSE = "-v";
protected static final String SWITCH_HELP = "-help";
protected static final String SWITCH_OUTPUT_DIR = "-d";
@@ -144,9 +135,7 @@ public class JspC extends Task implements Options {
protected static final String SWITCH_DUMP_SMAP = "-dumpsmap";
protected static final String SWITCH_VALIDATE_TLD = "-validateTld";
protected static final String SWITCH_VALIDATE_XML = "-validateXml";
- protected static final String SWITCH_BLOCK_EXTERNAL = "-blockExternal";
protected static final String SWITCH_NO_BLOCK_EXTERNAL = "-no-blockExternal";
- protected static final String SWITCH_STRICT_QUOTE_ESCAPING = "-strictQuoteEscaping";
protected static final String SWITCH_NO_STRICT_QUOTE_ESCAPING = "-no-strictQuoteEscaping";
protected static final String SWITCH_QUOTE_ATTRIBUTE_EL = "-quoteAttributeEL";
protected static final String SWITCH_NO_QUOTE_ATTRIBUTE_EL = "-no-quoteAttributeEL";
@@ -183,7 +172,7 @@ public class JspC extends Task implements Options {
protected boolean validateTld;
protected boolean validateXml;
protected boolean blockExternal = true;
- protected boolean strictQuoteEscaping = STRICT_QUOTE_ESCAPING_DEFAULT;
+ protected boolean strictQuoteEscaping = true;
protected boolean quoteAttributeEL = true;
protected boolean xpoweredBy;
protected boolean mappedFile = false;
@@ -241,7 +230,7 @@ public class JspC extends Task implements Options {
// Generation of web.xml fragments
protected String webxmlFile;
protected int webxmlLevel;
- protected String webxmlEncoding;
+ protected String webxmlEncoding = "UTF-8";
protected boolean addWebXmlMappings = false;
protected Writer mapout;
@@ -304,9 +293,8 @@ public class JspC extends Task implements Options {
/**
* Apply command-line arguments.
- *
- * @param arg
- * The arguments
+ * @param arg The arguments
+ * @throws JasperException JSPC error
*/
public void setArgs(String[] arg) throws JasperException {
args = arg;
@@ -397,12 +385,8 @@ public class JspC extends Task implements Options {
setValidateTld(true);
} else if (tok.equals(SWITCH_VALIDATE_XML)) {
setValidateXml(true);
- } else if (tok.equals(SWITCH_BLOCK_EXTERNAL)) {
- setBlockExternal(true);
} else if (tok.equals(SWITCH_NO_BLOCK_EXTERNAL)) {
setBlockExternal(false);
- } else if (tok.equals(SWITCH_STRICT_QUOTE_ESCAPING)) {
- setStrictQuoteEscaping(true);
} else if (tok.equals(SWITCH_NO_STRICT_QUOTE_ESCAPING)) {
setStrictQuoteEscaping(false);
} else if (tok.equals(SWITCH_QUOTE_ATTRIBUTE_EL)) {
@@ -452,6 +436,7 @@ public class JspC extends Task implements Options {
/**
* Sets the option to trim white spaces between directives or actions.
+ * @param ts New value
*/
public void setTrimSpaces(boolean ts) {
this.trimSpaces = ts;
@@ -467,6 +452,7 @@ public class JspC extends Task implements Options {
/**
* Sets the option to enable the tag handler pooling.
+ * @param poolingEnabled New value
*/
public void setPoolingEnabled(boolean poolingEnabled) {
this.poolingEnabled = poolingEnabled;
@@ -482,6 +468,7 @@ public class JspC extends Task implements Options {
/**
* Sets the option to enable generation of X-Powered-By response header.
+ * @param xpoweredBy New value
*/
public void setXpoweredBy(boolean xpoweredBy) {
this.xpoweredBy = xpoweredBy;
@@ -517,6 +504,7 @@ public class JspC extends Task implements Options {
/**
* 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) {
errorOnUseBeanInvalidClassAttribute = b;
@@ -536,6 +524,7 @@ public class JspC extends Task implements Options {
/**
* Sets the option to include debug information in compiled class.
+ * @param b New value
*/
public void setClassDebugInfo( boolean b ) {
classDebugInfo=b;
@@ -560,6 +549,7 @@ public class JspC extends Task implements Options {
/**
* Sets the option to enable caching.
+ * @param caching New value
*
* @see Options#isCaching()
*/
@@ -623,6 +613,7 @@ public class JspC extends Task implements Options {
/**
* Sets smapSuppressed flag.
+ * @param smapSuppressed New value
*/
public void setSmapSuppressed(boolean smapSuppressed) {
this.smapSuppressed = smapSuppressed;
@@ -638,6 +629,7 @@ public class JspC extends Task implements Options {
/**
* Sets smapDumped flag.
+ * @param smapDumped New value
*
* @see Options#isSmapDumped()
*/
@@ -702,6 +694,7 @@ public class JspC extends Task implements Options {
/**
* Sets the option to determine what compiler to use.
+ * @param c New value
*
* @see Options#getCompiler()
*/
@@ -727,6 +720,7 @@ public class JspC extends Task implements Options {
/**
* Sets the compiler target VM.
+ * @param vm New value
*
* @see Options#getCompilerTargetVM()
*/
@@ -744,6 +738,7 @@ public class JspC extends Task implements Options {
/**
* Sets the compiler source VM.
+ * @param vm New value
*
* @see Options#getCompilerSourceVM()
*/
@@ -801,6 +796,7 @@ public class JspC extends Task implements Options {
/**
* Sets the classpath used while compiling the servlets generated from JSP
* files
+ * @param s New value
*/
public void setClassPath(String s) {
classPath=s;
@@ -835,6 +831,7 @@ public class JspC extends Task implements Options {
/**
* Base dir for the webapp. Used to generate class names and resolve
* includes.
+ * @param s New value
*/
public void setUriroot( String s ) {
if (s == null) {
@@ -948,6 +945,7 @@ public class JspC extends Task implements Options {
/**
* Sets the package name to be used for the generated servlet classes.
+ * @param p New value
*/
public void setPackage( String p ) {
targetPackage=p;
@@ -957,6 +955,7 @@ public class JspC extends Task implements Options {
* 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;
@@ -964,6 +963,7 @@ public class JspC extends Task implements Options {
/**
* File where we generate a web.xml fragment with the class definitions.
+ * @param s New value
*/
public void setWebXmlFragment( String s ) {
webxmlFile=resolveFile(s).getAbsolutePath();
@@ -972,6 +972,7 @@ public class JspC extends Task implements Options {
/**
* 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();
@@ -982,7 +983,7 @@ public class JspC extends Task implements Options {
* Sets the encoding to be used to read and write web.xml files.
*
* <p>
- * If not specified, defaults to the platform default encoding.
+ * If not specified, defaults to UTF-8.
* </p>
*
* @param encoding
@@ -1008,14 +1009,15 @@ public class JspC extends Task implements Options {
/**
* Sets the option that throws an exception in case of a compilation error.
+ * @param b New value
*/
public void setFailOnError(final boolean b) {
failOnError = b;
}
/**
- * Returns true if an exception will be thrown in case of a compilation
- * error.
+ * @return <code>true</code> if an exception will be thrown
+ * in case of a compilation error.
*/
public boolean getFailOnError() {
return failOnError;
@@ -1046,6 +1048,7 @@ public class JspC extends Task implements Options {
* <code>/index.jsp</code>
* @param clctxt
* Compilation context of the servlet
+ * @throws IOException An IO error occurred
*/
public void generateWebMapping( String file, JspCompilationContext clctxt )
throws IOException
@@ -1084,6 +1087,7 @@ public class JspC extends Task implements Options {
/**
* Include the generated web.xml inside the webapp's web.xml.
+ * @throws IOException An IO error occurred
*/
protected void mergeIntoWebXml() throws IOException {
@@ -1223,7 +1227,7 @@ public class JspC extends Task implements Options {
if (temp == null) {
temp = "";
}
- scratchDir = new File(new File(temp).getAbsolutePath());
+ scratchDir = new File(temp).getAbsoluteFile();
}
String jspUri=file.replace('\\','/');
@@ -1300,6 +1304,7 @@ public class JspC extends Task implements Options {
/**
* Locate all jsp files in the webapp. Used if no explicit
* jsps are specified.
+ * @param base Base path
*/
public void scanFiles( File base ) {
Stack<String> dirs = new Stack<>();
@@ -1468,7 +1473,7 @@ public class JspC extends Task implements Options {
mappingout = null;
}
if (webxmlLevel >= ALL_WEBXML) {
- mapout.write(Localizer.getMessage("jspc.webxml.header"));
+ mapout.write(Localizer.getMessage("jspc.webxml.header", webxmlEncoding));
mapout.flush();
} else if ((webxmlLevel>= INC_WEBXML) && !addWebXmlMappings) {
mapout.write(Localizer.getMessage("jspc.webinc.header"));
@@ -1547,7 +1552,7 @@ public class JspC extends Task implements Options {
/**
* 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 {
@@ -1632,6 +1637,7 @@ public class JspC extends Task implements Options {
* Find the WEB-INF dir by looking up in the directory tree.
* This is used if no explicit docbase is set, but only files.
* XXX Maybe we should require the docbase.
+ * @param f The path from which it will start looking
*/
protected void locateUriRoot( File f ) {
String tUriBase = uriBase;
diff --git a/java/org/apache/jasper/JspCompilationContext.java b/java/org/apache/jasper/JspCompilationContext.java
index 987a35c..be253b8 100644
--- a/java/org/apache/jasper/JspCompilationContext.java
+++ b/java/org/apache/jasper/JspCompilationContext.java
@@ -141,7 +141,7 @@ public class JspCompilationContext {
/** ---------- Class path and loader ---------- */
/**
- * The classpath that is passed off to the Java compiler.
+ * @return the classpath that is passed off to the Java compiler.
*/
public String getClassPath() {
if( classPath != null ) {
@@ -152,6 +152,7 @@ public class JspCompilationContext {
/**
* The classpath that is passed off to the Java compiler.
+ * @param classPath The class path to use
*/
public void setClassPath(String classPath) {
this.classPath = classPath;
@@ -160,6 +161,7 @@ public class JspCompilationContext {
/**
* 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 ) {
@@ -193,6 +195,7 @@ public class JspCompilationContext {
* 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.
+ * @return the output directory in which the generated sources are placed
*/
public String getOutputDir() {
if (outputDir == null) {
@@ -206,6 +209,7 @@ public class JspCompilationContext {
* 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 ) {
@@ -263,6 +267,8 @@ public class JspCompilationContext {
/**
* 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) {
// sometimes we get uri's massaged from File(String), so check for
@@ -277,6 +283,7 @@ public class JspCompilationContext {
/**
* 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.
*/
@@ -297,6 +304,8 @@ public class JspCompilationContext {
/**
* 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) {
if (context != null) {
@@ -310,6 +319,7 @@ public class JspCompilationContext {
* 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() {
return this.tagJar;
@@ -324,6 +334,7 @@ public class JspCompilationContext {
/**
* Just the class name (does not include package name) of the
* generated class.
+ * @return the class name
*/
public String getServletClassName() {
@@ -351,6 +362,7 @@ public class JspCompilationContext {
/**
* 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() {
return jspUri;
@@ -424,9 +436,10 @@ public class JspCompilationContext {
}
/**
- * 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 <code>true</code> 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;
@@ -440,6 +453,7 @@ public class JspCompilationContext {
* Package name for the generated class is make 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() {
if (isTagFile()) {
@@ -470,13 +484,14 @@ public class JspCompilationContext {
/**
* The package name into which the servlet class is generated.
+ * @param servletPackageName The package name to use
*/
public void setServletPackageName(String servletPackageName) {
this.basePackageName = servletPackageName;
}
/**
- * Full path name of the Java file into which the servlet is being
+ * @return Full path name of the Java file into which the servlet is being
* generated.
*/
public String getServletJavaFileName() {
@@ -487,7 +502,7 @@ public class JspCompilationContext {
}
/**
- * Get hold of the Options object for this context.
+ * @return the Options object for this context.
*/
public Options getOptions() {
return options;
@@ -502,7 +517,7 @@ public class JspCompilationContext {
}
/**
- * Path of the Java file relative to the work directory.
+ * @return the path of the Java file relative to the work directory.
*/
public String getJavaPath() {
@@ -528,7 +543,7 @@ public class JspCompilationContext {
}
/**
- * Where is the servlet being generated?
+ * @return the writer that is used to write the generated Servlet source.
*/
public ServletWriter getWriter() {
return writer;
@@ -540,7 +555,7 @@ public class JspCompilationContext {
/**
* 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.
@@ -552,7 +567,7 @@ public class JspCompilationContext {
}
/**
- * Are we keeping generated code around?
+ * @return <code>true</code> if generated code is kept.
*/
public boolean keepGenerated() {
return getOptions().getKeepGenerated();
diff --git a/java/org/apache/jasper/Options.java b/java/org/apache/jasper/Options.java
index fa89656..f31c907 100644
--- a/java/org/apache/jasper/Options.java
+++ b/java/org/apache/jasper/Options.java
@@ -38,78 +38,85 @@ 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.
+ * @return <code>true</code> to get an error
*/
public boolean getErrorOnUseBeanInvalidClassAttribute();
/**
- * Are we keeping generated code around?
+ * @return <code>true</code> to keep the generated source
*/
public boolean getKeepGenerated();
/**
- * Returns true if tag handler pooling is enabled, false otherwise.
+ * @return <code>true</code> if tag handler pooling is enabled,
+ * <code>false</code> otherwise.
*/
public boolean isPoolingEnabled();
/**
- * Are we supporting HTML mapped servlets?
+ * @return <code>true</code> if HTML mapped Servlets are supported.
*/
public boolean getMappedFile();
/**
- * Should we include debug information in compiled class?
+ * @return <code>true</code> if debug information in included
+ * in compiled classes.
*/
public boolean getClassDebugInfo();
/**
- * Background compile thread check interval in seconds
+ * @return background compile thread check interval in seconds
*/
public int getCheckInterval();
/**
- * Is Jasper being used in development mode?
+ * Main development flag, which enables detailed error reports with
+ * sources, as well automatic recompilation of JSPs and tag files.
+ * This setting should usually be <code>false</code> when running
+ * in production.
+ * @return <code>true</code> if Jasper is in development mode
*/
public boolean getDevelopment();
/**
- * Should we include a source fragment in exception messages, which could be displayed
- * to the developer ?
+ * @return <code>true</code> to include a source fragment in exception
+ * messages.
*/
public boolean getDisplaySourceFragment();
/**
- * Is the generation of SMAP info for JSR45 debugging suppressed?
+ * @return <code>true</code> to suppress generation of SMAP info for
+ * JSR45 debugging.
*/
public boolean isSmapSuppressed();
/**
- * Indicates whether SMAP info for JSR45 debugging should be dumped to a
+ * This setting is ignored if suppressSmap() is <code>true</code>.
+ * @return <code>true</code> to write SMAP info for JSR45 debugging to a
* file.
- * Ignored if suppressSmap() is true.
*/
public boolean isSmapDumped();
/**
- * Should white spaces between directives or actions be trimmed?
+ * @return <code>true</code> to trim white spaces between
+ * directives or actions.
*/
public boolean getTrimSpaces();
/**
* Gets the class-id value that is sent to Internet Explorer when using
* <jsp:plugin> tags.
- *
* @return Class-id value
*/
public String getIeClassId();
/**
- * What is my scratch dir?
+ * @return the work folder
*/
public File getScratchDir();
/**
- * What classpath should I use while compiling the servlets
- * generated from JSP files?
+ * @return the classpath used to compile generated Servlets
*/
public String getClassPath();
@@ -122,21 +129,22 @@ public interface Options {
* <code>javac</code> 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
*/
public String getCompiler();
/**
- * The compiler target VM, e.g. 1.1, 1.2, 1.3, 1.4, 1.5 or 1.6.
+ * @return the compiler target VM, e.g. 1.8.
*/
public String getCompilerTargetVM();
/**
- * The compiler source VM, e.g. 1.3, 1.4, 1.5 or 1.6.
+ * @return the compiler source VM, e.g. 1.8.
*/
public String getCompilerSourceVM();
/**
- * Jasper Java compiler class to use.
+ * @return Jasper Java compiler class to use.
*/
public String getCompilerClassName();
@@ -148,13 +156,12 @@ public interface Options {
* of a taglib deployed in a jar file (WEB-INF/lib).
*
* @return the instance of the TldLocationsCache
- * for the web-application.
+ * for the web-application.
*/
public TldCache getTldCache();
/**
- * Java platform encoding to generate the JSP
- * page servlet.
+ * @return Java platform encoding to generate the JSP page servlet.
*/
public String getJavaEncoding();
@@ -164,21 +171,22 @@ public interface Options {
* <p>
* Is used only when Jasper uses an external java compiler (wrapped through
* a <code>javac</code> Apache Ant task).
+ * @return <code>true</code> to fork a process during compilation
*/
public boolean getFork();
/**
- * Obtain JSP configuration information specified in web.xml.
+ * @return JSP configuration information specified in web.xml.
*/
public JspConfig getJspConfig();
/**
- * Is generation of X-Powered-By response header enabled/disabled?
+ * @return <code>true</code> to generate a X-Powered-By response header.
*/
public boolean isXpoweredBy();
/**
- * Obtain a Tag Plugin Manager
+ * @return a Tag Plugin Manager
*/
public TagPluginManager getTagPluginManager();
@@ -191,18 +199,19 @@ public interface Options {
public boolean genStringAsCharArray();
/**
- * Modification test interval.
+ * @return modification test interval.
*/
public int getModificationTestInterval();
/**
- * Re-compile on failure.
+ * @return <code>true</code> if re-compile will occur on a failure.
*/
public boolean getRecompileOnFail();
/**
- * Is caching enabled (used for precompilation).
+ * @return <code>true</code> is caching is enabled
+ * (used for precompilation).
*/
public boolean isCaching();
@@ -223,11 +232,12 @@ public interface Options {
* 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
*/
public int getMaxLoadedJsps();
/**
- * The idle time in seconds after which a JSP is 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.
*/
public int getJspIdleTimeout();
diff --git a/java/org/apache/jasper/compiler/AntCompiler.java b/java/org/apache/jasper/compiler/AntCompiler.java
index a7deeea..b9c7ad4 100644
--- a/java/org/apache/jasper/compiler/AntCompiler.java
+++ b/java/org/apache/jasper/compiler/AntCompiler.java
@@ -312,6 +312,7 @@ public class AntCompiler extends Compiler {
/**
* Construct the handler to capture the output of the given steam.
+ * @param wrapped The wrapped stream
*/
public SystemLogHandler(PrintStream wrapped) {
super(wrapped);
@@ -356,6 +357,7 @@ public class AntCompiler extends Compiler {
/**
* Stop capturing thread's output and return captured data as a String.
+ * @return the captured output
*/
public static String unsetThread() {
ByteArrayOutputStream baos = data.get();
@@ -373,6 +375,7 @@ public class AntCompiler extends Compiler {
/**
* Find PrintStream to which the output must be written to.
+ * @return the current stream
*/
protected PrintStream findStream() {
PrintStream ps = streams.get();
diff --git a/java/org/apache/jasper/compiler/BeanRepository.java b/java/org/apache/jasper/compiler/BeanRepository.java
index 9b4b45f..1a28fdd 100644
--- a/java/org/apache/jasper/compiler/BeanRepository.java
+++ b/java/org/apache/jasper/compiler/BeanRepository.java
@@ -35,6 +35,8 @@ public class BeanRepository {
/**
* Constructor.
+ * @param loader The class loader
+ * @param err The error dispatcher that will be used to report errors
*/
public BeanRepository(ClassLoader loader, ErrorDispatcher err) {
this.loader = loader;
diff --git a/java/org/apache/jasper/compiler/Compiler.java b/java/org/apache/jasper/compiler/Compiler.java
index 51e87cc..2da20fb 100644
--- a/java/org/apache/jasper/compiler/Compiler.java
+++ b/java/org/apache/jasper/compiler/Compiler.java
@@ -85,6 +85,7 @@ public abstract class Compiler {
* return null. Used in development mode for generating detailed error
* messages. http://bz.apache.org/bugzilla/show_bug.cgi?id=37062.
* </p>
+ * @return the page nodes
*/
public Node.Nodes getPageNodes() {
return this.pageNodes;
@@ -95,6 +96,7 @@ public abstract class Compiler {
*
* @return a smap for the current JSP page, if one is generated, null
* otherwise
+ * @throws Exception Error generating Java source
*/
protected String[] generateJava() throws Exception {
@@ -310,13 +312,21 @@ public abstract class Compiler {
}
/**
- * Compile the servlet from .java file to .class file
+ * Servlet compilation. This compiles the generated sources into
+ * Servlets.
+ * @param smap The SMAP files for source debugging
+ * @throws FileNotFoundException Source files not found
+ * @throws JasperException Compilation error
+ * @throws Exception Some other error
*/
protected abstract void generateClass(String[] smap)
throws FileNotFoundException, JasperException, Exception;
/**
- * Compile the jsp file from the current engine context
+ * Compile the jsp file from the current engine context.
+ * @throws FileNotFoundException Source files not found
+ * @throws JasperException Compilation error
+ * @throws Exception Some other error
*/
public void compile() throws FileNotFoundException, JasperException,
Exception {
@@ -330,6 +340,9 @@ public abstract class Compiler {
* @param compileClass
* If true, generate both .java and .class file If false,
* generate only .java file
+ * @throws FileNotFoundException Source files not found
+ * @throws JasperException Compilation error
+ * @throws Exception Some other error
*/
public void compile(boolean compileClass) throws FileNotFoundException,
JasperException, Exception {
@@ -345,6 +358,9 @@ public abstract class Compiler {
* generate only .java file
* @param jspcMode
* true if invoked from JspC, false otherwise
+ * @throws FileNotFoundException Source files not found
+ * @throws JasperException Compilation error
+ * @throws Exception Some other error
*/
public void compile(boolean compileClass, boolean jspcMode)
throws FileNotFoundException, JasperException, Exception {
@@ -400,6 +416,8 @@ public abstract class Compiler {
/**
* This is a protected method intended to be overridden by subclasses of
* Compiler. This is used by the compile method to do all the compilation.
+ * @return <code>true</code> if the source generation and compilation
+ * should occur
*/
public boolean isOutDated() {
return isOutDated(true);
@@ -414,6 +432,8 @@ public abstract class Compiler {
* @param checkClass
* If true, check against .class file, if false, check against
* .java file.
+ * @return <code>true</code> if the source generation and compilation
+ * should occur
*/
public boolean isOutDated(boolean checkClass) {
@@ -428,29 +448,30 @@ public abstract class Compiler {
jsw.setLastModificationTest(System.currentTimeMillis());
}
- Long jspRealLastModified = ctxt.getLastModified(ctxt.getJspFile());
- if (jspRealLastModified.longValue() < 0) {
- // Something went wrong - assume modification
- return true;
- }
-
- long targetLastModified = 0;
+ // Test the target file first. Unless there is an error checking the
+ // last modified time of the source (unlikely) the target is going to
+ // have to be checked anyway. If the target doesn't exist (likely during
+ // startup) this saves an unnecessary check of the source.
File targetFile;
-
if (checkClass) {
targetFile = new File(ctxt.getClassFileName());
} else {
targetFile = new File(ctxt.getServletJavaFileName());
}
-
if (!targetFile.exists()) {
return true;
}
-
- targetLastModified = targetFile.lastModified();
+ long targetLastModified = targetFile.lastModified();
if (checkClass && jsw != null) {
jsw.setServletClassLastModifiedTime(targetLastModified);
}
+
+ Long jspRealLastModified = ctxt.getLastModified(ctxt.getJspFile());
+ if (jspRealLastModified.longValue() < 0) {
+ // Something went wrong - assume modification
+ return true;
+ }
+
if (targetLastModified != jspRealLastModified.longValue()) {
if (log.isDebugEnabled()) {
log.debug("Compiler: outdated: " + targetFile + " "
@@ -519,14 +540,14 @@ public abstract class Compiler {
}
/**
- * Gets the error dispatcher.
+ * @return the error dispatcher.
*/
public ErrorDispatcher getErrorDispatcher() {
return errDispatcher;
}
/**
- * Gets the info about the page under compilation
+ * @return the info about the page under compilation
*/
public PageInfo getPageInfo() {
return pageInfo;
diff --git a/java/org/apache/jasper/compiler/ELFunctionMapper.java b/java/org/apache/jasper/compiler/ELFunctionMapper.java
index fa28fd8..8271783 100644
--- a/java/org/apache/jasper/compiler/ELFunctionMapper.java
+++ b/java/org/apache/jasper/compiler/ELFunctionMapper.java
@@ -49,6 +49,7 @@ public class ELFunctionMapper {
* Creates the functions mappers for all EL expressions in the JSP page.
*
* @param page The current compilation unit.
+ * @throws JasperException EL error
*/
public static void map(Node.Nodes page)
throws JasperException {
diff --git a/java/org/apache/jasper/compiler/ELInterpreter.java b/java/org/apache/jasper/compiler/ELInterpreter.java
index 9f088bc..377cf6d 100644
--- a/java/org/apache/jasper/compiler/ELInterpreter.java
+++ b/java/org/apache/jasper/compiler/ELInterpreter.java
@@ -33,7 +33,8 @@ public interface ELInterpreter {
* String, Class, javax.servlet.jsp.PageContext,
* org.apache.jasper.runtime.ProtectedFunctionMapper)} but other
* implementations may produce more optimised code.
- *
+ * @param context The compilation context
+ * @param isTagFile <code>true</code> if in 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.
diff --git a/java/org/apache/jasper/compiler/ELInterpreterFactory.java b/java/org/apache/jasper/compiler/ELInterpreterFactory.java
index b4cfaec..367566b 100644
--- a/java/org/apache/jasper/compiler/ELInterpreterFactory.java
+++ b/java/org/apache/jasper/compiler/ELInterpreterFactory.java
@@ -43,6 +43,9 @@ public class ELInterpreterFactory {
/**
* Obtain the correct EL Interpreter for the given web application.
+ * @param context The Servlet context
+ * @return the EL interpreter
+ * @throws Exception If an error occurs creating the interpreter
*/
public static ELInterpreter getELInterpreter(ServletContext context)
throws Exception {
diff --git a/java/org/apache/jasper/compiler/ELNode.java b/java/org/apache/jasper/compiler/ELNode.java
index ea9984c..baad450 100644
--- a/java/org/apache/jasper/compiler/ELNode.java
+++ b/java/org/apache/jasper/compiler/ELNode.java
@@ -38,10 +38,6 @@ abstract class ELNode {
public abstract void accept(Visitor v) throws JasperException;
- /**
- * Child classes
- */
-
/**
* Represents an EL expression: anything in ${ and }.
@@ -204,8 +200,11 @@ abstract class ELNode {
}
/**
- * Visit the nodes in the list with the supplied visitor
+ * Visit the nodes in the list with the supplied visitor.
+ *
* @param v The visitor used
+ *
+ * @throws JasperException if an error occurs while visiting a node
*/
public void visit(Visitor v) throws JasperException {
Iterator<ELNode> iter = list.iterator();
diff --git a/java/org/apache/jasper/compiler/ELParser.java b/java/org/apache/jasper/compiler/ELParser.java
index 9dbd1e5..ced5dc2 100644
--- a/java/org/apache/jasper/compiler/ELParser.java
+++ b/java/org/apache/jasper/compiler/ELParser.java
@@ -96,7 +96,7 @@ public class ELParser {
*
* @return An ELNode.Nodes representing the EL expression
*
- * Note: This can not be refactored to use the standard EL implementation as
+ * Note: This cannot be refactored to use the standard EL implementation as
* the EL API does not provide the level of access required to the
* parsed expression.
*/
diff --git a/java/org/apache/jasper/compiler/ErrorDispatcher.java b/java/org/apache/jasper/compiler/ErrorDispatcher.java
index 565b764..f9bf3b3 100644
--- a/java/org/apache/jasper/compiler/ErrorDispatcher.java
+++ b/java/org/apache/jasper/compiler/ErrorDispatcher.java
@@ -42,14 +42,18 @@ import org.xml.sax.SAXException;
*/
public class ErrorDispatcher {
- // Custom error handler
+ /**
+ * Custom error handler
+ */
private final ErrorHandler errHandler;
- // Indicates whether the compilation was initiated by JspServlet or JspC
+ /**
+ * Indicates whether the compilation was initiated by JspServlet or JspC
+ */
private final boolean jspcMode;
- /*
+ /**
* Constructor.
*
* @param jspcMode true if compilation has been initiated by JspC, false
@@ -61,7 +65,7 @@ public class ErrorDispatcher {
this.jspcMode = jspcMode;
}
- /*
+ /**
* Dispatches the given JSP parse error to the configured error handler.
*
* The given error code is localized. If it is not found in the
@@ -70,12 +74,13 @@ public class ErrorDispatcher {
*
* @param errCode Error code
* @param args Arguments for parametric replacement
+ * @throws JasperException An error occurred
*/
public void jspError(String errCode, String... args) throws JasperException {
dispatch(null, errCode, args, null);
}
- /*
+ /**
* Dispatches the given JSP parse error to the configured error handler.
*
* The given error code is localized. If it is not found in the
@@ -85,13 +90,14 @@ public class ErrorDispatcher {
* @param where Error location
* @param errCode Error code
* @param args Arguments for parametric replacement
+ * @throws JasperException An error occurred
*/
public void jspError(Mark where, String errCode, String... args)
throws JasperException {
dispatch(where, errCode, args, null);
}
- /*
+ /**
* Dispatches the given JSP parse error to the configured error handler.
*
* The given error code is localized. If it is not found in the
@@ -101,22 +107,24 @@ public class ErrorDispatcher {
* @param n Node that caused the error
* @param errCode Error code
* @param args Arguments for parametric replacement
+ * @throws JasperException An error occurred
*/
public void jspError(Node n, String errCode, String... args)
throws JasperException {
dispatch(n.getStart(), errCode, args, null);
}
- /*
+ /**
* Dispatches the given parsing exception to the configured error handler.
*
* @param e Parsing exception
+ * @throws JasperException An error occurred
*/
public void jspError(Exception e) throws JasperException {
dispatch(null, null, null, e);
}
- /*
+ /**
* Dispatches the given JSP parse error to the configured error handler.
*
* The given error code is localized. If it is not found in the
@@ -126,13 +134,14 @@ public class ErrorDispatcher {
* @param errCode Error code
* @param args Arguments for parametric replacement
* @param e Parsing exception
+ * @throws JasperException An error occurred
*/
public void jspError(Exception e, String errCode, String... args)
throws JasperException {
dispatch(null, errCode, args, e);
}
- /*
+ /**
* Dispatches the given JSP parse error to the configured error handler.
*
* The given error code is localized. If it is not found in the
@@ -140,16 +149,17 @@ public class ErrorDispatcher {
* message.
*
* @param where Error location
+ * @param e Parsing exception
* @param errCode Error code
* @param args Arguments for parametric replacement
- * @param e Parsing exception
+ * @throws JasperException An error occurred
*/
public void jspError(Mark where, Exception e, String errCode, String... args)
throws JasperException {
dispatch(where, errCode, args, e);
}
- /*
+ /**
* Dispatches the given JSP parse error to the configured error handler.
*
* The given error code is localized. If it is not found in the
@@ -157,9 +167,10 @@ public class ErrorDispatcher {
* message.
*
* @param n Node that caused the error
+ * @param e Parsing exception
* @param errCode Error code
* @param args Arguments for parametric replacement
- * @param e Parsing exception
+ * @throws JasperException An error occurred
*/
public void jspError(Node n, Exception e, String errCode, String... args)
throws JasperException {
@@ -177,6 +188,8 @@ public class ErrorDispatcher {
*
* @return Array of javac compilation errors, or null if the given error
* message does not contain any compilation error line numbers
+ * @throws JasperException An error occurred
+ * @throws IOException IO error which usually should not occur
*/
public static JavacErrorDetail[] parseJavacErrors(String errMsg,
String fname,
@@ -186,11 +199,12 @@ public class ErrorDispatcher {
return parseJavacMessage(errMsg, fname, page);
}
- /*
+ /**
* Dispatches the given javac compilation errors to the configured error
* handler.
*
* @param javacErrors Array of javac compilation errors
+ * @throws JasperException An error occurred
*/
public void javacError(JavacErrorDetail[] javacErrors)
throws JasperException {
@@ -199,12 +213,13 @@ public class ErrorDispatcher {
}
- /*
+ /**
* Dispatches the given compilation error report and exception to the
* configured error handler.
*
* @param errorReport Compilation error report
* @param e Compilation exception
+ * @throws JasperException An error occurred
*/
public void javacError(String errorReport, Exception e)
throws JasperException {
@@ -216,7 +231,7 @@ public class ErrorDispatcher {
//*********************************************************************
// Private utility methods
- /*
+ /**
* Dispatches the given JSP parse error to the configured error handler.
*
* The given error code is localized. If it is not found in the
@@ -227,6 +242,7 @@ public class ErrorDispatcher {
* @param errCode Error code
* @param args Arguments for parametric replacement
* @param e Parsing exception
+ * @throws JasperException An error occurred
*/
private void dispatch(Mark where, String errCode, Object[] args,
Exception e) throws JasperException {
@@ -278,7 +294,7 @@ public class ErrorDispatcher {
}
}
- /*
+ /**
* Parses the given Java compilation error message, which may contain one
* or more compilation errors, into an array of JavacErrorDetail instances.
*
@@ -293,6 +309,8 @@ public class ErrorDispatcher {
*
* @return Array of JavacErrorDetail instances corresponding to the
* compilation errors
+ * @throws JasperException An error occurred
+ * @throws IOException IO error which usually should not occur
*/
private static JavacErrorDetail[] parseJavacMessage(
String errMsg, String fname, Node.Nodes page)
@@ -363,12 +381,13 @@ public class ErrorDispatcher {
/**
- * @param fname
- * @param page
- * @param errMsgBuf
- * @param lineNum
+ * Create a compilation error.
+ * @param fname The file name
+ * @param page The page nodes
+ * @param errMsgBuf The error message
+ * @param lineNum The source line number of the error
* @return JavacErrorDetail The error details
- * @throws JasperException
+ * @throws JasperException An error occurred
*/
public static JavacErrorDetail createJavacError(String fname,
Node.Nodes page, StringBuilder errMsgBuf, int lineNum)
@@ -378,13 +397,14 @@ public class ErrorDispatcher {
/**
- * @param fname
- * @param page
- * @param errMsgBuf
- * @param lineNum
- * @param ctxt
+ * Create a compilation error.
+ * @param fname The file name
+ * @param page The page nodes
+ * @param errMsgBuf The error message
+ * @param lineNum The source line number of the error
+ * @param ctxt The compilation context
* @return JavacErrorDetail The error details
- * @throws JasperException
+ * @throws JasperException An error occurred
*/
public static JavacErrorDetail createJavacError(String fname,
Node.Nodes page, StringBuilder errMsgBuf, int lineNum,
@@ -439,22 +459,24 @@ public class ErrorDispatcher {
}
- /*
+ /**
* Visitor responsible for mapping a line number in the generated servlet
* source code to the corresponding JSP node.
*/
private static class ErrorVisitor extends Node.Visitor {
- // Java source line number to be mapped
+ /**
+ * Java source line number to be mapped
+ */
private final int lineNum;
- /*
+ /**
* JSP node whose Java source code range in the generated servlet
* contains the Java source line number to be mapped
*/
private Node found;
- /*
+ /**
* Constructor.
*
* @param lineNum Source line number in the generated servlet code
@@ -471,7 +493,7 @@ public class ErrorDispatcher {
}
}
- /*
+ /**
* Gets the JSP node to which the source line number in the generated
* servlet code was mapped.
*
diff --git a/java/org/apache/jasper/compiler/ErrorHandler.java b/java/org/apache/jasper/compiler/ErrorHandler.java
index 8b2f028..5d069f0 100644
--- a/java/org/apache/jasper/compiler/ErrorHandler.java
+++ b/java/org/apache/jasper/compiler/ErrorHandler.java
@@ -40,6 +40,7 @@ public interface ErrorHandler {
* @param column Parse error column number
* @param msg Parse error message
* @param exception Parse exception
+ * @throws JasperException An error occurred
*/
public void jspError(String fname, int line, int column, String msg,
Exception exception) throws JasperException;
@@ -49,6 +50,7 @@ public interface ErrorHandler {
*
* @param msg Parse error message
* @param exception Parse exception
+ * @throws JasperException An error occurred
*/
public void jspError(String msg, Exception exception)
throws JasperException;
@@ -58,6 +60,7 @@ public interface ErrorHandler {
*
* @param details Array of JavacErrorDetail instances corresponding to the
* compilation errors
+ * @throws JasperException An error occurred
*/
public void javacError(JavacErrorDetail[] details)
throws JasperException;
@@ -67,6 +70,7 @@ public interface ErrorHandler {
*
* @param errorReport Compilation error report
* @param exception Compilation exception
+ * @throws JasperException An error occurred
*/
public void javacError(String errorReport, Exception exception)
throws JasperException;
diff --git a/java/org/apache/jasper/compiler/Generator.java b/java/org/apache/jasper/compiler/Generator.java
index 7e65028..40b3630 100644
--- a/java/org/apache/jasper/compiler/Generator.java
+++ b/java/org/apache/jasper/compiler/Generator.java
@@ -768,22 +768,23 @@ class Generator {
out.printin("public void ");
out.print(serviceMethodName);
out.println("(final javax.servlet.http.HttpServletRequest request, final javax.servlet.http.HttpServletResponse response)");
- out.println(" throws java.io.IOException, javax.servlet.ServletException {");
-
out.pushIndent();
+ out.pushIndent();
+ out.printil("throws java.io.IOException, javax.servlet.ServletException {");
+ out.popIndent();
out.println();
// Method check
if (!pageInfo.isErrorPage()) {
- out.println("final java.lang.String _jspx_method = request.getMethod();");
- out.print("if (!\"GET\".equals(_jspx_method) && !\"POST\".equals(_jspx_method) && !\"HEAD\".equals(_jspx_method) && ");
+ out.printil("final java.lang.String _jspx_method = request.getMethod();");
+ out.printin("if (!\"GET\".equals(_jspx_method) && !\"POST\".equals(_jspx_method) && !\"HEAD\".equals(_jspx_method) && ");
out.println("!javax.servlet.DispatcherType.ERROR.equals(request.getDispatcherType())) {");
out.pushIndent();
- out.print("response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, ");
+ out.printin("response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, ");
out.println("\"" + Localizer.getMessage("jsp.error.servlet.invalid.method") + "\");");
- out.println("return;");
+ out.printil("return;");
out.popIndent();
- out.println("}");
+ out.printil("}");
out.println();
}
@@ -930,9 +931,7 @@ class Generator {
private HashMap<String,String> textMap;
- /**
- * Constructor.
- */
+
public GenerateVisitor(boolean isTagFile, ServletWriter out,
ArrayList<GenBuffer> methodsBuffered,
FragmentHelperClass fragmentHelperClass) {
@@ -3384,7 +3383,11 @@ class Generator {
* Generate the code required to obtain the runtime value of the given
* named attribute.
*
+ * @param n The named attribute node whose value is required
+ *
* @return The name of the temporary variable the result is stored in.
+ *
+ * @throws JasperException If an error
*/
public String generateNamedAttributeValue(Node.NamedAttribute n)
throws JasperException {
@@ -3440,6 +3443,9 @@ class Generator {
* The variable the tag handler is stored in, so the fragment
* knows its parent tag.
* @return The name of the temporary variable the fragment is stored in.
+ *
+ * @throws JasperException If an error occurs trying to generate the
+ * fragment
*/
public String generateNamedAttributeJspFragment(Node.NamedAttribute n,
String tagHandlerVar) throws JasperException {
@@ -3614,6 +3620,8 @@ class Generator {
* The compiler
* @param page
* The input page
+ *
+ * @throws JasperException If something goes wrong during generation
*/
public static void generate(ServletWriter out, Compiler compiler,
Node.Nodes page) throws JasperException {
@@ -4116,23 +4124,14 @@ class Generator {
}
}
- /**
- * XXX
- */
public Method getSetterMethod(String attrName) {
return methodMaps.get(attrName);
}
- /**
- * XXX
- */
public Class<?> getPropertyEditorClass(String attrName) {
return propertyEditorMaps.get(attrName);
}
- /**
- * XXX
- */
public Class<?> getTagHandlerClass() {
return tagHandlerClass;
}
@@ -4185,6 +4184,9 @@ class Generator {
* Adjust the Java Lines. This is necessary because the Java lines
* stored with the nodes are relative the beginning of this buffer and
* need to be adjusted when this buffer is inserted into the source.
+ *
+ * @param offset The offset to apply to the start line and end line of
+ * and Java lines of nodes in this buffer
*/
public void adjustJavaLines(final int offset) {
diff --git a/java/org/apache/jasper/compiler/ImplicitTagLibraryInfo.java b/java/org/apache/jasper/compiler/ImplicitTagLibraryInfo.java
index 6f724e2..ed8a29d 100644
--- a/java/org/apache/jasper/compiler/ImplicitTagLibraryInfo.java
+++ b/java/org/apache/jasper/compiler/ImplicitTagLibraryInfo.java
@@ -63,9 +63,7 @@ class ImplicitTagLibraryInfo extends TagLibraryInfo {
private final PageInfo pi;
private final Vector<TagFileInfo> vec;
- /**
- * Constructor.
- */
+
public ImplicitTagLibraryInfo(JspCompilationContext ctxt,
ParserController pc,
PageInfo pi,
diff --git a/java/org/apache/jasper/compiler/JDTCompiler.java b/java/org/apache/jasper/compiler/JDTCompiler.java
index 96ecc63..d1adb41 100644
--- a/java/org/apache/jasper/compiler/JDTCompiler.java
+++ b/java/org/apache/jasper/compiler/JDTCompiler.java
@@ -471,8 +471,5 @@ public class JDTCompiler extends org.apache.jasper.compiler.Compiler {
if (! options.isSmapSuppressed()) {
SmapUtil.installSmap(smap);
}
-
}
-
-
}
diff --git a/java/org/apache/jasper/compiler/JarScannerFactory.java b/java/org/apache/jasper/compiler/JarScannerFactory.java
index 29a58a2..ea0350b 100644
--- a/java/org/apache/jasper/compiler/JarScannerFactory.java
+++ b/java/org/apache/jasper/compiler/JarScannerFactory.java
@@ -34,6 +34,8 @@ public class JarScannerFactory {
/**
* Obtain the {@link JarScanner} associated with the specified {@link
* ServletContext}. It is obtained via a context parameter.
+ * @param ctxt The Servlet context
+ * @return a scanner instance
*/
public static JarScanner getJarScanner(ServletContext ctxt) {
JarScanner jarScanner =
diff --git a/java/org/apache/jasper/compiler/JspConfig.java b/java/org/apache/jasper/compiler/JspConfig.java
index 431ae76..457d232 100644
--- a/java/org/apache/jasper/compiler/JspConfig.java
+++ b/java/org/apache/jasper/compiler/JspConfig.java
@@ -383,6 +383,8 @@ public class JspConfig {
/**
* To find out if an uri matches an url pattern in jsp config. If so,
* then the uri is a JSP page. This is used primarily for jspc.
+ * @param uri The path to check
+ * @return <code>true</code> if the path denotes a JSP page
*/
public boolean isJspPage(String uri) {
diff --git a/java/org/apache/jasper/compiler/JspRuntimeContext.java b/java/org/apache/jasper/compiler/JspRuntimeContext.java
index ea6b96b..1b6e32e 100644
--- a/java/org/apache/jasper/compiler/JspRuntimeContext.java
+++ b/java/org/apache/jasper/compiler/JspRuntimeContext.java
@@ -59,15 +59,17 @@ import org.apache.juli.logging.LogFactory;
*/
public final class JspRuntimeContext {
- // Logger
+ /**
+ * Logger
+ */
private final Log log = LogFactory.getLog(JspRuntimeContext.class);
- /*
+ /**
* Counts how many times the webapp's JSPs have been reloaded.
*/
private final AtomicInteger jspReloadCount = new AtomicInteger(0);
- /*
+ /**
* Counts how many times JSPs have been unloaded in this webapp.
*/
private final AtomicInteger jspUnloadCount = new AtomicInteger(0);
@@ -80,6 +82,7 @@ public final class JspRuntimeContext {
* Loads in any previously generated dependencies from file.
*
* @param context ServletContext for web application
+ * @param options The main Jasper options
*/
public JspRuntimeContext(ServletContext context, Options options) {
@@ -374,14 +377,14 @@ public final class JspRuntimeContext {
}
/**
- * The classpath that is passed off to the Java compiler.
+ * @return the classpath that is passed off to the Java compiler.
*/
public String getClassPath() {
return classpath;
}
/**
- * Last time the update background task has run
+ * @return Last time the update background task has run
*/
public long getLastJspQueueUpdate() {
return lastJspQueueUpdate;
@@ -393,6 +396,7 @@ public final class JspRuntimeContext {
/**
* Method used to initialize classpath for compiles.
+ * @return the compilation classpath
*/
private String initClassPath() {
@@ -434,7 +438,9 @@ public final class JspRuntimeContext {
return path;
}
- // Helper class to allow initSecurity() to return two items
+ /**
+ * Helper class to allow initSecurity() to return two items
+ */
private static class SecurityHolder{
private final CodeSource cs;
private final PermissionCollection pc;
diff --git a/java/org/apache/jasper/compiler/JspUtil.java b/java/org/apache/jasper/compiler/JspUtil.java
index 8ebb8d5..1be6530 100644
--- a/java/org/apache/jasper/compiler/JspUtil.java
+++ b/java/org/apache/jasper/compiler/JspUtil.java
@@ -63,7 +63,9 @@ public class JspUtil {
public static final int CHUNKSIZE = 1024;
/**
- * Takes a potential expression and converts it into XML form
+ * 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;
@@ -108,6 +110,11 @@ public class JspUtil {
* 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
+ * @throws JasperException An error occurred
*/
public static void checkAttributes(String typeOfTag, Node n,
ValidAttribute[] validAttributes, ErrorDispatcher err)
@@ -206,6 +213,8 @@ public class JspUtil {
/**
* Escape the 5 entities defined by XML.
+ * @param s String to escape
+ * @return XML escaped string
*/
public static String escapeXml(String s) {
if (s == null) {
@@ -276,6 +285,10 @@ public class JspUtil {
* name to the <tt>Class.forName()</tt> method, unless the given string
* name represents a primitive type, in which case it is converted to a
* <tt>Class</tt> 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 {
@@ -330,6 +343,8 @@ public class JspUtil {
/**
* Produces a String representing a call to the EL interpreter.
*
+ * @param isTagFile <code>true</code> if the file is a tag file
+ * rather than a JSP
* @param expression
* a String containing zero or more "${}" expressions
* @param expectedType
@@ -683,13 +698,13 @@ public class JspUtil {
* Gets the fully-qualified class name of the tag handler corresponding to
* the given tag file path.
*
- * @param path
- * Tag file path
- * @param err
- * Error dispatcher
+ * @param path Tag file path
+ * @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
+ * @throws JasperException Failed to generate a class name for the tag
*/
public static String getTagHandlerClassName(String path, String urn,
ErrorDispatcher err) throws JasperException {
@@ -852,6 +867,8 @@ public class JspUtil {
/**
* Mangle the specified character to create a legal Java class name.
+ * @param ch The character
+ * @return the replacement charater as a string
*/
public static final String mangleChar(char ch) {
char[] result = new char[5];
@@ -864,7 +881,9 @@ public class JspUtil {
}
/**
- * Test whether the argument is a Java keyword
+ * Test whether the argument is a Java keyword.
+ * @param key The name
+ * @return <code>true</code> if the name is a java identifier
*/
public static boolean isJavaKeyword(String key) {
int i = 0;
@@ -914,7 +933,8 @@ public class JspUtil {
* 'java.lang.Object.class' 'int' -> 'int.class' 'void' -> 'Void.TYPE'
* 'int[]' -> 'int[].class'
*
- * @param type
+ * @param type The type from the TLD
+ * @return the Java type
*/
public static String toJavaSourceTypeFromTld(String type) {
if (type == null || "void".equals(type)) {
@@ -927,6 +947,8 @@ public class JspUtil {
* 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) {
diff --git a/java/org/apache/jasper/compiler/Node.java b/java/org/apache/jasper/compiler/Node.java
index 9f7a634..324c4b5 100644
--- a/java/org/apache/jasper/compiler/Node.java
+++ b/java/org/apache/jasper/compiler/Node.java
@@ -230,6 +230,10 @@ abstract class Node implements TagConstants {
/**
* Get the attribute that is non request time expression, either from the
* attribute of the node, or from a jsp:attrbute
+ *
+ * @param name The name of the attribute
+ *
+ * @return The attribute value
*/
public String getTextAttribute(String name) {
@@ -247,12 +251,15 @@ abstract class Node implements TagConstants {
}
/**
- * Searches all subnodes of this node for jsp:attribute standard actions
- * with the given name, and returns the NamedAttribute node of the matching
- * named attribute, nor null if no such node is found.
+ * Searches all sub-nodes of this node for jsp:attribute standard actions
+ * with the given name.
* <p>
* 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.
*/
public NamedAttribute getNamedAttributeNode(String name) {
NamedAttribute result = null;
@@ -518,6 +525,8 @@ abstract class Node implements TagConstants {
/**
* Generates a new temporary variable name.
+ *
+ * @return The name to use for the temporary variable
*/
public String nextTemporaryVariableName() {
if (parentRoot == null) {
@@ -1701,6 +1710,10 @@ abstract class Node implements TagConstants {
/**
* Checks to see if the attribute of the given name is of type
* JspFragment.
+ *
+ * @param name The attribute to check
+ *
+ * @return {@code true} if it is a JspFragment
*/
public boolean checkIfAttributeIsJspFragment(String name) {
boolean result = false;
@@ -1775,12 +1788,12 @@ abstract class Node implements TagConstants {
}
/**
- * Returns true if this custom action has an empty body, and false
- * otherwise.
- *
* 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.
+ *
+ * @return {@code true} if this custom action has an empty body, and
+ * {@code false} otherwise.
*/
public boolean hasEmptyBody() {
boolean hasEmptyBody = true;
@@ -2054,7 +2067,7 @@ abstract class Node implements TagConstants {
}
/**
- * Returns true if this template text contains whitespace only.
+ * @return true if this template text contains whitespace only.
*/
public boolean isAllSpace() {
boolean isAllSpace = true;
@@ -2139,11 +2152,12 @@ abstract class Node implements TagConstants {
}
/**
- * Allow node to validate itself
+ * Allow node to validate itself.
*
- * @param ef
- * @param ctx
- * @throws ELException
+ * @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 {
@@ -2307,7 +2321,7 @@ abstract class Node implements TagConstants {
}
/**
- * <code>true</code> if the attribute is a "dynamic" attribute of a
+ * @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.
*/
@@ -2368,6 +2382,8 @@ abstract class Node implements TagConstants {
*
* @param v
* The visitor used
+ *
+ * @throws JasperException if an error occurs while visiting a node
*/
public void visit(Visitor v) throws JasperException {
Iterator<Node> iter = list.iterator();
diff --git a/java/org/apache/jasper/compiler/PageDataImpl.java b/java/org/apache/jasper/compiler/PageDataImpl.java
index 6f8e661..1240622 100644
--- a/java/org/apache/jasper/compiler/PageDataImpl.java
+++ b/java/org/apache/jasper/compiler/PageDataImpl.java
@@ -58,9 +58,10 @@ class PageDataImpl extends PageData implements TagConstants {
private final StringBuilder buf;
/**
- * Constructor.
- *
* @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
*/
public PageDataImpl(Node.Nodes page, Compiler compiler)
throws JasperException {
diff --git a/java/org/apache/jasper/compiler/PageInfo.java b/java/org/apache/jasper/compiler/PageInfo.java
index 0bdea4e..0a9993c 100644
--- a/java/org/apache/jasper/compiler/PageInfo.java
+++ b/java/org/apache/jasper/compiler/PageInfo.java
@@ -57,7 +57,7 @@ class PageInfo {
private String session;
private boolean isSession = true;
private String bufferValue;
- private int buffer = 8*1024; // XXX confirm
+ private int buffer = 8*1024;
private String autoFlush;
private boolean isAutoFlush = true;
private String isThreadSafeValue;
@@ -126,8 +126,11 @@ class PageInfo {
}
/**
- * Check if the plugin ID has been previously declared. Make a not
+ * 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
+ *
* @return true if Id has been declared.
*/
public boolean isPluginDeclared(String id) {
diff --git a/java/org/apache/jasper/compiler/Parser.java b/java/org/apache/jasper/compiler/Parser.java
index bd6d8c5..ec7cc41 100644
--- a/java/org/apache/jasper/compiler/Parser.java
+++ b/java/org/apache/jasper/compiler/Parser.java
@@ -104,14 +104,20 @@ class Parser implements TagConstants {
/**
* The main entry for Parser
*
- * @param pc
- * The ParseController, use for getting other objects in compiler
+ * @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 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
* @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,
@@ -177,6 +183,13 @@ class Parser implements TagConstants {
/**
* Parse Attributes for a reader, provided for external use
+ *
+ * @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 {
diff --git a/java/org/apache/jasper/compiler/ParserController.java b/java/org/apache/jasper/compiler/ParserController.java
index 7389180..3a5cbab 100644
--- a/java/org/apache/jasper/compiler/ParserController.java
+++ b/java/org/apache/jasper/compiler/ParserController.java
@@ -88,9 +88,14 @@ class ParserController implements TagConstants {
* Parses 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 nodes
+ *
+ * @throws JasperException If an error occurs during parsing
+ * @throws IOException If an I/O error occurs such as the file not being
+ * found
*/
- public Node.Nodes parse(String inFileName)
- throws FileNotFoundException, JasperException, IOException {
+ public Node.Nodes parse(String inFileName) throws JasperException, IOException {
// If we're parsing a packaged tag file or a resource included by it
// (using an include directive), ctxt.getTagFileJar() returns the
// JAR file from which to read the tag file or included resource,
@@ -105,9 +110,14 @@ class ParserController implements TagConstants {
* 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
*/
- public Node.Nodes parseDirectives(String inFileName)
- throws FileNotFoundException, JasperException, IOException {
+ public Node.Nodes parseDirectives(String inFileName) throws JasperException, IOException {
// If we're parsing a packaged tag file or a resource included by it
// (using an include directive), ctxt.getTagFileJar() returns the
// JAR file from which to read the tag file or included resource,
@@ -125,9 +135,15 @@ class ParserController implements TagConstants {
* @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
*/
public Node.Nodes parse(String inFileName, Node parent, Jar jar)
- throws FileNotFoundException, JasperException, IOException {
+ throws JasperException, IOException {
// For files that are statically included, isTagfile and directiveOnly
// remain unchanged.
return doParse(inFileName, parent, jar);
@@ -140,9 +156,15 @@ class ParserController implements TagConstants {
*
* @param inFileName The name of the tag file to be parsed.
* @param jar The location of the tag file.
+ *
+ * @return The parsed tage 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
*/
public Node.Nodes parseTagFileDirectives(String inFileName, Jar jar)
- throws FileNotFoundException, JasperException, IOException {
+ throws JasperException, IOException {
boolean isTagFileSave = isTagFile;
boolean directiveOnlySave = directiveOnly;
isTagFile = true;
@@ -159,11 +181,6 @@ class ParserController implements TagConstants {
* @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 isTagFile true if file to be parsed is tag file, and false if it
- * is a regular JSP page
- * @param directivesOnly true if the file to be parsed is a tag file and
- * we are only interested in the directives needed for constructing a
- * TagFileInfo.
* @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
*/
diff --git a/java/org/apache/jasper/compiler/ServletWriter.java b/java/org/apache/jasper/compiler/ServletWriter.java
index cf5ac8f..b34b8cf 100644
--- a/java/org/apache/jasper/compiler/ServletWriter.java
+++ b/java/org/apache/jasper/compiler/ServletWriter.java
@@ -29,14 +29,20 @@ public class ServletWriter implements AutoCloseable {
private static final int TAB_WIDTH = 2;
private static final String SPACES = " ";
- // Current indent level:
+ /**
+ * Current indent level.
+ */
private int indent = 0;
private int virtual_indent = 0;
- // The sink writer:
+ /**
+ * The sink writer.
+ */
private final PrintWriter writer;
- // servlet line numbers start from 1
+ /**
+ * Servlet line numbers start from 1.
+ */
private int javaLine = 1;
@@ -73,6 +79,7 @@ public class ServletWriter implements AutoCloseable {
/**
* Prints the given string followed by '\n'
+ * @param s The string
*/
public void println(String s) {
javaLine++;
@@ -96,6 +103,7 @@ public class ServletWriter implements AutoCloseable {
/**
* Prints the current indention, followed by the given string
+ * @param s The string
*/
public void printin(String s) {
writer.print(SPACES.substring(0, indent));
@@ -104,6 +112,7 @@ public class ServletWriter implements AutoCloseable {
/**
* Prints the current indention, and then the string, and a '\n'.
+ * @param s The string
*/
public void printil(String s) {
javaLine++;
@@ -115,6 +124,7 @@ public class ServletWriter implements AutoCloseable {
* Prints the given char.
*
* Use println() to print a '\n'.
+ * @param c The char
*/
public void print(char c) {
writer.print(c);
@@ -122,6 +132,7 @@ public class ServletWriter implements AutoCloseable {
/**
* Prints the given int.
+ * @param i The int
*/
public void print(int i) {
writer.print(i);
@@ -132,6 +143,7 @@ public class ServletWriter implements AutoCloseable {
*
* The string must not contain any '\n', otherwise the line count will be
* off.
+ * @param s The string
*/
public void print(String s) {
writer.print(s);
@@ -142,6 +154,7 @@ public class ServletWriter implements AutoCloseable {
*
* 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;
diff --git a/java/org/apache/jasper/compiler/SmapGenerator.java b/java/org/apache/jasper/compiler/SmapGenerator.java
index 3579e7a..efe09ee 100644
--- a/java/org/apache/jasper/compiler/SmapGenerator.java
+++ b/java/org/apache/jasper/compiler/SmapGenerator.java
@@ -57,6 +57,7 @@ public class SmapGenerator {
/**
* Sets the filename (without path information) for the generated
* source file. E.g., "foo$jsp.java".
+ * @param x The file name
*/
public synchronized void setOutputFileName(String x) {
outputFileName = x;
diff --git a/java/org/apache/jasper/compiler/SmapStratum.java b/java/org/apache/jasper/compiler/SmapStratum.java
index 83707d1..ff241b4 100644
--- a/java/org/apache/jasper/compiler/SmapStratum.java
+++ b/java/org/apache/jasper/compiler/SmapStratum.java
@@ -44,14 +44,12 @@ public class SmapStratum {
private int outputLineIncrement = 1;
private boolean lineFileIDSet = false;
- /** Sets InputStartLine. */
public void setInputStartLine(int inputStartLine) {
if (inputStartLine < 0)
throw new IllegalArgumentException("" + inputStartLine);
this.inputStartLine = inputStartLine;
}
- /** Sets OutputStartLine. */
public void setOutputStartLine(int outputStartLine) {
if (outputStartLine < 0)
throw new IllegalArgumentException("" + outputStartLine);
@@ -59,11 +57,13 @@ public class SmapStratum {
}
/**
- * 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.
- * <tt>LineInfo</tt> 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.
+ * <tt>LineInfo</tt> 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("" + lineFileID);
@@ -71,14 +71,12 @@ public class SmapStratum {
this.lineFileIDSet = true;
}
- /** Sets InputLineCount. */
public void setInputLineCount(int inputLineCount) {
if (inputLineCount < 0)
throw new IllegalArgumentException("" + inputLineCount);
this.inputLineCount = inputLineCount;
}
- /** Sets OutputLineIncrement. */
public void setOutputLineIncrement(int outputLineIncrement) {
if (outputLineIncrement < 0)
throw new IllegalArgumentException("" + outputLineIncrement);
@@ -86,9 +84,9 @@ public class SmapStratum {
}
/**
- * Retrieves 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)
@@ -281,14 +279,14 @@ public class SmapStratum {
// Methods to retrieve information
/**
- * Returns the name of the stratum.
+ * @return the name of the stratum.
*/
public String getStratumName() {
return stratumName;
}
/**
- * Returns the given stratum as a String: a StratumSection,
+ * @return the given stratum as a String: a StratumSection,
* followed by at least one FileSection and at least one LineSection.
*/
public String getString() {
diff --git a/java/org/apache/jasper/compiler/SmapUtil.java b/java/org/apache/jasper/compiler/SmapUtil.java
index 185e850..0a0fe8f 100644
--- a/java/org/apache/jasper/compiler/SmapUtil.java
+++ b/java/org/apache/jasper/compiler/SmapUtil.java
@@ -32,6 +32,8 @@ import java.util.Map;
import org.apache.jasper.JasperException;
import org.apache.jasper.JspCompilationContext;
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
/**
* Contains static utilities for generating SMAP data based on the
@@ -60,6 +62,7 @@ public class SmapUtil {
* @param ctxt Current compilation context
* @param pageNodes The current JSP page
* @return a SMAP for the page
+ * @throws IOException Error writing SMAP
*/
public static String[] generateSmap(
JspCompilationContext ctxt,
@@ -180,8 +183,7 @@ public class SmapUtil {
// Installation logic (from Robert Field, JSR-045 spec lead)
private static class SDEInstaller {
- private final org.apache.juli.logging.Log log=
- org.apache.juli.logging.LogFactory.getLog( SDEInstaller.class );
+ private final Log log = LogFactory.getLog(SDEInstaller.class);
static final String nameSDE = "SourceDebugExtension";
diff --git a/java/org/apache/jasper/compiler/TagFileProcessor.java b/java/org/apache/jasper/compiler/TagFileProcessor.java
index 9b85861..d8c2812 100644
--- a/java/org/apache/jasper/compiler/TagFileProcessor.java
+++ b/java/org/apache/jasper/compiler/TagFileProcessor.java
@@ -17,7 +17,6 @@
package org.apache.jasper.compiler;
-import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
@@ -483,6 +482,8 @@ class TagFileProcessor {
* @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,
@@ -495,8 +496,6 @@ class TagFileProcessor {
Node.Nodes page = null;
try {
page = pc.parseTagFileDirectives(path, jar);
- } catch (FileNotFoundException e) {
- err.jspError("jsp.error.file.not.found", path);
} catch (IOException e) {
err.jspError("jsp.error.file.not.found", path);
}
@@ -660,16 +659,14 @@ class TagFileProcessor {
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));
+ compiler.getCompilationContext().getLastModified(tagFilePath));
}
Class<?> c = loadTagFile(compiler, tagFilePath, n.getTagInfo(),
pageInfo);
@@ -684,6 +681,11 @@ class TagFileProcessor {
* 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 {
diff --git a/java/org/apache/jasper/compiler/TagLibraryInfoImpl.java b/java/org/apache/jasper/compiler/TagLibraryInfoImpl.java
index 554dc87..0ebe577 100644
--- a/java/org/apache/jasper/compiler/TagLibraryInfoImpl.java
+++ b/java/org/apache/jasper/compiler/TagLibraryInfoImpl.java
@@ -105,10 +105,7 @@ class TagLibraryInfoImpl extends TagLibraryInfo implements TagConstants {
return sw.toString();
}
- /**
- * Constructor.
- */
- @SuppressWarnings("null") // taglibXml can't be null
+
public TagLibraryInfoImpl(JspCompilationContext ctxt, ParserController pc,
PageInfo pi, String prefix, String uriIn,
TldResourcePath tldResourcePath, ErrorDispatcher err)
@@ -185,7 +182,11 @@ class TagLibraryInfoImpl extends TagLibraryInfo implements TagConstants {
}
// Populate the TagLibraryInfo attributes
- this.jspversion = taglibXml.getJspVersion();
+ // Never null. jspError always throws an Exception
+ // Slightly convoluted so the @SuppressWarnings has minimal scope
+ @SuppressWarnings("null")
+ String v = taglibXml.getJspVersion();
+ this.jspversion = v;
this.tlibversion = taglibXml.getTlibVersion();
this.shortname = taglibXml.getShortName();
this.urn = taglibXml.getUri();
diff --git a/java/org/apache/jasper/compiler/tagplugin/TagPluginContext.java b/java/org/apache/jasper/compiler/tagplugin/TagPluginContext.java
index 6a20965..599372f 100644
--- a/java/org/apache/jasper/compiler/tagplugin/TagPluginContext.java
+++ b/java/org/apache/jasper/compiler/tagplugin/TagPluginContext.java
@@ -56,21 +56,24 @@ public interface TagPluginContext {
* invoked with the same id more than once in the translation
* unit, only the first declaration will be taken.
* @param text The text of the declaration.
- **/
+ */
void generateDeclaration(String id, String text);
/**
- * Generate Java source codes
+ * Generate Java source code scriptlet
+ * @param s the scriptlet (raw Java source)
*/
void generateJavaSource(String s);
/**
+ * @param attribute The attribute name
* @return true if the attribute is specified and its value is a
* translation-time constant.
*/
boolean isConstantAttribute(String attribute);
/**
+ * @param attribute The attribute name
* @return A string that is the value of a constant attribute. Undefined
* if the attribute is not a (translation-time) constant.
* null if the attribute is not specified.
@@ -113,16 +116,21 @@ public interface TagPluginContext {
* Associate the attribute with a value in the current tagplugin context.
* The plugin attributes can be used for communication among tags that
* must work together as a group. See <c:when> for an example.
+ * @param attr The attribute name
+ * @param value The attribute value
*/
void setPluginAttribute(String attr, Object value);
/**
* Get the value of an attribute in the current tagplugin context.
+ * @param attr The attribute name
+ * @return the attribute value
*/
Object getPluginAttribute(String attr);
/**
* Is the tag being used inside a tag file?
+ * @return <code>true</code> if inside a tag file
*/
boolean isTagFile();
}
diff --git a/java/org/apache/jasper/el/ELResolverImpl.java b/java/org/apache/jasper/el/ELResolverImpl.java
index 534789c..3a143a3 100644
--- a/java/org/apache/jasper/el/ELResolverImpl.java
+++ b/java/org/apache/jasper/el/ELResolverImpl.java
@@ -18,6 +18,7 @@
package org.apache.jasper.el;
import java.util.Iterator;
+import java.util.Objects;
import javax.el.ELContext;
import javax.el.ELException;
@@ -40,9 +41,7 @@ public final class ELResolverImpl extends ELResolver {
@Override
public Object getValue(ELContext context, Object base, Object property) {
- if (context == null) {
- throw new NullPointerException();
- }
+ Objects.requireNonNull(context);
if (base == null) {
context.setPropertyResolved(base, property);
@@ -64,9 +63,7 @@ public final class ELResolverImpl extends ELResolver {
@Override
public Class<?> getType(ELContext context, Object base, Object property) {
- if (context == null) {
- throw new NullPointerException();
- }
+ Objects.requireNonNull(context);
if (base == null) {
context.setPropertyResolved(base, property);
@@ -90,9 +87,7 @@ public final class ELResolverImpl extends ELResolver {
@Override
public void setValue(ELContext context, Object base, Object property,
Object value) {
- if (context == null) {
- throw new NullPointerException();
- }
+ Objects.requireNonNull(context);
if (base == null) {
context.setPropertyResolved(base, property);
@@ -107,9 +102,7 @@ public final class ELResolverImpl extends ELResolver {
@Override
public boolean isReadOnly(ELContext context, Object base, Object property) {
- if (context == null) {
- throw new NullPointerException();
- }
+ Objects.requireNonNull(context);
if (base == null) {
context.setPropertyResolved(base, property);
diff --git a/java/org/apache/jasper/resources/LocalStrings.properties b/java/org/apache/jasper/resources/LocalStrings.properties
index 4dc8758..a647397 100644
--- a/java/org/apache/jasper/resources/LocalStrings.properties
+++ b/java/org/apache/jasper/resources/LocalStrings.properties
@@ -123,7 +123,7 @@ jsp.warning.suppressSmap=Warning: Invalid value for the initParam suppressSmap.
jsp.warning.displaySourceFragment=Warning: Invalid value for the initParam displaySourceFragment. Will use the default value of \"true\"
jsp.warning.maxLoadedJsps=Warning: Invalid value for the initParam maxLoadedJsps. Will use the default value of \"-1\"
jsp.warning.jspIdleTimeout=Warning: Invalid value for the initParam jspIdleTimeout. Will use the default value of \"-1\"
-jsp.warning.strictQuoteEscaping=Warning: Invalid value for the initParam strictQuoteEscaping. Will use the default value of \"{0}\"
+jsp.warning.strictQuoteEscaping=Warning: Invalid value for the initParam strictQuoteEscaping. Will use the default value of \"true\"
jsp.warning.quoteAttributeEL=Warning: Invalid value for the initParam quoteAttributeEL. Will use the default value of \"false\"
jsp.warning.unknown.element.in.taglib=Unknown element ({0}) in taglib
jsp.warning.unknown.element.in.tag=Unknown element ({0}) in tag
@@ -176,7 +176,7 @@ where options include:\n\
\ -webinc <file> Creates a partial servlet mappings in the file\n\
\ -webxml <file> Creates a complete web.xml in the file\n\
\ -webxmlencoding <enc> Set the encoding charset used to read and write the web.xml\n\
-\ file (default is platform default encoding)\n\
+\ file (default is UTF-8)\n\
\ -addwebxmlmappings Merge generated web.xml fragment into the web.xml file of the\n\
\ web-app, whose JSP pages we are processing\n\
\ -ieplugin <clsid> Java Plugin classid for Internet Explorer\n\
@@ -187,7 +187,7 @@ where options include:\n\
\ -source <version> Set the -source argument to the compiler (default 1.7)\n\
\ -target <version> Set the -target argument to the compiler (default 1.7)\n\
-jspc.webxml.header=<?xml version="1.0" encoding="ISO-8859-1"?>\n\
+jspc.webxml.header=<?xml version="1.0" encoding="{0}"?>\n\
\n\
<!DOCTYPE web-app\n\
\ PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"\n\
diff --git a/java/org/apache/jasper/resources/LocalStrings_es.properties b/java/org/apache/jasper/resources/LocalStrings_es.properties
index 94864b6..baac647 100644
--- a/java/org/apache/jasper/resources/LocalStrings_es.properties
+++ b/java/org/apache/jasper/resources/LocalStrings_es.properties
@@ -180,7 +180,7 @@ jspc.usage = Uso\: jspc <opciones> [--] <Archivos JSP>\n\
\ -javaEncoding <enc> Set the encoding charset for Java classes (default UTF-8)\n\
\ -source <version> Set the -source argument to the compiler (default 1.7)\n\
\ -target <version> Set the -target argument to the compiler (default 1.7)\n
-jspc.webxml.header = <?xml version\="1.0" encoding\="ISO-8859-1"?>\n\
+jspc.webxml.header = <?xml version\="1.0" encoding\="{0}"?>\n\
\n\
<\!DOCTYPE web-app\n\
\ PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"\n\
@@ -369,4 +369,4 @@ jsp.tldCache.noTldInJar = No se han hallado ficheros TLD en [{0}]. Considera a\u
jsp.tldCache.noTldSummary = Al menos un JAR, que se ha explorado buscando TLDs, a\u00FAn no conten\u00EDa TLDs. Activar historial de depuraci\u00F3n para este historiador para una completa lista de los JARs que fueron explorados y de los que nos se hall\u00F3 TLDs. Saltarse JARs no necesarios durante la exploraci\u00F3n puede dar lugar a una mejora de tiempo significativa en el arranque y compilaci\u00F3n de JSP .
#ELInterpreter
-jsp.error.el_interpreter_class.instantiation=No se puede cargar la clase ELInterpreter llamada [{0}]
\ No newline at end of file
+jsp.error.el_interpreter_class.instantiation=No se puede cargar la clase ELInterpreter llamada [{0}]
diff --git a/java/org/apache/jasper/resources/LocalStrings_fr.properties b/java/org/apache/jasper/resources/LocalStrings_fr.properties
index 45b8aaf..512b4d9 100644
--- a/java/org/apache/jasper/resources/LocalStrings_fr.properties
+++ b/java/org/apache/jasper/resources/LocalStrings_fr.properties
@@ -116,7 +116,7 @@ o\u00f9 les options comprennet:\n\
\ -source <version> Set the -source argument to the compiler (default 1.7)\n\
\ -target <version> Set the -target argument to the compiler (default 1.7)\n\
-jspc.webxml.header=<?xml version="1.0" encoding="ISO-8859-1"?>\n\
+jspc.webxml.header=<?xml version="1.0" encoding="{0}"?>\n\
\n\
<!DOCTYPE web-app\n\
\ PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"\n\
@@ -211,4 +211,4 @@ jsp.error.attributes.not.allowed = {0} ne doit avoir aucun attribut
#jsp.error.invalid.bean=
#ELInterpreter
-jsp.error.el_interpreter_class.instantiation=Impossible de charger ou d''instancier la classe ELInterpreter [{0}]
\ No newline at end of file
+jsp.error.el_interpreter_class.instantiation=Impossible de charger ou d''instancier la classe ELInterpreter [{0}]
diff --git a/java/org/apache/jasper/resources/LocalStrings_ja.properties b/java/org/apache/jasper/resources/LocalStrings_ja.properties
index 6a14964..1bc6aa0 100644
--- a/java/org/apache/jasper/resources/LocalStrings_ja.properties
+++ b/java/org/apache/jasper/resources/LocalStrings_ja.properties
@@ -158,7 +158,7 @@ JSP\u30d5\u30a1\u30a4\u30eb\u306e\u5834\u6240\u306f\u6b21\u306e\u30aa\u30d7\u30b
\ -source <version> Set the -source argument to the compiler (default 1.7)\n\
\ -target <version> Set the -target argument to the compiler (default 1.7)\n\
-jspc.webxml.header=<?xml version="1.0" encoding="ISO-8859-1"?>\n\
+jspc.webxml.header=<?xml version="1.0" encoding="{0}"?>\n\
\n\
<!DOCTYPE web-app\n\
\ PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"\n\
@@ -310,4 +310,4 @@ jsp.error.invalid.bean=useBean\u306e\u30af\u30e9\u30b9\u5c5e\u6027 {0} \u306e\u5
jsp.error.prefix.use_before_dcl=\u3053\u306e\u30bf\u30b0\u6307\u793a\u5b50\u3067\u6307\u5b9a\u3055\u308c\u3066\u3044\u308b\u30d7\u30ea\u30d5\u30a3\u30c3\u30af\u30b9 {0} \u306f\u3001\u3059\u3067\u306b\u30d5\u30a1\u30a4\u30eb {1} \u306e {2} \u884c\u76ee\u306e\u30a2\u30af\u30b7\u30e7\u30f3\u3067\u4f7f\u7528\u3055\u308c\u3066\u3044\u307e\u3059
#ELInterpreter
-jsp.error.el_interpreter_class.instantiation=ELInterpreter class\u306e\u30ed\u30fc\u30c9\u53c8\u306f\u30a4\u30f3\u30b9\u30bf\u30f3\u30b9\u5316\u306b\u5931\u6557\u3057\u307e\u3057\u305f [{0}]
\ No newline at end of file
+jsp.error.el_interpreter_class.instantiation=ELInterpreter class\u306e\u30ed\u30fc\u30c9\u53c8\u306f\u30a4\u30f3\u30b9\u30bf\u30f3\u30b9\u5316\u306b\u5931\u6557\u3057\u307e\u3057\u305f [{0}]
diff --git a/java/org/apache/jasper/runtime/BodyContentImpl.java b/java/org/apache/jasper/runtime/BodyContentImpl.java
index 0710081..c063b00 100644
--- a/java/org/apache/jasper/runtime/BodyContentImpl.java
+++ b/java/org/apache/jasper/runtime/BodyContentImpl.java
@@ -46,11 +46,14 @@ public class BodyContentImpl extends BodyContent {
private int nextChar;
private boolean closed;
- // Enclosed writer to which any output is written
+ /**
+ * Enclosed writer to which any output is written
+ */
private Writer writer;
/**
* Constructor.
+ * @param enclosingWriter The wrapped writer
*/
public BodyContentImpl(JspWriter enclosingWriter) {
super(enclosingWriter);
@@ -62,6 +65,8 @@ public class BodyContentImpl extends BodyContent {
/**
* Write a single character.
+ * @param c The char to write
+ * @throws IOException Error writing to wrapped writer
*/
@Override
public void write(int c) throws IOException {
@@ -90,6 +95,7 @@ public class BodyContentImpl extends BodyContent {
* @param cbuf A character array
* @param off Offset from which to start reading characters
* @param len Number of characters to write
+ * @throws IOException Error writing to wrapped writer
*/
@Override
public void write(char[] cbuf, int off, int len) throws IOException {
@@ -116,6 +122,8 @@ public class BodyContentImpl extends BodyContent {
/**
* Write an array of characters. This method cannot be inherited from the
* Writer class because it must suppress I/O exceptions.
+ * @param buf Content to write
+ * @throws IOException Error writing to wrapped writer
*/
@Override
public void write(char[] buf) throws IOException {
@@ -132,6 +140,7 @@ public class BodyContentImpl extends BodyContent {
* @param s String to be written
* @param off Offset from which to start reading characters
* @param len Number of characters to be written
+ * @throws IOException Error writing to wrapped writer
*/
@Override
public void write(String s, int off, int len) throws IOException {
@@ -150,6 +159,8 @@ public class BodyContentImpl extends BodyContent {
/**
* Write a string. This method cannot be inherited from the Writer class
* because it must suppress I/O exceptions.
+ * @param s String to be written
+ * @throws IOException Error writing to wrapped writer
*/
@Override
public void write(String s) throws IOException {
@@ -165,7 +176,7 @@ public class BodyContentImpl extends BodyContent {
* system property <tt>line.separator</tt>, and is not necessarily a single
* newline ('\n') character.
*
- * @throws IOException If an I/O error occurs
+ * @throws IOException Error writing to wrapped writer
*/
@Override
public void newLine() throws IOException {
@@ -184,7 +195,7 @@ public class BodyContentImpl extends BodyContent {
* #write(int)}</code> method.
*
* @param b The <code>boolean</code> to be printed
- * @throws IOException
+ * @throws IOException Error writing to wrapped writer
*/
@Override
public void print(boolean b) throws IOException {
@@ -202,7 +213,7 @@ public class BodyContentImpl extends BodyContent {
* #write(int)}</code> method.
*
* @param c The <code>char</code> to be printed
- * @throws IOException
+ * @throws IOException Error writing to wrapped writer
*/
@Override
public void print(char c) throws IOException {
@@ -221,7 +232,7 @@ public class BodyContentImpl extends BodyContent {
* method.
*
* @param i The <code>int</code> to be printed
- * @throws IOException
+ * @throws IOException Error writing to wrapped writer
*/
@Override
public void print(int i) throws IOException {
@@ -240,7 +251,7 @@ public class BodyContentImpl extends BodyContent {
* <code>{@link #write(int)}</code> method.
*
* @param l The <code>long</code> to be printed
- * @throws IOException
+ * @throws IOException Error writing to wrapped writer
*/
@Override
public void print(long l) throws IOException {
@@ -259,7 +270,7 @@ public class BodyContentImpl extends BodyContent {
* <code>{@link #write(int)}</code> method.
*
* @param f The <code>float</code> to be printed
- * @throws IOException
+ * @throws IOException Error writing to wrapped writer
*/
@Override
public void print(float f) throws IOException {
@@ -278,7 +289,7 @@ public class BodyContentImpl extends BodyContent {
* #write(int)}</code> method.
*
* @param d The <code>double</code> to be printed
- * @throws IOException
+ * @throws IOException Error writing to wrapped writer
*/
@Override
public void print(double d) throws IOException {
@@ -298,7 +309,7 @@ public class BodyContentImpl extends BodyContent {
* @param s The array of chars to be printed
*
* @throws NullPointerException If <code>s</code> is <code>null</code>
- * @throws IOException
+ * @throws IOException Error writing to wrapped writer
*/
@Override
public void print(char[] s) throws IOException {
@@ -317,7 +328,7 @@ public class BodyContentImpl extends BodyContent {
* <code>{@link #write(int)}</code> method.
*
* @param s The <code>String</code> to be printed
- * @throws IOException
+ * @throws IOException Error writing to wrapped writer
*/
@Override
public void print(String s) throws IOException {
@@ -337,7 +348,7 @@ public class BodyContentImpl extends BodyContent {
* <code>{@link #write(int)}</code> method.
*
* @param obj The <code>Object</code> to be printed
- * @throws IOException
+ * @throws IOException Error writing to wrapped writer
*/
@Override
public void print(Object obj) throws IOException {
@@ -354,7 +365,7 @@ public class BodyContentImpl extends BodyContent {
* <code>line.separator</code>, and is not necessarily a single newline
* character (<code>'\n'</code>).
*
- * @throws IOException
+ * @throws IOException Error writing to wrapped writer
*/
@Override
public void println() throws IOException {
@@ -366,7 +377,8 @@ public class BodyContentImpl extends BodyContent {
* as though it invokes <code>{@link #print(boolean)}</code> and then
* <code>{@link #println()}</code>.
*
- * @throws IOException
+ * @param x The <code>boolean</code> to be printed
+ * @throws IOException Error writing to wrapped writer
*/
@Override
public void println(boolean x) throws IOException {
@@ -379,7 +391,8 @@ public class BodyContentImpl extends BodyContent {
* though it invokes <code>{@link #print(char)}</code> and then
* <code>{@link #println()}</code>.
*
- * @throws IOException
+ * @param x The <code>char</code> to be printed
+ * @throws IOException Error writing to wrapped writer
*/
@Override
public void println(char x) throws IOException {
@@ -392,7 +405,8 @@ public class BodyContentImpl extends BodyContent {
* though it invokes <code>{@link #print(int)}</code> and then
* <code>{@link #println()}</code>.
*
- * @throws IOException
+ * @param x The <code>int</code> to be printed
+ * @throws IOException Error writing to wrapped writer
*/
@Override
public void println(int x) throws IOException {
@@ -405,7 +419,8 @@ public class BodyContentImpl extends BodyContent {
* as though it invokes <code>{@link #print(long)}</code> and then
* <code>{@link #println()}</code>.
*
- * @throws IOException
+ * @param x The <code>long</code> to be printed
+ * @throws IOException Error writing to wrapped writer
*/
@Override
public void println(long x) throws IOException {
@@ -418,7 +433,8 @@ public class BodyContentImpl extends BodyContent {
* behaves as though it invokes <code>{@link #print(float)}</code> and then
* <code>{@link #println()}</code>.
*
- * @throws IOException
+ * @param x The <code>float</code> to be printed
+ * @throws IOException Error writing to wrapped writer
*/
@Override
public void println(float x) throws IOException {
@@ -431,7 +447,8 @@ public class BodyContentImpl extends BodyContent {
* line. This method behaves as though it invokes <code>{@link
* #print(double)}</code> and then <code>{@link #println()}</code>.
*
- * @throws IOException
+ * @param x The <code>double</code> to be printed
+ * @throws IOException Error writing to wrapped writer
*/
@Override
public void println(double x) throws IOException{
@@ -444,7 +461,8 @@ public class BodyContentImpl extends BodyContent {
* behaves as though it invokes <code>{@link #print(char[])}</code> and
* then <code>{@link #println()}</code>.
*
- * @throws IOException
+ * @param x The <code>char</code> array to be printed
+ * @throws IOException Error writing to wrapped writer
*/
@Override
public void println(char x[]) throws IOException {
@@ -457,7 +475,8 @@ public class BodyContentImpl extends BodyContent {
* though it invokes <code>{@link #print(String)}</code> and then
* <code>{@link #println()}</code>.
*
- * @throws IOException
+ * @param x The string to be printed
+ * @throws IOException Error writing to wrapped writer
*/
@Override
public void println(String x) throws IOException {
@@ -470,7 +489,8 @@ public class BodyContentImpl extends BodyContent {
* though it invokes <code>{@link #print(Object)}</code> and then
* <code>{@link #println()}</code>.
*
- * @throws IOException
+ * @param x The object to be printed
+ * @throws IOException Error writing to wrapped writer
*/
@Override
public void println(Object x) throws IOException {
@@ -484,7 +504,7 @@ public class BodyContentImpl extends BodyContent {
* to signal the fact that some data has already been irrevocably
* written to the client response stream.
*
- * @throws IOException If an I/O error occurs
+ * @throws IOException If there is no wrapped writer
*/
@Override
public void clear() throws IOException {
@@ -505,7 +525,7 @@ public class BodyContentImpl extends BodyContent {
* flushed. It merely clears the current content of the buffer and
* returns.
*
- * @throws IOException If an I/O error occurs
+ * @throws IOException Should not happen
*/
@Override
public void clearBuffer() throws IOException {
@@ -519,7 +539,7 @@ public class BodyContentImpl extends BodyContent {
* further write() or flush() invocations will cause an IOException to be
* thrown. Closing a previously-closed stream, however, has no effect.
*
- * @throws IOException If an I/O error occurs
+ * @throws IOException Error writing to wrapped writer
*/
@Override
public void close() throws IOException {
@@ -583,6 +603,7 @@ public class BodyContentImpl extends BodyContent {
*
* @param out The writer into which to place the contents of this body
* evaluation
+ * @throws IOException Error writing to writer
*/
@Override
public void writeOut(Writer out) throws IOException {
diff --git a/java/org/apache/jasper/runtime/JspRuntimeLibrary.java b/java/org/apache/jasper/runtime/JspRuntimeLibrary.java
index f84579c..fe4bbcf 100644
--- a/java/org/apache/jasper/runtime/JspRuntimeLibrary.java
+++ b/java/org/apache/jasper/runtime/JspRuntimeLibrary.java
@@ -58,6 +58,8 @@ public class JspRuntimeLibrary {
* 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(
@@ -366,6 +368,13 @@ public class JspRuntimeLibrary {
* 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,
@@ -812,6 +821,7 @@ public class JspRuntimeLibrary {
*
* @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) {
diff --git a/java/org/apache/jasper/runtime/JspSourceDependent.java b/java/org/apache/jasper/runtime/JspSourceDependent.java
index 0775e41..c4a309e 100644
--- a/java/org/apache/jasper/runtime/JspSourceDependent.java
+++ b/java/org/apache/jasper/runtime/JspSourceDependent.java
@@ -33,6 +33,7 @@ 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
*/
public Map<String,Long> getDependants();
diff --git a/java/org/apache/jasper/runtime/JspWriterImpl.java b/java/org/apache/jasper/runtime/JspWriterImpl.java
index 8b4dc4f..bc109ed 100644
--- a/java/org/apache/jasper/runtime/JspWriterImpl.java
+++ b/java/org/apache/jasper/runtime/JspWriterImpl.java
@@ -61,7 +61,8 @@ public class JspWriterImpl extends JspWriter {
*
* @param response A Servlet Response
* @param sz Output-buffer size, a positive integer
- *
+ * @param autoFlush <code>true</code> to automatically flush on buffer
+ * full, <code>false</code> to throw an overflow exception in that case
* @exception IllegalArgumentException If sz is <= 0
*/
public JspWriterImpl(ServletResponse response, int sz,
@@ -83,7 +84,8 @@ public class JspWriterImpl extends JspWriter {
this.bufferSize=sz;
}
- /** Package-level access
+ /**
+ * Package-level access
*/
void recycle() {
flushed = false;
@@ -97,6 +99,7 @@ public class JspWriterImpl extends JspWriter {
* 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 {
if (bufferSize == 0)
diff --git a/java/org/apache/jasper/runtime/PageContextImpl.java b/java/org/apache/jasper/runtime/PageContextImpl.java
index 7b15346..3080546 100644
--- a/java/org/apache/jasper/runtime/PageContextImpl.java
+++ b/java/org/apache/jasper/runtime/PageContextImpl.java
@@ -925,6 +925,7 @@ public class PageContextImpl extends PageContext {
* @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,
diff --git a/java/org/apache/jasper/runtime/ProtectedFunctionMapper.java b/java/org/apache/jasper/runtime/ProtectedFunctionMapper.java
index d546509..2f354b6 100644
--- a/java/org/apache/jasper/runtime/ProtectedFunctionMapper.java
+++ b/java/org/apache/jasper/runtime/ProtectedFunctionMapper.java
@@ -111,6 +111,7 @@ public final class ProtectedFunctionMapper extends javax.el.FunctionMapper
* 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) {
diff --git a/java/org/apache/jasper/security/SecurityClassLoad.java b/java/org/apache/jasper/security/SecurityClassLoad.java
index a9a1d72..c13dd48 100644
--- a/java/org/apache/jasper/security/SecurityClassLoad.java
+++ b/java/org/apache/jasper/security/SecurityClassLoad.java
@@ -18,6 +18,9 @@
package org.apache.jasper.security;
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
+
/**
* Static class used to preload java classes when using the
* Java SecurityManager so that the defineClassInPackage
@@ -26,8 +29,7 @@ package org.apache.jasper.security;
public final class SecurityClassLoad {
- private static final org.apache.juli.logging.Log log=
- org.apache.juli.logging.LogFactory.getLog( SecurityClassLoad.class );
+ private static final Log log = LogFactory.getLog(SecurityClassLoad.class);
public static void securityClassLoad(ClassLoader loader){
diff --git a/java/org/apache/jasper/security/SecurityUtil.java b/java/org/apache/jasper/security/SecurityUtil.java
index 0a0eeab..5ca7210 100644
--- a/java/org/apache/jasper/security/SecurityUtil.java
+++ b/java/org/apache/jasper/security/SecurityUtil.java
@@ -30,6 +30,7 @@ public final class SecurityUtil{
/**
* Return the <code>SecurityManager</code> only if Security is enabled AND
* package protection mechanism is enabled.
+ * @return <code>true</code> if package protection is enabled
*/
public static boolean isPackageProtectionEnabled(){
if (packageDefinitionEnabled && Constants.IS_SECURITY_ENABLED){
@@ -45,6 +46,7 @@ public final class SecurityUtil{
* codes in the request URL that is often reported in error messages.
*
* @param message The message string to be filtered
+ * @return the HTML filtered message
*/
public static String filter(String message) {
diff --git a/java/org/apache/jasper/servlet/JasperLoader.java b/java/org/apache/jasper/servlet/JasperLoader.java
index 7218d51..bca4170 100644
--- a/java/org/apache/jasper/servlet/JasperLoader.java
+++ b/java/org/apache/jasper/servlet/JasperLoader.java
@@ -36,14 +36,12 @@ import org.apache.jasper.Constants;
public class JasperLoader extends URLClassLoader {
private final PermissionCollection permissionCollection;
- private final ClassLoader parent;
private final SecurityManager securityManager;
public JasperLoader(URL[] urls, ClassLoader parent,
PermissionCollection permissionCollection) {
super(urls, parent);
this.permissionCollection = permissionCollection;
- this.parent = parent;
this.securityManager = System.getSecurityManager();
}
@@ -122,7 +120,7 @@ public class JasperLoader extends URLClassLoader {
if( !name.startsWith(Constants.JSP_PACKAGE_NAME + '.') ) {
// Class is not in org.apache.jsp, therefore, have our
// parent load it
- clazz = parent.loadClass(name);
+ clazz = getParent().loadClass(name);
if( resolve )
resolveClass(clazz);
return clazz;
@@ -139,7 +137,7 @@ public class JasperLoader extends URLClassLoader {
*/
@Override
public InputStream getResourceAsStream(String name) {
- InputStream is = parent.getResourceAsStream(name);
+ InputStream is = getParent().getResourceAsStream(name);
if (is == null) {
URL url = findResource(name);
if (url != null) {
diff --git a/java/org/apache/jasper/servlet/JspCServletContext.java b/java/org/apache/jasper/servlet/JspCServletContext.java
index 77ab068..fc57a7e 100644
--- a/java/org/apache/jasper/servlet/JspCServletContext.java
+++ b/java/org/apache/jasper/servlet/JspCServletContext.java
@@ -120,7 +120,7 @@ public class JspCServletContext implements 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
+ * @throws JasperException An error occurred building the merged web.xml
*/
public JspCServletContext(PrintWriter aLogWriter, URL aResourceBaseURL,
ClassLoader classLoader, boolean validate, boolean blockExternal)
diff --git a/java/org/apache/jasper/servlet/JspServlet.java b/java/org/apache/jasper/servlet/JspServlet.java
index 8352e86..92c05d7 100644
--- a/java/org/apache/jasper/servlet/JspServlet.java
+++ b/java/org/apache/jasper/servlet/JspServlet.java
@@ -123,7 +123,7 @@ public class JspServlet extends HttpServlet implements PeriodicEventListener {
return;
}
} catch (MalformedURLException e) {
- throw new ServletException("Can not locate jsp file", e);
+ throw new ServletException("cannot locate jsp file", e);
}
try {
if (SecurityUtil.isPackageProtectionEnabled()){
@@ -235,22 +235,22 @@ public class JspServlet extends HttpServlet implements PeriodicEventListener {
String queryString = request.getQueryString();
if (queryString == null) {
- return (false);
+ return false;
}
int start = queryString.indexOf(Constants.PRECOMPILE);
if (start < 0) {
- return (false);
+ return false;
}
queryString =
queryString.substring(start + Constants.PRECOMPILE.length());
if (queryString.length() == 0) {
- return (true); // ?jsp_precompile
+ 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('&');
@@ -259,14 +259,14 @@ public class JspServlet extends HttpServlet implements PeriodicEventListener {
}
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("Cannot have request parameter " +
Constants.PRECOMPILE + " set to " +
@@ -276,25 +276,14 @@ public class JspServlet extends HttpServlet implements PeriodicEventListener {
}
- @SuppressWarnings("deprecation") // Use of JSP_FILE to be removed in 8.5.x
@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
+ // jspFile may be configured as an init-param for this servlet instance
String jspUri = jspFile;
if (jspUri == null) {
- // JSP specified via <jsp-file> in <servlet> declaration and
- // supplied through custom servlet container code
- String jspFile = (String) request.getAttribute(Constants.JSP_FILE);
- if (jspFile != null) {
- jspUri = jspFile;
- request.removeAttribute(Constants.JSP_FILE);
- }
- }
- if (jspUri == null) {
/*
* Check to see if the requested JSP has been the target of a
* RequestDispatcher.include()
diff --git a/java/org/apache/jasper/servlet/JspServletWrapper.java b/java/org/apache/jasper/servlet/JspServletWrapper.java
index 6bd4569..7c6c6b1 100644
--- a/java/org/apache/jasper/servlet/JspServletWrapper.java
+++ b/java/org/apache/jasper/servlet/JspServletWrapper.java
@@ -232,7 +232,9 @@ public class JspServletWrapper {
}
/**
- * Compile (if needed) and load a tag file
+ * 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 {
@@ -267,6 +269,8 @@ public class JspServletWrapper {
* when compiling tag files with circular dependencies. A prototype
* (skeleton) with no dependencies on other other tag files is
* generated and compiled.
+ * @return the loaded class
+ * @throws JasperException Error compiling or loading tag file
*/
public Class<?> loadTagFilePrototype() throws JasperException {
@@ -280,6 +284,7 @@ public class JspServletWrapper {
/**
* Get a list of files that the current page has source dependency on.
+ * @return the map of dependent resources
*/
public java.util.Map<String,Long> getDependants() {
try {
@@ -293,7 +298,7 @@ public class JspServletWrapper {
} else {
target = getServlet();
}
- if (target != null && target instanceof JspSourceDependent) {
+ if (target instanceof JspSourceDependent) {
return ((JspSourceDependent) target).getDependants();
}
} catch (AbstractMethodError ame) {
diff --git a/java/org/apache/jasper/servlet/TldScanner.java b/java/org/apache/jasper/servlet/TldScanner.java
index d95f676..f6c64b4 100644
--- a/java/org/apache/jasper/servlet/TldScanner.java
+++ b/java/org/apache/jasper/servlet/TldScanner.java
@@ -18,7 +18,6 @@ package org.apache.jasper.servlet;
import java.io.File;
import java.io.IOException;
-import java.net.JarURLConnection;
import java.net.URL;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
@@ -48,7 +47,6 @@ import org.apache.tomcat.JarScannerCallback;
import org.apache.tomcat.util.descriptor.tld.TaglibXml;
import org.apache.tomcat.util.descriptor.tld.TldParser;
import org.apache.tomcat.util.descriptor.tld.TldResourcePath;
-import org.apache.tomcat.util.scan.JarFactory;
import org.xml.sax.SAXException;
/**
@@ -153,6 +151,8 @@ public class TldScanner {
/**
* Scan for TLDs defined in <jsp-config>.
+ * @throws IOException Error reading resources
+ * @throws SAXException XML parsing error
*/
protected void scanJspConfig() throws IOException, SAXException {
JspConfigDescriptor jspConfigDescriptor = context.getJspConfigDescriptor();
@@ -293,12 +293,6 @@ public class TldScanner {
private boolean foundJarWithoutTld = false;
private boolean foundFileWithoutTld = false;
- @Override
- public void scan(JarURLConnection jarConn, String webappPath, boolean isWebapp)
- throws IOException {
- scan(JarFactory.newInstance(jarConn.getURL()), webappPath, isWebapp);
- }
-
@Override
public void scan(Jar jar, String webappPath, boolean isWebapp) throws IOException {
diff --git a/java/org/apache/jasper/servlet/mbeans-descriptors.xml b/java/org/apache/jasper/servlet/mbeans-descriptors.xml
index 5c6aafc..1014420 100644
--- a/java/org/apache/jasper/servlet/mbeans-descriptors.xml
+++ b/java/org/apache/jasper/servlet/mbeans-descriptors.xml
@@ -1,4 +1,4 @@
-<?xml version="1.0"?>
+<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
diff --git a/java/org/apache/jasper/tagplugins/jstl/Util.java b/java/org/apache/jasper/tagplugins/jstl/Util.java
index 2a02339..8ff26fd 100644
--- a/java/org/apache/jasper/tagplugins/jstl/Util.java
+++ b/java/org/apache/jasper/tagplugins/jstl/Util.java
@@ -93,6 +93,8 @@ public class Util {
* Returns <tt>true</tt> if our current URL is absolute,
* <tt>false</tt> otherwise.
* taken from org.apache.taglibs.standard.tag.common.core.ImportSupport
+ * @param url The URL
+ * @return <tt>true</tt> if the URL is absolute
*/
public static boolean isAbsoluteUrl(String url){
if(url == null){
@@ -117,6 +119,9 @@ public class Util {
* Get the value associated with a content-type attribute.
* Syntax defined in RFC 2045, section 5.1.
* taken from org.apache.taglibs.standard.tag.common.core.Util
+ * @param input The attribute string
+ * @param name The attribute name
+ * @return the attribute value
*/
public static String getContentTypeAttribute(String input, String name) {
int begin;
@@ -150,6 +155,8 @@ public class Util {
* and either EOS or a subsequent ';' (exclusive).
*
* taken from org.apache.taglibs.standard.tag.common.core.ImportSupport
+ * @param url The URL
+ * @return the URL without a user submitted session id parameter
*/
public static String stripSession(String url) {
StringBuilder u = new StringBuilder(url);
@@ -179,6 +186,8 @@ public class 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());
@@ -224,8 +233,14 @@ public class Util {
return escapedBuffer.toString();
}
- /** Utility methods
+ /**
+ * 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)
@@ -293,7 +308,6 @@ public class Util {
public ImportResponseWrapper(HttpServletResponse arg0) {
super(arg0);
- // TODO Auto-generated constructor stub
}
@Override
@@ -314,13 +328,11 @@ public class Util {
return sos;
}
- /** Has no effect. */
@Override
public void setContentType(String x) {
// ignore
}
- /** Has no effect. */
@Override
public void setLocale(Locale x) {
// ignore
diff --git a/java/org/apache/jasper/tagplugins/jstl/tagPlugins.xml b/java/org/apache/jasper/tagplugins/jstl/tagPlugins.xml
index f503c8b..9ad6b11 100644
--- a/java/org/apache/jasper/tagplugins/jstl/tagPlugins.xml
+++ b/java/org/apache/jasper/tagplugins/jstl/tagPlugins.xml
@@ -1,4 +1,4 @@
-<?xml version="1.0"?>
+<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
diff --git a/java/org/apache/jasper/xmlparser/EncodingMap.java b/java/org/apache/jasper/xmlparser/EncodingMap.java
index 68e17d0..af6aa7e 100644
--- a/java/org/apache/jasper/xmlparser/EncodingMap.java
+++ b/java/org/apache/jasper/xmlparser/EncodingMap.java
@@ -33,440 +33,440 @@ import java.util.Hashtable;
* encoding names used in XML instance documents <strong>must</strong>
* be the IANA encoding names specified or one of the aliases for those names
* which IANA defines.
- * <TABLE BORDER="0" WIDTH="100%">
+ * <TABLE WIDTH="100%">
* <caption>Mapping of IANA encoding names and Java encoding names</caption>
* <TR>
- * <TD WIDTH="33%">
- * <P ALIGN="CENTER"><B>Common Name</B>
+ * <TD>
+ * <P><B>Common Name</B>
* </TD>
- * <TD WIDTH="15%">
- * <P ALIGN="CENTER"><B>Use this name in XML files</B>
+ * <TD>
+ * <P><B>Use this name in XML files</B>
* </TD>
- * <TD WIDTH="12%">
- * <P ALIGN="CENTER"><B>Name Type</B>
+ * <TD>
+ * <P><B>Name Type</B>
* </TD>
- * <TD WIDTH="31%">
- * <P ALIGN="CENTER"><B>Xerces converts to this Java Encoder Name</B>
+ * <TD>
+ * <P><B>Xerces converts to this Java Encoder Name</B>
* </TD>
* </TR>
* <TR>
- * <TD WIDTH="33%">8 bit Unicode</TD>
- * <TD WIDTH="15%">
- * <P ALIGN="CENTER">UTF-8
+ * <TD>8 bit Unicode</TD>
+ * <TD>
+ * <P>UTF-8
* </TD>
- * <TD WIDTH="12%">
- * <P ALIGN="CENTER">IANA
+ * <TD>
+ * <P>IANA
* </TD>
- * <TD WIDTH="31%">
- * <P ALIGN="CENTER">UTF8
+ * <TD>
+ * <P>UTF8
* </TD>
* </TR>
* <TR>
- * <TD WIDTH="33%">ISO Latin 1</TD>
- * <TD WIDTH="15%">
- * <P ALIGN="CENTER">ISO-8859-1
+ * <TD>ISO Latin 1</TD>
+ * <TD>
+ * <P>ISO-8859-1
* </TD>
- * <TD WIDTH="12%">
- * <P ALIGN="CENTER">MIME
+ * <TD>
+ * <P>MIME
* </TD>
- * <TD WIDTH="31%">
- * <P ALIGN="CENTER">ISO-8859-1
+ * <TD>
+ * <P>ISO-8859-1
* </TD>
* </TR>
* <TR>
- * <TD WIDTH="33%">ISO Latin 2</TD>
- * <TD WIDTH="15%">
- * <P ALIGN="CENTER">ISO-8859-2
+ * <TD>ISO Latin 2</TD>
+ * <TD>
+ * <P>ISO-8859-2
* </TD>
- * <TD WIDTH="12%">
- * <P ALIGN="CENTER">MIME
+ * <TD>
+ * <P>MIME
* </TD>
- * <TD WIDTH="31%">
- * <P ALIGN="CENTER">ISO-8859-2
+ * <TD>
+ * <P>ISO-8859-2
* </TD>
* </TR>
* <TR>
- * <TD WIDTH="33%">ISO Latin 3</TD>
- * <TD WIDTH="15%">
- * <P ALIGN="CENTER">ISO-8859-3
+ * <TD>ISO Latin 3</TD>
+ * <TD>
+ * <P>ISO-8859-3
* </TD>
- * <TD WIDTH="12%">
- * <P ALIGN="CENTER">MIME
+ * <TD>
+ * <P>MIME
* </TD>
- * <TD WIDTH="31%">
- * <P ALIGN="CENTER">ISO-8859-3
+ * <TD>
+ * <P>ISO-8859-3
* </TD>
* </TR>
* <TR>
- * <TD WIDTH="33%">ISO Latin 4</TD>
- * <TD WIDTH="15%">
- * <P ALIGN="CENTER">ISO-8859-4
+ * <TD>ISO Latin 4</TD>
+ * <TD>
+ * <P>ISO-8859-4
* </TD>
- * <TD WIDTH="12%">
- * <P ALIGN="CENTER">MIME
+ * <TD>
+ * <P>MIME
* </TD>
- * <TD WIDTH="31%">
- * <P ALIGN="CENTER">ISO-8859-4
+ * <TD>
+ * <P>ISO-8859-4
* </TD>
* </TR>
* <TR>
- * <TD WIDTH="33%">ISO Latin Cyrillic</TD>
- * <TD WIDTH="15%">
- * <P ALIGN="CENTER">ISO-8859-5
+ * <TD>ISO Latin Cyrillic</TD>
+ * <TD>
+ * <P>ISO-8859-5
* </TD>
- * <TD WIDTH="12%">
- * <P ALIGN="CENTER">MIME
+ * <TD>
+ * <P>MIME
* </TD>
- * <TD WIDTH="31%">
- * <P ALIGN="CENTER">ISO-8859-5
+ * <TD>
+ * <P>ISO-8859-5
* </TD>
* </TR>
* <TR>
- * <TD WIDTH="33%">ISO Latin Arabic</TD>
- * <TD WIDTH="15%">
- * <P ALIGN="CENTER">ISO-8859-6
+ * <TD>ISO Latin Arabic</TD>
+ * <TD>
+ * <P>ISO-8859-6
* </TD>
- * <TD WIDTH="12%">
- * <P ALIGN="CENTER">MIME
+ * <TD>
+ * <P>MIME
* </TD>
- * <TD WIDTH="31%">
- * <P ALIGN="CENTER">ISO-8859-6
+ * <TD>
+ * <P>ISO-8859-6
* </TD>
* </TR>
* <TR>
- * <TD WIDTH="33%">ISO Latin Greek</TD>
- * <TD WIDTH="15%">
- * <P ALIGN="CENTER">ISO-8859-7
+ * <TD>ISO Latin Greek</TD>
+ * <TD>
+ * <P>ISO-8859-7
* </TD>
- * <TD WIDTH="12%">
- * <P ALIGN="CENTER">MIME
+ * <TD>
+ * <P>MIME
* </TD>
- * <TD WIDTH="31%">
- * <P ALIGN="CENTER">ISO-8859-7
+ * <TD>
+ * <P>ISO-8859-7
* </TD>
* </TR>
* <TR>
- * <TD WIDTH="33%">ISO Latin Hebrew</TD>
- * <TD WIDTH="15%">
- * <P ALIGN="CENTER">ISO-8859-8
+ * <TD>ISO Latin Hebrew</TD>
+ * <TD>
+ * <P>ISO-8859-8
* </TD>
- * <TD WIDTH="12%">
- * <P ALIGN="CENTER">MIME
+ * <TD>
+ * <P>MIME
* </TD>
- * <TD WIDTH="31%">
- * <P ALIGN="CENTER">ISO-8859-8
+ * <TD>
+ * <P>ISO-8859-8
* </TD>
* </TR>
* <TR>
- * <TD WIDTH="33%">ISO Latin 5</TD>
- * <TD WIDTH="15%">
- * <P ALIGN="CENTER">ISO-8859-9
+ * <TD>ISO Latin 5</TD>
+ * <TD>
+ * <P>ISO-8859-9
* </TD>
- * <TD WIDTH="12%">
- * <P ALIGN="CENTER">MIME
+ * <TD>
+ * <P>MIME
* </TD>
- * <TD WIDTH="31%">
- * <P ALIGN="CENTER">ISO-8859-9
+ * <TD>
+ * <P>ISO-8859-9
* </TD>
* </TR>
* <TR>
- * <TD WIDTH="33%">EBCDIC: US</TD>
- * <TD WIDTH="15%">
- * <P ALIGN="CENTER">ebcdic-cp-us
+ * <TD>EBCDIC: US</TD>
+ * <TD>
+ * <P>ebcdic-cp-us
* </TD>
- * <TD WIDTH="12%">
- * <P ALIGN="CENTER">IANA
+ * <TD>
+ * <P>IANA
* </TD>
- * <TD WIDTH="31%">
- * <P ALIGN="CENTER">cp037
+ * <TD>
+ * <P>cp037
* </TD>
* </TR>
* <TR>
- * <TD WIDTH="33%">EBCDIC: Canada</TD>
- * <TD WIDTH="15%">
- * <P ALIGN="CENTER">ebcdic-cp-ca
+ * <TD>EBCDIC: Canada</TD>
+ * <TD>
+ * <P>ebcdic-cp-ca
* </TD>
- * <TD WIDTH="12%">
- * <P ALIGN="CENTER">IANA
+ * <TD>
+ * <P>IANA
* </TD>
- * <TD WIDTH="31%">
- * <P ALIGN="CENTER">cp037
+ * <TD>
+ * <P>cp037
* </TD>
* </TR>
* <TR>
- * <TD WIDTH="33%">EBCDIC: Netherlands</TD>
- * <TD WIDTH="15%">
- * <P ALIGN="CENTER">ebcdic-cp-nl
+ * <TD>EBCDIC: Netherlands</TD>
+ * <TD>
+ * <P>ebcdic-cp-nl
* </TD>
- * <TD WIDTH="12%">
- * <P ALIGN="CENTER">IANA
+ * <TD>
+ * <P>IANA
* </TD>
- * <TD WIDTH="31%">
- * <P ALIGN="CENTER">cp037
+ * <TD>
+ * <P>cp037
* </TD>
* </TR>
* <TR>
- * <TD WIDTH="33%">EBCDIC: Denmark</TD>
- * <TD WIDTH="15%">
- * <P ALIGN="CENTER">ebcdic-cp-dk
+ * <TD>EBCDIC: Denmark</TD>
+ * <TD>
+ * <P>ebcdic-cp-dk
* </TD>
- * <TD WIDTH="12%">
- * <P ALIGN="CENTER">IANA
+ * <TD>
+ * <P>IANA
* </TD>
- * <TD WIDTH="31%">
- * <P ALIGN="CENTER">cp277
+ * <TD>
+ * <P>cp277
* </TD>
* </TR>
* <TR>
- * <TD WIDTH="33%">EBCDIC: Norway</TD>
- * <TD WIDTH="15%">
- * <P ALIGN="CENTER">ebcdic-cp-no
+ * <TD>EBCDIC: Norway</TD>
+ * <TD>
+ * <P>ebcdic-cp-no
* </TD>
- * <TD WIDTH="12%">
- * <P ALIGN="CENTER">IANA
+ * <TD>
+ * <P>IANA
* </TD>
- * <TD WIDTH="31%">
- * <P ALIGN="CENTER">cp277
+ * <TD>
+ * <P>cp277
* </TD>
* </TR>
* <TR>
- * <TD WIDTH="33%">EBCDIC: Finland</TD>
- * <TD WIDTH="15%">
- * <P ALIGN="CENTER">ebcdic-cp-fi
+ * <TD>EBCDIC: Finland</TD>
+ * <TD>
+ * <P>ebcdic-cp-fi
* </TD>
- * <TD WIDTH="12%">
- * <P ALIGN="CENTER">IANA
+ * <TD>
+ * <P>IANA
* </TD>
- * <TD WIDTH="31%">
- * <P ALIGN="CENTER">cp278
+ * <TD>
+ * <P>cp278
* </TD>
* </TR>
* <TR>
- * <TD WIDTH="33%">EBCDIC: Sweden</TD>
- * <TD WIDTH="15%">
- * <P ALIGN="CENTER">ebcdic-cp-se
+ * <TD>EBCDIC: Sweden</TD>
+ * <TD>
+ * <P>ebcdic-cp-se
* </TD>
- * <TD WIDTH="12%">
- * <P ALIGN="CENTER">IANA
+ * <TD>
+ * <P>IANA
* </TD>
- * <TD WIDTH="31%">
- * <P ALIGN="CENTER">cp278
+ * <TD>
+ * <P>cp278
* </TD>
* </TR>
* <TR>
- * <TD WIDTH="33%">EBCDIC: Italy</TD>
- * <TD WIDTH="15%">
- * <P ALIGN="CENTER">ebcdic-cp-it
+ * <TD>EBCDIC: Italy</TD>
+ * <TD>
+ * <P>ebcdic-cp-it
* </TD>
- * <TD WIDTH="12%">
- * <P ALIGN="CENTER">IANA
+ * <TD>
+ * <P>IANA
* </TD>
- * <TD WIDTH="31%">
- * <P ALIGN="CENTER">cp280
+ * <TD>
+ * <P>cp280
* </TD>
* </TR>
* <TR>
- * <TD WIDTH="33%">EBCDIC: Spain, Latin America</TD>
- * <TD WIDTH="15%">
- * <P ALIGN="CENTER">ebcdic-cp-es
+ * <TD>EBCDIC: Spain, Latin America</TD>
+ * <TD>
+ * <P>ebcdic-cp-es
* </TD>
- * <TD WIDTH="12%">
- * <P ALIGN="CENTER">IANA
+ * <TD>
+ * <P>IANA
* </TD>
- * <TD WIDTH="31%">
- * <P ALIGN="CENTER">cp284
+ * <TD>
+ * <P>cp284
* </TD>
* </TR>
* <TR>
- * <TD WIDTH="33%">EBCDIC: Great Britain</TD>
- * <TD WIDTH="15%">
- * <P ALIGN="CENTER">ebcdic-cp-gb
+ * <TD>EBCDIC: Great Britain</TD>
+ * <TD>
+ * <P>ebcdic-cp-gb
* </TD>
- * <TD WIDTH="12%">
- * <P ALIGN="CENTER">IANA
+ * <TD>
+ * <P>IANA
* </TD>
- * <TD WIDTH="31%">
- * <P ALIGN="CENTER">cp285
+ * <TD>
+ * <P>cp285
* </TD>
* </TR>
* <TR>
- * <TD WIDTH="33%">EBCDIC: France</TD>
- * <TD WIDTH="15%">
- * <P ALIGN="CENTER">ebcdic-cp-fr
+ * <TD>EBCDIC: France</TD>
+ * <TD>
+ * <P>ebcdic-cp-fr
* </TD>
- * <TD WIDTH="12%">
- * <P ALIGN="CENTER">IANA
+ * <TD>
+ * <P>IANA
* </TD>
- * <TD WIDTH="31%">
- * <P ALIGN="CENTER">cp297
+ * <TD>
+ * <P>cp297
* </TD>
* </TR>
* <TR>
- * <TD WIDTH="33%">EBCDIC: Arabic</TD>
- * <TD WIDTH="15%">
- * <P ALIGN="CENTER">ebcdic-cp-ar1
+ * <TD>EBCDIC: Arabic</TD>
+ * <TD>
+ * <P>ebcdic-cp-ar1
* </TD>
- * <TD WIDTH="12%">
- * <P ALIGN="CENTER">IANA
+ * <TD>
+ * <P>IANA
* </TD>
- * <TD WIDTH="31%">
- * <P ALIGN="CENTER">cp420
+ * <TD>
+ * <P>cp420
* </TD>
* </TR>
* <TR>
- * <TD WIDTH="33%">EBCDIC: Hebrew</TD>
- * <TD WIDTH="15%">
- * <P ALIGN="CENTER">ebcdic-cp-he
+ * <TD>EBCDIC: Hebrew</TD>
+ * <TD>
+ * <P>ebcdic-cp-he
* </TD>
- * <TD WIDTH="12%">
- * <P ALIGN="CENTER">IANA
+ * <TD>
+ * <P>IANA
* </TD>
- * <TD WIDTH="31%">
- * <P ALIGN="CENTER">cp424
+ * <TD>
+ * <P>cp424
* </TD>
* </TR>
* <TR>
- * <TD WIDTH="33%">EBCDIC: Switzerland</TD>
- * <TD WIDTH="15%">
- * <P ALIGN="CENTER">ebcdic-cp-ch
+ * <TD>EBCDIC: Switzerland</TD>
+ * <TD>
+ * <P>ebcdic-cp-ch
* </TD>
- * <TD WIDTH="12%">
- * <P ALIGN="CENTER">IANA
+ * <TD>
+ * <P>IANA
* </TD>
- * <TD WIDTH="31%">
- * <P ALIGN="CENTER">cp500
+ * <TD>
+ * <P>cp500
* </TD>
* </TR>
* <TR>
- * <TD WIDTH="33%">EBCDIC: Roece</TD>
- * <TD WIDTH="15%">
- * <P ALIGN="CENTER">ebcdic-cp-roece
+ * <TD>EBCDIC: Roece</TD>
+ * <TD>
+ * <P>ebcdic-cp-roece
* </TD>
- * <TD WIDTH="12%">
- * <P ALIGN="CENTER">IANA
+ * <TD>
+ * <P>IANA
* </TD>
- * <TD WIDTH="31%">
- * <P ALIGN="CENTER">cp870
+ * <TD>
+ * <P>cp870
* </TD>
* </TR>
* <TR>
- * <TD WIDTH="33%">EBCDIC: Yugoslavia</TD>
- * <TD WIDTH="15%">
- * <P ALIGN="CENTER">ebcdic-cp-yu
+ * <TD>EBCDIC: Yugoslavia</TD>
+ * <TD>
+ * <P>ebcdic-cp-yu
* </TD>
- * <TD WIDTH="12%">
- * <P ALIGN="CENTER">IANA
+ * <TD>
+ * <P>IANA
* </TD>
- * <TD WIDTH="31%">
- * <P ALIGN="CENTER">cp870
+ * <TD>
+ * <P>cp870
* </TD>
* </TR>
* <TR>
- * <TD WIDTH="33%">EBCDIC: Iceland</TD>
- * <TD WIDTH="15%">
- * <P ALIGN="CENTER">ebcdic-cp-is
+ * <TD>EBCDIC: Iceland</TD>
+ * <TD>
+ * <P>ebcdic-cp-is
* </TD>
- * <TD WIDTH="12%">
- * <P ALIGN="CENTER">IANA
+ * <TD>
+ * <P>IANA
* </TD>
- * <TD WIDTH="31%">
- * <P ALIGN="CENTER">cp871
+ * <TD>
+ * <P>cp871
* </TD>
* </TR>
* <TR>
- * <TD WIDTH="33%">EBCDIC: Urdu</TD>
- * <TD WIDTH="15%">
- * <P ALIGN="CENTER">ebcdic-cp-ar2
+ * <TD>EBCDIC: Urdu</TD>
+ * <TD>
+ * <P>ebcdic-cp-ar2
* </TD>
- * <TD WIDTH="12%">
- * <P ALIGN="CENTER">IANA
+ * <TD>
+ * <P>IANA
* </TD>
- * <TD WIDTH="31%">
- * <P ALIGN="CENTER">cp918
+ * <TD>
+ * <P>cp918
* </TD>
* </TR>
* <TR>
- * <TD WIDTH="33%">Chinese for PRC, mixed 1/2 byte</TD>
- * <TD WIDTH="15%">
- * <P ALIGN="CENTER">gb2312
+ * <TD>Chinese for PRC, mixed 1/2 byte</TD>
+ * <TD>
+ * <P>gb2312
* </TD>
- * <TD WIDTH="12%">
- * <P ALIGN="CENTER">MIME
+ * <TD>
+ * <P>MIME
* </TD>
- * <TD WIDTH="31%">
- * <P ALIGN="CENTER">GB2312
+ * <TD>
+ * <P>GB2312
* </TD>
* </TR>
* <TR>
- * <TD WIDTH="33%">Extended Unix Code, packed for Japanese</TD>
- * <TD WIDTH="15%">
- * <P ALIGN="CENTER">euc-jp
+ * <TD>Extended Unix Code, packed for Japanese</TD>
+ * <TD>
+ * <P>euc-jp
* </TD>
- * <TD WIDTH="12%">
- * <P ALIGN="CENTER">MIME
+ * <TD>
+ * <P>MIME
* </TD>
- * <TD WIDTH="31%">
- * <P ALIGN="CENTER">eucjis
+ * <TD>
+ * <P>eucjis
* </TD>
* </TR>
* <TR>
- * <TD WIDTH="33%">Japanese: iso-2022-jp</TD>
- * <TD WIDTH="15%">
- * <P ALIGN="CENTER">iso-2020-jp
+ * <TD>Japanese: iso-2022-jp</TD>
+ * <TD>
+ * <P>iso-2020-jp
* </TD>
- * <TD WIDTH="12%">
- * <P ALIGN="CENTER">MIME
+ * <TD>
+ * <P>MIME
* </TD>
- * <TD WIDTH="31%">
- * <P ALIGN="CENTER">JIS
+ * <TD>
+ * <P>JIS
* </TD>
* </TR>
* <TR>
- * <TD WIDTH="33%">Japanese: Shift JIS</TD>
- * <TD WIDTH="15%">
- * <P ALIGN="CENTER">Shift_JIS
+ * <TD>Japanese: Shift JIS</TD>
+ * <TD>
+ * <P>Shift_JIS
* </TD>
- * <TD WIDTH="12%">
- * <P ALIGN="CENTER">MIME
+ * <TD>
+ * <P>MIME
* </TD>
- * <TD WIDTH="31%">
- * <P ALIGN="CENTER">SJIS
+ * <TD>
+ * <P>SJIS
* </TD>
* </TR>
* <TR>
- * <TD WIDTH="33%">Chinese: Big5</TD>
- * <TD WIDTH="15%">
- * <P ALIGN="CENTER">Big5
+ * <TD>Chinese: Big5</TD>
+ * <TD>
+ * <P>Big5
* </TD>
- * <TD WIDTH="12%">
- * <P ALIGN="CENTER">MIME
+ * <TD>
+ * <P>MIME
* </TD>
- * <TD WIDTH="31%">
- * <P ALIGN="CENTER">Big5
+ * <TD>
+ * <P>Big5
* </TD>
* </TR>
* <TR>
- * <TD WIDTH="33%">Extended Unix Code, packed for Korean</TD>
- * <TD WIDTH="15%">
- * <P ALIGN="CENTER">euc-kr
+ * <TD>Extended Unix Code, packed for Korean</TD>
+ * <TD>
+ * <P>euc-kr
* </TD>
- * <TD WIDTH="12%">
- * <P ALIGN="CENTER">MIME
+ * <TD>
+ * <P>MIME
* </TD>
- * <TD WIDTH="31%">
- * <P ALIGN="CENTER">iso2022kr
+ * <TD>
+ * <P>iso2022kr
* </TD>
* </TR>
* <TR>
- * <TD WIDTH="33%">Cyrillic</TD>
- * <TD WIDTH="15%">
- * <P ALIGN="CENTER">koi8-r
+ * <TD>Cyrillic</TD>
+ * <TD>
+ * <P>koi8-r
* </TD>
- * <TD WIDTH="12%">
- * <P ALIGN="CENTER">MIME
+ * <TD>
+ * <P>MIME
* </TD>
- * <TD WIDTH="31%">
- * <P ALIGN="CENTER">koi8-r
+ * <TD>
+ * <P>koi8-r
* </TD>
* </TR>
* </TABLE>
@@ -866,7 +866,9 @@ public class EncodingMap {
// Constructors
//
- /** Default constructor. */
+ /**
+ * Default constructor.
+ */
public EncodingMap() {}
//
@@ -877,6 +879,7 @@ public class EncodingMap {
* Returns the Java encoding name for the specified IANA encoding name.
*
* @param ianaEncoding The IANA encoding name.
+ * @return the Java encoding
*/
public static String getIANA2JavaMapping(String ianaEncoding) {
return fIANA2JavaMap.get(ianaEncoding);
diff --git a/java/org/apache/jasper/xmlparser/SymbolTable.java b/java/org/apache/jasper/xmlparser/SymbolTable.java
index 6e3f94c..7aa1fbe 100644
--- a/java/org/apache/jasper/xmlparser/SymbolTable.java
+++ b/java/org/apache/jasper/xmlparser/SymbolTable.java
@@ -56,14 +56,18 @@ public class SymbolTable {
// Constants
//
- /** Default table size. */
+ /**
+ * Default table size.
+ */
private static final int TABLE_SIZE = 101;
//
// Data
//
- /** Buckets. */
+ /**
+ * Buckets.
+ */
private final Entry[] fBuckets;
// actual table size
@@ -73,12 +77,17 @@ public class SymbolTable {
// Constructors
//
- /** Constructs a symbol table with a default number of buckets. */
+ /**
+ * Constructs a symbol table with a default number of buckets.
+ */
public SymbolTable() {
this(TABLE_SIZE);
}
- /** Constructs a symbol table with a specified number of buckets. */
+ /**
+ * Constructs a symbol table with a specified number of buckets.
+ * @param tableSize The table size (default is 101)
+ */
public SymbolTable(int tableSize) {
fTableSize = tableSize;
fBuckets = new Entry[fTableSize];
@@ -97,6 +106,7 @@ public class SymbolTable {
* @param buffer The buffer containing the new symbol.
* @param offset The offset into the buffer of the new symbol.
* @param length The length of the new symbol in the buffer.
+ * @return the symbol added
*/
public String addSymbol(char[] buffer, int offset, int length) {
@@ -130,6 +140,7 @@ public class SymbolTable {
* @param offset The offset into the character buffer of the start
* of the symbol.
* @param length The length of the symbol.
+ * @return the hash value
*/
public int hash(char[] buffer, int offset, int length) {
@@ -151,11 +162,9 @@ public class SymbolTable {
*/
private static final class Entry {
- //
- // Data
- //
-
- /** Symbol. */
+ /**
+ * Symbol.
+ */
private final String symbol;
/**
@@ -164,14 +173,12 @@ public class SymbolTable {
*/
private final char[] characters;
- /** The next entry. */
+ /**
+ * The next entry.
+ */
private final Entry next;
- //
- // Constructors
- //
-
- /**
+ /*
* Constructs a new entry from the specified symbol information and
* next entry reference.
*/
@@ -181,7 +188,5 @@ public class SymbolTable {
symbol = new String(characters).intern();
this.next = next;
}
-
- } // class Entry
-
-} // class SymbolTable
+ }
+}
\ No newline at end of file
diff --git a/java/org/apache/jasper/xmlparser/UCSReader.java b/java/org/apache/jasper/xmlparser/UCSReader.java
index 3e86f35..a97ce09 100644
--- a/java/org/apache/jasper/xmlparser/UCSReader.java
+++ b/java/org/apache/jasper/xmlparser/UCSReader.java
@@ -21,6 +21,9 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
+
/**
* Reader for UCS-2 and UCS-4 encodings.
* (i.e., encodings from ISO-10646-UCS-(2|4)).
@@ -29,8 +32,7 @@ import java.io.Reader;
*/
public class UCSReader extends Reader {
- private final org.apache.juli.logging.Log log=
- org.apache.juli.logging.LogFactory.getLog( UCSReader.class );
+ private final Log log = LogFactory.getLog(UCSReader.class);
//
// Constants
diff --git a/java/org/apache/jasper/xmlparser/UTF8Reader.java b/java/org/apache/jasper/xmlparser/UTF8Reader.java
index f286680..ac93d74 100644
--- a/java/org/apache/jasper/xmlparser/UTF8Reader.java
+++ b/java/org/apache/jasper/xmlparser/UTF8Reader.java
@@ -23,6 +23,8 @@ import java.io.Reader;
import java.io.UTFDataFormatException;
import org.apache.jasper.compiler.Localizer;
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
/**
* @author Andy Clark, IBM
@@ -30,8 +32,7 @@ import org.apache.jasper.compiler.Localizer;
public class UTF8Reader
extends Reader {
- private final org.apache.juli.logging.Log log=
- org.apache.juli.logging.LogFactory.getLog( UTF8Reader.class );
+ private final Log log = LogFactory.getLog(UTF8Reader.class);
// debugging
diff --git a/java/org/apache/jasper/xmlparser/XMLChar.java b/java/org/apache/jasper/xmlparser/XMLChar.java
index fb23956..836d23b 100644
--- a/java/org/apache/jasper/xmlparser/XMLChar.java
+++ b/java/org/apache/jasper/xmlparser/XMLChar.java
@@ -716,11 +716,12 @@ public class XMLChar {
//
/**
- * Returns true the supplemental character corresponding to the given
+ * Returns the supplemental character corresponding to the given
* surrogates.
*
* @param h The high surrogate.
* @param l The low surrogate.
+ * @return the supplemental character
*/
public static int supplemental(char h, char l) {
return (h - 0xD800) * 0x400 + (l - 0xDC00) + 0x10000;
@@ -730,6 +731,7 @@ public class XMLChar {
* Returns whether the given character is a high surrogate
*
* @param c The character to check.
+ * @return <code>true</code> if the character is a surrogate
*/
public static boolean isHighSurrogate(int c) {
return (0xD800 <= c && c <= 0xDBFF);
@@ -739,6 +741,7 @@ public class XMLChar {
* Returns whether the given character is a low surrogate
*
* @param c The character to check.
+ * @return <code>true</code> if the character is a surrogate
*/
public static boolean isLowSurrogate(int c) {
return (0xDC00 <= c && c <= 0xDFFF);
@@ -754,6 +757,7 @@ public class XMLChar {
* the surrogate character range.
*
* @param c The character to check.
+ * @return <code>true</code> if the character is valid
*/
public static boolean isValid(int c) {
return (c < 0x10000 && (CHARS[c] & MASK_VALID) != 0) ||
@@ -764,6 +768,7 @@ public class XMLChar {
* Returns true if the specified character is invalid.
*
* @param c The character to check.
+ * @return <code>true</code> if the character is invalid
*/
public static boolean isInvalid(int c) {
return !isValid(c);
@@ -773,6 +778,7 @@ public class XMLChar {
* Returns true if the specified character can be considered content.
*
* @param c The character to check.
+ * @return <code>true</code> if the character is content
*/
public static boolean isContent(int c) {
return (c < 0x10000 && (CHARS[c] & MASK_CONTENT) != 0) ||
@@ -784,6 +790,7 @@ public class XMLChar {
* as defined by production [3] in the XML 1.0 specification.
*
* @param c The character to check.
+ * @return <code>true</code> if the character is space
*/
public static boolean isSpace(int c) {
return c <= 0x20 && (CHARS[c] & MASK_SPACE) != 0;
@@ -795,6 +802,7 @@ public class XMLChar {
* specification.
*
* @param c The character to check.
+ * @return <code>true</code> if the character is a valid start name
*/
public static boolean isNameStart(int c) {
return c < 0x10000 && (CHARS[c] & MASK_NAME_START) != 0;
@@ -806,6 +814,7 @@ public class XMLChar {
* specification.
*
* @param c The character to check.
+ * @return <code>true</code> if the character is valid in a name
*/
public static boolean isName(int c) {
return c < 0x10000 && (CHARS[c] & MASK_NAME) != 0;
@@ -820,6 +829,7 @@ public class XMLChar {
* IANA encoding name.
*
* @param ianaEncoding The IANA encoding name.
+ * @return <code>true</code> if the character is valid encoding
*/
public static boolean isValidIANAEncoding(String ianaEncoding) {
if (ianaEncoding != null) {
diff --git a/java/org/apache/jasper/xmlparser/XMLEncodingDetector.java b/java/org/apache/jasper/xmlparser/XMLEncodingDetector.java
index 800d36e..8c3edda 100644
--- a/java/org/apache/jasper/xmlparser/XMLEncodingDetector.java
+++ b/java/org/apache/jasper/xmlparser/XMLEncodingDetector.java
@@ -87,12 +87,17 @@ public class XMLEncodingDetector {
*
* Encoding autodetection is done according to the XML 1.0 specification,
* Appendix F.1: Detection Without External Encoding Information.
- *
+ * @param fname The file name
+ * @param jar The containing jar
+ * @param ctxt The compilation context
+ * @param err The error dispatcher
* @return Two-element array, where the first element (of type
* java.lang.String) contains the name of the (auto)detected encoding, and
* the second element (of type java.lang.Boolean) specifies whether the
* encoding was specified using the 'encoding' attribute of an XML prolog
* (TRUE) or autodetected (FALSE).
+ * @throws IOException Error reading resource
+ * @throws JasperException Other error, usually a bad encoding
*/
public static Object[] getEncoding(String fname, Jar jar,
JspCompilationContext ctxt,
@@ -194,6 +199,8 @@ public class XMLEncodingDetector {
* is bigEndian. null means unknown or not relevant.
*
* @return Returns a reader.
+ * @throws IOException Error reading resource
+ * @throws JasperException Other error, usually a bad encoding
*/
private Reader createReader(InputStream inputStream, String encoding,
Boolean isBigEndian)
@@ -359,7 +366,9 @@ public class XMLEncodingDetector {
// Adapted from:
// org.apache.xerces.impl.XMLEntityManager.EntityScanner.isExternal
- /** Returns true if the current entity being scanned is external. */
+ /**
+ * @return <code>true</code> if the current entity being scanned is external.
+ */
public boolean isExternal() {
return true;
}
@@ -370,7 +379,7 @@ public class XMLEncodingDetector {
* Returns the next character on the input.
* <p>
* <strong>Note:</strong> The character is <em>not</em> consumed.
- *
+ * @return the next char
* @throws IOException Thrown if i/o error occurs.
* @throws EOFException Thrown on end of file.
*/
@@ -400,7 +409,7 @@ public class XMLEncodingDetector {
* Returns the next character on the input.
* <p>
* <strong>Note:</strong> The character is consumed.
- *
+ * @return the next char
* @throws IOException Thrown if i/o error occurs.
* @throws EOFException Thrown on end of file.
*/
@@ -443,7 +452,7 @@ public class XMLEncodingDetector {
* <p>
* <strong>Note:</strong> The string returned must be a symbol. The
* SymbolTable can be used for this purpose.
- *
+ * @return the name
* @throws IOException Thrown if i/o error occurs.
* @throws EOFException Thrown on end of file.
*
@@ -650,7 +659,8 @@ public class XMLEncodingDetector {
* data to be scanned.
* @param buffer The data structure to fill.
*
- * @return Returns true if there is more data to scan, false otherwise.
+ * @return <code>true</code> if there is more data to scan,
+ * <code>false</code> otherwise.
*
* @throws IOException Thrown if i/o error occurs.
* @throws EOFException Thrown on end of file.
@@ -792,7 +802,7 @@ public class XMLEncodingDetector {
*
* @param c The character to skip.
*
- * @return Returns true if the character was skipped.
+ * @return <code>true</code> if the character was skipped.
*
* @throws IOException Thrown if i/o error occurs.
* @throws EOFException Thrown on end of file.
@@ -835,7 +845,7 @@ public class XMLEncodingDetector {
* <strong>Note:</strong> The characters are consumed only if they are
* space characters.
*
- * @return Returns true if at least one space character was skipped.
+ * @return <code>true</code> if at least one space character was skipped.
*
* @throws IOException Thrown if i/o error occurs.
* @throws EOFException Thrown on end of file.
@@ -904,7 +914,7 @@ public class XMLEncodingDetector {
*
* @param s The string to skip.
*
- * @return Returns true if the string was skipped.
+ * @return <code>true</code> if the string was skipped.
*
* @throws IOException Thrown if i/o error occurs.
* @throws EOFException Thrown on end of file.
@@ -951,8 +961,9 @@ public class XMLEncodingDetector {
* boundary will be signaled by the return
* value.
*
- * @return Returns true if the entity changed as a result of this
+ * @return <code>true</code> if the entity changed as a result of this
* load operation.
+ * @throws IOException Error reading data
*/
final boolean load(int offset, boolean changeEntity)
throws IOException {
@@ -1202,6 +1213,8 @@ public class XMLEncodingDetector {
* @param scanningTextDecl True if a text declaration is to
* be scanned instead of an XML
* declaration.
+ * @throws IOException Error reading data
+ * @throws JasperException Other error
*/
private void scanXMLDeclOrTextDecl(boolean scanningTextDecl)
throws IOException, JasperException {
@@ -1235,6 +1248,8 @@ public class XMLEncodingDetector {
* [77] TextDecl ::= '<?xml' VersionInfo? EncodingDecl S? '?>'
* </pre>
*
+ * <strong>Note:</strong> This method uses fString, anything in it
+ * at the time of calling is lost.
* @param scanningTextDecl True if a text declaration is to
* be scanned instead of an XML
* declaration.
@@ -1242,8 +1257,8 @@ public class XMLEncodingDetector {
* encoding and standalone pseudo attribute values
* (in that order).
*
- * <strong>Note:</strong> This method uses fString, anything in it
- * at the time of calling is lost.
+ * @throws IOException Error reading data
+ * @throws JasperException Other error
*/
private void scanXMLDeclOrTextDecl(boolean scanningTextDecl,
String[] pseudoAttributeValues)
@@ -1390,6 +1405,8 @@ public class XMLEncodingDetector {
/**
* Scans a pseudo attribute.
*
+ * <strong>Note:</strong> This method uses fStringBuffer2, anything in it
+ * at the time of calling is lost.
* @param scanningTextDecl True if scanning this pseudo-attribute for a
* TextDecl; false if scanning XMLDecl. This
* flag is needed to report the correct type of
@@ -1398,9 +1415,8 @@ public class XMLEncodingDetector {
* value.
*
* @return The name of the attribute
- *
- * <strong>Note:</strong> This method uses fStringBuffer2, anything in it
- * at the time of calling is lost.
+ * @throws IOException Error reading data
+ * @throws JasperException Other error
*/
public String scanPseudoAttribute(boolean scanningTextDecl,
XMLString value)
@@ -1475,6 +1491,8 @@ public class XMLEncodingDetector {
*
* @param target The PI target
* @param data The string to fill in with the data
+ * @throws IOException Error reading data
+ * @throws JasperException Other error
*/
private void scanPIData(String target, XMLString data)
throws IOException, JasperException {
@@ -1531,7 +1549,9 @@ public class XMLEncodingDetector {
* identified as a high surrogate.
*
* @param buf The StringBuffer to append the read surrogates to.
- * @return True if it succeeded.
+ * @return <code>true</code> if it succeeded.
+ * @throws IOException Error reading data
+ * @throws JasperException Other error
*/
private boolean scanSurrogates(XMLStringBuffer buf)
throws IOException, JasperException {
@@ -1567,6 +1587,9 @@ public class XMLEncodingDetector {
// org.apache.xerces.impl.XMLScanner.reportFatalError
/**
* Convenience function used in all XML scanners.
+ * @param msgId The message key
+ * @param arg The argument
+ * @throws JasperException The created exception
*/
private void reportFatalError(String msgId, String arg)
throws JasperException {
diff --git a/java/org/apache/jasper/xmlparser/XMLString.java b/java/org/apache/jasper/xmlparser/XMLString.java
index b0c7f1d..22a2dd4 100644
--- a/java/org/apache/jasper/xmlparser/XMLString.java
+++ b/java/org/apache/jasper/xmlparser/XMLString.java
@@ -53,20 +53,28 @@ public class XMLString {
// Data
//
- /** The character array. */
+ /**
+ * The character array.
+ */
public char[] ch;
- /** The offset into the character array. */
+ /**
+ * The offset into the character array.
+ */
public int offset;
- /** The length of characters from the offset. */
+ /**
+ * The length of characters from the offset.
+ */
public int length;
//
// Constructors
//
- /** Default constructor. */
+ /**
+ * Default constructor.
+ */
public XMLString() {
} // <init>()
@@ -95,13 +103,15 @@ public class XMLString {
* <strong>Note:</strong> This does not copy the character array;
* only the reference to the array is copied.
*
- * @param s
+ * @param s The string
*/
public void setValues(XMLString s) {
setValues(s.ch, s.offset, s.length);
} // setValues(XMLString)
- /** Resets all of the values to their defaults. */
+ /**
+ * Resets all of the values to their defaults.
+ */
public void clear() {
this.ch = null;
this.offset = 0;
@@ -114,6 +124,7 @@ public class XMLString {
* the specified string are equal.
*
* @param s The string to compare.
+ * @return <code>true</code> if equal
*/
public boolean equals(String s) {
if (s == null) {
@@ -139,7 +150,6 @@ public class XMLString {
// Object methods
//
- /** Returns a string representation of this object. */
@Override
public String toString() {
return length > 0 ? new String(ch, offset, length) : "";
diff --git a/java/org/apache/jasper/xmlparser/XMLStringBuffer.java b/java/org/apache/jasper/xmlparser/XMLStringBuffer.java
index bb79720..9093372 100644
--- a/java/org/apache/jasper/xmlparser/XMLStringBuffer.java
+++ b/java/org/apache/jasper/xmlparser/XMLStringBuffer.java
@@ -51,7 +51,9 @@ public class XMLStringBuffer
// Constants
//
- /** Default buffer size (32). */
+ /**
+ * Default buffer size (32).
+ */
private static final int DEFAULT_SIZE = 32;
//
@@ -59,16 +61,15 @@ public class XMLStringBuffer
//
/**
- *
+ * Build a string buffer with the default size (32).
*/
public XMLStringBuffer() {
this(DEFAULT_SIZE);
} // <init>()
/**
- *
- *
- * @param size
+ * Build a string buffer with the specified size.
+ * @param size The backing array size
*/
public XMLStringBuffer(int size) {
ch = new char[size];
@@ -78,7 +79,9 @@ public class XMLStringBuffer
// Public methods
//
- /** Clears the string buffer. */
+ /**
+ * Clears the string buffer.
+ */
@Override
public void clear() {
offset = 0;
@@ -86,9 +89,9 @@ public class XMLStringBuffer
}
/**
- * append
+ * Append character.
*
- * @param c
+ * @param c The character to append
*/
public void append(char c) {
if (this.length + 1 > this.ch.length) {
@@ -104,9 +107,9 @@ public class XMLStringBuffer
} // append(char)
/**
- * append
+ * Append string.
*
- * @param s
+ * @param s The string to append
*/
public void append(String s) {
int length = s.length();
@@ -123,11 +126,11 @@ public class XMLStringBuffer
} // append(String)
/**
- * append
+ * Append characters.
*
- * @param ch
- * @param offset
- * @param length
+ * @param ch The character array
+ * @param offset The offset
+ * @param length The length
*/
public void append(char[] ch, int offset, int length) {
if (this.length + length > this.ch.length) {
@@ -140,9 +143,9 @@ public class XMLStringBuffer
} // append(char[],int,int)
/**
- * append
+ * Append XML string
*
- * @param s
+ * @param s The string
*/
public void append(XMLString s) {
append(s.ch, s.offset, s.length);
diff --git a/java/org/apache/juli/AsyncFileHandler.java b/java/org/apache/juli/AsyncFileHandler.java
index 1776d94..786c066 100644
--- a/java/org/apache/juli/AsyncFileHandler.java
+++ b/java/org/apache/juli/AsyncFileHandler.java
@@ -40,20 +40,27 @@ import java.util.logging.LogRecord;
*/
public class AsyncFileHandler extends FileHandler {
- public static final int OVERFLOW_DROP_LAST = 1;
- public static final int OVERFLOW_DROP_FIRST = 2;
- public static final int OVERFLOW_DROP_FLUSH = 3;
+ public static final int OVERFLOW_DROP_LAST = 1;
+ public static final int OVERFLOW_DROP_FIRST = 2;
+ public static final int OVERFLOW_DROP_FLUSH = 3;
public static final int OVERFLOW_DROP_CURRENT = 4;
+ public static final int DEFAULT_OVERFLOW_DROP_TYPE = 1;
+ public static final int DEFAULT_MAX_RECORDS = 10000;
+ public static final int DEFAULT_LOGGER_SLEEP_TIME = 1000;
+
public static final int OVERFLOW_DROP_TYPE = Integer.parseInt(
- System.getProperty("org.apache.juli.AsyncOverflowDropType","1"));
- public static final int DEFAULT_MAX_RECORDS = Integer.parseInt(
- System.getProperty("org.apache.juli.AsyncMaxRecordCount","10000"));
+ System.getProperty("org.apache.juli.AsyncOverflowDropType",
+ Integer.toString(DEFAULT_OVERFLOW_DROP_TYPE)));
+ public static final int MAX_RECORDS = Integer.parseInt(
+ System.getProperty("org.apache.juli.AsyncMaxRecordCount",
+ Integer.toString(DEFAULT_MAX_RECORDS)));
public static final int LOGGER_SLEEP_TIME = Integer.parseInt(
- System.getProperty("org.apache.juli.AsyncLoggerPollInterval","1000"));
+ System.getProperty("org.apache.juli.AsyncLoggerPollInterval",
+ Integer.toString(DEFAULT_LOGGER_SLEEP_TIME)));
protected static final LinkedBlockingDeque<LogEntry> queue =
- new LinkedBlockingDeque<>(DEFAULT_MAX_RECORDS);
+ new LinkedBlockingDeque<>(MAX_RECORDS);
protected static final LoggerThread logger = new LoggerThread();
diff --git a/java/org/apache/juli/ClassLoaderLogManager.java b/java/org/apache/juli/ClassLoaderLogManager.java
index a919109..313a652 100644
--- a/java/org/apache/juli/ClassLoaderLogManager.java
+++ b/java/org/apache/juli/ClassLoaderLogManager.java
@@ -386,6 +386,7 @@ public class ClassLoaderLogManager extends LogManager {
*
* @param classLoader The classloader for which we will retrieve or build the
* configuration
+ * @return the log configuration
*/
protected synchronized ClassLoaderLogInfo getClassLoaderInfo(ClassLoader classLoader) {
@@ -415,8 +416,8 @@ public class ClassLoaderLogManager extends LogManager {
/**
* Read configuration for the specified classloader.
*
- * @param classLoader
- * @throws IOException Error
+ * @param classLoader The classloader
+ * @throws IOException Error reading configuration
*/
protected synchronized void readConfiguration(ClassLoader classLoader)
throws IOException {
@@ -584,8 +585,8 @@ public class ClassLoaderLogManager extends LogManager {
/**
* Set parent child relationship between the two specified loggers.
*
- * @param logger
- * @param parent
+ * @param logger The logger
+ * @param parent The parent logger
*/
protected static void doSetParentLogger(final Logger logger,
final Logger parent) {
diff --git a/java/org/apache/juli/DateFormatCache.java b/java/org/apache/juli/DateFormatCache.java
index ecbf31e..a311472 100644
--- a/java/org/apache/juli/DateFormatCache.java
+++ b/java/org/apache/juli/DateFormatCache.java
@@ -95,6 +95,10 @@ public class DateFormatCache {
return cache.getFormat(time);
}
+ public String getTimeFormat() {
+ return format;
+ }
+
private class Cache {
/* Second formatted in most recent invocation */
@@ -118,9 +122,6 @@ public class DateFormatCache {
private Cache(Cache parent) {
cache = new String[cacheSize];
- for (int i = 0; i < cacheSize; i++) {
- cache[i] = null;
- }
formatter = new SimpleDateFormat(format, Locale.US);
formatter.setTimeZone(TimeZone.getDefault());
this.parent = parent;
diff --git a/java/org/apache/juli/OneLineFormatter.java b/java/org/apache/juli/OneLineFormatter.java
index 0337f21..75732e9 100644
--- a/java/org/apache/juli/OneLineFormatter.java
+++ b/java/org/apache/juli/OneLineFormatter.java
@@ -14,7 +14,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
package org.apache.juli;
import java.io.PrintWriter;
@@ -26,6 +25,7 @@ import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.logging.Formatter;
+import java.util.logging.LogManager;
import java.util.logging.LogRecord;
/**
@@ -62,7 +62,7 @@ public class OneLineFormatter extends Formatter {
};
/* Timestamp format */
- private static final String timeFormat = "dd-MMM-yyyy HH:mm:ss";
+ private static final String DEFAULT_TIME_FORMAT = "dd-MMM-yyyy HH:mm:ss";
/**
* The size of our global date format cache
@@ -75,21 +75,48 @@ public class OneLineFormatter extends Formatter {
private static final int localCacheSize = 5;
/**
- * Global date format cache.
+ * Thread local date format cache.
*/
- private static final DateFormatCache globalDateCache =
- new DateFormatCache(globalCacheSize, timeFormat, null);
+ private ThreadLocal<DateFormatCache> localDateCache;
+
+
+ public OneLineFormatter() {
+ String timeFormat = LogManager.getLogManager().getProperty(
+ OneLineFormatter.class.getName() + ".timeFormat");
+ if (timeFormat == null) {
+ timeFormat = DEFAULT_TIME_FORMAT;
+ }
+ setTimeFormat(timeFormat);
+ }
+
/**
- * Thread local date format cache.
+ * Specify the time format to use for time stamps in log messages.
+ *
+ * @param timeFormat The format to use using the
+ * {@link java.text.SimpleDateFormat} syntax
*/
- private static final ThreadLocal<DateFormatCache> localDateCache =
- new ThreadLocal<DateFormatCache>() {
- @Override
- protected DateFormatCache initialValue() {
- return new DateFormatCache(localCacheSize, timeFormat, globalDateCache);
- }
- };
+ public void setTimeFormat(final String timeFormat) {
+ final DateFormatCache globalDateCache =
+ new DateFormatCache(globalCacheSize, timeFormat, null);
+ localDateCache = new ThreadLocal<DateFormatCache>() {
+ @Override
+ protected DateFormatCache initialValue() {
+ return new DateFormatCache(localCacheSize, timeFormat, globalDateCache);
+ }
+ };
+ }
+
+
+ /**
+ * Obtain the format currently being used for time stamps in log messages.
+ *
+ * @return The current format in {@link java.text.SimpleDateFormat} syntax
+ */
+ public String getTimeFormat() {
+ return localDateCache.get().getTimeFormat();
+ }
+
@Override
public String format(LogRecord record) {
diff --git a/java/org/apache/juli/logging/DirectJDKLog.java b/java/org/apache/juli/logging/DirectJDKLog.java
index 53b271a..b5c565a 100644
--- a/java/org/apache/juli/logging/DirectJDKLog.java
+++ b/java/org/apache/juli/logging/DirectJDKLog.java
@@ -50,11 +50,10 @@ class DirectJDKLog implements Log {
// it is also possible that the user modified jre/lib/logging.properties -
// but that's really stupid in most cases
Logger root=Logger.getLogger("");
- Handler handlers[]=root.getHandlers();
- for( int i=0; i< handlers.length; i++ ) {
+ for (Handler handler : root.getHandlers()) {
// I only care about console - that's what's used in default config anyway
- if( handlers[i] instanceof ConsoleHandler ) {
- handlers[i].setFormatter(fmt);
+ if (handler instanceof ConsoleHandler) {
+ handler.setFormatter(fmt);
}
}
} catch( Throwable t ) {
diff --git a/java/org/apache/juli/logging/Log.java b/java/org/apache/juli/logging/Log.java
index 6e3865e..e016676 100644
--- a/java/org/apache/juli/logging/Log.java
+++ b/java/org/apache/juli/logging/Log.java
@@ -71,6 +71,9 @@ public interface Log {
* <p> Call this method to prevent having to perform expensive operations
* (for example, <code>String</code> concatenation)
* when the log level is more than debug. </p>
+ *
+ * @return <code>true</code> if debug level logging is enabled, otherwise
+ * <code>false</code>
*/
public boolean isDebugEnabled();
@@ -81,6 +84,9 @@ public interface Log {
* <p> Call this method to prevent having to perform expensive operations
* (for example, <code>String</code> concatenation)
* when the log level is more than error. </p>
+ *
+ * @return <code>true</code> if error level logging is enabled, otherwise
+ * <code>false</code>
*/
public boolean isErrorEnabled();
@@ -91,6 +97,9 @@ public interface Log {
* <p> Call this method to prevent having to perform expensive operations
* (for example, <code>String</code> concatenation)
* when the log level is more than fatal. </p>
+ *
+ * @return <code>true</code> if fatal level logging is enabled, otherwise
+ * <code>false</code>
*/
public boolean isFatalEnabled();
@@ -101,6 +110,9 @@ public interface Log {
* <p> Call this method to prevent having to perform expensive operations
* (for example, <code>String</code> concatenation)
* when the log level is more than info. </p>
+ *
+ * @return <code>true</code> if info level logging is enabled, otherwise
+ * <code>false</code>
*/
public boolean isInfoEnabled();
@@ -111,6 +123,9 @@ public interface Log {
* <p> Call this method to prevent having to perform expensive operations
* (for example, <code>String</code> concatenation)
* when the log level is more than trace. </p>
+ *
+ * @return <code>true</code> if trace level logging is enabled, otherwise
+ * <code>false</code>
*/
public boolean isTraceEnabled();
@@ -121,6 +136,9 @@ public interface Log {
* <p> Call this method to prevent having to perform expensive operations
* (for example, <code>String</code> concatenation)
* when the log level is more than warn. </p>
+ *
+ * @return <code>true</code> if warn level logging is enabled, otherwise
+ * <code>false</code>
*/
public boolean isWarnEnabled();
diff --git a/java/org/apache/juli/logging/LogFactory.java b/java/org/apache/juli/logging/LogFactory.java
index a9e554e..6717610 100644
--- a/java/org/apache/juli/logging/LogFactory.java
+++ b/java/org/apache/juli/logging/LogFactory.java
@@ -107,6 +107,8 @@ public class LogFactory {
* 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 <code>Log</code>
* instance cannot be returned
*/
@@ -130,6 +132,8 @@ public class LogFactory {
*
* @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 <code>Log</code>
* instance cannot be returned
*/
@@ -166,6 +170,8 @@ public class LogFactory {
* properties defined in this file will be set as configuration attributes
* on the corresponding <code>LogFactory</code> instance.</p>
*
+ * @return The singleton LogFactory instance
+ *
* @exception LogConfigurationException if the implementation class is not
* available or cannot be instantiated.
*/
@@ -180,6 +186,8 @@ public class LogFactory {
*
* @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 <code>Log</code>
* instance cannot be returned
*/
@@ -198,6 +206,8 @@ public class LogFactory {
* 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 <code>Log</code>
* instance cannot be returned
*/
diff --git a/java/org/apache/naming/Constants.java b/java/org/apache/naming/Constants.java
index d6e6503..2b41257 100644
--- a/java/org/apache/naming/Constants.java
+++ b/java/org/apache/naming/Constants.java
@@ -14,15 +14,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
-
package org.apache.naming;
/**
* Static constants for this package.
+ *
+ * @deprecated Unused. Will be removed in Tomcat 9.
*/
-
+ at Deprecated
public final class Constants {
public static final String Package = "org.apache.naming";
diff --git a/java/org/apache/naming/ContextAccessController.java b/java/org/apache/naming/ContextAccessController.java
index cc76132..20f751b 100644
--- a/java/org/apache/naming/ContextAccessController.java
+++ b/java/org/apache/naming/ContextAccessController.java
@@ -14,8 +14,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
-
package org.apache.naming;
import java.util.Hashtable;
@@ -27,27 +25,22 @@ import java.util.Hashtable;
*/
public class ContextAccessController {
-
// -------------------------------------------------------------- Variables
-
/**
* Catalina context names on which writing is not allowed.
*/
- private static final Hashtable<Object,Object> readOnlyContexts =
- new Hashtable<>();
+ private static final Hashtable<Object,Object> readOnlyContexts = new Hashtable<>();
/**
* Security tokens repository.
*/
- private static final Hashtable<Object,Object> securityTokens =
- new Hashtable<>();
+ private static final Hashtable<Object,Object> securityTokens = new Hashtable<>();
// --------------------------------------------------------- Public Methods
-
/**
* Set a security token for a Catalina context. Can be set only once.
*
@@ -81,12 +74,14 @@ public class ContextAccessController {
/**
- * Check a submitted security token. The submitted token must be equal to
- * the token present in the repository. If no token is present for the
- * context, then returns true.
+ * Check a submitted security token.
*
* @param name Name of the Catalina context
* @param token Submitted security token
+ *
+ * @return <code>true</code> if the submitted token is equal to the token
+ * in the repository or if no token is present in the repository.
+ * Otherwise, <code>false</code>
*/
public static boolean checkSecurityToken
(Object name, Object token) {
@@ -118,14 +113,14 @@ public class ContextAccessController {
/**
- * Returns if a context is writable.
+ * Is the context is writable?
*
* @param name Name of the Catalina context
+ *
+ * @return <code>true</code> if it is writable, otherwise <code>false</code>
*/
public static boolean isWritable(Object name) {
return !(readOnlyContexts.containsKey(name));
}
-
-
}
diff --git a/java/org/apache/naming/ContextBindings.java b/java/org/apache/naming/ContextBindings.java
index 289b6fd..0f2f282 100644
--- a/java/org/apache/naming/ContextBindings.java
+++ b/java/org/apache/naming/ContextBindings.java
@@ -37,55 +37,46 @@ import javax.naming.NamingException;
*/
public class ContextBindings {
-
// -------------------------------------------------------------- Variables
-
/**
* Bindings object - naming context. Keyed by object.
*/
- private static final Hashtable<Object,Context> objectBindings =
- new Hashtable<>();
+ private static final Hashtable<Object,Context> objectBindings = new Hashtable<>();
/**
* Bindings thread - naming context. Keyed by thread.
*/
- private static final Hashtable<Thread,Context> threadBindings =
- new Hashtable<>();
+ private static final Hashtable<Thread,Context> threadBindings = new Hashtable<>();
/**
* Bindings thread - object. Keyed by thread.
*/
- private static final Hashtable<Thread,Object> threadObjectBindings =
- new Hashtable<>();
+ private static final Hashtable<Thread,Object> threadObjectBindings = new Hashtable<>();
/**
* Bindings class loader - naming context. Keyed by class loader.
*/
- private static final Hashtable<ClassLoader,Context> clBindings =
- new Hashtable<>();
+ private static final Hashtable<ClassLoader,Context> clBindings = new Hashtable<>();
/**
* Bindings class loader - object. Keyed by class loader.
*/
- private static final Hashtable<ClassLoader,Object> clObjectBindings =
- new Hashtable<>();
+ private static final Hashtable<ClassLoader,Object> clObjectBindings = new Hashtable<>();
/**
* The string manager for this package.
*/
- protected static final StringManager sm =
- StringManager.getManager(Constants.Package);
+ protected static final StringManager sm = StringManager.getManager(ContextBindings.class);
// --------------------------------------------------------- Public Methods
-
/**
* Binds an object and a naming context.
*
@@ -139,6 +130,9 @@ public class ContextBindings {
*
* @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
*/
public static void bindThread(Object obj, Object token) throws NamingException {
if (ContextAccessController.checkSecurityToken(obj, token)) {
@@ -169,6 +163,11 @@ public class ContextBindings {
/**
* Retrieves the naming context bound to the current thread.
+ *
+ * @return The naming context 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());
@@ -196,6 +195,9 @@ public class ContextBindings {
/**
* Tests if current thread is bound to a naming context.
+ *
+ * @return <code>true</code> if the current thread is bound to a naming
+ * context, otherwise <code>false</code>
*/
public static boolean isThreadBound() {
return (threadBindings.containsKey(Thread.currentThread()));
@@ -208,6 +210,9 @@ public class ContextBindings {
* @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
*/
public static void bindClassLoader(Object obj, Object token,
ClassLoader classLoader) throws NamingException {
@@ -245,6 +250,11 @@ public class ContextBindings {
/**
* Retrieves the naming context bound to a class loader.
+ *
+ * @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();
@@ -278,6 +288,10 @@ public class ContextBindings {
/**
* Tests if the thread context class loader is bound to a context.
+ *
+ * @return <code>true</code> if the thread context class loader or one of
+ * its parents is bound to a naming context, otherwise
+ * <code>false</code>
*/
public static boolean isClassLoaderBound() {
ClassLoader cl = Thread.currentThread().getContextClassLoader();
diff --git a/java/org/apache/naming/EjbRef.java b/java/org/apache/naming/EjbRef.java
index 71eac49..57a88a1 100644
--- a/java/org/apache/naming/EjbRef.java
+++ b/java/org/apache/naming/EjbRef.java
@@ -78,9 +78,12 @@ public class EjbRef extends Reference {
* 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
+ * @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) {
diff --git a/java/org/apache/naming/NamingContext.java b/java/org/apache/naming/NamingContext.java
index aa93dc8..53df219 100644
--- a/java/org/apache/naming/NamingContext.java
+++ b/java/org/apache/naming/NamingContext.java
@@ -40,6 +40,9 @@ import javax.naming.Reference;
import javax.naming.Referenceable;
import javax.naming.spi.NamingManager;
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
+
/**
* Catalina JNDI Context implementation.
*
@@ -57,8 +60,7 @@ public class NamingContext implements Context {
protected static final NameParser nameParser = new NameParserImpl();
- private static final org.apache.juli.logging.Log log =
- org.apache.juli.logging.LogFactory.getLog(NamingContext.class);
+ private static final Log log = LogFactory.getLog(NamingContext.class);
// ----------------------------------------------------------- Constructors
@@ -70,8 +72,7 @@ public class NamingContext implements Context {
* @param env The environment to use to construct the naming context
* @param name The name of the associated Catalina Context
*/
- public NamingContext(Hashtable<String,Object> env, String name)
- throws NamingException {
+ public NamingContext(Hashtable<String,Object> env, String name) {
this(env, name, new HashMap<String,NamingEntry>());
}
@@ -84,7 +85,7 @@ public class NamingContext implements Context {
* @param bindings The initial bindings for the naming context
*/
public NamingContext(Hashtable<String,Object> env, String name,
- HashMap<String,NamingEntry> bindings) throws NamingException {
+ HashMap<String,NamingEntry> bindings) {
this.env = new Hashtable<>();
this.name = name;
@@ -112,7 +113,7 @@ public class NamingContext implements Context {
/**
* The string manager for this package.
*/
- protected static final StringManager sm = StringManager.getManager(Constants.Package);
+ protected static final StringManager sm = StringManager.getManager(NamingContext.class);
/**
@@ -682,8 +683,7 @@ public class NamingContext implements Context {
* @exception NamingException if a naming exception is encountered
*/
@Override
- public Name composeName(Name name, Name prefix)
- throws NamingException {
+ public Name composeName(Name name, Name prefix) throws NamingException {
prefix = (Name) prefix.clone();
return prefix.addAll(name);
}
@@ -695,11 +695,9 @@ public class NamingContext implements Context {
* @param name a name relative to this context
* @param prefix the name of this context relative to one of its ancestors
* @return the composition of prefix and name
- * @exception NamingException if a naming exception is encountered
*/
@Override
- public String composeName(String name, String prefix)
- throws NamingException {
+ public String composeName(String name, String prefix) {
return prefix + "/" + name;
}
@@ -711,11 +709,9 @@ public class NamingContext implements Context {
* @param propName the name of the environment property to add; may not
* be null
* @param propVal the value of the property to add; may not be null
- * @exception NamingException if a naming exception is encountered
*/
@Override
- public Object addToEnvironment(String propName, Object propVal)
- throws NamingException {
+ public Object addToEnvironment(String propName, Object propVal) {
return env.put(propName, propVal);
}
@@ -725,11 +721,9 @@ public class NamingContext implements Context {
*
* @param propName the name of the environment property to remove;
* may not be null
- * @exception NamingException if a naming exception is encountered
*/
@Override
- public Object removeFromEnvironment(String propName)
- throws NamingException {
+ public Object removeFromEnvironment(String propName){
return env.remove(propName);
}
@@ -742,11 +736,9 @@ public class NamingContext implements Context {
* may be changed using addToEnvironment() and removeFromEnvironment().
*
* @return the environment of this context; never null
- * @exception NamingException if a naming exception is encountered
*/
@Override
- public Hashtable<?,?> getEnvironment()
- throws NamingException {
+ public Hashtable<?,?> getEnvironment() {
return env;
}
@@ -948,7 +940,7 @@ public class NamingContext implements Context {
/**
- * Returns true if writing is allowed on this context.
+ * @return <code>true</code> if writing is allowed on this context.
*/
protected boolean isWritable() {
return ContextAccessController.isWritable(name);
@@ -957,6 +949,9 @@ public class NamingContext implements Context {
/**
* Throws a naming exception is Context is not writable.
+ * @return <code>true</code> if the Context is writable
+ * @throws NamingException if the Context is not writable and
+ * <code>exceptionOnFailedWrite</code> is <code>true</code>
*/
protected boolean checkWritable() throws NamingException {
if (isWritable()) {
diff --git a/java/org/apache/naming/ResourceLinkRef.java b/java/org/apache/naming/ResourceLinkRef.java
index f0aa710..dd5f3ad 100644
--- a/java/org/apache/naming/ResourceLinkRef.java
+++ b/java/org/apache/naming/ResourceLinkRef.java
@@ -48,6 +48,9 @@ public class ResourceLinkRef extends 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)
*/
public ResourceLinkRef(String resourceClass, String globalName,
String factory, String factoryLocation) {
diff --git a/java/org/apache/naming/ResourceRef.java b/java/org/apache/naming/ResourceRef.java
index 4673c17..24caa09 100644
--- a/java/org/apache/naming/ResourceRef.java
+++ b/java/org/apache/naming/ResourceRef.java
@@ -74,8 +74,11 @@ public class ResourceRef extends Reference {
* 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)?
*/
public ResourceRef(String resourceClass, String description,
String scope, String auth, boolean singleton) {
@@ -87,8 +90,14 @@ public class ResourceRef extends Reference {
* 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,
diff --git a/java/org/apache/naming/SelectorContext.java b/java/org/apache/naming/SelectorContext.java
index 3fcbdd5..e957945 100644
--- a/java/org/apache/naming/SelectorContext.java
+++ b/java/org/apache/naming/SelectorContext.java
@@ -28,6 +28,9 @@ import javax.naming.NameParser;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
+
/**
* Catalina JNDI Context implementation.
*
@@ -57,14 +60,14 @@ public class SelectorContext implements Context {
public static final String IC_PREFIX = "IC_";
- private static final org.apache.juli.logging.Log log =
- org.apache.juli.logging.LogFactory.getLog(SelectorContext.class);
+ private static final Log log = LogFactory.getLog(SelectorContext.class);
// ----------------------------------------------------------- Constructors
/**
* Builds a Catalina selector context using the given environment.
+ * @param env The environment
*/
public SelectorContext(Hashtable<String,Object> env) {
this.env = env;
@@ -74,6 +77,9 @@ public class SelectorContext implements Context {
/**
* Builds a Catalina selector context using the given environment.
+ * @param env The environment
+ * @param initialContext <code>true</code> if this is the main
+ * initial context
*/
public SelectorContext(Hashtable<String,Object> env,
boolean initialContext) {
@@ -94,7 +100,7 @@ public class SelectorContext implements Context {
/**
* The string manager for this package.
*/
- protected static final StringManager sm = StringManager.getManager(Constants.Package);
+ protected static final StringManager sm = StringManager.getManager(SelectorContext.class);
/**
@@ -701,6 +707,9 @@ public class SelectorContext implements Context {
/**
* Get the bound context.
+ * @return the Context bound with either the current thread or
+ * the current classloader
+ * @throws NamingException Bindings exception
*/
protected Context getBoundContext()
throws NamingException {
@@ -733,7 +742,7 @@ public class SelectorContext implements Context {
/**
* 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
@@ -757,7 +766,7 @@ public class SelectorContext implements Context {
/**
* 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
diff --git a/java/org/apache/naming/ServiceRef.java b/java/org/apache/naming/ServiceRef.java
index 5e62616..a33f963 100644
--- a/java/org/apache/naming/ServiceRef.java
+++ b/java/org/apache/naming/ServiceRef.java
@@ -134,6 +134,7 @@ public class ServiceRef extends Reference {
/**
* Add and Get Handlers classes.
+ * @return the handler
*/
public HandlerRef getHandler() {
return handlers.remove(0);
@@ -153,6 +154,7 @@ public class ServiceRef extends Reference {
/**
* Retrieves the class name of the factory of the object to which this
* reference refers.
+ * @return the factory
*/
@Override
public String getFactoryClassName() {
diff --git a/java/org/apache/naming/factory/FactoryBase.java b/java/org/apache/naming/factory/FactoryBase.java
index 2c58e85..4147e40 100644
--- a/java/org/apache/naming/factory/FactoryBase.java
+++ b/java/org/apache/naming/factory/FactoryBase.java
@@ -121,7 +121,7 @@ public abstract class FactoryBase implements ObjectFactory {
* @return The default factory for the given reference object or
* <code>null</code> if no default factory exists.
*
- * @throws NamingException If the default factory can not be craeted
+ * @throws NamingException If the default factory cannot be craeted
*/
protected abstract ObjectFactory getDefaultFactory(Reference ref)
throws NamingException;
@@ -133,6 +133,7 @@ public abstract class FactoryBase implements ObjectFactory {
*
* @return The linked object or <code>null</code> 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 --git a/java/org/apache/naming/factory/SendMailFactory.java b/java/org/apache/naming/factory/SendMailFactory.java
index d09ff35..882cc4f 100644
--- a/java/org/apache/naming/factory/SendMailFactory.java
+++ b/java/org/apache/naming/factory/SendMailFactory.java
@@ -41,7 +41,6 @@ import javax.naming.spi.ObjectFactory;
* of your server.xml configuration file.
* <p>
* Example:
- * <p>
* <pre>
* <Resource name="mail/send" auth="CONTAINER"
* type="javax.mail.internet.MimePartDataSource"/>
diff --git a/java/org/apache/tomcat/ContextBind.java b/java/org/apache/tomcat/ContextBind.java
index b0f0197..ab9a1d7 100644
--- a/java/org/apache/tomcat/ContextBind.java
+++ b/java/org/apache/tomcat/ContextBind.java
@@ -39,7 +39,7 @@ public interface ContextBind {
* the thread context class loader in use when the method was
* called. If no change was made then this method returns null.
*/
- public ClassLoader bind(boolean usePrivilegedAction, ClassLoader originalClassLoader);
+ ClassLoader bind(boolean usePrivilegedAction, ClassLoader originalClassLoader);
/**
* Restore the current thread context class loader to the original class
@@ -56,6 +56,5 @@ public interface ContextBind {
* @param originalClassLoader
* The class loader to restore as the thread context class loader
*/
- public void unbind(boolean usePrivilegedAction, ClassLoader originalClassLoader);
-
+ void unbind(boolean usePrivilegedAction, ClassLoader originalClassLoader);
}
diff --git a/java/org/apache/tomcat/InstanceManagerBindings.java b/java/org/apache/tomcat/InstanceManagerBindings.java
new file mode 100644
index 0000000..66bc61a
--- /dev/null
+++ b/java/org/apache/tomcat/InstanceManagerBindings.java
@@ -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.tomcat;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+public final class InstanceManagerBindings {
+
+ private static final Map<ClassLoader, InstanceManager> bindings = new ConcurrentHashMap<>();
+
+ public static final void bind(ClassLoader classLoader, InstanceManager instanceManager) {
+ bindings.put(classLoader, instanceManager);
+ }
+ public static final void unbind(ClassLoader classLoader) {
+ bindings.remove(classLoader);
+ }
+ public static final InstanceManager get(ClassLoader classLoader) {
+ return bindings.get(classLoader);
+ }
+
+}
diff --git a/java/org/apache/tomcat/JarScannerCallback.java b/java/org/apache/tomcat/JarScannerCallback.java
index 689243e..991e123 100644
--- a/java/org/apache/tomcat/JarScannerCallback.java
+++ b/java/org/apache/tomcat/JarScannerCallback.java
@@ -18,7 +18,6 @@ package org.apache.tomcat;
import java.io.File;
import java.io.IOException;
-import java.net.JarURLConnection;
/**
* This interface is implemented by clients of the {@link JarScanner} to enable
@@ -30,27 +29,6 @@ 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.
*
- * @param urlConn The connection to the identified JAR
- * @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 <code>false</code> the JAR should
- * be treated as being provided by the container
- *
- * @deprecated Use {@link #scan(Jar, String, boolean)} instead. Because the
- * urlConn parameter could refer directly to a JAR or to a JAR
- * as an entry in a WAR, it required further processing that
- * included obtaining the original URL. It is simpler to provide
- * the URL to start with.
- * This method will be removed in Tomcat 8.5.x onwards.
- */
- @Deprecated
- public void scan(JarURLConnection urlConn, String webappPath, boolean isWebapp)
- throws IOException;
-
- /**
- * A JAR was found and may be accessed for further processing via the
- * provided URL connection.
- *
* @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
@@ -72,6 +50,8 @@ public interface JarScannerCallback {
* @param isWebapp Indicates if the JAR was found within a web
* application. If <code>false</code> the JAR should
* be treated as being provided by the container
+ *
+ * @throws IOException if an I/O error occurs while scanning the JAR
*/
public void scan(File file, String webappPath, boolean isWebapp) throws IOException;
@@ -80,6 +60,8 @@ public interface JarScannerCallback {
* /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
*/
public void scanWebInfClasses() throws IOException;
}
diff --git a/java/org/apache/tomcat/buildutil/SignCode.java b/java/org/apache/tomcat/buildutil/SignCode.java
index 4b0b1e4..eb8474d 100644
--- a/java/org/apache/tomcat/buildutil/SignCode.java
+++ b/java/org/apache/tomcat/buildutil/SignCode.java
@@ -349,6 +349,9 @@ public class SignCode extends Task {
private static String getApplicationString(List<String> fileNames, List<File> files)
throws IOException {
// 16 MB should be more than enough for Tomcat
+ // TODO: Refactoring this entire class so it uses streaming rather than
+ // buffering the entire set of files in memory would make it more
+ // widely useful.
ByteArrayOutputStream baos = new ByteArrayOutputStream(16 * 1024 * 1024);
try (ZipOutputStream zos = new ZipOutputStream(baos)) {
byte[] buf = new byte[32 * 1024];
diff --git a/java/org/apache/tomcat/dbcp/dbcp2/BasicDataSource.java b/java/org/apache/tomcat/dbcp/dbcp2/BasicDataSource.java
index 3ee6f2c..b061f33 100644
--- a/java/org/apache/tomcat/dbcp/dbcp2/BasicDataSource.java
+++ b/java/org/apache/tomcat/dbcp/dbcp2/BasicDataSource.java
@@ -204,6 +204,7 @@ public class BasicDataSource implements DataSource, BasicDataSourceMXBean, MBean
* Obtain the default query timeout that will be used for {@link java.sql.Statement Statement}s
* created from this connection. <code>null</code> means that the driver
* default will be used.
+ * @return the timoeut
*/
public Integer getDefaultQueryTimeout() {
return defaultQueryTimeout;
@@ -214,6 +215,7 @@ public class BasicDataSource implements DataSource, BasicDataSourceMXBean, MBean
* Set the default query timeout that will be used for {@link java.sql.Statement Statement}s
* created from this connection. <code>null</code> means that the driver
* default will be used.
+ * @param defaultQueryTimeout The new default timeout
*/
public void setDefaultQueryTimeout(final Integer defaultQueryTimeout) {
this.defaultQueryTimeout = defaultQueryTimeout;
@@ -306,7 +308,7 @@ public class BasicDataSource implements DataSource, BasicDataSourceMXBean, MBean
* following methods is invoked: <code>getConnection, setLogwriter,
* setLoginTimeout, getLoginTimeout, getLogWriter.</code></p>
*
- * @param driver
+ * @param driver The driver to use
*/
public synchronized void setDriver(final Driver driver) {
this.driver = driver;
@@ -366,6 +368,7 @@ public class BasicDataSource implements DataSource, BasicDataSourceMXBean, MBean
* {@link #setDriverClassLoader(ClassLoader)}. It does not return the class
* loader of any driver that may have been set via
* {@link #setDriver(Driver)}.
+ * @return the classloader
*/
public synchronized ClassLoader getDriverClassLoader() {
return this.driverClassLoader;
@@ -398,7 +401,7 @@ public class BasicDataSource implements DataSource, BasicDataSourceMXBean, MBean
/**
* Returns the LIFO property.
*
- * @return true if connection pool behaves as a LIFO queue.
+ * @return <code>true</code> if connection pool behaves as a LIFO queue.
*/
@Override
public synchronized boolean getLifo() {
@@ -410,7 +413,6 @@ public class BasicDataSource implements DataSource, BasicDataSourceMXBean, MBean
* false means FIFO.
*
* @param lifo the new value for the LIFO property
- *
*/
public synchronized void setLifo(final boolean lifo) {
this.lifo = lifo;
@@ -604,7 +606,7 @@ public class BasicDataSource implements DataSource, BasicDataSourceMXBean, MBean
/**
* Returns true if we are pooling statements.
*
- * @return true if prepared and callable statements are pooled
+ * @return <code>true</code> if prepared and callable statements are pooled
*/
@Override
public synchronized boolean isPoolPreparedStatements() {
@@ -674,8 +676,8 @@ public class BasicDataSource implements DataSource, BasicDataSourceMXBean, MBean
/**
* Returns the {@link #testOnCreate} property.
*
- * @return true if objects are validated immediately after they are created
- * by the pool
+ * @return <code>true</code> if objects are validated immediately
+ * after they are created by the pool
*
* @see #testOnCreate
*/
@@ -708,8 +710,8 @@ public class BasicDataSource implements DataSource, BasicDataSourceMXBean, MBean
/**
* Returns the {@link #testOnBorrow} property.
*
- * @return true if objects are validated before being borrowed from the
- * pool
+ * @return <code>true</code> if objects are validated before being
+ * borrowed from the pool
*
* @see #testOnBorrow
*/
@@ -741,8 +743,8 @@ public class BasicDataSource implements DataSource, BasicDataSourceMXBean, MBean
/**
* Returns the value of the {@link #testOnReturn} property.
*
- * @return true if objects are validated before being returned to the
- * pool
+ * @return <code>true</code> if objects are validated before being
+ * returned to the pool
* @see #testOnReturn
*/
public synchronized boolean getTestOnReturn() {
@@ -917,6 +919,7 @@ public class BasicDataSource implements DataSource, BasicDataSourceMXBean, MBean
/**
* Gets the EvictionPolicy implementation in use with this connection pool.
+ * @return the eviction policy
*/
public synchronized String getEvictionPolicyClassName() {
return evictionPolicyClassName;
@@ -946,8 +949,8 @@ public class BasicDataSource implements DataSource, BasicDataSourceMXBean, MBean
/**
* Returns the value of the {@link #testWhileIdle} property.
*
- * @return true if objects examined by the idle object evictor are
- * validated
+ * @return <code>true</code> if objects examined by
+ * the idle object evictor are validated
* @see #testWhileIdle
*/
@Override
@@ -1295,6 +1298,7 @@ public class BasicDataSource implements DataSource, BasicDataSourceMXBean, MBean
* initialized. The pool is initialized the first time one of the
* following methods is invoked: <code>getConnection, setLogwriter,
* setLoginTimeout, getLoginTimeout, getLogWriter.</code></p>
+ * @param maxConnLifetimeMillis The maximum connection lifetime
*/
public void setMaxConnLifetimeMillis(final long maxConnLifetimeMillis) {
this.maxConnLifetimeMillis = maxConnLifetimeMillis;
@@ -1305,6 +1309,7 @@ public class BasicDataSource implements DataSource, BasicDataSourceMXBean, MBean
* this property determines whether or not log messages are generated when the
* pool closes connections due to maximum lifetime exceeded. Set this property
* to false to suppress log messages when connections expire.
+ * @param logExpiredConnections <code>true</code> to log expired connections
*/
public void setLogExpiredConnections(final boolean logExpiredConnections) {
this.logExpiredConnections = logExpiredConnections;
@@ -1313,7 +1318,7 @@ public class BasicDataSource implements DataSource, BasicDataSourceMXBean, MBean
private String jmxName = null;
/**
- * Returns the JMX name that has been requested for this DataSource. If the
+ * @return the JMX name that has been requested for this DataSource. If the
* requested name is not valid, an alternative may be chosen.
*/
public String getJmxName() {
@@ -1326,6 +1331,7 @@ public class BasicDataSource implements DataSource, BasicDataSourceMXBean, MBean
* DataSource will attempt to register itself using this name. If another
* component registers this DataSource with JMX and this name is valid this
* name will be used in preference to any specified by the other component.
+ * @param jmxName The JMX name
*/
public void setJmxName(final String jmxName) {
this.jmxName = jmxName;
@@ -1340,6 +1346,7 @@ public class BasicDataSource implements DataSource, BasicDataSourceMXBean, MBean
* {@link Connection#setAutoCommit(boolean) Connection.setAutoCommit(true)}
* if the auto commit setting is {@code false} when the connection
* is returned. It is <code>true</code> by default.
+ * @return <code>true</code> to commit automatically
*/
public boolean getEnableAutoCommitOnReturn() {
return enableAutoCommitOnReturn;
@@ -1351,6 +1358,7 @@ public class BasicDataSource implements DataSource, BasicDataSourceMXBean, MBean
* {@link Connection#setAutoCommit(boolean) Connection.setAutoCommit(true)}
* if the auto commit setting is {@code false} when the connection
* is returned. It is <code>true</code> by default.
+ * @param enableAutoCommitOnReturn The new value
*/
public void setEnableAutoCommitOnReturn(final boolean enableAutoCommitOnReturn) {
this.enableAutoCommitOnReturn = enableAutoCommitOnReturn;
@@ -1362,6 +1370,7 @@ public class BasicDataSource implements DataSource, BasicDataSourceMXBean, MBean
* Gets the current value of the flag that controls if a connection will be
* rolled back when it is returned to the pool if auto commit is not enabled
* and the connection is not read only.
+ * @return <code>true</code> to rollback non committed connections
*/
public boolean getRollbackOnReturn() {
return rollbackOnReturn;
@@ -1371,6 +1380,7 @@ public class BasicDataSource implements DataSource, BasicDataSourceMXBean, MBean
* Sets the flag that controls if a connection will be rolled back when it
* is returned to the pool if auto commit is not enabled and the connection
* is not read only.
+ * @param rollbackOnReturn The new value
*/
public void setRollbackOnReturn(final boolean rollbackOnReturn) {
this.rollbackOnReturn = rollbackOnReturn;
@@ -1395,6 +1405,7 @@ public class BasicDataSource implements DataSource, BasicDataSourceMXBean, MBean
/**
* Provides the same data as {@link #getDisconnectionSqlCodes} but in an
* array so it is accessible via JMX.
+ * @return fatal disconnection state codes
* @since 2.1
*/
@Override
@@ -1540,7 +1551,7 @@ public class BasicDataSource implements DataSource, BasicDataSourceMXBean, MBean
* is being made
* @param pass The database user's password
*
- * @throws UnsupportedOperationException
+ * @throws UnsupportedOperationException This is not supported
* @throws SQLException if a database access error occurs
* @return nothing - always throws UnsupportedOperationException
*/
@@ -1776,6 +1787,7 @@ public class BasicDataSource implements DataSource, BasicDataSourceMXBean, MBean
/**
* Gets the log writer to be used by this configuration to log
* information on abandoned objects.
+ * @return the log writer
*/
public PrintWriter getAbandonedLogWriter() {
if (abandonedConfig != null) {
@@ -1962,7 +1974,7 @@ public class BasicDataSource implements DataSource, BasicDataSourceMXBean, MBean
/**
* Manually invalidates a connection, effectively requesting the pool to try
* to close it, remove it from the pool and reclaim pool capacity.
- *
+ * @param connection The connection to close
* @throws IllegalStateException
* if invalidating the connection failed.
* @since 2.1
@@ -1986,13 +1998,6 @@ public class BasicDataSource implements DataSource, BasicDataSourceMXBean, MBean
throw new IllegalStateException("Cannot invalidate connection: Unwrapping poolable connection failed.", e);
}
- // attempt to close the connection for good measure
- try {
- connection.close();
- } catch (Exception e) {
- // ignore any exceptions here
- }
-
try {
connectionPool.invalidateObject(poolableConnection);
} catch (final Exception e) {
@@ -2006,7 +2011,7 @@ public class BasicDataSource implements DataSource, BasicDataSourceMXBean, MBean
/**
* <p>Create (if necessary) and return the internal data source we are
* using to manage our connections.</p>
- *
+ * @return the data source
* @throws SQLException if the object pool cannot be created.
*/
protected DataSource createDataSource()
@@ -2109,6 +2114,8 @@ public class BasicDataSource implements DataSource, BasicDataSourceMXBean, MBean
* {@link DriverManager} using the specified {@link #url}.
* </ol>
* This method exists so subclasses can replace the implementation class.
+ * @return the connection factory
+ * @throws SQLException Error creating connection factory
*/
protected ConnectionFactory createConnectionFactory() throws SQLException {
// Load the JDBC driver class
@@ -2190,6 +2197,7 @@ public class BasicDataSource implements DataSource, BasicDataSourceMXBean, MBean
* {@link #startPoolMaintenance()}, since setting timeBetweenEvictionRunsMillis
* to a positive value causes {@link GenericObjectPool}'s eviction timer
* to be started.
+ * @param factory The connection factory
*/
protected void createConnectionPool(final PoolableConnectionFactory factory) {
// Create an object pool to contain our active connections
@@ -2250,7 +2258,7 @@ public class BasicDataSource implements DataSource, BasicDataSourceMXBean, MBean
/**
* Creates the actual data source instance. This method only exists so
* that subclasses can replace the implementation class.
- *
+ * @return the data source
* @throws SQLException if unable to create a datasource instance
*/
protected DataSource createDataSourceInstance() throws SQLException {
@@ -2264,6 +2272,7 @@ public class BasicDataSource implements DataSource, BasicDataSourceMXBean, MBean
* so subclasses can replace the default implementation.
*
* @param driverConnectionFactory JDBC connection factory
+ * @return the connection factory
* @throws SQLException if an error occurs creating the PoolableConnectionFactory
*/
protected PoolableConnectionFactory createPoolableConnectionFactory(
diff --git a/java/org/apache/tomcat/dbcp/dbcp2/BasicDataSourceFactory.java b/java/org/apache/tomcat/dbcp/dbcp2/BasicDataSourceFactory.java
index fc9f915..fb07e2e 100644
--- a/java/org/apache/tomcat/dbcp/dbcp2/BasicDataSourceFactory.java
+++ b/java/org/apache/tomcat/dbcp/dbcp2/BasicDataSourceFactory.java
@@ -177,7 +177,8 @@ public class BasicDataSourceFactory implements ObjectFactory {
PROP_ENABLE_AUTOCOMMIT_ON_RETURN,
PROP_DEFAULT_QUERYTIMEOUT,
PROP_FASTFAIL_VALIDATION,
- PROP_DISCONNECTION_SQL_CODES
+ PROP_DISCONNECTION_SQL_CODES,
+ PROP_JMX_NAME
};
/**
@@ -331,6 +332,7 @@ public class BasicDataSourceFactory implements ObjectFactory {
* given properties.
*
* @param properties the datasource configuration properties
+ * @return the data source instance
* @throws Exception if an error occurs creating the data source
*/
public static BasicDataSource createDataSource(final Properties properties) throws Exception {
diff --git a/java/org/apache/tomcat/dbcp/dbcp2/DelegatingCallableStatement.java b/java/org/apache/tomcat/dbcp/dbcp2/DelegatingCallableStatement.java
index a0b166a..b9797e0 100644
--- a/java/org/apache/tomcat/dbcp/dbcp2/DelegatingCallableStatement.java
+++ b/java/org/apache/tomcat/dbcp/dbcp2/DelegatingCallableStatement.java
@@ -113,7 +113,7 @@ public class DelegatingCallableStatement extends DelegatingPreparedStatement
public double getDouble(final int parameterIndex) throws SQLException
{ checkOpen(); try { return ((CallableStatement)getDelegate()).getDouble( parameterIndex); } catch (final SQLException e) { handleException(e); return 0; } }
- /** @deprecated */
+ /** @deprecated Use {@link #getBigDecimal(int)} or {@link #getBigDecimal(String)} */
@Override
@Deprecated
public BigDecimal getBigDecimal(final int parameterIndex, final int scale) throws SQLException
diff --git a/java/org/apache/tomcat/dbcp/dbcp2/DelegatingConnection.java b/java/org/apache/tomcat/dbcp/dbcp2/DelegatingConnection.java
index 4ddb10f..3a19a7d 100644
--- a/java/org/apache/tomcat/dbcp/dbcp2/DelegatingConnection.java
+++ b/java/org/apache/tomcat/dbcp/dbcp2/DelegatingConnection.java
@@ -174,6 +174,7 @@ public class DelegatingConnection<C extends Connection> extends AbandonedTrace
* This method is useful when you may have nested
* {@code DelegatingConnection}s, and you want to make
* sure to obtain a "genuine" {@link Connection}.
+ * @return the connection
*/
public Connection getInnermostDelegate() {
return getInnermostDelegateInternal();
@@ -184,6 +185,7 @@ public class DelegatingConnection<C extends Connection> extends AbandonedTrace
* Although this method is public, it is part of the internal API and should
* not be used by clients. The signature of this method may change at any
* time including in ways that break backwards compatibility.
+ * @return the connection
*/
public final Connection getInnermostDelegateInternal() {
Connection c = _conn;
@@ -196,7 +198,10 @@ public class DelegatingConnection<C extends Connection> extends AbandonedTrace
return c;
}
- /** Sets my delegate. */
+ /**
+ * Sets my delegate.
+ * @param c The connection
+ */
public void setDelegate(final C c) {
_conn = c;
}
@@ -210,6 +215,7 @@ public class DelegatingConnection<C extends Connection> extends AbandonedTrace
* connection</li>
* <li>Set _closed to <code>false</code></li>
* </ol>
+ * @throws SQLException Error closing connection
*/
@Override
public void close() throws SQLException {
@@ -499,6 +505,7 @@ public class DelegatingConnection<C extends Connection> extends AbandonedTrace
* Obtain the default query timeout that will be used for {@link Statement}s
* created from this connection. <code>null</code> means that the driver
* default will be used.
+ * @return the timeout
*/
public Integer getDefaultQueryTimeout() {
return defaultQueryTimeout;
@@ -509,6 +516,7 @@ public class DelegatingConnection<C extends Connection> extends AbandonedTrace
* Set the default query timeout that will be used for {@link Statement}s
* created from this connection. <code>null</code> means that the driver
* default will be used.
+ * @param defaultQueryTimeout The timeout value
*/
public void setDefaultQueryTimeout(final Integer defaultQueryTimeout) {
this.defaultQueryTimeout = defaultQueryTimeout;
diff --git a/java/org/apache/tomcat/dbcp/dbcp2/DelegatingDatabaseMetaData.java b/java/org/apache/tomcat/dbcp/dbcp2/DelegatingDatabaseMetaData.java
index b7c2017..7eff210 100644
--- a/java/org/apache/tomcat/dbcp/dbcp2/DelegatingDatabaseMetaData.java
+++ b/java/org/apache/tomcat/dbcp/dbcp2/DelegatingDatabaseMetaData.java
@@ -64,6 +64,7 @@ public class DelegatingDatabaseMetaData implements DatabaseMetaData {
* This method is useful when you may have nested
* {@code DelegatingResultSet}s, and you want to make
* sure to obtain a "genuine" {@link ResultSet}.
+ * @return the database meta data
*/
public DatabaseMetaData getInnermostDelegate() {
DatabaseMetaData m = _meta;
diff --git a/java/org/apache/tomcat/dbcp/dbcp2/DelegatingPreparedStatement.java b/java/org/apache/tomcat/dbcp/dbcp2/DelegatingPreparedStatement.java
index ec3ad6a..ef6a806 100644
--- a/java/org/apache/tomcat/dbcp/dbcp2/DelegatingPreparedStatement.java
+++ b/java/org/apache/tomcat/dbcp/dbcp2/DelegatingPreparedStatement.java
@@ -161,7 +161,7 @@ public class DelegatingPreparedStatement extends DelegatingStatement
public void setAsciiStream(final int parameterIndex, final InputStream x, final int length) throws SQLException
{ checkOpen(); try { ((PreparedStatement)getDelegate()).setAsciiStream(parameterIndex,x,length); } catch (final SQLException e) { handleException(e); } }
- /** @deprecated */
+ /** @deprecated Use setAsciiStream(), setCharacterStream() or setNCharacterStream() */
@Deprecated
@Override
public void setUnicodeStream(final int parameterIndex, final InputStream x, final int length) throws SQLException
diff --git a/java/org/apache/tomcat/dbcp/dbcp2/DelegatingResultSet.java b/java/org/apache/tomcat/dbcp/dbcp2/DelegatingResultSet.java
index d575210..5b65d71 100644
--- a/java/org/apache/tomcat/dbcp/dbcp2/DelegatingResultSet.java
+++ b/java/org/apache/tomcat/dbcp/dbcp2/DelegatingResultSet.java
@@ -133,6 +133,7 @@ public final class DelegatingResultSet extends AbandonedTrace implements ResultS
* This method is useful when you may have nested
* {@code DelegatingResultSet}s, and you want to make
* sure to obtain a "genuine" {@link ResultSet}.
+ * @return the result set
*/
public ResultSet getInnermostDelegate() {
ResultSet r = _res;
@@ -225,7 +226,7 @@ public final class DelegatingResultSet extends AbandonedTrace implements ResultS
public double getDouble(final int columnIndex) throws SQLException
{ try { return _res.getDouble(columnIndex); } catch (final SQLException e) { handleException(e); return 0; } }
- /** @deprecated */
+ /** @deprecated Use {@link #getBigDecimal(int)} */
@Deprecated
@Override
public BigDecimal getBigDecimal(final int columnIndex, final int scale) throws SQLException
@@ -251,7 +252,7 @@ public final class DelegatingResultSet extends AbandonedTrace implements ResultS
public InputStream getAsciiStream(final int columnIndex) throws SQLException
{ try { return _res.getAsciiStream(columnIndex); } catch (final SQLException e) { handleException(e); return null; } }
- /** @deprecated */
+ /** @deprecated Use {@link #getCharacterStream(int)} */
@Deprecated
@Override
public InputStream getUnicodeStream(final int columnIndex) throws SQLException
@@ -293,7 +294,7 @@ public final class DelegatingResultSet extends AbandonedTrace implements ResultS
public double getDouble(final String columnName) throws SQLException
{ try { return _res.getDouble(columnName); } catch (final SQLException e) { handleException(e); return 0; } }
- /** @deprecated */
+ /** @deprecated Use {@link #getBigDecimal(String)} */
@Deprecated
@Override
public BigDecimal getBigDecimal(final String columnName, final int scale) throws SQLException
@@ -319,7 +320,7 @@ public final class DelegatingResultSet extends AbandonedTrace implements ResultS
public InputStream getAsciiStream(final String columnName) throws SQLException
{ try { return _res.getAsciiStream(columnName); } catch (final SQLException e) { handleException(e); return null; } }
- /** @deprecated */
+ /** @deprecated Use {@link #getCharacterStream(String)} */
@Deprecated
@Override
public InputStream getUnicodeStream(final String columnName) throws SQLException
diff --git a/java/org/apache/tomcat/dbcp/dbcp2/DelegatingStatement.java b/java/org/apache/tomcat/dbcp/dbcp2/DelegatingStatement.java
index 06a6285..e470f39 100644
--- a/java/org/apache/tomcat/dbcp/dbcp2/DelegatingStatement.java
+++ b/java/org/apache/tomcat/dbcp/dbcp2/DelegatingStatement.java
@@ -87,6 +87,7 @@ public class DelegatingStatement extends AbandonedTrace implements Statement {
* {@code DelegatingStatement}s, and you want to make
* sure to obtain a "genuine" {@link Statement}.
* @see #getDelegate
+ * @return the statement
*/
public Statement getInnermostDelegate() {
Statement s = _stmt;
@@ -99,7 +100,10 @@ public class DelegatingStatement extends AbandonedTrace implements Statement {
return s;
}
- /** Sets my delegate. */
+ /**
+ * Sets my delegate.
+ * @param s The statement
+ */
public void setDelegate(final Statement s) {
_stmt = s;
}
diff --git a/java/org/apache/tomcat/dbcp/dbcp2/LifetimeExceededException.java b/java/org/apache/tomcat/dbcp/dbcp2/LifetimeExceededException.java
index 0fb8fd7..53fd322 100644
--- a/java/org/apache/tomcat/dbcp/dbcp2/LifetimeExceededException.java
+++ b/java/org/apache/tomcat/dbcp/dbcp2/LifetimeExceededException.java
@@ -34,9 +34,10 @@ package org.apache.tomcat.dbcp.dbcp2;
/**
* Create a LifetimeExceededException with the given message.
+ *
+ * @param message The message with which to create the exception
*/
public LifetimeExceededException(final String message) {
super(message);
}
-
}
diff --git a/java/org/apache/tomcat/dbcp/dbcp2/PoolableConnection.java b/java/org/apache/tomcat/dbcp/dbcp2/PoolableConnection.java
index 47ed051..f005d73 100644
--- a/java/org/apache/tomcat/dbcp/dbcp2/PoolableConnection.java
+++ b/java/org/apache/tomcat/dbcp/dbcp2/PoolableConnection.java
@@ -50,7 +50,7 @@ public class PoolableConnection extends DelegatingConnection<Connection>
static {
try {
MBEAN_SERVER = ManagementFactory.getPlatformMBeanServer();
- } catch (Exception ex) {
+ } catch (NoClassDefFoundError | Exception ex) {
// ignore - JMX not available
}
}
diff --git a/java/org/apache/tomcat/dbcp/dbcp2/PoolableConnectionFactory.java b/java/org/apache/tomcat/dbcp/dbcp2/PoolableConnectionFactory.java
index 7c4229e..4bc0690 100644
--- a/java/org/apache/tomcat/dbcp/dbcp2/PoolableConnectionFactory.java
+++ b/java/org/apache/tomcat/dbcp/dbcp2/PoolableConnectionFactory.java
@@ -54,6 +54,7 @@ public class PoolableConnectionFactory
/**
* Create a new {@code PoolableConnectionFactory}.
* @param connFactory the {@link ConnectionFactory} from which to obtain
+ * @param dataSourceJmxName The JMX name
* base {@link Connection}s
*/
public PoolableConnectionFactory(final ConnectionFactory connFactory,
@@ -166,6 +167,7 @@ public class PoolableConnectionFactory
* connection will always fail activation, passivation and validation. A
* value of zero or less indicates an infinite lifetime. The default value
* is -1.
+ * @param maxConnLifetimeMillis The maximum connection lifetime
*/
public void setMaxConnLifetimeMillis(final long maxConnLifetimeMillis) {
this.maxConnLifetimeMillis = maxConnLifetimeMillis;
@@ -220,7 +222,7 @@ public class PoolableConnectionFactory
/**
* @see #getDisconnectionSqlCodes()
- * @param disconnectionSqlCodes
+ * @param disconnectionSqlCodes The disconnection codes
* @since 2.1
*/
public void setDisconnectionSqlCodes(final Collection<String> disconnectionSqlCodes) {
diff --git a/java/org/apache/tomcat/dbcp/dbcp2/PoolingConnection.java b/java/org/apache/tomcat/dbcp/dbcp2/PoolingConnection.java
index d34b55a..a8b9838 100644
--- a/java/org/apache/tomcat/dbcp/dbcp2/PoolingConnection.java
+++ b/java/org/apache/tomcat/dbcp/dbcp2/PoolingConnection.java
@@ -47,7 +47,9 @@ import org.apache.tomcat.dbcp.pool2.impl.DefaultPooledObject;
public class PoolingConnection extends DelegatingConnection<Connection>
implements KeyedPooledObjectFactory<PStmtKey,DelegatingPreparedStatement> {
- /** Pool of {@link PreparedStatement}s. and {@link CallableStatement}s */
+ /**
+ * Pool of {@link PreparedStatement}s. and {@link CallableStatement}s
+ */
private KeyedObjectPool<PStmtKey,DelegatingPreparedStatement> _pstmtPool = null;
/**
@@ -141,6 +143,7 @@ public class PoolingConnection extends DelegatingConnection<Connection>
* @param resultSetType result set type
* @param resultSetConcurrency result set concurrency
* @return a {@link PoolablePreparedStatement}
+ * @throws SQLException An error occurred
*/
@Override
public PreparedStatement prepareStatement(final String sql, final int resultSetType, final int resultSetConcurrency) throws SQLException {
@@ -163,7 +166,7 @@ public class PoolingConnection extends DelegatingConnection<Connection>
* Create or obtain a {@link CallableStatement} from the pool.
* @param sql the sql string used to define the CallableStatement
* @return a {@link PoolableCallableStatement}
- * @throws SQLException
+ * @throws SQLException An error occurred
*/
@Override
public CallableStatement prepareCall(final String sql) throws SQLException {
@@ -184,7 +187,7 @@ public class PoolingConnection extends DelegatingConnection<Connection>
* @param resultSetType result set type
* @param resultSetConcurrency result set concurrency
* @return a {@link PoolableCallableStatement}
- * @throws SQLException
+ * @throws SQLException An error occurred
*/
@Override
public CallableStatement prepareCall(final String sql, final int resultSetType, final int resultSetConcurrency) throws SQLException {
@@ -236,6 +239,7 @@ public class PoolingConnection extends DelegatingConnection<Connection>
* @param sql the sql string used to define the statement
* @param resultSetType result set type
* @param resultSetConcurrency result set concurrency
+ * @return the key
*/
protected PStmtKey createKey(final String sql, final int resultSetType, final int resultSetConcurrency) {
String catalog = null;
@@ -253,6 +257,7 @@ public class PoolingConnection extends DelegatingConnection<Connection>
* @param resultSetType result set type
* @param resultSetConcurrency result set concurrency
* @param stmtType statement type
+ * @return the key
*/
protected PStmtKey createKey(final String sql, final int resultSetType, final int resultSetConcurrency, final StatementType stmtType) {
String catalog = null;
@@ -267,6 +272,7 @@ public class PoolingConnection extends DelegatingConnection<Connection>
/**
* Create a PStmtKey for the given arguments.
* @param sql the sql string used to define the statement
+ * @return the key
*/
protected PStmtKey createKey(final String sql) {
String catalog = null;
@@ -282,6 +288,7 @@ public class PoolingConnection extends DelegatingConnection<Connection>
* Create a PStmtKey for the given arguments.
* @param sql the SQL string used to define the statement
* @param stmtType statement type
+ * @return the key
*/
protected PStmtKey createKey(final String sql, final StatementType stmtType) {
String catalog = null;
@@ -296,6 +303,8 @@ public class PoolingConnection extends DelegatingConnection<Connection>
/**
* Normalize the given SQL statement, producing a
* canonical form that is semantically equivalent to the original.
+ * @param sql The SQL statement
+ * @return the trimmed SQL statement
*/
protected String normalizeSQL(final String sql) {
return sql.trim();
@@ -308,6 +317,8 @@ public class PoolingConnection extends DelegatingConnection<Connection>
* a PoolablePreparedStatement or PoolableCallableStatement is created.
*
* @param key the key for the {@link PreparedStatement} to be created
+ * @return the object
+ * @throws Exception An error occurred
* @see #createKey(String, int, int, StatementType)
*/
@Override
@@ -353,6 +364,7 @@ public class PoolingConnection extends DelegatingConnection<Connection>
*
* @param key ignored
* @param p the wrapped pooled statement to be destroyed.
+ * @throws Exception An error occurred
*/
@Override
public void destroyObject(final PStmtKey key,
@@ -381,6 +393,7 @@ public class PoolingConnection extends DelegatingConnection<Connection>
*
* @param key ignored
* @param p wrapped pooled statement to be activated
+ * @throws Exception An error occurred
*/
@Override
public void activateObject(final PStmtKey key,
@@ -395,6 +408,7 @@ public class PoolingConnection extends DelegatingConnection<Connection>
*
* @param key ignored
* @param p a wrapped {@link PreparedStatement}
+ * @throws Exception An error occurred
*/
@Override
public void passivateObject(final PStmtKey key,
diff --git a/java/org/apache/tomcat/dbcp/dbcp2/PoolingDataSource.java b/java/org/apache/tomcat/dbcp/dbcp2/PoolingDataSource.java
index 23e97d9..32ddccd 100644
--- a/java/org/apache/tomcat/dbcp/dbcp2/PoolingDataSource.java
+++ b/java/org/apache/tomcat/dbcp/dbcp2/PoolingDataSource.java
@@ -149,7 +149,7 @@ public class PoolingDataSource<C extends Connection> implements DataSource, Auto
/**
* Throws {@link UnsupportedOperationException}
- * @throws UnsupportedOperationException
+ * @throws UnsupportedOperationException This is unsupported
*/
@Override
public Connection getConnection(final String uname, final String passwd) throws SQLException {
diff --git a/java/org/apache/tomcat/dbcp/dbcp2/PoolingDriver.java b/java/org/apache/tomcat/dbcp/dbcp2/PoolingDriver.java
index 25889a2..5352ae1 100644
--- a/java/org/apache/tomcat/dbcp/dbcp2/PoolingDriver.java
+++ b/java/org/apache/tomcat/dbcp/dbcp2/PoolingDriver.java
@@ -41,7 +41,9 @@ import org.apache.tomcat.dbcp.pool2.ObjectPool;
* @since 2.0
*/
public class PoolingDriver implements Driver {
- /** Register myself with the {@link DriverManager}. */
+ /**
+ * Register myself with the {@link DriverManager}.
+ */
static {
try {
DriverManager.registerDriver(new PoolingDriver());
@@ -49,11 +51,15 @@ public class PoolingDriver implements Driver {
}
}
- /** The map of registered pools. */
+ /**
+ * The map of registered pools.
+ */
protected static final HashMap<String,ObjectPool<? extends Connection>> pools =
new HashMap<>();
- /** Controls access to the underlying connection */
+ /**
+ * Controls access to the underlying connection
+ */
private final boolean accessToUnderlyingConnectionAllowed;
public PoolingDriver() {
@@ -62,6 +68,7 @@ public class PoolingDriver implements Driver {
/**
* For unit testing purposes.
+ * @param accessToUnderlyingConnectionAllowed The new flag
*/
protected PoolingDriver(final boolean accessToUnderlyingConnectionAllowed) {
this.accessToUnderlyingConnectionAllowed = accessToUnderlyingConnectionAllowed;
@@ -71,7 +78,8 @@ public class PoolingDriver implements Driver {
/**
* Returns the value of the accessToUnderlyingConnectionAllowed property.
*
- * @return true if access to the underlying is allowed, false otherwise.
+ * @return <code>true</code> if access to the underlying is allowed,
+ * <code>false</code> otherwise.
*/
protected boolean isAccessToUnderlyingConnectionAllowed() {
return accessToUnderlyingConnectionAllowed;
diff --git a/java/org/apache/tomcat/dbcp/dbcp2/Utils.java b/java/org/apache/tomcat/dbcp/dbcp2/Utils.java
index f9a87a2..90d7a0a 100644
--- a/java/org/apache/tomcat/dbcp/dbcp2/Utils.java
+++ b/java/org/apache/tomcat/dbcp/dbcp2/Utils.java
@@ -116,6 +116,8 @@ public final class Utils {
/**
* Obtain the correct i18n message for the given key.
+ * @param key The message key
+ * @return the message
*/
public static String getMessage(final String key) {
return getMessage(key, (Object[]) null);
@@ -125,6 +127,9 @@ public final class Utils {
/**
* Obtain the correct i18n message for the given key with placeholders
* replaced by the supplied arguments.
+ * @param key The message key
+ * @param args The arguments
+ * @return the message
*/
public static String getMessage(final String key, final Object... args) {
final String msg = messages.getString(key);
diff --git a/java/org/apache/tomcat/dbcp/dbcp2/cpdsadapter/DriverAdapterCPDS.java b/java/org/apache/tomcat/dbcp/dbcp2/cpdsadapter/DriverAdapterCPDS.java
index a8c6d71..4213010 100644
--- a/java/org/apache/tomcat/dbcp/dbcp2/cpdsadapter/DriverAdapterCPDS.java
+++ b/java/org/apache/tomcat/dbcp/dbcp2/cpdsadapter/DriverAdapterCPDS.java
@@ -492,6 +492,7 @@ public class DriverAdapterCPDS
* Sets the driver classname. Setting the driver classname cause the
* driver to be registered with the DriverManager.
* @param v Value to assign to driver.
+ * @throws ClassNotFoundException Driver class was not found
* @throws IllegalStateException if {@link #getPooledConnection()} has been called
*/
public void setDriver(final String v) throws ClassNotFoundException {
@@ -596,7 +597,7 @@ public class DriverAdapterCPDS
* idle object evictor thread.
* When non-positive, no idle object evictor thread will be
* run.
- * @param timeBetweenEvictionRunsMillis
+ * @param timeBetweenEvictionRunsMillis The time between runs
* @see #getTimeBetweenEvictionRunsMillis()
* @throws IllegalStateException if {@link #getPooledConnection()} has been called
*/
@@ -610,8 +611,9 @@ public class DriverAdapterCPDS
* Gets the number of statements to examine during each run of the
* idle object evictor thread (if any).
*
- * *see #setNumTestsPerEvictionRun
- * *see #setTimeBetweenEvictionRunsMillis
+ * @see #setNumTestsPerEvictionRun(int)
+ * @see #setTimeBetweenEvictionRunsMillis(long)
+ * @return the number of statements
*/
public int getNumTestsPerEvictionRun() {
return _numTestsPerEvictionRun;
@@ -640,8 +642,9 @@ public class DriverAdapterCPDS
* before it is eligible for eviction by the idle object evictor
* (if any).
*
- * *see #setMinEvictableIdleTimeMillis
- * *see #setTimeBetweenEvictionRunsMillis
+ * @see #setMinEvictableIdleTimeMillis(int)
+ * @see #setTimeBetweenEvictionRunsMillis(long)
+ * @return the amount of time
*/
public int getMinEvictableIdleTimeMillis() {
return _minEvictableIdleTimeMillis;
@@ -666,7 +669,8 @@ public class DriverAdapterCPDS
/**
* Returns the value of the accessToUnderlyingConnectionAllowed property.
*
- * @return true if access to the underlying is allowed, false otherwise.
+ * @return <code>true</code> if access to the underlying is allowed,
+ * <code>false</code> otherwise.
*/
public synchronized boolean isAccessToUnderlyingConnectionAllowed() {
return this.accessToUnderlyingConnectionAllowed;
diff --git a/java/org/apache/tomcat/dbcp/dbcp2/datasources/CPDSConnectionFactory.java b/java/org/apache/tomcat/dbcp/dbcp2/datasources/CPDSConnectionFactory.java
index dec69b0..014db18 100644
--- a/java/org/apache/tomcat/dbcp/dbcp2/datasources/CPDSConnectionFactory.java
+++ b/java/org/apache/tomcat/dbcp/dbcp2/datasources/CPDSConnectionFactory.java
@@ -86,8 +86,8 @@ class CPDSConnectionFactory
* @param validationQueryTimeout Timeout in seconds before validation fails
* @param rollbackAfterValidation whether a rollback should be issued
* after {@link #validateObject validating} {@link Connection}s.
- * @param username
- * @param password
+ * @param username The user name to use to create connections
+ * @param password The password to use to create connections
*/
public CPDSConnectionFactory(final ConnectionPoolDataSource cpds,
final String validationQuery,
@@ -331,9 +331,10 @@ class CPDSConnectionFactory
/**
* Sets the maximum lifetime in milliseconds of a connection after which the
- * connection will always fail activation, passivation and validation. A
- * value of zero or less indicates an infinite lifetime. The default value
- * is -1.
+ * 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.
*/
public void setMaxConnLifetimeMillis(final long maxConnLifetimeMillis) {
this.maxConnLifetimeMillis = maxConnLifetimeMillis;
diff --git a/java/org/apache/tomcat/dbcp/dbcp2/datasources/InstanceKeyDataSource.java b/java/org/apache/tomcat/dbcp/dbcp2/datasources/InstanceKeyDataSource.java
index 9f114bc..da225b9 100644
--- a/java/org/apache/tomcat/dbcp/dbcp2/datasources/InstanceKeyDataSource.java
+++ b/java/org/apache/tomcat/dbcp/dbcp2/datasources/InstanceKeyDataSource.java
@@ -213,7 +213,7 @@ public abstract class InstanceKeyDataSource
// Properties
/**
- * Gets the default value for
+ * @return the default value for
* {@link GenericKeyedObjectPoolConfig#getBlockWhenExhausted()} for each per
* user pool.
*/
@@ -225,6 +225,7 @@ public abstract class InstanceKeyDataSource
* Sets the default value for
* {@link GenericKeyedObjectPoolConfig#getBlockWhenExhausted()} for each per
* user pool.
+ * @param blockWhenExhausted The new value
*/
public void setDefaultBlockWhenExhausted(final boolean blockWhenExhausted) {
assertInitializationAllowed();
@@ -232,7 +233,7 @@ public abstract class InstanceKeyDataSource
}
/**
- * Gets the default value for
+ * @return the default value for
* {@link GenericKeyedObjectPoolConfig#getEvictionPolicyClassName()} for
* each per user pool.
*/
@@ -244,6 +245,7 @@ public abstract class InstanceKeyDataSource
* Sets the default value for
* {@link GenericKeyedObjectPoolConfig#getEvictionPolicyClassName()} for
* each per user pool.
+ * @param evictionPolicyClassName The new value
*/
public void setDefaultEvictionPolicyClassName(
final String evictionPolicyClassName) {
@@ -252,7 +254,7 @@ public abstract class InstanceKeyDataSource
}
/**
- * Gets the default value for
+ * @return the default value for
* {@link GenericKeyedObjectPoolConfig#getLifo()} for each per user pool.
*/
public boolean getDefaultLifo() {
@@ -262,6 +264,7 @@ public abstract class InstanceKeyDataSource
/**
* Sets the default value for
* {@link GenericKeyedObjectPoolConfig#getLifo()} for each per user pool.
+ * @param lifo The new value
*/
public void setDefaultLifo(final boolean lifo) {
assertInitializationAllowed();
@@ -269,7 +272,7 @@ public abstract class InstanceKeyDataSource
}
/**
- * Gets the default value for
+ * @return the default value for
* {@link GenericKeyedObjectPoolConfig#getMaxIdlePerKey()} for each per user
* pool.
*/
@@ -281,6 +284,7 @@ public abstract class InstanceKeyDataSource
* Sets the default value for
* {@link GenericKeyedObjectPoolConfig#getMaxIdlePerKey()} for each per user
* pool.
+ * @param maxIdle The new value
*/
public void setDefaultMaxIdle(final int maxIdle) {
assertInitializationAllowed();
@@ -288,7 +292,7 @@ public abstract class InstanceKeyDataSource
}
/**
- * Gets the default value for
+ * @return the default value for
* {@link GenericKeyedObjectPoolConfig#getMaxTotalPerKey()} for each per
* user pool.
*/
@@ -300,6 +304,7 @@ public abstract class InstanceKeyDataSource
* Sets the default value for
* {@link GenericKeyedObjectPoolConfig#getMaxTotalPerKey()} for each per
* user pool.
+ * @param maxTotal The new value
*/
public void setDefaultMaxTotal(final int maxTotal) {
assertInitializationAllowed();
@@ -307,7 +312,7 @@ public abstract class InstanceKeyDataSource
}
/**
- * Gets the default value for
+ * @return the default value for
* {@link GenericKeyedObjectPoolConfig#getMaxWaitMillis()} for each per user
* pool.
*/
@@ -319,6 +324,7 @@ public abstract class InstanceKeyDataSource
* Sets the default value for
* {@link GenericKeyedObjectPoolConfig#getMaxWaitMillis()} for each per user
* pool.
+ * @param maxWaitMillis The new value
*/
public void setDefaultMaxWaitMillis(final long maxWaitMillis) {
assertInitializationAllowed();
@@ -326,7 +332,7 @@ public abstract class InstanceKeyDataSource
}
/**
- * Gets the default value for
+ * @return the default value for
* {@link GenericKeyedObjectPoolConfig#getMinEvictableIdleTimeMillis()} for
* each per user pool.
*/
@@ -338,6 +344,7 @@ public abstract class InstanceKeyDataSource
* Sets the default value for
* {@link GenericKeyedObjectPoolConfig#getMinEvictableIdleTimeMillis()} for
* each per user pool.
+ * @param minEvictableIdleTimeMillis The new value
*/
public void setDefaultMinEvictableIdleTimeMillis(
final long minEvictableIdleTimeMillis) {
@@ -346,7 +353,7 @@ public abstract class InstanceKeyDataSource
}
/**
- * Gets the default value for
+ * @return the default value for
* {@link GenericKeyedObjectPoolConfig#getMinIdlePerKey()} for each per user
* pool.
*/
@@ -358,6 +365,7 @@ public abstract class InstanceKeyDataSource
* Sets the default value for
* {@link GenericKeyedObjectPoolConfig#getMinIdlePerKey()} for each per user
* pool.
+ * @param minIdle The new value
*/
public void setDefaultMinIdle(final int minIdle) {
assertInitializationAllowed();
@@ -365,7 +373,7 @@ public abstract class InstanceKeyDataSource
}
/**
- * Gets the default value for
+ * @return the default value for
* {@link GenericKeyedObjectPoolConfig#getNumTestsPerEvictionRun()} for each
* per user pool.
*/
@@ -377,6 +385,7 @@ public abstract class InstanceKeyDataSource
* Sets the default value for
* {@link GenericKeyedObjectPoolConfig#getNumTestsPerEvictionRun()} for each
* per user pool.
+ * @param numTestsPerEvictionRun The new value
*/
public void setDefaultNumTestsPerEvictionRun(final int numTestsPerEvictionRun) {
assertInitializationAllowed();
@@ -384,7 +393,7 @@ public abstract class InstanceKeyDataSource
}
/**
- * Gets the default value for
+ * @return the default value for
* {@link org.apache.tomcat.dbcp.pool2.impl.GenericObjectPool GenericObjectPool#getSoftMinEvictableIdleTimeMillis()} for each
* per user pool.
*/
@@ -395,6 +404,7 @@ public abstract class InstanceKeyDataSource
/**
* Sets the default value for
* {@link org.apache.tomcat.dbcp.pool2.impl.GenericObjectPool GenericObjectPool#getSoftMinEvictableIdleTimeMillis()} for each per user pool.
+ * @param softMinEvictableIdleTimeMillis The new value
*/
public void setDefaultSoftMinEvictableIdleTimeMillis(
final long softMinEvictableIdleTimeMillis) {
@@ -403,7 +413,7 @@ public abstract class InstanceKeyDataSource
}
/**
- * Gets the default value for
+ * @return the default value for
* {@link org.apache.tomcat.dbcp.pool2.impl.GenericObjectPool GenericObjectPool#getTestOnCreate()} for each per user pool.
*/
public boolean getDefaultTestOnCreate() {
@@ -413,6 +423,7 @@ public abstract class InstanceKeyDataSource
/**
* Sets the default value for
* {@link org.apache.tomcat.dbcp.pool2.impl.GenericObjectPool GenericObjectPool#getTestOnCreate()} for each per user pool.
+ * @param testOnCreate The new value
*/
public void setDefaultTestOnCreate(final boolean testOnCreate) {
assertInitializationAllowed();
@@ -420,7 +431,7 @@ public abstract class InstanceKeyDataSource
}
/**
- * Gets the default value for
+ * @return the default value for
* {@link org.apache.tomcat.dbcp.pool2.impl.GenericObjectPool GenericObjectPool#getTestOnBorrow()} for each per user pool.
*/
public boolean getDefaultTestOnBorrow() {
@@ -430,6 +441,7 @@ public abstract class InstanceKeyDataSource
/**
* Sets the default value for
* {@link org.apache.tomcat.dbcp.pool2.impl.GenericObjectPool GenericObjectPool#getTestOnBorrow()} for each per user pool.
+ * @param testOnBorrow The new value
*/
public void setDefaultTestOnBorrow(final boolean testOnBorrow) {
assertInitializationAllowed();
@@ -437,7 +449,7 @@ public abstract class InstanceKeyDataSource
}
/**
- * Gets the default value for
+ * @return the default value for
* {@link org.apache.tomcat.dbcp.pool2.impl.GenericObjectPool GenericObjectPool#getTestOnReturn()} for each per user pool.
*/
public boolean getDefaultTestOnReturn() {
@@ -447,6 +459,7 @@ public abstract class InstanceKeyDataSource
/**
* Sets the default value for
* {@link org.apache.tomcat.dbcp.pool2.impl.GenericObjectPool GenericObjectPool#getTestOnReturn()} for each per user pool.
+ * @param testOnReturn The new value
*/
public void setDefaultTestOnReturn(final boolean testOnReturn) {
assertInitializationAllowed();
@@ -454,7 +467,7 @@ public abstract class InstanceKeyDataSource
}
/**
- * Gets the default value for
+ * @return the default value for
* {@link org.apache.tomcat.dbcp.pool2.impl.GenericObjectPool GenericObjectPool#getTestWhileIdle()} for each per user pool.
*/
public boolean getDefaultTestWhileIdle() {
@@ -464,6 +477,7 @@ public abstract class InstanceKeyDataSource
/**
* Sets the default value for
* {@link org.apache.tomcat.dbcp.pool2.impl.GenericObjectPool GenericObjectPool#getTestWhileIdle()} for each per user pool.
+ * @param testWhileIdle The new value
*/
public void setDefaultTestWhileIdle(final boolean testWhileIdle) {
assertInitializationAllowed();
@@ -471,7 +485,7 @@ public abstract class InstanceKeyDataSource
}
/**
- * Gets the default value for
+ * @return the default value for
* {@link org.apache.tomcat.dbcp.pool2.impl.GenericObjectPool GenericObjectPool#getTimeBetweenEvictionRunsMillis ()} for each
* per user pool.
*/
@@ -483,6 +497,7 @@ public abstract class InstanceKeyDataSource
* Sets the default value for
* {@link org.apache.tomcat.dbcp.pool2.impl.GenericObjectPool GenericObjectPool#getTimeBetweenEvictionRunsMillis ()} for each
* per user pool.
+ * @param timeBetweenEvictionRunsMillis The new value
*/
public void setDefaultTimeBetweenEvictionRunsMillis (
final long timeBetweenEvictionRunsMillis ) {
@@ -675,7 +690,7 @@ public abstract class InstanceKeyDataSource
* Get the value of jndiEnvironment which is used when instantiating
* a jndi InitialContext. This InitialContext is used to locate the
* backend ConnectionPoolDataSource.
- *
+ * @param key The environment property name
* @return value of jndiEnvironment.
*/
public String getJndiEnvironment(final String key) {
@@ -764,6 +779,7 @@ public abstract class InstanceKeyDataSource
* <strong>MUST</strong> be an SQL SELECT statement that returns at least
* one row. If not specified, {@link Connection#isValid(int)} will be used
* to validate connections.
+ * @return the validation query
*/
public String getValidationQuery() {
return this.validationQuery;
@@ -775,6 +791,7 @@ public abstract class InstanceKeyDataSource
* <strong>MUST</strong> be an SQL SELECT statement that returns at least
* one row. If not specified, connections will be validated using
* {@link Connection#isValid(int)}.
+ * @param validationQuery The validation query
*/
public void setValidationQuery(final String validationQuery) {
assertInitializationAllowed();
@@ -782,7 +799,7 @@ public abstract class InstanceKeyDataSource
}
/**
- * Returns the timeout in seconds before the validation query fails.
+ * @return the timeout in seconds before the validation query fails.
*/
public int getValidationQueryTimeout() {
return validationQueryTimeout;
@@ -824,7 +841,7 @@ public abstract class InstanceKeyDataSource
}
/**
- * Returns the maximum permitted lifetime of a connection in milliseconds. A
+ * @return the maximum permitted lifetime of a connection in milliseconds. A
* value of zero or less indicates an infinite lifetime.
*/
public long getMaxConnLifetimeMillis() {
@@ -839,6 +856,7 @@ public abstract class InstanceKeyDataSource
* initialized. The pool is initialized the first time one of the
* following methods is invoked: <code>getConnection, setLogwriter,
* setLoginTimeout, getLoginTimeout, getLogWriter.</code></p>
+ * @param maxConnLifetimeMillis The maximum connection lifetime
*/
public void setMaxConnLifetimeMillis(final long maxConnLifetimeMillis) {
this.maxConnLifetimeMillis = maxConnLifetimeMillis;
@@ -852,6 +870,8 @@ public abstract class InstanceKeyDataSource
/**
* Attempt to establish a database connection.
+ * @return the connection
+ * @throws SQLException Connection failed
*/
@Override
public Connection getConnection() throws SQLException {
@@ -868,7 +888,10 @@ public abstract class InstanceKeyDataSource
* means that the database password has been changed. In this case, the <code>PooledConnectionAndInfo</code>
* instance retrieved with the old password is destroyed and the <code>getPooledConnectionAndInfo</code> is
* repeatedly invoked until a <code>PooledConnectionAndInfo</code> instance with the new password is returned.
- *
+ * @param username The user name to use to connect
+ * @param password The password
+ * @return the connection
+ * @throws SQLException Connection failed
*/
@Override
public Connection getConnection(final String username, final String password)
diff --git a/java/org/apache/tomcat/dbcp/dbcp2/datasources/InstanceKeyDataSourceFactory.java b/java/org/apache/tomcat/dbcp/dbcp2/datasources/InstanceKeyDataSourceFactory.java
index 7e59ef9..56392a3 100644
--- a/java/org/apache/tomcat/dbcp/dbcp2/datasources/InstanceKeyDataSourceFactory.java
+++ b/java/org/apache/tomcat/dbcp/dbcp2/datasources/InstanceKeyDataSourceFactory.java
@@ -72,6 +72,7 @@ abstract class InstanceKeyDataSourceFactory implements ObjectFactory {
/**
* Close all pools associated with this class.
+ * @throws Exception Close exception
*/
public static void closeAll() throws Exception {
//Get iterator to loop over all instances of this datasource.
@@ -85,8 +86,8 @@ abstract class InstanceKeyDataSourceFactory implements ObjectFactory {
/**
- * implements ObjectFactory to create an instance of SharedPoolDataSource
- * or PerUserPoolDataSource
+ * Implements ObjectFactory to create an instance of SharedPoolDataSource
+ * or PerUserPoolDataSource.
*/
@Override
public Object getObjectInstance(final Object refObj, final Name name,
@@ -295,6 +296,7 @@ abstract class InstanceKeyDataSourceFactory implements ObjectFactory {
/**
+ * @param className The class name
* @return true if and only if className is the value returned
* from getClass().getName().toString()
*/
@@ -303,12 +305,20 @@ abstract class InstanceKeyDataSourceFactory implements ObjectFactory {
/**
* Creates an instance of the subclass and sets any properties
* contained in the Reference.
+ * @param ref The reference
+ * @return the data source
+ * @throws IOException IO error
+ * @throws ClassNotFoundException Couldn't load data source implementation
*/
protected abstract InstanceKeyDataSource getNewInstance(Reference ref)
throws IOException, ClassNotFoundException;
/**
- * used to set some properties saved within a Reference
+ * Used to set some properties saved within a Reference.
+ * @param data Object data
+ * @return the deserialized object
+ * @throws IOException Stream error
+ * @throws ClassNotFoundException Couldn't load object class
*/
protected static final Object deserialize(final byte[] data)
throws IOException, ClassNotFoundException {
diff --git a/java/org/apache/tomcat/dbcp/dbcp2/datasources/KeyedCPDSConnectionFactory.java b/java/org/apache/tomcat/dbcp/dbcp2/datasources/KeyedCPDSConnectionFactory.java
index 5eec558..23e5bc4 100644
--- a/java/org/apache/tomcat/dbcp/dbcp2/datasources/KeyedCPDSConnectionFactory.java
+++ b/java/org/apache/tomcat/dbcp/dbcp2/datasources/KeyedCPDSConnectionFactory.java
@@ -81,6 +81,8 @@ class KeyedCPDSConnectionFactory
* {@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 validationQueryTimeout 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.
*/
@@ -330,9 +332,10 @@ class KeyedCPDSConnectionFactory
/**
* Sets the maximum lifetime in milliseconds of a connection after which the
- * connection will always fail activation, passivation and validation. A
- * value of zero or less indicates an infinite lifetime. The default value
- * is -1.
+ * 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.
*/
public void setMaxConnLifetimeMillis(final long maxConnLifetimeMillis) {
this.maxConnLifetimeMillis = maxConnLifetimeMillis;
diff --git a/java/org/apache/tomcat/dbcp/dbcp2/datasources/PerUserPoolDataSource.java b/java/org/apache/tomcat/dbcp/dbcp2/datasources/PerUserPoolDataSource.java
index d6dd0e2..87ae16f 100644
--- a/java/org/apache/tomcat/dbcp/dbcp2/datasources/PerUserPoolDataSource.java
+++ b/java/org/apache/tomcat/dbcp/dbcp2/datasources/PerUserPoolDataSource.java
@@ -119,6 +119,8 @@ public class PerUserPoolDataSource extends InstanceKeyDataSource {
* Gets the user specific value for
* {@link GenericObjectPool#getBlockWhenExhausted()} for the
* specified user's pool or the default if no user specific value is defined.
+ * @param key The user
+ * @return <code>true</code> to block
*/
public boolean getPerUserBlockWhenExhausted(final String key) {
Boolean value = null;
@@ -135,6 +137,8 @@ public class PerUserPoolDataSource extends InstanceKeyDataSource {
* Sets a user specific value for
* {@link GenericObjectPool#getBlockWhenExhausted()} for the specified
* user's pool.
+ * @param username The user
+ * @param value The value
*/
public void setPerUserBlockWhenExhausted(final String username,
final Boolean value) {
@@ -161,6 +165,8 @@ public class PerUserPoolDataSource extends InstanceKeyDataSource {
* Gets the user specific value for
* {@link GenericObjectPool#getEvictionPolicyClassName()} for the
* specified user's pool or the default if no user specific value is defined.
+ * @param key The user
+ * @return the policy class name
*/
public String getPerUserEvictionPolicyClassName(final String key) {
String value = null;
@@ -177,6 +183,8 @@ public class PerUserPoolDataSource extends InstanceKeyDataSource {
* Sets a user specific value for
* {@link GenericObjectPool#getEvictionPolicyClassName()} for the specified
* user's pool.
+ * @param username The user
+ * @param value The value
*/
public void setPerUserEvictionPolicyClassName(final String username,
final String value) {
@@ -203,6 +211,8 @@ public class PerUserPoolDataSource extends InstanceKeyDataSource {
* Gets the user specific value for {@link GenericObjectPool#getLifo()} for
* the specified user's pool or the default if no user specific value is
* defined.
+ * @param key The user
+ * @return <code>true</code> to use LIFO
*/
public boolean getPerUserLifo(final String key) {
Boolean value = null;
@@ -219,6 +229,8 @@ public class PerUserPoolDataSource extends InstanceKeyDataSource {
* Sets a user specific value for
* {@link GenericObjectPool#getLifo()} for the specified
* user's pool.
+ * @param username The user
+ * @param value The value
*/
public void setPerUserLifo(final String username, final Boolean value) {
assertInitializationAllowed();
@@ -243,6 +255,8 @@ public class PerUserPoolDataSource extends InstanceKeyDataSource {
* Gets the user specific value for
* {@link GenericObjectPool#getMaxIdle()} for the
* specified user's pool or the default if no user specific value is defined.
+ * @param key The user
+ * @return the maximum idle
*/
public int getPerUserMaxIdle(final String key) {
Integer value = null;
@@ -259,6 +273,8 @@ public class PerUserPoolDataSource extends InstanceKeyDataSource {
* Sets a user specific value for
* {@link GenericObjectPool#getMaxIdle()} for the specified
* user's pool.
+ * @param username The user
+ * @param value The value
*/
public void setPerUserMaxIdle(final String username, final Integer value) {
assertInitializationAllowed();
@@ -283,6 +299,8 @@ public class PerUserPoolDataSource extends InstanceKeyDataSource {
* Gets the user specific value for
* {@link GenericObjectPool#getMaxTotal()} for the
* specified user's pool or the default if no user specific value is defined.
+ * @param key The user
+ * @return the maximum total
*/
public int getPerUserMaxTotal(final String key) {
Integer value = null;
@@ -299,6 +317,8 @@ public class PerUserPoolDataSource extends InstanceKeyDataSource {
* Sets a user specific value for
* {@link GenericObjectPool#getMaxTotal()} for the specified
* user's pool.
+ * @param username The user
+ * @param value The value
*/
public void setPerUserMaxTotal(final String username, final Integer value) {
assertInitializationAllowed();
@@ -323,6 +343,8 @@ public class PerUserPoolDataSource extends InstanceKeyDataSource {
* Gets the user specific value for
* {@link GenericObjectPool#getMaxWaitMillis()} for the
* specified user's pool or the default if no user specific value is defined.
+ * @param key The user
+ * @return the maximum wait time
*/
public long getPerUserMaxWaitMillis(final String key) {
Long value = null;
@@ -339,6 +361,8 @@ public class PerUserPoolDataSource extends InstanceKeyDataSource {
* Sets a user specific value for
* {@link GenericObjectPool#getMaxWaitMillis()} for the specified
* user's pool.
+ * @param username The user
+ * @param value The value
*/
public void setPerUserMaxWaitMillis(final String username, final Long value) {
assertInitializationAllowed();
@@ -364,6 +388,8 @@ public class PerUserPoolDataSource extends InstanceKeyDataSource {
* Gets the user specific value for
* {@link GenericObjectPool#getMinEvictableIdleTimeMillis()} for the
* specified user's pool or the default if no user specific value is defined.
+ * @param key The user
+ * @return the minimum idle time for eviction
*/
public long getPerUserMinEvictableIdleTimeMillis(final String key) {
Long value = null;
@@ -380,6 +406,8 @@ public class PerUserPoolDataSource extends InstanceKeyDataSource {
* Sets a user specific value for
* {@link GenericObjectPool#getMinEvictableIdleTimeMillis()} for the
* specified user's pool.
+ * @param username The user
+ * @param value The value
*/
public void setPerUserMinEvictableIdleTimeMillis(final String username,
final Long value) {
@@ -407,6 +435,8 @@ public class PerUserPoolDataSource extends InstanceKeyDataSource {
* Gets the user specific value for
* {@link GenericObjectPool#getMinIdle()} for the
* specified user's pool or the default if no user specific value is defined.
+ * @param key The user
+ * @return the minimum idle count
*/
public int getPerUserMinIdle(final String key) {
Integer value = null;
@@ -423,6 +453,8 @@ public class PerUserPoolDataSource extends InstanceKeyDataSource {
* Sets a user specific value for
* {@link GenericObjectPool#getMinIdle()} for the specified
* user's pool.
+ * @param username The user
+ * @param value The value
*/
public void setPerUserMinIdle(final String username, final Integer value) {
assertInitializationAllowed();
@@ -447,6 +479,8 @@ public class PerUserPoolDataSource extends InstanceKeyDataSource {
* Gets the user specific value for
* {@link GenericObjectPool#getNumTestsPerEvictionRun()} for the
* specified user's pool or the default if no user specific value is defined.
+ * @param key The user
+ * @return the tests count
*/
public int getPerUserNumTestsPerEvictionRun(final String key) {
Integer value = null;
@@ -463,6 +497,8 @@ public class PerUserPoolDataSource extends InstanceKeyDataSource {
* Sets a user specific value for
* {@link GenericObjectPool#getNumTestsPerEvictionRun()} for the specified
* user's pool.
+ * @param username The user
+ * @param value The value
*/
public void setPerUserNumTestsPerEvictionRun(final String username,
final Integer value) {
@@ -489,6 +525,8 @@ public class PerUserPoolDataSource extends InstanceKeyDataSource {
* Gets the user specific value for
* {@link GenericObjectPool#getSoftMinEvictableIdleTimeMillis()} for the
* specified user's pool or the default if no user specific value is defined.
+ * @param key The user
+ * @return the soft minimum idle time for eviction
*/
public long getPerUserSoftMinEvictableIdleTimeMillis(final String key) {
Long value = null;
@@ -505,6 +543,8 @@ public class PerUserPoolDataSource extends InstanceKeyDataSource {
* Sets a user specific value for
* {@link GenericObjectPool#getSoftMinEvictableIdleTimeMillis()} for the
* specified user's pool.
+ * @param username The user
+ * @param value The value
*/
public void setPerUserSoftMinEvictableIdleTimeMillis(final String username,
final Long value) {
@@ -531,6 +571,8 @@ public class PerUserPoolDataSource extends InstanceKeyDataSource {
* Gets the user specific value for
* {@link GenericObjectPool#getTestOnCreate()} for the
* specified user's pool or the default if no user specific value is defined.
+ * @param key The user
+ * @return <code>true</code> to test on create
*/
public boolean getPerUserTestOnCreate(final String key) {
Boolean value = null;
@@ -547,6 +589,8 @@ public class PerUserPoolDataSource extends InstanceKeyDataSource {
* Sets a user specific value for
* {@link GenericObjectPool#getTestOnCreate()} for the specified
* user's pool.
+ * @param username The user
+ * @param value The value
*/
public void setPerUserTestOnCreate(final String username, final Boolean value) {
assertInitializationAllowed();
@@ -571,6 +615,8 @@ public class PerUserPoolDataSource extends InstanceKeyDataSource {
* Gets the user specific value for
* {@link GenericObjectPool#getTestOnBorrow()} for the
* specified user's pool or the default if no user specific value is defined.
+ * @param key The user
+ * @return <code>true</code> to test on borrow
*/
public boolean getPerUserTestOnBorrow(final String key) {
Boolean value = null;
@@ -587,6 +633,8 @@ public class PerUserPoolDataSource extends InstanceKeyDataSource {
* Sets a user specific value for
* {@link GenericObjectPool#getTestOnBorrow()} for the specified
* user's pool.
+ * @param username The user
+ * @param value The value
*/
public void setPerUserTestOnBorrow(final String username, final Boolean value) {
assertInitializationAllowed();
@@ -611,6 +659,8 @@ public class PerUserPoolDataSource extends InstanceKeyDataSource {
* Gets the user specific value for
* {@link GenericObjectPool#getTestOnReturn()} for the
* specified user's pool or the default if no user specific value is defined.
+ * @param key The user
+ * @return <code>true</code> to test on return
*/
public boolean getPerUserTestOnReturn(final String key) {
Boolean value = null;
@@ -627,6 +677,8 @@ public class PerUserPoolDataSource extends InstanceKeyDataSource {
* Sets a user specific value for
* {@link GenericObjectPool#getTestOnReturn()} for the specified
* user's pool.
+ * @param username The user
+ * @param value The value
*/
public void setPerUserTestOnReturn(final String username, final Boolean value) {
assertInitializationAllowed();
@@ -652,6 +704,8 @@ public class PerUserPoolDataSource extends InstanceKeyDataSource {
* Gets the user specific value for
* {@link GenericObjectPool#getTestWhileIdle()} for the
* specified user's pool or the default if no user specific value is defined.
+ * @param key The user
+ * @return <code>true</code> to test while idle
*/
public boolean getPerUserTestWhileIdle(final String key) {
Boolean value = null;
@@ -668,6 +722,8 @@ public class PerUserPoolDataSource extends InstanceKeyDataSource {
* Sets a user specific value for
* {@link GenericObjectPool#getTestWhileIdle()} for the specified
* user's pool.
+ * @param username The user
+ * @param value The value
*/
public void setPerUserTestWhileIdle(final String username, final Boolean value) {
assertInitializationAllowed();
@@ -693,6 +749,8 @@ public class PerUserPoolDataSource extends InstanceKeyDataSource {
* Gets the user specific value for
* {@link GenericObjectPool#getTimeBetweenEvictionRunsMillis()} for the
* specified user's pool or the default if no user specific value is defined.
+ * @param key The user
+ * @return time between eviction runs
*/
public long getPerUserTimeBetweenEvictionRunsMillis(final String key) {
Long value = null;
@@ -709,6 +767,8 @@ public class PerUserPoolDataSource extends InstanceKeyDataSource {
* Sets a user specific value for
* {@link GenericObjectPool#getTimeBetweenEvictionRunsMillis ()} for the specified
* user's pool.
+ * @param username The user
+ * @param value The value
*/
public void setPerUserTimeBetweenEvictionRunsMillis(final String username,
final Long value) {
@@ -735,6 +795,8 @@ public class PerUserPoolDataSource extends InstanceKeyDataSource {
/**
* Gets the user specific default value for
* {@link Connection#setAutoCommit(boolean)} for the specified user's pool.
+ * @param key The user
+ * @return <code>true</code> to commit automatically
*/
public Boolean getPerUserDefaultAutoCommit(final String key) {
Boolean value = null;
@@ -747,6 +809,8 @@ public class PerUserPoolDataSource extends InstanceKeyDataSource {
/**
* Sets a user specific default value for
* {@link Connection#setAutoCommit(boolean)} for the specified user's pool.
+ * @param username The user
+ * @param value The value
*/
public void setPerUserDefaultAutoCommit(final String username, final Boolean value) {
assertInitializationAllowed();
@@ -770,6 +834,8 @@ public class PerUserPoolDataSource extends InstanceKeyDataSource {
/**
* Gets the user specific default value for
* {@link Connection#setReadOnly(boolean)} for the specified user's pool.
+ * @param key The user
+ * @return <code>true</code> is read only by default
*/
public Boolean getPerUserDefaultReadOnly(final String key) {
Boolean value = null;
@@ -782,6 +848,8 @@ public class PerUserPoolDataSource extends InstanceKeyDataSource {
/**
* Sets a user specific default value for
* {@link Connection#setReadOnly(boolean)} for the specified user's pool.
+ * @param username The user
+ * @param value The value
*/
public void setPerUserDefaultReadOnly(final String username, final Boolean value) {
assertInitializationAllowed();
@@ -805,6 +873,8 @@ public class PerUserPoolDataSource extends InstanceKeyDataSource {
/**
* Gets the user specific default value for
* {@link Connection#setTransactionIsolation(int)} for the specified user's pool.
+ * @param key The user
+ * @return the default transaction isolation
*/
public Integer getPerUserDefaultTransactionIsolation(final String key) {
Integer value = null;
@@ -817,6 +887,8 @@ public class PerUserPoolDataSource extends InstanceKeyDataSource {
/**
* Sets a user specific default value for
* {@link Connection#setTransactionIsolation(int)} for the specified user's pool.
+ * @param username The user
+ * @param value The value
*/
public void setPerUserDefaultTransactionIsolation(final String username,
final Integer value) {
@@ -843,14 +915,15 @@ public class PerUserPoolDataSource extends InstanceKeyDataSource {
// Instrumentation Methods
/**
- * Get the number of active connections in the default pool.
+ * @return the number of active connections in the default pool.
*/
public int getNumActive() {
return getNumActive(null);
}
/**
- * Get the number of active connections in the pool for a given user.
+ * @param username The user
+ * @return the number of active connections in the pool for a given user.
*/
public int getNumActive(final String username) {
final ObjectPool<PooledConnectionAndInfo> pool =
@@ -859,14 +932,15 @@ public class PerUserPoolDataSource extends InstanceKeyDataSource {
}
/**
- * Get the number of idle connections in the default pool.
+ * @return the number of idle connections in the default pool.
*/
public int getNumIdle() {
return getNumIdle(null);
}
/**
- * Get the number of idle connections in the pool for a given user.
+ * @param username The user
+ * @return the number of idle connections in the pool for a given user.
*/
public int getNumIdle(final String username) {
final ObjectPool<PooledConnectionAndInfo> pool =
@@ -984,7 +1058,8 @@ public class PerUserPoolDataSource extends InstanceKeyDataSource {
}
/**
- * Returns a <code>PerUserPoolDataSource</code> {@link Reference}.
+ * @return a <code>PerUserPoolDataSource</code> {@link Reference}.
+ * @throws NamingException Should not happen
*/
@Override
public Reference getReference() throws NamingException {
@@ -998,7 +1073,7 @@ public class PerUserPoolDataSource extends InstanceKeyDataSource {
* Create a pool key from the provided parameters.
*
* @param username User name
- * @return The pool key
+ * @return the pool key
*/
private PoolKey getPoolKey(final String username) {
return new PoolKey(getDataSourceName(), username);
diff --git a/java/org/apache/tomcat/dbcp/dbcp2/datasources/SharedPoolDataSource.java b/java/org/apache/tomcat/dbcp/dbcp2/datasources/SharedPoolDataSource.java
index 3267f45..19b5908 100644
--- a/java/org/apache/tomcat/dbcp/dbcp2/datasources/SharedPoolDataSource.java
+++ b/java/org/apache/tomcat/dbcp/dbcp2/datasources/SharedPoolDataSource.java
@@ -81,14 +81,15 @@ public class SharedPoolDataSource extends InstanceKeyDataSource {
// Properties
/**
- * Set {@link GenericKeyedObjectPool#getMaxTotal()} for this pool.
+ * @return {@link GenericKeyedObjectPool#getMaxTotal()} for this pool.
*/
public int getMaxTotal() {
return this.maxTotal;
}
/**
- * Get {@link GenericKeyedObjectPool#getMaxTotal()} for this pool.
+ * Set {@link GenericKeyedObjectPool#getMaxTotal()} for this pool.
+ * @param maxTotal The max total value
*/
public void setMaxTotal(final int maxTotal) {
assertInitializationAllowed();
@@ -100,14 +101,14 @@ public class SharedPoolDataSource extends InstanceKeyDataSource {
// Instrumentation Methods
/**
- * Get the number of active connections in the pool.
+ * @return the number of active connections in the pool.
*/
public int getNumActive() {
return pool == null ? 0 : pool.getNumActive();
}
/**
- * Get the number of idle connections in the pool.
+ * @return the number of idle connections in the pool.
*/
public int getNumIdle() {
return pool == null ? 0 : pool.getNumIdle();
@@ -151,7 +152,8 @@ public class SharedPoolDataSource extends InstanceKeyDataSource {
}
/**
- * Returns a <code>SharedPoolDataSource</code> {@link Reference}.
+ * @return a <code>SharedPoolDataSource</code> {@link Reference}.
+ * @throws NamingException Should not occur
*/
@Override
public Reference getReference() throws NamingException {
diff --git a/java/org/apache/tomcat/dbcp/pool2/impl/GenericObjectPool.java b/java/org/apache/tomcat/dbcp/pool2/impl/GenericObjectPool.java
index cf92f39..3e49757 100644
--- a/java/org/apache/tomcat/dbcp/pool2/impl/GenericObjectPool.java
+++ b/java/org/apache/tomcat/dbcp/pool2/impl/GenericObjectPool.java
@@ -1194,5 +1194,4 @@ public class GenericObjectPool<T> extends BaseGenericObjectPool<T>
builder.append(", abandonedConfig=");
builder.append(abandonedConfig);
}
-
}
diff --git a/java/org/apache/tomcat/dbcp/pool2/impl/SoftReferenceObjectPool.java b/java/org/apache/tomcat/dbcp/pool2/impl/SoftReferenceObjectPool.java
index 47d3cc5..c898647 100644
--- a/java/org/apache/tomcat/dbcp/pool2/impl/SoftReferenceObjectPool.java
+++ b/java/org/apache/tomcat/dbcp/pool2/impl/SoftReferenceObjectPool.java
@@ -110,7 +110,7 @@ public class SoftReferenceObjectPool<T> extends BaseObjectPool<T> {
* if an exception occurs creating a new instance
* @return a valid, activated object instance
*/
- @SuppressWarnings("null") // ref can not be null
+ @SuppressWarnings("null") // ref cannot be null
@Override
public synchronized T borrowObject() throws Exception {
assertOpen();
diff --git a/java/org/apache/tomcat/jni/Address.java b/java/org/apache/tomcat/jni/Address.java
index d04be3b..6bf9ec8 100644
--- a/java/org/apache/tomcat/jni/Address.java
+++ b/java/org/apache/tomcat/jni/Address.java
@@ -28,12 +28,14 @@ public class Address {
* Fill the Sockaddr class from apr_sockaddr_t
* @param info Sockaddr class to fill
* @param sa Structure pointer
+ * @return <code>true</code> if the operation was successful
*/
public static native boolean fill(Sockaddr info, long sa);
/**
* Create the Sockaddr object from apr_sockaddr_t
* @param sa Structure pointer
+ * @return the socket address
*/
public static native Sockaddr getInfo(long sa);
@@ -59,6 +61,7 @@ public class Address {
* </PRE>
* @param p The pool for the apr_sockaddr_t and associated storage.
* @return The new apr_sockaddr_t.
+ * @throws Exception Operation failed
*/
public static native long info(String hostname, int family,
int port, int flags, long p)
@@ -93,6 +96,7 @@ public class Address {
* @param which Which interface do we want the apr_sockaddr_t for?
* @param sock The socket to use
* @return The returned apr_sockaddr_t.
+ * @throws Exception An error occurred
*/
public static native long get(int which, long sock)
throws Exception;
@@ -104,8 +108,7 @@ public class Address {
*
* @param a One of the APR socket addresses.
* @param b The other APR socket address.
- * The return value will be True if the addresses
- * are equivalent.
+ * @return <code>true</code> if the addresses are equal
*/
public static native boolean equal(long a, long b);
diff --git a/java/org/apache/tomcat/jni/Buffer.java b/java/org/apache/tomcat/jni/Buffer.java
index 703bfca..b79214e 100644
--- a/java/org/apache/tomcat/jni/Buffer.java
+++ b/java/org/apache/tomcat/jni/Buffer.java
@@ -77,12 +77,14 @@ public class Buffer {
/**
* Returns the memory address of the ByteBuffer.
* @param buf Previously allocated ByteBuffer.
+ * @return the memory address
*/
public static native long address(ByteBuffer buf);
/**
* Returns the allocated memory size of the ByteBuffer.
* @param buf Previously allocated ByteBuffer.
+ * @return the size
*/
public static native long size(ByteBuffer buf);
diff --git a/java/org/apache/tomcat/jni/CertificateVerifier.java b/java/org/apache/tomcat/jni/CertificateVerifier.java
new file mode 100644
index 0000000..b9b0d48
--- /dev/null
+++ b/java/org/apache/tomcat/jni/CertificateVerifier.java
@@ -0,0 +1,34 @@
+/*
+ * 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;
+
+/**
+ * Is called during handshake and hooked into openssl via {@code SSL_CTX_set_cert_verify_callback}.
+ */
+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.
+ *
+ * @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 --git a/java/org/apache/tomcat/jni/Directory.java b/java/org/apache/tomcat/jni/Directory.java
index 7c2de6e..8a94972 100644
--- a/java/org/apache/tomcat/jni/Directory.java
+++ b/java/org/apache/tomcat/jni/Directory.java
@@ -28,15 +28,18 @@ public class Directory {
* @param path the path for the directory to be created. (use / on all systems)
* @param perm Permissions for the new directory.
* @param pool the pool to use.
+ * @return the operation result
*/
public static native int make(String path, int perm, long pool);
- /** Creates a new directory on the file system, but behaves like
+ /**
+ * Creates a new directory on the file system, but behaves like
* 'mkdir -p'. Creates intermediate directories as required. No error
* will be reported if PATH already exists.
* @param path the path for the directory to be created. (use / on all systems)
* @param perm Permissions for the new directory.
* @param pool the pool to use.
+ * @return the operation result
*/
public static native int makeRecursive(String path, int perm, long pool);
@@ -44,6 +47,7 @@ public class Directory {
* Remove directory from the file system.
* @param path the path for the directory to be removed. (use / on all systems)
* @param pool the pool to use.
+ * @return the operation result
*/
public static native int remove(String path, long pool);
@@ -57,7 +61,6 @@ public class Directory {
* directory is found, that location is cached by the library. Thus,
* callers only pay the cost of this algorithm once if that one time
* is successful.
- *
*/
public static native String tempGet(long pool);
@@ -66,6 +69,7 @@ public class Directory {
* @param dirname The full path to the directory (use / on all systems)
* @param pool The pool to use.
* @return The opened directory descriptor.
+ * @throws Error An error occurred
*/
public static native long open(String dirname, long pool)
throws Error;
@@ -73,12 +77,14 @@ public class Directory {
/**
* close the specified directory.
* @param thedir the directory descriptor to close.
+ * @return the operation result
*/
public static native int close(long thedir);
/**
* Rewind the directory to the first entry.
* @param thedir the directory descriptor to rewind.
+ * @return the operation result
*/
public static native int rewind(long thedir);
@@ -89,6 +95,7 @@ public class Directory {
* @param wanted The desired apr_finfo_t fields, as a bit flag of APR_FINFO_ values
* @param thedir the directory descriptor returned from apr_dir_open
* No ordering is guaranteed for the entries read.
+ * @return the operation result
*/
public static native int read(FileInfo finfo, int wanted, long thedir);
diff --git a/java/org/apache/tomcat/jni/File.java b/java/org/apache/tomcat/jni/File.java
index 1117e40..e1378d4 100644
--- a/java/org/apache/tomcat/jni/File.java
+++ b/java/org/apache/tomcat/jni/File.java
@@ -217,6 +217,7 @@ public class File {
* If perm is APR_OS_DEFAULT and the file is being created,
* appropriate default permissions will be used.
* @return The opened file descriptor.
+ * @throws Error An error occurred
*/
public static native long open(String fname, int flag, int perm, long pool)
throws Error;
@@ -224,12 +225,14 @@ public class File {
/**
* Close the specified file.
* @param file The file descriptor to close.
+ * @return the operation status
*/
public static native int close(long file);
/**
* Flush the file's buffer.
* @param thefile The file descriptor to flush
+ * @return the operation status
*/
public static native int flush(long thefile);
@@ -247,7 +250,7 @@ public class File {
* with a string that makes the filename unique. Since it will be modified,
* template must not be a string constant, but should be declared as a character
* array.
- *
+ * @throws Error An error occurred
*/
public static native long mktemp(String templ, int flags, long pool)
throws Error;
@@ -258,6 +261,7 @@ public class File {
* @param pool The pool to use.
* If the file is open, it won't be removed until all
* instances are closed.
+ * @return the operation status
*/
public static native int remove(String path, long pool);
@@ -269,6 +273,7 @@ public class File {
* @param fromPath The full path to the original file (using / on all systems)
* @param toPath The full path to the new file (using / on all systems)
* @param pool The pool to use.
+ * @return the operation status
*/
public static native int rename(String fromPath, String toPath, long pool);
@@ -283,6 +288,7 @@ public class File {
* value APR_FILE_SOURCE_PERMS may be given, in which case the source
* file's permissions are copied.
* @param pool The pool to use.
+ * @return the operation status
*/
public static native int copy(String fromPath, String toPath, int perms, long pool);
@@ -296,6 +302,7 @@ public class File {
* value APR_FILE_SOURCE_PERMS may be given, in which case the source
* file's permissions are copied.
* @param pool The pool to use.
+ * @return the operation status
*/
public static native int append(String fromPath, String toPath, int perms, long pool);
@@ -303,6 +310,7 @@ public class File {
* Write the string into the specified file.
* @param str The string to write. Must be NUL terminated!
* @param thefile The file descriptor to write to
+ * @return the operation status
*/
public static native int puts(byte [] str, long thefile);
@@ -317,6 +325,7 @@ public class File {
* </PRE>
* @param offset The offset to move the pointer to.
* @return Offset the pointer was actually moved to.
+ * @throws Error If an error occurs reading the file
*/
public static native long seek(long thefile, int where, long offset)
throws Error;
@@ -325,6 +334,7 @@ public class File {
* Write a character into the specified file.
* @param ch The character to write.
* @param thefile The file descriptor to write to
+ * @return the operation status
*/
public static native int putc(byte ch, long thefile);
@@ -332,6 +342,7 @@ public class File {
* Put a character back onto a specified stream.
* @param ch The character to write.
* @param thefile The file descriptor to write to
+ * @return the operation status
*/
public static native int ungetc(byte ch, long thefile);
@@ -535,6 +546,7 @@ public class File {
* @param buf The buffer to store the string in.
* @param offset Start offset in buf
* @param thefile The file descriptor to read from
+ * @return the number of bytes read.
*/
public static native int gets(byte[] buf, int offset, long thefile);
@@ -543,6 +555,7 @@ public class File {
* Read a character from the specified file.
* @param thefile The file descriptor to read from
* @return The read character
+ * @throws Error If an error occurs reading the file
*/
public static native int getc(long thefile)
throws Error;
@@ -555,8 +568,9 @@ public class File {
public static native int eof(long fptr);
/**
- * return the file name of the current file.
+ * Return the file name of the current file.
* @param thefile The currently open file.
+ * @return the name
*/
public static native String nameGet(long thefile);
@@ -569,7 +583,7 @@ public class File {
* APR_ENOTIMPL.
* @param fname The file (name) to apply the permissions to.
* @param perms The permission bits to apply to the file.
- *
+ * @return the operation status
*/
public static native int permsSet(String fname, int perms);
@@ -590,6 +604,7 @@ public class File {
* </PRE>
* @param mask Mask of valid bits in attributes.
* @param pool the pool to use.
+ * @return the operation status
*/
public static native int attrsSet(String fname, int attributes, int mask, long pool);
@@ -600,6 +615,7 @@ public class File {
* @param fname The full path to the file (using / on all systems)
* @param mtime The mtime to apply to the file in microseconds
* @param pool The pool to use.
+ * @return the operation status
*/
public static native int mtimeSet(String fname, long mtime, long pool);
@@ -611,12 +627,14 @@ public class File {
* block.
* @param thefile The file to lock.
* @param type The type of lock to establish on the file.
+ * @return the operation status
*/
public static native int lock(long thefile, int type);
/**
* Remove any outstanding locks on the file.
* @param thefile The file to unlock.
+ * @return the operation status
*/
public static native int unlock(long thefile);
@@ -632,6 +650,7 @@ public class File {
* Truncate the file's length to the specified offset
* @param fp The file to truncate
* @param offset The offset to truncate to.
+ * @return the operation status
*/
public static native int trunc(long fp, long offset);
@@ -640,6 +659,7 @@ public class File {
* @param io io[0] The file descriptors to use as input to the pipe.
* io[1] The file descriptor to use as output from the pipe.
* @param pool The pool to operate on.
+ * @return the operation status
*/
public static native int pipeCreate(long [] io, long pool);
@@ -647,6 +667,7 @@ public class File {
* Get the timeout value for a pipe or manipulate the blocking state.
* @param thepipe The pipe we are getting a timeout for.
* @return The current timeout value in microseconds.
+ * @throws Error If an error occurs
*/
public static native long pipeTimeoutGet(long thepipe)
throws Error;
@@ -656,6 +677,7 @@ public class File {
* @param thepipe The pipe we are setting a timeout on.
* @param timeout The timeout value in microseconds. Values < 0 mean
* wait forever, 0 means do not wait at all.
+ * @return the operation status
*/
public static native int pipeTimeoutSet(long thepipe, long timeout);
@@ -666,6 +688,7 @@ public class File {
* @param oldFile The file to duplicate.
* @param pool The pool to use for the new file.
* @return Duplicated file structure.
+ * @throws Error If an error occurs reading the file descriptor
*/
public static native long dup(long newFile, long oldFile, long pool)
throws Error;
@@ -676,7 +699,7 @@ public class File {
* newFile MUST point at a valid apr_file_t. It cannot be NULL.
* @param oldFile The file to duplicate.
* @param pool The pool to use for the new file.
- * @return Status code.
+ * @return the operation status
*/
public static native int dup2(long newFile, long oldFile, long pool);
@@ -688,6 +711,7 @@ public class File {
* @param fname The name of the file to stat.
* @param wanted The desired apr_finfo_t fields, as a bit flag of APR_FINFO_ values
* @param pool the pool to use to allocate the new file.
+ * @return the operation status
*/
public static native int stat(FileInfo finfo, String fname, int wanted, long pool);
@@ -706,6 +730,7 @@ public class File {
* @param finfo Where to store the information about the file.
* @param wanted The desired apr_finfo_t fields, as a bit flag of APR_FINFO_ values
* @param thefile The file to get information about.
+ * @return the operation status
*/
public static native int infoGet(FileInfo finfo, int wanted, long thefile);
diff --git a/java/org/apache/tomcat/jni/Global.java b/java/org/apache/tomcat/jni/Global.java
index 60a4e8f..c1d1eb5 100644
--- a/java/org/apache/tomcat/jni/Global.java
+++ b/java/org/apache/tomcat/jni/Global.java
@@ -45,6 +45,7 @@ public class Global {
* </PRE>
* @param pool the pool from which to allocate the mutex.
* @return Newly created mutex.
+ * @throws Error If an error occurred
*/
public static native long create(String fname, int mech, long pool)
throws Error;
@@ -59,6 +60,7 @@ public class Global {
* This function must be called to maintain portability, even
* if the underlying lock mechanism does not require it.
* @return Newly opened mutex.
+ * @throws Error If an error occurred
*/
public static native long childInit(String fname, long pool)
throws Error;
@@ -67,6 +69,7 @@ public class Global {
* Acquire the lock for the given mutex. If the mutex is already locked,
* the current thread will be put to sleep until the lock becomes available.
* @param mutex the mutex on which to acquire the lock.
+ * @return the operation status
*/
public static native int lock(long mutex);
@@ -76,18 +79,21 @@ public class Global {
* is important that the APR_STATUS_IS_EBUSY(s) macro be used to determine
* if the return value was APR_EBUSY, for portability reasons.
* @param mutex the mutex on which to attempt the lock acquiring.
+ * @return the operation status
*/
public static native int trylock(long mutex);
/**
* Release the lock for the given mutex.
* @param mutex the mutex from which to release the lock.
+ * @return the operation status
*/
public static native int unlock(long mutex);
/**
* Destroy the mutex and free the memory associated with the lock.
* @param mutex the mutex to destroy.
+ * @return the operation status
*/
public static native int destroy(long mutex);
diff --git a/java/org/apache/tomcat/jni/Local.java b/java/org/apache/tomcat/jni/Local.java
index 256af62..50ed955 100644
--- a/java/org/apache/tomcat/jni/Local.java
+++ b/java/org/apache/tomcat/jni/Local.java
@@ -17,7 +17,8 @@
package org.apache.tomcat.jni;
-/** Local socket
+/**
+ * Local socket.
*
* @author Mladen Turk
*/
@@ -28,6 +29,7 @@ public class Local {
* @param path The address of the new socket.
* @param cont The parent pool to use
* @return The new socket that has been set up.
+ * @throws Exception If socket creation failed
*/
public static native long create(String path, long cont)
throws Exception;
@@ -38,6 +40,7 @@ public class Local {
* @param sa The socket address to bind to
* This may be where we will find out if there is any other process
* using the selected port.
+ * @return the operation status
*/
public static native int bind(long sock, long sa);
@@ -47,7 +50,7 @@ public class Local {
* @param backlog The number of outstanding connections allowed in the sockets
* listen queue. If this value is less than zero, for NT pipes
* the number of instances is unlimited.
- *
+ * @return the operation status
*/
public static native int listen(long sock, int backlog);
@@ -57,6 +60,7 @@ public class Local {
* @return A copy of the socket that is connected to the socket that
* made the connection request. This is the socket which should
* be used for all future communication.
+ * @throws Exception If accept failed
*/
public static native long accept(long sock)
throws Exception;
@@ -67,6 +71,7 @@ public class Local {
* @param sock The socket we wish to use for our side of the connection
* @param sa The address of the machine we wish to connect to.
* Unused for NT Pipes.
+ * @return the operation status
*/
public static native int connect(long sock, long sa);
diff --git a/java/org/apache/tomcat/jni/Lock.java b/java/org/apache/tomcat/jni/Lock.java
index 0c8aa05..6bb789a 100644
--- a/java/org/apache/tomcat/jni/Lock.java
+++ b/java/org/apache/tomcat/jni/Lock.java
@@ -54,6 +54,7 @@ public class Lock {
* </PRE>
* @param pool the pool from which to allocate the mutex.
* @return Newly created mutex.
+ * @throws Error An error occurred
*/
public static native long create(String fname, int mech, long pool)
throws Error;
@@ -68,6 +69,7 @@ public class Lock {
* same one that was passed to apr_proc_mutex_create().
* @param pool The pool to operate on.
* @return Newly opened mutex.
+ * @throws Error An error occurred
*/
public static native long childInit(String fname, long pool)
throws Error;
@@ -76,6 +78,7 @@ public class Lock {
* Acquire the lock for the given mutex. If the mutex is already locked,
* the current thread will be put to sleep until the lock becomes available.
* @param mutex the mutex on which to acquire the lock.
+ * @return the operation status
*/
public static native int lock(long mutex);
@@ -85,24 +88,29 @@ public class Lock {
* is important that the APR_STATUS_IS_EBUSY(s) macro be used to determine
* if the return value was APR_EBUSY, for portability reasons.
* @param mutex the mutex on which to attempt the lock acquiring.
+ * @return the operation status
*/
public static native int trylock(long mutex);
/**
* Release the lock for the given mutex.
* @param mutex the mutex from which to release the lock.
+ * @return the operation status
*/
public static native int unlock(long mutex);
/**
* Destroy the mutex and free the memory associated with the lock.
* @param mutex the mutex to destroy.
+ * @return the operation status
*/
public static native int destroy(long mutex);
/**
* Return the name of the lockfile for the mutex, or NULL
* if the mutex doesn't use a lock file
+ * @param mutex the name of the mutex
+ * @return the name of the lock file
*/
public static native String lockfile(long mutex);
@@ -110,11 +118,13 @@ public class Lock {
* Display the name of the mutex, as it relates to the actual method used.
* This matches the valid options for Apache's AcceptMutex directive
* @param mutex the name of the mutex
+ * @return the name of the mutex
*/
public static native String name(long mutex);
/**
* Display the name of the default mutex: APR_LOCK_DEFAULT
+ * @return the default name
*/
public static native String defname();
diff --git a/java/org/apache/tomcat/jni/Mmap.java b/java/org/apache/tomcat/jni/Mmap.java
index 92d7687..08bc795 100644
--- a/java/org/apache/tomcat/jni/Mmap.java
+++ b/java/org/apache/tomcat/jni/Mmap.java
@@ -40,6 +40,7 @@ public class Mmap {
* </PRE>
* @param pool The pool to use when creating the mmap.
* @return The newly created mmap'ed file.
+ * @throws Error Error creating memory mapping
*/
public static native long create(long file, long offset, long size, int flag, long pool)
throws Error;
@@ -49,6 +50,7 @@ public class Mmap {
* @param mmap The mmap to duplicate.
* @param pool The pool to use for new_mmap.
* @return Duplicated mmap'ed file.
+ * @throws Error Error duplicating memory mapping
*/
public static native long dup(long mmap, long pool)
throws Error;
@@ -56,6 +58,7 @@ public class Mmap {
/**
* Remove a mmap'ed.
* @param mm The mmap'ed file.
+ * @return the operation status
*/
public static native int delete(long mm);
@@ -64,6 +67,7 @@ public class Mmap {
* @param mm The mmap'ed file.
* @param offset The offset to move to.
* @return The pointer to the offset specified.
+ * @throws Error Error reading file
*/
public static native long offset(long mm, long offset)
throws Error;
diff --git a/java/org/apache/tomcat/jni/Multicast.java b/java/org/apache/tomcat/jni/Multicast.java
index edbb300..44d123b 100644
--- a/java/org/apache/tomcat/jni/Multicast.java
+++ b/java/org/apache/tomcat/jni/Multicast.java
@@ -31,6 +31,7 @@ public class Multicast {
* default multicast interface will be used. (OS Dependent)
* @param source Source Address to accept transmissions from (non-NULL
* implies Source-Specific Multicast)
+ * @return the operation status
*/
public static native int join(long sock, long join,
long iface, long source);
@@ -44,6 +45,7 @@ public class Multicast {
* default multicast interface will be used. (OS Dependent)
* @param source Source Address to accept transmissions from (non-NULL
* implies Source-Specific Multicast)
+ * @return the operation status
*/
public static native int leave(long sock, long addr,
long iface, long source);
@@ -55,6 +57,7 @@ public class Multicast {
* <br><b>Remark :</b> If the TTL is 0, packets will only be seen
* by sockets on the local machine,
* and only when multicast loopback is enabled.
+ * @return the operation status
*/
public static native int hops(long sock, int ttl);
@@ -62,6 +65,7 @@ public class Multicast {
* Toggle IP Multicast Loopback
* @param sock The socket to set multicast loopback
* @param opt false=disable, true=enable
+ * @return the operation status
*/
public static native int loopback(long sock, boolean opt);
@@ -70,6 +74,7 @@ public class Multicast {
* Set the Interface to be used for outgoing Multicast Transmissions.
* @param sock The socket to set the multicast interface on
* @param iface Address of the interface to use for Multicast
+ * @return the operation status
*/
public static native int ointerface(long sock, long iface);
diff --git a/java/org/apache/tomcat/jni/OS.java b/java/org/apache/tomcat/jni/OS.java
index e7791af..4eba9b3 100644
--- a/java/org/apache/tomcat/jni/OS.java
+++ b/java/org/apache/tomcat/jni/OS.java
@@ -58,6 +58,7 @@ public class OS {
/**
* Get the name of the system default character set.
* @param pool the pool to allocate the name from, if needed
+ * @return the encoding
*/
public static native String defaultEncoding(long pool);
@@ -66,6 +67,7 @@ public class OS {
* Defers to apr_os_default_encoding if the current locale's
* data can't be retrieved on this system.
* @param pool the pool to allocate the name from, if needed
+ * @return the encoding
*/
public static native String localeEncoding(long pool);
@@ -73,6 +75,7 @@ public class OS {
* Generate random bytes.
* @param buf Buffer to fill with random bytes
* @param len Length of buffer in bytes
+ * @return the operation status
*/
public static native int random(byte [] buf, int len);
@@ -102,6 +105,7 @@ public class OS {
* </PRE>
* @param inf array that will be filled with system information.
* Array length must be at least 16.
+ * @return the operation status
*/
public static native int info(long [] inf);
diff --git a/java/org/apache/tomcat/jni/Poll.java b/java/org/apache/tomcat/jni/Poll.java
index 0fc8308..112d02e 100644
--- a/java/org/apache/tomcat/jni/Poll.java
+++ b/java/org/apache/tomcat/jni/Poll.java
@@ -66,12 +66,14 @@ public class Poll {
* @param flags Optional flags to modify the operation of the pollset.
* @param ttl Maximum time to live for a particular socket.
* @return The pointer in which to return the newly created object
+ * @throws Error Pollset creation failed
*/
public static native long create(int size, long p, int flags, long ttl)
throws Error;
/**
* Destroy a pollset object
* @param pollset The pollset to destroy
+ * @return the operation status
*/
public static native int destroy(long pollset);
@@ -80,6 +82,7 @@ public class Poll {
* @param pollset The pollset to which to add the socket
* @param sock The sockets to add
* @param reqevents requested events
+ * @return the operation status
*/
public static native int add(long pollset, long sock,
int reqevents);
@@ -90,6 +93,7 @@ public class Poll {
* @param sock The sockets to add
* @param reqevents requested events
* @param timeout requested timeout in microseconds (-1 for infinite)
+ * @return the operation status
*/
public static native int addWithTimeout(long pollset, long sock,
int reqevents, long timeout);
@@ -98,6 +102,7 @@ public class Poll {
* Remove a descriptor from a pollset
* @param pollset The pollset from which to remove the descriptor
* @param sock The socket to remove
+ * @return the operation status
*/
public static native int remove(long pollset, long sock);
@@ -163,4 +168,21 @@ public class Poll {
* or negative APR error code.
*/
public static native int pollset(long pollset, long [] descriptors);
+
+ /**
+ * Make poll() return.
+ *
+ * @param pollset The pollset to use
+ * @return Negative APR error code
+ */
+ public static native int interrupt(long pollset);
+
+ /**
+ * Check if interrupt() is allowed.
+ *
+ * @param pollset The pollset to use
+ * @return <code>true</code> if {@link #interrupt(long)} is allowed, else
+ * <code>false</code>
+ */
+ public static native boolean wakeable(long pollset);
}
diff --git a/java/org/apache/tomcat/jni/Pool.java b/java/org/apache/tomcat/jni/Pool.java
index 617b1b7..f08fa80 100644
--- a/java/org/apache/tomcat/jni/Pool.java
+++ b/java/org/apache/tomcat/jni/Pool.java
@@ -150,6 +150,7 @@ public class Pool {
* Return the data associated with the current pool.
* @param key The key for the data to retrieve
* @param pool The current pool.
+ * @return the data
*/
public static native Object dataGet(long pool, String key);
diff --git a/java/org/apache/tomcat/jni/Proc.java b/java/org/apache/tomcat/jni/Proc.java
index c4c3c43..cbae420 100644
--- a/java/org/apache/tomcat/jni/Proc.java
+++ b/java/org/apache/tomcat/jni/Proc.java
@@ -102,6 +102,7 @@ public class Proc {
* Allocate apr_proc_t structure from pool
* This is not an apr function.
* @param cont The pool to use.
+ * @return the pointer
*/
public static native long alloc(long cont);
@@ -119,6 +120,7 @@ public class Proc {
* Create a new process and execute a new program within that process.
* This function returns without waiting for the new process to terminate;
* use apr_proc_wait for that.
+ * @param proc The process handle
* @param progname The program to run
* @param args The arguments to pass to the new program. The first
* one should be the program name.
@@ -186,6 +188,7 @@ public class Proc {
* child is dead or not.
* </PRE>
* @param pool Pool to allocate child information out of.
+ * @return the operation status
*/
public static native int waitAllProcs(long proc, int [] exit,
int waithow, long pool);
@@ -195,6 +198,7 @@ public class Proc {
* @param daemonize set to non-zero if the process should daemonize
* and become a background process, else it will
* stay in the foreground.
+ * @return the operation status
*/
public static native int detach(int daemonize);
@@ -202,6 +206,7 @@ public class Proc {
* Terminate a process.
* @param proc The process to terminate.
* @param sig How to kill the process.
+ * @return the operation status
*/
public static native int kill(long proc, int sig);
diff --git a/java/org/apache/tomcat/jni/Procattr.java b/java/org/apache/tomcat/jni/Procattr.java
index 7f6869c..fe7ba61 100644
--- a/java/org/apache/tomcat/jni/Procattr.java
+++ b/java/org/apache/tomcat/jni/Procattr.java
@@ -27,6 +27,7 @@ public class Procattr {
* Create and initialize a new procattr variable
* @param cont The pool to use
* @return The newly created procattr.
+ * @throws Error An error occurred
*/
public static native long create(long cont)
throws Error;
@@ -38,8 +39,10 @@ public class Procattr {
* @param in Should stdin be a pipe back to the parent?
* @param out Should stdout be a pipe back to the parent?
* @param err Should stderr be a pipe back to the parent?
+ * @return the operation status
*/
public static native int ioSet(long attr, int in, int out, int err);
+
/**
* Set the child_in and/or parent_in values to existing apr_file_t values.
* <br>
@@ -52,6 +55,7 @@ public class Procattr {
* @param attr The procattr we care about.
* @param in apr_file_t value to use as child_in. Must be a valid file.
* @param parent apr_file_t value to use as parent_in. Must be a valid file.
+ * @return the operation status
*/
public static native int childInSet(long attr, long in, long parent);
@@ -65,6 +69,7 @@ public class Procattr {
* @param attr The procattr we care about.
* @param out apr_file_t value to use as child_out. Must be a valid file.
* @param parent apr_file_t value to use as parent_out. Must be a valid file.
+ * @return the operation status
*/
public static native int childOutSet(long attr, long out, long parent);
@@ -78,6 +83,7 @@ public class Procattr {
* @param attr The procattr we care about.
* @param err apr_file_t value to use as child_err. Must be a valid file.
* @param parent apr_file_t value to use as parent_err. Must be a valid file.
+ * @return the operation status
*/
public static native int childErrSet(long attr, long err, long parent);
@@ -87,6 +93,7 @@ public class Procattr {
* @param dir Which dir to start in. By default, this is the same dir as
* the parent currently resides in, when the createprocess call
* is made.
+ * @return the operation status
*/
public static native int dirSet(long attr, String dir);
@@ -100,6 +107,7 @@ public class Procattr {
* APR_PROGRAM_ENV -- Executable program, copy environment
* APR_PROGRAM_PATH -- Executable program on PATH, copy env
* </PRE>
+ * @return the operation status
*/
public static native int cmdtypeSet(long attr, int cmd);
@@ -107,6 +115,7 @@ public class Procattr {
* Determine if the child should start in detached state.
* @param attr The procattr we care about.
* @param detach Should the child start in detached state? Default is no.
+ * @return the operation status
*/
public static native int detachSet(long attr, int detach);
@@ -122,6 +131,7 @@ public class Procattr {
* fork() is used. This leads to extra overhead in the calling
* process, but that may help the application handle such
* errors more gracefully.
+ * @return the operation status
*/
public static native int errorCheckSet(long attr, int chk);
@@ -131,6 +141,7 @@ public class Procattr {
* @param attr The procattr we care about.
* @param addrspace Should the child start in its own address space? Default
* is no on NetWare and yes on other platforms.
+ * @return the operation status
*/
public static native int addrspaceSet(long attr, int addrspace);
@@ -156,6 +167,7 @@ public class Procattr {
* @param password User password if needed. Password is needed on WIN32
* or any other platform having
* APR_PROCATTR_USER_SET_REQUIRES_PASSWORD set.
+ * @return the operation status
*/
public static native int userSet(long attr, String username, String password);
@@ -163,8 +175,8 @@ public class Procattr {
* Set the group used for running process
* @param attr The procattr we care about.
* @param groupname The group name used
+ * @return the operation status
*/
public static native int groupSet(long attr, String groupname);
-
}
diff --git a/java/org/apache/tomcat/jni/Registry.java b/java/org/apache/tomcat/jni/Registry.java
index 9c493e7..e1f814c 100644
--- a/java/org/apache/tomcat/jni/Registry.java
+++ b/java/org/apache/tomcat/jni/Registry.java
@@ -57,6 +57,7 @@ public class Registry {
* @param sam Access mask that specifies the access rights for the key.
* @param pool Pool used for native memory allocation
* @return Opened Registry key
+ * @throws Error An error occurred
*/
public static native long create(int root, String name, int sam, long pool)
throws Error;
@@ -68,6 +69,7 @@ public class Registry {
* @param sam Access mask that specifies the access rights for the key.
* @param pool Pool used for native memory allocation
* @return Opened Registry key
+ * @throws Error An error occurred
*/
public static native long open(int root, String name, int sam, long pool)
throws Error;
@@ -75,6 +77,7 @@ public class Registry {
/**
* Close the specified Registry key.
* @param key The Registry key descriptor to close.
+ * @return the operation status
*/
public static native int close(long key);
@@ -91,6 +94,7 @@ public class Registry {
* @param key The Registry key descriptor to use.
* @param name The name of the value to query
* @return Registry key value
+ * @throws Error An error occurred
*/
public static native int getValueI(long key, String name)
throws Error;
@@ -100,6 +104,7 @@ public class Registry {
* @param key The Registry key descriptor to use.
* @param name The name of the value to query
* @return Registry key value
+ * @throws Error An error occurred
*/
public static native long getValueJ(long key, String name)
throws Error;
@@ -117,6 +122,7 @@ public class Registry {
* @param key The Registry key descriptor to use.
* @param name The name of the value to query
* @return Registry key value
+ * @throws Error An error occurred
*/
public static native String getValueS(long key, String name)
throws Error;
@@ -126,6 +132,7 @@ public class Registry {
* @param key The Registry key descriptor to use.
* @param name The name of the value to query
* @return Registry key value
+ * @throws Error An error occurred
*/
public static native String[] getValueA(long key, String name)
throws Error;
@@ -135,6 +142,7 @@ public class Registry {
* @param key The Registry key descriptor to use.
* @param name The name of the value to query
* @return Registry key value
+ * @throws Error An error occurred
*/
public static native byte[] getValueB(long key, String name)
throws Error;
@@ -198,6 +206,7 @@ public class Registry {
* Enumerate the Registry subkeys
* @param key The Registry key descriptor to use.
* @return Array of all subkey names
+ * @throws Error An error occurred
*/
public static native String[] enumKeys(long key)
throws Error;
@@ -206,6 +215,7 @@ public class Registry {
* Enumerate the Registry values
* @param key The Registry key descriptor to use.
* @return Array of all value names
+ * @throws Error An error occurred
*/
public static native String[] enumValues(long key)
throws Error;
diff --git a/java/org/apache/tomcat/jni/SSL.java b/java/org/apache/tomcat/jni/SSL.java
index da27cf3..da588b8 100644
--- a/java/org/apache/tomcat/jni/SSL.java
+++ b/java/org/apache/tomcat/jni/SSL.java
@@ -36,7 +36,8 @@ public final class SSL {
public static final int SSL_AIDX_RSA = 0;
public static final int SSL_AIDX_DSA = 1;
- public static final int SSL_AIDX_MAX = 2;
+ 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
*/
@@ -226,6 +227,14 @@ public final class SSL {
* Add certificate chain number to that flag (0 ... verify depth)
*/
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;
+ public static final long SSL_SESS_CACHE_SERVER = 0x0002;
+
+ public static final int SSL_SELECTOR_FAILURE_NO_ADVERTISE = 0;
+ public static final int SSL_SELECTOR_FAILURE_CHOOSE_MY_LAST_PROTOCOL = 1;
+
/* Return OpenSSL version number (compile time version, if version < 1.1.0) */
public static native int version();
@@ -273,6 +282,7 @@ public final class SSL {
* set, $HOME/.rnd otherwise.
* In case both files are unavailable builtin
* random seed generator is used.
+ * @return <code>true</code> if the operation was successful
*/
public static native boolean randLoad(String filename);
@@ -281,6 +291,7 @@ public final class SSL {
* file <code>filename</code> which can be used to initialize the PRNG
* by calling randLoad in a later session.
* @param filename Filename to save the data
+ * @return <code>true</code> if the operation was successful
*/
public static native boolean randSave(String filename);
@@ -289,6 +300,7 @@ public final class SSL {
* @param filename Filename to save the data
* @param len The length of random sequence in bytes
* @param base64 Output the data in Base64 encoded format
+ * @return <code>true</code> if the operation was successful
*/
public static native boolean randMake(String filename, int len,
boolean base64);
@@ -306,6 +318,7 @@ public final class SSL {
* @param pool The pool to use.
* @param callback BIOCallback to use
* @return New BIO handle
+ * @throws Exception An error occurred
*/
public static native long newBIO(long pool, BIOCallback callback)
throws Exception;
@@ -330,46 +343,8 @@ public final class SSL {
public static native void setPassword(String password);
/**
- * Generate temporary RSA key.
- * <br>
- * Index can be one of:
- * <PRE>
- * SSL_TMP_KEY_RSA_512
- * SSL_TMP_KEY_RSA_1024
- * SSL_TMP_KEY_RSA_2048
- * SSL_TMP_KEY_RSA_4096
- * </PRE>
- * By default 512 and 1024 keys are generated on startup.
- * You can use a low priority thread to generate them on the fly.
- * @param idx temporary key index.
- */
- /**
- * @deprecated Only useful in combination with EXPORT Cipher
- */
- @Deprecated
- public static native boolean generateRSATempKey(int idx);
-
- /**
- * Load temporary DSA key from file
- * <br>
- * Index can be one of:
- * <PRE>
- * SSL_TMP_KEY_DH_512
- * SSL_TMP_KEY_DH_1024
- * SSL_TMP_KEY_DH_2048
- * SSL_TMP_KEY_DH_4096
- * </PRE>
- * @param idx temporary key index.
- * @param file File containing DH params.
- */
- /**
- * @deprecated Now automatically loaded from certificate file
- */
- @Deprecated
- public static native boolean loadDSATempKey(int idx, String file);
-
- /**
* Return last SSL error string
+ * @return the error string
*/
public static native String getLastError();
@@ -386,4 +361,321 @@ public final class SSL {
* @return true if all SSL_OP_* are supported by OpenSSL library.
*/
public static native boolean hasOp(int op);
+
+ /**
+ * Return the handshake completed count.
+ * @param ssl SSL pointer
+ * @return the count
+ */
+ public static native int getHandshakeCount(long ssl);
+
+ /*
+ * Begin Twitter API additions
+ */
+
+ 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_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;
+
+ /**
+ * 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
+ * @return pointer to SSL instance (SSL *)
+ */
+ public static native long newSSL(long ctx, boolean server);
+
+ /**
+ * SSL_set_bio
+ * @param ssl SSL pointer (SSL *)
+ * @param rbio read BIO pointer (BIO *)
+ * @param wbio write BIO pointer (BIO *)
+ */
+ public static native void setBIO(long ssl, long rbio, long wbio);
+
+ /**
+ * SSL_get_error
+ * @param ssl SSL pointer (SSL *)
+ * @param ret TLS/SSL I/O return value
+ * @return the error status
+ */
+ public static native int getError(long ssl, int ret);
+
+ /**
+ * 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 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 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 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 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_set_shutdown
+ * @param ssl the SSL instance (SSL *)
+ * @param mode Shutdown mode
+ */
+ public static native void setShutdown(long ssl, int mode);
+
+ /**
+ * 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.
+ *
+ * <b>Warning: you must explicitly free this resource by calling freeBIO</b>
+ *
+ * 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.
+ * @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_in_init.
+ * @param ssl the SSL instance (SSL *)
+ * @return the status
+ */
+ public static native int isInInit(long ssl);
+
+ /**
+ * SSL_get0_next_proto_negotiated
+ * @param ssl the SSL instance (SSL *)
+ * @return the NPN protocol negotiated
+ */
+ public static native String getNextProtoNegotiated(long ssl);
+
+ /*
+ * End Twitter API Additions
+ */
+
+ /**
+ * 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.
+ * @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.
+ * @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.
+ * <br>
+ * 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 a SSL renegotiation with
+ * the reconfigured client verification level after the HTTP request was read
+ * but before the HTTP response is sent.
+ * <br>
+ * The following levels are available for level:
+ * <pre>
+ * SSL_CVERIFY_NONE - No client Certificate is required at all
+ * SSL_CVERIFY_OPTIONAL - The client may present a valid Certificate
+ * SSL_CVERIFY_REQUIRE - The client has to present a valid Certificate
+ * SSL_CVERIFY_OPTIONAL_NO_CA - The client may present a valid Certificate
+ * but it need not to be (successfully) verifiable
+ * </pre>
+ * <br>
+ * 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 level Type of 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.
+ */
+ 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.
+ */
+ public static native int getOptions(long ssl);
+
+ /**
+ * Returns all Returns the cipher suites that are available 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.
+ * <br>
+ * 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 a 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
+ * @return <code>true</code> if the operation was successful
+ * @throws Exception An error occurred
+ */
+ public static native boolean setCipherSuites(long ssl, String ciphers)
+ 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 --git a/java/org/apache/tomcat/jni/SSLContext.java b/java/org/apache/tomcat/jni/SSLContext.java
index 7860e2f..50a2908 100644
--- a/java/org/apache/tomcat/jni/SSLContext.java
+++ b/java/org/apache/tomcat/jni/SSLContext.java
@@ -17,15 +17,18 @@
package org.apache.tomcat.jni;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
/** SSL Context
*
* @author Mladen Turk
*/
public final class SSLContext {
-
/**
- * Initialize new SSL context
+ * 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:
@@ -43,9 +46,13 @@ public final class SSLContext {
* SSL_MODE_SERVER
* SSL_MODE_COMBINED
* </PRE>
+ *
+ * @return The Java representation of a pointer to the newly created SSL
+ * Context
+ *
+ * @throws Exception If the SSL Context could not be created
*/
- public static native long make(long pool, int protocol, int mode)
- throws Exception;
+ public static native long make(long pool, int protocol, int mode) throws Exception;
/**
* Free the resources used by the Context
@@ -90,6 +97,13 @@ public final class SSLContext {
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.
+ */
+ 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.
@@ -129,7 +143,9 @@ public final class SSLContext {
* 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 SSL cipher specification.
+ * @param ciphers An OpenSSL cipher specification.
+ * @return <code>true</code> if the operation was successful
+ * @throws Exception An error occurred
*/
public static native boolean setCipherSuite(long ctx, String ciphers)
throws Exception;
@@ -152,6 +168,8 @@ public final class SSLContext {
* @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 <code>true</code> if the operation was successful
+ * @throws Exception An error occurred
*/
public static native boolean setCARevocation(long ctx, String file,
String path)
@@ -176,6 +194,7 @@ public final class SSLContext {
* @param file File of PEM-encoded Server CA Certificates.
* @param skipfirst Skip first certificate if chain file is inside
* certificate file.
+ * @return <code>true</code> if the operation was successful
*/
public static native boolean setCertificateChainFile(long ctx, String file,
boolean skipfirst);
@@ -201,6 +220,8 @@ public final class SSLContext {
* @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 <code>true</code> if the operation was successful
+ * @throws Exception An error occurred
*/
public static native boolean setCertificate(long ctx, String cert,
String key, String password,
@@ -208,6 +229,79 @@ public final class SSLContext {
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.
+ * @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
+ * @param ctx Server or Client context to use.
+ * @return the size
+ */
+ public static native long getSessionCacheSize(long ctx);
+
+ /**
+ * 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 timeoutSeconds Timeout value
+ * @return the value set
+ */
+ public static native long setSessionCacheTimeout(long ctx, long timeoutSeconds);
+
+ /**
+ * 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 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
+ */
+ 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 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
* <br>
@@ -227,6 +321,8 @@ public final class SSLContext {
* @param file File of concatenated PEM-encoded CA Certificates for
* Client Auth.
* @param path Directory of PEM-encoded CA Certificates for Client Auth.
+ * @return <code>true</code> if the operation was successful
+ * @throws Exception An error occurred
*/
public static native boolean setCACertificate(long ctx, String file,
String path)
@@ -288,4 +384,182 @@ public final class SSLContext {
*/
public static native void setVerify(long ctx, int level, int depth);
+ public static native int setALPN(long ctx, byte[] proto, int len);
+
+ /**
+ * 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));
+ if (sniCallBack == null) {
+ return 0;
+ }
+ return sniCallBack.getSslContext(sniHostName);
+ }
+
+ /**
+ * 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<Long,SNICallBack> sniCallBacks = new ConcurrentHashMap<>();
+
+ /**
+ * Register an OpenSSL SSLContext that will be used to initiate TLS
+ * connections that may use the SNI extension with the component that will
+ * be used to map the requested hostname to the correct OpenSSL SSLContext
+ * for the remainder of the connection.
+ *
+ * @param defaultSSLContext The Java representation of a pointer to the
+ * OpenSSL SSLContext that will be used to
+ * initiate TLS connections
+ * @param sniCallBack The component that will map SNI hosts names received
+ * via connections initiated using
+ * <code>defaultSSLContext</code> to the correct OpenSSL
+ * SSLContext
+ */
+ public static void registerDefault(Long defaultSSLContext,
+ SNICallBack sniCallBack) {
+ sniCallBacks.put(defaultSSLContext, sniCallBack);
+ }
+
+ /**
+ * Unregister an OpenSSL SSLContext that will no longer be used to initiate
+ * TLS connections that may use the SNI extension.
+ *
+ * @param defaultSSLContext The Java representation of a pointer to the
+ * OpenSSL SSLContext that will no longer be used
+ */
+ public static void unregisterDefault(Long defaultSSLContext) {
+ sniCallBacks.remove(defaultSSLContext);
+ }
+
+
+ /**
+ * 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 static interface SNICallBack {
+
+ /**
+ * 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
+ *
+ * @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 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.
+ * @param verifier the verifier to call during handshake.
+ */
+ public static native void setCertVerifyCallback(long ctx, CertificateVerifier verifier);
+
+ /**
+ * Set next protocol for next protocol negotiation extension
+ * @param ctx Server context to use.
+ * @param nextProtos comma delimited list of protocols in priority order
+ *
+ * @deprecated use {@link #setNpnProtos(long, String[], int)}
+ */
+ @Deprecated
+ public static void setNextProtos(long ctx, String nextProtos) {
+ setNpnProtos(ctx, nextProtos.split(","), SSL.SSL_SELECTOR_FAILURE_CHOOSE_MY_LAST_PROTOCOL);
+ }
+
+ /**
+ * Set next protocol for next protocol negotiation extension
+ * @param ctx Server context to use.
+ * @param nextProtos 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 setNpnProtos(long ctx, String[] nextProtos, int selectorFailureBehavior);
+
+ /**
+ * 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}
+ */
+ public static native void setAlpnProtos(long ctx, String[] alpnProtos, int selectorFailureBehavior);
+
+ /**
+ * Set DH parameters
+ * @param ctx Server context to use.
+ * @param cert DH param file (can be generated from e.g. {@code openssl dhparam -rand - 2048 > dhparam.pem} -
+ * see the <a href="https://www.openssl.org/docs/apps/dhparam.html">OpenSSL documentation</a>).
+ * @throws Exception An error occurred
+ */
+ public static native void setTmpDH(long ctx, String cert)
+ throws Exception;
+
+ /**
+ * Set ECDH elliptic curve by name
+ * @param ctx Server context to use.
+ * @param curveName the name of the elliptic curve to use
+ * (available names can be obtained from {@code openssl ecparam -list_curves}).
+ * @throws Exception An error occurred
+ */
+ public static native void setTmpECDHByCurveName(long ctx, String curveName)
+ throws Exception;
+
+ /**
+ * 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
+ * @return {@code true} if success, {@code false} otherwise.
+ */
+ public static native boolean setSessionIdContext(long ctx, byte[] sidCtx);
+
+ /**
+ * Set CertificateRaw
+ * <br>
+ * 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 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.
+ *
+ * <br>
+ * Use keystore a certificate chain to fill the BIOP
+ * @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);
}
diff --git a/java/org/apache/tomcat/jni/SSLSocket.java b/java/org/apache/tomcat/jni/SSLSocket.java
index 82a4b98..c8108a0 100644
--- a/java/org/apache/tomcat/jni/SSLSocket.java
+++ b/java/org/apache/tomcat/jni/SSLSocket.java
@@ -28,6 +28,7 @@ public class SSLSocket {
* @param ctx SSLContext to use.
* @param sock APR Socket that already did physical connect or accept.
* @return APR_STATUS code.
+ * @throws Exception An error occurred
*/
public static native int attach(long ctx, long sock)
throws Exception;
@@ -35,6 +36,7 @@ public class SSLSocket {
/**
* Do a SSL handshake.
* @param thesocket The socket to use
+ * @return the handshake status
*/
public static native int handshake(long thesocket);
@@ -51,6 +53,7 @@ public class SSLSocket {
* only), so Apache has no API hook for this step.
*
* @param thesocket The socket to use
+ * @return the operation status
*/
public static native int renegotiate(long thesocket);
@@ -74,6 +77,9 @@ public class SSLSocket {
* <br>
* @param sock The socket to change.
* @param level Type of Client Certificate verification.
+ * @param depth Maximum number of certificates to permit in chain from
+ * client to trusted CA. Use a value of 0 or less to leave the
+ * current value unchanged
*/
public static native void setVerify(long sock, int level, int depth);
@@ -83,6 +89,7 @@ public class SSLSocket {
* @param sock The socket to read the data from.
* @param id Parameter id.
* @return Byte array containing info id value.
+ * @throws Exception An error occurred
*/
public static native byte[] getInfoB(long sock, int id)
throws Exception;
@@ -93,6 +100,7 @@ public class SSLSocket {
* @param sock The socket to read the data from.
* @param id Parameter id.
* @return String containing info id value.
+ * @throws Exception An error occurred
*/
public static native String getInfoS(long sock, int id)
throws Exception;
@@ -103,8 +111,21 @@ public class SSLSocket {
* @param sock The socket to read the data from.
* @param id Parameter id.
* @return Integer containing info id value or -1 on error.
+ * @throws Exception An error occurred
*/
public static native int getInfoI(long sock, int id)
throws Exception;
+
+ /**
+ * Obtain the name of the protocol negotiated via ALPN. Only valid after the
+ * TLS handshake has completed.
+ *
+ * @param sock Socket
+ * @param negotiatedProtocol Byte array in which to store agreed protocol
+ *
+ * @return Length of agreed protocol. Zero means no protocol agreed.
+ */
+ public static native int getALPN(long sock, byte[] negotiatedProtocol);
+
}
diff --git a/java/org/apache/tomcat/jni/Shm.java b/java/org/apache/tomcat/jni/Shm.java
index 6ccf9cf..43c662c 100644
--- a/java/org/apache/tomcat/jni/Shm.java
+++ b/java/org/apache/tomcat/jni/Shm.java
@@ -48,7 +48,7 @@ public class Shm {
* @param pool the pool from which to allocate the shared memory
* structure.
* @return The created shared memory structure.
- *
+ * @throws Error An error occurred
*/
public static native long create(long reqsize, String filename, long pool)
throws Error;
@@ -62,12 +62,14 @@ public class Shm {
* @param filename The filename associated with shared-memory segment which
* needs to be removed
* @param pool The pool used for file operations
+ * @return the operation status
*/
public static native int remove(String filename, long pool);
/**
* Destroy a shared memory segment and associated memory.
* @param m The shared memory segment structure to destroy.
+ * @return the operation status
*/
public static native int destroy(long m);
@@ -79,6 +81,7 @@ public class Shm {
* @param pool the pool from which to allocate the shared memory
* structure for this process.
* @return The created shared memory structure.
+ * @throws Error An error occurred
*/
public static native long attach(String filename, long pool)
throws Error;
@@ -87,6 +90,7 @@ public class Shm {
* Detach from a shared memory segment without destroying it.
* @param m The shared memory structure representing the segment
* to detach from.
+ * @return the operation status
*/
public static native int detach(long m);
@@ -105,6 +109,7 @@ public class Shm {
* Retrieve the length of a shared memory segment in bytes.
* @param m The shared memory segment from which to retrieve
* the segment length.
+ * @return the length of the segment
*/
public static native long size(long m);
diff --git a/java/org/apache/tomcat/jni/Socket.java b/java/org/apache/tomcat/jni/Socket.java
index 3a5bcbf..976dd38 100644
--- a/java/org/apache/tomcat/jni/Socket.java
+++ b/java/org/apache/tomcat/jni/Socket.java
@@ -76,11 +76,6 @@ public class Socket {
public static final int APR_IPV4_ADDR_OK = 0x01;
public static final int APR_IPV6_ADDR_OK = 0x02;
- /* TODO: Missing:
- * APR_INET
- * APR_UNSPEC
- * APR_INET6
- */
public static final int APR_UNSPEC = 0;
public static final int APR_INET = 1;
public static final int APR_INET6 = 2;
@@ -109,6 +104,7 @@ public class Socket {
* @param protocol The protocol of the socket (e.g., APR_PROTO_TCP).
* @param cont The parent pool to use
* @return The new socket that has been set up.
+ * @throws Exception Error creating socket
*/
public static native long create(int family, int type,
int protocol, long cont)
@@ -127,12 +123,14 @@ public class Socket {
* APR_SHUTDOWN_WRITE no longer allow write requests
* APR_SHUTDOWN_READWRITE no longer allow read or write requests
* </PRE>
+ * @return the operation status
*/
public static native int shutdown(long thesocket, int how);
/**
* Close a socket.
* @param thesocket The socket to close
+ * @return the operation status
*/
public static native int close(long thesocket);
@@ -148,6 +146,7 @@ public class Socket {
* @param sa The socket address to bind to
* This may be where we will find out if there is any other process
* using the selected port.
+ * @return the operation status
*/
public static native int bind(long sock, long sa);
@@ -157,6 +156,7 @@ public class Socket {
* @param backlog The number of outstanding connections allowed in the sockets
* listen queue. If this value is less than zero, the listen
* queue size is set to zero.
+ * @return the operation status
*/
public static native int listen(long sock, int backlog);
@@ -167,6 +167,7 @@ public class Socket {
* @return A copy of the socket that is connected to the socket that
* made the connection request. This is the socket which should
* be used for all future communication.
+ * @throws Exception Socket accept error
*/
public static native long acceptx(long sock, long pool)
throws Exception;
@@ -177,6 +178,7 @@ public class Socket {
* @return A copy of the socket that is connected to the socket that
* made the connection request. This is the socket which should
* be used for all future communication.
+ * @throws Exception Socket accept error
*/
public static native long accept(long sock)
throws Exception;
@@ -187,14 +189,15 @@ public class Socket {
* @param name The accept filter
* @param args Any extra args to the accept filter. Passing NULL here removes
* the accept filter.
+ * @return the operation status
*/
public static native int acceptfilter(long sock, String name, String args);
/**
* Query the specified socket if at the OOB/Urgent data mark
* @param sock The socket to query
- * @return True if socket is at the OOB/urgent mark,
- * otherwise return false.
+ * @return <code>true</code> if socket is at the OOB/urgent mark,
+ * otherwise <code>false</code>.
*/
public static native boolean atmark(long sock);
@@ -203,6 +206,7 @@ public class Socket {
* or a different one.
* @param sock The socket we wish to use for our side of the connection
* @param sa The address of the machine we wish to connect to.
+ * @return the operation status
*/
public static native int connect(long sock, long sa);
@@ -221,8 +225,7 @@ public class Socket {
* @param buf The buffer which contains the data to be sent.
* @param offset Offset in the byte buffer.
* @param len The number of bytes to write; (-1) for full array.
- * @return The number of bytes send.
- *
+ * @return The number of bytes sent
*/
public static native int send(long sock, byte[] buf, int offset, int len);
@@ -244,8 +247,7 @@ public class Socket {
* and no larger than buf.length
* @param len The maximum number of buffers to be accessed; must be non-negative
* and no larger than buf.length - offset
- * @return The number of bytes send.
- *
+ * @return The number of bytes sent
*/
public static native int sendb(long sock, ByteBuffer buf,
int offset, int len);
@@ -267,14 +269,20 @@ public class Socket {
* and no larger than buf.length
* @param len The maximum number of buffers to be accessed; must be non-negative
* and no larger than buf.length - offset
- * @return The number of bytes send.
- *
+ * @return The number of bytes sent
*/
public static native int sendib(long sock, ByteBuffer buf,
int offset, int len);
/**
* Send data over a network using internally set ByteBuffer
+ * @param sock The socket to send the data over.
+ * @param offset The offset within the buffer array of the first buffer from
+ * which bytes are to be retrieved; must be non-negative
+ * and no larger than buf.length
+ * @param len The maximum number of buffers to be accessed; must be non-negative
+ * and no larger than buf.length - offset
+ * @return The number of bytes sent
*/
public static native int sendbb(long sock,
int offset, int len);
@@ -282,6 +290,13 @@ public class Socket {
/**
* Send data over a network using internally set ByteBuffer
* without internal retry.
+ * @param sock The socket to send the data over.
+ * @param offset The offset within the buffer array of the first buffer from
+ * which bytes are to be retrieved; must be non-negative
+ * and no larger than buf.length
+ * @param len The maximum number of buffers to be accessed; must be non-negative
+ * and no larger than buf.length - offset
+ * @return The number of bytes sent
*/
public static native int sendibb(long sock,
int offset, int len);
@@ -300,7 +315,7 @@ public class Socket {
* </PRE>
* @param sock The socket to send the data over.
* @param vec The array from which to get the data to send.
- *
+ * @return The number of bytes sent
*/
public static native int sendv(long sock, byte[][] vec);
@@ -311,6 +326,7 @@ public class Socket {
* @param buf The data to send
* @param offset Offset in the byte buffer.
* @param len The length of the data to send
+ * @return The number of bytes sent
*/
public static native int sendto(long sock, long where, int flags,
byte[] buf, int offset, int len);
@@ -385,9 +401,13 @@ public class Socket {
*/
public static native int recvb(long sock, ByteBuffer buf,
int offset, int nbytes);
+
/**
* Read data from a network using internally set ByteBuffer.
*
+ * @param sock The socket to read the data from.
+ * @param offset Offset in the byte buffer.
+ * @param nbytes The number of bytes to read (-1) for full array.
* @return If > 0, the return value is the number of bytes read. If == 0,
* the return value indicates EOF and if < 0 the return value is the
* error code. Note a non-blocking read with no data current
@@ -420,6 +440,11 @@ public class Socket {
int offset, int nbytes, long timeout);
/**
* Read data from a network with timeout using internally set ByteBuffer
+ * @param sock The socket to read the data from.
+ * @param offset Offset in the byte buffer.
+ * @param nbytes The number of bytes to read (-1) for full array.
+ * @param timeout The socket timeout in microseconds.
+ * @return the number of bytes received.
*/
public static native int recvbbt(long sock,
int offset, int nbytes, long timeout);
@@ -457,6 +482,7 @@ public class Socket {
* APR_SO_RCVBUF -- Set the ReceiveBufferSize
* </PRE>
* @param on Value for the option.
+ * @return the operation status
*/
public static native int optSet(long sock, int opt, int on);
@@ -478,6 +504,7 @@ public class Socket {
* (Currently only used on Windows)
* </PRE>
* @return Socket option returned on the call.
+ * @throws Exception An error occurred
*/
public static native int optGet(long sock, int opt)
throws Exception;
@@ -492,6 +519,7 @@ public class Socket {
* t == 0 -- read and write calls never block
* t < 0 -- read and write calls block
* </PRE>
+ * @return the operation status
*/
public static native int timeoutSet(long sock, long t);
@@ -499,6 +527,7 @@ public class Socket {
* Query socket timeout for the specified socket
* @param sock The socket to query
* @return Socket timeout returned from the query.
+ * @throws Exception An error occurred
*/
public static native long timeoutGet(long sock)
throws Exception;
@@ -522,7 +551,6 @@ public class Socket {
* @param flags APR flags that are mapped to OS specific flags
* @return Number of bytes actually sent, including headers,
* file, and trailers
- *
*/
public static native long sendfile(long sock, long file, byte [][] headers,
byte[][] trailers, long offset,
@@ -530,6 +558,12 @@ public class Socket {
/**
* Send a file without header and trailer arrays.
+ * @param sock The socket to which we're writing
+ * @param file The open file from which to read
+ * @param offset Offset into the file where we should begin writing
+ * @param len Number of bytes to send from the file
+ * @param flags APR flags that are mapped to OS specific flags
+ * @return Number of bytes actually sent
*/
public static native long sendfilen(long sock, long file, long offset,
long len, int flags);
@@ -537,6 +571,8 @@ public class Socket {
/**
* Create a child pool from associated socket pool.
* @param thesocket The socket to use
+ * @return a pointer to the pool
+ * @throws Exception An error occurred
*/
public static native long pool(long thesocket)
throws Exception;
@@ -578,14 +614,16 @@ public class Socket {
* @param sock The currently open socket.
* @param data The user data to associate with the socket.
* @param key The key to associate with the data.
+ * @return the operation status
*/
public static native int dataSet(long sock, String key, Object data);
/**
* Return the data associated with the current socket
- * @param key The key to associate with the user data.
* @param sock The currently open socket.
+ * @param key The key to associate with the user data.
* @return Data or null in case of error.
*/
public static native Object dataGet(long sock, String key);
+
}
diff --git a/java/org/apache/tomcat/jni/Status.java b/java/org/apache/tomcat/jni/Status.java
index bb79797..53b5dd4 100644
--- a/java/org/apache/tomcat/jni/Status.java
+++ b/java/org/apache/tomcat/jni/Status.java
@@ -181,7 +181,7 @@ public class Status {
public static final int ETIMEDOUT = (APR_OS_START_USERERR + 5);
private static native boolean is(int err, int idx);
- /**
+ /*
* APR_STATUS_IS Status Value Tests
* <br><b>Warning :</b> For any particular error condition, more than one of these tests
* may match. This is because platform-specific error codes may not
diff --git a/java/org/apache/tomcat/jni/Stdlib.java b/java/org/apache/tomcat/jni/Stdlib.java
index 532a53e..b2fa630 100644
--- a/java/org/apache/tomcat/jni/Stdlib.java
+++ b/java/org/apache/tomcat/jni/Stdlib.java
@@ -28,6 +28,7 @@ public class Stdlib {
* @param dst Destination byte array
* @param src Source memory address
* @param sz Number of bytes to copy.
+ * @return <code>true</code> if the operation was successful
*/
public static native boolean memread(byte [] dst, long src, int sz);
@@ -36,6 +37,7 @@ public class Stdlib {
* @param dst Destination memory address
* @param src Source byte array
* @param sz Number of bytes to copy.
+ * @return <code>true</code> if the operation was successful
*/
public static native boolean memwrite(long dst, byte [] src, int sz);
@@ -44,12 +46,14 @@ public class Stdlib {
* @param dst Destination memory address
* @param c Character to set.
* @param sz Number of characters.
+ * @return <code>true</code> if the operation was successful
*/
public static native boolean memset(long dst, int c, int sz);
/**
* Allocates memory blocks.
* @param sz Bytes to allocate.
+ * @return a pointer
*/
public static native long malloc(int sz);
@@ -57,6 +61,7 @@ public class Stdlib {
* Reallocate memory blocks.
* @param mem Pointer to previously allocated memory block.
* @param sz New size in bytes.
+ * @return a pointer
*/
public static native long realloc(long mem, int sz);
@@ -64,6 +69,7 @@ public class Stdlib {
* Allocates an array in memory with elements initialized to 0.
* @param num Number of elements.
* @param sz Length in bytes of each element.
+ * @return a pointer
*/
public static native long calloc(int num, int sz);
diff --git a/java/org/apache/tomcat/jni/Thread.java b/java/org/apache/tomcat/jni/Thread.java
index e7fc920..7435bf3 100644
--- a/java/org/apache/tomcat/jni/Thread.java
+++ b/java/org/apache/tomcat/jni/Thread.java
@@ -24,7 +24,7 @@ package org.apache.tomcat.jni;
public class Thread {
/**
- * Get the current thread ID handle.
+ * @return the current thread ID handle.
*/
public static native long current();
diff --git a/java/org/apache/tomcat/jni/Time.java b/java/org/apache/tomcat/jni/Time.java
index 02e6d49..22aeb25 100644
--- a/java/org/apache/tomcat/jni/Time.java
+++ b/java/org/apache/tomcat/jni/Time.java
@@ -28,13 +28,19 @@ public class Time {
/** number of milliseconds per microsecond */
public static final long APR_MSEC_PER_USEC = 1000L;
- /** @return apr_time_t as a second */
+ /**
+ * @param t The time
+ * @return apr_time_t as a second
+ */
public static long sec(long t)
{
return t / APR_USEC_PER_SEC;
}
- /** @return apr_time_t as a msec */
+ /**
+ * @param t The time
+ * @return apr_time_t as a msec
+ */
public static long msec(long t)
{
return t / APR_MSEC_PER_USEC;
@@ -50,6 +56,7 @@ public class Time {
* Formats dates in the RFC822
* format in an efficient manner.
* @param t the time to convert
+ * @return the formatted date
*/
public static native String rfc822(long t);
@@ -59,6 +66,7 @@ public class Time {
* Unlike ANSI/ISO C ctime(), apr_ctime() does not include
* a \n at the end of the string.
* @param t the time to convert
+ * @return the formatted date
*/
public static native String ctime(long t);
diff --git a/java/org/apache/tomcat/jni/User.java b/java/org/apache/tomcat/jni/User.java
index 0af7283..20bb762 100644
--- a/java/org/apache/tomcat/jni/User.java
+++ b/java/org/apache/tomcat/jni/User.java
@@ -28,6 +28,7 @@ public class User {
* This function is available only if APR_HAS_USER is defined.
* @param p The pool from which to allocate working space
* @return Returns the user id
+ * @throws Error If an error occurred
*/
public static native long uidCurrent(long p)
throws Error;
@@ -37,6 +38,7 @@ public class User {
* This function is available only if APR_HAS_USER is defined.
* @param p The pool from which to allocate working space
* @return Returns the group id
+ * @throws Error If an error occurred
*/
public static native long gidCurrent(long p)
throws Error;
@@ -48,6 +50,7 @@ public class User {
* @param username The username to lookup
* @param p The pool from which to allocate working space
* @return Returns the user id
+ * @throws Error If an error occurred
*/
public static native long uid(String username, long p)
throws Error;
@@ -58,6 +61,7 @@ public class User {
* @param username The username to lookup
* @param p The pool from which to allocate working space
* @return Returns the user's group id
+ * @throws Error If an error occurred
*/
public static native long usergid(String username, long p)
throws Error;
@@ -68,6 +72,7 @@ public class User {
* @param groupname The group name to look up
* @param p The pool from which to allocate working space
* @return Returns the user's group id
+ * @throws Error If an error occurred
*/
public static native long gid(String groupname, long p)
throws Error;
@@ -78,6 +83,7 @@ public class User {
* @param userid The userid
* @param p The pool from which to allocate the string
* @return New string containing user name
+ * @throws Error If an error occurred
*/
public static native String username(long userid, long p)
throws Error;
@@ -88,6 +94,7 @@ public class User {
* @param groupid The groupid
* @param p The pool from which to allocate the string
* @return New string containing group name
+ * @throws Error If an error occurred
*/
public static native String groupname(long groupid, long p)
throws Error;
@@ -118,6 +125,7 @@ public class User {
* @param username The named user
* @param p The pool from which to allocate the string
* @return New string containing directory name
+ * @throws Error If an error occurred
*/
public static native String homepath(String username, long p)
throws Error;
diff --git a/java/org/apache/tomcat/util/Diagnostics.java b/java/org/apache/tomcat/util/Diagnostics.java
index 8d61e22..74b3479 100644
--- a/java/org/apache/tomcat/util/Diagnostics.java
+++ b/java/org/apache/tomcat/util/Diagnostics.java
@@ -60,6 +60,8 @@ import java.util.Map;
import java.util.logging.LogManager;
import java.util.logging.LoggingMXBean;
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.res.StringManager;
public class Diagnostics {
@@ -73,8 +75,7 @@ public class Diagnostics {
private static final String CRLF = "\r\n";
private static final String vminfoSystemProperty = "java.vm.info";
- private static final org.apache.juli.logging.Log log=
- org.apache.juli.logging.LogFactory.getLog(Diagnostics.class);
+ private static final Log log = LogFactory.getLog(Diagnostics.class);
private static final SimpleDateFormat timeformat =
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
diff --git a/java/org/apache/tomcat/util/IntrospectionUtils.java b/java/org/apache/tomcat/util/IntrospectionUtils.java
index 58d7b3d..aa96446 100644
--- a/java/org/apache/tomcat/util/IntrospectionUtils.java
+++ b/java/org/apache/tomcat/util/IntrospectionUtils.java
@@ -23,19 +23,25 @@ import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Hashtable;
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
+
/**
* Utils for introspection and reflection
*/
public final class IntrospectionUtils {
- private static final org.apache.juli.logging.Log log=
- org.apache.juli.logging.LogFactory.getLog( IntrospectionUtils.class );
+ private static final Log log = LogFactory.getLog(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
+ * @param value The property value
+ * @return <code>true</code> if operation was successful
*/
public static boolean setProperty(Object o, String name, String value) {
return setProperty(o,name,value,true);
@@ -226,7 +232,11 @@ public final class IntrospectionUtils {
}
/**
- * Replace ${NAME} with the property value
+ * Replace ${NAME} with the property value.
+ * @param value The value
+ * @param staticProp Replacement properties
+ * @param dynamicProp Replacement properties
+ * @return the replacement value
*/
public static String replaceProperties(String value,
Hashtable<Object,Object> staticProp, PropertySource dynamicProp[]) {
@@ -280,7 +290,9 @@ public final class IntrospectionUtils {
}
/**
- * Reverse of Introspector.decapitalize
+ * Reverse of Introspector.decapitalize.
+ * @param name The name
+ * @return the capitalized string
*/
public static String capitalize(String name) {
if (name == null || name.length() == 0) {
diff --git a/java/org/apache/tomcat/util/buf/Ascii.java b/java/org/apache/tomcat/util/buf/Ascii.java
index cc3135c..daf9310 100644
--- a/java/org/apache/tomcat/util/buf/Ascii.java
+++ b/java/org/apache/tomcat/util/buf/Ascii.java
@@ -56,14 +56,16 @@ public final class Ascii {
/**
* Returns the lower case equivalent of the specified ASCII character.
+ * @param c The char
+ * @return the lower case equivalent char
*/
-
public static int toLower(int c) {
return toLower[c & 0xff] & 0xff;
}
/**
- * Returns true if the specified ASCII character is a digit.
+ * @return <code>true</code> if the specified ASCII character is a digit.
+ * @param c The char
*/
private static boolean isDigit(int c) {
return isDigit[c & 0xff];
@@ -74,6 +76,7 @@ public final class Ascii {
* @param b the bytes to parse
* @param off the start offset of the bytes
* @param len the length of the bytes
+ * @return the long value
* @exception NumberFormatException if the long format was invalid
*/
public static long parseLong(byte[] b, int off, int len)
diff --git a/java/org/apache/tomcat/util/buf/B2CConverter.java b/java/org/apache/tomcat/util/buf/B2CConverter.java
index 11a0569..798746d 100644
--- a/java/org/apache/tomcat/util/buf/B2CConverter.java
+++ b/java/org/apache/tomcat/util/buf/B2CConverter.java
@@ -67,6 +67,13 @@ public class B2CConverter {
/**
* Only to be used when it is known that the encoding name is in lower case.
+ * @param lowerCaseEnc The name of the encoding for the required charset in
+ * lower case
+ *
+ * @return The Charset corresponding to the requested encoding
+ *
+ * @throws UnsupportedEncodingException If the requested Charset is not
+ * available
*/
public static Charset getCharsetLower(String lowerCaseEnc)
throws UnsupportedEncodingException {
@@ -90,12 +97,11 @@ public class B2CConverter {
*/
private final ByteBuffer leftovers;
- public B2CConverter(String encoding) throws IOException {
- this(encoding, false);
+ public B2CConverter(Charset charset) {
+ this(charset, false);
}
- public B2CConverter(String encoding, boolean replaceOnError)
- throws IOException {
+ public B2CConverter(Charset charset, boolean replaceOnError) {
byte[] left = new byte[LEFTOVER_SIZE];
leftovers = ByteBuffer.wrap(left);
CodingErrorAction action;
@@ -104,7 +110,6 @@ public class B2CConverter {
} else {
action = CodingErrorAction.REPORT;
}
- Charset charset = getCharset(encoding);
// Special case. Use the Apache Harmony based UTF-8 decoder because it
// - a) rejects invalid sequences that the JVM decoder does not
// - b) fails faster for some invalid sequences
@@ -131,6 +136,8 @@ public class B2CConverter {
* @param bc byte input
* @param cc char output
* @param endOfInput Is this all of the available data
+ *
+ * @throws IOException If the conversion can not be completed
*/
public void convert(ByteChunk bc, CharChunk cc, boolean endOfInput)
throws IOException {
@@ -191,4 +198,85 @@ public class B2CConverter {
}
}
}
+
+ /**
+ * Convert the given bytes to characters.
+ *
+ * @param bc byte input
+ * @param cc char output
+ * @param ic byte input channel
+ * @param endOfInput Is this all of the available data
+ *
+ * @throws IOException If the conversion can not be completed
+ */
+ public void convert(ByteBuffer bc, CharBuffer cc, ByteChunk.ByteInputChannel ic, boolean endOfInput)
+ throws IOException {
+ if ((bb == null) || (bb.array() != bc.array())) {
+ // Create a new byte buffer if anything changed
+ bb = ByteBuffer.wrap(bc.array(), bc.arrayOffset() + bc.position(), bc.remaining());
+ } else {
+ // Initialize the byte buffer
+ bb.limit(bc.limit());
+ bb.position(bc.position());
+ }
+ if ((cb == null) || (cb.array() != cc.array())) {
+ // Create a new char buffer if anything changed
+ cb = CharBuffer.wrap(cc.array(), cc.limit(), cc.capacity() - cc.limit());
+ } else {
+ // Initialize the char buffer
+ cb.limit(cc.capacity());
+ cb.position(cc.limit());
+ }
+ CoderResult result = null;
+ // Parse leftover if any are present
+ if (leftovers.position() > 0) {
+ int pos = cb.position();
+ // Loop until one char is decoded or there is a decoder error
+ do {
+ byte chr;
+ if (bc.remaining() == 0) {
+ int n = ic.realReadBytes();
+ chr = n < 0 ? -1 : bc.get();
+ } else {
+ chr = bc.get();
+ }
+ leftovers.put(chr);
+ leftovers.flip();
+ result = decoder.decode(leftovers, cb, endOfInput);
+ leftovers.position(leftovers.limit());
+ leftovers.limit(leftovers.array().length);
+ } while (result.isUnderflow() && (cb.position() == pos));
+ if (result.isError() || result.isMalformed()) {
+ result.throwException();
+ }
+ bb.position(bc.position());
+ leftovers.position(0);
+ }
+ // Do the decoding and get the results into the byte chunk and the char
+ // chunk
+ result = decoder.decode(bb, cb, endOfInput);
+ if (result.isError() || result.isMalformed()) {
+ result.throwException();
+ } else if (result.isOverflow()) {
+ // Propagate current positions to the byte chunk and char chunk, if
+ // this continues the char buffer will get resized
+ bc.position(bb.position());
+ cc.limit(cb.position());
+ } else if (result.isUnderflow()) {
+ // Propagate current positions to the byte chunk and char chunk
+ bc.position(bb.position());
+ cc.limit(cb.position());
+ // Put leftovers in the leftovers byte buffer
+ if (bc.remaining() > 0) {
+ leftovers.limit(leftovers.array().length);
+ leftovers.position(bc.remaining());
+ bc.get(leftovers.array(), 0, bc.remaining());
+ }
+ }
+ }
+
+
+ public Charset getCharset() {
+ return decoder.charset();
+ }
}
diff --git a/java/org/apache/tomcat/util/buf/ByteBufferHolder.java b/java/org/apache/tomcat/util/buf/ByteBufferHolder.java
new file mode 100644
index 0000000..d124196
--- /dev/null
+++ b/java/org/apache/tomcat/util/buf/ByteBufferHolder.java
@@ -0,0 +1,55 @@
+/*
+ * 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.nio.ByteBuffer;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * Simple wrapper for a {@link ByteBuffer} that remembers if the buffer has been
+ * flipped or not.
+ */
+public class ByteBufferHolder {
+
+ private final ByteBuffer buf;
+ private final AtomicBoolean flipped;
+
+ public ByteBufferHolder(ByteBuffer buf, boolean flipped) {
+ this.buf = buf;
+ this.flipped = new AtomicBoolean(flipped);
+ }
+
+
+ public ByteBuffer getBuf() {
+ return buf;
+ }
+
+
+ public boolean isFlipped() {
+ return flipped.get();
+ }
+
+
+ public boolean flip() {
+ if (flipped.compareAndSet(false, true)) {
+ buf.flip();
+ return true;
+ } else {
+ return false;
+ }
+ }
+}
\ No newline at end of file
diff --git a/java/org/apache/tomcat/util/buf/ByteBufferUtils.java b/java/org/apache/tomcat/util/buf/ByteBufferUtils.java
new file mode 100644
index 0000000..e38457d
--- /dev/null
+++ b/java/org/apache/tomcat/util/buf/ByteBufferUtils.java
@@ -0,0 +1,112 @@
+/*
+ * 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.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(Constants.Package);
+ private static final Log log = LogFactory.getLog(ByteBufferUtils.class);
+
+ private static final Method cleanerMethod;
+ private static final Method cleanMethod;
+
+ static {
+ ByteBuffer tempBuffer = ByteBuffer.allocateDirect(0);
+ Method cleanerMethodLocal = null;
+ Method cleanMethodLocal = null;
+ try {
+ cleanerMethodLocal = tempBuffer.getClass().getMethod("cleaner");
+ cleanerMethodLocal.setAccessible(true);
+ Object cleanerObject = cleanerMethodLocal.invoke(tempBuffer);
+ if (cleanerObject instanceof Runnable) {
+ cleanMethodLocal = Runnable.class.getMethod("run");
+ } else {
+ cleanMethodLocal = cleanerObject.getClass().getMethod("clean");
+ }
+ cleanMethodLocal.invoke(cleanerObject);
+ } catch (IllegalAccessException | IllegalArgumentException
+ | InvocationTargetException | NoSuchMethodException | SecurityException e) {
+ log.warn(sm.getString("byteBufferUtils.cleaner"), e);
+ cleanerMethodLocal = null;
+ cleanMethodLocal = null;
+ }
+ cleanerMethod = cleanerMethodLocal;
+ cleanMethod = cleanMethodLocal;
+ }
+
+ private ByteBufferUtils() {
+ // Hide the default constructor since this is a utility class.
+ }
+
+
+ /**
+ * Expands buffer to the given size unless it is already as big or bigger.
+ * Buffers are assumed to be in 'write to' mode since there would be no need
+ * to expand a buffer while it was in 'read from' mode.
+ *
+ * @param in Buffer to expand
+ * @param newSize The size t which the buffer should be expanded
+ * @return The expanded buffer with any data from the input buffer
+ * copied in to it or the original buffer if there was no
+ * need for expansion
+ */
+ public static ByteBuffer expand(ByteBuffer in, int newSize) {
+ if (in.capacity() >= newSize) {
+ return in;
+ }
+
+ ByteBuffer out;
+ boolean direct = false;
+ if (in.isDirect()) {
+ out = ByteBuffer.allocateDirect(newSize);
+ direct = true;
+ } else {
+ out = ByteBuffer.allocate(newSize);
+ }
+
+ // Copy data
+ in.flip();
+ out.put(in);
+
+ if (direct) {
+ cleanDirectBuffer(in);
+ }
+
+ return out;
+ }
+
+ public static void cleanDirectBuffer(ByteBuffer buf) {
+ if (cleanMethod != null) {
+ try {
+ cleanMethod.invoke(cleanerMethod.invoke(buf));
+ } catch (IllegalAccessException | IllegalArgumentException
+ | InvocationTargetException | SecurityException e) {
+ // Ignore
+ }
+ }
+ }
+
+}
diff --git a/java/org/apache/tomcat/util/buf/ByteChunk.java b/java/org/apache/tomcat/util/buf/ByteChunk.java
index 37c1d9f..480268c 100644
--- a/java/org/apache/tomcat/util/buf/ByteChunk.java
+++ b/java/org/apache/tomcat/util/buf/ByteChunk.java
@@ -75,12 +75,13 @@ public final class ByteChunk implements Cloneable, Serializable {
*/
public static interface ByteInputChannel {
/**
- * Read new bytes ( usually the internal conversion buffer ).
- * The implementation is allowed to ignore the parameters,
- * and mutate the chunk if it wishes to implement its own buffering.
+ * Read new bytes.
+ *
+ * @return The number of bytes read
+ *
+ * @throws IOException If an I/O occurs while reading the bytes
*/
- public int realReadBytes(byte cbuf[], int off, int len)
- throws IOException;
+ public int realReadBytes() throws IOException;
}
/** Same as java.nio.channel.WrittableByteChannel.
@@ -89,9 +90,23 @@ public final class ByteChunk implements Cloneable, Serializable {
/**
* Send the bytes ( usually the internal conversion buffer ).
* Expect 8k output if the buffer is full.
+ *
+ * @param cbuf bytes that will be written
+ * @param off offset in the bytes array
+ * @param len length that will be written
+ * @throws IOException If an I/O occurs while writing the bytes
*/
public void realWriteBytes(byte cbuf[], int off, int len)
throws IOException;
+
+ /**
+ * Send the bytes ( usually the internal conversion buffer ).
+ * Expect 8k output if the buffer is full.
+ *
+ * @param from bytes that will be written
+ * @throws IOException If an I/O occurs while writing the bytes
+ */
+ public void realWriteBytes(ByteBuffer from) throws IOException;
}
// --------------------
@@ -141,7 +156,6 @@ public final class ByteChunk implements Cloneable, Serializable {
* Resets the message buff to an uninitialized state.
*/
public void recycle() {
- // buff = null;
charset=null;
start=0;
end=0;
@@ -149,10 +163,6 @@ public final class ByteChunk implements Cloneable, Serializable {
hasHashCode = false;
}
- public void reset() {
- buff=null;
- }
-
// -------------------- Setup --------------------
public void allocate( int initial, int limit ) {
@@ -193,21 +203,21 @@ public final class ByteChunk implements Cloneable, Serializable {
}
/**
- * Returns the message bytes.
+ * @return the message bytes.
*/
public byte[] getBytes() {
return getBuffer();
}
/**
- * Returns the message bytes.
+ * @return the message bytes.
*/
public byte[] getBuffer() {
return buff;
}
/**
- * Returns the start offset of the bytes.
+ * @return the start offset of the bytes.
* For output this is the end of the buffer.
*/
public int getStart() {
@@ -226,19 +236,19 @@ public final class ByteChunk implements Cloneable, Serializable {
}
/**
- * Returns the length of the bytes.
- * XXX need to clean this up
+ * @return the length of the bytes.
*/
public int getLength() {
return end-start;
}
- /** Maximum amount of data in this buffer.
- *
- * If -1 or not set, the buffer will grow indefinitely.
- * Can be smaller than the current buffer size ( which will not shrink ).
- * When the limit is reached, the buffer will be flushed ( if out is set )
- * or throw exception.
+ /**
+ * Maximum amount of data in this buffer.
+ * If -1 or not set, the buffer will grow indefinitely.
+ * Can be smaller than the current buffer size ( which will not shrink ).
+ * When the limit is reached, the buffer will be flushed ( if out is set )
+ * or throw exception.
+ * @param limit The new limit
*/
public void setLimit(int limit) {
this.limit=limit;
@@ -250,15 +260,17 @@ public final class ByteChunk implements Cloneable, Serializable {
/**
* When the buffer is empty, read the data from the input channel.
+ * @param in The input channel
*/
public void setByteInputChannel(ByteInputChannel in) {
this.in = in;
}
- /** When the buffer is full, write the data to the output channel.
- * Also used when large amount of data is appended.
- *
- * If not set, the buffer will grow to the limit.
+ /**
+ * When the buffer is full, write the data to the output channel.
+ * Also used when large amount of data is appended.
+ * If not set, the buffer will grow to the limit.
+ * @param out The output channel
*/
public void setByteOutputChannel(ByteOutputChannel out) {
this.out=out;
@@ -291,11 +303,14 @@ public final class ByteChunk implements Cloneable, Serializable {
append( src.getBytes(), src.getStart(), src.getLength());
}
- /** Add data to the buffer
+ /**
+ * Add data to the buffer.
+ * @param src Bytes array
+ * @param off Offset
+ * @param len Length
+ * @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 );
@@ -351,61 +366,141 @@ public final class ByteChunk implements Cloneable, Serializable {
}
- // -------------------- Removing data from the buffer --------------------
+ /**
+ * Add data to the buffer.
+ *
+ * @param from the ByteBuffer with the data
+ * @throws IOException Writing overflow data to the output channel failed
+ */
+ public void append(ByteBuffer from) throws IOException {
+ int len = from.remaining();
- public int substract()
- throws IOException {
+ // will grow, up to limit
+ makeSpace(len);
- if ((end - start) == 0) {
- if (in == null) {
- return -1;
- }
- int n = in.realReadBytes( buff, 0, buff.length );
- if (n < 0) {
- return -1;
- }
+ // if we don't have limit: makeSpace can grow as it wants
+ if (limit < 0) {
+ // assert: makeSpace made enough space
+ from.get(buff, end, len);
+ end += len;
+ return;
}
- return (buff[start++] & 0xFF);
+ // Optimize on a common case.
+ // If the buffer is empty and the source is going to fill up all the
+ // space in buffer, may as well write it directly to the output,
+ // and avoid an extra copy
+ if (len == limit && end == start && out != null) {
+ out.realWriteBytes(from);
+ from.position(from.limit());
+ return;
+ }
+ // if we have limit and we're below
+ if (len <= limit - end) {
+ // makeSpace will grow the buffer to the limit,
+ // so we have space
+ from.get(buff, end, len);
+ end += len;
+ return;
+ }
- }
+ // need more space than we can afford, need to flush
+ // buffer
- public byte substractB()
- throws IOException {
+ // the buffer is already at ( or bigger than ) limit
- if ((end - start) == 0) {
- if (in == null)
- return -1;
- int n = in.realReadBytes( buff, 0, buff.length );
- if (n < 0)
- return -1;
+ // We chunk the data into slices fitting in the buffer limit, although
+ // if the data is written directly if it doesn't fit
+
+ int avail = limit - end;
+ from.get(buff, end, avail);
+ end += avail;
+
+ flushBuffer();
+
+ int fromLimit = from.limit();
+ int remain = len - avail;
+ avail = limit - end;
+ while (remain >= avail) {
+ from.limit(from.position() + avail);
+ out.realWriteBytes(from);
+ from.position(from.limit());
+ remain = remain - avail;
}
- return (buff[start++]);
+ from.limit(fromLimit);
+ from.get(buff, end, remain);
+ end += remain;
+ }
+
+ // -------------------- Removing data from the buffer --------------------
+
+ public int substract() throws IOException {
+ if (checkEof()) {
+ return -1;
+ }
+ return buff[start++] & 0xFF;
}
- public int substract( byte src[], int off, int len )
- throws IOException {
- if ((end - start) == 0) {
- if (in == null) {
- return -1;
- }
- int n = in.realReadBytes( buff, 0, buff.length );
- if (n < 0) {
- return -1;
- }
+ public byte substractB() throws IOException {
+ if (checkEof()) {
+ return -1;
}
+ return buff[start++];
+ }
+
+ public int substract(byte dest[], int off, int len ) throws IOException {
+ if (checkEof()) {
+ return -1;
+ }
int n = len;
if (len > getLength()) {
n = getLength();
}
- System.arraycopy(buff, start, src, off, n);
+ System.arraycopy(buff, start, dest, off, n);
start += n;
return n;
+ }
+
+ /**
+ * Transfers bytes from the buffer to the specified ByteBuffer. After the
+ * operation the position of the ByteBuffer will be returned to the one
+ * before the operation, the limit will be the position incremented by
+ * the number of the transfered bytes.
+ *
+ * @param to the ByteBuffer into which bytes are to be written.
+ * @return an integer specifying the actual number of bytes read, or -1 if
+ * the end of the stream is reached
+ * @throws IOException if an input or output exception has occurred
+ */
+ public int substract(ByteBuffer to) throws IOException {
+ if (checkEof()) {
+ return -1;
+ }
+ int n = Math.min(to.remaining(), getLength());
+ to.put(buff, start, n);
+ to.limit(to.position());
+ to.position(to.position() - n);
+ start += n;
+ return n;
+ }
+
+
+ private boolean checkEof() throws IOException {
+ if ((end - start) == 0) {
+ if (in == null) {
+ return true;
+ }
+ int n = in.realReadBytes();
+ if (n < 0) {
+ return true;
+ }
+ }
+ return false;
}
@@ -413,7 +508,7 @@ public final class ByteChunk implements Cloneable, Serializable {
* Send the buffer to the sink. Called by append() when the limit is
* reached. You can also call it explicitly to force the data to be written.
*
- * @throws IOException
+ * @throws IOException Writing overflow data to the output channel failed
*/
public void flushBuffer()
throws IOException
@@ -428,8 +523,9 @@ public final class ByteChunk implements Cloneable, Serializable {
}
/**
- * Make space for len chars. If len is small, allocate a reserve space too.
+ * Make space for len bytes. If len is small, allocate a reserve space too.
* Never grow bigger than limit.
+ * @param count The size
*/
public void makeSpace(int count) {
byte[] tmp = null;
@@ -459,20 +555,15 @@ public final class ByteChunk implements Cloneable, Serializable {
// grow in larger chunks
if( desiredSize < 2 * buff.length ) {
newSize= buff.length * 2;
- if( limit >0 &&
- newSize > limit ) {
- newSize=limit;
- }
- tmp=new byte[newSize];
} else {
newSize= buff.length * 2 + count ;
- if( limit > 0 &&
- newSize > limit ) {
- newSize=limit;
- }
- tmp=new byte[newSize];
}
+ if (limit > 0 && newSize > limit) {
+ newSize = limit;
+ }
+ tmp = new byte[newSize];
+
System.arraycopy(buff, start, tmp, 0, end-start);
buff = tmp;
tmp = null;
@@ -615,6 +706,7 @@ public final class ByteChunk implements Cloneable, Serializable {
* Returns true if the message bytes starts with the specified string.
* @param s the string
* @param pos The position
+ * @return <code>true</code> if the start matches
*/
public boolean startsWithIgnoreCase(String s, int pos) {
byte[] b = buff;
diff --git a/java/org/apache/tomcat/util/buf/C2BConverter.java b/java/org/apache/tomcat/util/buf/C2BConverter.java
index 36bc934..e5062de 100644
--- a/java/org/apache/tomcat/util/buf/C2BConverter.java
+++ b/java/org/apache/tomcat/util/buf/C2BConverter.java
@@ -19,6 +19,7 @@ package org.apache.tomcat.util.buf;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
+import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder;
import java.nio.charset.CoderResult;
import java.nio.charset.CodingErrorAction;
@@ -37,12 +38,10 @@ public final class C2BConverter {
*/
private final CharBuffer leftovers;
- public C2BConverter(String encoding) throws IOException {
- encoder = B2CConverter.getCharset(encoding).newEncoder();
- // FIXME: See if unmappable/malformed behavior configuration is needed
- // in practice
+ public C2BConverter(Charset charset) {
+ encoder = charset.newEncoder();
encoder.onUnmappableCharacter(CodingErrorAction.REPLACE)
- .onMalformedInput(CodingErrorAction.REPLACE);
+ .onMalformedInput(CodingErrorAction.REPLACE);
char[] left = new char[4];
leftovers = CharBuffer.wrap(left);
}
@@ -64,13 +63,12 @@ public final class C2BConverter {
*
* @param cc char input
* @param bc byte output
+ * @throws IOException An encoding error occurred
*/
- public void convert(CharChunk cc, ByteChunk bc)
- throws IOException {
+ public void convert(CharChunk cc, ByteChunk bc) throws IOException {
if ((bb == null) || (bb.array() != bc.getBuffer())) {
// Create a new byte buffer if anything changed
- bb = ByteBuffer.wrap(bc.getBuffer(), bc.getEnd(),
- bc.getBuffer().length - bc.getEnd());
+ bb = ByteBuffer.wrap(bc.getBuffer(), bc.getEnd(), bc.getBuffer().length - bc.getEnd());
} else {
// Initialize the byte buffer
bb.limit(bc.getBuffer().length);
@@ -78,8 +76,7 @@ public final class C2BConverter {
}
if ((cb == null) || (cb.array() != cc.getBuffer())) {
// Create a new char buffer if anything changed
- cb = CharBuffer.wrap(cc.getBuffer(), cc.getStart(),
- cc.getLength());
+ cb = CharBuffer.wrap(cc.getBuffer(), cc.getStart(), cc.getLength());
} else {
// Initialize the char buffer
cb.limit(cc.getEnd());
@@ -125,4 +122,71 @@ public final class C2BConverter {
}
}
+ /**
+ * Convert the given characters to bytes.
+ *
+ * @param cc char input
+ * @param bc byte output
+ * @throws IOException An encoding error occurred
+ */
+ public void convert(CharBuffer cc, ByteBuffer bc) throws IOException {
+ if ((bb == null) || (bb.array() != bc.array())) {
+ // Create a new byte buffer if anything changed
+ bb = ByteBuffer.wrap(bc.array(), bc.limit(), bc.capacity() - bc.limit());
+ } else {
+ // Initialize the byte buffer
+ bb.limit(bc.capacity());
+ bb.position(bc.limit());
+ }
+ if ((cb == null) || (cb.array() != cc.array())) {
+ // Create a new char buffer if anything changed
+ cb = CharBuffer.wrap(cc.array(), cc.arrayOffset() + cc.position(), cc.remaining());
+ } else {
+ // Initialize the char buffer
+ cb.limit(cc.limit());
+ cb.position(cc.position());
+ }
+ CoderResult result = null;
+ // 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
+ do {
+ leftovers.put(cc.get());
+ leftovers.flip();
+ result = encoder.encode(leftovers, bb, false);
+ leftovers.position(leftovers.limit());
+ leftovers.limit(leftovers.array().length);
+ } while (result.isUnderflow() && (bb.position() == pos));
+ if (result.isError() || result.isMalformed()) {
+ result.throwException();
+ }
+ cb.position(cc.position());
+ leftovers.position(0);
+ }
+ // Do the decoding and get the results into the byte chunk and the char
+ // chunk
+ result = encoder.encode(cb, bb, false);
+ if (result.isError() || result.isMalformed()) {
+ result.throwException();
+ } else if (result.isOverflow()) {
+ // Propagate current positions to the byte chunk and char chunk
+ bc.limit(bb.position());
+ cc.position(cb.position());
+ } else if (result.isUnderflow()) {
+ // Propagate current positions to the byte chunk and char chunk
+ bc.limit(bb.position());
+ cc.position(cb.position());
+ // Put leftovers in the leftovers char buffer
+ if (cc.remaining() > 0) {
+ leftovers.limit(leftovers.array().length);
+ leftovers.position(cc.remaining());
+ cc.get(leftovers.array(), 0, cc.remaining());
+ }
+ }
+ }
+
+ public Charset getCharset() {
+ return encoder.charset();
+ }
}
diff --git a/java/org/apache/tomcat/util/buf/CharChunk.java b/java/org/apache/tomcat/util/buf/CharChunk.java
index 341725e..7c082b4 100644
--- a/java/org/apache/tomcat/util/buf/CharChunk.java
+++ b/java/org/apache/tomcat/util/buf/CharChunk.java
@@ -37,20 +37,27 @@ public final class CharChunk implements Cloneable, Serializable, CharSequence {
// Input interface, used when the buffer is emptied.
public static interface CharInputChannel {
/**
- * Read new bytes ( usually the internal conversion buffer ).
- * The implementation is allowed to ignore the parameters,
- * and mutate the chunk if it wishes to implement its own buffering.
+ * Read new characters.
+ *
+ * @return The number of characters read
+ *
+ * @throws IOException If an I/O error occurs reading the characters
*/
- public int realReadChars(char cbuf[], int off, int len)
- throws IOException;
+ public int realReadChars() throws IOException;
}
/**
* When we need more space we'll either
* grow the buffer ( up to the limit ) or send it to a channel.
*/
public static interface CharOutputChannel {
- /** Send the bytes ( usually the internal conversion buffer ).
- * Expect 8k output if the buffer is full.
+ /**
+ * Send the bytes ( usually the internal conversion buffer ).
+ * Expect 8k output if the buffer is full.
+ *
+ * @param cbuf characters that will be written
+ * @param off offset in the characters array
+ * @param len length that will be written
+ * @throws IOException If an I/O occurs while writing the characters
*/
public void realWriteChars(char cbuf[], int off, int len)
throws IOException;
@@ -135,12 +142,13 @@ public final class CharChunk implements Cloneable, Serializable, CharSequence {
hasHashCode = false;
}
- /** Maximum amount of data in this buffer.
- *
- * If -1 or not set, the buffer will grow indefinitely.
- * Can be smaller than the current buffer size ( which will not shrink ).
- * When the limit is reached, the buffer will be flushed ( if out is set )
- * or throw exception.
+ /**
+ * Maximum amount of data in this buffer.
+ * If -1 or not set, the buffer will grow indefinitely.
+ * Can be smaller than the current buffer size ( which will not shrink ).
+ * When the limit is reached, the buffer will be flushed ( if out is set )
+ * or throw exception.
+ * @param limit The new limit
*/
public void setLimit(int limit) {
this.limit=limit;
@@ -152,15 +160,17 @@ public final class CharChunk implements Cloneable, Serializable, CharSequence {
/**
* When the buffer is empty, read the data from the input channel.
+ * @param in The input channel
*/
public void setCharInputChannel(CharInputChannel in) {
this.in = in;
}
- /** When the buffer is full, write the data to the output channel.
- * Also used when large amount of data is appended.
- *
- * If not set, the buffer will grow to the limit.
+ /**
+ * When the buffer is full, write the data to the output channel.
+ * Also used when large amount of data is appended.
+ * If not set, the buffer will grow to the limit.
+ * @param out The output channel
*/
public void setCharOutputChannel(CharOutputChannel out) {
this.out=out;
@@ -178,7 +188,7 @@ public final class CharChunk implements Cloneable, Serializable, CharSequence {
}
/**
- * Returns the start offset of the bytes.
+ * @return the start offset of the chars.
* For output this is the end of the buffer.
*/
public int getStart() {
@@ -190,14 +200,14 @@ public final class CharChunk implements Cloneable, Serializable, CharSequence {
}
/**
- * Returns the start offset of the bytes.
+ * @param off The offset
*/
public void setOffset(int off) {
start=off;
}
/**
- * Returns the length of the bytes.
+ * @return the length of the bytes.
*/
public int getLength() {
return end-start;
@@ -232,7 +242,12 @@ public final class CharChunk implements Cloneable, Serializable, CharSequence {
append( src.getBuffer(), src.getOffset(), src.getLength());
}
- /** Add data to the buffer
+ /**
+ * Add data to the buffer.
+ * @param src Char array
+ * @param off Offset
+ * @param len Length
+ * @throws IOException Writing overflow data to the output channel failed
*/
public void append( char src[], int off, int len )
throws IOException
@@ -302,13 +317,21 @@ public final class CharChunk implements Cloneable, Serializable, CharSequence {
}
- /** Append a string to the buffer
+ /**
+ * Append a string to the buffer.
+ * @param s The string
+ * @throws IOException Writing overflow data to the output channel failed
*/
public void append(String s) throws IOException {
append(s, 0, s.length());
}
- /** Append a string to the buffer
+ /**
+ * Append a string to the buffer.
+ * @param s The string
+ * @param off Offset
+ * @param len Length
+ * @throws IOException Writing overflow data to the output channel failed
*/
public void append(String s, int off, int len) throws IOException {
if (s==null) {
@@ -341,31 +364,25 @@ public final class CharChunk implements Cloneable, Serializable, CharSequence {
// -------------------- Removing data from the buffer --------------------
- public int substract()
- throws IOException {
-
+ public int substract() throws IOException {
if ((end - start) == 0) {
if (in == null) {
return -1;
}
- int n = in.realReadChars(buff, end, buff.length - end);
+ int n = in.realReadChars();
if (n < 0) {
return -1;
}
}
-
return (buff[start++]);
-
}
- public int substract( char src[], int off, int len )
- throws IOException {
-
+ public int substract(char dest[], int off, int len) throws IOException {
if ((end - start) == 0) {
if (in == null) {
return -1;
}
- int n = in.realReadChars( buff, end, buff.length - end);
+ int n = in.realReadChars();
if (n < 0) {
return -1;
}
@@ -375,16 +392,13 @@ public final class CharChunk implements Cloneable, Serializable, CharSequence {
if (len > getLength()) {
n = getLength();
}
- System.arraycopy(buff, start, src, off, n);
+ System.arraycopy(buff, start, dest, off, n);
start += n;
return n;
-
}
- public void flushBuffer()
- throws IOException
- {
+ public void flushBuffer() throws IOException {
//assert out!=null
if( out==null ) {
throw new IOException( "Buffer overflow, no sink " + limit + " " +
@@ -394,8 +408,10 @@ public final class CharChunk implements Cloneable, Serializable, CharSequence {
end=start;
}
- /** Make space for len chars. If len is small, allocate
- * a reserve space too. Never grow bigger than limit.
+ /**
+ * Make space for len chars. If len is small, allocate
+ * a reserve space too. Never grow bigger than limit.
+ * @param count The size
*/
public void makeSpace(int count)
{
@@ -426,20 +442,15 @@ public final class CharChunk implements Cloneable, Serializable, CharSequence {
// grow in larger chunks
if( desiredSize < 2 * buff.length ) {
newSize= buff.length * 2;
- if( limit >0 &&
- newSize > limit ) {
- newSize=limit;
- }
- tmp=new char[newSize];
} else {
newSize= buff.length * 2 + count ;
- if( limit > 0 &&
- newSize > limit ) {
- newSize=limit;
- }
- tmp=new char[newSize];
}
+ if (limit > 0 && newSize > limit) {
+ newSize = limit;
+ }
+ tmp = new char[newSize];
+
System.arraycopy(buff, 0, tmp, 0, end);
buff = tmp;
tmp = null;
@@ -474,7 +485,7 @@ public final class CharChunk implements Cloneable, Serializable, CharSequence {
/**
* Compares the message bytes to the specified String object.
* @param s the String to compare
- * @return true if the comparison succeeded, false otherwise
+ * @return <code>true</code> if the comparison succeeded, <code>false</code> otherwise
*/
public boolean equals(String s) {
char[] c = buff;
@@ -494,7 +505,7 @@ public final class CharChunk implements Cloneable, Serializable, CharSequence {
/**
* Compares the message bytes to the specified String object.
* @param s the String to compare
- * @return true if the comparison succeeded, false otherwise
+ * @return <code>true</code> if the comparison succeeded, <code>false</code> otherwise
*/
public boolean equalsIgnoreCase(String s) {
char[] c = buff;
@@ -535,8 +546,8 @@ public final class CharChunk implements Cloneable, Serializable, CharSequence {
}
/**
- * Returns true if the message bytes starts with the specified string.
- * @param s the string
+ * @return <code>true</code> if the message bytes starts with the specified string.
+ * @param s The string
*/
public boolean startsWith(String s) {
char[] c = buff;
@@ -554,8 +565,9 @@ public final class CharChunk implements Cloneable, Serializable, CharSequence {
}
/**
- * Returns true if the message bytes starts with the specified string.
- * @param s the string
+ * @return <code>true</code> if the message bytes starts with the specified string.
+ * @param s The string
+ * @param pos The position at which the comparison will be made
*/
public boolean startsWithIgnoreCase(String s, int pos) {
char[] c = buff;
@@ -574,8 +586,8 @@ public final class CharChunk implements Cloneable, Serializable, CharSequence {
/**
- * Returns true if the message bytes end with the specified string.
- * @param s the string
+ * @return <code>true</code> if the message bytes end with the specified string.
+ * @param s The string
*/
public boolean endsWith(String s) {
char[] c = buff;
@@ -621,8 +633,9 @@ public final class CharChunk implements Cloneable, Serializable, CharSequence {
}
/**
- * Returns true if the message bytes starts with the specified string.
+ * @return <code>true</code> if the message bytes starts with the specified string.
* @param c the character
+ * @param starting Start position
*/
public int indexOf(char c, int starting) {
int ret = indexOf( buff, start+starting, end, c );
diff --git a/java/org/apache/tomcat/util/buf/LocalStrings.properties b/java/org/apache/tomcat/util/buf/LocalStrings.properties
index 5d64768..9f20691 100644
--- a/java/org/apache/tomcat/util/buf/LocalStrings.properties
+++ b/java/org/apache/tomcat/util/buf/LocalStrings.properties
@@ -22,3 +22,5 @@ hexUtils.fromHex.nonHex=The input must consist only of hex digits
uDecoder.urlDecode.missingDigit=The % character must be followed by two hexademical digits
uDecoder.convertHexDigit.notHex=[{0}] is not a hexadecimal digit
uDecoder.urlDecode.uee=Unable to URL decode the specified input since the encoding [{0}] is not supported.
+
+byteBufferUtils.cleaner=Cannot use direct ByteBuffer cleaner, memory leaking may occur
\ No newline at end of file
diff --git a/java/org/apache/tomcat/util/buf/MessageBytes.java b/java/org/apache/tomcat/util/buf/MessageBytes.java
index 22355c3..6b9d275 100644
--- a/java/org/apache/tomcat/util/buf/MessageBytes.java
+++ b/java/org/apache/tomcat/util/buf/MessageBytes.java
@@ -73,16 +73,16 @@ public final class MessageBytes implements Cloneable, Serializable {
private MessageBytes() {
}
- /** Construct a new MessageBytes instance
+ /**
+ * Construct a new MessageBytes instance.
+ * @return the instance
*/
public static MessageBytes newInstance() {
return factory.newInstance();
}
public boolean isNull() {
- // should we check also hasStrValue ???
return byteC.isNull() && charC.isNull() && ! hasStrValue;
- // bytes==null && strValue==null;
}
/**
@@ -119,9 +119,9 @@ public final class MessageBytes implements Cloneable, Serializable {
/**
* Sets the content to be a char[]
*
- * @param c the bytes
- * @param off the start offset of the bytes
- * @param len the length of the bytes
+ * @param c the chars
+ * @param off the start offset of the chars
+ * @param len the length of the chars
*/
public void setChars( char[] c, int off, int len ) {
charC.setChars( c, off, len );
@@ -133,6 +133,7 @@ public final class MessageBytes implements Cloneable, Serializable {
/**
* Set the content to be a string
+ * @param s The string
*/
public void setString( String s ) {
strValue=s;
@@ -149,7 +150,9 @@ public final class MessageBytes implements Cloneable, Serializable {
// -------------------- Conversion and getters --------------------
- /** Compute the string value
+ /**
+ * Compute the string value.
+ * @return the string
*/
@Override
public String toString() {
@@ -171,8 +174,10 @@ public final class MessageBytes implements Cloneable, Serializable {
}
//----------------------------------------
- /** Return the type of the original content. Can be
- * T_STR, T_BYTES, T_CHARS or T_NULL
+ /**
+ * Return the type of the original content. Can be
+ * T_STR, T_BYTES, T_CHARS or T_NULL
+ * @return the type
*/
public int getType() {
return type;
@@ -181,6 +186,7 @@ public final class MessageBytes implements Cloneable, Serializable {
/**
* Returns the byte chunk, representing the byte[] and offset/length.
* Valid only if T_BYTES or after a conversion was made.
+ * @return the byte chunk
*/
public ByteChunk getByteChunk() {
return byteC;
@@ -189,6 +195,7 @@ public final class MessageBytes implements Cloneable, Serializable {
/**
* Returns the char chunk, representing the char[] and offset/length.
* Valid only if T_CHARS or after a conversion was made.
+ * @return the char chunk
*/
public CharChunk getCharChunk() {
return charC;
@@ -197,13 +204,14 @@ public final class MessageBytes implements Cloneable, Serializable {
/**
* Returns the string value.
* Valid only if T_STR or after a conversion was made.
+ * @return the string
*/
public String getString() {
return strValue;
}
/**
- * Get the Charset used for string<->byte conversions.
+ * @return the Charset used for string<->byte conversions.
*/
public Charset getCharset() {
return byteC.getCharset();
@@ -211,12 +219,14 @@ public final class MessageBytes implements Cloneable, Serializable {
/**
* Set the Charset used for string<->byte conversions.
+ * @param charset The charset
*/
public void setCharset(Charset charset) {
byteC.setCharset(charset);
}
- /** Do a char->byte conversion.
+ /**
+ * Do a char->byte conversion.
*/
public void toBytes() {
if (!byteC.isNull()) {
@@ -230,8 +240,9 @@ public final class MessageBytes implements Cloneable, Serializable {
byteC.setBytes(result.array(), result.arrayOffset(), result.limit());
}
- /** Convert to char[] and fill the CharChunk.
- * XXX Not optimized - it converts to String first.
+ /**
+ * Convert to char[] and fill the CharChunk.
+ * XXX Not optimized - it converts to String first.
*/
public void toChars() {
if( ! charC.isNull() ) {
@@ -250,6 +261,7 @@ public final class MessageBytes implements Cloneable, Serializable {
* Returns the length of the original buffer.
* Note that the length in bytes may be different from the length
* in chars.
+ * @return the length
*/
public int getLength() {
if(type==T_BYTES) {
@@ -273,7 +285,7 @@ public final class MessageBytes implements Cloneable, Serializable {
/**
* Compares the message bytes to the specified String object.
* @param s the String to compare
- * @return true if the comparison succeeded, false otherwise
+ * @return <code>true</code> if the comparison succeeded, <code>false</code> otherwise
*/
public boolean equals(String s) {
switch (type) {
@@ -294,7 +306,7 @@ public final class MessageBytes implements Cloneable, Serializable {
/**
* Compares the message bytes to the specified String object.
* @param s the String to compare
- * @return true if the comparison succeeded, false otherwise
+ * @return <code>true</code> if the comparison succeeded, <code>false</code> otherwise
*/
public boolean equalsIgnoreCase(String s) {
switch (type) {
@@ -354,7 +366,7 @@ public final class MessageBytes implements Cloneable, Serializable {
/**
- * Returns true if the message bytes starts with the specified string.
+ * @return <code>true</code> if the message bytes starts with the specified string.
* @param s the string
* @param pos The start position
*/
@@ -438,8 +450,10 @@ public final class MessageBytes implements Cloneable, Serializable {
return upper.indexOf( sU, starting );
}
- /** Copy the src into this MessageBytes, allocating more space if
- * needed
+ /**
+ * Copy the src into this MessageBytes, allocating more space if needed.
+ * @param src The source
+ * @throws IOException Writing overflow data to the output channel failed
*/
public void duplicate( MessageBytes src ) throws IOException
{
@@ -471,7 +485,9 @@ public final class MessageBytes implements Cloneable, Serializable {
private long longValue;
private boolean hasLongValue=false;
- /** Set the buffer to the representation of an long
+ /**
+ * Set the buffer to the representation of an long.
+ * @param l The long
*/
public void setLong(long l) {
byteC.allocate(32, 64);
@@ -513,7 +529,9 @@ public final class MessageBytes implements Cloneable, Serializable {
}
// Used for headers conversion
- /** Convert the buffer to an long, cache the value
+ /**
+ * Convert the buffer to an long, cache the value.
+ * @return the long value
*/
public long getLong() {
if( hasLongValue ) {
diff --git a/java/org/apache/tomcat/util/buf/StringCache.java b/java/org/apache/tomcat/util/buf/StringCache.java
index da65fee..cd599aa 100644
--- a/java/org/apache/tomcat/util/buf/StringCache.java
+++ b/java/org/apache/tomcat/util/buf/StringCache.java
@@ -22,6 +22,9 @@ import java.util.HashMap;
import java.util.Map.Entry;
import java.util.TreeMap;
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
+
/**
* This class implements a String cache for ByteChunk and CharChunk.
*
@@ -30,8 +33,7 @@ import java.util.TreeMap;
public class StringCache {
- private static final org.apache.juli.logging.Log log=
- org.apache.juli.logging.LogFactory.getLog( StringCache.class );
+ private static final Log log = LogFactory.getLog(StringCache.class);
// ------------------------------------------------------- Static Variables
@@ -448,7 +450,9 @@ public class StringCache {
/**
* Compare given byte chunk with byte array.
- * Return -1, 0 or +1 if inferior, equal, or superior to the String.
+ * @param name The name to compare
+ * @param compareTo The compared to data
+ * @return -1, 0 or +1 if inferior, equal, or superior to the String.
*/
protected static final int compare(ByteChunk name, byte[] compareTo) {
int result = 0;
@@ -482,6 +486,8 @@ public class StringCache {
/**
* Find an entry given its name in the cache and return the associated
* String.
+ * @param name The name to find
+ * @return the corresponding value
*/
protected static final String find(ByteChunk name) {
int pos = findClosest(name, bcCache, bcCache.length);
@@ -498,6 +504,10 @@ public class StringCache {
* Find an entry given its name in a sorted array of map elements.
* This will return the index for the closest inferior or equal item in the
* given array.
+ * @param name The name to find
+ * @param array The array in which to look
+ * @param len The effective length of the array
+ * @return the position of the best match
*/
protected static final int findClosest(ByteChunk name, ByteEntry[] array,
int len) {
@@ -543,7 +553,9 @@ public class StringCache {
/**
* Compare given char chunk with char array.
- * Return -1, 0 or +1 if inferior, equal, or superior to the String.
+ * @param name The name to compare
+ * @param compareTo The compared to data
+ * @return -1, 0 or +1 if inferior, equal, or superior to the String.
*/
protected static final int compare(CharChunk name, char[] compareTo) {
int result = 0;
@@ -577,6 +589,8 @@ public class StringCache {
/**
* Find an entry given its name in the cache and return the associated
* String.
+ * @param name The name to find
+ * @return the corresponding value
*/
protected static final String find(CharChunk name) {
int pos = findClosest(name, ccCache, ccCache.length);
@@ -592,6 +606,10 @@ public class StringCache {
* Find an entry given its name in a sorted array of map elements.
* This will return the index for the closest inferior or equal item in the
* given array.
+ * @param name The name to find
+ * @param array The array in which to look
+ * @param len The effective length of the array
+ * @return the position of the best match
*/
protected static final int findClosest(CharChunk name, CharEntry[] array,
int len) {
diff --git a/java/org/apache/tomcat/util/buf/UDecoder.java b/java/org/apache/tomcat/util/buf/UDecoder.java
index 58a954d..9cd1b24 100644
--- a/java/org/apache/tomcat/util/buf/UDecoder.java
+++ b/java/org/apache/tomcat/util/buf/UDecoder.java
@@ -71,7 +71,11 @@ public final class UDecoder {
{
}
- /** URLDecode, will modify the source.
+ /**
+ * URLDecode, will modify the source.
+ * @param mb The URL encoded bytes
+ * @param query <code>true</code> if this is a query string
+ * @throws IOException Invalid %xx URL encoding
*/
public void convert( ByteChunk mb, boolean query )
throws IOException
@@ -129,7 +133,11 @@ public final class UDecoder {
// -------------------- Additional methods --------------------
// XXX What do we do about charset ????
- /** In-buffer processing - the buffer will be modified
+ /**
+ * In-buffer processing - the buffer will be modified.
+ * @param mb The URL encoded chars
+ * @param query <code>true</code> if this is a query string
+ * @throws IOException Invalid %xx URL encoding
*/
public void convert( CharChunk mb, boolean query )
throws IOException
@@ -183,7 +191,11 @@ public final class UDecoder {
mb.setEnd( idx );
}
- /** URLDecode, will modify the source
+ /**
+ * URLDecode, will modify the source
+ * @param mb The URL encoded String, bytes or chars
+ * @param query <code>true</code> if this is a query string
+ * @throws IOException Invalid %xx URL encoding
*/
public void convert(MessageBytes mb, boolean query)
throws IOException
@@ -212,8 +224,12 @@ public final class UDecoder {
}
}
- // XXX Old code, needs to be replaced !!!!
- //
+ /**
+ * %xx decoding of a string. FIXME: this is inefficient.
+ * @param str The URL encoded string
+ * @param query <code>true</code> if this is a query string
+ * @return the decoded string
+ */
public final String convert(String str, boolean query)
{
if (str == null) {
@@ -284,7 +300,7 @@ public final class UDecoder {
* servers. It is assumed the string is not a query string.
*
* @param str The url-encoded string
- *
+ * @return the decoded string
* @exception IllegalArgumentException if a '%' character is not followed
* by a valid 2-digit hexadecimal number
*/
@@ -300,6 +316,7 @@ public final class UDecoder {
* @param str The url-encoded string
* @param enc The encoding to use; if null, the default encoding is used. If
* an unsupported encoding is specified null will be returned
+ * @return the decoded string
* @exception IllegalArgumentException if a '%' character is not followed
* by a valid 2-digit hexadecimal number
*/
@@ -315,6 +332,7 @@ public final class UDecoder {
* @param enc The encoding to use; if null, the default encoding is used. If
* an unsupported encoding is specified null will be returned
* @param isQuery Is this a query string being processed
+ * @return the decoded string
* @exception IllegalArgumentException if a '%' character is not followed
* by a valid 2-digit hexadecimal number
*/
@@ -351,6 +369,7 @@ public final class UDecoder {
* @param enc The encoding to use; if null, the default encoding is used. If
* an unsupported encoding is specified null will be returned
* @param isQuery Is this a query string being processed
+ * @return the decoded string
* @exception IllegalArgumentException if a '%' character is not followed
* by a valid 2-digit hexadecimal number
*/
diff --git a/java/org/apache/tomcat/util/buf/UEncoder.java b/java/org/apache/tomcat/util/buf/UEncoder.java
index 2da1177..0f73684 100644
--- a/java/org/apache/tomcat/util/buf/UEncoder.java
+++ b/java/org/apache/tomcat/util/buf/UEncoder.java
@@ -17,6 +17,7 @@
package org.apache.tomcat.util.buf;
import java.io.IOException;
+import java.nio.charset.StandardCharsets;
import java.util.BitSet;
/**
@@ -55,44 +56,25 @@ public final class UEncoder {
private ByteChunk bb=null;
private CharChunk cb=null;
private CharChunk output=null;
- private final boolean readOnlySafeChars;
-
- private final String ENCODING = "UTF8";
-
- public UEncoder() {
- this.safeChars = initialSafeChars();
- readOnlySafeChars = false;
- }
/**
* Create a UEncoder with an unmodifiable safe character set.
- * <p>
- * Calls to {@link UEncoder#addSafeCharacter(char) addSafeCharacter(char)}
- * on instances created by this constructor will throw an
- * {@link IllegalStateException}.
*
- * @param safeCharsSet
- * safe characters for this encoder
+ * @param safeCharsSet safe characters for this encoder
*/
public UEncoder(SafeCharsSet safeCharsSet) {
this.safeChars = safeCharsSet.getSafeChars();
- readOnlySafeChars = true;
}
- public void addSafeCharacter( char c ) {
- if (readOnlySafeChars) {
- throw new IllegalStateException("UEncoders safeChararacters are read only");
- }
- safeChars.set( c );
- }
-
-
/**
* URL Encode string, using a specified encoding.
*
* @param s string to be encoded
* @param start the beginning index, inclusive
* @param end the ending index, exclusive
+ *
+ * @return A new CharChunk contained the URL encoded string
+ *
* @throws IOException If an I/O error occurs
*/
public CharChunk encodeURL(String s, int start, int end)
@@ -101,7 +83,7 @@ public final class UEncoder {
bb = new ByteChunk(8); // small enough.
cb = new CharChunk(2); // small enough.
output = new CharChunk(64); // small enough.
- c2b = new C2BConverter(ENCODING);
+ c2b = new C2BConverter(StandardCharsets.UTF_8);
} else {
bb.recycle();
cb.recycle();
diff --git a/java/org/apache/tomcat/util/codec/binary/Base64.java b/java/org/apache/tomcat/util/codec/binary/Base64.java
index d566e44..5496fed 100644
--- a/java/org/apache/tomcat/util/codec/binary/Base64.java
+++ b/java/org/apache/tomcat/util/codec/binary/Base64.java
@@ -111,13 +111,15 @@ public class Base64 extends BaseNCodec {
* http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/
*/
private static final byte[] DECODE_TABLE = {
- -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, 62, -1, 62, -1, 63, 52, 53, 54,
- 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4,
- 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
- 24, 25, -1, -1, -1, -1, 63, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34,
- 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51
+ // 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
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 10-1f
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, 62, -1, 63, // 20-2f + - /
+ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, // 30-3f 0-9
+ -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, // 40-4f A-O
+ 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
};
/**
@@ -437,17 +439,16 @@ public class Base64 extends BaseNCodec {
// We're done.
context.eof = true;
break;
- } else {
- if (b >= 0 && b < DECODE_TABLE.length) {
- final int result = DECODE_TABLE[b];
- if (result >= 0) {
- 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);
- buffer[context.pos++] = (byte) ((context.ibitWorkArea >> 8) & MASK_8BITS);
- buffer[context.pos++] = (byte) (context.ibitWorkArea & MASK_8BITS);
- }
+ }
+ if (b >= 0 && b < DECODE_TABLE.length) {
+ final int result = DECODE_TABLE[b];
+ if (result >= 0) {
+ 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);
+ buffer[context.pos++] = (byte) ((context.ibitWorkArea >> 8) & MASK_8BITS);
+ buffer[context.pos++] = (byte) (context.ibitWorkArea & MASK_8BITS);
}
}
}
diff --git a/java/org/apache/tomcat/util/codec/binary/BaseNCodec.java b/java/org/apache/tomcat/util/codec/binary/BaseNCodec.java
index e4b0121..75471de 100644
--- a/java/org/apache/tomcat/util/codec/binary/BaseNCodec.java
+++ b/java/org/apache/tomcat/util/codec/binary/BaseNCodec.java
@@ -415,16 +415,36 @@ public abstract class BaseNCodec implements BinaryEncoder, BinaryDecoder {
*
* @param pArray
* a byte array containing binary data
- * @return A byte array containing only the basen alphabetic character data
+ * @return A byte array containing only the base N alphabetic character data
*/
@Override
public byte[] encode(final byte[] pArray) {
if (pArray == null || pArray.length == 0) {
return pArray;
}
+ return encode(pArray, 0, pArray.length);
+ }
+
+ /**
+ * 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.
+ * @return A byte array containing only the base N alphabetic character data
+ * @since 1.11
+ */
+ public byte[] encode(final byte[] pArray, int offset, int length) {
+ if (pArray == null || pArray.length == 0) {
+ return pArray;
+ }
final Context context = new Context();
- encode(pArray, 0, pArray.length, context);
- encode(pArray, 0, EOF, context); // Notify encoder of EOF.
+ encode(pArray, offset, length, context);
+ encode(pArray, offset, EOF, context); // Notify encoder of EOF.
final byte[] buf = new byte[context.pos - context.readPos];
readResults(buf, 0, buf.length, context);
return buf;
diff --git a/java/org/apache/tomcat/util/collections/CaseInsensitiveKeyMap.java b/java/org/apache/tomcat/util/collections/CaseInsensitiveKeyMap.java
new file mode 100644
index 0000000..d33a6dd
--- /dev/null
+++ b/java/org/apache/tomcat/util/collections/CaseInsensitiveKeyMap.java
@@ -0,0 +1,208 @@
+/*
+ * 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.collections;
+
+import java.util.AbstractMap;
+import java.util.AbstractSet;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.tomcat.util.res.StringManager;
+
+/**
+ * A Map implementation that uses case-insensitive (using {@link
+ * Locale#ENGLISH}) strings as keys.
+ * <p>
+ * Keys must be instances of {@link String}. Note that this means that
+ * <code>null</code> keys are not permitted.
+ * <p>
+ * This implementation is not thread-safe.
+ *
+ * @param <V> Type of values placed in this Map.
+ */
+public class CaseInsensitiveKeyMap<V> extends AbstractMap<String,V> {
+
+ private static final StringManager sm =
+ StringManager.getManager(CaseInsensitiveKeyMap.class);
+
+ private final Map<Key,V> map = new HashMap<>();
+
+
+ @Override
+ public V get(Object key) {
+ return map.get(Key.getInstance(key));
+ }
+
+
+ @Override
+ public V put(String key, V value) {
+ Key caseInsensitiveKey = Key.getInstance(key);
+ if (caseInsensitiveKey == null) {
+ throw new NullPointerException(sm.getString("caseInsensitiveKeyMap.nullKey"));
+ }
+ return map.put(caseInsensitiveKey, value);
+ }
+
+
+ /**
+ * {@inheritDoc}
+ * <p>
+ * <b>Use this method with caution</b>. 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<? extends String, ? extends V> m) {
+ super.putAll(m);
+ }
+
+
+ @Override
+ public boolean containsKey(Object key) {
+ return map.containsKey(Key.getInstance(key));
+ }
+
+
+ @Override
+ public V remove(Object key) {
+ return map.remove(Key.getInstance(key));
+ }
+
+
+ @Override
+ public Set<Entry<String, V>> entrySet() {
+ return new EntrySet<>(map.entrySet());
+ }
+
+
+ private static class EntrySet<V> extends AbstractSet<Entry<String,V>> {
+
+ private final Set<Entry<Key,V>> entrySet;
+
+ public EntrySet(Set<Map.Entry<Key,V>> entrySet) {
+ this.entrySet = entrySet;
+ }
+
+ @Override
+ public Iterator<Entry<String,V>> iterator() {
+ return new EntryIterator<>(entrySet.iterator());
+ }
+
+ @Override
+ public int size() {
+ return entrySet.size();
+ }
+ }
+
+
+ private static class EntryIterator<V> implements Iterator<Entry<String,V>> {
+
+ private final Iterator<Entry<Key,V>> iterator;
+
+ public EntryIterator(Iterator<Entry<Key,V>> iterator) {
+ this.iterator = iterator;
+ }
+
+ @Override
+ public boolean hasNext() {
+ return iterator.hasNext();
+ }
+
+ @Override
+ public Entry<String,V> next() {
+ Entry<Key,V> entry = iterator.next();
+ return new EntryImpl<>(entry.getKey().getKey(), entry.getValue());
+ }
+
+ @Override
+ public void remove() {
+ iterator.remove();
+ }
+ }
+
+
+ private static class EntryImpl<V> implements Entry<String,V> {
+
+ private final String key;
+ private final V value;
+
+ public EntryImpl(String key, V value) {
+ this.key = key;
+ this.value = value;
+ }
+
+ @Override
+ public String getKey() {
+ return key;
+ }
+
+ @Override
+ public V getValue() {
+ return value;
+ }
+
+ @Override
+ public V setValue(V value) {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ private static class Key {
+
+ private final String key;
+ private final String lcKey;
+
+ private Key(String key) {
+ this.key = key;
+ this.lcKey = key.toLowerCase(Locale.ENGLISH);
+ }
+
+ public String getKey() {
+ return key;
+ }
+
+ @Override
+ public int hashCode() {
+ return lcKey.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ Key other = (Key) obj;
+ return lcKey.equals(other.lcKey);
+ }
+
+ public static Key getInstance(Object o) {
+ if (o instanceof String) {
+ return new Key((String) o);
+ }
+ return null;
+ }
+ }
+}
diff --git a/java/org/apache/tomcat/util/collections/ManagedConcurrentWeakHashMap.java b/java/org/apache/tomcat/util/collections/ManagedConcurrentWeakHashMap.java
index 0d2e758..5ccad11 100644
--- a/java/org/apache/tomcat/util/collections/ManagedConcurrentWeakHashMap.java
+++ b/java/org/apache/tomcat/util/collections/ManagedConcurrentWeakHashMap.java
@@ -24,6 +24,7 @@ import java.util.AbstractSet;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
+import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
@@ -33,6 +34,9 @@ import java.util.concurrent.ConcurrentMap;
* <code>WeakHashMap</code> 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-<code>null</code>.
+ *
+ * @param <K> The type of keys used with the Map instance
+ * @param <V> The type of values used with the Map instance
*/
public class ManagedConcurrentWeakHashMap<K, V> extends AbstractMap<K, V> implements
ConcurrentMap<K, V> {
@@ -119,12 +123,6 @@ public class ManagedConcurrentWeakHashMap<K, V> extends AbstractMap<K, V> implem
return new Key(key, null);
}
- private static void checkNotNull(Object value) {
- if (value == null) {
- throw new NullPointerException();
- }
- }
-
@Override
public int size() {
return map.size();
@@ -161,7 +159,7 @@ public class ManagedConcurrentWeakHashMap<K, V> extends AbstractMap<K, V> implem
@Override
public V put(K key, V value) {
- checkNotNull(value);
+ Objects.requireNonNull(value);
return map.put(createStoreKey(key), value);
}
@@ -180,7 +178,7 @@ public class ManagedConcurrentWeakHashMap<K, V> extends AbstractMap<K, V> implem
@Override
public V putIfAbsent(K key, V value) {
- checkNotNull(value);
+ Objects.requireNonNull(value);
Key storeKey = createStoreKey(key);
V oldValue = map.putIfAbsent(storeKey, value);
if (oldValue != null) { // ack that key has not been stored
@@ -199,13 +197,13 @@ public class ManagedConcurrentWeakHashMap<K, V> extends AbstractMap<K, V> implem
@Override
public boolean replace(K key, V oldValue, V newValue) {
- checkNotNull(newValue);
+ Objects.requireNonNull(newValue);
return map.replace(createLookupKey(key), oldValue, newValue);
}
@Override
public V replace(K key, V value) {
- checkNotNull(value);
+ Objects.requireNonNull(value);
return map.replace(createLookupKey(key), value);
}
@@ -256,7 +254,7 @@ public class ManagedConcurrentWeakHashMap<K, V> extends AbstractMap<K, V> implem
@Override
public V setValue(V value) {
- checkNotNull(value);
+ Objects.requireNonNull(value);
return en.setValue(value);
}
};
diff --git a/java/org/apache/tomcat/util/collections/SynchronizedQueue.java b/java/org/apache/tomcat/util/collections/SynchronizedQueue.java
index 95e8bcd..44c9352 100644
--- a/java/org/apache/tomcat/util/collections/SynchronizedQueue.java
+++ b/java/org/apache/tomcat/util/collections/SynchronizedQueue.java
@@ -22,6 +22,8 @@ package org.apache.tomcat.util.collections;
* 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 <T> The type of object managed by this queue
*/
public class SynchronizedQueue<T> {
diff --git a/java/org/apache/tomcat/util/collections/SynchronizedStack.java b/java/org/apache/tomcat/util/collections/SynchronizedStack.java
index d2facac..1af00ce 100644
--- a/java/org/apache/tomcat/util/collections/SynchronizedStack.java
+++ b/java/org/apache/tomcat/util/collections/SynchronizedStack.java
@@ -22,6 +22,8 @@ package org.apache.tomcat.util.collections;
* 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 <T> The type of object managed by this stack
*/
public class SynchronizedStack<T> {
@@ -44,7 +46,11 @@ public class SynchronizedStack<T> {
}
public SynchronizedStack(int size, int limit) {
- this.size = size;
+ if (limit > -1 && size > limit) {
+ this.size = limit;
+ } else {
+ this.size = size;
+ }
this.limit = limit;
stack = new Object[size];
}
diff --git a/java/org/apache/tomcat/util/compat/Jre8Compat.java b/java/org/apache/tomcat/util/compat/Jre8Compat.java
index a71fe66..f383261 100644
--- a/java/org/apache/tomcat/util/compat/Jre8Compat.java
+++ b/java/org/apache/tomcat/util/compat/Jre8Compat.java
@@ -21,25 +21,18 @@ import java.lang.reflect.Method;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLParameters;
-import javax.net.ssl.SSLServerSocket;
class Jre8Compat extends JreCompat {
- private static final Method getSSLParametersMethod;
private static final Method setUseCipherSuitesOrderMethod;
- private static final Method setSSLParametersMethod;
static {
Method m1 = null;
- Method m2 = null;
- Method m3 = null;
try {
// Get this class first since it is Java 8+ only
- Class<?> c2 = Class.forName("javax.net.ssl.SSLParameters");
- m1 = SSLServerSocket.class.getMethod("getSSLParameters");
- m2 = c2.getMethod("setUseCipherSuitesOrder", boolean.class);
- m3 = SSLServerSocket.class.getMethod("setSSLParameters", c2);
+ Class<?> c1 = Class.forName("javax.net.ssl.SSLParameters");
+ m1 = c1.getMethod("setUseCipherSuitesOrder", boolean.class);
} catch (SecurityException e) {
// Should never happen
} catch (NoSuchMethodException e) {
@@ -47,9 +40,7 @@ class Jre8Compat extends JreCompat {
} catch (ClassNotFoundException e) {
// Should never happen
}
- getSSLParametersMethod = m1;
- setUseCipherSuitesOrderMethod = m2;
- setSSLParametersMethod = m3;
+ setUseCipherSuitesOrderMethod = m1;
}
@@ -59,30 +50,12 @@ class Jre8Compat extends JreCompat {
@Override
- public void setUseServerCipherSuitesOrder(SSLServerSocket socket,
- boolean useCipherSuitesOrder) {
- try {
- Object sslParameters = getSSLParametersMethod.invoke(socket);
- setUseCipherSuitesOrderMethod.invoke(
- sslParameters, Boolean.valueOf(useCipherSuitesOrder));
- setSSLParametersMethod.invoke(socket, sslParameters);
- return;
- } catch (IllegalArgumentException e) {
- throw new UnsupportedOperationException(e);
- } catch (IllegalAccessException e) {
- throw new UnsupportedOperationException(e);
- } catch (InvocationTargetException e) {
- throw new UnsupportedOperationException(e);
- }
- }
-
-
- @Override
public void setUseServerCipherSuitesOrder(SSLEngine engine,
boolean useCipherSuitesOrder) {
SSLParameters sslParameters = engine.getSSLParameters();
try {
- setUseCipherSuitesOrderMethod.invoke(sslParameters, Boolean.valueOf(useCipherSuitesOrder));
+ setUseCipherSuitesOrderMethod.invoke(sslParameters,
+ Boolean.valueOf(useCipherSuitesOrder));
engine.setSSLParameters(sslParameters);
} catch (IllegalArgumentException e) {
throw new UnsupportedOperationException(e);
diff --git a/java/org/apache/tomcat/util/compat/JreCompat.java b/java/org/apache/tomcat/util/compat/JreCompat.java
index 0c5ae8d..d9f98ae 100644
--- a/java/org/apache/tomcat/util/compat/JreCompat.java
+++ b/java/org/apache/tomcat/util/compat/JreCompat.java
@@ -17,7 +17,6 @@
package org.apache.tomcat.util.compat;
import javax.net.ssl.SSLEngine;
-import javax.net.ssl.SSLServerSocket;
import org.apache.tomcat.util.res.StringManager;
@@ -69,15 +68,7 @@ public class JreCompat {
@SuppressWarnings("unused")
- public void setUseServerCipherSuitesOrder(SSLServerSocket socket,
- boolean useCipherSuitesOrder) {
- throw new UnsupportedOperationException(sm.getString("jreCompat.noServerCipherSuiteOrder"));
- }
-
-
- @SuppressWarnings("unused")
- public void setUseServerCipherSuitesOrder(SSLEngine engine,
- boolean useCipherSuitesOrder) {
+ public void setUseServerCipherSuitesOrder(SSLEngine engine, boolean useCipherSuitesOrder) {
throw new UnsupportedOperationException(sm.getString("jreCompat.noServerCipherSuiteOrder"));
}
diff --git a/java/org/apache/tomcat/util/descriptor/DigesterFactory.java b/java/org/apache/tomcat/util/descriptor/DigesterFactory.java
index 7a49a5c..19bb141 100644
--- a/java/org/apache/tomcat/util/descriptor/DigesterFactory.java
+++ b/java/org/apache/tomcat/util/descriptor/DigesterFactory.java
@@ -157,6 +157,7 @@ public class DigesterFactory {
* @param xmlNamespaceAware turn on/off namespace validation
* @param rule an instance of <code>RuleSet</code> 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,
diff --git a/java/org/apache/tomcat/util/descriptor/LocalResolver.java b/java/org/apache/tomcat/util/descriptor/LocalResolver.java
index c54131b..5802c1c 100644
--- a/java/org/apache/tomcat/util/descriptor/LocalResolver.java
+++ b/java/org/apache/tomcat/util/descriptor/LocalResolver.java
@@ -40,7 +40,7 @@ public class LocalResolver implements EntityResolver2 {
private static final String[] JAVA_EE_NAMESPACES = {
XmlIdentifiers.JAVAEE_1_4_NS,
XmlIdentifiers.JAVAEE_5_NS,
- XmlIdentifiers.JAVAEE_7_NS };
+ XmlIdentifiers.JAVAEE_7_NS};
private final Map<String,String> publicIds;
diff --git a/java/org/apache/tomcat/util/descriptor/tld/ImplicitTldRuleSet.java b/java/org/apache/tomcat/util/descriptor/tld/ImplicitTldRuleSet.java
index faaed3b..7502ebd 100644
--- a/java/org/apache/tomcat/util/descriptor/tld/ImplicitTldRuleSet.java
+++ b/java/org/apache/tomcat/util/descriptor/tld/ImplicitTldRuleSet.java
@@ -29,8 +29,7 @@ import org.xml.sax.Attributes;
*/
public class ImplicitTldRuleSet extends RuleSetBase {
- private static final StringManager sm = StringManager.getManager(
- ImplicitTldRuleSet.class.getPackage().getName());
+ private static final StringManager sm = StringManager.getManager(ImplicitTldRuleSet.class);
private static final String PREFIX = "taglib";
private static final String VALIDATOR_PREFIX = PREFIX + "/validator";
diff --git a/java/org/apache/tomcat/util/descriptor/tld/TldResourcePath.java b/java/org/apache/tomcat/util/descriptor/tld/TldResourcePath.java
index a4e33a9..de1b372 100644
--- a/java/org/apache/tomcat/util/descriptor/tld/TldResourcePath.java
+++ b/java/org/apache/tomcat/util/descriptor/tld/TldResourcePath.java
@@ -127,14 +127,6 @@ public class TldResourcePath {
}
}
- /**
- * @deprecated Renamed, as it is not just a getter method. Use {@link #openJar()}.
- */
- @Deprecated
- public Jar getJar() throws IOException {
- return openJar();
- }
-
public Jar openJar() throws IOException {
if (entryName == null) {
return null;
diff --git a/java/org/apache/tomcat/util/descriptor/web/ContextHandler.java b/java/org/apache/tomcat/util/descriptor/web/ContextHandler.java
index cfab02f..8c303f8 100644
--- a/java/org/apache/tomcat/util/descriptor/web/ContextHandler.java
+++ b/java/org/apache/tomcat/util/descriptor/web/ContextHandler.java
@@ -73,6 +73,8 @@ public class ContextHandler extends ResourceBase {
/**
* Set a configured property.
+ * @param name The property name
+ * @param value The property value
*/
public void setProperty(String name, String value) {
this.setProperty(name, (Object) value);
diff --git a/java/org/apache/tomcat/util/descriptor/web/ContextService.java b/java/org/apache/tomcat/util/descriptor/web/ContextService.java
index fec36a6..45c6ccb 100644
--- a/java/org/apache/tomcat/util/descriptor/web/ContextService.java
+++ b/java/org/apache/tomcat/util/descriptor/web/ContextService.java
@@ -165,7 +165,7 @@ public class ContextService extends ResourceBase {
* 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<String> getServiceendpoints() {
return this.listProperties();
diff --git a/java/org/apache/tomcat/util/descriptor/web/ContextTransaction.java b/java/org/apache/tomcat/util/descriptor/web/ContextTransaction.java
index eee52dd..ee1f605 100644
--- a/java/org/apache/tomcat/util/descriptor/web/ContextTransaction.java
+++ b/java/org/apache/tomcat/util/descriptor/web/ContextTransaction.java
@@ -42,7 +42,8 @@ public class ContextTransaction implements Serializable {
private final HashMap<String, Object> properties = new HashMap<>();
/**
- * Return a configured property.
+ * @param name The property name
+ * @return a configured property.
*/
public Object getProperty(String name) {
return properties.get(name);
@@ -50,13 +51,16 @@ public class ContextTransaction implements Serializable {
/**
* Set a configured property.
+ * @param name The property name
+ * @param value The property value
*/
public void setProperty(String name, Object value) {
properties.put(name, value);
}
/**
- * remove a configured property.
+ * Remove a configured property.
+ * @param name The property name
*/
public void removeProperty(String name) {
properties.remove(name);
@@ -64,6 +68,7 @@ public class ContextTransaction implements Serializable {
/**
* List properties.
+ * @return the property names iterator
*/
public Iterator<String> listProperties() {
return properties.keySet().iterator();
diff --git a/java/org/apache/tomcat/util/descriptor/web/ErrorPage.java b/java/org/apache/tomcat/util/descriptor/web/ErrorPage.java
index a75617b..731f7e1 100644
--- a/java/org/apache/tomcat/util/descriptor/web/ErrorPage.java
+++ b/java/org/apache/tomcat/util/descriptor/web/ErrorPage.java
@@ -57,7 +57,7 @@ public class ErrorPage implements Serializable {
/**
- * Return the error code.
+ * @return the error code.
*/
public int getErrorCode() {
@@ -94,7 +94,7 @@ public class ErrorPage implements Serializable {
/**
- * Return the exception type.
+ * @return the exception type.
*/
public String getExceptionType() {
@@ -116,7 +116,7 @@ public class ErrorPage implements Serializable {
/**
- * Return the location.
+ * @return the location.
*/
public String getLocation() {
diff --git a/java/org/apache/tomcat/util/descriptor/web/FilterMap.java b/java/org/apache/tomcat/util/descriptor/web/FilterMap.java
index a2836c6..4e9b617 100644
--- a/java/org/apache/tomcat/util/descriptor/web/FilterMap.java
+++ b/java/org/apache/tomcat/util/descriptor/web/FilterMap.java
@@ -141,9 +141,10 @@ public class FilterMap extends XmlEncodingBase implements Serializable {
}
/**
- *
* 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);
diff --git a/java/org/apache/tomcat/util/descriptor/web/FragmentJarScannerCallback.java b/java/org/apache/tomcat/util/descriptor/web/FragmentJarScannerCallback.java
index 8a645e7..d57f2a4 100644
--- a/java/org/apache/tomcat/util/descriptor/web/FragmentJarScannerCallback.java
+++ b/java/org/apache/tomcat/util/descriptor/web/FragmentJarScannerCallback.java
@@ -20,14 +20,12 @@ import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
-import java.net.JarURLConnection;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import org.apache.tomcat.Jar;
import org.apache.tomcat.JarScannerCallback;
-import org.apache.tomcat.util.scan.JarFactory;
import org.xml.sax.InputSource;
/**
@@ -50,12 +48,6 @@ public class FragmentJarScannerCallback implements JarScannerCallback {
this.parseRequired = parseRequired;
}
- @Override
- public void scan(JarURLConnection jarConn, String webappPath, boolean isWebapp)
- throws IOException {
- scan(JarFactory.newInstance(jarConn.getURL()), webappPath, isWebapp);
- }
-
@Override
public void scan(Jar jar, String webappPath, boolean isWebapp) throws IOException {
diff --git a/java/org/apache/tomcat/util/descriptor/web/ResourceBase.java b/java/org/apache/tomcat/util/descriptor/web/ResourceBase.java
index 231c507..0d447b1 100644
--- a/java/org/apache/tomcat/util/descriptor/web/ResourceBase.java
+++ b/java/org/apache/tomcat/util/descriptor/web/ResourceBase.java
@@ -86,7 +86,8 @@ public class ResourceBase implements Serializable, Injectable {
private final HashMap<String, Object> properties = new HashMap<>();
/**
- * Return a configured property.
+ * @param name The property name
+ * @return a configured property.
*/
public Object getProperty(String name) {
return properties.get(name);
@@ -94,6 +95,8 @@ public class ResourceBase implements Serializable, Injectable {
/**
* Set a configured property.
+ * @param name The property name
+ * @param value The property value
*/
public void setProperty(String name, Object value) {
properties.put(name, value);
@@ -101,6 +104,7 @@ public class ResourceBase implements Serializable, Injectable {
/**
* Remove a configured property.
+ * @param name The property name
*/
public void removeProperty(String name) {
properties.remove(name);
@@ -108,6 +112,7 @@ public class ResourceBase implements Serializable, Injectable {
/**
* List properties.
+ * @return the property names iterator
*/
public Iterator<String> listProperties() {
return properties.keySet().iterator();
diff --git a/java/org/apache/tomcat/util/descriptor/web/SecurityCollection.java b/java/org/apache/tomcat/util/descriptor/web/SecurityCollection.java
index 34320f6..0117543 100644
--- a/java/org/apache/tomcat/util/descriptor/web/SecurityCollection.java
+++ b/java/org/apache/tomcat/util/descriptor/web/SecurityCollection.java
@@ -127,7 +127,7 @@ public class SecurityCollection implements Serializable {
/**
- * Return the description of this web resource collection.
+ * @return the description of this web resource collection.
*/
public String getDescription() {
@@ -149,7 +149,7 @@ public class SecurityCollection implements Serializable {
/**
- * Return the name of this web resource collection.
+ * @return the name of this web resource collection.
*/
public String getName() {
@@ -171,7 +171,7 @@ public class SecurityCollection implements Serializable {
/**
- * Return if this constraint was defined in a deployment descriptor.
+ * @return if this constraint was defined in a deployment descriptor.
*/
public boolean isFromDescriptor() {
return isFromDescriptor;
@@ -180,6 +180,7 @@ public class SecurityCollection implements Serializable {
/**
* Set if this constraint was defined in a deployment descriptor.
+ * @param isFromDescriptor <code>true</code> was declared in a descriptor
*/
public void setFromDescriptor(boolean isFromDescriptor) {
this.isFromDescriptor = isFromDescriptor;
@@ -192,6 +193,7 @@ public class SecurityCollection implements Serializable {
/**
* Add an HTTP request method to be explicitly part of this web resource
* collection.
+ * @param method The method
*/
public void addMethod(String method) {
@@ -209,6 +211,7 @@ public class SecurityCollection implements Serializable {
/**
* Add an HTTP request method to the methods explicitly excluded from this
* web resource collection.
+ * @param method The method
*/
public void addOmittedMethod(String method) {
if (method == null)
@@ -222,6 +225,7 @@ public class SecurityCollection implements Serializable {
/**
* 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, "UTF-8"));
@@ -242,15 +246,15 @@ public class SecurityCollection implements Serializable {
/**
- * Return <code>true</code> if the specified HTTP request method is
- * part of this web resource collection.
- *
+ * Check if the collection applies to the specified method.
* @param method Request method to check
+ * @return <code>true</code> if the specified HTTP request method is
+ * part of this web resource collection.
*/
public boolean findMethod(String method) {
if (methods.length == 0 && omittedMethods.length == 0)
- return (true);
+ return true;
if (methods.length > 0) {
for (int i = 0; i < methods.length; i++) {
if (methods[i].equals(method))
@@ -269,7 +273,7 @@ public class SecurityCollection implements Serializable {
/**
- * Return the set of HTTP request methods that are part of this web
+ * @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.
*/
@@ -281,7 +285,7 @@ public class SecurityCollection implements Serializable {
/**
- * Return the set of HTTP request methods that are explicitly excluded from
+ * @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.
*/
@@ -296,20 +300,21 @@ public class SecurityCollection implements Serializable {
* Is the specified pattern part of this web resource collection?
*
* @param pattern Pattern to be compared
+ * @return <code>true</code> if the pattern is part of the collection
*/
public boolean findPattern(String pattern) {
for (int i = 0; i < patterns.length; i++) {
if (patterns[i].equals(pattern))
- return (true);
+ return true;
}
- return (false);
+ return false;
}
/**
- * Return the set of URL patterns that are part of this web resource
+ * @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.
*/
diff --git a/java/org/apache/tomcat/util/descriptor/web/SecurityConstraint.java b/java/org/apache/tomcat/util/descriptor/web/SecurityConstraint.java
index 0c19422..e0b7f4d 100644
--- a/java/org/apache/tomcat/util/descriptor/web/SecurityConstraint.java
+++ b/java/org/apache/tomcat/util/descriptor/web/SecurityConstraint.java
@@ -131,6 +131,7 @@ public class SecurityConstraint extends XmlEncodingBase implements Serializable
/**
* Was the "all roles" wildcard included in this authentication
* constraint?
+ * @return <code>true</code> if all roles
*/
public boolean getAllRoles() {
@@ -142,6 +143,7 @@ public class SecurityConstraint extends XmlEncodingBase implements Serializable
/**
* Was the "all authenticated users" wildcard included in this
* authentication constraint?
+ * @return <code>true</code> if all authenticated users
*/
public boolean getAuthenticatedUsers() {
return this.authenticatedUsers;
@@ -151,6 +153,7 @@ public class SecurityConstraint extends XmlEncodingBase implements Serializable
/**
* Return the authorization constraint present flag for this security
* constraint.
+ * @return <code>true</code> if this needs authorization
*/
public boolean getAuthConstraint() {
@@ -162,6 +165,7 @@ public class SecurityConstraint extends XmlEncodingBase implements Serializable
/**
* Set the authorization constraint present flag for this security
* constraint.
+ * @param authConstraint The new value
*/
public void setAuthConstraint(boolean authConstraint) {
@@ -171,7 +175,7 @@ public class SecurityConstraint extends XmlEncodingBase implements Serializable
/**
- * Return the display name of this security constraint.
+ * @return the display name of this security constraint.
*/
public String getDisplayName() {
@@ -182,6 +186,7 @@ public class SecurityConstraint extends XmlEncodingBase implements Serializable
/**
* Set the display name of this security constraint.
+ * @param displayName The new value
*/
public void setDisplayName(String displayName) {
@@ -192,6 +197,7 @@ public class SecurityConstraint extends XmlEncodingBase implements Serializable
/**
* Return the user data constraint for this security constraint.
+ * @return the user constraint
*/
public String getUserConstraint() {
@@ -288,20 +294,21 @@ public class SecurityConstraint extends XmlEncodingBase implements Serializable
/**
- * Return <code>true</code> if the specified role is permitted access to
- * the resources protected by this security constraint.
+ * Check a role.
*
* @param role Role name to be checked
+ * @return <code>true</code> if the specified role is permitted access to
+ * the resources protected by this security constraint.
*/
public boolean findAuthRole(String role) {
if (role == null)
- return (false);
+ return false;
for (int i = 0; i < authRoles.length; i++) {
if (role.equals(authRoles[i]))
- return (true);
+ return true;
}
- return (false);
+ return false;
}
@@ -311,6 +318,7 @@ public class SecurityConstraint extends XmlEncodingBase implements Serializable
* 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() {
@@ -324,6 +332,7 @@ public class SecurityConstraint extends XmlEncodingBase implements Serializable
* otherwise, return <code>null</code>.
*
* @param name Web resource collection name to return
+ * @return the collection
*/
public SecurityCollection findCollection(String name) {
@@ -342,6 +351,7 @@ public class SecurityConstraint extends XmlEncodingBase implements Serializable
* 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() {
@@ -351,17 +361,17 @@ public class SecurityConstraint extends XmlEncodingBase implements Serializable
/**
- * Return <code>true</code> if the specified context-relative URI (and
- * associated HTTP method) are protected by this security constraint.
- *
+ * Check if the contraint applies to a URI and method.
* @param uri Context-relative URI to check
* @param method Request method being used
+ * @return <code>true</code> if the specified context-relative URI (and
+ * associated HTTP method) are protected by this security constraint.
*/
public boolean included(String uri, String method) {
// We cannot match without a valid request method
if (method == null)
- return (false);
+ return false;
// Check all of the collections included in this constraint
for (int i = 0; i < collections.length; i++) {
@@ -370,12 +380,12 @@ public class SecurityConstraint extends XmlEncodingBase implements Serializable
String patterns[] = collections[i].findPatterns();
for (int j = 0; j < patterns.length; j++) {
if (matchPattern(uri, patterns[j]))
- return (true);
+ return true;
}
}
// No collection included in this constraint matches this request
- return (false);
+ return false;
}
@@ -491,24 +501,24 @@ public class SecurityConstraint extends XmlEncodingBase implements Serializable
// Check for exact match
if (path.equals(pattern))
- return (true);
+ return true;
// 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 "/"
+ return true; // "/*" is the same as "/"
if (path.endsWith("/"))
path = path.substring(0, path.length() - 1);
while (true) {
if (pattern.equals(path))
- return (true);
+ return true;
int slash = path.lastIndexOf('/');
if (slash <= 0)
break;
path = path.substring(0, slash);
}
- return (false);
+ return false;
}
// Check for suffix matching
@@ -517,16 +527,16 @@ public class SecurityConstraint extends XmlEncodingBase implements Serializable
int period = path.lastIndexOf('.');
if ((slash >= 0) && (period > slash) &&
path.endsWith(pattern.substring(1))) {
- return (true);
+ return true;
}
- return (false);
+ return false;
}
// Check for universal mapping
if (pattern.equals("/"))
- return (true);
+ return true;
- return (false);
+ return false;
}
@@ -717,32 +727,8 @@ public class SecurityConstraint extends XmlEncodingBase implements Serializable
// pattern is fully covered.
omittedMethods.removeAll(methods);
- if (omittedMethods.size() > 0) {
- 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()));
- SecurityCollection collection = new SecurityCollection();
- for (String method : omittedMethods) {
- collection.addMethod(method);
- }
- collection.addPattern(pattern);
- collection.setName("deny-uncovered-http-methods");
- SecurityConstraint constraint = new SecurityConstraint();
- constraint.setAuthConstraint(true);
- constraint.addCollection(collection);
- newConstraints.add(constraint);
- } else {
- log.error(sm.getString(
- "securityConstraint.uncoveredHttpOmittedMethod",
- pattern, msg.toString().trim()));
- }
- }
+ handleOmittedMethods(omittedMethods, pattern, denyUncoveredHttpMethods,
+ newConstraints, log);
}
for (Map.Entry<String, Set<String>> entry :
urlOmittedMethodMap.entrySet()) {
@@ -752,37 +738,41 @@ public class SecurityConstraint extends XmlEncodingBase implements Serializable
continue;
}
- Set<String> omittedMethods = entry.getValue();
+ handleOmittedMethods(entry.getValue(), pattern, denyUncoveredHttpMethods,
+ newConstraints, log);
+ }
- if (omittedMethods.size() > 0) {
- StringBuilder msg = new StringBuilder();
+ return newConstraints.toArray(new SecurityConstraint[newConstraints.size()]);
+ }
+
+
+ private static void handleOmittedMethods(Set<String> omittedMethods, String pattern,
+ boolean denyUncoveredHttpMethods, List<SecurityConstraint> newConstraints, Log log) {
+ if (omittedMethods.size() > 0) {
+ 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()));
+ SecurityCollection collection = new SecurityCollection();
for (String method : omittedMethods) {
- msg.append(method);
- msg.append(' ');
- }
- if (denyUncoveredHttpMethods) {
- log.info(sm.getString(
- "securityConstraint.uncoveredHttpOmittedMethodFix",
- pattern, msg.toString().trim()));
- SecurityCollection collection = new SecurityCollection();
- for (String method : omittedMethods) {
- collection.addMethod(method);
- }
- collection.addPatternDecoded(pattern);
- collection.setName("deny-uncovered-http-methods");
- SecurityConstraint constraint = new SecurityConstraint();
- constraint.setAuthConstraint(true);
- constraint.addCollection(collection);
- newConstraints.add(constraint);
- } else {
- log.error(sm.getString(
- "securityConstraint.uncoveredHttpOmittedMethod",
- pattern, msg.toString().trim()));
+ collection.addMethod(method);
}
+ collection.addPatternDecoded(pattern);
+ collection.setName("deny-uncovered-http-methods");
+ SecurityConstraint constraint = new SecurityConstraint();
+ constraint.setAuthConstraint(true);
+ constraint.addCollection(collection);
+ newConstraints.add(constraint);
+ } else {
+ log.error(sm.getString(
+ "securityConstraint.uncoveredHttpOmittedMethod",
+ pattern, msg.toString().trim()));
}
}
-
- return newConstraints.toArray(
- new SecurityConstraint[newConstraints.size()]);
}
}
diff --git a/java/org/apache/tomcat/util/descriptor/web/ServletDef.java b/java/org/apache/tomcat/util/descriptor/web/ServletDef.java
index 4883ae7..aa1adf0 100644
--- a/java/org/apache/tomcat/util/descriptor/web/ServletDef.java
+++ b/java/org/apache/tomcat/util/descriptor/web/ServletDef.java
@@ -212,6 +212,7 @@ public class ServletDef implements Serializable {
/**
* 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) {
securityRoleRefs.add(securityRoleRef);
diff --git a/java/org/apache/tomcat/util/descriptor/web/WebRuleSet.java b/java/org/apache/tomcat/util/descriptor/web/WebRuleSet.java
index a21db87..70ef763 100644
--- a/java/org/apache/tomcat/util/descriptor/web/WebRuleSet.java
+++ b/java/org/apache/tomcat/util/descriptor/web/WebRuleSet.java
@@ -118,6 +118,7 @@ public class WebRuleSet extends RuleSetBase {
/**
* Construct an instance of this <code>RuleSet</code> with the default
* matching pattern prefix.
+ * @param fragment <code>true</code> if this is a web fragment
*/
public WebRuleSet(boolean fragment) {
@@ -132,6 +133,7 @@ public class WebRuleSet extends RuleSetBase {
*
* @param prefix Prefix for matching pattern rules (including the
* trailing slash character)
+ * @param fragment <code>true</code> if this is a web fragment
*/
public WebRuleSet(String prefix, boolean fragment) {
diff --git a/java/org/apache/tomcat/util/descriptor/web/WebXml.java b/java/org/apache/tomcat/util/descriptor/web/WebXml.java
index ece77c4..7322d33 100644
--- a/java/org/apache/tomcat/util/descriptor/web/WebXml.java
+++ b/java/org/apache/tomcat/util/descriptor/web/WebXml.java
@@ -39,6 +39,8 @@ import javax.servlet.descriptor.JspConfigDescriptor;
import javax.servlet.descriptor.JspPropertyGroupDescriptor;
import javax.servlet.descriptor.TaglibDescriptor;
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.buf.UDecoder;
import org.apache.tomcat.util.descriptor.XmlIdentifiers;
import org.apache.tomcat.util.digester.DocumentProperties;
@@ -60,12 +62,13 @@ public class WebXml extends XmlEncodingBase implements DocumentProperties.Encodi
private static final StringManager sm =
StringManager.getManager(Constants.PACKAGE_NAME);
- private static final org.apache.juli.logging.Log log=
- org.apache.juli.logging.LogFactory.getLog(WebXml.class);
+ private static final Log log = LogFactory.getLog(WebXml.class);
- // 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;
@@ -74,8 +77,10 @@ public class WebXml extends XmlEncodingBase implements DocumentProperties.Encodi
this.overridable = overridable;
}
- // web.xml only elements
- // Absolute Ordering
+ /**
+ * web.xml only elements
+ * Absolute Ordering
+ */
private Set<String> absoluteOrdering = null;
public void createAbsoluteOrdering() {
if (absoluteOrdering == null) {
@@ -94,8 +99,10 @@ public class WebXml extends XmlEncodingBase implements DocumentProperties.Encodi
return absoluteOrdering;
}
- // web-fragment.xml only elements
- // Relative ordering
+ /**
+ * web-fragment.xml only elements
+ * Relative ordering
+ */
private final Set<String> after = new LinkedHashSet<>();
public void addAfterOrdering(String fragmentName) {
after.add(fragmentName);
@@ -207,7 +214,7 @@ public class WebXml extends XmlEncodingBase implements DocumentProperties.Encodi
}
// Derived major and minor version attributes
- // Default to 3.1 until we know otherwise
+ // Default to 3,1
private int majorVersion = 3;
private int minorVersion = 1;
public int getMajorVersion() { return majorVersion; }
@@ -342,6 +349,8 @@ public class WebXml extends XmlEncodingBase implements DocumentProperties.Encodi
/**
* When merging/parsing web.xml files into this web.xml should the current
* set be completely replaced?
+ * @param replaceWelcomeFiles <code>true</code> to replace welcome files
+ * rather than add to the list
*/
public void setReplaceWelcomeFiles(boolean replaceWelcomeFiles) {
this.replaceWelcomeFiles = replaceWelcomeFiles;
@@ -349,6 +358,7 @@ public class WebXml extends XmlEncodingBase implements DocumentProperties.Encodi
/**
* 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 <code>true</code> to add welcome files
*/
public void setAlwaysAddWelcomeFiles(boolean alwaysAddWelcomeFiles) {
this.alwaysAddWelcomeFiles = alwaysAddWelcomeFiles;
diff --git a/java/org/apache/tomcat/util/descriptor/web/mbeans-descriptors.xml b/java/org/apache/tomcat/util/descriptor/web/mbeans-descriptors.xml
index 820fb04..0fef1f8 100644
--- a/java/org/apache/tomcat/util/descriptor/web/mbeans-descriptors.xml
+++ b/java/org/apache/tomcat/util/descriptor/web/mbeans-descriptors.xml
@@ -1,4 +1,4 @@
-<?xml version="1.0"?>
+<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
diff --git a/java/org/apache/tomcat/util/digester/ArrayStack.java b/java/org/apache/tomcat/util/digester/ArrayStack.java
index d0fdaa6..580d6e1 100644
--- a/java/org/apache/tomcat/util/digester/ArrayStack.java
+++ b/java/org/apache/tomcat/util/digester/ArrayStack.java
@@ -38,6 +38,8 @@ import java.util.EmptyStackException;
* <p>Unlike <code>Stack</code>, <code>ArrayStack</code> accepts null entries.
* </p>
*
+ * @param <E> Type of object in this stack
+ *
* @see java.util.Stack
* @since Digester 1.6 (from Commons Collections 1.0)
*/
diff --git a/java/org/apache/tomcat/util/digester/CallMethodRule.java b/java/org/apache/tomcat/util/digester/CallMethodRule.java
index b8fe760..0c2896e 100644
--- a/java/org/apache/tomcat/util/digester/CallMethodRule.java
+++ b/java/org/apache/tomcat/util/digester/CallMethodRule.java
@@ -218,6 +218,7 @@ public class CallMethodRule extends Rule {
/**
* Should <code>MethodUtils.invokeExactMethod</code>
* be used for the reflection.
+ * @return <code>true</code> if invokeExactMethod is used
*/
public boolean getUseExactMatch() {
return useExactMatch;
@@ -226,6 +227,7 @@ public class CallMethodRule extends Rule {
/**
* Set whether <code>MethodUtils.invokeExactMethod</code>
* should be used for the reflection.
+ * @param useExactMatch The flag value
*/
public void setUseExactMatch(boolean useExactMatch)
{
diff --git a/java/org/apache/tomcat/util/digester/Digester.java b/java/org/apache/tomcat/util/digester/Digester.java
index 15a2cea..34c9bdf 100644
--- a/java/org/apache/tomcat/util/digester/Digester.java
+++ b/java/org/apache/tomcat/util/digester/Digester.java
@@ -313,6 +313,7 @@ public class Digester extends DefaultHandler2 {
* go dynamically as the document is parsed.
*
* @param prefix Prefix to look up
+ * @return the namespace URI
*/
public String findNamespaceURI(String prefix) {
@@ -338,6 +339,7 @@ public class Digester extends DefaultHandler2 {
* <code>useContextClassLoader</code> property is set to true</li>
* <li>The class loader used to load the Digester class itself.
* </ul>
+ * @return the classloader
*/
public ClassLoader getClassLoader() {
@@ -370,7 +372,7 @@ public class Digester extends DefaultHandler2 {
/**
- * Return the current depth of the element stack.
+ * @return the current depth of the element stack.
*/
public int getCount() {
@@ -380,7 +382,7 @@ public class Digester extends DefaultHandler2 {
/**
- * Return the name of the XML element that is currently being processed.
+ * @return the name of the XML element that is currently being processed.
*/
public String getCurrentElementName() {
@@ -395,7 +397,7 @@ public class Digester extends DefaultHandler2 {
/**
- * Return the error handler for this Digester.
+ * @return the error handler for this Digester.
*/
public ErrorHandler getErrorHandler() {
@@ -417,10 +419,11 @@ public class Digester extends DefaultHandler2 {
/**
- * Return the SAXParserFactory we will use, creating one if necessary.
- * @throws ParserConfigurationException
- * @throws SAXNotSupportedException
- * @throws SAXNotRecognizedException
+ * 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
*/
public SAXParserFactory getFactory() throws SAXNotRecognizedException, SAXNotSupportedException,
ParserConfigurationException {
@@ -476,7 +479,7 @@ public class Digester extends DefaultHandler2 {
/**
- * Return the current Logger associated with this instance of the Digester
+ * @return the current Logger associated with this instance of the Digester
*/
public Log getLogger() {
@@ -487,6 +490,7 @@ public class Digester extends DefaultHandler2 {
/**
* Set the current logger for this Digester.
+ * @param log The logger that will be used
*/
public void setLogger(Log log) {
@@ -499,6 +503,7 @@ public class Digester extends DefaultHandler2 {
* <strong>Note</strong> the output is finely grained.
*
* @since 1.6
+ * @return the SAX logger
*/
public Log getSAXLogger() {
@@ -519,7 +524,7 @@ public class Digester extends DefaultHandler2 {
}
/**
- * Return the current rule match path
+ * @return the current rule match path
*/
public String getMatch() {
@@ -529,7 +534,7 @@ public class Digester extends DefaultHandler2 {
/**
- * Return the "namespace aware" flag for parsers we create.
+ * @return the "namespace aware" flag for parsers we create.
*/
public boolean getNamespaceAware() {
@@ -560,7 +565,7 @@ public class Digester extends DefaultHandler2 {
/**
- * Return the public identifier of the DTD we are currently
+ * @return the public identifier of the DTD we are currently
* parsing under, if any.
*/
public String getPublicId() {
@@ -571,7 +576,7 @@ public class Digester extends DefaultHandler2 {
/**
- * Return the namespace URI that will be applied to all subsequently
+ * @return the namespace URI that will be applied to all subsequently
* added <code>Rule</code> objects.
*/
public String getRuleNamespaceURI() {
@@ -597,7 +602,7 @@ public class Digester extends DefaultHandler2 {
/**
- * Return the SAXParser we will use to parse the input stream. If there
+ * @return the SAXParser we will use to parse the input stream. If there
* is a problem creating the parser, return <code>null</code>.
*/
public SAXParser getParser() {
@@ -628,7 +633,7 @@ public class Digester extends DefaultHandler2 {
* 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
@@ -646,6 +651,7 @@ public class Digester extends DefaultHandler2 {
* Return the <code>Rules</code> 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() {
@@ -673,7 +679,7 @@ public class Digester extends DefaultHandler2 {
/**
- * Return the boolean as to whether the context classloader should be used.
+ * @return the boolean as to whether the context classloader should be used.
*/
public boolean getUseContextClassLoader() {
@@ -699,7 +705,7 @@ public class Digester extends DefaultHandler2 {
/**
- * Return the validating parser flag.
+ * @return the validating parser flag.
*/
public boolean getValidating() {
@@ -722,7 +728,7 @@ public class Digester extends DefaultHandler2 {
/**
- * Return the rules validation flag.
+ * @return the rules validation flag.
*/
public boolean getRulesValidation() {
@@ -745,7 +751,7 @@ public class Digester extends DefaultHandler2 {
/**
- * Return the fake attributes list.
+ * @return the fake attributes list.
*/
public Map<Class<?>, List<String>> getFakeAttributes() {
@@ -756,6 +762,9 @@ public class Digester extends DefaultHandler2 {
/**
* Determine if an attribute is a fake attribute.
+ * @param object The object
+ * @param name The attribute name
+ * @return <code>true</code> if this is a fake attribute
*/
public boolean isFakeAttribute(Object object, String name) {
@@ -792,6 +801,7 @@ public class Digester extends DefaultHandler2 {
*
* 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 {
@@ -1418,7 +1428,7 @@ public class Digester extends DefaultHandler2 {
* 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 SAXException if a parsing exception occurs
*/
@@ -1438,7 +1448,7 @@ public class Digester extends DefaultHandler2 {
* 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 SAXException if a parsing exception occurs
*/
@@ -1456,7 +1466,7 @@ public class Digester extends DefaultHandler2 {
* 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 SAXException if a parsing exception occurs
*/
@@ -1700,6 +1710,7 @@ public class Digester extends DefaultHandler2 {
/**
* Return the top object on the stack without removing it. If there are
* no objects on the stack, return <code>null</code>.
+ * @return the top object
*/
public Object peek() {
@@ -1720,6 +1731,7 @@ public class Digester extends DefaultHandler2 {
*
* @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) {
@@ -1736,6 +1748,7 @@ public class Digester extends DefaultHandler2 {
/**
* Pop the top object off of the stack, and return it. If there are
* no objects on the stack, return <code>null</code>.
+ * @return the top object
*/
public Object pop() {
@@ -1814,6 +1827,7 @@ public class Digester extends DefaultHandler2 {
*
* <p>The parameters stack is used to store <code>CallMethodRule</code> parameters.
* See {@link #params}.</p>
+ * @return the top object on the parameters stack
*/
public Object peekParams() {
@@ -1833,6 +1847,7 @@ public class Digester extends DefaultHandler2 {
*
* <p>The parameters stack is used to store <code>CallMethodRule</code> parameters.
* See {@link #params}.</p>
+ * @return the top object on the parameters stack
*/
public Object popParams() {
@@ -1868,7 +1883,8 @@ public class Digester extends DefaultHandler2 {
/**
* 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
* @return the new exception
*/
public SAXException createSAXException(String message, Exception e) {
@@ -1904,7 +1920,7 @@ public class Digester extends DefaultHandler2 {
/**
* 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) {
@@ -1926,7 +1942,7 @@ public class Digester extends DefaultHandler2 {
/**
* 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) {
diff --git a/java/org/apache/tomcat/util/digester/ObjectCreationFactory.java b/java/org/apache/tomcat/util/digester/ObjectCreationFactory.java
index ab6856b..3000a3a 100644
--- a/java/org/apache/tomcat/util/digester/ObjectCreationFactory.java
+++ b/java/org/apache/tomcat/util/digester/ObjectCreationFactory.java
@@ -33,23 +33,23 @@ import org.xml.sax.Attributes;
public interface ObjectCreationFactory {
/**
- * <p>Factory method called by {@link FactoryCreateRule} to supply an
+ * Factory method called by {@link FactoryCreateRule} to supply an
* object based on the element's attributes.
*
* @param attributes the element's attributes
- *
+ * @return the creted object
* @throws Exception any exception thrown will be propagated upwards
*/
public Object createObject(Attributes attributes) throws Exception;
/**
- * <p>Returns the {@link Digester} that was set by the
+ * @return the {@link Digester} that was set by the
* {@link FactoryCreateRule} upon initialization.
*/
public Digester getDigester();
/**
- * <p>Set the {@link Digester} to allow the implementation to do logging,
+ * 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 --git a/java/org/apache/tomcat/util/digester/Rule.java b/java/org/apache/tomcat/util/digester/Rule.java
index fa1ea0d..8f7a2c0 100644
--- a/java/org/apache/tomcat/util/digester/Rule.java
+++ b/java/org/apache/tomcat/util/digester/Rule.java
@@ -14,25 +14,18 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
-
package org.apache.tomcat.util.digester;
-
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.
*/
-
public abstract class Rule {
-
// ----------------------------------------------------------- Constructors
-
/**
* <p>Base constructor.
* Now the digester will be set when the rule is added.</p>
@@ -57,32 +50,35 @@ public abstract class Rule {
// ------------------------------------------------------------- Properties
-
/**
- * Return the Digester with which this Rule is associated.
+ * Identify the Digester with which this Rule is associated.
+ *
+ * @return the Digester with which this Rule is associated.
*/
public Digester getDigester() {
-
- return (this.digester);
-
+ return digester;
}
+
/**
- * Set the <code>Digester</code> with which this <code>Rule</code> is associated.
+ * Set the <code>Digester</code> with which this <code>Rule</code> is
+ * associated.
+ *
+ * @param digester The digester with which to associate this rule
*/
public void setDigester(Digester digester) {
-
this.digester = digester;
-
}
+
/**
* Return the namespace URI for which this Rule is relevant, if any.
+ *
+ * @return The namespace URI for which this rule is relevant or
+ * <code>null</code> if none.
*/
public String getNamespaceURI() {
-
- return (this.namespaceURI);
-
+ return namespaceURI;
}
@@ -93,134 +89,73 @@ public abstract class Rule {
* or <code>null</code> to match independent of namespace.
*/
public void setNamespaceURI(String namespaceURI) {
-
this.namespaceURI = namespaceURI;
-
}
// --------------------------------------------------------- Public Methods
-
- /**
- * This method is called when the beginning of a matching XML element
- * is encountered.
- *
- * @param attributes The attribute list of this element
- * @deprecated Use the {@link #begin(String,String,Attributes) begin}
- * method with <code>namespace</code> and <code>name</code>
- * parameters instead.
- */
- @Deprecated
- public void begin(Attributes attributes) throws Exception {
- // The default implementation does nothing
- }
-
-
/**
* This method is called when the beginning of a matching XML element
- * is encountered. The default implementation delegates to the deprecated
- * method {@link #begin(Attributes) begin} without the
- * <code>namespace</code> and <code>name</code> parameters, to retain
- * backwards compatibility.
+ * 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
+ * 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
+ * the element name otherwise
* @param attributes The attribute list of this element
- * @since Digester 1.4
- */
- public void begin(String namespace, String name, Attributes attributes)
- throws Exception {
-
- begin(attributes);
-
- }
-
-
- /**
- * 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.
*
- * @param text The text of the body of this element
- * @deprecated Use the {@link #body(String,String,String) body} method
- * with <code>namespace</code> and <code>name</code> parameters
- * instead.
+ * @throws Exception if an error occurs while processing the event
*/
- @Deprecated
- public void body(String text) throws Exception {
- // The default implementation does nothing
+ public void begin(String namespace, String name, Attributes attributes) throws Exception {
+ // NO-OP by default.
}
/**
* 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 delegates to the deprecated method
- * {@link #body(String) body} without the <code>namespace</code> and
- * <code>name</code> parameters, to retain backwards compatibility.
+ * 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 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
- * @since Digester 1.4
- */
- public void body(String namespace, String name, String text)
- throws Exception {
-
- body(text);
-
- }
-
-
- /**
- * This method is called when the end of a matching XML element
- * is encountered.
*
- * @deprecated Use the {@link #end(String,String) end} method with
- * <code>namespace</code> and <code>name</code> parameters instead.
+ * @throws Exception if an error occurs while processing the event
*/
- @Deprecated
- public void end() throws Exception {
- // The default implementation does nothing
+ public void body(String namespace, String name, String text) throws Exception {
+ // NO-OP by default.
}
/**
* This method is called when the end of a matching XML element
- * is encountered. The default implementation delegates to the deprecated
- * method {@link #end end} without the
- * <code>namespace</code> and <code>name</code> parameters, to retain
- * backwards compatibility.
+ * 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
- * @since Digester 1.4
+ * @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
*/
- public void end(String namespace, String name)
- throws Exception {
-
- end();
-
+ public void end(String namespace, String name) throws Exception {
+ // NO-OP by default.
}
/**
* 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
*/
public void finish() throws Exception {
- // The default implementation does nothing
+ // NO-OP by default.
}
-
-
}
diff --git a/java/org/apache/tomcat/util/digester/RuleSet.java b/java/org/apache/tomcat/util/digester/RuleSet.java
index deb8cbe..ebfa186 100644
--- a/java/org/apache/tomcat/util/digester/RuleSet.java
+++ b/java/org/apache/tomcat/util/digester/RuleSet.java
@@ -42,7 +42,7 @@ public interface RuleSet {
/**
- * Return the namespace URI that will be applied to all Rule instances
+ * @return the namespace URI that will be applied to all Rule instances
* created from this RuleSet.
*/
public String getNamespaceURI();
diff --git a/java/org/apache/tomcat/util/digester/Rules.java b/java/org/apache/tomcat/util/digester/Rules.java
index 490906f..e12bf27 100644
--- a/java/org/apache/tomcat/util/digester/Rules.java
+++ b/java/org/apache/tomcat/util/digester/Rules.java
@@ -36,7 +36,7 @@ public interface Rules {
/**
- * Return the Digester instance with which this Rules instance is
+ * @return the Digester instance with which this Rules instance is
* associated.
*/
public Digester getDigester();
@@ -51,7 +51,7 @@ public interface Rules {
/**
- * Return the namespace URI that will be applied to all subsequently
+ * @return the namespace URI that will be applied to all subsequently
* added <code>Rule</code> objects.
*/
public String getNamespaceURI();
@@ -96,6 +96,7 @@ public interface Rules {
* @param namespaceURI Namespace URI for which to select matching rules,
* or <code>null</code> to match regardless of namespace URI
* @param pattern Nesting pattern to be matched
+ * @return a rules list
*/
public List<Rule> match(String namespaceURI, String pattern);
@@ -106,6 +107,7 @@ public interface Rules {
* instance has been registered, they <strong>must</strong> be returned
* in the order originally registered through the <code>add()</code>
* method.
+ * @return a rules list
*/
public List<Rule> rules();
diff --git a/java/org/apache/tomcat/util/digester/RulesBase.java b/java/org/apache/tomcat/util/digester/RulesBase.java
index cdfba47..2025af1 100644
--- a/java/org/apache/tomcat/util/digester/RulesBase.java
+++ b/java/org/apache/tomcat/util/digester/RulesBase.java
@@ -253,6 +253,7 @@ public class RulesBase implements Rules {
* @param namespaceURI Namespace URI to match, or <code>null</code> to
* select matching rules regardless of namespace URI
* @param pattern Pattern to be matched
+ * @return a rules list
*/
protected List<Rule> lookup(String namespaceURI, String pattern) {
diff --git a/java/org/apache/tomcat/util/http/CookieProcessor.java b/java/org/apache/tomcat/util/http/CookieProcessor.java
index 5755e5b..e0efbf1 100644
--- a/java/org/apache/tomcat/util/http/CookieProcessor.java
+++ b/java/org/apache/tomcat/util/http/CookieProcessor.java
@@ -24,17 +24,28 @@ public interface CookieProcessor {
/**
* Parse the provided headers into server cookie objects.
+ *
+ * @param headers The HTTP headers to parse
+ * @param serverCookies The server cookies object to populate with the
+ * results of the parsing
*/
void parseCookieHeader(MimeHeaders headers, ServerCookies serverCookies);
/**
- * Generate the HTTP header value for the given Cookie.
+ * Generate the {@code Set-Cookie} HTTP header value for the given Cookie.
+ *
+ * @param cookie The cookie for which the header will be generated
+ *
+ * @return The header value in a form that can be added directly to the
+ * response
*/
String generateHeader(Cookie cookie);
/**
- * The character set that will be used when converting between bytes and
- * characters when parsing and/or generating HTTP headers for cookies.
+ * Obtain the character set that will be used when converting between bytes
+ * and characters when parsing and/or generating HTTP headers for cookies.
+ *
+ * @return The character set used for byte<->character conversions
*/
Charset getCharset();
}
diff --git a/java/org/apache/tomcat/util/http/CookieSupport.java b/java/org/apache/tomcat/util/http/CookieSupport.java
deleted file mode 100644
index 8887685..0000000
--- a/java/org/apache/tomcat/util/http/CookieSupport.java
+++ /dev/null
@@ -1,175 +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;
-
-
-/**
- * Static constants for this package.
- *
- * @deprecated Will be removed in Tomcat 9.
- */
- at Deprecated
-public final class CookieSupport {
-
- // --------------------------------------------------------------- Constants
- /**
- * If set to true, we parse cookies strictly according to the servlet,
- * cookie and HTTP specs by default.
- */
- public static final boolean STRICT_SERVLET_COMPLIANCE;
-
- /**
- * If true, cookie values are allowed to contain an equals character without
- * being quoted.
- */
- public static final boolean ALLOW_EQUALS_IN_VALUE;
-
- /**
- * If true, separators that are not explicitly dis-allowed by the v0 cookie
- * spec but are disallowed by the HTTP spec will be allowed in v0 cookie
- * names and values. These characters are: \"()/:<=>?@[\\]{} Note that
- * the inclusion of / depends on the value of {@link #FWD_SLASH_IS_SEPARATOR}.
- */
- public static final boolean ALLOW_HTTP_SEPARATORS_IN_V0;
-
- /**
- * If set to true, the <code>/</code> character will be treated as a
- * separator. Default is usually false. If STRICT_SERVLET_COMPLIANCE==true
- * then default is true. Explicitly setting always takes priority.
- */
- public static final boolean FWD_SLASH_IS_SEPARATOR;
-
- /**
- * If true, name only cookies will be permitted.
- */
- public static final boolean ALLOW_NAME_ONLY;
-
- /**
- * @deprecated Unused. As of 8.0.31, cookie headers are always preserved.
- */
- @Deprecated
- public static final boolean PRESERVE_COOKIE_HEADER = true;
-
- /**
- * The list of separators that apply to version 0 cookies. To quote the
- * spec, these are comma, semi-colon and white-space. The HTTP spec
- * definition of linear white space is [CRLF] 1*( SP | HT )
- */
- private static final char[] V0_SEPARATORS = {',', ';', ' ', '\t'};
- private static final boolean[] V0_SEPARATOR_FLAGS = new boolean[128];
-
- /**
- * The list of separators that apply to version 1 cookies. This may or may
- * not include '/' depending on the setting of
- * {@link #FWD_SLASH_IS_SEPARATOR}.
- */
- private static final char[] HTTP_SEPARATORS;
- private static final boolean[] HTTP_SEPARATOR_FLAGS = new boolean[128];
-
- static {
- STRICT_SERVLET_COMPLIANCE = Boolean.parseBoolean(System.getProperty(
- "org.apache.catalina.STRICT_SERVLET_COMPLIANCE",
- "false"));
-
- ALLOW_EQUALS_IN_VALUE = Boolean.parseBoolean(System.getProperty(
- "org.apache.tomcat.util.http.ServerCookie.ALLOW_EQUALS_IN_VALUE",
- "false"));
-
- ALLOW_HTTP_SEPARATORS_IN_V0 = Boolean.parseBoolean(System.getProperty(
- "org.apache.tomcat.util.http.ServerCookie.ALLOW_HTTP_SEPARATORS_IN_V0",
- "false"));
-
- String fwdSlashIsSeparator = System.getProperty(
- "org.apache.tomcat.util.http.ServerCookie.FWD_SLASH_IS_SEPARATOR");
- if (fwdSlashIsSeparator == null) {
- FWD_SLASH_IS_SEPARATOR = STRICT_SERVLET_COMPLIANCE;
- } else {
- FWD_SLASH_IS_SEPARATOR = Boolean.parseBoolean(fwdSlashIsSeparator);
- }
-
- ALLOW_NAME_ONLY = Boolean.parseBoolean(System.getProperty(
- "org.apache.tomcat.util.http.ServerCookie.ALLOW_NAME_ONLY",
- "false"));
-
-
- /*
- Excluding the '/' char by default violates the RFC, but
- it looks like a lot of people put '/'
- in unquoted values: '/': ; //47
- '\t':9 ' ':32 '\"':34 '(':40 ')':41 ',':44 ':':58 ';':59 '<':60
- '=':61 '>':62 '?':63 '@':64 '[':91 '\\':92 ']':93 '{':123 '}':125
- */
- if (CookieSupport.FWD_SLASH_IS_SEPARATOR) {
- HTTP_SEPARATORS = new char[] { '\t', ' ', '\"', '(', ')', ',', '/',
- ':', ';', '<', '=', '>', '?', '@', '[', '\\', ']', '{', '}' };
- } else {
- HTTP_SEPARATORS = new char[] { '\t', ' ', '\"', '(', ')', ',',
- ':', ';', '<', '=', '>', '?', '@', '[', '\\', ']', '{', '}' };
- }
- for (int i = 0; i < 128; i++) {
- V0_SEPARATOR_FLAGS[i] = false;
- HTTP_SEPARATOR_FLAGS[i] = false;
- }
- for (char V0_SEPARATOR : V0_SEPARATORS) {
- V0_SEPARATOR_FLAGS[V0_SEPARATOR] = true;
- }
- for (char HTTP_SEPARATOR : HTTP_SEPARATORS) {
- HTTP_SEPARATOR_FLAGS[HTTP_SEPARATOR] = true;
- }
-
- }
-
- // ----------------------------------------------------------------- Methods
-
- /**
- * Returns true if the byte is a separator as defined by V0 of the cookie
- * spec.
- */
- public static final boolean isV0Separator(final char c) {
- if (c < 0x20 || c >= 0x7f) {
- if (c != 0x09) {
- throw new IllegalArgumentException(
- "Control character in cookie value or attribute.");
- }
- }
-
- return V0_SEPARATOR_FLAGS[c];
- }
-
- /**
- * Returns true if the byte is a separator as defined by V1 of the cookie
- * spec, RFC2109.
- * @throws IllegalArgumentException if a control character was supplied as
- * input
- */
- public static final boolean isHttpSeparator(final char c) {
- if (c < 0x20 || c >= 0x7f) {
- if (c != 0x09) {
- throw new IllegalArgumentException(
- "Control character in cookie value or attribute.");
- }
- }
-
- return HTTP_SEPARATOR_FLAGS[c];
- }
-
-
- // ------------------------------------------------------------- Constructor
- private CookieSupport() {
- // Utility class. Don't allow instances to be created.
- }
-}
diff --git a/java/org/apache/tomcat/util/http/FastHttpDateFormat.java b/java/org/apache/tomcat/util/http/FastHttpDateFormat.java
index 9e8e445..f30c04b 100644
--- a/java/org/apache/tomcat/util/http/FastHttpDateFormat.java
+++ b/java/org/apache/tomcat/util/http/FastHttpDateFormat.java
@@ -50,16 +50,6 @@ public final class FastHttpDateFormat {
new SimpleDateFormat(RFC1123_DATE, Locale.US);
- /**
- * The set of SimpleDateFormat formats to use in getDateHeader().
- */
- private static final SimpleDateFormat formats[] = {
- new SimpleDateFormat(RFC1123_DATE, Locale.US),
- new SimpleDateFormat("EEEEEE, dd-MMM-yy HH:mm:ss zzz", Locale.US),
- new SimpleDateFormat("EEE MMMM d HH:mm:ss yyyy", Locale.US)
- };
-
-
private static final TimeZone gmtZone = TimeZone.getTimeZone("GMT");
@@ -67,13 +57,7 @@ public final class FastHttpDateFormat {
* GMT timezone - all HTTP dates are on GMT
*/
static {
-
format.setTimeZone(gmtZone);
-
- formats[0].setTimeZone(gmtZone);
- formats[1].setTimeZone(gmtZone);
- formats[2].setTimeZone(gmtZone);
-
}
@@ -106,6 +90,7 @@ public final class FastHttpDateFormat {
/**
* Get the current date in HTTP format.
+ * @return the HTTP date
*/
public static final String getCurrentDate() {
@@ -125,6 +110,9 @@ public final class FastHttpDateFormat {
/**
* Get the HTTP format of the specified date.
+ * @param value The date
+ * @param threadLocalformat Local format to avoid synchronization
+ * @return the HTTP date
*/
public static final String formatDate
(long value, DateFormat threadLocalformat) {
@@ -152,6 +140,9 @@ public final class FastHttpDateFormat {
/**
* Try to parse the given date as a HTTP date.
+ * @param value The HTTP date
+ * @param threadLocalformats Local format to avoid synchronization
+ * @return the date as a long
*/
public static final long parseDate(String value,
DateFormat[] threadLocalformats) {
@@ -166,8 +157,7 @@ public final class FastHttpDateFormat {
date = internalParseDate(value, threadLocalformats);
updateParseCache(value, date);
} else {
- date = internalParseDate(value, formats);
- updateParseCache(value, date);
+ throw new IllegalArgumentException();
}
if (date == null) {
return (-1L);
diff --git a/java/org/apache/tomcat/util/http/HttpMessages.java b/java/org/apache/tomcat/util/http/HttpMessages.java
deleted file mode 100644
index 935b98b..0000000
--- a/java/org/apache/tomcat/util/http/HttpMessages.java
+++ /dev/null
@@ -1,181 +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;
-
-import java.util.Locale;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-
-import org.apache.tomcat.util.res.StringManager;
-
-/**
- * Handle (internationalized) HTTP messages.
- *
- * @author James Duncan Davidson [duncan at eng.sun.com]
- * @author James Todd [gonzo at eng.sun.com]
- * @author Jason Hunter [jch at eng.sun.com]
- * @author Harish Prabandham
- * @author costin at eng.sun.com
- */
-public class HttpMessages {
-
- private static final Map<Locale,HttpMessages> instances =
- new ConcurrentHashMap<>();
-
- // Keep this in separate package from standard i18n messages
- private static final HttpMessages DEFAULT = new HttpMessages(
- StringManager.getManager("org.apache.tomcat.util.http.res",
- Locale.getDefault()));
-
-
- private final StringManager sm;
-
- private final String st_200;
- private final String st_302;
- private final String st_400;
- private final String st_404;
-
- private HttpMessages(StringManager sm) {
- // There is a performance tradeoff here. This implementation incurs
- // ~160ns (40ns per StringManager) lookup delay on first access but all
- // subsequent lookups take ~0.25ns.
- // The alternative approach (lazy init of each cached String) delays the
- // StringManager lookup until required but increases the time for
- // subsequent lookups to ~0.5ns.
- // These times will be in the noise for most requests. This
- // implementation was chosen because:
- // - Over anything more than a few hundred requests it is faster.
- // - The code is a lot simpler. Thread safe lazy init needs care to get
- // right. See http://markmail.org/thread/wjp3oejdyxcrz7do
- this.sm = sm;
- st_200 = sm.getString("sc.200");
- st_302 = sm.getString("sc.302");
- st_400 = sm.getString("sc.400");
- st_404 = sm.getString("sc.404");
- }
-
-
- /**
- * Get the status string associated with a status code. Common messages are
- * cached.
- *
- * @param status The HTTP status code to retrieve the message for
- *
- * @return The HTTP status string that conforms to the requirements of the
- * HTTP specification
- */
- public String getMessage(int status) {
- switch (status) {
- case 200:
- return st_200;
- case 302:
- return st_302;
- case 400:
- return st_400;
- case 404:
- return st_404;
- default:
- return sm.getString("sc."+ status);
- }
- }
-
-
- public static HttpMessages getInstance(Locale locale) {
- HttpMessages result = instances.get(locale);
- if (result == null) {
- StringManager sm = StringManager.getManager(
- "org.apache.tomcat.util.http.res", locale);
- if (Locale.getDefault().equals(sm.getLocale())) {
- result = DEFAULT;
- } else {
- result = new HttpMessages(sm);
- }
- instances.put(locale, result);
- }
- return result;
- }
-
-
- /**
- * Filter the specified message string for characters that are sensitive
- * in HTML. This avoids potential attacks caused by including JavaScript
- * codes in the request URL that is often reported in error messages.
- *
- * @param message The message string to be filtered
- */
- public static String filter(String message) {
-
- if (message == null) {
- return (null);
- }
-
- char content[] = new char[message.length()];
- message.getChars(0, message.length(), content, 0);
- StringBuilder result = new StringBuilder(content.length + 50);
- for (int i = 0; i < content.length; i++) {
- switch (content[i]) {
- case '<':
- result.append("<");
- break;
- case '>':
- result.append(">");
- break;
- case '&':
- result.append("&");
- break;
- case '"':
- result.append(""");
- break;
- default:
- result.append(content[i]);
- }
- }
- return (result.toString());
- }
-
- /**
- * Is the provided message safe to use in an HTTP header. Safe messages must
- * meet the requirements of RFC2616 - i.e. must consist only of TEXT.
- *
- * @param msg The message to test
- * @return <code>true</code> if the message is safe to use in an HTTP
- * header else <code>false</code>
- */
- public static boolean isSafeInHttpHeader(String msg) {
- // Nulls are fine. It is up to the calling code to address any NPE
- // concerns
- if (msg == null) {
- return true;
- }
-
- // Reason-Phrase is defined as *<TEXT, excluding CR, LF>
- // TEXT is defined as any OCTET except CTLs, but including LWS
- // OCTET is defined as an 8-bit sequence of data
- // CTL is defined as octets 0-31 and 127
- // LWS, if we exclude CR LF pairs, is defined as SP or HT (32, 9)
- final int len = msg.length();
- for (int i = 0; i < len; i++) {
- char c = msg.charAt(i);
- if (32 <= c && c <= 126 || 128 <= c && c <= 255 || c == 9) {
- continue;
- }
- return false;
- }
-
- return true;
- }
-}
diff --git a/java/org/apache/tomcat/util/http/LegacyCookieProcessor.java b/java/org/apache/tomcat/util/http/LegacyCookieProcessor.java
index fff2c84..2cf1b52 100644
--- a/java/org/apache/tomcat/util/http/LegacyCookieProcessor.java
+++ b/java/org/apache/tomcat/util/http/LegacyCookieProcessor.java
@@ -84,19 +84,16 @@ public final class LegacyCookieProcessor implements CookieProcessor {
ANCIENT_DATE = COOKIE_DATE_FORMAT.get().format(new Date(10000));
}
+ private final boolean STRICT_SERVLET_COMPLIANCE =
+ Boolean.getBoolean("org.apache.catalina.STRICT_SERVLET_COMPLIANCE");
- @SuppressWarnings("deprecation") // Default to false when deprecated code is removed
- private boolean allowEqualsInValue = CookieSupport.ALLOW_EQUALS_IN_VALUE;
+ private boolean allowEqualsInValue = false;
- @SuppressWarnings("deprecation") // Default to false when deprecated code is removed
- private boolean allowNameOnly = CookieSupport.ALLOW_NAME_ONLY;
+ private boolean allowNameOnly = false;
- @SuppressWarnings("deprecation") // Default to false when deprecated code is removed
- private boolean allowHttpSepsInV0 = CookieSupport.ALLOW_HTTP_SEPARATORS_IN_V0;
+ private boolean allowHttpSepsInV0 = false;
- @SuppressWarnings("deprecation") // Default to !STRICT_SERVLET_COMPLIANCE
- // when deprecated code is removed
- private boolean alwaysAddExpires = SetCookieSupport.ALWAYS_ADD_EXPIRES;
+ private boolean alwaysAddExpires = !STRICT_SERVLET_COMPLIANCE;
private final BitSet httpSeparatorFlags = new BitSet(128);
@@ -108,9 +105,7 @@ public final class LegacyCookieProcessor implements CookieProcessor {
for (char c : HTTP_SEPARATORS) {
httpSeparatorFlags.set(c);
}
- @SuppressWarnings("deprecation") // Default to STRICT_SERVLET_COMPLIANCE
- // when deprecated code is removed
- boolean b = CookieSupport.FWD_SLASH_IS_SEPARATOR;
+ boolean b = STRICT_SERVLET_COMPLIANCE;
if (b) {
httpSeparatorFlags.set('/');
}
@@ -191,31 +186,6 @@ public final class LegacyCookieProcessor implements CookieProcessor {
}
- /**
- * @return Always returns true
- *
- * @deprecated No longer used. Cookie headers are now always preserved. Will
- * be removed in Tomcat 8.5.x.
- */
- @Deprecated
- public boolean getPreserveCookieHeader() {
- return true;
- }
-
-
- /**
- * NO-OP.
- *
- * @param preserveCookieHeader Ignored
- *
- * @deprecated No longer used. Cookie headers are now always preserved. Will
- * be removed in Tomcat 8.5.x.
- */
- @Deprecated
- public void setPreserveCookieHeader(boolean preserveCookieHeader) {
- }
-
-
public boolean getForwardSlashIsSeparator() {
return httpSeparatorFlags.get('/');
}
@@ -266,7 +236,8 @@ public final class LegacyCookieProcessor implements CookieProcessor {
if (cookieValue != null && !cookieValue.isNull() ) {
if (cookieValue.getType() != MessageBytes.T_BYTES ) {
Exception e = new Exception();
- log.warn("Cookies: Parsing cookie as String. Expected bytes.", e);
+ // TODO: Review this in light of HTTP/2
+ log.debug("Cookies: Parsing cookie as String. Expected bytes.", e);
cookieValue.toBytes();
}
if (log.isDebugEnabled()) {
diff --git a/java/org/apache/tomcat/util/http/MimeHeaders.java b/java/org/apache/tomcat/util/http/MimeHeaders.java
index b1e2bdb..59504ee 100644
--- a/java/org/apache/tomcat/util/http/MimeHeaders.java
+++ b/java/org/apache/tomcat/util/http/MimeHeaders.java
@@ -16,6 +16,7 @@
*/
package org.apache.tomcat.util.http;
+import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Enumeration;
@@ -23,9 +24,6 @@ import java.util.Enumeration;
import org.apache.tomcat.util.buf.MessageBytes;
import org.apache.tomcat.util.res.StringManager;
-/* XXX XXX XXX Need a major rewrite !!!!
- */
-
/**
* This class is used to contain standard internet message headers,
* used for SMTP (RFC822) and HTTP (RFC2068) messages as well as for
@@ -124,6 +122,7 @@ public class MimeHeaders {
/**
* Set limit on the number of header fields.
+ * @param limit The new limit
*/
public void setLimit(int limit) {
this.limit = limit;
@@ -174,17 +173,28 @@ public class MimeHeaders {
return sw.toString();
}
+
+ public void duplicate(MimeHeaders source) throws IOException {
+ for (int i = 0; i < source.size(); i++) {
+ MimeHeaderField mhf = createHeader();
+ mhf.getName().duplicate(source.getName(i));
+ mhf.getValue().duplicate(source.getValue(i));
+ }
+ }
+
+
// -------------------- Idx access to headers ----------
/**
- * Returns the current number of header fields.
+ * @return the current number of header fields.
*/
public int size() {
return count;
}
/**
- * Returns the Nth header name, or null if there is no such header.
+ * @param n The header index
+ * @return the Nth header name, or null if there is no such header.
* This may be used to iterate through all header fields.
*/
public MessageBytes getName(int n) {
@@ -192,14 +202,19 @@ public class MimeHeaders {
}
/**
- * Returns the Nth header value, or null if there is no such header.
+ * @param n The header index
+ * @return the Nth header value, or null if there is no such header.
* This may be used to iterate through all header fields.
*/
public MessageBytes getValue(int n) {
return n >= 0 && n < count ? headers[n].getValue() : null;
}
- /** Find the index of a header with the given name.
+ /**
+ * Find the index of a header with the given name.
+ * @param name The header name
+ * @param starting Index on which to start looking
+ * @return the header index
*/
public int findHeader( String name, int starting ) {
// We can use a hash - but it's not clear how much
@@ -223,6 +238,7 @@ public class MimeHeaders {
* Returns an enumeration of strings representing the header field names.
* Field names may appear multiple times in this enumeration, indicating
* that multiple fields with that name exist in this header.
+ * @return the enumeration
*/
public Enumeration<String> names() {
return new NamesEnumerator(this);
@@ -263,18 +279,26 @@ public class MimeHeaders {
return mh;
}
- /** Create a new named header , return the MessageBytes
- container for the new value
- */
+ /**
+ * Create a new named header , return the MessageBytes
+ * container for the new value
+ * @param name The header name
+ * @return the message bytes container for the value
+ */
public MessageBytes addValue( String name ) {
MimeHeaderField mh = createHeader();
mh.getName().setString(name);
return mh.getValue();
}
- /** Create a new named header using un-translated byte[].
- The conversion to chars can be delayed until
- encoding is known.
+ /**
+ * Create a new named header using un-translated byte[].
+ * The conversion to chars can be delayed until
+ * encoding is known.
+ * @param b The header name bytes
+ * @param startN Offset
+ * @param len Length
+ * @return the message bytes container for the value
*/
public MessageBytes addValue(byte b[], int startN, int len)
{
@@ -283,11 +307,12 @@ public class MimeHeaders {
return mhf.getValue();
}
- /** Allow "set" operations -
- return a MessageBytes container for the
- header value ( existing header or new
- if this .
- */
+ /**
+ * Allow "set" operations, which removes all current values
+ * for this header.
+ * @param name The header name
+ * @return the message bytes container for the value
+ */
public MessageBytes setValue( String name ) {
for ( int i = 0; i < count; i++ ) {
if(headers[i].getName().equalsIgnoreCase(name)) {
@@ -309,6 +334,8 @@ public class MimeHeaders {
* Finds and returns a header field with the given name. If no such
* field exists, null is returned. If more than one such field is
* in the header, an arbitrary one is returned.
+ * @param name The header name
+ * @return the value
*/
public MessageBytes getValue(String name) {
for (int i = 0; i < count; i++) {
@@ -323,6 +350,9 @@ public class MimeHeaders {
* Finds and returns a unique header field with the given name. If no such
* field exists, null is returned. If the specified header field is not
* unique then an {@link IllegalArgumentException} is thrown.
+ * @param name The header name
+ * @return the value if unique
+ * @throws IllegalArgumentException if the header has multiple values
*/
public MessageBytes getUniqueValue(String name) {
MessageBytes result = null;
diff --git a/java/org/apache/tomcat/util/http/Parameters.java b/java/org/apache/tomcat/util/http/Parameters.java
index 7df6a8a..2dc6249 100644
--- a/java/org/apache/tomcat/util/http/Parameters.java
+++ b/java/org/apache/tomcat/util/http/Parameters.java
@@ -26,6 +26,8 @@ import java.util.Enumeration;
import java.util.LinkedHashMap;
import java.util.Map;
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.buf.B2CConverter;
import org.apache.tomcat.util.buf.ByteChunk;
import org.apache.tomcat.util.buf.MessageBytes;
@@ -39,8 +41,7 @@ import org.apache.tomcat.util.res.StringManager;
*/
public final class Parameters {
- private static final org.apache.juli.logging.Log log =
- org.apache.juli.logging.LogFactory.getLog(Parameters.class );
+ private static final Log log = LogFactory.getLog(Parameters.class);
private static final UserDataHelper userDataLog = new UserDataHelper(log);
diff --git a/java/org/apache/tomcat/util/http/Rfc6265CookieProcessor.java b/java/org/apache/tomcat/util/http/Rfc6265CookieProcessor.java
index e58aef2..6606809 100644
--- a/java/org/apache/tomcat/util/http/Rfc6265CookieProcessor.java
+++ b/java/org/apache/tomcat/util/http/Rfc6265CookieProcessor.java
@@ -73,8 +73,11 @@ public class Rfc6265CookieProcessor implements CookieProcessor {
if (cookieValue != null && !cookieValue.isNull() ) {
if (cookieValue.getType() != MessageBytes.T_BYTES ) {
- Exception e = new Exception();
- log.warn("Cookies: Parsing cookie as String. Expected bytes.", e);
+ if (log.isDebugEnabled()) {
+ Exception e = new Exception();
+ // TODO: Review this in light of HTTP/2
+ log.debug("Cookies: Parsing cookie as String. Expected bytes.", e);
+ }
cookieValue.toBytes();
}
if (log.isDebugEnabled()) {
@@ -96,7 +99,7 @@ public class Rfc6265CookieProcessor implements CookieProcessor {
public String generateHeader(javax.servlet.http.Cookie cookie) {
StringBuilder header = new StringBuilder();
- // TODO: Name validation takes place in Cookie and can not be configured
+ // TODO: Name validation takes place in Cookie and cannot be configured
// per Context. Moving it to here would allow per Context config
// but delay validation until the header is generated. However,
// the spec requires an IllegalArgumentException on Cookie
diff --git a/java/org/apache/tomcat/util/http/ServerCookies.java b/java/org/apache/tomcat/util/http/ServerCookies.java
index 2cdd0f1..401cffb 100644
--- a/java/org/apache/tomcat/util/http/ServerCookies.java
+++ b/java/org/apache/tomcat/util/http/ServerCookies.java
@@ -40,6 +40,7 @@ public class ServerCookies {
* Register a new, initialized cookie. Cookies are recycled, and most of the
* time an existing ServerCookie object is returned. The caller can set the
* name/value and attributes for the cookie.
+ * @return the new cookie
*/
public ServerCookie addCookie() {
if (limit > -1 && cookieCount >= limit) {
diff --git a/java/org/apache/tomcat/util/http/SetCookieSupport.java b/java/org/apache/tomcat/util/http/SetCookieSupport.java
deleted file mode 100644
index 8f8cde8..0000000
--- a/java/org/apache/tomcat/util/http/SetCookieSupport.java
+++ /dev/null
@@ -1,49 +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;
-
-import javax.servlet.http.Cookie;
-
-/**
- * Support class for generating Set-Cookie header values.
- *
- * @deprecated Will be removed in Tomcat 9.
- */
- at Deprecated
-public class SetCookieSupport {
- /**
- * If set to false, we don't use the IE6/7 Max-Age/Expires work around.
- * Default is usually true. If STRICT_SERVLET_COMPLIANCE==true then default
- * is false. Explicitly setting always takes priority.
- */
- static final boolean ALWAYS_ADD_EXPIRES;
- static {
- String alwaysAddExpires = System.getProperty(
- "org.apache.tomcat.util.http.ServerCookie.ALWAYS_ADD_EXPIRES");
- if (alwaysAddExpires != null) {
- ALWAYS_ADD_EXPIRES = Boolean.parseBoolean(alwaysAddExpires);
- } else {
- ALWAYS_ADD_EXPIRES = !Boolean.getBoolean("org.apache.catalina.STRICT_SERVLET_COMPLIANCE");
- }
- }
-
- private static final CookieProcessor cookieProcessor = new LegacyCookieProcessor();
-
- public static String generateHeader(Cookie cookie) {
- return cookieProcessor.generateHeader(cookie);
- }
-}
diff --git a/java/org/apache/tomcat/util/http/fileupload/MultipartStream.java b/java/org/apache/tomcat/util/http/fileupload/MultipartStream.java
index 4fbdfad..19ad36d 100644
--- a/java/org/apache/tomcat/util/http/fileupload/MultipartStream.java
+++ b/java/org/apache/tomcat/util/http/fileupload/MultipartStream.java
@@ -456,7 +456,7 @@ public class MultipartStream {
throws IllegalBoundaryException {
if (boundary.length != boundaryLength - BOUNDARY_PREFIX.length) {
throw new IllegalBoundaryException(
- "The length of a boundary token can not be changed");
+ "The length of a boundary token cannot be changed");
}
System.arraycopy(boundary, 0, this.boundary, BOUNDARY_PREFIX.length,
boundary.length);
diff --git a/java/org/apache/tomcat/util/http/parser/Authorization.java b/java/org/apache/tomcat/util/http/parser/Authorization.java
index a9ea0cb..a61b4f0 100644
--- a/java/org/apache/tomcat/util/http/parser/Authorization.java
+++ b/java/org/apache/tomcat/util/http/parser/Authorization.java
@@ -22,11 +22,15 @@ import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
+import org.apache.tomcat.util.res.StringManager;
+
/**
* Parser for an "Authorization" header.
*/
public class Authorization {
+ private static final StringManager sm = StringManager.getManager(Authorization.class);
+
@SuppressWarnings("unused") // Unused due to buggy client implementations
private static final Integer FIELD_TYPE_TOKEN = Integer.valueOf(0);
private static final Integer FIELD_TYPE_QUOTED_STRING = Integer.valueOf(1);
@@ -119,7 +123,8 @@ public class Authorization {
break;
default:
// Error
- throw new IllegalArgumentException("TODO i18n: Unsupported type");
+ throw new IllegalArgumentException(
+ sm.getString("authorization.unknownType", type));
}
if (value == null) {
diff --git a/java/org/apache/tomcat/util/http/parser/LocalStrings.properties b/java/org/apache/tomcat/util/http/parser/LocalStrings.properties
index 32fd453..9051ddb 100644
--- a/java/org/apache/tomcat/util/http/parser/LocalStrings.properties
+++ b/java/org/apache/tomcat/util/http/parser/LocalStrings.properties
@@ -13,6 +13,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+authorization.unknownType=Unknown Type [{0}]
cookie.fallToDebug=Note: further occurrences of this error will be logged at DEBUG level.
cookie.invalidCookieValue=A cookie header was received [{0}] that contained an invalid cookie. That cookie will be ignored.
cookie.invalidCookieVersion=A cookie header was received using an unrecognised cookie version of [{0}]. The header and the cookies it contains will be ignored.
diff --git a/java/org/apache/tomcat/util/http/res/LocalStrings.properties b/java/org/apache/tomcat/util/http/res/LocalStrings.properties
deleted file mode 100644
index 67b00f0..0000000
--- a/java/org/apache/tomcat/util/http/res/LocalStrings.properties
+++ /dev/null
@@ -1,78 +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.
-
-# HttpMessages. The values in this file will be used in HTTP headers and as such
-# may only contain TEXT as defined by RFC 2616
-# All status codes registered with IANA can be found at
-# http://www.iana.org/assignments/http-status-codes/http-status-codes.xml
-# The list might be kept in sync with the one in
-# java/org/apache/catalina/valves/LocalStrings.properties
-sc.100=Continue
-sc.101=Switching Protocols
-sc.102=Processing
-sc.200=OK
-sc.201=Created
-sc.202=Accepted
-sc.203=Non-Authoritative Information
-sc.204=No Content
-sc.205=Reset Content
-sc.206=Partial Content
-sc.207=Multi-Status
-sc.208=Already Reported
-sc.226=IM Used
-sc.300=Multiple Choices
-sc.301=Moved Permanently
-sc.302=Found
-sc.303=See Other
-sc.304=Not Modified
-sc.305=Use Proxy
-sc.307=Temporary Redirect
-sc.308=Permanent Redirect
-sc.400=Bad Request
-sc.401=Unauthorized
-sc.402=Payment Required
-sc.403=Forbidden
-sc.404=Not Found
-sc.405=Method Not Allowed
-sc.406=Not Acceptable
-sc.407=Proxy Authentication Required
-sc.408=Request Timeout
-sc.409=Conflict
-sc.410=Gone
-sc.411=Length Required
-sc.412=Precondition Failed
-sc.413=Request Entity Too Large
-sc.414=Request-URI Too Long
-sc.415=Unsupported Media Type
-sc.416=Requested Range Not Satisfiable
-sc.417=Expectation Failed
-sc.422=Unprocessable Entity
-sc.423=Locked
-sc.424=Failed Dependency
-sc.426=Upgrade Required
-sc.428=Precondition Required
-sc.429=Too Many Requests
-sc.431=Request Header Fields Too Large
-sc.500=Internal Server Error
-sc.501=Not Implemented
-sc.502=Bad Gateway
-sc.503=Service Unavailable
-sc.504=Gateway Timeout
-sc.505=HTTP Version Not Supported
-sc.506=Variant Also Negotiates (Experimental)
-sc.507=Insufficient Storage
-sc.508=Loop Detected
-sc.510=Not Extended
-sc.511=Network Authentication Required
diff --git a/java/org/apache/tomcat/util/http/res/LocalStrings_es.properties b/java/org/apache/tomcat/util/http/res/LocalStrings_es.properties
deleted file mode 100644
index 9251884..0000000
--- a/java/org/apache/tomcat/util/http/res/LocalStrings_es.properties
+++ /dev/null
@@ -1,61 +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.
-# HttpMessages. The values in this file will be used in HTTP headers and as such
-# may only contain TEXT as defined by RFC 2616
-sc.100 = Continuar
-sc.101 = Cambiando Protocolos
-sc.200 = OK
-sc.201 = Creado
-sc.202 = Aceptado
-sc.203 = Informaci\u00F3n No-Autorizativa
-sc.204 = Sin Contenido
-sc.205 = Reponer Contenido
-sc.206 = Contenido Parcial
-sc.207 = Multi-Estado
-sc.300 = M\u00FAltiples Elecciones
-sc.301 = Movido permanentemente
-sc.302 = Movido temporalmente
-sc.303 = Mirar Otro
-sc.304 = No Modificado
-sc.305 = Usar Proxy
-sc.307 = Redirecci\u00F3n Temporal
-sc.400 = Petici\u00F3n incorrecta
-sc.401 = No Autorizado
-sc.402 = Pago requerido
-sc.403 = Prohibido
-sc.404 = No Encontrado
-sc.405 = M\u00E9todo No Permitido
-sc.406 = No Aceptable
-sc.407 = Autentificaci\u00F3n Proxy Requerida
-sc.408 = Request Caducada
-sc.409 = Conflicto
-sc.410 = Ido
-sc.411 = Longitud Requerida
-sc.412 = Precondici\u00F3n Fallada
-sc.413 = Entidad de Request Demasiado Grande
-sc.414 = Request-URI Demasiado Larga
-sc.415 = Tipo de Medio No Soportado
-sc.416 = El Rango Pedido No Ser Satisfecho
-sc.417 = Expectativa Fallada
-sc.422 = Entidad Improcesable
-sc.423 = Bloqueado
-sc.424 = Dependencia Fallida
-sc.500 = Error Interno del Servidor
-sc.501 = No Implementado
-sc.502 = Pasarela Incorrecta
-sc.503 = Servicio no Disponible
-sc.504 = Pasarela Caducada
-sc.505 = Versi\u00F3n de HTTP No Soportada
-sc.507 = Almacenaje Insuficiente
diff --git a/java/org/apache/tomcat/util/http/res/LocalStrings_fr.properties b/java/org/apache/tomcat/util/http/res/LocalStrings_fr.properties
deleted file mode 100644
index 79e57ca..0000000
--- a/java/org/apache/tomcat/util/http/res/LocalStrings_fr.properties
+++ /dev/null
@@ -1,61 +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.
-
-# HttpMessages. The values in this file will be used in HTTP headers and as such
-# may only contain TEXT as defined by RFC 2616
-sc.100=Continuer
-sc.101=Changement de Protocols
-sc.200=OK
-sc.201=Cr\u00e9e
-sc.202=Accept\u00e9
-sc.203=Information Sans-Autorit\u00e9
-sc.204=Pas de Contenu
-sc.205=Remise \u00e0 Z\u00e9ro de Contenu
-sc.206=Contenu Partiel
-sc.207=Etat Multiple
-sc.300=Choix Multiples
-sc.301=D\u00e9plac\u00e9 de fa\u00e7on Permanente
-sc.302=D\u00e9plac\u00e9 Temporairement
-sc.303=Voir Autre
-sc.304=Non Modifi\u00e9
-sc.305=Utilisation de Relais
-sc.307=Redirection Temporaire
-sc.400=Mauvaise Requ\u00eate
-sc.401=Non-Autoris\u00e9
-sc.402=Paiement N\u00e9cessaire
-sc.403=Interdit
-sc.404=Introuvable
-sc.405=M\u00e9thode Non Autoris\u00e9e
-sc.406=Inacceptable
-sc.407=Authentification de Relais N\u00e9cessaire
-sc.408=D\u00e9passement de D\u00e9lais pour la Requ\u00eate
-sc.409=Conflit
-sc.410=Parti
-sc.411=Taille Demand\u00e9e
-sc.412=Echec de Pr\u00e9-condition
-sc.413=Entit\u00e9 de Requ\u00eate Trop Grande
-sc.414=URI de Requ\u00eate Trop Grande
-sc.415=Type de Support Non Support\u00e9
-sc.416=Etendue de Requ\u00eate Irr\u00e9alisable
-sc.417=Echec d'Attente
-sc.422=Entit\u00e9 Ing\u00e9rable
-sc.424=Echec de D\u00e9pendance
-sc.500=Erreur Interne de Servlet
-sc.501=Non Impl\u00e9ment\u00e9
-sc.502=Mauvaise Passerelle
-sc.503=Service Indisponible
-sc.504=D\u00e9passement de D\u00e9lais pour la Passerelle
-sc.505=Version HTTP Non Support\u00e9e
-sc.507=Stockage Insuffisant
diff --git a/java/org/apache/tomcat/util/http/res/LocalStrings_ja.properties b/java/org/apache/tomcat/util/http/res/LocalStrings_ja.properties
deleted file mode 100644
index c65b618..0000000
--- a/java/org/apache/tomcat/util/http/res/LocalStrings_ja.properties
+++ /dev/null
@@ -1,19 +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.
-
-# HttpMessages. The values in this file will be used in HTTP headers and as such
-# may only contain TEXT as defined by RFC 2616. Since Japanese language
-# messages do not meet this requirement, English text is used.
-# The English messages can be found in the default file LocalStrings.properties.
diff --git a/java/org/apache/tomcat/util/log/SystemLogHandler.java b/java/org/apache/tomcat/util/log/SystemLogHandler.java
index 69b3f59..b1af0b0 100644
--- a/java/org/apache/tomcat/util/log/SystemLogHandler.java
+++ b/java/org/apache/tomcat/util/log/SystemLogHandler.java
@@ -39,6 +39,8 @@ public class SystemLogHandler extends PrintStream {
/**
* Construct the handler to capture the output of the given steam.
+ *
+ * @param wrapped The stream to capture
*/
public SystemLogHandler(PrintStream wrapped) {
super(wrapped);
@@ -56,7 +58,7 @@ public class SystemLogHandler extends PrintStream {
/**
- * Thread <-> CaptureLog associations.
+ * Thread <-> CaptureLog associations.
*/
private static final ThreadLocal<Stack<CaptureLog>> logs = new ThreadLocal<>();
@@ -94,7 +96,9 @@ public class SystemLogHandler extends PrintStream {
/**
- * Stop capturing thread's output and return captured data as a String.
+ * Stop capturing thread's output.
+ *
+ * @return The captured data
*/
public static String stopCapture() {
Stack<CaptureLog> stack = logs.get();
@@ -117,6 +121,7 @@ public class SystemLogHandler extends PrintStream {
/**
* Find PrintStream to which the output must be written to.
+ * @return the print stream
*/
protected PrintStream findStream() {
Stack<CaptureLog> stack = logs.get();
diff --git a/java/org/apache/tomcat/util/modeler/AttributeInfo.java b/java/org/apache/tomcat/util/modeler/AttributeInfo.java
index 89586d2..b932fa0 100644
--- a/java/org/apache/tomcat/util/modeler/AttributeInfo.java
+++ b/java/org/apache/tomcat/util/modeler/AttributeInfo.java
@@ -42,7 +42,7 @@ public class AttributeInfo extends FeatureInfo {
// ------------------------------------------------------------- Properties
/**
- * The display name of this attribute.
+ * @return the display name of this attribute.
*/
public String getDisplayName() {
return (this.displayName);
@@ -53,7 +53,7 @@ public class AttributeInfo extends FeatureInfo {
}
/**
- * The name of the property getter method, if non-standard.
+ * @return the name of the property getter method, if non-standard.
*/
public String getGetMethod() {
if(getMethod == null)
@@ -67,6 +67,8 @@ public class AttributeInfo extends FeatureInfo {
/**
* Is this a boolean attribute with an "is" getter?
+ * @return <code>true</code> if this is a boolean attribute
+ * with an "is" getter
*/
public boolean isIs() {
return (this.is);
@@ -79,6 +81,7 @@ public class AttributeInfo extends FeatureInfo {
/**
* Is this attribute readable by management applications?
+ * @return <code>true</code> if readable
*/
public boolean isReadable() {
return (this.readable);
@@ -90,7 +93,7 @@ public class AttributeInfo extends FeatureInfo {
/**
- * The name of the property setter method, if non-standard.
+ * @return the name of the property setter method, if non-standard.
*/
public String getSetMethod() {
if( setMethod == null )
@@ -104,6 +107,7 @@ public class AttributeInfo extends FeatureInfo {
/**
* Is this attribute writable by management applications?
+ * @return <code>true</code> if writable
*/
public boolean isWriteable() {
return (this.writeable);
@@ -119,6 +123,7 @@ public class AttributeInfo extends FeatureInfo {
/**
* Create and return a <code>ModelMBeanAttributeInfo</code> object that
* corresponds to the attribute described by this instance.
+ * @return the attribute info
*/
MBeanAttributeInfo createAttributeInfo() {
// Return our cached information (if any)
@@ -139,6 +144,7 @@ public class AttributeInfo extends FeatureInfo {
* @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?
+ * @return the method name
*/
private String getMethodName(String name, boolean getter, boolean is) {
diff --git a/java/org/apache/tomcat/util/modeler/BaseAttributeFilter.java b/java/org/apache/tomcat/util/modeler/BaseAttributeFilter.java
index 529c251..befe287 100644
--- a/java/org/apache/tomcat/util/modeler/BaseAttributeFilter.java
+++ b/java/org/apache/tomcat/util/modeler/BaseAttributeFilter.java
@@ -100,6 +100,7 @@ public class BaseAttributeFilter implements NotificationFilter {
* 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() {
@@ -124,16 +125,16 @@ public class BaseAttributeFilter implements NotificationFilter {
public boolean isNotificationEnabled(Notification notification) {
if (notification == null)
- return (false);
+ return false;
if (!(notification instanceof AttributeChangeNotification))
- return (false);
+ return false;
AttributeChangeNotification acn =
(AttributeChangeNotification) notification;
if (!AttributeChangeNotification.ATTRIBUTE_CHANGE.equals(acn.getType()))
- return (false);
+ return false;
synchronized (names) {
if (names.size() < 1)
- return (true);
+ return true;
else
return (names.contains(acn.getAttributeName()));
}
diff --git a/java/org/apache/tomcat/util/modeler/BaseModelMBean.java b/java/org/apache/tomcat/util/modeler/BaseModelMBean.java
index 1d27987..2efcf54 100644
--- a/java/org/apache/tomcat/util/modeler/BaseModelMBean.java
+++ b/java/org/apache/tomcat/util/modeler/BaseModelMBean.java
@@ -496,6 +496,7 @@ public class BaseModelMBean implements DynamicMBean, MBeanRegistration, ModelMBe
* 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
diff --git a/java/org/apache/tomcat/util/modeler/FeatureInfo.java b/java/org/apache/tomcat/util/modeler/FeatureInfo.java
index 54330ca..c463a2f 100644
--- a/java/org/apache/tomcat/util/modeler/FeatureInfo.java
+++ b/java/org/apache/tomcat/util/modeler/FeatureInfo.java
@@ -45,7 +45,7 @@ public class FeatureInfo implements Serializable {
// ------------------------------------------------------------- Properties
/**
- * The human-readable description of this feature.
+ * @return the human-readable description of this feature.
*/
public String getDescription() {
return (this.description);
@@ -57,8 +57,8 @@ public class FeatureInfo implements Serializable {
/**
- * 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);
@@ -69,7 +69,7 @@ public class FeatureInfo implements Serializable {
}
/**
- * The fully qualified Java class name of this element.
+ * @return the fully qualified Java class name of this element.
*/
public String getType() {
return (this.type);
diff --git a/java/org/apache/tomcat/util/modeler/ManagedBean.java b/java/org/apache/tomcat/util/modeler/ManagedBean.java
index d4ac071..f561327 100644
--- a/java/org/apache/tomcat/util/modeler/ManagedBean.java
+++ b/java/org/apache/tomcat/util/modeler/ManagedBean.java
@@ -90,7 +90,7 @@ public class ManagedBean implements java.io.Serializable {
/**
- * The collection of attributes for this MBean.
+ * @return the collection of attributes for this MBean.
*/
public AttributeInfo[] getAttributes() {
AttributeInfo result[] = new AttributeInfo[attributes.size()];
@@ -104,6 +104,7 @@ public class ManagedBean implements java.io.Serializable {
* described by this descriptor. If not specified, the standard JMX
* class (<code>javax.management.modelmbean.RequiredModeLMBean</code>)
* will be utilized.
+ * @return the class name
*/
public String getClassName() {
return this.className;
@@ -121,7 +122,7 @@ public class ManagedBean implements java.io.Serializable {
/**
- * The human-readable description of this MBean.
+ * @return the human-readable description of this MBean.
*/
public String getDescription() {
return this.description;
@@ -139,8 +140,8 @@ public class ManagedBean implements java.io.Serializable {
/**
- * The (optional) <code>ObjectName</code> domain in which this MBean
- * should be registered in the MBeanServer.
+ * @return the (optional) <code>ObjectName</code> domain in which
+ * this MBean should be registered in the MBeanServer.
*/
public String getDomain() {
return this.domain;
@@ -152,7 +153,7 @@ public class ManagedBean implements java.io.Serializable {
/**
- * The (optional) group to which this MBean belongs.
+ * @return the (optional) group to which this MBean belongs.
*/
public String getGroup() {
return this.group;
@@ -164,8 +165,8 @@ public class ManagedBean implements java.io.Serializable {
/**
- * 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;
@@ -183,7 +184,7 @@ public class ManagedBean implements java.io.Serializable {
/**
- * The collection of notifications for this MBean.
+ * @return the collection of notifications for this MBean.
*/
public NotificationInfo[] getNotifications() {
return this.notifications;
@@ -191,7 +192,7 @@ public class ManagedBean implements java.io.Serializable {
/**
- * The collection of operations for this MBean.
+ * @return the collection of operations for this MBean.
*/
public OperationInfo[] getOperations() {
OperationInfo[] result = new OperationInfo[operations.size()];
@@ -201,7 +202,7 @@ public class ManagedBean implements java.io.Serializable {
/**
- * The fully qualified name of the Java class of the resource
+ * @return the fully qualified name of the Java class of the resource
* implementation class described by the managed bean described
* by this descriptor.
*/
@@ -274,7 +275,7 @@ public class ManagedBean implements java.io.Serializable {
*
* @param instance Instanced of the managed object, or <code>null</code>
* 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
@@ -342,6 +343,7 @@ public class ManagedBean implements java.io.Serializable {
/**
* Create and return a <code>ModelMBeanInfo</code> object that
* describes this entire managed bean.
+ * @return the MBean info
*/
MBeanInfo getMBeanInfo() {
diff --git a/java/org/apache/tomcat/util/modeler/NotificationInfo.java b/java/org/apache/tomcat/util/modeler/NotificationInfo.java
index 4929b83..098ccf5 100644
--- a/java/org/apache/tomcat/util/modeler/NotificationInfo.java
+++ b/java/org/apache/tomcat/util/modeler/NotificationInfo.java
@@ -71,7 +71,7 @@ public class NotificationInfo extends FeatureInfo {
/**
- * The set of notification types for this MBean.
+ * @return the set of notification types for this MBean.
*/
public String[] getNotifTypes() {
Lock readLock = notifTypesLock.readLock();
@@ -112,6 +112,7 @@ public class NotificationInfo extends FeatureInfo {
/**
* Create and return a <code>ModelMBeanNotificationInfo</code> object that
* corresponds to the attribute described by this instance.
+ * @return the notification info
*/
public MBeanNotificationInfo createNotificationInfo() {
diff --git a/java/org/apache/tomcat/util/modeler/OperationInfo.java b/java/org/apache/tomcat/util/modeler/OperationInfo.java
index 9197d64..55f4883 100644
--- a/java/org/apache/tomcat/util/modeler/OperationInfo.java
+++ b/java/org/apache/tomcat/util/modeler/OperationInfo.java
@@ -55,8 +55,9 @@ public class OperationInfo extends FeatureInfo {
// ------------------------------------------------------------- Properties
/**
- * 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;
@@ -71,7 +72,7 @@ public class OperationInfo extends FeatureInfo {
/**
- * The role of this operation ("getter", "setter", "operation", or
+ * @return the role of this operation ("getter", "setter", "operation", or
* "constructor").
*/
public String getRole() {
@@ -84,7 +85,7 @@ public class OperationInfo extends FeatureInfo {
/**
- * The fully qualified Java class name of the return type for this
+ * @return the fully qualified Java class name of the return type for this
* operation.
*/
public String getReturnType() {
@@ -99,7 +100,7 @@ public class OperationInfo extends FeatureInfo {
}
/**
- * The set of parameters for this operation.
+ * @return the set of parameters for this operation.
*/
public ParameterInfo[] getSignature() {
Lock readLock = parametersLock.readLock();
@@ -138,6 +139,7 @@ public class OperationInfo extends FeatureInfo {
/**
* Create and return a <code>ModelMBeanOperationInfo</code> object that
* corresponds to the attribute described by this instance.
+ * @return the operation info
*/
MBeanOperationInfo createOperationInfo() {
diff --git a/java/org/apache/tomcat/util/modeler/ParameterInfo.java b/java/org/apache/tomcat/util/modeler/ParameterInfo.java
index a6f4edc..758d943 100644
--- a/java/org/apache/tomcat/util/modeler/ParameterInfo.java
+++ b/java/org/apache/tomcat/util/modeler/ParameterInfo.java
@@ -43,6 +43,7 @@ public class ParameterInfo extends FeatureInfo {
/**
* Create and return a <code>MBeanParameterInfo</code> object that
* corresponds to the parameter described by this instance.
+ * @return a parameter info
*/
public MBeanParameterInfo createParameterInfo() {
diff --git a/java/org/apache/tomcat/util/modeler/Registry.java b/java/org/apache/tomcat/util/modeler/Registry.java
index 41debde..a4384c6 100644
--- a/java/org/apache/tomcat/util/modeler/Registry.java
+++ b/java/org/apache/tomcat/util/modeler/Registry.java
@@ -59,10 +59,6 @@ import org.apache.tomcat.util.modeler.modules.ModelerSource;
* 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.
- *
* This class is itself an mbean.
*
* IMPORTANT: public methods not marked with @since x.x are experimental or
@@ -134,9 +130,6 @@ public class Registry implements RegistryMBean, MBeanRegistration {
* Factory method to create (if necessary) and return our
* <code>Registry</code> instance.
*
- * Use this method to obtain a Registry - all other static methods
- * are deprecated and shouldn't be used.
- *
* The current version uses a static - future versions could use
* the thread class loader.
*
@@ -144,7 +137,7 @@ public class Registry implements RegistryMBean, MBeanRegistration {
* loader will be used ( if setUseContextClassLoader is called ) or the
* default registry is returned.
* @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) {
@@ -194,7 +187,8 @@ public class Registry implements RegistryMBean, MBeanRegistration {
searchedPaths=new HashMap<>();
}
- /** Register a bean by creating a modeler mbean and adding it to the
+ /**
+ * 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
@@ -220,7 +214,7 @@ public class Registry implements RegistryMBean, MBeanRegistration {
* @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
@@ -230,10 +224,11 @@ public class Registry implements RegistryMBean, MBeanRegistration {
registerComponent(bean, new ObjectName(oname), type);
}
- /** Unregister a component. We'll first check if it is registered,
+ /**
+ * Unregister a component. We'll first check if it is registered,
* and mask all errors. This is mostly a helper.
*
- * @param oname
+ * @param oname Name used for unregistration
*
* @since 1.1
*/
@@ -247,13 +242,14 @@ public class Registry implements RegistryMBean, MBeanRegistration {
}
- /** Invoke a operation on a list of mbeans. Can be used to implement
+ /**
+ * Invoke a 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
- * @throws Exception
+ * @throws Exception Error invoking operation
* @since 1.1
*/
@Override
@@ -284,12 +280,13 @@ public class Registry implements RegistryMBean, MBeanRegistration {
// -------------------- ID registry --------------------
- /** Return an int ID for faster access. Will be used for notifications
+ /**
+ * 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 An unique id for the domain:name combination
+ * @param name Type of the notification
+ * @return An unique id for the domain:name combination
* @since 1.1
*/
@Override
@@ -346,6 +343,7 @@ public class Registry implements RegistryMBean, MBeanRegistration {
*
* @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) {
@@ -358,10 +356,11 @@ public class Registry implements RegistryMBean, MBeanRegistration {
// -------------------- Helpers --------------------
- /** Get the type of an attribute of the object, from the metadata.
+ /**
+ * Get the type of an attribute of the object, from the metadata.
*
- * @param oname
- * @param attName
+ * @param oname The bean name
+ * @param attName The attribute name
* @return null if metadata about the attribute is not found
* @since 1.1
*/
@@ -386,10 +385,11 @@ public class Registry implements RegistryMBean, MBeanRegistration {
return null;
}
- /** Find the operation info for a method
+ /**
+ * Find the operation info for a method
*
- * @param oname
- * @param opName
+ * @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 )
@@ -410,46 +410,52 @@ public class Registry implements RegistryMBean, MBeanRegistration {
return null;
}
- /** Unregister a component. This is just a helper that
+ /**
+ * Unregister a component. This is just a helper that
* avoids exceptions by checking if the mbean is already registered
*
- * @param oname
+ * @param oname The bean name
*/
public void unregisterComponent( ObjectName oname ) {
try {
- if( getMBeanServer().isRegistered(oname)) {
+ if (oname != null && getMBeanServer().isRegistered(oname)) {
getMBeanServer().unregisterMBean(oname);
}
- } catch( Throwable t ) {
- log.error( "Error unregistering mbean ", t);
+ } catch (Throwable t) {
+ log.error("Error unregistering mbean", t);
}
}
/**
* Factory method to create (if necessary) and return our
* <code>MBeanServer</code> instance.
- *
+ * @return the MBean server
*/
public synchronized MBeanServer getMBeanServer() {
- long t1=System.currentTimeMillis();
-
if (server == null) {
- if( MBeanServerFactory.findMBeanServer(null).size() > 0 ) {
+ long t1 = System.currentTimeMillis();
+ if (MBeanServerFactory.findMBeanServer(null).size() > 0) {
server = MBeanServerFactory.findMBeanServer(null).get(0);
- if( log.isDebugEnabled() ) {
- log.debug("Using existing MBeanServer " + (System.currentTimeMillis() - t1 ));
+ if (log.isDebugEnabled()) {
+ log.debug("Using existing MBeanServer " + (System.currentTimeMillis() - t1));
}
} else {
server = ManagementFactory.getPlatformMBeanServer();
- if( log.isDebugEnabled() ) {
- log.debug("Creating MBeanServer"+ (System.currentTimeMillis() - t1 ));
+ if (log.isDebugEnabled()) {
+ log.debug("Creating MBeanServer" + (System.currentTimeMillis() - t1));
}
}
}
- return (server);
+ return server;
}
- /** Find or load metadata.
+ /**
+ * Find or load metadata.
+ * @param bean The bean
+ * @param beanClass The bean class
+ * @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 {
@@ -496,7 +502,8 @@ public class Registry implements RegistryMBean, MBeanRegistration {
}
- /** EXPERIMENTAL Convert a string to object, based on type. Used by several
+ /**
+ * EXPERIMENTAL 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
*
@@ -531,13 +538,14 @@ public class Registry implements RegistryMBean, MBeanRegistration {
return objValue;
}
- /** Experimental.
+ /**
+ * Experimental. Load descriptors.
*
- * @param sourceType
- * @param source
- * @param param
+ * @param sourceType The source type
+ * @param source The bean
+ * @param param A type to load
* @return List of descriptors
- * @throws Exception
+ * @throws Exception Error loading descriptors
*/
public List<ObjectName> load( String sourceType, Object source,
String param) throws Exception {
@@ -586,13 +594,13 @@ public class Registry implements RegistryMBean, MBeanRegistration {
}
- /** Register a component
- * XXX make it private
+ /**
+ * Register a component
*
- * @param bean
- * @param oname
- * @param type
- * @throws Exception
+ * @param bean The bean
+ * @param oname The object name
+ * @param type The registry type
+ * @throws Exception Error registering component
*/
public void registerComponent(Object bean, ObjectName oname, String type)
throws Exception
@@ -630,10 +638,12 @@ public class Registry implements RegistryMBean, MBeanRegistration {
}
}
- /** Lookup the component descriptor in the package and
+ /**
+ * Lookup the component descriptor in the package and
* in the parent packages.
*
- * @param packageName
+ * @param packageName The package name
+ * @param classLoader The class loader
*/
public void loadDescriptors( String packageName, ClassLoader classLoader ) {
String res=packageName.replace( '.', '/');
@@ -662,11 +672,9 @@ public class Registry implements RegistryMBean, MBeanRegistration {
}
}
- /** Lookup the component descriptor in the package and
+ /**
+ * Lookup the component descriptor in the package and
* in the parent packages.
- *
- * @param beanClass
- * @param type
*/
private void findDescriptor(Class<?> beanClass, String type) {
if( type==null ) {
diff --git a/java/org/apache/tomcat/util/modeler/RegistryMBean.java b/java/org/apache/tomcat/util/modeler/RegistryMBean.java
index 3a7c66f..17317ea 100644
--- a/java/org/apache/tomcat/util/modeler/RegistryMBean.java
+++ b/java/org/apache/tomcat/util/modeler/RegistryMBean.java
@@ -40,18 +40,20 @@ import javax.management.ObjectName;
*/
public interface RegistryMBean {
- /** Invoke an operation on a set of mbeans.
+ /**
+ * Invoke an operation on a set of mbeans.
*
* @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
- * @throws Exception
+ * @throws Exception Error invoking operation
*/
- public void invoke( List<ObjectName> mbeans, String operation, boolean failFirst )
+ public void invoke(List<ObjectName> mbeans, String operation, boolean failFirst)
throws Exception;
- /** Register a bean by creating a modeler mbean and adding it to the
+ /**
+ * 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
@@ -77,23 +79,26 @@ public interface RegistryMBean {
* @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
*/
public void registerComponent(Object bean, String oname, String type)
throws Exception;
- /** Unregister a component. We'll first check if it is registered,
+ /**
+ * Unregister a component. We'll first check if it is registered,
* and mask all errors. This is mostly a helper.
*
- * @param oname
+ * @param oname The name used by the bean
*
* @since 1.1
*/
- public void unregisterComponent( String oname );
+ public void unregisterComponent(String oname);
- /** Return an int ID for faster access. Will be used for notifications
+ /**
+ * Return an int ID for faster access. Will be used for notifications
* and for other operations we want to optimize.
*
* @param domain Namespace
@@ -101,10 +106,11 @@ public interface RegistryMBean {
* @return An unique id for the domain:name combination
* @since 1.1
*/
- public int getId( String domain, String name);
+ public int getId(String domain, String name);
- /** Reset all metadata cached by this registry. Should be called
+ /**
+ * 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.
diff --git a/java/org/apache/tomcat/util/modeler/mbeans-descriptors.dtd b/java/org/apache/tomcat/util/modeler/mbeans-descriptors.dtd
index fd5c983..5575083 100644
--- a/java/org/apache/tomcat/util/modeler/mbeans-descriptors.dtd
+++ b/java/org/apache/tomcat/util/modeler/mbeans-descriptors.dtd
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="ISO-8859-1"?>
+<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
diff --git a/java/org/apache/tomcat/util/modeler/modules/MbeansDescriptorsIntrospectionSource.java b/java/org/apache/tomcat/util/modeler/modules/MbeansDescriptorsIntrospectionSource.java
index 788eac0..f3637f4 100644
--- a/java/org/apache/tomcat/util/modeler/modules/MbeansDescriptorsIntrospectionSource.java
+++ b/java/org/apache/tomcat/util/modeler/modules/MbeansDescriptorsIntrospectionSource.java
@@ -49,9 +49,10 @@ public class MbeansDescriptorsIntrospectionSource extends ModelerSource
this.registry=reg;
}
- /** Used if a single component is loaded
+ /**
+ * Used if a single component is loaded
*
- * @param type
+ * @param type The type
*/
public void setType( String type ) {
this.type=type;
diff --git a/java/org/apache/tomcat/util/modeler/modules/ModelerSource.java b/java/org/apache/tomcat/util/modeler/modules/ModelerSource.java
index 582b2f1..d2699b8 100644
--- a/java/org/apache/tomcat/util/modeler/modules/ModelerSource.java
+++ b/java/org/apache/tomcat/util/modeler/modules/ModelerSource.java
@@ -31,10 +31,11 @@ public abstract class ModelerSource {
/**
* Load data, returns a list of items.
*
- * @param registry
- * @param type
+ * @param registry The registry
+ * @param type The bean registry type
* @param source Introspected object or some other source
- * @throws Exception
+ * @return a list of object names
+ * @throws Exception Error loading descriptors
*/
public abstract List<ObjectName> loadDescriptors(Registry registry,
String type, Object source) throws Exception;
diff --git a/java/org/apache/tomcat/util/net/AbstractEndpoint.java b/java/org/apache/tomcat/util/net/AbstractEndpoint.java
index 1773153..c05bb87 100644
--- a/java/org/apache/tomcat/util/net/AbstractEndpoint.java
+++ b/java/org/apache/tomcat/util/net/AbstractEndpoint.java
@@ -20,21 +20,19 @@ import java.io.OutputStreamWriter;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.HashMap;
-import java.util.Iterator;
+import java.util.List;
import java.util.Set;
-import java.util.StringTokenizer;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.Executor;
+import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.TimeUnit;
-import javax.net.ssl.KeyManagerFactory;
-import javax.net.ssl.SSLEngine;
-
import org.apache.juli.logging.Log;
+import org.apache.tomcat.util.ExceptionUtils;
import org.apache.tomcat.util.IntrospectionUtils;
-import org.apache.tomcat.util.compat.JreCompat;
+import org.apache.tomcat.util.collections.SynchronizedStack;
import org.apache.tomcat.util.net.AbstractEndpoint.Acceptor.AcceptorState;
import org.apache.tomcat.util.res.StringManager;
import org.apache.tomcat.util.threads.LimitLatch;
@@ -42,7 +40,9 @@ import org.apache.tomcat.util.threads.ResizableExecutor;
import org.apache.tomcat.util.threads.TaskQueue;
import org.apache.tomcat.util.threads.TaskThreadFactory;
import org.apache.tomcat.util.threads.ThreadPoolExecutor;
+
/**
+ * @param <S> The type for the sockets managed by this endpoint.
*
* @author Mladen Turk
* @author Remy Maucherat
@@ -51,11 +51,10 @@ public abstract class AbstractEndpoint<S> {
// -------------------------------------------------------------- Constants
- protected static final String DEFAULT_CIPHERS = "HIGH:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!kRSA";
+ protected static final StringManager sm = StringManager.getManager(AbstractEndpoint.class);
- protected static final StringManager sm = StringManager.getManager("org.apache.tomcat.util.net.res");
+ public static interface Handler<S> {
- public static interface Handler {
/**
* Different types of socket states to react upon.
*/
@@ -67,12 +66,51 @@ public abstract class AbstractEndpoint<S> {
/**
+ * Process the provided socket with the given current status.
+ *
+ * @param socket The socket to process
+ * @param status The current socket status
+ *
+ * @return The state of the socket after processing
+ */
+ public SocketState process(SocketWrapperBase<S> socket,
+ SocketEvent status);
+
+
+ /**
* Obtain the GlobalRequestProcessor associated with the handler.
+ *
+ * @return the GlobalRequestProcessor
*/
public Object getGlobal();
/**
+ * Obtain the currently open sockets.
+ *
+ * @return The sockets for which the handler is tracking a currently
+ * open connection
+ */
+ public Set<S> getOpenSockets();
+
+ /**
+ * Release any resources associated with the given SocketWrapper.
+ *
+ * @param socketWrapper The socketWrapper to release resources for
+ */
+ public void release(SocketWrapperBase<S> socketWrapper);
+
+
+ /**
+ * 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.
+ */
+ public void pause();
+
+
+ /**
* Recycle resources associated with the handler.
*/
public void recycle();
@@ -106,59 +144,8 @@ public abstract class AbstractEndpoint<S> {
private static final int MAX_ERROR_DELAY = 1600;
- /**
- * Async timeout thread
- */
- protected class AsyncTimeout implements Runnable {
-
- private volatile boolean asyncTimeoutRunning = true;
-
- /**
- * The background thread that checks async requests and fires the
- * timeout if there has been no activity.
- */
- @Override
- public void run() {
-
- // Loop until we receive a shutdown command
- while (asyncTimeoutRunning) {
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- // Ignore
- }
- long now = System.currentTimeMillis();
- for (SocketWrapper<S> socket : waitingRequests) {
- long access = socket.getLastAccess();
- if (socket.getTimeout() > 0 && (now - access) > socket.getTimeout()) {
- // Prevent multiple timeouts
- socket.setTimeout(-1);
- processSocket(socket, SocketStatus.TIMEOUT, true);
- }
- }
-
- // Loop if endpoint is paused
- while (paused && asyncTimeoutRunning) {
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- // Ignore
- }
- }
-
- }
- }
-
-
- protected void stop() {
- asyncTimeoutRunning = false;
- }
- }
-
-
// ----------------------------------------------------------------- Fields
-
/**
* Running state of the endpoint.
*/
@@ -194,9 +181,106 @@ public abstract class AbstractEndpoint<S> {
*/
protected Acceptor[] acceptors;
+ /**
+ * Cache for SocketProcessor objects
+ */
+ protected SynchronizedStack<SocketProcessorBase<S>> processorCache;
// ----------------------------------------------------------------- Properties
+ private String defaultSSLHostConfigName = SSLHostConfig.DEFAULT_SSL_HOST_NAME;
+ public String getDefaultSSLHostConfigName() {
+ return defaultSSLHostConfigName;
+ }
+ public void setDefaultSSLHostConfigName(String defaultSSLHostConfigName) {
+ this.defaultSSLHostConfigName = defaultSSLHostConfigName;
+ }
+
+
+ protected ConcurrentMap<String,SSLHostConfig> sslHostConfigs = new ConcurrentHashMap<>();
+ public void addSslHostConfig(SSLHostConfig sslHostConfig) throws IllegalArgumentException {
+ String key = sslHostConfig.getHostName();
+ if (key == null || key.length() == 0) {
+ throw new IllegalArgumentException(sm.getString("endpoint.noSslHostName"));
+ }
+ sslHostConfig.setConfigType(getSslConfigType());
+ if (bindState != BindState.UNBOUND) {
+ try {
+ createSSLContext(sslHostConfig);
+ } catch (Exception e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+ SSLHostConfig duplicate = sslHostConfigs.putIfAbsent(key, sslHostConfig);
+ if (duplicate != null) {
+ releaseSSLContext(sslHostConfig);
+ throw new IllegalArgumentException(sm.getString("endpoint.duplicateSslHostName", key));
+ }
+ }
+ public SSLHostConfig[] findSslHostConfigs() {
+ return sslHostConfigs.values().toArray(new SSLHostConfig[0]);
+ }
+
+ protected abstract SSLHostConfig.Type getSslConfigType();
+
+ /**
+ * Create the SSLContextfor the 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;
+
+ /**
+ * Release the SSLContext, if any, associated with the SSLHostConfig.
+ *
+ * @param sslHostConfig The SSLHostConfig for which the SSLContext should be
+ * released
+ */
+ protected abstract void releaseSSLContext(SSLHostConfig sslHostConfig);
+
+ protected SSLHostConfig getSSLHostConfig(String sniHostName) {
+ SSLHostConfig result = null;
+
+ if (sniHostName != null) {
+ // First choice - direct match
+ result = sslHostConfigs.get(sniHostName);
+ if (result != null) {
+ return result;
+ }
+ // Second choice, wildcard match
+ int indexOfDot = sniHostName.indexOf('.');
+ if (indexOfDot > -1) {
+ result = sslHostConfigs.get("*" + sniHostName.substring(indexOfDot));
+ }
+ }
+
+ // Fall-back. Use the default
+ if (result == null) {
+ result = sslHostConfigs.get(getDefaultSSLHostConfigName());
+ }
+ if (result == null) {
+ // Should never happen.
+ throw new IllegalStateException();
+ }
+ return result;
+ }
+
+
+ /**
+ * 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).
@@ -216,7 +300,7 @@ public abstract class AbstractEndpoint<S> {
/**
* Acceptor thread count.
*/
- protected int acceptorThreadCount = 0;
+ protected int acceptorThreadCount = 1;
public void setAcceptorThreadCount(int acceptorThreadCount) {
this.acceptorThreadCount = acceptorThreadCount;
@@ -319,7 +403,7 @@ public abstract class AbstractEndpoint<S> {
private boolean bindOnInit = true;
public boolean getBindOnInit() { return bindOnInit; }
public void setBindOnInit(boolean b) { this.bindOnInit = b; }
- private BindState bindState = BindState.UNBOUND;
+ private volatile BindState bindState = BindState.UNBOUND;
/**
* Keepalive timeout, if not set the soTimeout is used.
@@ -339,6 +423,9 @@ public abstract class AbstractEndpoint<S> {
/**
* Socket TCP no delay.
+ *
+ * @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); }
@@ -346,6 +433,9 @@ public abstract class AbstractEndpoint<S> {
/**
* Socket linger.
+ *
+ * @return The current socket linger time for sockets created by this
+ * endpoint
*/
public int getSoLinger() { return socketProperties.getSoLingerTime(); }
public void setSoLinger(int soLinger) {
@@ -356,6 +446,8 @@ public abstract class AbstractEndpoint<S> {
/**
* Socket timeout.
+ *
+ * @return The current socket timeout for sockets created by this endpoint
*/
public int getSoTimeout() { return socketProperties.getSoTimeout(); }
public void setSoTimeout(int soTimeout) { socketProperties.setSoTimeout(soTimeout); }
@@ -414,9 +506,6 @@ public abstract class AbstractEndpoint<S> {
return -1;
}
}
- protected int getMaxThreadsInternal() {
- return maxThreads;
- }
/**
@@ -479,12 +568,27 @@ public abstract class AbstractEndpoint<S> {
protected abstract boolean getDeferAccept();
+ protected final List<String> negotiableProtocols = new ArrayList<>();
+ public void addNegotiatedProtocol(String negotiableProtocol) {
+ negotiableProtocols.add(negotiableProtocol);
+ }
+ public boolean hasNegotiableProtocols() {
+ return (negotiableProtocols.size() > 0);
+ }
+
+
+ /**
+ * Handling of accepted sockets.
+ */
+ private Handler<S> handler = null;
+ public void setHandler(Handler<S> handler ) { this.handler = handler; }
+ public Handler<S> 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. One example of such a
- * sub-component is the
- * {@link org.apache.tomcat.util.net.ServerSocketFactory}.
+ * properties available on those sub-components.
*/
protected HashMap<String, Object> attributes = new HashMap<>();
@@ -494,6 +598,9 @@ public abstract class AbstractEndpoint<S> {
* {@link org.apache.coyote.ProtocolHandler} needs to be made available to
* sub-components. 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
*/
public void setAttribute(String name, Object value) {
if (getLog().isTraceEnabled()) {
@@ -503,6 +610,11 @@ public abstract class AbstractEndpoint<S> {
}
/**
* Used by sub-components to retrieve configuration information.
+ *
+ * @param key The name of the property for which the value should be
+ * retrieved
+ *
+ * @return The value of the specified property
*/
public Object getAttribute(String key) {
Object value = attributes.get(key);
@@ -654,7 +766,6 @@ public abstract class AbstractEndpoint<S> {
if (getSocketProperties().getUnlockTimeout() > utmo)
utmo = getSocketProperties().getUnlockTimeout();
s.setSoTimeout(stmo);
- // TODO Consider hard-coding to s.setSoLinger(true,0)
s.setSoLinger(getSocketProperties().getSoLingerOn(),getSocketProperties().getSoLingerTime());
if (getLog().isDebugEnabled()) {
getLog().debug("About to unlock socket for:"+saddr);
@@ -703,43 +814,48 @@ public abstract class AbstractEndpoint<S> {
* selected the socket.
*
* @param socketWrapper The socket wrapper to process
- * @param socketStatus The input status to the processing
+ * @param event The socket event to be processed
* @param dispatch Should the processing be performed on a new
* container thread
+ *
+ * @return if processing was triggered successfully
*/
- public abstract void processSocket(SocketWrapper<S> socketWrapper,
- SocketStatus socketStatus, boolean dispatch);
-
-
- public void executeNonBlockingDispatches(SocketWrapper<S> socketWrapper) {
- /*
- * This method is called when non-blocking IO is initiated by defining
- * a read and/or write listener in a non-container thread. It is called
- * once the non-container thread completes so that the first calls to
- * onWritePossible() and/or onDataAvailable() as appropriate are made by
- * the container.
- *
- * Processing the dispatches requires (for BIO and APR/native at least)
- * that the socket has been added to the waitingRequests queue. This may
- * not have occurred by the time that the non-container thread completes
- * triggering the call to this method. Therefore, the coded syncs on the
- * SocketWrapper as the container thread that initiated this
- * non-container thread holds a lock on the SocketWrapper. The container
- * thread will add the socket to the waitingRequests queue before
- * releasing the lock on the socketWrapper. Therefore, by obtaining the
- * lock on socketWrapper before processing the dispatches, we can be
- * sure that the socket has been added to the waitingRequests queue.
- */
- synchronized (socketWrapper) {
- Iterator<DispatchType> dispatches = socketWrapper.getIteratorAndClearDispatches();
-
- while (dispatches != null && dispatches.hasNext()) {
- DispatchType dispatchType = dispatches.next();
- processSocket(socketWrapper, dispatchType.getSocketStatus(), false);
+ public boolean processSocket(SocketWrapperBase<S> socketWrapper,
+ SocketEvent event, boolean dispatch) {
+ try {
+ if (socketWrapper == null) {
+ return false;
}
+ SocketProcessorBase<S> sc = processorCache.pop();
+ if (sc == null) {
+ sc = createSocketProcessor(socketWrapper, event);
+ } else {
+ sc.reset(socketWrapper, event);
+ }
+ Executor executor = getExecutor();
+ if (dispatch && executor != null) {
+ executor.execute(sc);
+ } else {
+ sc.run();
+ }
+ } catch (RejectedExecutionException ree) {
+ getLog().warn(sm.getString("endpoint.executor.fail", socketWrapper) , ree);
+ return false;
+ } catch (Throwable t) {
+ ExceptionUtils.handleThrowable(t);
+ // This means we got an OOM or similar creating a thread, or that
+ // the pool and its queue are full
+ getLog().error(sm.getString("endpoint.process.fail"), t);
+ return false;
}
+ return true;
}
+
+ protected abstract SocketProcessorBase<S> createSocketProcessor(
+ SocketWrapperBase<S> socketWrapper, SocketEvent event);
+
+
// ------------------------------------------------------- Lifecycle methods
/*
@@ -754,21 +870,13 @@ public abstract class AbstractEndpoint<S> {
public abstract void startInternal() throws Exception;
public abstract void stopInternal() throws Exception;
- public final void init() throws Exception {
- testServerCipherSuitesOrderSupport();
+ public void init() throws Exception {
if (bindOnInit) {
bind();
bindState = BindState.BOUND_ON_INIT;
}
}
- protected void testServerCipherSuitesOrderSupport() {
- // Only test this feature if the user explicitly requested its use.
- if(!"".equals(getUseServerCipherSuitesOrder().trim()) && !JreCompat.isJre8Available()) {
- throw new UnsupportedOperationException(
- sm.getString("endpoint.jsse.cannotHonorServerCipherOrder"));
- }
- }
public final void start() throws Exception {
if (bindState == BindState.UNBOUND) {
@@ -796,6 +904,7 @@ public abstract class AbstractEndpoint<S> {
/**
* Hook to allow Endpoints to provide a specific Acceptor implementation.
+ * @return the acceptor
*/
protected abstract Acceptor createAcceptor();
@@ -807,6 +916,7 @@ public abstract class AbstractEndpoint<S> {
if (running && !paused) {
paused = true;
unlockAccept();
+ getHandler().pause();
}
}
@@ -836,13 +946,6 @@ public abstract class AbstractEndpoint<S> {
}
protected abstract Log getLog();
- // Flags to indicate optional feature support
- // Some of these are always hard-coded, some are hard-coded to false (i.e.
- // the endpoint does not support them) and some are configurable.
- public abstract boolean getUseSendfile();
- public abstract boolean getUseComet();
- public abstract boolean getUseCometTimeout();
- public abstract boolean getUsePolling();
protected LimitLatch initializeConnectionLatch() {
if (maxConnections==-1) return null;
@@ -870,7 +973,7 @@ public abstract class AbstractEndpoint<S> {
if (latch!=null) {
long result = latch.countDown();
if (result<0) {
- getLog().warn("Incorrect connection count, multiple socket.close called on the same socket." );
+ getLog().warn(sm.getString("endpoint.warn.incorrectConnectionCount"));
}
return result;
} else return -1;
@@ -905,182 +1008,6 @@ public abstract class AbstractEndpoint<S> {
} else {
return MAX_ERROR_DELAY;
}
-
- }
-
- // -------------------- SSL related properties --------------------
-
- private String algorithm = KeyManagerFactory.getDefaultAlgorithm();
- public String getAlgorithm() { return algorithm;}
- public void setAlgorithm(String s ) { this.algorithm = s;}
-
- private String clientAuth = "false";
- public String getClientAuth() { return clientAuth;}
- public void setClientAuth(String s ) { this.clientAuth = s;}
-
- private String keystoreFile = System.getProperty("user.home")+"/.keystore";
- public String getKeystoreFile() { return keystoreFile;}
- public void setKeystoreFile(String s ) { keystoreFile = s; }
-
- private String keystorePass = null;
- public String getKeystorePass() { return keystorePass;}
- public void setKeystorePass(String s ) { this.keystorePass = s;}
-
- private String keystoreType = "JKS";
- public String getKeystoreType() { return keystoreType;}
- public void setKeystoreType(String s ) { this.keystoreType = s;}
-
- private String keystoreProvider = null;
- public String getKeystoreProvider() { return keystoreProvider;}
- public void setKeystoreProvider(String s ) { this.keystoreProvider = s;}
-
- private String sslProtocol = Constants.SSL_PROTO_TLS;
- public String getSslProtocol() { return sslProtocol;}
- public void setSslProtocol(String s) { sslProtocol = s;}
-
- private String ciphers = DEFAULT_CIPHERS;
- public String getCiphers() { return ciphers;}
- public void setCiphers(String s) {
- ciphers = s;
- }
- /**
- * @return The ciphers in use by this Endpoint
- */
- public abstract String[] getCiphersUsed();
-
- private String useServerCipherSuitesOrder = "";
- public String getUseServerCipherSuitesOrder() { return useServerCipherSuitesOrder;}
- public void setUseServerCipherSuitesOrder(String s) { this.useServerCipherSuitesOrder = s;}
-
- private String keyAlias = null;
- public String getKeyAlias() { return keyAlias;}
- public void setKeyAlias(String s ) { keyAlias = s;}
-
- private String keyPass = null;
- public String getKeyPass() { return keyPass;}
- public void setKeyPass(String s ) { this.keyPass = s;}
-
- private String truststoreFile = System.getProperty("javax.net.ssl.trustStore");
- public String getTruststoreFile() {return truststoreFile;}
- public void setTruststoreFile(String s) { truststoreFile = s; }
-
- private String truststorePass =
- System.getProperty("javax.net.ssl.trustStorePassword");
- public String getTruststorePass() {return truststorePass;}
- public void setTruststorePass(String truststorePass) {
- this.truststorePass = truststorePass;
- }
-
- private String truststoreType =
- System.getProperty("javax.net.ssl.trustStoreType");
- public String getTruststoreType() {return truststoreType;}
- public void setTruststoreType(String truststoreType) {
- this.truststoreType = truststoreType;
- }
-
- private String truststoreProvider = null;
- public String getTruststoreProvider() {return truststoreProvider;}
- public void setTruststoreProvider(String truststoreProvider) {
- this.truststoreProvider = truststoreProvider;
- }
-
- private String truststoreAlgorithm = null;
- public String getTruststoreAlgorithm() {return truststoreAlgorithm;}
- public void setTruststoreAlgorithm(String truststoreAlgorithm) {
- this.truststoreAlgorithm = truststoreAlgorithm;
- }
-
- private String trustManagerClassName = null;
- public String getTrustManagerClassName() {return trustManagerClassName;}
- public void setTrustManagerClassName(String trustManagerClassName) {
- this.trustManagerClassName = trustManagerClassName;
- }
-
- private String crlFile = null;
- public String getCrlFile() {return crlFile;}
- public void setCrlFile(String crlFile) {
- this.crlFile = crlFile;
- }
-
- private String trustMaxCertLength = null;
- public String getTrustMaxCertLength() {return trustMaxCertLength;}
- public void setTrustMaxCertLength(String trustMaxCertLength) {
- this.trustMaxCertLength = trustMaxCertLength;
- }
-
- private String sessionCacheSize = null;
- public String getSessionCacheSize() { return sessionCacheSize;}
- public void setSessionCacheSize(String s) { sessionCacheSize = s;}
-
- private String sessionTimeout = "86400";
- public String getSessionTimeout() { return sessionTimeout;}
- public void setSessionTimeout(String s) { sessionTimeout = s;}
-
- private String allowUnsafeLegacyRenegotiation = null;
- public String getAllowUnsafeLegacyRenegotiation() {
- return allowUnsafeLegacyRenegotiation;
- }
- public void setAllowUnsafeLegacyRenegotiation(String s) {
- allowUnsafeLegacyRenegotiation = s;
- }
-
-
- private String[] sslEnabledProtocolsarr = new String[0];
- public String[] getSslEnabledProtocolsArray() {
- return this.sslEnabledProtocolsarr;
- }
- public void setSslEnabledProtocols(String s) {
- if (s == null) {
- this.sslEnabledProtocolsarr = new String[0];
- } else {
- ArrayList<String> sslEnabledProtocols = new ArrayList<>();
- StringTokenizer t = new StringTokenizer(s,",");
- while (t.hasMoreTokens()) {
- String p = t.nextToken().trim();
- if (p.length() > 0) {
- sslEnabledProtocols.add(p);
- }
- }
- sslEnabledProtocolsarr = sslEnabledProtocols.toArray(
- new String[sslEnabledProtocols.size()]);
- }
- }
-
-
- protected final Set<SocketWrapper<S>> waitingRequests = Collections
- .newSetFromMap(new ConcurrentHashMap<SocketWrapper<S>, Boolean>());
- public void removeWaitingRequest(SocketWrapper<S> socketWrapper) {
- waitingRequests.remove(socketWrapper);
- }
-
-
- /**
- * Configures SSLEngine to honor cipher suites ordering based upon
- * endpoint configuration.
- */
- protected void configureUseServerCipherSuitesOrder(SSLEngine engine) {
- String useServerCipherSuitesOrderStr = this
- .getUseServerCipherSuitesOrder().trim();
-
- // Only use this feature if the user explicitly requested its use.
- if(!"".equals(useServerCipherSuitesOrderStr)) {
- boolean useServerCipherSuitesOrder =
- ("true".equalsIgnoreCase(useServerCipherSuitesOrderStr)
- || "yes".equalsIgnoreCase(useServerCipherSuitesOrderStr));
- JreCompat.getInstance().setUseServerCipherSuitesOrder(engine,
- useServerCipherSuitesOrder);
- }
- }
-
- /**
- * The async timeout thread.
- */
- private AsyncTimeout asyncTimeout = null;
- public AsyncTimeout getAsyncTimeout() {
- return asyncTimeout;
- }
- public void setAsyncTimeout(AsyncTimeout asyncTimeout) {
- this.asyncTimeout = asyncTimeout;
}
}
diff --git a/java/org/apache/tomcat/util/net/AbstractJsseEndpoint.java b/java/org/apache/tomcat/util/net/AbstractJsseEndpoint.java
new file mode 100644
index 0000000..96b4bd0
--- /dev/null
+++ b/java/org/apache/tomcat/util/net/AbstractJsseEndpoint.java
@@ -0,0 +1,240 @@
+/*
+ * 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.Iterator;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLSessionContext;
+
+import org.apache.tomcat.util.compat.JreCompat;
+import org.apache.tomcat.util.net.SSLHostConfig.Type;
+import org.apache.tomcat.util.net.openssl.OpenSSLImplementation;
+import org.apache.tomcat.util.net.openssl.ciphers.Cipher;
+
+public abstract class AbstractJsseEndpoint<S> extends AbstractEndpoint<S> {
+
+ private String sslImplementationName = null;
+ private int sniParseLimit = 64 * 1024;
+
+ private SSLImplementation sslImplementation = null;
+
+ public String getSslImplementationName() {
+ return sslImplementationName;
+ }
+
+
+ public void setSslImplementationName(String s) {
+ this.sslImplementationName = s;
+ }
+
+
+ public SSLImplementation getSslImplementation() {
+ return sslImplementation;
+ }
+
+
+ public int getSniParseLimit() {
+ return sniParseLimit;
+ }
+
+
+ public void setSniParseLimit(int sniParseLimit) {
+ this.sniParseLimit = sniParseLimit;
+ }
+
+
+
+ @Override
+ protected Type getSslConfigType() {
+ if (OpenSSLImplementation.class.getName().equals(sslImplementationName)) {
+ return SSLHostConfig.Type.EITHER;
+ } else {
+ return SSLHostConfig.Type.JSSE;
+ }
+ }
+
+
+ protected void initialiseSsl() throws Exception {
+ if (isSSLEnabled()) {
+ sslImplementation = SSLImplementation.getInstance(getSslImplementationName());
+
+ for (SSLHostConfig sslHostConfig : sslHostConfigs.values()) {
+ createSSLContext(sslHostConfig);
+ }
+ }
+ }
+
+
+ @Override
+ protected void createSSLContext(SSLHostConfig sslHostConfig) throws IllegalArgumentException {
+ boolean firstCertificate = true;
+ for (SSLHostConfigCertificate certificate : sslHostConfig.getCertificates(true)) {
+ SSLUtil sslUtil = sslImplementation.getSSLUtil(certificate);
+ if (firstCertificate) {
+ firstCertificate = false;
+ sslHostConfig.setEnabledProtocols(sslUtil.getEnabledProtocols());
+ sslHostConfig.setEnabledCiphers(sslUtil.getEnabledCiphers());
+ }
+
+ SSLContext sslContext;
+ try {
+ sslContext = sslUtil.createSSLContext(negotiableProtocols);
+ sslContext.init(sslUtil.getKeyManagers(), sslUtil.getTrustManagers(), null);
+ } catch (Exception e) {
+ throw new IllegalArgumentException(e);
+ }
+
+ SSLSessionContext sessionContext = sslContext.getServerSessionContext();
+ if (sessionContext != null) {
+ sslUtil.configureSessionContext(sessionContext);
+ }
+ certificate.setSslContext(sslContext);
+ }
+ }
+
+
+ protected void destroySsl() throws Exception {
+ if (isSSLEnabled()) {
+ for (SSLHostConfig sslHostConfig : sslHostConfigs.values()) {
+ releaseSSLContext(sslHostConfig);
+ }
+ }
+ }
+
+
+ @Override
+ protected void releaseSSLContext(SSLHostConfig sslHostConfig) {
+ for (SSLHostConfigCertificate certificate : sslHostConfig.getCertificates(true)) {
+ if (certificate.getSslContext() != null) {
+ SSLContext sslContext = certificate.getSslContext();
+ if (sslContext != null) {
+ sslContext.destroy();
+ }
+ }
+ }
+ }
+
+
+ protected SSLEngine createSSLEngine(String sniHostName, List<Cipher> clientRequestedCiphers) {
+ SSLHostConfig sslHostConfig = getSSLHostConfig(sniHostName);
+
+ SSLHostConfigCertificate certificate = selectCertificate(sslHostConfig, clientRequestedCiphers);
+
+ SSLContext sslContext = certificate.getSslContext();
+ if (sslContext == null) {
+ throw new IllegalStateException(
+ sm.getString("endpoint.jsse.noSslContext", sniHostName));
+ }
+
+ SSLEngine engine = sslContext.createSSLEngine();
+ switch (sslHostConfig.getCertificateVerification()) {
+ case NONE:
+ engine.setNeedClientAuth(false);
+ engine.setWantClientAuth(false);
+ break;
+ case OPTIONAL:
+ case OPTIONAL_NO_CA:
+ engine.setWantClientAuth(true);
+ break;
+ case REQUIRED:
+ engine.setNeedClientAuth(true);
+ break;
+ }
+ engine.setUseClientMode(false);
+ engine.setEnabledCipherSuites(sslHostConfig.getEnabledCiphers());
+ engine.setEnabledProtocols(sslHostConfig.getEnabledProtocols());
+
+ String honorCipherOrderStr = sslHostConfig.getHonorCipherOrder();
+ if (honorCipherOrderStr != null) {
+ boolean honorCipherOrder = Boolean.parseBoolean(honorCipherOrderStr);
+ JreCompat.getInstance().setUseServerCipherSuitesOrder(engine, honorCipherOrder);
+ }
+ return engine;
+ }
+
+
+ private SSLHostConfigCertificate selectCertificate(
+ SSLHostConfig sslHostConfig, List<Cipher> clientCiphers) {
+
+ Set<SSLHostConfigCertificate> certificates = sslHostConfig.getCertificates(true);
+ if (certificates.size() == 1) {
+ return certificates.iterator().next();
+ }
+
+ LinkedHashSet<Cipher> serverCiphers = sslHostConfig.getCipherList();
+
+ List<Cipher> candidateCiphers = new ArrayList<>();
+ if (Boolean.parseBoolean(sslHostConfig.getHonorCipherOrder())) {
+ candidateCiphers.addAll(serverCiphers);
+ candidateCiphers.retainAll(clientCiphers);
+ } else {
+ candidateCiphers.addAll(clientCiphers);
+ candidateCiphers.retainAll(serverCiphers);
+ }
+
+ Iterator<Cipher> candidateIter = candidateCiphers.iterator();
+ while (candidateIter.hasNext()) {
+ Cipher candidate = candidateIter.next();
+ for (SSLHostConfigCertificate certificate : certificates) {
+ if (certificate.getType().isCompatibleWith(candidate.getAu())) {
+ return certificate;
+ }
+ }
+ }
+
+ // No matches. Just return the first certificate. The handshake will
+ // then fail due to no matching ciphers.
+ return certificates.iterator().next();
+ }
+
+
+ @Override
+ public void init() throws Exception {
+ testServerCipherSuitesOrderSupport();
+ super.init();
+ }
+
+
+ private void testServerCipherSuitesOrderSupport() {
+ // Only need to test for this if running on Java < 8 and not using the
+ // OpenSSL SSLImplementation
+ if(!JreCompat.isJre8Available() &&
+ !OpenSSLImplementation.class.getName().equals(getSslImplementationName())) {
+ for (SSLHostConfig sslHostConfig : sslHostConfigs.values()) {
+ if (sslHostConfig.getHonorCipherOrder() != null) {
+ throw new UnsupportedOperationException(
+ sm.getString("endpoint.jsse.cannotHonorServerCipherOrder"));
+ }
+ }
+ }
+ }
+
+
+ @Override
+ public void unbind() throws Exception {
+ for (SSLHostConfig sslHostConfig : sslHostConfigs.values()) {
+ for (SSLHostConfigCertificate certificate : sslHostConfig.getCertificates(true)) {
+ certificate.setSslContext(null);
+ }
+ }
+ }
+}
diff --git a/java/org/apache/tomcat/util/net/ApplicationBufferHandler.java b/java/org/apache/tomcat/util/net/ApplicationBufferHandler.java
new file mode 100644
index 0000000..d9a22ed
--- /dev/null
+++ b/java/org/apache/tomcat/util/net/ApplicationBufferHandler.java
@@ -0,0 +1,33 @@
+/*
+ * 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.nio.ByteBuffer;
+
+/**
+ * Callback interface to be able to expand buffers when buffer overflow
+ * exceptions happen or to replace buffers
+ */
+public interface ApplicationBufferHandler {
+
+ public void setByteBuffer(ByteBuffer buffer);
+
+ public ByteBuffer getByteBuffer();
+
+ public void expand(int size);
+
+}
diff --git a/java/org/apache/tomcat/util/net/AprEndpoint.java b/java/org/apache/tomcat/util/net/AprEndpoint.java
index 2a57d08..af85c40 100644
--- a/java/org/apache/tomcat/util/net/AprEndpoint.java
+++ b/java/org/apache/tomcat/util/net/AprEndpoint.java
@@ -16,15 +16,21 @@
*/
package org.apache.tomcat.util.net;
+import java.io.EOFException;
+import java.io.IOException;
+import java.net.SocketTimeoutException;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
-import java.util.HashSet;
+import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.Executor;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
@@ -37,13 +43,18 @@ import org.apache.tomcat.jni.Poll;
import org.apache.tomcat.jni.Pool;
import org.apache.tomcat.jni.SSL;
import org.apache.tomcat.jni.SSLContext;
+import org.apache.tomcat.jni.SSLContext.SNICallBack;
import org.apache.tomcat.jni.SSLSocket;
import org.apache.tomcat.jni.Sockaddr;
import org.apache.tomcat.jni.Socket;
import org.apache.tomcat.jni.Status;
import org.apache.tomcat.util.ExceptionUtils;
+import org.apache.tomcat.util.buf.ByteBufferUtils;
+import org.apache.tomcat.util.collections.SynchronizedStack;
import org.apache.tomcat.util.net.AbstractEndpoint.Acceptor.AcceptorState;
import org.apache.tomcat.util.net.AbstractEndpoint.Handler.SocketState;
+import org.apache.tomcat.util.net.SSLHostConfig.Type;
+import org.apache.tomcat.util.net.openssl.OpenSSLEngine;
/**
@@ -61,25 +72,14 @@ import org.apache.tomcat.util.net.AbstractEndpoint.Handler.SocketState;
* @author Mladen Turk
* @author Remy Maucherat
*/
-public class AprEndpoint extends AbstractEndpoint<Long> {
-
+public class AprEndpoint extends AbstractEndpoint<Long> implements SNICallBack {
// -------------------------------------------------------------- Constants
-
private static final Log log = LogFactory.getLog(AprEndpoint.class);
- protected static final Set<String> SSL_PROTO_ALL = new HashSet<>();
-
- static {
- /* Default used if SSLProtocol is not configured, also
- used if SSLProtocol="All" */
- SSL_PROTO_ALL.add(Constants.SSL_PROTO_TLSv1);
- SSL_PROTO_ALL.add(Constants.SSL_PROTO_TLSv1_1);
- SSL_PROTO_ALL.add(Constants.SSL_PROTO_TLSv1_2);
- }
-
// ----------------------------------------------------------------- Fields
+
/**
* Root APR memory pool.
*/
@@ -106,6 +106,7 @@ public class AprEndpoint extends AbstractEndpoint<Long> {
private final Map<Long,AprSocketWrapper> connections = new ConcurrentHashMap<>();
+
// ------------------------------------------------------------ Constructor
public AprEndpoint() {
@@ -135,14 +136,6 @@ public class AprEndpoint extends AbstractEndpoint<Long> {
/**
- * Handling of accepted sockets.
- */
- protected Handler handler = null;
- public void setHandler(Handler handler ) { this.handler = handler; }
- public Handler getHandler() { return handler; }
-
-
- /**
* Poll interval, in microseconds. The smaller the value, the more CPU the poller
* will use, but the more responsive to activity it will be.
*/
@@ -151,10 +144,6 @@ public class AprEndpoint extends AbstractEndpoint<Long> {
public void setPollTime(int pollTime) { if (pollTime > 0) { this.pollTime = pollTime; } }
- /**
- * Use sendfile for sending static files.
- */
- protected boolean useSendfile = false;
/*
* When the endpoint is created and configured, the APR library will not
* have been initialised. This flag is used to determine if the default
@@ -163,33 +152,17 @@ public class AprEndpoint extends AbstractEndpoint<Long> {
* by configuration, that configuration will always take priority.
*/
private boolean useSendFileSet = false;
+ @Override
public void setUseSendfile(boolean useSendfile) {
useSendFileSet = true;
- this.useSendfile = useSendfile;
+ super.setUseSendfile(useSendfile);
}
- @Override
- public boolean getUseSendfile() { return useSendfile; }
-
-
- /**
- * Allow comet request handling.
- */
- protected boolean useComet = true;
- public void setUseComet(boolean useComet) { this.useComet = useComet; }
- @Override
- public boolean getUseComet() { return useComet; }
- @Override
- public boolean getUseCometTimeout() { return false; } // Not supported
- @Override
- public boolean getUsePolling() { return true; } // Always supported
-
-
- /**
- * Sendfile thread count.
+ /*
+ * For internal use to avoid setting the useSendFileSet flag
*/
- protected int sendfileThreadCount = 0;
- public void setSendfileThreadCount(int sendfileThreadCount) { this.sendfileThreadCount = sendfileThreadCount; }
- public int getSendfileThreadCount() { return sendfileThreadCount; }
+ private void setUseSendfileInternal(boolean useSendfile) {
+ super.setUseSendfile(useSendfile);
+ }
/**
@@ -210,139 +183,11 @@ public class AprEndpoint extends AbstractEndpoint<Long> {
}
- /**
- * SSL protocols.
- */
- protected String SSLProtocol = "all";
- public String getSSLProtocol() { return SSLProtocol; }
- public void setSSLProtocol(String SSLProtocol) { this.SSLProtocol = SSLProtocol; }
-
-
- /**
- * SSL password (if a cert is encrypted, and no password has been provided, a callback
- * will ask for a password).
- */
- protected String SSLPassword = null;
- public String getSSLPassword() { return SSLPassword; }
- public void setSSLPassword(String SSLPassword) { this.SSLPassword = SSLPassword; }
-
-
- /**
- * SSL cipher suite.
- */
- protected String SSLCipherSuite = DEFAULT_CIPHERS;
- public String getSSLCipherSuite() { return SSLCipherSuite; }
- public void setSSLCipherSuite(String SSLCipherSuite) { this.SSLCipherSuite = SSLCipherSuite; }
-
-
- /**
- * SSL certificate file.
- */
- protected String SSLCertificateFile = null;
- public String getSSLCertificateFile() { return SSLCertificateFile; }
- public void setSSLCertificateFile(String SSLCertificateFile) { this.SSLCertificateFile = SSLCertificateFile; }
-
-
- /**
- * SSL certificate key file.
- */
- protected String SSLCertificateKeyFile = null;
- public String getSSLCertificateKeyFile() { return SSLCertificateKeyFile; }
- public void setSSLCertificateKeyFile(String SSLCertificateKeyFile) { this.SSLCertificateKeyFile = SSLCertificateKeyFile; }
-
-
- /**
- * SSL certificate chain file.
- */
- protected String SSLCertificateChainFile = null;
- public String getSSLCertificateChainFile() { return SSLCertificateChainFile; }
- public void setSSLCertificateChainFile(String SSLCertificateChainFile) { this.SSLCertificateChainFile = SSLCertificateChainFile; }
-
-
- /**
- * SSL CA certificate path.
- */
- protected String SSLCACertificatePath = null;
- public String getSSLCACertificatePath() { return SSLCACertificatePath; }
- public void setSSLCACertificatePath(String SSLCACertificatePath) { this.SSLCACertificatePath = SSLCACertificatePath; }
-
-
- /**
- * SSL CA certificate file.
- */
- protected String SSLCACertificateFile = null;
- public String getSSLCACertificateFile() { return SSLCACertificateFile; }
- public void setSSLCACertificateFile(String SSLCACertificateFile) { this.SSLCACertificateFile = SSLCACertificateFile; }
-
-
- /**
- * SSL CA revocation path.
- */
- protected String SSLCARevocationPath = null;
- public String getSSLCARevocationPath() { return SSLCARevocationPath; }
- public void setSSLCARevocationPath(String SSLCARevocationPath) { this.SSLCARevocationPath = SSLCARevocationPath; }
-
-
- /**
- * SSL CA revocation file.
- */
- protected String SSLCARevocationFile = null;
- public String getSSLCARevocationFile() { return SSLCARevocationFile; }
- public void setSSLCARevocationFile(String SSLCARevocationFile) { this.SSLCARevocationFile = SSLCARevocationFile; }
-
- /**
- * SSL disable TLS Session Tickets (RFC 4507).
- */
- protected boolean SSLDisableSessionTickets = false;
- public boolean getSSLDisableSessionTickets() { return SSLDisableSessionTickets; }
- public void setSSLDisableSessionTickets(boolean SSLDisableSessionTickets) { this.SSLDisableSessionTickets = SSLDisableSessionTickets; }
-
- /**
- * SSL verify client.
- */
- protected String SSLVerifyClient = "none";
- public String getSSLVerifyClient() { return SSLVerifyClient; }
- public void setSSLVerifyClient(String SSLVerifyClient) { this.SSLVerifyClient = SSLVerifyClient; }
-
-
- /**
- * SSL verify depth.
- */
- protected int SSLVerifyDepth = 10;
- public int getSSLVerifyDepth() { return SSLVerifyDepth; }
- public void setSSLVerifyDepth(int SSLVerifyDepth) { this.SSLVerifyDepth = SSLVerifyDepth; }
-
-
- /**
- * SSL allow insecure renegotiation for the the client that does not
- * support the secure renegotiation.
- */
- protected boolean SSLInsecureRenegotiation = false;
- public void setSSLInsecureRenegotiation(boolean SSLInsecureRenegotiation) { this.SSLInsecureRenegotiation = SSLInsecureRenegotiation; }
- public boolean getSSLInsecureRenegotiation() { return SSLInsecureRenegotiation; }
-
- protected boolean SSLHonorCipherOrder = false;
- /**
- * Set to <code>true</code> to enforce the <i>server's</i> cipher order
- * instead of the default which is to allow the client to choose a
- * preferred cipher.
- */
- public void setSSLHonorCipherOrder(boolean SSLHonorCipherOrder) { this.SSLHonorCipherOrder = SSLHonorCipherOrder; }
- public boolean getSSLHonorCipherOrder() { return SSLHonorCipherOrder; }
-
- /**
- * Disables compression of the SSL stream. This thwarts CRIME attack
- * and possibly improves performance by not compressing uncompressible
- * content such as JPEG, etc.
- */
- protected boolean SSLDisableCompression = false;
+ @Override
+ protected Type getSslConfigType() {
+ return SSLHostConfig.Type.OPENSSL;
+ }
- /**
- * Set to <code>true</code> to disable SSL compression. This thwarts CRIME
- * attack.
- */
- public void setSSLDisableCompression(boolean SSLDisableCompression) { this.SSLDisableCompression = SSLDisableCompression; }
- public boolean getSSLDisableCompression() { return SSLDisableCompression; }
/**
* Port in use.
@@ -365,14 +210,6 @@ public class AprEndpoint extends AbstractEndpoint<Long> {
}
- @Override
- public String[] getCiphersUsed() {
- // TODO : Investigate if it is possible to extract the current list of
- // available ciphers. Native code changes will be required.
- return new String[] { getSSLCipherSuite() };
- }
-
-
/**
* This endpoint does not support <code>-1</code> for unlimited connections,
* nor does it support setting this attribute while the endpoint is running.
@@ -398,7 +235,9 @@ public class AprEndpoint extends AbstractEndpoint<Long> {
// --------------------------------------------------------- Public Methods
/**
- * Number of keepalive sockets.
+ * Obtain the number of kept alive sockets.
+ *
+ * @return The number of open sockets currently managed by the Poller
*/
public int getKeepAliveCount() {
if (poller == null) {
@@ -410,7 +249,9 @@ public class AprEndpoint extends AbstractEndpoint<Long> {
/**
- * Number of sendfile sockets.
+ * Obtain the number of sendfile sockets.
+ *
+ * @return The number of sockets currently managed by the Sendfile poller.
*/
public int getSendfileCount() {
if (sendfile == null) {
@@ -483,9 +324,9 @@ public class AprEndpoint extends AbstractEndpoint<Long> {
// Enable Sendfile by default if it has not been configured but usage on
// systems which don't support it cause major problems
if (!useSendFileSet) {
- useSendfile = Library.APR_HAS_SENDFILE;
- } else if (useSendfile && !Library.APR_HAS_SENDFILE) {
- useSendfile = false;
+ setUseSendfileInternal(Library.APR_HAS_SENDFILE);
+ } else if (getUseSendfile() && !Library.APR_HAS_SENDFILE) {
+ setUseSendfileInternal(false);
}
// Initialize thread count default for acceptor
@@ -505,186 +346,203 @@ public class AprEndpoint extends AbstractEndpoint<Long> {
// Initialize SSL if needed
if (isSSLEnabled()) {
-
- if (SSLCertificateFile == null) {
- // This is required
- throw new Exception(sm.getString("endpoint.apr.noSslCertFile"));
+ for (SSLHostConfig sslHostConfig : sslHostConfigs.values()) {
+ createSSLContext(sslHostConfig);
}
+ SSLHostConfig defaultSSLHostConfig = sslHostConfigs.get(getDefaultSSLHostConfigName());
+ Long defaultSSLContext = defaultSSLHostConfig.getOpenSslContext();
+ sslContext = defaultSSLContext.longValue();
+ SSLContext.registerDefault(defaultSSLContext, this);
+ }
+ }
- // SSL protocol
- int value = SSL.SSL_PROTOCOL_NONE;
- if (SSLProtocol == null || SSLProtocol.length() == 0) {
- value = SSL.SSL_PROTOCOL_ALL;
- } else {
-
- Set<String> protocols = new HashSet<>();
-
- // List of protocol names, separated by "+" or "-".
- // Semantics is adding ("+") or removing ("-") from left
- // to right, starting with an empty protocol set.
- // Tokens are individual protocol names or "all" for a
- // default set of supported protocols.
-
- // Split using a positive lookahead to keep the separator in
- // the capture so we can check which case it is.
- for (String protocol : SSLProtocol.split("(?=[-+])")) {
- String trimmed = protocol.trim();
- // Ignore token which only consists of prefix character
- if (trimmed.length() > 1) {
- if (trimmed.charAt(0) == '-') {
- trimmed = trimmed.substring(1).trim();
- if (trimmed.equalsIgnoreCase(Constants.SSL_PROTO_ALL)) {
- protocols.removeAll(SSL_PROTO_ALL);
- } else {
- protocols.remove(trimmed);
- }
- } else {
- if (trimmed.charAt(0) == '+') {
- trimmed = trimmed.substring(1).trim();
- }
- if (trimmed.equalsIgnoreCase(Constants.SSL_PROTO_ALL)) {
- protocols.addAll(SSL_PROTO_ALL);
- } else {
- protocols.add(trimmed);
- }
- }
- }
- }
-
- for (String protocol : protocols) {
- if (Constants.SSL_PROTO_SSLv2.equalsIgnoreCase(protocol)) {
- value |= SSL.SSL_PROTOCOL_SSLV2;
- } else if (Constants.SSL_PROTO_SSLv3.equalsIgnoreCase(protocol)) {
- value |= SSL.SSL_PROTOCOL_SSLV3;
- } else if (Constants.SSL_PROTO_TLSv1.equalsIgnoreCase(protocol)) {
- value |= SSL.SSL_PROTOCOL_TLSV1;
- } else if (Constants.SSL_PROTO_TLSv1_1.equalsIgnoreCase(protocol)) {
- value |= SSL.SSL_PROTOCOL_TLSV1_1;
- } else if (Constants.SSL_PROTO_TLSv1_2.equalsIgnoreCase(protocol)) {
- value |= SSL.SSL_PROTOCOL_TLSV1_2;
- } else {
- // Protocol not recognized, fail to start as it is safer than
- // continuing with the default which might enable more than the
- // is required
- throw new Exception(sm.getString(
- "endpoint.apr.invalidSslProtocol", SSLProtocol));
- }
- }
- }
- // Create SSL Context
- try {
- sslContext = SSLContext.make(rootPool, value, SSL.SSL_MODE_SERVER);
- } catch (Exception e) {
- // If the sslEngine is disabled on the AprLifecycleListener
- // there will be an Exception here but there is no way to check
- // the AprLifecycleListener settings from here
- throw new Exception(
- sm.getString("endpoint.apr.failSslContextMake"), e);
- }
- if (SSLInsecureRenegotiation) {
- boolean legacyRenegSupported = false;
- try {
- legacyRenegSupported = SSL.hasOp(SSL.SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION);
- if (legacyRenegSupported)
- SSLContext.setOptions(sslContext, SSL.SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION);
- } catch (UnsatisfiedLinkError e) {
- // Ignore
- }
- if (!legacyRenegSupported) {
- // OpenSSL does not support unsafe legacy renegotiation.
- log.warn(sm.getString("endpoint.warn.noInsecureReneg",
- SSL.versionString()));
- }
+ @Override
+ protected void createSSLContext(SSLHostConfig sslHostConfig) throws Exception {
+ Set<SSLHostConfigCertificate> certificates = sslHostConfig.getCertificates(true);
+ boolean firstCertificate = true;
+ for (SSLHostConfigCertificate certificate : certificates) {
+ if (SSLHostConfig.adjustRelativePath(certificate.getCertificateFile()) == null) {
+ // This is required
+ throw new Exception(sm.getString("endpoint.apr.noSslCertFile"));
}
-
- // Set cipher order: client (default) or server
- if (SSLHonorCipherOrder) {
- boolean orderCiphersSupported = false;
- try {
- orderCiphersSupported = SSL.hasOp(SSL.SSL_OP_CIPHER_SERVER_PREFERENCE);
- if (orderCiphersSupported)
- SSLContext.setOptions(sslContext, SSL.SSL_OP_CIPHER_SERVER_PREFERENCE);
- } catch (UnsatisfiedLinkError e) {
- // Ignore
- }
- if (!orderCiphersSupported) {
- // OpenSSL does not support ciphers ordering.
- log.warn(sm.getString("endpoint.warn.noHonorCipherOrder",
- SSL.versionString()));
+ if (firstCertificate) {
+ // TODO: Duplicates code in SSLUtilBase. Consider
+ // refactoring to reduce duplication
+ firstCertificate = false;
+ // Configure the enabled protocols
+ List<String> enabledProtocols = SSLUtilBase.getEnabled("protocols", log,
+ true, sslHostConfig.getProtocols(),
+ OpenSSLEngine.IMPLEMENTED_PROTOCOLS_SET);
+ sslHostConfig.setEnabledProtocols(
+ enabledProtocols.toArray(new String[enabledProtocols.size()]));
+ // Configure the enabled ciphers
+ List<String> enabledCiphers = SSLUtilBase.getEnabled("ciphers", log,
+ false, sslHostConfig.getJsseCipherNames(),
+ OpenSSLEngine.AVAILABLE_CIPHER_SUITES);
+ sslHostConfig.setEnabledCiphers(
+ enabledCiphers.toArray(new String[enabledCiphers.size()]));
+ }
+ }
+ if (certificates.size() > 2) {
+ // TODO: Can this limitation be removed?
+ throw new Exception(sm.getString("endpoint.apr.tooManyCertFiles"));
+ }
+
+ // SSL protocol
+ int value = SSL.SSL_PROTOCOL_NONE;
+ if (sslHostConfig.getProtocols().size() == 0) {
+ // Native fallback used if protocols=""
+ value = SSL.SSL_PROTOCOL_ALL;
+ } else {
+ for (String protocol : sslHostConfig.getEnabledProtocols()) {
+ if (Constants.SSL_PROTO_SSLv2Hello.equalsIgnoreCase(protocol)) {
+ // NO-OP. OpenSSL always supports SSLv2Hello
+ } else if (Constants.SSL_PROTO_SSLv2.equalsIgnoreCase(protocol)) {
+ value |= SSL.SSL_PROTOCOL_SSLV2;
+ } else if (Constants.SSL_PROTO_SSLv3.equalsIgnoreCase(protocol)) {
+ value |= SSL.SSL_PROTOCOL_SSLV3;
+ } else if (Constants.SSL_PROTO_TLSv1.equalsIgnoreCase(protocol)) {
+ value |= SSL.SSL_PROTOCOL_TLSV1;
+ } else if (Constants.SSL_PROTO_TLSv1_1.equalsIgnoreCase(protocol)) {
+ value |= SSL.SSL_PROTOCOL_TLSV1_1;
+ } else if (Constants.SSL_PROTO_TLSv1_2.equalsIgnoreCase(protocol)) {
+ value |= SSL.SSL_PROTOCOL_TLSV1_2;
+ } else {
+ // Should not happen since filtering to build
+ // enabled protocols removes invalid values.
+ throw new Exception(sm.getString(
+ "endpoint.apr.invalidSslProtocol", protocol));
}
}
+ }
- // Disable compression if requested
- if (SSLDisableCompression) {
- boolean disableCompressionSupported = false;
- try {
- disableCompressionSupported = SSL.hasOp(SSL.SSL_OP_NO_COMPRESSION);
- if (disableCompressionSupported)
- SSLContext.setOptions(sslContext, SSL.SSL_OP_NO_COMPRESSION);
- } catch (UnsatisfiedLinkError e) {
- // Ignore
- }
- if (!disableCompressionSupported) {
- // OpenSSL does not support ciphers ordering.
- log.warn(sm.getString("endpoint.warn.noDisableCompression",
- SSL.versionString()));
- }
- }
+ // Create SSL Context
+ long ctx = 0;
+ try {
+ ctx = SSLContext.make(rootPool, value, SSL.SSL_MODE_SERVER);
+ } catch (Exception e) {
+ // If the sslEngine is disabled on the AprLifecycleListener
+ // there will be an Exception here but there is no way to check
+ // the AprLifecycleListener settings from here
+ throw new Exception(
+ sm.getString("endpoint.apr.failSslContextMake"), e);
+ }
- // Disable TLS Session Tickets (RFC4507) to protect perfect forward secrecy
- if (SSLDisableSessionTickets) {
- boolean disableSessionTicketsSupported = false;
- try {
- disableSessionTicketsSupported = SSL.hasOp(SSL.SSL_OP_NO_TICKET);
- if (disableSessionTicketsSupported)
- SSLContext.setOptions(sslContext, SSL.SSL_OP_NO_TICKET);
- } catch (UnsatisfiedLinkError e) {
- // Ignore
- }
+ if (sslHostConfig.getInsecureRenegotiation()) {
+ SSLContext.setOptions(ctx, SSL.SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION);
+ } else {
+ SSLContext.clearOptions(ctx, SSL.SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION);
+ }
- if (!disableSessionTicketsSupported) {
- // OpenSSL is too old to support TLS Session Tickets.
- log.warn(sm.getString("endpoint.warn.noDisableSessionTickets",
- SSL.versionString()));
- }
+ // Use server's preference order for ciphers (rather than client's)
+ String honorCipherOrderStr = sslHostConfig.getHonorCipherOrder();
+ if (honorCipherOrderStr != null) {
+ boolean honorCipherOrder = Boolean.valueOf(honorCipherOrderStr).booleanValue();
+ if (honorCipherOrder) {
+ SSLContext.setOptions(ctx, SSL.SSL_OP_CIPHER_SERVER_PREFERENCE);
+ } else {
+ SSLContext.clearOptions(ctx, SSL.SSL_OP_CIPHER_SERVER_PREFERENCE);
}
+ }
- // List the ciphers that the client is permitted to negotiate
- SSLContext.setCipherSuite(sslContext, SSLCipherSuite);
- // Load Server key and certificate
- SSLContext.setCertificate(sslContext, SSLCertificateFile, SSLCertificateKeyFile, SSLPassword, SSL.SSL_AIDX_RSA);
+ // Disable compression if requested
+ if (sslHostConfig.getDisableCompression()) {
+ SSLContext.setOptions(ctx, SSL.SSL_OP_NO_COMPRESSION);
+ } else {
+ SSLContext.clearOptions(ctx, SSL.SSL_OP_NO_COMPRESSION);
+ }
+
+ // Disable TLS Session Tickets (RFC4507) to protect perfect forward secrecy
+ if (sslHostConfig.getDisableSessionTickets()) {
+ SSLContext.setOptions(ctx, SSL.SSL_OP_NO_TICKET);
+ } else {
+ SSLContext.clearOptions(ctx, SSL.SSL_OP_NO_TICKET);
+ }
+
+ // List the ciphers that the client is permitted to negotiate
+ SSLContext.setCipherSuite(ctx, sslHostConfig.getCiphers());
+ // Load Server key and certificate
+ // TODO: Confirm assumption that idx is not specific to
+ // key/certificate type
+ int idx = 0;
+ for (SSLHostConfigCertificate certificate : sslHostConfig.getCertificates(true)) {
+ SSLContext.setCertificate(ctx,
+ SSLHostConfig.adjustRelativePath(certificate.getCertificateFile()),
+ SSLHostConfig.adjustRelativePath(certificate.getCertificateKeyFile()),
+ certificate.getCertificateKeyPassword(), idx++);
// Set certificate chain file
- SSLContext.setCertificateChainFile(sslContext, SSLCertificateChainFile, false);
- // Support Client Certificates
- SSLContext.setCACertificate(sslContext, SSLCACertificateFile, SSLCACertificatePath);
- // Set revocation
- SSLContext.setCARevocation(sslContext, SSLCARevocationFile, SSLCARevocationPath);
- // Client certificate verification
+ SSLContext.setCertificateChainFile(ctx,
+ SSLHostConfig.adjustRelativePath(certificate.getCertificateChainFile()), false);
+ }
+ // Support Client Certificates
+ SSLContext.setCACertificate(ctx,
+ SSLHostConfig.adjustRelativePath(sslHostConfig.getCaCertificateFile()),
+ SSLHostConfig.adjustRelativePath(sslHostConfig.getCaCertificatePath()));
+ // Set revocation
+ SSLContext.setCARevocation(ctx,
+ SSLHostConfig.adjustRelativePath(
+ sslHostConfig.getCertificateRevocationListFile()),
+ SSLHostConfig.adjustRelativePath(
+ sslHostConfig.getCertificateRevocationListPath()));
+ // Client certificate verification
+ switch (sslHostConfig.getCertificateVerification()) {
+ case NONE:
value = SSL.SSL_CVERIFY_NONE;
- if ("optional".equalsIgnoreCase(SSLVerifyClient)) {
- value = SSL.SSL_CVERIFY_OPTIONAL;
- } else if ("require".equalsIgnoreCase(SSLVerifyClient)) {
- value = SSL.SSL_CVERIFY_REQUIRE;
- } else if ("optionalNoCA".equalsIgnoreCase(SSLVerifyClient)) {
- value = SSL.SSL_CVERIFY_OPTIONAL_NO_CA;
- }
- SSLContext.setVerify(sslContext, value, SSLVerifyDepth);
- // For now, sendfile is not supported with SSL
- if (useSendfile) {
- useSendfile = false;
- if (useSendFileSet) {
- log.warn(sm.getString("endpoint.apr.noSendfileWithSSL"));
- }
- }
+ 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(ctx, value, sslHostConfig.getCertificateVerificationDepth());
+ // For now, sendfile is not supported with SSL
+ if (getUseSendfile()) {
+ setUseSendfileInternal(false);
+ if (useSendFileSet) {
+ log.warn(sm.getString("endpoint.apr.noSendfileWithSSL"));
+ }
+ }
+
+ if (negotiableProtocols.size() > 0) {
+ ArrayList<String> protocols = new ArrayList<>();
+ protocols.addAll(negotiableProtocols);
+ protocols.add("http/1.1");
+ String[] protocolsArray = protocols.toArray(new String[0]);
+ SSLContext.setAlpnProtos(ctx, protocolsArray, SSL.SSL_SELECTOR_FAILURE_NO_ADVERTISE);
+ }
+ sslHostConfig.setOpenSslContext(Long.valueOf(ctx));
+ }
+
+
+ @Override
+ protected void releaseSSLContext(SSLHostConfig sslHostConfig) {
+ Long ctx = sslHostConfig.getOpenSslContext();
+ if (ctx != null) {
+ SSLContext.free(ctx.longValue());
+ sslHostConfig.setOpenSslContext(null);
}
}
- public long getJniSslContext() {
- return sslContext;
+
+ @Override
+ public long getSslContext(String sniHostName) {
+ SSLHostConfig sslHostConfig = getSSLHostConfig(sniHostName);
+ Long ctx = sslHostConfig.getOpenSslContext();
+ if (ctx != null) {
+ return ctx.longValue();
+ }
+ // Default
+ return 0;
}
+
/**
* Start the APR endpoint, creating acceptor, poller and sendfile threads.
*/
@@ -695,6 +553,9 @@ public class AprEndpoint extends AbstractEndpoint<Long> {
running = true;
paused = false;
+ processorCache = new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE,
+ socketProperties.getProcessorCache());
+
// Create worker collection
if (getExecutor() == null) {
createExecutor();
@@ -711,7 +572,7 @@ public class AprEndpoint extends AbstractEndpoint<Long> {
pollerThread.start();
// Start sendfile thread
- if (useSendfile) {
+ if (getUseSendfile()) {
sendfile = new Sendfile();
sendfile.init();
Thread sendfileThread =
@@ -722,13 +583,6 @@ public class AprEndpoint extends AbstractEndpoint<Long> {
}
startAcceptorThreads();
-
- // Start async timeout thread
- setAsyncTimeout(new AsyncTimeout());
- Thread timeoutThread = new Thread(getAsyncTimeout(), getName() + "-AsyncTimeout");
- timeoutThread.setPriority(threadPriority);
- timeoutThread.setDaemon(true);
- timeoutThread.start();
}
}
@@ -745,7 +599,14 @@ public class AprEndpoint extends AbstractEndpoint<Long> {
if (running) {
running = false;
poller.stop();
- getAsyncTimeout().stop();
+ for (SocketWrapperBase<Long> socketWrapper : connections.values()) {
+ try {
+ socketWrapper.close();
+ getHandler().release(socketWrapper);
+ } catch (IOException e) {
+ // Ignore
+ }
+ }
for (AbstractEndpoint.Acceptor acceptor : acceptors) {
long waitLeft = 10000;
while (waitLeft > 0 &&
@@ -776,7 +637,7 @@ public class AprEndpoint extends AbstractEndpoint<Long> {
}
poller = null;
connections.clear();
- if (useSendfile) {
+ if (getUseSendfile()) {
try {
sendfile.destroy();
} catch (Exception e) {
@@ -784,6 +645,7 @@ public class AprEndpoint extends AbstractEndpoint<Long> {
}
sendfile = null;
}
+ processorCache.clear();
}
shutdownExecutor();
}
@@ -810,7 +672,14 @@ public class AprEndpoint extends AbstractEndpoint<Long> {
serverSock = 0;
}
- sslContext = 0;
+ if (sslContext != 0) {
+ Long ctx = Long.valueOf(sslContext);
+ SSLContext.unregisterDefault(ctx);
+ for (SSLHostConfig sslHostConfig : sslHostConfigs.values()) {
+ sslHostConfig.setOpenSslContext(null);
+ }
+ sslContext = 0;
+ }
// Close all APR memory pools and resources if initialised
if (rootPool != 0) {
@@ -818,7 +687,7 @@ public class AprEndpoint extends AbstractEndpoint<Long> {
rootPool = 0;
}
- handler.recycle();
+ getHandler().recycle();
}
@@ -832,8 +701,13 @@ public class AprEndpoint extends AbstractEndpoint<Long> {
/**
* Process the specified connection.
+ * @param socketWrapper The socket wrapper
+ * @return <code>true</code> if the socket was correctly configured
+ * and processing may continue, <code>false</code> if the socket needs to be
+ * close immediately
*/
- protected boolean setSocketOptions(long socket) {
+ protected boolean setSocketOptions(SocketWrapperBase<Long> socketWrapper) {
+ long socket = socketWrapper.getSocket().longValue();
// Process the connection
int step = 1;
try {
@@ -855,8 +729,20 @@ public class AprEndpoint extends AbstractEndpoint<Long> {
}
return false;
}
- }
+ if (negotiableProtocols.size() > 0) {
+ byte[] negotiated = new byte[256];
+ int len = SSLSocket.getALPN(socket, negotiated);
+ String negotiatedProtocol =
+ new String(negotiated, 0, len, StandardCharsets.UTF_8);
+ if (negotiatedProtocol.length() > 0) {
+ socketWrapper.setNegotiatedProtocol(negotiatedProtocol);
+ if (log.isDebugEnabled()) {
+ log.debug(sm.getString("endpoint.alpn.negotiated", negotiatedProtocol));
+ }
+ }
+ }
+ }
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
if (log.isDebugEnabled()) {
@@ -875,6 +761,10 @@ public class AprEndpoint extends AbstractEndpoint<Long> {
/**
* Allocate a new poller of the specified size.
+ * @param size The size
+ * @param pool The pool from which the poller will be allocated
+ * @param timeout The timeout
+ * @return the poller pointer
*/
protected long allocatePoller(int size, long pool, int timeout) {
try {
@@ -893,6 +783,10 @@ public class AprEndpoint extends AbstractEndpoint<Long> {
/**
* Process given socket. This is called when the socket has been
* accepted.
+ * @param socket The socket
+ * @return <code>true</code> if the socket was correctly configured
+ * and processing may continue, <code>false</code> if the socket needs to be
+ * close immediately
*/
protected boolean processSocketWithOptions(long socket) {
try {
@@ -905,6 +799,8 @@ public class AprEndpoint extends AbstractEndpoint<Long> {
AprSocketWrapper wrapper = new AprSocketWrapper(Long.valueOf(socket), this);
wrapper.setKeepAliveLeft(getMaxKeepAliveRequests());
wrapper.setSecure(isSSLEnabled());
+ wrapper.setReadTimeout(getSoTimeout());
+ wrapper.setWriteTimeout(getSoTimeout());
connections.put(Long.valueOf(socket), wrapper);
getExecutor().execute(new SocketWithOptionsProcessor(wrapper));
}
@@ -923,70 +819,34 @@ public class AprEndpoint extends AbstractEndpoint<Long> {
/**
- * Process given socket. Called in non-comet mode, typically keep alive
- * or upgraded protocol.
+ * Process the given socket. Typically keep alive or upgraded protocol.
+ *
+ * @param socket The socket to process
+ * @param event The event to process
+ *
+ * @return <code>true</code> if the processing completed normally otherwise
+ * <code>false</code> which indicates an error occurred and that the
+ * socket should be closed
*/
- public boolean processSocket(long socket, SocketStatus status) {
- try {
- Executor executor = getExecutor();
- if (executor == null) {
- log.warn(sm.getString("endpoint.warn.noExector",
- Long.valueOf(socket), null));
- } else {
- SocketWrapper<Long> wrapper =
- connections.get(Long.valueOf(socket));
- // Make sure connection hasn't been closed
- if (wrapper != null) {
- executor.execute(new SocketProcessor(wrapper, status));
- }
- }
- } catch (RejectedExecutionException x) {
- log.warn("Socket processing request was rejected for:"+socket,x);
- return false;
- } catch (Throwable t) {
- ExceptionUtils.handleThrowable(t);
- // This means we got an OOM or similar creating a thread, or that
- // the pool and its queue are full
- log.error(sm.getString("endpoint.process.fail"), t);
- return false;
- }
- return true;
+ protected boolean processSocket(long socket, SocketEvent event) {
+ SocketWrapperBase<Long> socketWrapper = connections.get(Long.valueOf(socket));
+ return processSocket(socketWrapper, event, true);
}
@Override
- public void processSocket(SocketWrapper<Long> socket, SocketStatus status,
- boolean dispatch) {
- try {
- // Synchronisation is required here as this code may be called as a
- // result of calling AsyncContext.dispatch() from a non-container
- // thread
- synchronized (socket) {
- if (waitingRequests.remove(socket)) {
- SocketProcessor proc = new SocketProcessor(socket, status);
- Executor executor = getExecutor();
- if (dispatch && executor != null) {
- executor.execute(proc);
- } else {
- proc.run();
- }
- }
- }
- } catch (RejectedExecutionException ree) {
- log.warn(sm.getString("endpoint.executor.fail", socket) , ree);
- } catch (Throwable t) {
- ExceptionUtils.handleThrowable(t);
- // This means we got an OOM or similar creating a thread, or that
- // the pool and its queue are full
- log.error(sm.getString("endpoint.process.fail"), t);
- }
+ protected SocketProcessorBase<Long> createSocketProcessor(
+ SocketWrapperBase<Long> socketWrapper, SocketEvent event) {
+ return new SocketProcessor(socketWrapper, event);
}
+
private void closeSocket(long socket) {
// Once this is called, the mapping from socket to wrapper will no
// longer be required.
- SocketWrapper<Long> wrapper = connections.remove(Long.valueOf(socket));
+ SocketWrapperBase<Long> wrapper = connections.remove(Long.valueOf(socket));
if (wrapper != null) {
+ // Cast to avoid having to catch an IOE that is never thrown.
((AprSocketWrapper) wrapper).close();
}
}
@@ -1022,14 +882,6 @@ public class AprEndpoint extends AbstractEndpoint<Long> {
return log;
}
-
-
- @Override
- protected void testServerCipherSuitesOrderSupport() {
- // NO_OP. This always passes for APR since it can only fail with JSSE.
- }
-
-
// --------------------------------------------------- Acceptor Inner Class
/**
* The background thread that listens for incoming TCP/IP connections and
@@ -1131,7 +983,7 @@ public class AprEndpoint extends AbstractEndpoint<Long> {
public static class SocketInfo {
public long socket;
- public int timeout;
+ public long timeout;
public int flags;
public boolean read() {
return (flags & Poll.APR_POLLIN) == Poll.APR_POLLIN;
@@ -1181,6 +1033,8 @@ public class AprEndpoint extends AbstractEndpoint<Long> {
/**
* Removes the specified socket from the poller.
*
+ * @param socket The socket to remove
+ *
* @return The configured timeout for the socket or zero if the socket
* was not in the list of socket timeouts
*/
@@ -1223,7 +1077,7 @@ public class AprEndpoint extends AbstractEndpoint<Long> {
protected int pos;
protected long[] sockets;
- protected int[] timeouts;
+ protected long[] timeouts;
protected int[] flags;
protected SocketInfo info = new SocketInfo();
@@ -1232,7 +1086,7 @@ public class AprEndpoint extends AbstractEndpoint<Long> {
this.size = 0;
pos = 0;
sockets = new long[size];
- timeouts = new int[size];
+ timeouts = new long[size];
flags = new int[size];
}
@@ -1257,7 +1111,7 @@ public class AprEndpoint extends AbstractEndpoint<Long> {
pos = 0;
}
- public boolean add(long socket, int timeout, int flag) {
+ public boolean add(long socket, long timeout, int flag) {
if (size == sockets.length) {
return false;
} else {
@@ -1490,14 +1344,11 @@ public class AprEndpoint extends AbstractEndpoint<Long> {
// Close all sockets in the add queue
info = addList.get();
while (info != null) {
- boolean comet = connections.get(Long.valueOf(info.socket)).isComet();
- if (!comet || !processSocket(info.socket, SocketStatus.STOP)) {
- // Make sure the socket isn't in the poller before we close it
- removeFromPoller(info.socket);
- // Poller isn't running at this point so use destroySocket()
- // directly
- destroySocket(info.socket);
- }
+ // Make sure the socket isn't in the poller before we close it
+ removeFromPoller(info.socket);
+ // Poller isn't running at this point so use destroySocket()
+ // directly
+ destroySocket(info.socket);
info = addList.get();
}
addList.clear();
@@ -1506,11 +1357,7 @@ public class AprEndpoint extends AbstractEndpoint<Long> {
int rv = Poll.pollset(pollers[i], desc);
if (rv > 0) {
for (int n = 0; n < rv; n++) {
- boolean comet = connections.get(
- Long.valueOf(desc[n*2+1])).isComet();
- if (!comet || !processSocket(desc[n*2+1], SocketStatus.STOP)) {
- destroySocket(desc[n*2+1]);
- }
+ destroySocket(desc[n*2+1]);
}
}
}
@@ -1529,14 +1376,14 @@ public class AprEndpoint extends AbstractEndpoint<Long> {
* be removed from the poller.
*
* @param socket to add to the poller
- * @param timeout to use for this connection
+ * @param timeout to use for this connection in milliseconds
* @param flags Events to poll for (Poll.APR_POLLIN and/or
* Poll.APR_POLLOUT)
*/
- private void add(long socket, int timeout, int flags) {
+ private void add(long socket, long timeout, int flags) {
if (log.isDebugEnabled()) {
String msg = sm.getString("endpoint.debug.pollerAdd",
- Long.valueOf(socket), Integer.valueOf(timeout),
+ Long.valueOf(socket), Long.valueOf(timeout),
Integer.valueOf(flags));
if (log.isTraceEnabled()) {
log.trace(msg, new Exception());
@@ -1634,14 +1481,9 @@ public class AprEndpoint extends AbstractEndpoint<Long> {
log.debug(sm.getString("endpoint.debug.socketTimeout",
Long.valueOf(socket)));
}
- removeFromPoller(socket);
- boolean comet = connections.get(
- Long.valueOf(socket)).isComet();
- if (!comet || !processSocket(socket, SocketStatus.TIMEOUT)) {
- destroySocket(socket);
- addList.remove(socket);
- closeList.remove(socket);
- }
+ SocketWrapperBase<Long> socketWrapper = connections.get(Long.valueOf(socket));
+ socketWrapper.setError(new SocketTimeoutException());
+ processSocket(socketWrapper, SocketEvent.ERROR, true);
socket = timeouts.check(date);
}
@@ -1667,8 +1509,9 @@ public class AprEndpoint extends AbstractEndpoint<Long> {
}
/**
- * The background thread that listens for incoming TCP/IP connections
- * and hands them off to an appropriate processor.
+ * 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() {
@@ -1679,14 +1522,6 @@ public class AprEndpoint extends AbstractEndpoint<Long> {
// Loop until we receive a shutdown command
while (pollerRunning) {
- // Loop if endpoint is paused
- while (pollerRunning && paused) {
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- // Ignore
- }
- }
// Check timeouts if the poller is empty.
while (pollerRunning && connectionCount.get() < 1 &&
addList.size() < 1 && closeList.size() < 1) {
@@ -1769,10 +1604,6 @@ public class AprEndpoint extends AbstractEndpoint<Long> {
continue;
}
if (info.read() || info.write()) {
- boolean comet = wrapper.isComet();
- if (comet || wrapper.pollerFlags != 0) {
- removeFromPoller(info.socket);
- }
wrapper.pollerFlags = wrapper.pollerFlags |
(info.read() ? Poll.APR_POLLIN : 0) |
(info.write() ? Poll.APR_POLLOUT : 0);
@@ -1783,11 +1614,7 @@ public class AprEndpoint extends AbstractEndpoint<Long> {
// the poller.
removeFromPoller(info.socket);
if (!addToPoller(info.socket, wrapper.pollerFlags)) {
- // Can't do anything: close the socket right
- // away
- if (!comet || !processSocket(info.socket, SocketStatus.ERROR)) {
- closeSocket(info.socket);
- }
+ closeSocket(info.socket);
} else {
timeouts.add(info.socket,
System.currentTimeMillis() +
@@ -1841,83 +1668,41 @@ public class AprEndpoint extends AbstractEndpoint<Long> {
}
wrapper.pollerFlags = wrapper.pollerFlags & ~((int) desc[n*2]);
// Check for failed sockets and hand this socket off to a worker
- if (wrapper.isComet()) {
- // Event processes either a read or a write depending on what the poller returns
- if (((desc[n*2] & Poll.APR_POLLHUP) == Poll.APR_POLLHUP)
- || ((desc[n*2] & Poll.APR_POLLERR) == Poll.APR_POLLERR)
- || ((desc[n*2] & Poll.APR_POLLNVAL) == Poll.APR_POLLNVAL)) {
- if (!processSocket(desc[n*2+1], SocketStatus.ERROR)) {
- // Close socket and clear pool
- closeSocket(desc[n*2+1]);
- }
- } else if ((desc[n*2] & Poll.APR_POLLIN) == Poll.APR_POLLIN) {
- if (wrapper.pollerFlags != 0) {
- add(desc[n*2+1], 1, wrapper.pollerFlags);
- }
- if (!processSocket(desc[n*2+1], SocketStatus.OPEN_READ)) {
+ if (((desc[n*2] & Poll.APR_POLLHUP) == Poll.APR_POLLHUP)
+ || ((desc[n*2] & Poll.APR_POLLERR) == Poll.APR_POLLERR)
+ || ((desc[n*2] & Poll.APR_POLLNVAL) == Poll.APR_POLLNVAL)) {
+ // Need to trigger error handling. Poller may return error
+ // codes plus the flags it was waiting for or it may just
+ // return an error code. We could handle the error here but
+ // if we do, there will be no exception associated with the
+ // error in application code. By signalling read/write is
+ // possible, a read/write will be attempted, fail and that
+ // will trigger an exception the application will see.
+ // Check the return flags first, followed by what the socket
+ // was registered for
+ if ((desc[n*2] & Poll.APR_POLLIN) == Poll.APR_POLLIN) {
+ // Error probably occurred during a non-blocking read
+ if (!processSocket(desc[n*2+1], SocketEvent.OPEN_READ)) {
// Close socket and clear pool
closeSocket(desc[n*2+1]);
}
} else if ((desc[n*2] & Poll.APR_POLLOUT) == Poll.APR_POLLOUT) {
- if (wrapper.pollerFlags != 0) {
- add(desc[n*2+1], 1, wrapper.pollerFlags);
- }
- if (!processSocket(desc[n*2+1], SocketStatus.OPEN_WRITE)) {
+ // Error probably occurred during a non-blocking write
+ if (!processSocket(desc[n*2+1], SocketEvent.OPEN_WRITE)) {
// Close socket and clear pool
closeSocket(desc[n*2+1]);
}
- } else {
- // Unknown event
- getLog().warn(sm.getString(
- "endpoint.apr.pollUnknownEvent",
- Long.valueOf(desc[n*2])));
- if (!processSocket(desc[n*2+1], SocketStatus.ERROR)) {
+ } else if ((wrapper.pollerFlags & Poll.APR_POLLIN) == Poll.APR_POLLIN) {
+ // Can't tell what was happening when the error occurred but the
+ // socket is registered for non-blocking read so use that
+ if (!processSocket(desc[n*2+1], SocketEvent.OPEN_READ)) {
// Close socket and clear pool
closeSocket(desc[n*2+1]);
}
- }
- } else if (((desc[n*2] & Poll.APR_POLLHUP) == Poll.APR_POLLHUP)
- || ((desc[n*2] & Poll.APR_POLLERR) == Poll.APR_POLLERR)
- || ((desc[n*2] & Poll.APR_POLLNVAL) == Poll.APR_POLLNVAL)) {
- if (wrapper.isAsync() || wrapper.isUpgraded()) {
- // Must be using non-blocking IO for the socket to be in the
- // poller during async processing. Need to trigger error
- // handling. Poller may return error codes plus the flags it
- // was waiting for or it may just return an error code. We
- // could return ASYNC_[WRITE|READ]_ERROR here but if we do,
- // there will be no exception associated with the error in
- // application code. By signalling read/write is possible, a
- // read/write will be attempted, fail and that will trigger
- // an exception the application will see.
- // Check the return flags first, followed by what the socket
- // was registered for
- if ((desc[n*2] & Poll.APR_POLLIN) == Poll.APR_POLLIN) {
- // Error probably occurred during a non-blocking read
- if (!processSocket(desc[n*2+1], SocketStatus.OPEN_READ)) {
- // Close socket and clear pool
- closeSocket(desc[n*2+1]);
- }
- } else if ((desc[n*2] & Poll.APR_POLLOUT) == Poll.APR_POLLOUT) {
- // Error probably occurred during a non-blocking write
- if (!processSocket(desc[n*2+1], SocketStatus.OPEN_WRITE)) {
- // Close socket and clear pool
- closeSocket(desc[n*2+1]);
- }
- } else if ((wrapper.pollerFlags & Poll.APR_POLLIN) == Poll.APR_POLLIN) {
- // Can't tell what was happening when the error occurred but the
- // socket is registered for non-blocking read so use that
- if (!processSocket(desc[n*2+1], SocketStatus.OPEN_READ)) {
- // Close socket and clear pool
- closeSocket(desc[n*2+1]);
- }
- } else if ((wrapper.pollerFlags & Poll.APR_POLLOUT) == Poll.APR_POLLOUT) {
- // Can't tell what was happening when the error occurred but the
- // socket is registered for non-blocking write so use that
- if (!processSocket(desc[n*2+1], SocketStatus.OPEN_WRITE)) {
- // Close socket and clear pool
- closeSocket(desc[n*2+1]);
- }
- } else {
+ } else if ((wrapper.pollerFlags & Poll.APR_POLLOUT) == Poll.APR_POLLOUT) {
+ // Can't tell what was happening when the error occurred but the
+ // socket is registered for non-blocking write so use that
+ if (!processSocket(desc[n*2+1], SocketEvent.OPEN_WRITE)) {
// Close socket and clear pool
closeSocket(desc[n*2+1]);
}
@@ -1929,14 +1714,14 @@ public class AprEndpoint extends AbstractEndpoint<Long> {
|| ((desc[n*2] & Poll.APR_POLLOUT) == Poll.APR_POLLOUT)) {
boolean error = false;
if (((desc[n*2] & Poll.APR_POLLIN) == Poll.APR_POLLIN) &&
- !processSocket(desc[n*2+1], SocketStatus.OPEN_READ)) {
+ !processSocket(desc[n*2+1], SocketEvent.OPEN_READ)) {
error = true;
// Close socket and clear pool
closeSocket(desc[n*2+1]);
}
if (!error &&
((desc[n*2] & Poll.APR_POLLOUT) == Poll.APR_POLLOUT) &&
- !processSocket(desc[n*2+1], SocketStatus.OPEN_WRITE)) {
+ !processSocket(desc[n*2+1], SocketEvent.OPEN_WRITE)) {
// Close socket and clear pool
error = true;
closeSocket(desc[n*2+1]);
@@ -2061,30 +1846,24 @@ public class AprEndpoint extends AbstractEndpoint<Long> {
// ----------------------------------------------- SendfileData Inner Class
-
/**
* SendfileData class.
*/
- public static class SendfileData {
+ public static class SendfileData extends SendfileDataBase {
// File
- public String fileName;
- public long fd;
- public long fdpool;
- // Range information
- public long start;
- public long end;
+ protected long fd;
+ protected long fdpool;
// Socket and socket pool
- public long socket;
- // Position
- public long pos;
- // KeepAlive flag
- public boolean keepAlive;
+ protected long socket;
+
+ public SendfileData(String filename, long pos, long length) {
+ super(filename, pos, length);
+ }
}
// --------------------------------------------------- Sendfile Inner Class
-
public class Sendfile implements Runnable {
protected long sendfilePollset = 0;
@@ -2166,7 +1945,7 @@ public class AprEndpoint extends AbstractEndpoint<Long> {
* @return true if all the data has been sent right away, and false
* otherwise
*/
- public boolean add(SendfileData data) {
+ public SendfileState add(SendfileData data) {
// Initialize fd from data given
try {
data.fdpool = Socket.pool(data.socket);
@@ -2174,36 +1953,35 @@ public class AprEndpoint extends AbstractEndpoint<Long> {
(data.fileName, File.APR_FOPEN_READ
| File.APR_FOPEN_SENDFILE_ENABLED | File.APR_FOPEN_BINARY,
0, data.fdpool);
- data.pos = data.start;
// Set the socket to nonblocking mode
Socket.timeoutSet(data.socket, 0);
while (true) {
long nw = Socket.sendfilen(data.socket, data.fd,
- data.pos, data.end - data.pos, 0);
+ data.pos, data.length, 0);
if (nw < 0) {
if (!(-nw == Status.EAGAIN)) {
Pool.destroy(data.fdpool);
data.socket = 0;
- return false;
+ return SendfileState.ERROR;
} else {
// Break the loop and add the socket to poller.
break;
}
} else {
- data.pos = data.pos + nw;
- if (data.pos >= data.end) {
+ data.pos += nw;
+ data.length -= nw;
+ if (data.length == 0) {
// Entire file has been sent
Pool.destroy(data.fdpool);
// Set back socket to blocking mode
- Socket.timeoutSet(
- data.socket, getSoTimeout() * 1000);
- return true;
+ Socket.timeoutSet(data.socket, getSoTimeout() * 1000);
+ return SendfileState.DONE;
}
}
}
} catch (Exception e) {
log.warn(sm.getString("endpoint.sendfile.error"), e);
- return false;
+ return SendfileState.ERROR;
}
// Add socket to the list. Newly added sockets will wait
// at most for pollTime before being polled
@@ -2211,7 +1989,7 @@ public class AprEndpoint extends AbstractEndpoint<Long> {
addS.add(data);
this.notify();
}
- return false;
+ return SendfileState.PENDING;
}
/**
@@ -2308,7 +2086,7 @@ public class AprEndpoint extends AbstractEndpoint<Long> {
// Write some data using sendfile
long nw = Socket.sendfilen(state.socket, state.fd,
state.pos,
- state.end - state.pos, 0);
+ state.length, 0);
if (nw < 0) {
// Close socket and clear pool
remove(state);
@@ -2318,8 +2096,9 @@ public class AprEndpoint extends AbstractEndpoint<Long> {
continue;
}
- state.pos = state.pos + nw;
- if (state.pos >= state.end) {
+ state.pos += nw;
+ state.length -= nw;
+ if (state.length == 0) {
remove(state);
if (state.keepAlive) {
// Destroy file descriptor pool, which should close the file
@@ -2388,19 +2167,6 @@ public class AprEndpoint extends AbstractEndpoint<Long> {
}
- // ------------------------------------------------ Handler Inner Interface
-
-
- /**
- * Bare bones interface used for socket processing. Per thread data is to be
- * stored in the ThreadWithAttributes extra folders, or alternately in
- * thread local fields.
- */
- public interface Handler extends AbstractEndpoint.Handler {
- public SocketState process(SocketWrapper<Long> socket,
- SocketStatus status);
- }
-
// --------------------------------- SocketWithOptionsProcessor Inner Class
@@ -2413,10 +2179,10 @@ public class AprEndpoint extends AbstractEndpoint<Long> {
*/
protected class SocketWithOptionsProcessor implements Runnable {
- protected SocketWrapper<Long> socket = null;
+ protected SocketWrapperBase<Long> socket = null;
- public SocketWithOptionsProcessor(SocketWrapper<Long> socket) {
+ public SocketWithOptionsProcessor(SocketWrapperBase<Long> socket) {
this.socket = socket;
}
@@ -2425,7 +2191,7 @@ public class AprEndpoint extends AbstractEndpoint<Long> {
synchronized (socket) {
if (!deferAccept) {
- if (setSocketOptions(socket.getSocket().longValue())) {
+ if (setSocketOptions(socket)) {
getPoller().add(socket.getSocket().longValue(),
getSoTimeout(), Poll.APR_POLLIN);
} else {
@@ -2435,24 +2201,19 @@ public class AprEndpoint extends AbstractEndpoint<Long> {
}
} else {
// Process the request from this socket
- if (!setSocketOptions(socket.getSocket().longValue())) {
+ if (!setSocketOptions(socket)) {
// Close socket and pool
closeSocket(socket.getSocket().longValue());
socket = null;
return;
}
// Process the request from this socket
- Handler.SocketState state = handler.process(socket,
- SocketStatus.OPEN_READ);
+ Handler.SocketState state = getHandler().process(socket,
+ SocketEvent.OPEN_READ);
if (state == Handler.SocketState.CLOSED) {
// Close socket and pool
closeSocket(socket.getSocket().longValue());
socket = null;
- } else if (state == Handler.SocketState.LONG) {
- socket.access();
- if (socket.isAsync()) {
- waitingRequests.add(socket);
- }
}
}
}
@@ -2467,78 +2228,235 @@ public class AprEndpoint extends AbstractEndpoint<Long> {
* This class is the equivalent of the Worker, but will simply use in an
* external Executor thread pool.
*/
- protected class SocketProcessor implements Runnable {
+ protected class SocketProcessor extends SocketProcessorBase<Long> {
- private final SocketWrapper<Long> socket;
- private final SocketStatus status;
+ public SocketProcessor(SocketWrapperBase<Long> socketWrapper, SocketEvent event) {
+ super(socketWrapper, event);
+ }
- public SocketProcessor(SocketWrapper<Long> socket,
- SocketStatus status) {
- this.socket = socket;
- if (status == null) {
- // Should never happen
- throw new NullPointerException();
+ @Override
+ protected void doRun() {
+ try {
+ // Process the request from this socket
+ SocketState state = getHandler().process(socketWrapper, event);
+ if (state == Handler.SocketState.CLOSED) {
+ // Close socket and pool
+ closeSocket(socketWrapper.getSocket().longValue());
+ }
+ } finally {
+ socketWrapper = null;
+ event = null;
+ //return to cache
+ if (running && !paused) {
+ processorCache.push(this);
+ }
}
- this.status = status;
}
+ }
+
+
+ public static class AprSocketWrapper extends SocketWrapperBase<Long> {
+
+ private static final int SSL_OUTPUT_BUFFER_SIZE = 8192;
+
+ private final ByteBuffer sslOutputBuffer;
+
+ private final Object closedLock = new Object();
+ private volatile boolean closed = false;
+
+ // This field should only be used by Poller#run()
+ private int pollerFlags = 0;
+
+
+ public AprSocketWrapper(Long socket, AprEndpoint endpoint) {
+ super(socket, endpoint);
+
+ // TODO Make the socketWriteBuffer size configurable and align the
+ // SSL and app buffer size settings with NIO & NIO2.
+ if (endpoint.isSSLEnabled()) {
+ sslOutputBuffer = ByteBuffer.allocateDirect(SSL_OUTPUT_BUFFER_SIZE);
+ sslOutputBuffer.position(SSL_OUTPUT_BUFFER_SIZE);
+ } else {
+ sslOutputBuffer = null;
+ }
+
+ socketBufferHandler = new SocketBufferHandler(6 * 1500, 6 * 1500, true);
+ }
+
@Override
- public void run() {
+ public int read(boolean block, byte[] b, int off, int len) throws IOException {
+ int nRead = populateReadBuffer(b, off, len);
+ 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.
+ */
+ }
- // Upgraded connections need to allow multiple threads to access the
- // connection at the same time to enable blocking IO to be used when
- // Servlet 3.1 NIO has been configured
- if (socket.isUpgraded() && SocketStatus.OPEN_WRITE == status) {
- synchronized (socket.getWriteThreadLock()) {
- doRun();
- }
+ // Fill the read buffer as best we can.
+ nRead = fillReadBuffer(block);
+
+ // Fill as much of the remaining byte array as possible with the
+ // data that was just read
+ if (nRead > 0) {
+ socketBufferHandler.configureReadBufferForRead();
+ nRead = Math.min(nRead, len);
+ socketBufferHandler.getReadBuffer().get(b, off, nRead);
+ }
+ return nRead;
+ }
+
+
+ @Override
+ public int read(boolean block, ByteBuffer to) throws IOException {
+ int nRead = populateReadBuffer(to);
+ 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.
+ */
+ }
+
+ // The socket read buffer capacity is socket.appReadBufSize
+ int limit = socketBufferHandler.getReadBuffer().capacity();
+ if (to.isDirect() && to.remaining() >= limit) {
+ to.limit(to.position() + limit);
+ nRead = fillReadBuffer(block, to);
} else {
- synchronized (socket) {
- doRun();
+ // Fill the read buffer as best we can.
+ nRead = fillReadBuffer(block);
+
+ // Fill as much of the remaining byte array as possible with the
+ // data that was just read
+ if (nRead > 0) {
+ nRead = populateReadBuffer(to);
}
}
+ return nRead;
}
- private void doRun() {
- // Process the request from this socket
- if (socket.getSocket() == null) {
- // Closed in another thread
- return;
+
+ private int fillReadBuffer(boolean block) throws IOException {
+ socketBufferHandler.configureReadBufferForWrite();
+ return fillReadBuffer(block, socketBufferHandler.getReadBuffer());
+ }
+
+
+ private int fillReadBuffer(boolean block, ByteBuffer to) throws IOException {
+ if (closed) {
+ throw new IOException(sm.getString("socket.apr.closed", getSocket()));
}
- SocketState state = handler.process(socket, status);
- if (state == Handler.SocketState.CLOSED) {
- // Close socket and pool
- closeSocket(socket.getSocket().longValue());
- } else if (state == Handler.SocketState.LONG) {
- socket.access();
- if (socket.isAsync()) {
- waitingRequests.add(socket);
+
+ Lock readLock = getBlockingStatusReadLock();
+ WriteLock writeLock = getBlockingStatusWriteLock();
+
+ boolean readDone = false;
+ int result = 0;
+ readLock.lock();
+ try {
+ if (getBlockingStatus() == block) {
+ if (block) {
+ Socket.timeoutSet(getSocket().longValue(), getReadTimeout() * 1000);
+ }
+ result = Socket.recvb(getSocket().longValue(), to, to.position(),
+ to.remaining());
+ readDone = true;
}
- } else if (state == Handler.SocketState.ASYNC_END) {
- socket.access();
- SocketProcessor proc = new SocketProcessor(socket,
- SocketStatus.OPEN_READ);
- getExecutor().execute(proc);
+ } finally {
+ readLock.unlock();
}
- }
- }
+ if (!readDone) {
+ writeLock.lock();
+ try {
+ // Set the current settings for this socket
+ setBlockingStatus(block);
+ if (block) {
+ Socket.timeoutSet(getSocket().longValue(), getReadTimeout() * 1000);
+ } else {
+ Socket.timeoutSet(getSocket().longValue(), 0);
+ }
+ // Downgrade the lock
+ readLock.lock();
+ try {
+ writeLock.unlock();
+ result = Socket.recvb(getSocket().longValue(), to, to.position(),
+ to.remaining());
+ } finally {
+ readLock.unlock();
+ }
+ } finally {
+ // Should have been released above but may not have been on some
+ // exception paths
+ if (writeLock.isHeldByCurrentThread()) {
+ writeLock.unlock();
+ }
+ }
+ }
- private static class AprSocketWrapper extends SocketWrapper<Long> {
+ if (result > 0) {
+ to.position(to.position() + result);
+ return result;
+ } else if (result == 0 || -result == Status.EAGAIN) {
+ return 0;
+ } else if (-result == Status.APR_EGENERAL && isSecure()) {
+ // Not entirely sure why this is necessary. Testing to date has not
+ // identified any issues with this but log it so it can be tracked
+ // if it is suspected of causing issues in the future.
+ if (log.isDebugEnabled()) {
+ log.debug(sm.getString("socket.apr.read.sslGeneralError", getSocket(), this));
+ }
+ return 0;
+ } else if ((-result) == Status.ETIMEDOUT || (-result) == Status.TIMEUP) {
+ if (block) {
+ throw new SocketTimeoutException(sm.getString("iib.readtimeout"));
+ } else {
+ // Attempting to read from the socket when the poller
+ // has not signalled that there is data to read appears
+ // to behave like a blocking read with a short timeout
+ // on OSX rather than like a non-blocking read. If no
+ // data is read, treat the resulting timeout like a
+ // non-blocking read that returned no data.
+ return 0;
+ }
+ } else if (-result == Status.APR_EOF) {
+ return -1;
+ } else if ((OS.IS_WIN32 || OS.IS_WIN64) &&
+ (-result == Status.APR_OS_START_SYSERR + 10053)) {
+ // 10053 on Windows is connection aborted
+ throw new EOFException(sm.getString("socket.apr.clientAbort"));
+ } else {
+ throw new IOException(sm.getString("socket.apr.read.error",
+ Integer.valueOf(-result), getSocket(), this));
+ }
+ }
- private final Object closedLock = new Object();
- private boolean closed = false;
- // This field should only be used by Poller#run()
- private int pollerFlags = 0;
+ @Override
+ public boolean isReadyForRead() throws IOException {
+ socketBufferHandler.configureReadBufferForRead();
- private final AprEndpoint endpoint;
+ if (socketBufferHandler.getReadBuffer().remaining() > 0) {
+ return true;
+ }
- public AprSocketWrapper(Long socket, AprEndpoint endpoint) {
- super(socket);
- this.endpoint = endpoint;
+ fillReadBuffer(false);
+
+ boolean isReady = socketBufferHandler.getReadBuffer().position() > 0;
+ return isReady;
}
+
+ @Override
public void close() {
synchronized (closedLock) {
// APR typically crashes if the same socket is closed twice so
@@ -2547,20 +2465,332 @@ public class AprEndpoint extends AbstractEndpoint<Long> {
return;
}
closed = true;
- endpoint.getPoller().close(getSocket().longValue());
+ if (sslOutputBuffer != null) {
+ ByteBufferUtils.cleanDirectBuffer(sslOutputBuffer);
+ }
+ ((AprEndpoint) getEndpoint()).getPoller().close(getSocket().longValue());
}
}
+
@Override
- public void registerforEvent(int timeout, boolean read, boolean write) {
+ public boolean isClosed() {
+ synchronized (closedLock) {
+ return closed;
+ }
+ }
+
+
+ @Override
+ protected void writeByteBufferBlocking(ByteBuffer from) throws IOException {
+ if (from.isDirect()) {
+ super.writeByteBufferBlocking(from);
+ } else {
+ // The socket write buffer capacity is socket.appWriteBufSize
+ ByteBuffer writeBuffer = socketBufferHandler.getWriteBuffer();
+ int limit = writeBuffer.capacity();
+ while (from.remaining() >= limit) {
+ socketBufferHandler.configureWriteBufferForWrite();
+ transfer(from, writeBuffer);
+ doWrite(true);
+ }
+
+ if (from.remaining() > 0) {
+ socketBufferHandler.configureWriteBufferForWrite();
+ transfer(from, writeBuffer);
+ }
+ }
+ }
+
+
+ @Override
+ protected boolean writeByteBufferNonBlocking(ByteBuffer from) throws IOException {
+ if (from.isDirect()) {
+ return super.writeByteBufferNonBlocking(from);
+ } else {
+ // The socket write buffer capacity is socket.appWriteBufSize
+ ByteBuffer writeBuffer = socketBufferHandler.getWriteBuffer();
+ int limit = writeBuffer.capacity();
+ while (from.remaining() >= limit) {
+ socketBufferHandler.configureWriteBufferForWrite();
+ transfer(from, writeBuffer);
+ int newPosition = writeBuffer.position() + limit;
+ doWrite(false);
+ if (writeBuffer.position() != newPosition) {
+ // Didn't write the whole amount of data in the last
+ // non-blocking write.
+ // Exit the loop.
+ return true;
+ }
+ }
+
+ if (from.remaining() > 0) {
+ socketBufferHandler.configureWriteBufferForWrite();
+ transfer(from, writeBuffer);
+ }
+
+ return false;
+ }
+ }
+
+
+ @Override
+ protected void doWrite(boolean block, ByteBuffer from) throws IOException {
+ if (closed) {
+ throw new IOException(sm.getString("socket.apr.closed", getSocket()));
+ }
+
+ Lock readLock = getBlockingStatusReadLock();
+ WriteLock writeLock = getBlockingStatusWriteLock();
+
+ readLock.lock();
+ try {
+ if (getBlockingStatus() == block) {
+ if (block) {
+ Socket.timeoutSet(getSocket().longValue(), getWriteTimeout() * 1000);
+ }
+ doWriteInternal(from);
+ return;
+ }
+ } finally {
+ readLock.unlock();
+ }
+
+ writeLock.lock();
+ try {
+ // Set the current settings for this socket
+ setBlockingStatus(block);
+ if (block) {
+ Socket.timeoutSet(getSocket().longValue(), getWriteTimeout() * 1000);
+ } else {
+ Socket.timeoutSet(getSocket().longValue(), 0);
+ }
+
+ // Downgrade the lock
+ readLock.lock();
+ try {
+ writeLock.unlock();
+ doWriteInternal(from);
+ } finally {
+ readLock.unlock();
+ }
+ } finally {
+ // Should have been released above but may not have been on some
+ // exception paths
+ if (writeLock.isHeldByCurrentThread()) {
+ writeLock.unlock();
+ }
+ }
+ }
+
+
+ private void doWriteInternal(ByteBuffer from) throws IOException {
+ int thisTime;
+
+ do {
+ thisTime = 0;
+ if (getEndpoint().isSSLEnabled()) {
+ if (sslOutputBuffer.remaining() == 0) {
+ // Buffer was fully written last time around
+ sslOutputBuffer.clear();
+ transfer(from, sslOutputBuffer);
+ sslOutputBuffer.flip();
+ } else {
+ // Buffer still has data from previous attempt to write
+ // APR + SSL requires that exactly the same parameters are
+ // passed when re-attempting the write
+ }
+ thisTime = Socket.sendb(getSocket().longValue(), sslOutputBuffer,
+ sslOutputBuffer.position(), sslOutputBuffer.limit());
+ if (thisTime > 0) {
+ sslOutputBuffer.position(sslOutputBuffer.position() + thisTime);
+ }
+ } else {
+ thisTime = Socket.sendb(getSocket().longValue(), from, from.position(),
+ from.remaining());
+ if (thisTime > 0) {
+ from.position(from.position() + thisTime);
+ }
+ }
+ if (Status.APR_STATUS_IS_EAGAIN(-thisTime)) {
+ thisTime = 0;
+ } else if (-thisTime == Status.APR_EOF) {
+ throw new EOFException(sm.getString("socket.apr.clientAbort"));
+ } else if ((OS.IS_WIN32 || OS.IS_WIN64) &&
+ (-thisTime == Status.APR_OS_START_SYSERR + 10053)) {
+ // 10053 on Windows is connection aborted
+ throw new EOFException(sm.getString("socket.apr.clientAbort"));
+ } else if (thisTime < 0) {
+ throw new IOException(sm.getString("socket.apr.write.error",
+ Integer.valueOf(-thisTime), getSocket(), this));
+ }
+ } while ((thisTime > 0 || getBlockingStatus()) && from.hasRemaining());
+
+ // If there is data left in the buffer the socket will be registered for
+ // write further up the stack. This is to ensure the socket is only
+ // registered for write once as both container and user code can trigger
+ // write registration.
+ }
+
+
+ @Override
+ public void registerReadInterest() {
// Make sure an already closed socket is not added to the poller
synchronized (closedLock) {
if (closed) {
return;
}
- endpoint.getPoller().add(getSocket().longValue(), timeout,
- (read ? Poll.APR_POLLIN : 0) | (write ? Poll.APR_POLLOUT : 0));
+ Poller p = ((AprEndpoint) getEndpoint()).getPoller();
+ if (p != null) {
+ p.add(getSocket().longValue(), getReadTimeout(), Poll.APR_POLLIN);
+ }
}
}
+
+
+ @Override
+ public void registerWriteInterest() {
+ // Make sure an already closed socket is not added to the poller
+ synchronized (closedLock) {
+ if (closed) {
+ return;
+ }
+ ((AprEndpoint) getEndpoint()).getPoller().add(
+ getSocket().longValue(), getWriteTimeout(), Poll.APR_POLLOUT);
+ }
+ }
+
+
+ @Override
+ public SendfileDataBase createSendfileData(String filename, long pos, long length) {
+ return new SendfileData(filename, pos, length);
+ }
+
+
+ @Override
+ public SendfileState processSendfile(SendfileDataBase sendfileData) {
+ ((SendfileData) sendfileData).socket = getSocket().longValue();
+ return ((AprEndpoint) getEndpoint()).getSendfile().add((SendfileData) sendfileData);
+ }
+
+
+ @Override
+ protected void populateRemoteAddr() {
+ if (closed) {
+ return;
+ }
+ try {
+ long socket = getSocket().longValue();
+ long sa = Address.get(Socket.APR_REMOTE, socket);
+ remoteAddr = Address.getip(sa);
+ } catch (Exception e) {
+ log.warn(sm.getString("endpoint.warn.noRemoteAddr", getSocket()), e);
+ }
+ }
+
+
+ @Override
+ protected void populateRemoteHost() {
+ if (closed) {
+ return;
+ }
+ try {
+ long socket = getSocket().longValue();
+ long sa = Address.get(Socket.APR_REMOTE, socket);
+ remoteHost = Address.getnameinfo(sa, 0);
+ if (remoteAddr == null) {
+ remoteAddr = Address.getip(sa);
+ }
+ } catch (Exception e) {
+ log.warn(sm.getString("endpoint.warn.noRemoteHost", getSocket()), e);
+ }
+ }
+
+
+ @Override
+ protected void populateRemotePort() {
+ if (closed) {
+ return;
+ }
+ try {
+ long socket = getSocket().longValue();
+ long sa = Address.get(Socket.APR_REMOTE, socket);
+ Sockaddr addr = Address.getInfo(sa);
+ remotePort = addr.port;
+ } catch (Exception e) {
+ log.warn(sm.getString("endpoint.warn.noRemotePort", getSocket()), e);
+ }
+ }
+
+
+ @Override
+ protected void populateLocalName() {
+ if (closed) {
+ return;
+ }
+ try {
+ long socket = getSocket().longValue();
+ long sa = Address.get(Socket.APR_LOCAL, socket);
+ localName =Address.getnameinfo(sa, 0);
+ } catch (Exception e) {
+ log.warn(sm.getString("endpoint.warn.noLocalName"), e);
+ }
+ }
+
+
+ @Override
+ protected void populateLocalAddr() {
+ if (closed) {
+ return;
+ }
+ try {
+ long socket = getSocket().longValue();
+ long sa = Address.get(Socket.APR_LOCAL, socket);
+ localAddr = Address.getip(sa);
+ } catch (Exception e) {
+ log.warn(sm.getString("endpoint.warn.noLocalAddr"), e);
+ }
+ }
+
+
+ @Override
+ protected void populateLocalPort() {
+ if (closed) {
+ return;
+ }
+ try {
+ long socket = getSocket().longValue();
+ long sa = Address.get(Socket.APR_LOCAL, socket);
+ Sockaddr addr = Address.getInfo(sa);
+ localPort = addr.port;
+ } catch (Exception e) {
+ log.warn(sm.getString("endpoint.warn.noLocalPort"), e);
+ }
+ }
+
+
+ @Override
+ public SSLSupport getSslSupport(String clientCertProvider) {
+ if (getEndpoint().isSSLEnabled()) {
+ return new AprSSLSupport(this, clientCertProvider);
+ } else {
+ return null;
+ }
+ }
+
+
+ @Override
+ public void doClientAuth(SSLSupport sslSupport) {
+ long socket = getSocket().longValue();
+ // Configure connection to require a certificate
+ SSLSocket.setVerify(socket, SSL.SSL_CVERIFY_REQUIRE, -1);
+ SSLSocket.renegotiate(socket);
+ }
+
+
+ @Override
+ public void setAppReadBufHandler(ApplicationBufferHandler handler) {
+ // no-op
+ }
}
}
diff --git a/java/org/apache/tomcat/util/net/AprSSLSupport.java b/java/org/apache/tomcat/util/net/AprSSLSupport.java
new file mode 100644
index 0000000..b620ce9
--- /dev/null
+++ b/java/org/apache/tomcat/util/net/AprSSLSupport.java
@@ -0,0 +1,134 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tomcat.util.net;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+
+import org.apache.tomcat.jni.SSL;
+import org.apache.tomcat.jni.SSLSocket;
+
+/**
+ * Implementation of SSLSupport for APR.
+ * <p>
+ * TODO: Add a mechanism (or figure out how to use what we already have) to
+ * invalidate the session.
+ */
+public class AprSSLSupport implements SSLSupport {
+
+ private final SocketWrapperBase<Long> socketWrapper;
+ private final String clientCertProvider;
+
+
+ public AprSSLSupport(SocketWrapperBase<Long> socketWrapper, String clientCertProvider) {
+ this.socketWrapper = socketWrapper;
+ this.clientCertProvider = clientCertProvider;
+ }
+
+
+ @Override
+ public String getCipherSuite() throws IOException {
+ long socketRef = socketWrapper.getSocket().longValue();
+ if (socketRef == 0) {
+ return null;
+ }
+ try {
+ return SSLSocket.getInfoS(socketRef, SSL.SSL_INFO_CIPHER);
+ } catch (Exception e) {
+ throw new IOException(e);
+ }
+ }
+
+
+ @Override
+ public X509Certificate[] getPeerCertificateChain() throws IOException {
+ long socketRef = socketWrapper.getSocket().longValue();
+ if (socketRef == 0) {
+ return null;
+ }
+
+ try {
+ // certLength == -1 indicates an error
+ int certLength = SSLSocket.getInfoI(socketRef, SSL.SSL_INFO_CLIENT_CERT_CHAIN);
+ byte[] clientCert = SSLSocket.getInfoB(socketRef, SSL.SSL_INFO_CLIENT_CERT);
+ X509Certificate[] certs = null;
+ if (clientCert != null && certLength > -1) {
+ certs = new X509Certificate[certLength + 1];
+ CertificateFactory cf;
+ if (clientCertProvider == null) {
+ cf = CertificateFactory.getInstance("X.509");
+ } else {
+ cf = CertificateFactory.getInstance("X.509", clientCertProvider);
+ }
+ certs[0] = (X509Certificate) cf.generateCertificate(new ByteArrayInputStream(clientCert));
+ for (int i = 0; i < certLength; i++) {
+ byte[] data = SSLSocket.getInfoB(socketRef, SSL.SSL_INFO_CLIENT_CERT_CHAIN + i);
+ certs[i+1] = (X509Certificate) cf.generateCertificate(new ByteArrayInputStream(data));
+ }
+ }
+ return certs;
+ } catch (Exception e) {
+ throw new IOException(e);
+ }
+ }
+
+
+ @Override
+ public Integer getKeySize() throws IOException {
+ long socketRef = socketWrapper.getSocket().longValue();
+ if (socketRef == 0) {
+ return null;
+ }
+
+ try {
+ return Integer.valueOf(SSLSocket.getInfoI(socketRef, SSL.SSL_INFO_CIPHER_USEKEYSIZE));
+ } catch (Exception e) {
+ throw new IOException(e);
+ }
+ }
+
+
+ @Override
+ public String getSessionId() throws IOException {
+ long socketRef = socketWrapper.getSocket().longValue();
+ if (socketRef == 0) {
+ return null;
+ }
+
+ try {
+ return SSLSocket.getInfoS(socketRef, SSL.SSL_INFO_SESSION_ID);
+ } catch (Exception e) {
+ throw new IOException(e);
+ }
+ }
+
+ @Override
+ public String getProtocol() throws IOException {
+ long socketRef = socketWrapper.getSocket().longValue();
+ if (socketRef == 0) {
+ return null;
+ }
+
+ try {
+ return SSLSocket.getInfoS(socketRef, SSL.SSL_INFO_PROTOCOL);
+ } catch (Exception e) {
+ throw new IOException(e);
+ }
+ }
+}
diff --git a/java/org/apache/tomcat/util/net/Constants.java b/java/org/apache/tomcat/util/net/Constants.java
index f2fc0c2..c6087a7 100644
--- a/java/org/apache/tomcat/util/net/Constants.java
+++ b/java/org/apache/tomcat/util/net/Constants.java
@@ -24,13 +24,6 @@ public class Constants {
*/
public static final String CATALINA_BASE_PROP = "catalina.base";
-
- /**
- * Has security been turned on?
- */
- public static final boolean IS_SECURITY_ENABLED =
- (System.getSecurityManager() != null);
-
/**
* JSSE and OpenSSL protocol names
*/
@@ -43,4 +36,5 @@ public class Constants {
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";
}
diff --git a/java/org/apache/tomcat/util/net/DefaultServerSocketFactory.java b/java/org/apache/tomcat/util/net/DefaultServerSocketFactory.java
deleted file mode 100644
index 497cfaa..0000000
--- a/java/org/apache/tomcat/util/net/DefaultServerSocketFactory.java
+++ /dev/null
@@ -1,67 +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.net;
-
-import java.io.IOException;
-import java.net.InetAddress;
-import java.net.ServerSocket;
-import java.net.Socket;
-
-/**
- * Default server socket factory. Doesn't do much except give us
- * plain old server sockets.
- *
- * @author db at eng.sun.com
- * @author Harish Prabandham
- */
-public class DefaultServerSocketFactory implements ServerSocketFactory {
-
- /**
- *
- * @param endpoint Unused in this implementation.
- */
- public DefaultServerSocketFactory(AbstractEndpoint<?> endpoint) {
- }
-
- @Override
- public ServerSocket createSocket (int port) throws IOException {
- return new ServerSocket (port);
- }
-
- @Override
- public ServerSocket createSocket (int port, int backlog)
- throws IOException {
- return new ServerSocket (port, backlog);
- }
-
- @Override
- public ServerSocket createSocket (int port, int backlog,
- InetAddress ifAddress) throws IOException {
- return new ServerSocket (port, backlog, ifAddress);
- }
-
- @Override
- public Socket acceptSocket(ServerSocket socket) throws IOException {
- return socket.accept();
- }
-
- @Override
- public void handshake(Socket sock) throws IOException {
- // NOOP
- }
-}
diff --git a/java/org/apache/tomcat/util/net/DispatchType.java b/java/org/apache/tomcat/util/net/DispatchType.java
index cd104b2..9b8a078 100644
--- a/java/org/apache/tomcat/util/net/DispatchType.java
+++ b/java/org/apache/tomcat/util/net/DispatchType.java
@@ -23,16 +23,16 @@ package org.apache.tomcat.util.net;
*/
public enum DispatchType {
- NON_BLOCKING_READ(SocketStatus.OPEN_READ),
- NON_BLOCKING_WRITE(SocketStatus.OPEN_WRITE);
+ NON_BLOCKING_READ(SocketEvent.OPEN_READ),
+ NON_BLOCKING_WRITE(SocketEvent.OPEN_WRITE);
- private final SocketStatus status;
+ private final SocketEvent status;
- private DispatchType(SocketStatus status) {
+ private DispatchType(SocketEvent status) {
this.status = status;
}
- public SocketStatus getSocketStatus() {
+ public SocketEvent getSocketStatus() {
return status;
}
}
diff --git a/java/org/apache/tomcat/util/net/JIoEndpoint.java b/java/org/apache/tomcat/util/net/JIoEndpoint.java
deleted file mode 100644
index 83ad57f..0000000
--- a/java/org/apache/tomcat/util/net/JIoEndpoint.java
+++ /dev/null
@@ -1,551 +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.net;
-
-import java.io.IOException;
-import java.net.BindException;
-import java.net.ServerSocket;
-import java.net.Socket;
-import java.net.SocketException;
-import java.util.concurrent.Executor;
-import java.util.concurrent.RejectedExecutionException;
-
-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.AbstractEndpoint.Handler.SocketState;
-import org.apache.tomcat.util.net.jsse.JSSESocketFactory;
-
-
-/**
- * Handle incoming TCP connections.
- *
- * This class implement a simple server model: one listener thread accepts on a socket and
- * creates a new worker thread for each incoming connection.
- *
- * More advanced Endpoints will reuse the threads, use queues, etc.
- *
- * @author James Duncan Davidson
- * @author Jason Hunter
- * @author James Todd
- * @author Costin Manolache
- * @author Gal Shachor
- * @author Yoav Shapira
- * @author Remy Maucherat
- */
-public class JIoEndpoint extends AbstractEndpoint<Socket> {
-
-
- // -------------------------------------------------------------- Constants
-
- private static final Log log = LogFactory.getLog(JIoEndpoint.class);
-
- // ----------------------------------------------------------------- Fields
-
- /**
- * Associated server socket.
- */
- protected ServerSocket serverSocket = null;
-
-
- // ------------------------------------------------------------ Constructor
-
- public JIoEndpoint() {
- // Set maxConnections to zero so we can tell if the user has specified
- // their own value on the connector when we reach bind()
- setMaxConnections(0);
- // Reduce the executor timeout for BIO as threads in keep-alive will not
- // terminate when the executor interrupts them.
- setExecutorTerminationTimeoutMillis(0);
- // If running on Java 7, the insecure DHE ciphers need to be excluded by
- // default
- if (!JreCompat.isJre8Available()) {
- setCiphers(DEFAULT_CIPHERS + ":!DHE");
- }
- }
-
- // ------------------------------------------------------------- Properties
-
- /**
- * Handling of accepted sockets.
- */
- protected Handler handler = null;
- public void setHandler(Handler handler ) { this.handler = handler; }
- public Handler getHandler() { return handler; }
-
- /**
- * Server socket factory.
- */
- protected ServerSocketFactory serverSocketFactory = null;
- public void setServerSocketFactory(ServerSocketFactory factory) { this.serverSocketFactory = factory; }
- public ServerSocketFactory getServerSocketFactory() { return serverSocketFactory; }
-
- /**
- * Port in use.
- */
- @Override
- public int getLocalPort() {
- ServerSocket s = serverSocket;
- if (s == null) {
- return -1;
- } else {
- return s.getLocalPort();
- }
- }
-
-
- @Override
- public String[] getCiphersUsed() {
- if (serverSocketFactory instanceof JSSESocketFactory) {
- return ((JSSESocketFactory) serverSocketFactory).getEnabledCiphers();
- }
- return new String[0];
- }
-
-
- /*
- * Optional feature support.
- */
- @Override
- public boolean getUseSendfile() { return false; } // Not supported
- @Override
- public boolean getUseComet() { return false; } // Not supported
- @Override
- public boolean getUseCometTimeout() { return false; } // Not supported
- @Override
- public boolean getDeferAccept() { return false; } // Not supported
- @Override
- public boolean getUsePolling() { return false; } // Not supported
-
-
- // ------------------------------------------------ Handler Inner Interface
-
- /**
- * Bare bones interface used for socket processing. Per thread data is to be
- * stored in the ThreadWithAttributes extra folders, or alternately in
- * thread local fields.
- */
- public interface Handler extends AbstractEndpoint.Handler {
- public SocketState process(SocketWrapper<Socket> socket,
- SocketStatus status);
- public SSLImplementation getSslImplementation();
- public void beforeHandshake(SocketWrapper<Socket> socket);
- }
-
-
- // --------------------------------------------------- Acceptor Inner Class
- /**
- * The background thread that listens for incoming TCP/IP connections and
- * hands them off to an appropriate processor.
- */
- protected class Acceptor extends AbstractEndpoint.Acceptor {
-
- @Override
- public void run() {
-
- int errorDelay = 0;
-
- // Loop until we receive a shutdown command
- while (running) {
-
- // Loop if endpoint is paused
- while (paused && running) {
- state = AcceptorState.PAUSED;
- try {
- Thread.sleep(50);
- } catch (InterruptedException e) {
- // Ignore
- }
- }
-
- if (!running) {
- break;
- }
- state = AcceptorState.RUNNING;
-
- try {
- //if we have reached max connections, wait
- countUpOrAwaitConnection();
-
- Socket socket = null;
- try {
- // Accept the next incoming connection from the server
- // socket
- socket = serverSocketFactory.acceptSocket(serverSocket);
- } catch (IOException ioe) {
- countDownConnection();
- // Introduce delay if necessary
- errorDelay = handleExceptionWithDelay(errorDelay);
- // re-throw
- throw ioe;
- }
- // Successful accept, reset the error delay
- errorDelay = 0;
-
- // Configure the socket
- if (running && !paused && setSocketOptions(socket)) {
- // Hand this socket off to an appropriate processor
- if (!processSocket(socket)) {
- countDownConnection();
- // Close socket right away
- closeSocket(socket);
- }
- } else {
- countDownConnection();
- // Close socket right away
- closeSocket(socket);
- }
- } catch (IOException x) {
- if (running) {
- log.error(sm.getString("endpoint.accept.fail"), x);
- }
- } catch (NullPointerException npe) {
- if (running) {
- log.error(sm.getString("endpoint.accept.fail"), npe);
- }
- } catch (Throwable t) {
- ExceptionUtils.handleThrowable(t);
- log.error(sm.getString("endpoint.accept.fail"), t);
- }
- }
- state = AcceptorState.ENDED;
- }
- }
-
-
- private void closeSocket(Socket socket) {
- try {
- socket.close();
- } catch (IOException e) {
- // Ignore
- }
- }
-
-
- // ------------------------------------------- SocketProcessor Inner Class
-
-
- /**
- * This class is the equivalent of the Worker, but will simply use in an
- * external Executor thread pool.
- */
- protected class SocketProcessor implements Runnable {
-
- protected SocketWrapper<Socket> socket = null;
- protected SocketStatus status = null;
-
- public SocketProcessor(SocketWrapper<Socket> socket) {
- if (socket==null) throw new NullPointerException();
- this.socket = socket;
- }
-
- public SocketProcessor(SocketWrapper<Socket> socket, SocketStatus status) {
- this(socket);
- this.status = status;
- }
-
- @Override
- public void run() {
- boolean launch = false;
- synchronized (socket) {
- try {
- SocketState state = SocketState.OPEN;
- handler.beforeHandshake(socket);
- try {
- // SSL handshake
- serverSocketFactory.handshake(socket.getSocket());
- } catch (Throwable t) {
- ExceptionUtils.handleThrowable(t);
- if (log.isDebugEnabled()) {
- log.debug(sm.getString("endpoint.err.handshake"), t);
- }
- // Tell to close the socket
- state = SocketState.CLOSED;
- }
-
- if ((state != SocketState.CLOSED)) {
- if (status == null) {
- state = handler.process(socket, SocketStatus.OPEN_READ);
- } else {
- state = handler.process(socket,status);
- }
- }
- if (state == SocketState.CLOSED) {
- // Close socket
- if (log.isTraceEnabled()) {
- log.trace("Closing socket:"+socket);
- }
- countDownConnection();
- try {
- socket.getSocket().close();
- } catch (IOException e) {
- // Ignore
- }
- } else if (state == SocketState.OPEN ||
- state == SocketState.UPGRADING ||
- state == SocketState.UPGRADED){
- socket.setKeptAlive(true);
- socket.access();
- launch = true;
- } else if (state == SocketState.LONG) {
- socket.access();
- waitingRequests.add(socket);
- }
- } finally {
- if (launch) {
- try {
- getExecutor().execute(new SocketProcessor(socket, SocketStatus.OPEN_READ));
- } catch (RejectedExecutionException x) {
- log.warn("Socket reprocessing request was rejected for:"+socket,x);
- try {
- //unable to handle connection at this time
- handler.process(socket, SocketStatus.DISCONNECT);
- } finally {
- countDownConnection();
- }
-
-
- } catch (NullPointerException npe) {
- if (running) {
- log.error(sm.getString("endpoint.launch.fail"),
- npe);
- }
- }
- }
- }
- }
- socket = null;
- // Finish up this request
- }
-
- }
-
-
- // -------------------- Public methods --------------------
-
- @Override
- public void bind() throws Exception {
-
- // Initialize thread count defaults for acceptor
- if (acceptorThreadCount == 0) {
- acceptorThreadCount = 1;
- }
- // Initialize maxConnections
- if (getMaxConnections() == 0) {
- // User hasn't set a value - use the default
- setMaxConnections(getMaxThreadsInternal());
- }
-
- if (serverSocketFactory == null) {
- if (isSSLEnabled()) {
- serverSocketFactory =
- handler.getSslImplementation().getServerSocketFactory(this);
- } else {
- serverSocketFactory = new DefaultServerSocketFactory(this);
- }
- }
-
- if (serverSocket == null) {
- try {
- if (getAddress() == null) {
- serverSocket = serverSocketFactory.createSocket(getPort(),
- getBacklog());
- } else {
- serverSocket = serverSocketFactory.createSocket(getPort(),
- getBacklog(), getAddress());
- }
- } catch (BindException orig) {
- String msg;
- if (getAddress() == null)
- msg = orig.getMessage() + " <null>:" + getPort();
- else
- msg = orig.getMessage() + " " +
- getAddress().toString() + ":" + getPort();
- BindException be = new BindException(msg);
- be.initCause(orig);
- throw be;
- }
- }
-
- }
-
- @Override
- public void startInternal() throws Exception {
-
- if (!running) {
- running = true;
- paused = false;
-
- // Create worker collection
- if (getExecutor() == null) {
- createExecutor();
- }
-
- initializeConnectionLatch();
-
- startAcceptorThreads();
-
- // Start async timeout thread
- setAsyncTimeout(new AsyncTimeout());
- Thread timeoutThread = new Thread(getAsyncTimeout(), getName() + "-AsyncTimeout");
- timeoutThread.setPriority(threadPriority);
- timeoutThread.setDaemon(true);
- timeoutThread.start();
- }
- }
-
- @Override
- public void stopInternal() {
- releaseConnectionLatch();
- if (!paused) {
- pause();
- }
- if (running) {
- running = false;
- getAsyncTimeout().stop();
- unlockAccept();
- }
- shutdownExecutor();
- }
-
- /**
- * Deallocate APR memory pools, and close server socket.
- */
- @Override
- public void unbind() throws Exception {
- if (running) {
- stop();
- }
- if (serverSocket != null) {
- try {
- if (serverSocket != null)
- serverSocket.close();
- } catch (Exception e) {
- log.error(sm.getString("endpoint.err.close"), e);
- }
- serverSocket = null;
- }
- handler.recycle();
- }
-
-
- @Override
- protected AbstractEndpoint.Acceptor createAcceptor() {
- return new Acceptor();
- }
-
-
- /**
- * Configure the socket.
- */
- protected boolean setSocketOptions(Socket socket) {
- try {
- // 1: Set socket options: timeout, linger, etc
- socketProperties.setProperties(socket);
- } catch (SocketException s) {
- //error here is common if the client has reset the connection
- if (log.isDebugEnabled()) {
- log.debug(sm.getString("endpoint.err.unexpected"), s);
- }
- // Close the socket
- return false;
- } catch (Throwable t) {
- ExceptionUtils.handleThrowable(t);
- log.error(sm.getString("endpoint.err.unexpected"), t);
- // Close the socket
- return false;
- }
- return true;
- }
-
-
- /**
- * Process a new connection from a new client. Wraps the socket so
- * keep-alive and other attributes can be tracked and then passes the socket
- * to the executor for processing.
- *
- * @param socket The socket associated with the client.
- *
- * @return <code>true</code> if the socket is passed to the
- * executor, <code>false</code> if something went wrong or
- * if the endpoint is shutting down. Returning
- * <code>false</code> is an indication to close the socket
- * immediately.
- */
- protected boolean processSocket(Socket socket) {
- // Process the request from this socket
- try {
- SocketWrapper<Socket> wrapper = new SocketWrapper<>(socket);
- wrapper.setKeepAliveLeft(getMaxKeepAliveRequests());
- wrapper.setSecure(isSSLEnabled());
- // During shutdown, executor may be null - avoid NPE
- if (!running) {
- return false;
- }
- getExecutor().execute(new SocketProcessor(wrapper));
- } catch (RejectedExecutionException x) {
- log.warn("Socket processing request was rejected for:"+socket,x);
- return false;
- } catch (Throwable t) {
- ExceptionUtils.handleThrowable(t);
- // This means we got an OOM or similar creating a thread, or that
- // the pool and its queue are full
- log.error(sm.getString("endpoint.process.fail"), t);
- return false;
- }
- return true;
- }
-
-
- @Override
- public void processSocket(SocketWrapper<Socket> socket,
- SocketStatus status, boolean dispatch) {
- try {
- // Synchronisation is required here as this code may be called as a
- // result of calling AsyncContext.dispatch() from a non-container
- // thread
- synchronized (socket) {
- if (waitingRequests.remove(socket)) {
- SocketProcessor proc = new SocketProcessor(socket,status);
- Executor executor = getExecutor();
- if (dispatch && executor != null) {
- // During shutdown, executor may be null - avoid NPE
- if (!running) {
- return;
- }
- getExecutor().execute(proc);
- } else {
- proc.run();
- }
- }
- }
- } catch (RejectedExecutionException ree) {
- log.warn(sm.getString("endpoint.executor.fail", socket) , ree);
- } catch (Throwable t) {
- ExceptionUtils.handleThrowable(t);
- // This means we got an OOM or similar creating a thread, or that
- // the pool and its queue are full
- log.error(sm.getString("endpoint.process.fail"), t);
- }
- }
-
- @Override
- protected Log getLog() {
- return log;
- }
-}
diff --git a/java/org/apache/tomcat/util/net/LocalStrings.properties b/java/org/apache/tomcat/util/net/LocalStrings.properties
new file mode 100644
index 0000000..6139b70
--- /dev/null
+++ b/java/org/apache/tomcat/util/net/LocalStrings.properties
@@ -0,0 +1,133 @@
+# 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.
+
+# net resources
+endpoint.err.close=Caught exception trying to close socket
+endpoint.err.handshake=Handshake failed
+endpoint.err.unexpected=Unexpected error processing socket
+endpoint.warn.unlockAcceptorFailed=Acceptor thread [{0}] failed to unlock. Forcing hard socket shutdown.
+endpoint.warn.executorShutdown=The executor associated with thread pool [{0}] has not fully shutdown. Some application threads may still be running.
+endpoint.warn.noRemoteAddr=Unable to determine remote address for socket [{0}]
+endpoint.warn.noRemoteHost=Unable to determine remote host name for socket [{0}]
+endpoint.warn.noRemotePort=Unable to determine remote port for socket [{0}]
+endpoint.warn.noLocalAddr=Unable to determine local address for socket [{0}]
+endpoint.warn.noLocalName=Unable to determine local host name for socket [{0}]
+endpoint.warn.noLocalPort=Unable to determine local port for socket [{0}]
+endpoint.warn.noSendfileWithSSL=Sendfile is not supported for the connector when SSL is enabled
+endpoint.warn.incorrectConnectionCount=Incorrect connection count, multiple socket.close called on the same socket.
+endpoint.debug.channelCloseFail=Failed to close channel
+endpoint.debug.destroySocket=Destroying socket [{0}]
+endpoint.debug.pollerAdd=Add to addList socket [{0}], timeout [{1}], flags [{2}]
+endpoint.debug.pollerAddDo=Add to poller socket [{0}]
+endpoint.debug.pollerProcess=Processing socket [{0}] for event(s) [{1}]
+endpoint.debug.pollerRemove=Attempting to remove [{0}] from poller
+endpoint.debug.pollerRemoved=Removed [{0}] from poller
+endpoint.debug.socket=socket [{0}]
+endpoint.debug.socketCloseFail=Failed to close socket
+endpoint.debug.socketTimeout=Timing out [{0}]
+endpoint.debug.unlock=Caught exception trying to unlock accept on port {0}
+endpoint.accept.fail=Socket accept failed
+endpoint.alpn.fail=Failed to configure endpoint for ALPN using {0}
+endpoint.alpn.negotiated=Negotiated [{0}] protocol using ALPN
+endpoint.duplicateSslHostName=Multiple SSLHostConfig elements were provided for the host name [{0}]. Host names must be unique.
+endpoint.executor.fail=Executor rejected socket [{0}] for processing
+endpoint.getAttribute=[{0}] is [{1}]
+endpoint.init.bind=Socket bind failed: [{0}] {1}
+endpoint.init.listen=Socket listen failed: [{0}] {1}
+endpoint.init.notavail=APR not available
+endpoint.launch.fail=Failed to launch new runnable
+endpoint.noSslHostName=No host name was provided for the SSL host configuration
+endpoint.poll.limitedpollsize=Failed to create poller with specified size of {0}
+endpoint.poll.initfail=Poller creation failed
+endpoint.poll.fail=Critical poller failure (restarting poller): [{0}] {1}
+endpoint.poll.error=Unexpected poller error
+endpoint.process.fail=Error allocating socket processor
+endpoint.processing.fail=Error running socket processor
+endpoint.sendfile.error=Unexpected sendfile error
+endpoint.sendfile.addfail=Sendfile failure: [{0}] {1}
+endpoint.setAttribute=Set [{0}] to [{1}]
+endpoint.timeout.err=Error processing socket timeout
+endpoint.apr.failSslContextMake=Unable to create SSLContext. Check that SSLEngine is enabled in the AprLifecycleListener, the AprLifecycleListener has initialised correctly and that a valid SSLProtocol has been specified
+endpoint.apr.invalidSslProtocol=An invalid value [{0}] was provided for the SSLProtocol attribute
+endpoint.apr.maxConnections.running=The APR endpoint does not support the setting of maxConnections while it is running. The existing value of [{0}] will continue to be used.
+endpoint.apr.maxConnections.unlimited=The APR endpoint does not support unlimited connections. The existing value of [{0}] will continue to be used.
+endpoint.apr.noSendfileWithSSL=Sendfile is not supported for the APR/native connector when SSL is enabled
+endpoint.apr.noSslCertFile=Connector attribute SSLCertificateFile must be defined when using SSL with APR
+endpoint.apr.pollAddInvalid=Invalid attempted to add a socket [{0}] to the poller
+endpoint.apr.pollError=Poller failed with error [{0}] : [{1}]
+endpoint.apr.pollMergeEvents=Merge poller event [{1}] for socket [{0}] to create merged event [{2}]
+endpoint.apr.pollUnknownEvent=A socket was returned from the poller with an unrecognized event [{0}]
+endpoint.apr.tooManyCertFiles=More certificate files were configured than the AprEndpoint can handle
+endpoint.apr.remoteport=APR socket [{0}] opened with remote port [{1}]
+endpoint.jsse.cannotHonorServerCipherOrder=The Java Runtime does not support "useServerCipherSuitesOrder" with JSSE. You must use OpenSSL or Java 8 onwards to use this feature.
+endpoint.jsse.noSslContext=No SSLContext could be found for the host name [{0}]
+endpoint.nio.registerFail=Failed to register socket with selector from poller
+endpoint.nio.selectorCloseFail=Failed to close selector when closing the poller
+endpoint.nio.timeoutCme=Exception during processing of timeouts. The code has been checked repeatedly and no concurrent modification has been found. If you are able to repeat this error please open a Tomcat bug and provide the steps to reproduce.
+endpoint.nio2.exclusiveExecutor=The NIO2 connector requires an exclusive executor to operate properly on shutdown
+
+channel.nio.interrupted=The current thread was interrupted
+channel.nio.ssl.notHandshaking=NOT_HANDSHAKING during handshake
+channel.nio.ssl.handhakeError=Handshake error
+channel.nio.ssl.unexpectedStatusDuringWrap=Unexpected status [{0}] during handshake WRAP.
+channel.nio.ssl.unexpectedStatusDuringUnwrap=Unexpected status [{0}] during handshake UNWRAP.
+channel.nio.ssl.invalidStatus=Unexpected status [{0}].
+channel.nio.ssl.netInputNotEmpty=Network input buffer still contains data. Handshake will fail.
+channel.nio.ssl.netOutputNotEmpty=Network output buffer still contains data. Handshake will fail.
+channel.nio.ssl.appInputNotEmpty=Application input buffer still contains data. Data would have been lost.
+channel.nio.ssl.appOutputNotEmpty=Application output buffer still contains data. Data would have been lost.
+channel.nio.ssl.eofDuringHandshake=EOF during handshake.
+channel.nio.ssl.timeoutDuringHandshake=Timeout during handshake.
+channel.nio.ssl.remainingDataDuringClose=Remaining data in the network buffer, can't send SSL close message, force a close with close(true) instead
+channel.nio.ssl.pendingWriteDuringClose=Pending write, so remaining data in the network buffer, can't send SSL close message, force a close with close(true) instead
+channel.nio.ssl.invalidCloseState=Invalid close state, will not send network data.
+channel.nio.ssl.unwrapFail=Unable to unwrap data, invalid status [{0}]
+channel.nio.ssl.unwrapFailResize=Unable to unwrap data because buffer is too small, invalid status [{0}]
+channel.nio.ssl.wrapException=Handshake failed during wrap
+channel.nio.ssl.wrapFail=Unable to wrap data, invalid status [{0}]
+channel.nio.ssl.incompleteHandshake=Handshake incomplete, you must complete handshake before reading data.
+channel.nio.ssl.closing=Channel is in closing state.
+channel.nio.ssl.invalidBuffer=You can only read using the application read buffer provided by the handler.
+channel.nio.ssl.expandNetInBuffer=Expanding network input buffer to [{0}] bytes
+channel.nio.ssl.expandNetOutBuffer=Expanding network output buffer to [{0}] bytes
+channel.nio.ssl.sniDefault=Unable to buffer enough data to determine requested SNI host name. Using default
+channel.nio.ssl.sniHostName=The SNI host name extracted for this connection was [{0}]
+
+jsse.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.
+jsse.keystore_load_failed=Failed to load keystore type [{0}] with path [{1}] due to [{2}]
+
+sniExtractor.clientHelloTooBig=The ClientHello was not presented in a single TLS record so no SNI information could be extracted
+
+socket.closed=The socket associated with this connection has been closed.
+socket.sslreneg=Exception re-negotiating SSL connection
+
+
+socket.apr.clientAbort=The client aborted the connection.
+socket.apr.read.error=Unexpected error [{0}] reading data from the APR/native socket [{1}] with wrapper [{2}].
+socket.apr.read.sslGeneralError=An APR general error was returned by the SSL read operation on APR/native socket [{0}] with wrapper [{1}]. It will be treated as EAGAIN and the socket returned to the poller.
+socket.apr.write.error=Unexpected error [{0}] writing data to the APR/native socket [{1}] with wrapper [{2}].
+socket.apr.closed=The socket [{0}] associated with this connection has been closed.
+
+sslHostConfig.certificateVerificationInvalid=The certificate verification value [{0}] is not recognised
+sslHostConfig.certificate.notype=Multiple certificates were specified and at least one is missing the required attribute type
+sslHostConfig.mismatch=The property [{0}] was set on the SSLHostConfig named [{1}] and is for connectors of type [{2}] but the SSLHostConfig is being used with a connector of type [{3}]
+sslHostConfig.prefix_missing=The protocol [{0}] was added to the list of protocols on the SSLHostConfig named [{1}]. Check if a +/- prefix is missing.
+sslHostConfigCertificate.mismatch=The property [{0}] was set on the SSLHostConfigCertificate named [{1}] and is for certificate storage type [{2}] but the certificate is being used with a storage of type [{3}]
+
+sslImplementation.cnfe= Unable to create SSLImplementation for class [{0}]
+
+sslUtilBase.noneSupported=None of the [{0}] specified are supported by the SSL engine : [{1}]
+sslUtilBase.active=The [{0}] that are active are : [{1}]
+sslUtilBase.skipped=Some of the specified [{0}] are not supported by the SSL engine and have been skipped: [{1}]
diff --git a/java/org/apache/tomcat/util/net/LocalStrings_es.properties b/java/org/apache/tomcat/util/net/LocalStrings_es.properties
new file mode 100644
index 0000000..fa530ba
--- /dev/null
+++ b/java/org/apache/tomcat/util/net/LocalStrings_es.properties
@@ -0,0 +1,39 @@
+# 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.
+# net resources
+endpoint.err.handshake = Acuerdo fallido
+endpoint.err.unexpected = Error inesperado al procesar conector
+endpoint.debug.unlock = Excepci\u00F3n cogida intentando desbloquear aceptaci\u00F3n en puerto {0}
+endpoint.err.close = Excepci\u00F3n cogida intentando cerrar conector
+endpoint.init.bind = Ligado de conector fall\u00F3\: [{0}] {1}
+endpoint.init.listen = Escucha de conector fall\u00F3\: [{0}] {1}
+endpoint.init.notavail = APR no disponible
+endpoint.accept.fail = Aceptaci\u00F3n de conector fall\u00F3
+endpoint.poll.limitedpollsize = No pude crear encuestador de medida espec\u00EDfica de {0}
+endpoint.poll.initfail = Fall\u00F3 la creaci\u00F3n del encuestador
+endpoint.poll.fail = Fallo cr\u00EDtico de encuestador (reiniciando encuestador)\: [{0}] {1}
+endpoint.poll.error = Error inesperado de encuestador
+endpoint.process.fail = Error reservando procesador de conector
+endpoint.sendfile.error = Error inesperado de env\u00EDo de fichero
+endpoint.sendfile.addfail = Fallo en Sednfile\: [{0}] {1}
+endpoint.warn.noInsecureReneg = La renegociaci\u00F3n segura no est\u00E1 soportada por la biblioteca SSL {0}
+endpoint.warn.unlockAcceptorFailed = El hilo aceptador [{0}] fall\u00F3 al desbloquear. Forzando apagado de enchufe (socket).
+endpoint.debug.channelCloseFail = No puede cerrar el canal
+endpoint.debug.socketCloseFail = No pude cerrar el enchufe (socket)
+endpoint.apr.noSslCertFile = El atribiuto del conector SSLCertificateFile debe de ser definido al usar SSL con APR
+endpoint.apr.invalidSslProtocol = Se ha proporcionado un valor inv\u00E1lido [{0}] para el atributo SSLProtocol
+
+jsse.invalid_truststore_password = La clave del almac\u00E9n de confianza suministrada no se pudo usar para desbloquear y/o validar el almac\u00E9n de confianza. Reintentando acceder el almac\u00E9n de confianza con una clave nula que se saltar\u00E1 la validaci\u00F3n.
+jsse.keystore_load_failed = No pude cargar almac\u00E9n de claves de tipo [{0}] con ruta [{1}] debido a [{2}]
diff --git a/java/org/apache/tomcat/util/net/res/LocalStrings_fr.properties b/java/org/apache/tomcat/util/net/LocalStrings_fr.properties
similarity index 100%
rename from java/org/apache/tomcat/util/net/res/LocalStrings_fr.properties
rename to java/org/apache/tomcat/util/net/LocalStrings_fr.properties
diff --git a/java/org/apache/tomcat/util/net/res/LocalStrings_ja.properties b/java/org/apache/tomcat/util/net/LocalStrings_ja.properties
similarity index 100%
rename from java/org/apache/tomcat/util/net/res/LocalStrings_ja.properties
rename to java/org/apache/tomcat/util/net/LocalStrings_ja.properties
diff --git a/java/org/apache/tomcat/util/net/Nio2Channel.java b/java/org/apache/tomcat/util/net/Nio2Channel.java
index 7b44936..9cf678e 100644
--- a/java/org/apache/tomcat/util/net/Nio2Channel.java
+++ b/java/org/apache/tomcat/util/net/Nio2Channel.java
@@ -26,8 +26,6 @@ import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
-import org.apache.tomcat.util.net.SecureNio2Channel.ApplicationBufferHandler;
-
/**
* Base class for a SocketChannel wrapper used by the endpoint.
* This way, logic for a SSL socket channel remains the same as for
@@ -35,41 +33,43 @@ import org.apache.tomcat.util.net.SecureNio2Channel.ApplicationBufferHandler;
*/
public class Nio2Channel implements AsynchronousByteChannel {
- protected static ByteBuffer emptyBuf = ByteBuffer.allocate(0);
+ protected static final ByteBuffer emptyBuf = ByteBuffer.allocate(0);
protected AsynchronousSocketChannel sc = null;
- protected SocketWrapper<Nio2Channel> socket = null;
- protected ApplicationBufferHandler bufHandler;
+ protected SocketWrapperBase<Nio2Channel> socket = null;
+ protected final SocketBufferHandler bufHandler;
- public Nio2Channel(ApplicationBufferHandler bufHandler) {
+ public Nio2Channel(SocketBufferHandler bufHandler) {
this.bufHandler = bufHandler;
}
/**
- * Reset the channel
+ * Reset the channel.
+ *
+ * @param channel The new async channel to associate with this NIO2 channel
+ * @param socket The new socket to associate with this NIO2 channel
*
* @throws IOException If a problem was encountered resetting the channel
*/
- public void reset(AsynchronousSocketChannel channel, SocketWrapper<Nio2Channel> socket)
+ public void reset(AsynchronousSocketChannel channel, SocketWrapperBase<Nio2Channel> socket)
throws IOException {
this.sc = channel;
this.socket = socket;
- bufHandler.getReadBuffer().clear();
- bufHandler.getWriteBuffer().clear();
+ bufHandler.reset();
}
- public SocketWrapper<Nio2Channel> getSocket() {
- return socket;
+ /**
+ * Free the channel memory
+ */
+ public void free() {
+ bufHandler.free();
}
- public int getBufferSize() {
- if ( bufHandler == null ) return 0;
- int size = 0;
- size += bufHandler.getReadBuffer()!=null?bufHandler.getReadBuffer().capacity():0;
- size += bufHandler.getWriteBuffer()!=null?bufHandler.getWriteBuffer().capacity():0;
- return size;
+ public SocketWrapperBase<Nio2Channel> getSocket() {
+ return socket;
}
+
/**
* Closes this channel.
*
@@ -80,12 +80,21 @@ public class Nio2Channel implements AsynchronousByteChannel {
sc.close();
}
+
+ /**
+ * 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 {
if (isOpen() || force) {
close();
}
}
+
/**
* Tells whether or not this channel is open.
*
@@ -96,7 +105,7 @@ public class Nio2Channel implements AsynchronousByteChannel {
return sc.isOpen();
}
- public ApplicationBufferHandler getBufHandler() {
+ public SocketBufferHandler getBufHandler() {
return bufHandler;
}
@@ -117,7 +126,8 @@ public class Nio2Channel implements AsynchronousByteChannel {
* implementation.
*
* @return Always returns zero
- * @throws IOException
+ *
+ * @throws IOException Never for non-secure channel
*/
public int handshake() throws IOException {
return 0;
@@ -145,6 +155,12 @@ public class Nio2Channel implements AsynchronousByteChannel {
sc.read(dst, timeout, unit, attachment, handler);
}
+ public <A> void read(ByteBuffer[] dsts,
+ int offset, int length, long timeout, TimeUnit unit,
+ A attachment, CompletionHandler<Long,? super A> handler) {
+ sc.read(dsts, offset, length, timeout, unit, attachment, handler);
+ }
+
@Override
public Future<Integer> write(ByteBuffer src) {
return sc.write(src);
@@ -197,4 +213,12 @@ public class Nio2Channel implements AsynchronousByteChannel {
return DONE;
}
+
+ private ApplicationBufferHandler appReadBufHandler;
+ public void setAppReadBufHandler(ApplicationBufferHandler handler) {
+ this.appReadBufHandler = handler;
+ }
+ protected ApplicationBufferHandler getAppReadBufHandler() {
+ return appReadBufHandler;
+ }
}
diff --git a/java/org/apache/tomcat/util/net/Nio2Endpoint.java b/java/org/apache/tomcat/util/net/Nio2Endpoint.java
index e2a71d4..a42c7d0 100644
--- a/java/org/apache/tomcat/util/net/Nio2Endpoint.java
+++ b/java/org/apache/tomcat/util/net/Nio2Endpoint.java
@@ -14,7 +14,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
package org.apache.tomcat.util.net;
import java.io.EOFException;
@@ -22,39 +21,42 @@ import java.io.File;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
+import java.net.SocketTimeoutException;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousChannelGroup;
+import java.nio.channels.AsynchronousCloseException;
import java.nio.channels.AsynchronousServerSocketChannel;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.CompletionHandler;
import java.nio.channels.FileChannel;
+import java.nio.channels.ReadPendingException;
+import java.nio.channels.WritePendingException;
import java.nio.file.StandardOpenOption;
-import java.util.Locale;
-import java.util.concurrent.Executor;
+import java.util.ArrayList;
+import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
-import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicInteger;
-import javax.net.ssl.KeyManager;
-import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
-import javax.net.ssl.SSLSessionContext;
-import javax.net.ssl.X509KeyManager;
+import javax.net.ssl.SSLSession;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.ExceptionUtils;
+import org.apache.tomcat.util.buf.ByteBufferHolder;
import org.apache.tomcat.util.collections.SynchronizedStack;
-import org.apache.tomcat.util.compat.JreCompat;
import org.apache.tomcat.util.net.AbstractEndpoint.Handler.SocketState;
-import org.apache.tomcat.util.net.SecureNio2Channel.ApplicationBufferHandler;
-import org.apache.tomcat.util.net.jsse.NioX509KeyManager;
+import org.apache.tomcat.util.net.jsse.JSSESupport;
/**
* NIO2 endpoint.
*/
-public class Nio2Endpoint extends AbstractEndpoint<Nio2Channel> {
+public class Nio2Endpoint extends AbstractJsseEndpoint<Nio2Channel> {
// -------------------------------------------------------------- Constants
@@ -71,16 +73,6 @@ public class Nio2Endpoint extends AbstractEndpoint<Nio2Channel> {
private AsynchronousServerSocketChannel serverSock = null;
/**
- * use send file
- */
- private boolean useSendfile = true;
-
- /**
- * The size of the OOM parachute.
- */
- private int oomParachute = 1024*1024;
-
- /**
* Allows detecting if a completion handler completes inline.
*/
private static ThreadLocal<Boolean> inlineCompletion = new ThreadLocal<>();
@@ -93,91 +85,24 @@ public class Nio2Endpoint extends AbstractEndpoint<Nio2Channel> {
private volatile boolean allClosed;
/**
- * The oom parachute, when an OOM error happens,
- * will release the data, giving the JVM instantly
- * a chunk of data to be able to recover with.
- */
- private byte[] oomParachuteData = null;
-
- /**
- * Make sure this string has already been allocated
- */
- private static final String oomParachuteMsg =
- "SEVERE:Memory usage is low, parachute is non existent, your system may start failing.";
-
- /**
- * Keep track of OOM warning messages.
- */
- private long lastParachuteCheck = System.currentTimeMillis();
-
- /**
- * Cache for SocketProcessor objects
- */
- private SynchronizedStack<SocketProcessor> processorCache;
-
- /**
* Bytebuffer cache, each channel holds a set of buffers (two, except for SSL holds four)
*/
private SynchronizedStack<Nio2Channel> nioChannels;
- // ------------------------------------------------------------ Constructor
-
public Nio2Endpoint() {
- // If running on Java 7, the insecure DHE ciphers need to be excluded by
- // default
- if (!JreCompat.isJre8Available()) {
- setCiphers(DEFAULT_CIPHERS + ":!DHE");
- }
+ // Override the defaults for NIO2
+ // Disable maxConnections by default for NIO2 (see BZ58103)
+ setMaxConnections(-1);
}
// ------------------------------------------------------------- Properties
- /**
- * Use the object caches to reduce GC at the expense of additional memory use.
- */
- private boolean useCaches = false;
- public void setUseCaches(boolean useCaches) { this.useCaches = useCaches; }
- public boolean getUseCaches() { return useCaches; }
-
-
- /**
- * Priority of the poller threads.
- */
- private int pollerThreadPriority = Thread.NORM_PRIORITY;
- public void setPollerThreadPriority(int pollerThreadPriority) { this.pollerThreadPriority = pollerThreadPriority; }
- public int getPollerThreadPriority() { return pollerThreadPriority; }
-
-
- /**
- * Handling of accepted sockets.
- */
- private Handler handler = null;
- public void setHandler(Handler handler ) { this.handler = handler; }
- public Handler getHandler() { return handler; }
-
-
- /**
- * Allow comet request handling.
- */
- private boolean useComet = true;
- public void setUseComet(boolean useComet) { this.useComet = useComet; }
- @Override
- public boolean getUseComet() { return useComet; }
- @Override
- public boolean getUseCometTimeout() { return getUseComet(); }
- @Override
- public boolean getUsePolling() { return true; } // Always supported
-
public void setSocketProperties(SocketProperties socketProperties) {
this.socketProperties = socketProperties;
}
- public void setUseSendfile(boolean useSendfile) {
- this.useSendfile = useSendfile;
- }
-
/**
* Is deferAccept supported?
*/
@@ -187,20 +112,6 @@ public class Nio2Endpoint extends AbstractEndpoint<Nio2Channel> {
return false;
}
- public void setOomParachute(int oomParachute) {
- this.oomParachute = oomParachute;
- }
-
- public void setOomParachuteData(byte[] oomParachuteData) {
- this.oomParachuteData = oomParachuteData;
- }
-
-
- private SSLContext sslContext = null;
- public SSLContext getSSLContext() { return sslContext;}
- public void setSSLContext(SSLContext c) { sslContext = c;}
- private String[] enabledCiphers;
- private String[] enabledProtocols;
/**
* Port in use.
@@ -213,7 +124,7 @@ public class Nio2Endpoint extends AbstractEndpoint<Nio2Channel> {
} else {
try {
SocketAddress sa = ssc.getLocalAddress();
- if (sa != null && sa instanceof InetSocketAddress) {
+ if (sa instanceof InetSocketAddress) {
return ((InetSocketAddress) sa).getPort();
} else {
return -1;
@@ -225,47 +136,12 @@ public class Nio2Endpoint extends AbstractEndpoint<Nio2Channel> {
}
- @Override
- public String[] getCiphersUsed() {
- return enabledCiphers;
- }
-
-
- // --------------------------------------------------------- OOM Parachute Methods
-
- protected void checkParachute() {
- boolean para = reclaimParachute(false);
- if (!para && (System.currentTimeMillis()-lastParachuteCheck)>10000) {
- try {
- log.fatal(oomParachuteMsg);
- }catch (Throwable t) {
- ExceptionUtils.handleThrowable(t);
- System.err.println(oomParachuteMsg);
- }
- lastParachuteCheck = System.currentTimeMillis();
- }
- }
-
- protected boolean reclaimParachute(boolean force) {
- if ( oomParachuteData != null ) return true;
- if ( oomParachute > 0 && ( force || (Runtime.getRuntime().freeMemory() > (oomParachute*2))) )
- oomParachuteData = new byte[oomParachute];
- return oomParachuteData != null;
- }
-
- protected void releaseCaches() {
- if (useCaches) {
- this.nioChannels.clear();
- this.processorCache.clear();
- }
- if ( handler != null ) handler.recycle();
-
- }
-
// --------------------------------------------------------- Public Methods
/**
- * Number of keepalive sockets.
+ * Number of keep-alive sockets.
+ *
+ * @return Always returns -1.
*/
public int getKeepAliveCount() {
// For this connector, only the overall connection count is relevant
@@ -306,42 +182,7 @@ public class Nio2Endpoint extends AbstractEndpoint<Nio2Channel> {
}
// Initialize SSL if needed
- if (isSSLEnabled()) {
- SSLUtil sslUtil = handler.getSslImplementation().getSSLUtil(this);
-
- sslContext = sslUtil.createSSLContext();
- sslContext.init(wrap(sslUtil.getKeyManagers()),
- sslUtil.getTrustManagers(), null);
-
- SSLSessionContext sessionContext =
- sslContext.getServerSessionContext();
- if (sessionContext != null) {
- sslUtil.configureSessionContext(sessionContext);
- }
- // Determine which cipher suites and protocols to enable
- enabledCiphers = sslUtil.getEnableableCiphers(sslContext);
- enabledProtocols = sslUtil.getEnableableProtocols(sslContext);
- }
-
- if (oomParachute>0) reclaimParachute(true);
- }
-
- public KeyManager[] wrap(KeyManager[] managers) {
- if (managers==null) return null;
- KeyManager[] result = new KeyManager[managers.length];
- for (int i=0; i<result.length; i++) {
- if (managers[i] instanceof X509KeyManager && getKeyAlias()!=null) {
- String keyAlias = getKeyAlias();
- // JKS keystores always convert the alias name to lower case
- if ("jks".equalsIgnoreCase(getKeystoreType())) {
- keyAlias = keyAlias.toLowerCase(Locale.ENGLISH);
- }
- result[i] = new NioX509KeyManager((X509KeyManager) managers[i], keyAlias);
- } else {
- result[i] = managers[i];
- }
- }
- return result;
+ initialiseSsl();
}
@@ -356,12 +197,10 @@ public class Nio2Endpoint extends AbstractEndpoint<Nio2Channel> {
running = true;
paused = false;
- if (useCaches) {
- processorCache = new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE,
- socketProperties.getProcessorCache());
- nioChannels = new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE,
- socketProperties.getBufferPool());
- }
+ processorCache = new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE,
+ socketProperties.getProcessorCache());
+ nioChannels = new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE,
+ socketProperties.getBufferPool());
// Create worker collection
if ( getExecutor() == null ) {
@@ -370,12 +209,6 @@ public class Nio2Endpoint extends AbstractEndpoint<Nio2Channel> {
initializeConnectionLatch();
startAcceptorThreads();
-
- setAsyncTimeout(new AsyncTimeout());
- Thread timeoutThread = new Thread(getAsyncTimeout(), getName() + "-AsyncTimeout");
- timeoutThread.setPriority(threadPriority);
- timeoutThread.setDaemon(true);
- timeoutThread.start();
}
}
@@ -391,20 +224,17 @@ public class Nio2Endpoint extends AbstractEndpoint<Nio2Channel> {
}
if (running) {
running = false;
- getAsyncTimeout().stop();
unlockAccept();
// Use the executor to avoid binding the main thread if something bad
// occurs and unbind will also wait for a bit for it to complete
getExecutor().execute(new Runnable() {
@Override
public void run() {
- // Timeout any pending async request
- for (SocketWrapper<Nio2Channel> socket : waitingRequests) {
- processSocket(socket, SocketStatus.TIMEOUT, false);
- }
- // Then close all active connections if any remains
+ // Then close all active connections if any remain
try {
- handler.closeAll();
+ for (Nio2Channel channel : getHandler().getOpenSockets()) {
+ closeSocket(channel.getSocket());
+ }
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
} finally {
@@ -412,10 +242,8 @@ public class Nio2Endpoint extends AbstractEndpoint<Nio2Channel> {
}
}
});
- if (useCaches) {
- nioChannels.clear();
- processorCache.clear();
- }
+ nioChannels.clear();
+ processorCache.clear();
}
}
@@ -431,10 +259,13 @@ public class Nio2Endpoint extends AbstractEndpoint<Nio2Channel> {
// Close server socket
serverSock.close();
serverSock = null;
- sslContext = null;
+ destroySsl();
+ super.unbind();
// Unlike other connectors, the thread pool is tied to the server socket
shutdownExecutor();
- releaseCaches();
+ if (getHandler() != null) {
+ getHandler().recycle();
+ }
}
@@ -478,187 +309,84 @@ public class Nio2Endpoint extends AbstractEndpoint<Nio2Channel> {
}
@Override
- public boolean getUseSendfile() {
- return useSendfile;
- }
-
- public int getOomParachute() {
- return oomParachute;
- }
-
- public byte[] getOomParachuteData() {
- return oomParachuteData;
- }
-
-
- @Override
protected AbstractEndpoint.Acceptor createAcceptor() {
return new Acceptor();
}
-
/**
* Process the specified connection.
+ * @param socket The socket channel
+ * @return <code>true</code> if the socket was correctly configured
+ * and processing may continue, <code>false</code> if the socket needs to be
+ * close immediately
*/
protected boolean setSocketOptions(AsynchronousSocketChannel socket) {
- // Process the connection
try {
socketProperties.setProperties(socket);
-
- Nio2Channel channel = (useCaches) ? nioChannels.pop() : null;
+ Nio2Channel channel = nioChannels.pop();
if (channel == null) {
- // SSL setup
- if (sslContext != null) {
- SSLEngine engine = createSSLEngine();
- int appBufferSize = engine.getSession().getApplicationBufferSize();
- NioBufferHandler bufhandler = new NioBufferHandler(
- Math.max(appBufferSize, socketProperties.getAppReadBufSize()),
- Math.max(appBufferSize, socketProperties.getAppWriteBufSize()),
- socketProperties.getDirectBuffer());
- channel = new SecureNio2Channel(engine, bufhandler, this);
+ SocketBufferHandler bufhandler = new SocketBufferHandler(
+ socketProperties.getAppReadBufSize(),
+ socketProperties.getAppWriteBufSize(),
+ socketProperties.getDirectBuffer());
+ if (isSSLEnabled()) {
+ channel = new SecureNio2Channel(bufhandler, this);
} else {
- NioBufferHandler bufhandler = new NioBufferHandler(
- socketProperties.getAppReadBufSize(),
- socketProperties.getAppWriteBufSize(),
- socketProperties.getDirectBuffer());
channel = new Nio2Channel(bufhandler);
}
- } else {
- if (sslContext != null) {
- SSLEngine engine = createSSLEngine();
- ((SecureNio2Channel) channel).setSSLEngine(engine);
- }
}
- Nio2SocketWrapper socketWrapper = new Nio2SocketWrapper(channel);
+ Nio2SocketWrapper socketWrapper = new Nio2SocketWrapper(channel, this);
channel.reset(socket, socketWrapper);
- socketWrapper.setTimeout(getSocketProperties().getSoTimeout());
+ socketWrapper.setReadTimeout(getSocketProperties().getSoTimeout());
+ socketWrapper.setWriteTimeout(getSocketProperties().getSoTimeout());
socketWrapper.setKeepAliveLeft(Nio2Endpoint.this.getMaxKeepAliveRequests());
socketWrapper.setSecure(isSSLEnabled());
- if (sslContext != null) {
- // Use the regular processing, as the first handshake needs to be done there
- processSocket(socketWrapper, SocketStatus.OPEN_READ, true);
- } else {
- // Wait until some bytes are available to start the real processing
- awaitBytes(socketWrapper);
- }
+ socketWrapper.setReadTimeout(getSoTimeout());
+ socketWrapper.setWriteTimeout(getSoTimeout());
+ // Continue processing on another thread
+ return processSocket(socketWrapper, SocketEvent.OPEN_READ, true);
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
- try {
- log.error("",t);
- } catch (Throwable tt) {
- ExceptionUtils.handleThrowable(t);
- }
- // Tell to close the socket
- return false;
+ log.error("",t);
}
- return true;
- }
-
- protected SSLEngine createSSLEngine() {
- SSLEngine engine = sslContext.createSSLEngine();
- if ("false".equals(getClientAuth())) {
- engine.setNeedClientAuth(false);
- engine.setWantClientAuth(false);
- } else if ("true".equals(getClientAuth()) || "yes".equals(getClientAuth())){
- engine.setNeedClientAuth(true);
- } else if ("want".equals(getClientAuth())) {
- engine.setWantClientAuth(true);
- }
- engine.setUseClientMode(false);
- engine.setEnabledCipherSuites(enabledCiphers);
- engine.setEnabledProtocols(enabledProtocols);
-
- configureUseServerCipherSuitesOrder(engine);
-
- return engine;
- }
-
-
- /**
- * Returns true if a worker thread is available for processing.
- * @return boolean
- */
- protected boolean isWorkerAvailable() {
- return true;
+ // Tell to close the socket
+ return false;
}
@Override
- public void processSocket(SocketWrapper<Nio2Channel> socketWrapper,
- SocketStatus socketStatus, boolean dispatch) {
- processSocket0(socketWrapper, socketStatus, dispatch);
+ protected SocketProcessorBase<Nio2Channel> createSocketProcessor(
+ SocketWrapperBase<Nio2Channel> socketWrapper, SocketEvent event) {
+ return new SocketProcessor(socketWrapper, event);
}
- protected boolean processSocket0(SocketWrapper<Nio2Channel> socketWrapper, SocketStatus status, boolean dispatch) {
- try {
- waitingRequests.remove(socketWrapper);
- SocketProcessor sc = (useCaches) ? processorCache.pop() : null;
- if (sc == null) {
- sc = new SocketProcessor(socketWrapper, status);
- } else {
- sc.reset(socketWrapper, status);
- }
- Executor executor = getExecutor();
- if (dispatch && executor != null) {
- executor.execute(sc);
- } else {
- sc.run();
- }
- } catch (RejectedExecutionException ree) {
- log.debug(sm.getString("endpoint.executor.fail", socketWrapper), ree);
- return false;
- } catch (Throwable t) {
- ExceptionUtils.handleThrowable(t);
- // This means we got an OOM or similar creating a thread, or that
- // the pool and its queue are full
- log.error(sm.getString("endpoint.process.fail"), t);
- return false;
- }
- return true;
- }
- public void closeSocket(SocketWrapper<Nio2Channel> socket, SocketStatus status) {
+ public void closeSocket(SocketWrapperBase<Nio2Channel> socket) {
+ if (log.isDebugEnabled()) {
+ log.debug("Calling [" + this + "].closeSocket([" + socket + "],[" + socket.getSocket() + "])",
+ new Exception());
+ }
if (socket == null) {
return;
}
try {
- if (socket.isComet() && status != null) {
- socket.setComet(false);//to avoid a loop
- if (status == SocketStatus.TIMEOUT) {
- if (processSocket0(socket, status, true)) {
- return; // don't close on comet timeout
- }
- } else {
- // Don't dispatch if the lines below are canceling the key
- processSocket0(socket, status, false);
- }
- }
+ getHandler().release(socket);
} catch (Throwable e) {
ExceptionUtils.handleThrowable(e);
if (log.isDebugEnabled()) log.error("",e);
}
try {
- handler.release(socket);
+ synchronized (socket.getSocket()) {
+ if (socket.getSocket().isOpen()) {
+ countDownConnection();
+ socket.getSocket().close(true);
+ }
+ }
} catch (Throwable e) {
ExceptionUtils.handleThrowable(e);
if (log.isDebugEnabled()) log.error("",e);
}
try {
- if (socket.getSocket() != null) {
- synchronized (socket.getSocket()) {
- if (socket.getSocket() != null && socket.getSocket().isOpen()) {
- countDownConnection();
- socket.getSocket().close(true);
- }
- }
- }
- } catch (Exception e){
- if (log.isDebugEnabled()) {
- log.debug(sm.getString(
- "endpoint.debug.socketCloseFail"), e);
- }
- }
- try {
Nio2SocketWrapper nio2Socket = (Nio2SocketWrapper) socket;
if (nio2Socket.getSendfileData() != null
&& nio2Socket.getSendfileData().fchannel != null
@@ -738,12 +466,24 @@ public class Nio2Endpoint extends AbstractEndpoint<Nio2Channel> {
// Hand this socket off to an appropriate processor
if (!setSocketOptions(socket)) {
countDownConnection();
- closeSocket(socket);
- }
+ try {
+ socket.close();
+ } catch (IOException ioe) {
+ if (log.isDebugEnabled()) {
+ log.debug("", ioe);
+ }
+ }
+ }
} else {
countDownConnection();
// Close socket right away
- closeSocket(socket);
+ try {
+ socket.close();
+ } catch (IOException ioe) {
+ if (log.isDebugEnabled()) {
+ log.debug("", ioe);
+ }
+ }
}
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
@@ -756,326 +496,1204 @@ public class Nio2Endpoint extends AbstractEndpoint<Nio2Channel> {
}
- private void closeSocket(AsynchronousSocketChannel socket) {
- try {
- socket.close();
- } catch (IOException ioe) {
- if (log.isDebugEnabled()) {
- log.debug("", ioe);
+ public static class Nio2SocketWrapper extends SocketWrapperBase<Nio2Channel> {
+
+ private static final ThreadLocal<AtomicInteger> nestedWriteCompletionCount =
+ new ThreadLocal<AtomicInteger>() {
+ @Override
+ protected AtomicInteger initialValue() {
+ return new AtomicInteger(0);
}
- }
- }
+ };
+ private SendfileData sendfileData = null;
- public static class Nio2SocketWrapper extends SocketWrapper<Nio2Channel> {
+ private final CompletionHandler<Integer, SocketWrapperBase<Nio2Channel>> readCompletionHandler;
+ private final Semaphore readPending = new Semaphore(1);
+ private boolean readInterest = false; // Guarded by readCompletionHandler
- private SendfileData sendfileData = null;
- private boolean upgradeInit = false;
+ private final CompletionHandler<Integer, ByteBuffer> writeCompletionHandler;
+ private final CompletionHandler<Long, ByteBuffer[]> gatheringWriteCompletionHandler;
+ private final Semaphore writePending = new Semaphore(1);
+ private boolean writeInterest = false; // Guarded by writeCompletionHandler
+ private boolean writeNotify = false;
- public Nio2SocketWrapper(Nio2Channel channel) {
- super(channel);
- }
+ private CompletionHandler<Integer, SocketWrapperBase<Nio2Channel>> awaitBytesHandler
+ = new CompletionHandler<Integer, SocketWrapperBase<Nio2Channel>>() {
- @Override
- public long getTimeout() {
- long timeout = super.getTimeout();
- return (timeout > 0) ? timeout : Long.MAX_VALUE;
- }
+ @Override
+ public void completed(Integer nBytes, SocketWrapperBase<Nio2Channel> attachment) {
+ if (nBytes.intValue() < 0) {
+ failed(new ClosedChannelException(), attachment);
+ return;
+ }
+ getEndpoint().processSocket(attachment, SocketEvent.OPEN_READ, Nio2Endpoint.isInline());
+ }
- @Override
- public void setUpgraded(boolean upgraded) {
- if (upgraded && !isUpgraded()) {
- upgradeInit = true;
+ @Override
+ public void failed(Throwable exc, SocketWrapperBase<Nio2Channel> attachment) {
+ getEndpoint().processSocket(attachment, SocketEvent.DISCONNECT, true);
+ }
+ };
+
+ private CompletionHandler<Integer, SendfileData> sendfileHandler
+ = new CompletionHandler<Integer, SendfileData>() {
+
+ @Override
+ public void completed(Integer nWrite, SendfileData attachment) {
+ if (nWrite.intValue() < 0) {
+ failed(new EOFException(), attachment);
+ return;
+ }
+ attachment.pos += nWrite.intValue();
+ ByteBuffer buffer = getSocket().getBufHandler().getWriteBuffer();
+ if (!buffer.hasRemaining()) {
+ if (attachment.length <= 0) {
+ // All data has now been written
+ setSendfileData(null);
+ try {
+ attachment.fchannel.close();
+ } catch (IOException e) {
+ // Ignore
+ }
+ if (attachment.keepAlive) {
+ if (!isInline()) {
+ awaitBytes();
+ } else {
+ attachment.doneInline = true;
+ }
+ } else {
+ if (!isInline()) {
+ getEndpoint().processSocket(Nio2SocketWrapper.this, SocketEvent.DISCONNECT, false);
+ } else {
+ attachment.doneInline = true;
+ }
+ }
+ return;
+ } else {
+ getSocket().getBufHandler().configureWriteBufferForWrite();
+ int nRead = -1;
+ try {
+ nRead = attachment.fchannel.read(buffer);
+ } catch (IOException e) {
+ failed(e, attachment);
+ return;
+ }
+ if (nRead > 0) {
+ getSocket().getBufHandler().configureWriteBufferForRead();
+ if (attachment.length < buffer.remaining()) {
+ buffer.limit(buffer.limit() - buffer.remaining() + (int) attachment.length);
+ }
+ attachment.length -= nRead;
+ } else {
+ failed(new EOFException(), attachment);
+ return;
+ }
+ }
+ }
+ getSocket().write(buffer, getNio2WriteTimeout(), TimeUnit.MILLISECONDS, attachment, this);
+ }
+
+ @Override
+ public void failed(Throwable exc, SendfileData attachment) {
+ try {
+ attachment.fchannel.close();
+ } catch (IOException e) {
+ // Ignore
+ }
+ if (!isInline()) {
+ getEndpoint().processSocket(Nio2SocketWrapper.this, SocketEvent.ERROR, false);
+ } else {
+ attachment.doneInline = true;
+ attachment.error = true;
+ }
}
- super.setUpgraded(upgraded);
+ };
+
+ public Nio2SocketWrapper(Nio2Channel channel, final Nio2Endpoint endpoint) {
+ super(channel, endpoint);
+ socketBufferHandler = channel.getBufHandler();
+
+ this.readCompletionHandler = new CompletionHandler<Integer, SocketWrapperBase<Nio2Channel>>() {
+ @Override
+ public void completed(Integer nBytes, SocketWrapperBase<Nio2Channel> attachment) {
+ boolean notify = false;
+ if (log.isDebugEnabled()) {
+ log.debug("Socket: [" + attachment + "], Interest: [" + readInterest + "]");
+ }
+ synchronized (readCompletionHandler) {
+ if (nBytes.intValue() < 0) {
+ failed(new EOFException(), attachment);
+ } else {
+ if (readInterest && !Nio2Endpoint.isInline()) {
+ readInterest = false;
+ notify = true;
+ } else {
+ // Release here since there will be no
+ // notify/dispatch to do the release.
+ readPending.release();
+ }
+ }
+ }
+ if (notify) {
+ getEndpoint().processSocket(attachment, SocketEvent.OPEN_READ, false);
+ }
+ }
+ @Override
+ public void failed(Throwable exc, SocketWrapperBase<Nio2Channel> attachment) {
+ IOException ioe;
+ if (exc instanceof IOException) {
+ ioe = (IOException) exc;
+ } else {
+ ioe = new IOException(exc);
+ }
+ setError(ioe);
+ if (exc instanceof AsynchronousCloseException) {
+ // Release here since there will be no
+ // notify/dispatch to do the release.
+ readPending.release();
+ // If already closed, don't call onError and close again
+ return;
+ }
+ getEndpoint().processSocket(attachment, SocketEvent.ERROR, true);
+ }
+ };
+
+ this.writeCompletionHandler = new CompletionHandler<Integer, ByteBuffer>() {
+ @Override
+ public void completed(Integer nBytes, ByteBuffer attachment) {
+ writeNotify = false;
+ synchronized (writeCompletionHandler) {
+ if (nBytes.intValue() < 0) {
+ failed(new EOFException(sm.getString("iob.failedwrite")), attachment);
+ } else if (bufferedWrites.size() > 0) {
+ nestedWriteCompletionCount.get().incrementAndGet();
+ // Continue writing data using a gathering write
+ ArrayList<ByteBuffer> arrayList = new ArrayList<>();
+ if (attachment.hasRemaining()) {
+ arrayList.add(attachment);
+ }
+ for (ByteBufferHolder buffer : bufferedWrites) {
+ buffer.flip();
+ arrayList.add(buffer.getBuf());
+ }
+ bufferedWrites.clear();
+ ByteBuffer[] array = arrayList.toArray(new ByteBuffer[arrayList.size()]);
+ getSocket().write(array, 0, array.length,
+ getNio2WriteTimeout(), TimeUnit.MILLISECONDS,
+ array, gatheringWriteCompletionHandler);
+ nestedWriteCompletionCount.get().decrementAndGet();
+ } else if (attachment.hasRemaining()) {
+ // Regular write
+ nestedWriteCompletionCount.get().incrementAndGet();
+ getSocket().write(attachment, getNio2WriteTimeout(),
+ TimeUnit.MILLISECONDS, attachment, writeCompletionHandler);
+ nestedWriteCompletionCount.get().decrementAndGet();
+ } else {
+ // All data has been written
+ if (writeInterest) {
+ writeInterest = false;
+ writeNotify = true;
+ }
+ writePending.release();
+ }
+ }
+ if (writeNotify && nestedWriteCompletionCount.get().get() == 0) {
+ endpoint.processSocket(Nio2SocketWrapper.this, SocketEvent.OPEN_WRITE, Nio2Endpoint.isInline());
+ }
+ }
+
+ @Override
+ public void failed(Throwable exc, ByteBuffer attachment) {
+ IOException ioe;
+ if (exc instanceof IOException) {
+ ioe = (IOException) exc;
+ } else {
+ ioe = new IOException(exc);
+ }
+ setError(ioe);
+ writePending.release();
+ endpoint.processSocket(Nio2SocketWrapper.this, SocketEvent.ERROR, true);
+ }
+ };
+
+ gatheringWriteCompletionHandler = new CompletionHandler<Long, ByteBuffer[]>() {
+ @Override
+ public void completed(Long nBytes, ByteBuffer[] attachment) {
+ writeNotify = false;
+ synchronized (writeCompletionHandler) {
+ if (nBytes.longValue() < 0) {
+ failed(new EOFException(sm.getString("iob.failedwrite")), attachment);
+ } else if (bufferedWrites.size() > 0 || arrayHasData(attachment)) {
+ // Continue writing data
+ nestedWriteCompletionCount.get().incrementAndGet();
+ ArrayList<ByteBuffer> arrayList = new ArrayList<>();
+ for (ByteBuffer buffer : attachment) {
+ if (buffer.hasRemaining()) {
+ arrayList.add(buffer);
+ }
+ }
+ for (ByteBufferHolder buffer : bufferedWrites) {
+ buffer.flip();
+ arrayList.add(buffer.getBuf());
+ }
+ bufferedWrites.clear();
+ ByteBuffer[] array = arrayList.toArray(new ByteBuffer[arrayList.size()]);
+ getSocket().write(array, 0, array.length,
+ getNio2WriteTimeout(), TimeUnit.MILLISECONDS,
+ array, gatheringWriteCompletionHandler);
+ nestedWriteCompletionCount.get().decrementAndGet();
+ } else {
+ // All data has been written
+ if (writeInterest) {
+ writeInterest = false;
+ writeNotify = true;
+ }
+ writePending.release();
+ }
+ }
+ if (writeNotify && nestedWriteCompletionCount.get().get() == 0) {
+ endpoint.processSocket(Nio2SocketWrapper.this, SocketEvent.OPEN_WRITE, Nio2Endpoint.isInline());
+ }
+ }
+
+ @Override
+ public void failed(Throwable exc, ByteBuffer[] attachment) {
+ IOException ioe;
+ if (exc instanceof IOException) {
+ ioe = (IOException) exc;
+ } else {
+ ioe = new IOException(exc);
+ }
+ setError(ioe);
+ writePending.release();
+ endpoint.processSocket(Nio2SocketWrapper.this, SocketEvent.ERROR, true);
+ }
+ };
+
}
- public boolean isUpgradeInit() {
- boolean value = upgradeInit;
- upgradeInit = false;
- return value;
+ private static boolean arrayHasData(ByteBuffer[] byteBuffers) {
+ for (ByteBuffer byteBuffer : byteBuffers) {
+ if (byteBuffer.hasRemaining()) {
+ return true;
+ }
+ }
+ return false;
}
+
public void setSendfileData(SendfileData sf) { this.sendfileData = sf; }
public SendfileData getSendfileData() { return this.sendfileData; }
- }
+ @Override
+ public boolean isReadyForRead() throws IOException {
+ synchronized (readCompletionHandler) {
+ if (!readPending.tryAcquire()) {
+ readInterest = true;
+ return false;
+ }
+
+ if (!socketBufferHandler.isReadBufferEmpty()) {
+ readPending.release();
+ return true;
+ }
+
+ int nRead = fillReadBuffer(false);
- // ------------------------------------------------ Application Buffer Handler
- public static class NioBufferHandler implements ApplicationBufferHandler {
- private ByteBuffer readbuf = null;
- private ByteBuffer writebuf = null;
+ boolean isReady = nRead > 0;
- public NioBufferHandler(int readsize, int writesize, boolean direct) {
- if ( direct ) {
- readbuf = ByteBuffer.allocateDirect(readsize);
- writebuf = ByteBuffer.allocateDirect(writesize);
- }else {
- readbuf = ByteBuffer.allocate(readsize);
- writebuf = ByteBuffer.allocate(writesize);
+ if (!isReady) {
+ readInterest = true;
+ }
+ return isReady;
}
}
+
@Override
- public ByteBuffer getReadBuffer() {return readbuf;}
- @Override
- public ByteBuffer getWriteBuffer() {return writebuf;}
+ public int read(boolean block, byte[] b, int off, int len) throws IOException {
+ checkError();
- }
+ if (log.isDebugEnabled()) {
+ log.debug("Socket: [" + this + "], block: [" + block + "], length: [" + len + "]");
+ }
- // ------------------------------------------------ Handler Inner Interface
+ if (socketBufferHandler == null) {
+ throw new IOException(sm.getString("socket.closed"));
+ }
+ if (block) {
+ try {
+ readPending.acquire();
+ } catch (InterruptedException e) {
+ throw new IOException(e);
+ }
+ } else {
+ if (!readPending.tryAcquire()) {
+ if (log.isDebugEnabled()) {
+ log.debug("Socket: [" + this + "], Read in progress. Returning [0]");
+ }
+ return 0;
+ }
+ }
- /**
- * Bare bones interface used for socket processing. Per thread data is to be
- * stored in the ThreadWithAttributes extra folders, or alternately in
- * thread local fields.
- */
- public interface Handler extends AbstractEndpoint.Handler {
- public SocketState process(SocketWrapper<Nio2Channel> socket,
- SocketStatus status);
- public void release(SocketWrapper<Nio2Channel> socket);
- public void closeAll();
- public SSLImplementation getSslImplementation();
- }
+ int nRead = populateReadBuffer(b, off, len);
+ if (nRead > 0) {
+ // 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.
+ readPending.release();
+ return nRead;
+ }
- /**
- * The completion handler used for asynchronous read operations
- */
- private CompletionHandler<Integer, SocketWrapper<Nio2Channel>> awaitBytes
- = new CompletionHandler<Integer, SocketWrapper<Nio2Channel>>() {
+ synchronized (readCompletionHandler) {
+ // Fill the read buffer as best we can.
+ nRead = fillReadBuffer(block);
+
+ // Fill as much of the remaining byte array as possible with the
+ // data that was just read
+ if (nRead > 0) {
+ socketBufferHandler.configureReadBufferForRead();
+ nRead = Math.min(nRead, len);
+ socketBufferHandler.getReadBuffer().get(b, off, nRead);
+ } else if (nRead == 0 && !block) {
+ readInterest = true;
+ }
- @Override
- public synchronized void completed(Integer nBytes, SocketWrapper<Nio2Channel> attachment) {
- if (nBytes.intValue() < 0) {
- failed(new ClosedChannelException(), attachment);
- return;
+ if (log.isDebugEnabled()) {
+ log.debug("Socket: [" + this + "], Read: [" + nRead + "]");
+ }
+ return nRead;
}
- processSocket0(attachment, SocketStatus.OPEN_READ, true);
}
+
@Override
- public void failed(Throwable exc, SocketWrapper<Nio2Channel> attachment) {
- processSocket0(attachment, SocketStatus.DISCONNECT, true);
- }
- };
+ public int read(boolean block, ByteBuffer to) throws IOException {
+ checkError();
- public void addTimeout(SocketWrapper<Nio2Channel> socket) {
- waitingRequests.add(socket);
- }
+ if (socketBufferHandler == null) {
+ throw new IOException(sm.getString("socket.closed"));
+ }
- public boolean removeTimeout(SocketWrapper<Nio2Channel> socket) {
- return waitingRequests.remove(socket);
- }
+ if (block) {
+ try {
+ readPending.acquire();
+ } catch (InterruptedException e) {
+ throw new IOException(e);
+ }
+ } else {
+ if (!readPending.tryAcquire()) {
+ if (log.isDebugEnabled()) {
+ log.debug("Socket: [" + this + "], Read in progress. Returning [0]");
+ }
+ return 0;
+ }
+ }
- public static void startInline() {
- inlineCompletion.set(Boolean.TRUE);
- }
+ int nRead = populateReadBuffer(to);
+ if (nRead > 0) {
+ // 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.
+ readPending.release();
+ return nRead;
+ }
- public static void endInline() {
- inlineCompletion.set(Boolean.FALSE);
- }
+ synchronized (readCompletionHandler) {
+ // The socket read buffer capacity is socket.appReadBufSize
+ int limit = socketBufferHandler.getReadBuffer().capacity();
+ if (block && to.remaining() >= limit) {
+ to.limit(to.position() + limit);
+ nRead = fillReadBuffer(block, to);
+ } else {
+ // Fill the read buffer as best we can.
+ nRead = fillReadBuffer(block);
- public static boolean isInline() {
- Boolean flag = inlineCompletion.get();
- if (flag == null) {
- return false;
- } else {
- return flag.booleanValue();
+ // Fill as much of the remaining byte array as possible with the
+ // data that was just read
+ if (nRead > 0) {
+ nRead = populateReadBuffer(to);
+ } else if (nRead == 0 && !block) {
+ readInterest = true;
+ }
+ }
+
+ return nRead;
+ }
}
- }
- public void awaitBytes(SocketWrapper<Nio2Channel> socket) {
- if (socket == null || socket.getSocket() == null) {
- return;
+
+ @Override
+ public void close() throws IOException {
+ getSocket().close();
}
- ByteBuffer byteBuffer = socket.getSocket().getBufHandler().getReadBuffer();
- byteBuffer.clear();
- socket.getSocket().read(byteBuffer, socket.getTimeout(),
- TimeUnit.MILLISECONDS, socket, awaitBytes);
- }
- private CompletionHandler<Integer, SendfileData> sendfile = new CompletionHandler<Integer, SendfileData>() {
@Override
- public void completed(Integer nWrite, SendfileData attachment) {
- if (nWrite.intValue() < 0) { // Reach the end of stream
- failed(new EOFException(), attachment);
- return;
- }
- attachment.pos += nWrite.intValue();
- if (!attachment.buffer.hasRemaining()) {
- if (attachment.length <= 0) {
- // All data has now been written
- attachment.socket.setSendfileData(null);
- attachment.buffer.clear();
- try {
- attachment.fchannel.close();
- } catch (IOException e) {
- // Ignore
+ public boolean isClosed() {
+ return !getSocket().isOpen();
+ }
+
+
+ @Override
+ public boolean hasAsyncIO() {
+ return false;
+ }
+
+ /**
+ * Internal state tracker for scatter/gather operations.
+ */
+ private static class OperationState<A> {
+ private final ByteBuffer[] buffers;
+ private final int offset;
+ private final int length;
+ private final A attachment;
+ private final long timeout;
+ private final TimeUnit unit;
+ private final CompletionCheck check;
+ private final CompletionHandler<Long, ? super A> handler;
+ private OperationState(ByteBuffer[] buffers, int offset, int length,
+ long timeout, TimeUnit unit, A attachment, CompletionCheck check,
+ CompletionHandler<Long, ? super A> handler) {
+ this.buffers = buffers;
+ this.offset = offset;
+ this.length = length;
+ this.timeout = timeout;
+ this.unit = unit;
+ this.attachment = attachment;
+ this.check = check;
+ this.handler = handler;
+ }
+ private volatile long nBytes = 0;
+ private volatile CompletionState state = CompletionState.PENDING;
+ }
+
+ private class ScatterReadCompletionHandler<A> implements CompletionHandler<Long, OperationState<A>> {
+ @Override
+ public void completed(Long nBytes, OperationState<A> state) {
+ if (nBytes.intValue() < 0) {
+ failed(new EOFException(), state);
+ } else {
+ state.nBytes += nBytes.longValue();
+ CompletionState currentState = Nio2Endpoint.isInline() ? CompletionState.INLINE : CompletionState.DONE;
+ boolean complete = true;
+ boolean completion = true;
+ if (state.check != null) {
+ switch (state.check.callHandler(currentState, state.buffers, state.offset, state.length)) {
+ case CONTINUE:
+ complete = false;
+ break;
+ case DONE:
+ break;
+ case NONE:
+ completion = false;
+ break;
+ }
}
- if (attachment.keepAlive) {
- if (!isInline()) {
- awaitBytes(attachment.socket);
- } else {
- attachment.doneInline = true;
+ if (complete) {
+ readPending.release();
+ state.state = currentState;
+ if (completion && state.handler != null) {
+ state.handler.completed(Long.valueOf(state.nBytes), state.attachment);
}
} else {
- if (!isInline()) {
- processSocket(attachment.socket, SocketStatus.DISCONNECT, false);
- } else {
- attachment.doneInline = true;
- }
+ getSocket().read(state.buffers, state.offset, state.length,
+ state.timeout, state.unit, state, this);
}
+ }
+ }
+ @Override
+ public void failed(Throwable exc, OperationState<A> state) {
+ IOException ioe;
+ if (exc instanceof IOException) {
+ ioe = (IOException) exc;
+ } else {
+ ioe = new IOException(exc);
+ }
+ setError(ioe);
+ readPending.release();
+ if (exc instanceof AsynchronousCloseException) {
+ // If already closed, don't call onError and close again
return;
+ }
+ state.state = Nio2Endpoint.isInline() ? CompletionState.ERROR : CompletionState.DONE;
+ if (state.handler != null) {
+ state.handler.failed(ioe, state.attachment);
+ }
+ }
+ }
+
+ private class GatherWriteCompletionHandler<A> implements CompletionHandler<Long, OperationState<A>> {
+ @Override
+ public void completed(Long nBytes, OperationState<A> state) {
+ if (nBytes.longValue() < 0) {
+ failed(new EOFException(), state);
} else {
- attachment.buffer.clear();
- int nRead = -1;
- try {
- nRead = attachment.fchannel.read(attachment.buffer);
- } catch (IOException e) {
- failed(e, attachment);
- return;
+ state.nBytes += nBytes.longValue();
+ CompletionState currentState = Nio2Endpoint.isInline() ? CompletionState.INLINE : CompletionState.DONE;
+ boolean complete = true;
+ boolean completion = true;
+ if (state.check != null) {
+ switch (state.check.callHandler(currentState, state.buffers, state.offset, state.length)) {
+ case CONTINUE:
+ complete = false;
+ break;
+ case DONE:
+ break;
+ case NONE:
+ completion = false;
+ break;
+ }
}
- if (nRead > 0) {
- attachment.buffer.flip();
- if (attachment.length < attachment.buffer.remaining()) {
- attachment.buffer.limit(attachment.buffer.limit() - attachment.buffer.remaining() + (int) attachment.length);
+ if (complete) {
+ writePending.release();
+ state.state = currentState;
+ if (completion && state.handler != null) {
+ state.handler.completed(Long.valueOf(state.nBytes), state.attachment);
}
- attachment.length -= nRead;
} else {
- failed(new EOFException(), attachment);
- return;
+ getSocket().write(state.buffers, state.offset, state.length,
+ state.timeout, state.unit, state, this);
}
}
}
- attachment.socket.getSocket().write(attachment.buffer, attachment.socket.getTimeout(),
- TimeUnit.MILLISECONDS, attachment, this);
+ @Override
+ public void failed(Throwable exc, OperationState<A> state) {
+ IOException ioe;
+ if (exc instanceof IOException) {
+ ioe = (IOException) exc;
+ } else {
+ ioe = new IOException(exc);
+ }
+ setError(ioe);
+ writePending.release();
+ state.state = Nio2Endpoint.isInline() ? CompletionState.ERROR : CompletionState.DONE;
+ if (state.handler != null) {
+ state.handler.failed(ioe, state.attachment);
+ }
+ }
}
@Override
- public void failed(Throwable exc, SendfileData attachment) {
+ public <A> CompletionState read(ByteBuffer[] dsts, int offset, int length,
+ boolean block, long timeout, TimeUnit unit, A attachment,
+ CompletionCheck check, CompletionHandler<Long, ? super A> handler) {
+ OperationState<A> state = new OperationState<>(dsts, offset, length, timeout, unit, attachment, check, handler);
try {
- attachment.fchannel.close();
- } catch (IOException e) {
- // Ignore
+ if ((!block && readPending.tryAcquire()) || (block && readPending.tryAcquire(timeout, unit))) {
+ Nio2Endpoint.startInline();
+ getSocket().read(dsts, offset, length, timeout, unit, state, new ScatterReadCompletionHandler<A>());
+ Nio2Endpoint.endInline();
+ } else {
+ throw new ReadPendingException();
+ }
+ if (block && state.state == CompletionState.PENDING && readPending.tryAcquire(timeout, unit)) {
+ readPending.release();
+ }
+ } catch (InterruptedException e) {
+ handler.failed(e, attachment);
}
- if (!isInline()) {
- processSocket(attachment.socket, SocketStatus.ERROR, false);
+ return state.state;
+ }
+
+ @Override
+ public boolean isWritePending() {
+ synchronized (writeCompletionHandler) {
+ return writePending.availablePermits() == 0;
+ }
+ }
+
+ @Override
+ public <A> CompletionState write(ByteBuffer[] srcs, int offset, int length,
+ boolean block, long timeout, TimeUnit unit, A attachment,
+ CompletionCheck check, CompletionHandler<Long, ? super A> handler) {
+ OperationState<A> state = new OperationState<>(srcs, offset, length, timeout, unit, attachment, check, handler);
+ try {
+ if ((!block && writePending.tryAcquire()) || (block && writePending.tryAcquire(timeout, unit))) {
+ Nio2Endpoint.startInline();
+ getSocket().write(srcs, offset, length, timeout, unit, state, new GatherWriteCompletionHandler<A>());
+ Nio2Endpoint.endInline();
+ } else {
+ throw new WritePendingException();
+ }
+ if (block && state.state == CompletionState.PENDING && writePending.tryAcquire(timeout, unit)) {
+ writePending.release();
+ }
+ } catch (InterruptedException e) {
+ handler.failed(e, attachment);
+ }
+ return state.state;
+ }
+
+ /* 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.
+ */
+ private int fillReadBuffer(boolean block) throws IOException {
+ socketBufferHandler.configureReadBufferForWrite();
+ return fillReadBuffer(block, socketBufferHandler.getReadBuffer());
+ }
+
+ private int fillReadBuffer(boolean block, ByteBuffer to) throws IOException {
+ int nRead = 0;
+ Future<Integer> integer = null;
+ if (block) {
+ try {
+ integer = getSocket().read(to);
+ nRead = integer.get(getNio2ReadTimeout(), TimeUnit.MILLISECONDS).intValue();
+ } catch (ExecutionException e) {
+ if (e.getCause() instanceof IOException) {
+ throw (IOException) e.getCause();
+ } else {
+ throw new IOException(e);
+ }
+ } catch (InterruptedException e) {
+ throw new IOException(e);
+ } catch (TimeoutException e) {
+ integer.cancel(true);
+ throw new SocketTimeoutException();
+ } finally {
+ // Blocking read so need to release here since there will
+ // not be a callback to a completion handler.
+ readPending.release();
+ }
} else {
- attachment.doneInline = true;
- attachment.error = true;
+ Nio2Endpoint.startInline();
+ getSocket().read(to, getNio2ReadTimeout(), TimeUnit.MILLISECONDS, this,
+ readCompletionHandler);
+ Nio2Endpoint.endInline();
+ if (readPending.availablePermits() == 1) {
+ nRead = to.position();
+ }
}
+ return nRead;
}
- };
- public SendfileState processSendfile(Nio2SocketWrapper socket) {
- // Configure the send file data
- SendfileData data = socket.getSendfileData();
- if (data.fchannel == null || !data.fchannel.isOpen()) {
- java.nio.file.Path path = new File(data.fileName).toPath();
+ /**
+ * {@inheritDoc}
+ * <p>
+ * 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
+ // done rather than continue buffering
+ // Also allows doing autoblocking
+ // Could be "smart" with coordination with the main CoyoteOutputStream to
+ // indicate the end of a write
+ // Uses: if (writePending.tryAcquire(socketWrapper.getTimeout(), TimeUnit.MILLISECONDS))
+ synchronized (writeCompletionHandler) {
+ if (writePending.tryAcquire()) {
+ // No pending completion handler, so writing to the main buffer
+ // is possible
+ socketBufferHandler.configureWriteBufferForWrite();
+ int thisTime = transfer(buf, off, len, socketBufferHandler.getWriteBuffer());
+ len = len - thisTime;
+ off = off + thisTime;
+ if (len > 0) {
+ // Remaining data must be buffered
+ addToBuffers(buf, off, len);
+ }
+ flushNonBlocking(true);
+ } else {
+ addToBuffers(buf, off, len);
+ }
+ }
+ }
+
+
+ /**
+ * {@inheritDoc}
+ * <p>
+ * 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 {
+ // 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
+ // done rather than continue buffering
+ // Also allows doing autoblocking
+ // Could be "smart" with coordination with the main CoyoteOutputStream to
+ // indicate the end of a write
+ // Uses: if (writePending.tryAcquire(socketWrapper.getTimeout(), TimeUnit.MILLISECONDS))
+ synchronized (writeCompletionHandler) {
+ if (writePending.tryAcquire()) {
+ // No pending completion handler, so writing to the main buffer
+ // is possible
+ socketBufferHandler.configureWriteBufferForWrite();
+ transfer(from, socketBufferHandler.getWriteBuffer());
+ if (from.remaining() > 0) {
+ // Remaining data must be buffered
+ addToBuffers(from);
+ }
+ flushNonBlocking(true);
+ } else {
+ addToBuffers(from);
+ }
+ }
+ }
+
+
+ /**
+ * @param block Ignored since this method is only called in the
+ * blocking case
+ */
+ @Override
+ protected void doWrite(boolean block, ByteBuffer from) throws IOException {
+ Future<Integer> integer = null;
try {
- data.fchannel = java.nio.channels.FileChannel
- .open(path, StandardOpenOption.READ).position(data.pos);
- } catch (IOException e) {
- return SendfileState.ERROR;
+ do {
+ integer = getSocket().write(from);
+ if (integer.get(getNio2WriteTimeout(), TimeUnit.MILLISECONDS).intValue() < 0) {
+ throw new EOFException(sm.getString("iob.failedwrite"));
+ }
+ } while (from.hasRemaining());
+ } catch (ExecutionException e) {
+ if (e.getCause() instanceof IOException) {
+ throw (IOException) e.getCause();
+ } else {
+ throw new IOException(e);
+ }
+ } catch (InterruptedException e) {
+ throw new IOException(e);
+ } catch (TimeoutException e) {
+ integer.cancel(true);
+ throw new SocketTimeoutException();
}
}
- ByteBuffer buffer = socket.getSocket().getBufHandler().getWriteBuffer();
- buffer.clear();
- int nRead = -1;
- try {
- nRead = data.fchannel.read(buffer);
- } catch (IOException e1) {
- return SendfileState.ERROR;
+
+
+ @Override
+ protected void flushBlocking() throws IOException {
+ checkError();
+
+ // Before doing a blocking flush, make sure that any pending non
+ // blocking write has completed.
+ try {
+ if (writePending.tryAcquire(getNio2WriteTimeout(), TimeUnit.MILLISECONDS)) {
+ writePending.release();
+ } else {
+ throw new SocketTimeoutException();
+ }
+ } catch (InterruptedException e) {
+ // Ignore
+ }
+
+ super.flushBlocking();
+ }
+
+ @Override
+ protected boolean flushNonBlocking() throws IOException {
+ return flushNonBlocking(false);
}
- if (nRead >= 0) {
- buffer.flip();
- data.socket = socket;
- data.buffer = buffer;
- data.length -= nRead;
- startInline();
+ private boolean flushNonBlocking(boolean hasPermit) throws IOException {
+ checkError();
+ synchronized (writeCompletionHandler) {
+ if (hasPermit || writePending.tryAcquire()) {
+ socketBufferHandler.configureWriteBufferForRead();
+ if (bufferedWrites.size() > 0) {
+ // Gathering write of the main buffer plus all leftovers
+ ArrayList<ByteBuffer> arrayList = new ArrayList<>();
+ if (socketBufferHandler.getWriteBuffer().hasRemaining()) {
+ arrayList.add(socketBufferHandler.getWriteBuffer());
+ }
+ for (ByteBufferHolder buffer : bufferedWrites) {
+ buffer.flip();
+ arrayList.add(buffer.getBuf());
+ }
+ bufferedWrites.clear();
+ ByteBuffer[] array = arrayList.toArray(new ByteBuffer[arrayList.size()]);
+ Nio2Endpoint.startInline();
+ getSocket().write(array, 0, array.length, getNio2WriteTimeout(),
+ TimeUnit.MILLISECONDS, array, gatheringWriteCompletionHandler);
+ Nio2Endpoint.endInline();
+ } else if (socketBufferHandler.getWriteBuffer().hasRemaining()) {
+ // Regular write
+ Nio2Endpoint.startInline();
+ getSocket().write(socketBufferHandler.getWriteBuffer(), getNio2WriteTimeout(),
+ TimeUnit.MILLISECONDS, socketBufferHandler.getWriteBuffer(),
+ writeCompletionHandler);
+ Nio2Endpoint.endInline();
+ } else {
+ // Nothing was written
+ if (!hasPermit) {
+ writePending.release();
+ }
+ }
+ }
+ return hasDataToWrite();
+ }
+ }
+
+
+ @Override
+ public boolean hasDataToWrite() {
+ synchronized (writeCompletionHandler) {
+ return !socketBufferHandler.isWriteBufferEmpty() ||
+ bufferedWrites.size() > 0 || getError() != null;
+ }
+ }
+
+
+ @Override
+ public boolean isReadPending() {
+ synchronized (readCompletionHandler) {
+ return readPending.availablePermits() == 0;
+ }
+ }
+
+
+ @Override
+ public boolean awaitReadComplete(long timeout, TimeUnit unit) {
try {
- socket.getSocket().write(buffer, socket.getTimeout(), TimeUnit.MILLISECONDS,
- data, sendfile);
- } finally {
- endInline();
+ if (readPending.tryAcquire(timeout, unit)) {
+ readPending.release();
+ }
+ } catch (InterruptedException e) {
+ return false;
}
- if (data.doneInline) {
- if (data.error) {
+ return true;
+ }
+
+
+ @Override
+ public boolean awaitWriteComplete(long timeout, TimeUnit unit) {
+ try {
+ if (writePending.tryAcquire(timeout, unit)) {
+ writePending.release();
+ }
+ } catch (InterruptedException e) {
+ return false;
+ }
+ return true;
+ }
+
+ /*
+ * This should only be called from a thread that currently holds a lock
+ * on the socket. This prevents a race condition between a pending read
+ * being completed and processed and a thread triggering a new read.
+ */
+ void releaseReadPending() {
+ synchronized (readCompletionHandler) {
+ if (readPending.availablePermits() == 0) {
+ readPending.release();
+ }
+ }
+ }
+
+
+ @Override
+ public void registerReadInterest() {
+ synchronized (readCompletionHandler) {
+ if (readPending.availablePermits() == 0) {
+ readInterest = true;
+ } else {
+ // If no read is pending, start waiting for data
+ awaitBytes();
+ }
+ }
+ }
+
+
+ @Override
+ public void registerWriteInterest() {
+ synchronized (writeCompletionHandler) {
+ if (writePending.availablePermits() == 0) {
+ writeInterest = true;
+ } else {
+ // If no write is pending, notify
+ getEndpoint().processSocket(this, SocketEvent.OPEN_WRITE, true);
+ }
+ }
+ }
+
+
+ public void awaitBytes() {
+ // NO-OP is there is already a read in progress.
+ if (readPending.tryAcquire()) {
+ getSocket().getBufHandler().configureReadBufferForWrite();
+ Nio2Endpoint.startInline();
+ getSocket().read(getSocket().getBufHandler().getReadBuffer(),
+ getNio2ReadTimeout(), TimeUnit.MILLISECONDS, this, awaitBytesHandler);
+ Nio2Endpoint.endInline();
+ }
+ }
+
+
+ @Override
+ public SendfileDataBase createSendfileData(String filename, long pos, long length) {
+ return new SendfileData(filename, pos, length);
+ }
+
+
+ @Override
+ public SendfileState processSendfile(SendfileDataBase sendfileData) {
+ SendfileData data = (SendfileData) sendfileData;
+ setSendfileData(data);
+ // Configure the send file data
+ if (data.fchannel == null || !data.fchannel.isOpen()) {
+ java.nio.file.Path path = new File(sendfileData.fileName).toPath();
+ try {
+ data.fchannel = java.nio.channels.FileChannel
+ .open(path, StandardOpenOption.READ).position(sendfileData.pos);
+ } catch (IOException e) {
return SendfileState.ERROR;
+ }
+ }
+ getSocket().getBufHandler().configureWriteBufferForWrite();
+ ByteBuffer buffer = getSocket().getBufHandler().getWriteBuffer();
+ int nRead = -1;
+ try {
+ nRead = data.fchannel.read(buffer);
+ } catch (IOException e1) {
+ return SendfileState.ERROR;
+ }
+
+ if (nRead >= 0) {
+ data.length -= nRead;
+ getSocket().getBufHandler().configureWriteBufferForRead();
+ Nio2Endpoint.startInline();
+ getSocket().write(buffer, getNio2WriteTimeout(), TimeUnit.MILLISECONDS,
+ data, sendfileHandler);
+ Nio2Endpoint.endInline();
+ if (data.doneInline) {
+ if (data.error) {
+ return SendfileState.ERROR;
+ } else {
+ return SendfileState.DONE;
+ }
} else {
- return SendfileState.DONE;
+ return SendfileState.PENDING;
}
} else {
- return SendfileState.PENDING;
+ return SendfileState.ERROR;
}
- } else {
- return SendfileState.ERROR;
}
- }
- // ---------------------------------------------- SocketProcessor Inner Class
- /**
- * This class is the equivalent of the Worker, but will simply use in an
- * external Executor thread pool.
- */
- protected class SocketProcessor implements Runnable {
- private SocketWrapper<Nio2Channel> socket = null;
- private SocketStatus status = null;
+ private long getNio2ReadTimeout() {
+ long readTimeout = getReadTimeout();
+ if (readTimeout > 0) {
+ return readTimeout;
+ }
+ // NIO2 can't do infinite timeout so use Long.MAX_VALUE
+ return Long.MAX_VALUE;
+ }
+
- public SocketProcessor(SocketWrapper<Nio2Channel> socket, SocketStatus status) {
- reset(socket,status);
+ private long getNio2WriteTimeout() {
+ long writeTimeout = getWriteTimeout();
+ if (writeTimeout > 0) {
+ return writeTimeout;
+ }
+ // NIO2 can't do infinite timeout so use Long.MAX_VALUE
+ return Long.MAX_VALUE;
}
- public void reset(SocketWrapper<Nio2Channel> socket, SocketStatus status) {
- this.socket = socket;
- this.status = status;
+
+ @Override
+ protected void populateRemoteAddr() {
+ SocketAddress socketAddress = null;
+ try {
+ socketAddress = getSocket().getIOChannel().getRemoteAddress();
+ } catch (IOException e) {
+ // Ignore
+ }
+ if (socketAddress instanceof InetSocketAddress) {
+ remoteAddr = ((InetSocketAddress) socketAddress).getAddress().getHostAddress();
+ }
}
+
@Override
- public void run() {
- // Upgraded connections need to allow multiple threads to access the
- // connection at the same time to enable blocking IO to be used when
- // NIO has been configured
- if (socket.isUpgraded() &&
- SocketStatus.OPEN_WRITE == status) {
- synchronized (socket.getWriteThreadLock()) {
- doRun();
+ protected void populateRemoteHost() {
+ SocketAddress socketAddress = null;
+ try {
+ socketAddress = getSocket().getIOChannel().getRemoteAddress();
+ } catch (IOException e) {
+ log.warn(sm.getString("endpoint.warn.noRemoteHost", getSocket()), e);
+ }
+ if (socketAddress instanceof InetSocketAddress) {
+ remoteHost = ((InetSocketAddress) socketAddress).getAddress().getHostName();
+ if (remoteAddr == null) {
+ remoteAddr = ((InetSocketAddress) socketAddress).getAddress().getHostAddress();
}
+ }
+ }
+
+
+ @Override
+ protected void populateRemotePort() {
+ SocketAddress socketAddress = null;
+ try {
+ socketAddress = getSocket().getIOChannel().getRemoteAddress();
+ } catch (IOException e) {
+ log.warn(sm.getString("endpoint.warn.noRemotePort", getSocket()), e);
+ }
+ if (socketAddress instanceof InetSocketAddress) {
+ remotePort = ((InetSocketAddress) socketAddress).getPort();
+ }
+ }
+
+
+ @Override
+ protected void populateLocalName() {
+ SocketAddress socketAddress = null;
+ try {
+ socketAddress = getSocket().getIOChannel().getLocalAddress();
+ } catch (IOException e) {
+ log.warn(sm.getString("endpoint.warn.noLocalName", getSocket()), e);
+ }
+ if (socketAddress instanceof InetSocketAddress) {
+ localName = ((InetSocketAddress) socketAddress).getHostName();
+ }
+ }
+
+
+ @Override
+ protected void populateLocalAddr() {
+ SocketAddress socketAddress = null;
+ try {
+ socketAddress = getSocket().getIOChannel().getLocalAddress();
+ } catch (IOException e) {
+ log.warn(sm.getString("endpoint.warn.noLocalAddr", getSocket()), e);
+ }
+ if (socketAddress instanceof InetSocketAddress) {
+ localAddr = ((InetSocketAddress) socketAddress).getAddress().getHostAddress();
+ }
+ }
+
+
+ @Override
+ protected void populateLocalPort() {
+ SocketAddress socketAddress = null;
+ try {
+ socketAddress = getSocket().getIOChannel().getLocalAddress();
+ } catch (IOException e) {
+ log.warn(sm.getString("endpoint.warn.noLocalPort", getSocket()), e);
+ }
+ if (socketAddress instanceof InetSocketAddress) {
+ localPort = ((InetSocketAddress) socketAddress).getPort();
+ }
+ }
+
+
+ /**
+ * {@inheritDoc}
+ * @param clientCertProvider Ignored for this implementation
+ */
+ @Override
+ public SSLSupport getSslSupport(String clientCertProvider) {
+ if (getSocket() instanceof SecureNio2Channel) {
+ SecureNio2Channel ch = (SecureNio2Channel) getSocket();
+ SSLSession session = ch.getSslEngine().getSession();
+ return ((Nio2Endpoint) getEndpoint()).getSslImplementation().getSSLSupport(session);
} else {
- synchronized (socket) {
- doRun();
+ return null;
+ }
+ }
+
+
+ @Override
+ public void doClientAuth(SSLSupport sslSupport) {
+ SecureNio2Channel sslChannel = (SecureNio2Channel) getSocket();
+ SSLEngine engine = sslChannel.getSslEngine();
+ if (!engine.getNeedClientAuth()) {
+ // Need to re-negotiate SSL connection
+ engine.setNeedClientAuth(true);
+ try {
+ sslChannel.rehandshake();
+ ((JSSESupport) sslSupport).setSession(engine.getSession());
+ } catch (IOException ioe) {
+ log.warn(sm.getString("socket.sslreneg"), ioe);
}
}
}
- private void doRun() {
+
+ @Override
+ public void setAppReadBufHandler(ApplicationBufferHandler handler) {
+ getSocket().setAppReadBufHandler(handler);
+ }
+ }
+
+
+ public static void startInline() {
+ inlineCompletion.set(Boolean.TRUE);
+ }
+
+ public static void endInline() {
+ inlineCompletion.set(Boolean.FALSE);
+ }
+
+ public static boolean isInline() {
+ Boolean flag = inlineCompletion.get();
+ if (flag == null) {
+ return false;
+ } else {
+ return flag.booleanValue();
+ }
+ }
+
+
+ // ---------------------------------------------- SocketProcessor Inner Class
+ /**
+ * This class is the equivalent of the Worker, but will simply use in an
+ * external Executor thread pool.
+ */
+ protected class SocketProcessor extends SocketProcessorBase<Nio2Channel> {
+
+ public SocketProcessor(SocketWrapperBase<Nio2Channel> socketWrapper, SocketEvent event) {
+ super(socketWrapper, event);
+ }
+
+ @Override
+ protected void doRun() {
+ if (SocketEvent.OPEN_WRITE != event) {
+ // Anything other than OPEN_WRITE is a genuine read or an
+ // error condition so for all of those release the semaphore
+ ((Nio2SocketWrapper) socketWrapper).releaseReadPending();
+ }
boolean launch = false;
try {
int handshake = -1;
try {
- if (socket.getSocket() != null) {
- // For STOP there is no point trying to handshake as the
- // Poller has been stopped.
- if (socket.getSocket().isHandshakeComplete() ||
- status == SocketStatus.STOP) {
- handshake = 0;
- } else {
- handshake = socket.getSocket().handshake();
- // The handshake process reads/writes from/to the
- // socket. status may therefore be OPEN_WRITE once
- // the handshake completes. However, the handshake
- // happens when the socket is opened so the status
- // must always be OPEN_READ after it completes. It
- // is OK to always set this as it is only used if
- // the handshake completes.
- status = SocketStatus.OPEN_READ;
- }
+ if (socketWrapper.getSocket().isHandshakeComplete()) {
+ // No TLS handshaking required. Let the handler
+ // process this socket / event combination.
+ handshake = 0;
+ } else if (event == SocketEvent.STOP || event == SocketEvent.DISCONNECT ||
+ event == SocketEvent.ERROR) {
+ // Unable to complete the TLS handshake. Treat it as
+ // if the handshake failed.
+ handshake = -1;
+ } else {
+ handshake = socketWrapper.getSocket().handshake();
+ // The handshake process reads/writes from/to the
+ // socket. status may therefore be OPEN_WRITE once
+ // the handshake completes. However, the handshake
+ // happens when the socket is opened so the status
+ // must always be OPEN_READ after it completes. It
+ // is OK to always set this as it is only used if
+ // the handshake completes.
+ event = SocketEvent.OPEN_READ;
}
} catch (IOException x) {
handshake = -1;
@@ -1086,54 +1704,41 @@ public class Nio2Endpoint extends AbstractEndpoint<Nio2Channel> {
if (handshake == 0) {
SocketState state = SocketState.OPEN;
// Process the request from this socket
- if (status == null) {
- state = handler.process(socket, SocketStatus.OPEN_READ);
+ if (event == null) {
+ state = getHandler().process(socketWrapper, SocketEvent.OPEN_READ);
} else {
- state = handler.process(socket, status);
+ state = getHandler().process(socketWrapper, event);
}
if (state == SocketState.CLOSED) {
// Close socket and pool
- socket.setComet(false);
- closeSocket(socket, SocketStatus.ERROR);
- if (useCaches && running && !paused) {
- nioChannels.push(socket.getSocket());
+ closeSocket(socketWrapper);
+ if (running && !paused) {
+ if (!nioChannels.push(socketWrapper.getSocket())) {
+ socketWrapper.getSocket().free();
+ }
}
} else if (state == SocketState.UPGRADING) {
- socket.setKeptAlive(true);
- socket.access();
launch = true;
}
} else if (handshake == -1 ) {
- closeSocket(socket, SocketStatus.DISCONNECT);
- if (useCaches && running && !paused) {
- nioChannels.push(socket.getSocket());
- }
- }
- } catch (OutOfMemoryError oom) {
- try {
- oomParachuteData = null;
- log.error("", oom);
- closeSocket(socket, SocketStatus.ERROR);
- releaseCaches();
- } catch (Throwable oomt) {
- try {
- System.err.println(oomParachuteMsg);
- oomt.printStackTrace();
- } catch (Throwable letsHopeWeDontGetHere){
- ExceptionUtils.handleThrowable(letsHopeWeDontGetHere);
+ closeSocket(socketWrapper);
+ if (running && !paused) {
+ if (!nioChannels.push(socketWrapper.getSocket())) {
+ socketWrapper.getSocket().free();
+ }
}
}
} catch (VirtualMachineError vme) {
ExceptionUtils.handleThrowable(vme);
} catch (Throwable t) {
log.error(sm.getString("endpoint.processing.fail"), t);
- if (socket != null) {
- closeSocket(socket, SocketStatus.ERROR);
+ if (socketWrapper != null) {
+ closeSocket(socketWrapper);
}
} finally {
if (launch) {
try {
- getExecutor().execute(new SocketProcessor(socket, SocketStatus.OPEN_READ));
+ getExecutor().execute(new SocketProcessor(socketWrapper, SocketEvent.OPEN_READ));
} catch (NullPointerException npe) {
if (running) {
log.error(sm.getString("endpoint.launch.fail"),
@@ -1141,10 +1746,10 @@ public class Nio2Endpoint extends AbstractEndpoint<Nio2Channel> {
}
}
}
- socket = null;
- status = null;
+ socketWrapper = null;
+ event = null;
//return to cache
- if (useCaches && running && !paused) {
+ if (running && !paused) {
processorCache.push(this);
}
}
@@ -1155,18 +1760,14 @@ public class Nio2Endpoint extends AbstractEndpoint<Nio2Channel> {
/**
* SendfileData class.
*/
- public static class SendfileData {
- // File
- public String fileName;
- public FileChannel fchannel;
- public long pos;
- public long length;
- // KeepAlive flag
- public boolean keepAlive;
+ public static class SendfileData extends SendfileDataBase {
+ private FileChannel fchannel;
// Internal use only
- private Nio2SocketWrapper socket;
- private ByteBuffer buffer;
private boolean doneInline = false;
private boolean error = false;
+
+ public SendfileData(String filename, long pos, long length) {
+ super(filename, pos, length);
+ }
}
}
diff --git a/java/org/apache/tomcat/util/net/NioBlockingSelector.java b/java/org/apache/tomcat/util/net/NioBlockingSelector.java
index 2f87e4c..068f1fc 100644
--- a/java/org/apache/tomcat/util/net/NioBlockingSelector.java
+++ b/java/org/apache/tomcat/util/net/NioBlockingSelector.java
@@ -35,7 +35,7 @@ import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.ExceptionUtils;
import org.apache.tomcat.util.collections.SynchronizedQueue;
import org.apache.tomcat.util.collections.SynchronizedStack;
-import org.apache.tomcat.util.net.NioEndpoint.KeyAttachment;
+import org.apache.tomcat.util.net.NioEndpoint.NioSocketWrapper;
public class NioBlockingSelector {
@@ -90,7 +90,7 @@ public class NioBlockingSelector {
if (reference == null) {
reference = new KeyReference();
}
- KeyAttachment att = (KeyAttachment) key.attachment();
+ NioSocketWrapper att = (NioSocketWrapper) key.attachment();
int written = 0;
boolean timedout = false;
int keycount = 1; //assume we can write
@@ -162,7 +162,7 @@ public class NioBlockingSelector {
if (reference == null) {
reference = new KeyReference();
}
- KeyAttachment att = (KeyAttachment) key.attachment();
+ NioSocketWrapper att = (NioSocketWrapper) key.attachment();
int read = 0;
boolean timedout = false;
int keycount = 1; //assume we can read
@@ -171,10 +171,9 @@ public class NioBlockingSelector {
while(!timedout) {
if (keycount > 0) { //only read if we were registered for a read
read = socket.read(buf);
- if (read == -1)
- throw new EOFException();
- if (read > 0)
+ if (read != 0) {
break;
+ }
}
try {
if ( att.getReadLatch()==null || att.getReadLatch().getCount()==0) att.startReadLatch(1);
@@ -234,7 +233,7 @@ public class NioBlockingSelector {
if (wakeupCounter.addAndGet(1)==0) selector.wakeup();
}
- public void cancel(SelectionKey sk, KeyAttachment key, int ops){
+ public void cancel(SelectionKey sk, NioSocketWrapper key, int ops){
if (sk!=null) {
sk.cancel();
sk.attach(null);
@@ -243,10 +242,9 @@ public class NioBlockingSelector {
}
}
- public void add(final KeyAttachment key, final int ops, final KeyReference ref) {
+ public void add(final NioSocketWrapper key, final int ops, final KeyReference ref) {
if ( key == null ) return;
NioChannel nch = key.getSocket();
- if ( nch == null ) return;
final SocketChannel ch = nch.getIOChannel();
if ( ch == null ) return;
@@ -274,10 +272,9 @@ public class NioBlockingSelector {
wakeup();
}
- public void remove(final KeyAttachment key, final int ops) {
+ public void remove(final NioSocketWrapper key, final int ops) {
if ( key == null ) return;
NioChannel nch = key.getSocket();
- if ( nch == null ) return;
final SocketChannel ch = nch.getIOChannel();
if ( ch == null ) return;
@@ -364,9 +361,8 @@ public class NioBlockingSelector {
// any active event.
while (run && iterator != null && iterator.hasNext()) {
SelectionKey sk = iterator.next();
- KeyAttachment attachment = (KeyAttachment)sk.attachment();
+ NioSocketWrapper attachment = (NioSocketWrapper)sk.attachment();
try {
- attachment.access();
iterator.remove();
sk.interestOps(sk.interestOps() & (~sk.readyOps()));
if ( sk.isReadable() ) {
diff --git a/java/org/apache/tomcat/util/net/NioChannel.java b/java/org/apache/tomcat/util/net/NioChannel.java
index 7a00cb4..34a70f5 100644
--- a/java/org/apache/tomcat/util/net/NioChannel.java
+++ b/java/org/apache/tomcat/util/net/NioChannel.java
@@ -24,11 +24,9 @@ import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import org.apache.tomcat.util.net.NioEndpoint.Poller;
-import org.apache.tomcat.util.net.SecureNioChannel.ApplicationBufferHandler;
import org.apache.tomcat.util.res.StringManager;
/**
- *
* Base class for a SocketChannel wrapper used by the endpoint.
* This way, logic for a SSL socket channel remains the same as for
* a non SSL, making sure we don't need to code for any exception cases.
@@ -37,18 +35,18 @@ import org.apache.tomcat.util.res.StringManager;
*/
public class NioChannel implements ByteChannel {
- protected static final StringManager sm =
- StringManager.getManager("org.apache.tomcat.util.net.res");
+ protected static final StringManager sm = StringManager.getManager(NioChannel.class);
- protected static ByteBuffer emptyBuf = ByteBuffer.allocate(0);
+ protected static final ByteBuffer emptyBuf = ByteBuffer.allocate(0);
protected SocketChannel sc = null;
+ protected SocketWrapperBase<NioChannel> socketWrapper = null;
- protected ApplicationBufferHandler bufHandler;
+ protected final SocketBufferHandler bufHandler;
protected Poller poller;
- public NioChannel(SocketChannel channel, ApplicationBufferHandler bufHandler) {
+ public NioChannel(SocketChannel channel, SocketBufferHandler bufHandler) {
this.sc = channel;
this.bufHandler = bufHandler;
}
@@ -59,16 +57,19 @@ public class NioChannel implements ByteChannel {
* @throws IOException If a problem was encountered resetting the channel
*/
public void reset() throws IOException {
- bufHandler.getReadBuffer().clear();
- bufHandler.getWriteBuffer().clear();
+ bufHandler.reset();
}
- public int getBufferSize() {
- if ( bufHandler == null ) return 0;
- int size = 0;
- size += bufHandler.getReadBuffer()!=null?bufHandler.getReadBuffer().capacity():0;
- size += bufHandler.getWriteBuffer()!=null?bufHandler.getWriteBuffer().capacity():0;
- return size;
+
+ void setSocketWrapper(SocketWrapperBase<NioChannel> socketWrapper) {
+ this.socketWrapper = socketWrapper;
+ }
+
+ /**
+ * Free the channel memory
+ */
+ public void free() {
+ bufHandler.free();
}
/**
@@ -79,7 +80,8 @@ public class NioChannel implements ByteChannel {
* @param timeout Unused. May be used when overridden
* @return Always returns <code>true</code> since there is no network buffer
* in the regular channel
- * @throws IOException
+ *
+ * @throws IOException Never for non-secure channel
*/
public boolean flush(boolean block, Selector s, long timeout)
throws IOException {
@@ -98,9 +100,17 @@ public class NioChannel implements ByteChannel {
getIOChannel().close();
}
+ /**
+ * 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 {
if (isOpen() || force ) close();
}
+
/**
* Tells whether or not this channel is open.
*
@@ -145,7 +155,7 @@ public class NioChannel implements ByteChannel {
return att;
}
- public ApplicationBufferHandler getBufHandler() {
+ public SocketBufferHandler getBufHandler() {
return bufHandler;
}
@@ -172,7 +182,7 @@ public class NioChannel implements ByteChannel {
* @param read Unused in non-secure implementation
* @param write Unused in non-secure implementation
* @return Always returns zero
- * @throws IOException
+ * @throws IOException Never for non-secure channel
*/
public int handshake(boolean read, boolean write) throws IOException {
return 0;
@@ -196,8 +206,11 @@ public class NioChannel implements ByteChannel {
}
/**
- * Return true if the buffer wrote data
- * @throws IOException
+ * 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 {
return false;
@@ -212,10 +225,20 @@ public class NioChannel implements ByteChannel {
* 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
*/
protected void checkInterruptStatus() throws IOException {
if (Thread.interrupted()) {
throw new IOException(sm.getString("channel.nio.interrupted"));
}
}
+
+
+ private ApplicationBufferHandler appReadBufHandler;
+ public void setAppReadBufHandler(ApplicationBufferHandler handler) {
+ this.appReadBufHandler = handler;
+ }
+ protected ApplicationBufferHandler getAppReadBufHandler() {
+ return appReadBufHandler;
+ }
}
diff --git a/java/org/apache/tomcat/util/net/NioEndpoint.java b/java/org/apache/tomcat/util/net/NioEndpoint.java
index a045533..e2e23ea 100644
--- a/java/org/apache/tomcat/util/net/NioEndpoint.java
+++ b/java/org/apache/tomcat/util/net/NioEndpoint.java
@@ -14,12 +14,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
package org.apache.tomcat.util.net;
+import java.io.EOFException;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
+import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
@@ -34,20 +35,13 @@ import java.nio.channels.SocketChannel;
import java.nio.channels.WritableByteChannel;
import java.util.ConcurrentModificationException;
import java.util.Iterator;
-import java.util.Locale;
-import java.util.Set;
import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.Executor;
-import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
-import javax.net.ssl.KeyManager;
-import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
-import javax.net.ssl.SSLSessionContext;
-import javax.net.ssl.X509KeyManager;
+import javax.net.ssl.SSLSession;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
@@ -55,10 +49,8 @@ import org.apache.tomcat.util.ExceptionUtils;
import org.apache.tomcat.util.IntrospectionUtils;
import org.apache.tomcat.util.collections.SynchronizedQueue;
import org.apache.tomcat.util.collections.SynchronizedStack;
-import org.apache.tomcat.util.compat.JreCompat;
import org.apache.tomcat.util.net.AbstractEndpoint.Handler.SocketState;
-import org.apache.tomcat.util.net.SecureNioChannel.ApplicationBufferHandler;
-import org.apache.tomcat.util.net.jsse.NioX509KeyManager;
+import org.apache.tomcat.util.net.jsse.JSSESupport;
/**
* NIO tailored thread pool, providing the following services:
@@ -74,7 +66,7 @@ import org.apache.tomcat.util.net.jsse.NioX509KeyManager;
* @author Mladen Turk
* @author Remy Maucherat
*/
-public class NioEndpoint extends AbstractEndpoint<NioChannel> {
+public class NioEndpoint extends AbstractJsseEndpoint<NioChannel> {
// -------------------------------------------------------------- Constants
@@ -84,8 +76,6 @@ public class NioEndpoint extends AbstractEndpoint<NioChannel> {
public static final int OP_REGISTER = 0x100; //register interest op
- @Deprecated // Unused. Will be removed in Tomcat 8.5.x
- public static final int OP_CALLBACK = 0x200; //callback interest op
// ----------------------------------------------------------------- Fields
@@ -97,43 +87,11 @@ public class NioEndpoint extends AbstractEndpoint<NioChannel> {
private ServerSocketChannel serverSock = null;
/**
- * use send file
- */
- private boolean useSendfile = true;
-
- /**
- * The size of the OOM parachute.
- */
- private int oomParachute = 1024*1024;
- /**
- * The oom parachute, when an OOM error happens,
- * will release the data, giving the JVM instantly
- * a chunk of data to be able to recover with.
- */
- private byte[] oomParachuteData = null;
-
- /**
- * Make sure this string has already been allocated
- */
- private static final String oomParachuteMsg =
- "SEVERE:Memory usage is low, parachute is non existent, your system may start failing.";
-
- /**
- * Keep track of OOM warning messages.
- */
- private long lastParachuteCheck = System.currentTimeMillis();
-
- /**
*
*/
private volatile CountDownLatch stopLatch = null;
/**
- * Cache for SocketProcessor objects
- */
- private SynchronizedStack<SocketProcessor> processorCache;
-
- /**
* Cache for poller events
*/
private SynchronizedStack<PollerEvent> eventCache;
@@ -144,19 +102,9 @@ public class NioEndpoint extends AbstractEndpoint<NioChannel> {
private SynchronizedStack<NioChannel> nioChannels;
- // ------------------------------------------------------------ Constructor
-
- public NioEndpoint() {
- // If running on Java 7, the insecure DHE ciphers need to be excluded by
- // default
- if (!JreCompat.isJre8Available()) {
- setCiphers(DEFAULT_CIPHERS + ":!DHE");
- }
- }
-
-
// ------------------------------------------------------------- Properties
+
/**
* Generic properties, introspected
*/
@@ -185,27 +133,6 @@ public class NioEndpoint extends AbstractEndpoint<NioChannel> {
/**
- * Handling of accepted sockets.
- */
- private Handler handler = null;
- public void setHandler(Handler handler ) { this.handler = handler; }
- public Handler getHandler() { return handler; }
-
-
- /**
- * Allow comet request handling.
- */
- private boolean useComet = true;
- public void setUseComet(boolean useComet) { this.useComet = useComet; }
- @Override
- public boolean getUseComet() { return useComet; }
- @Override
- public boolean getUseCometTimeout() { return getUseComet(); }
- @Override
- public boolean getUsePolling() { return true; } // Always supported
-
-
- /**
* Poller thread count.
*/
private int pollerThreadCount = Math.min(2,Runtime.getRuntime().availableProcessors());
@@ -221,7 +148,9 @@ public class NioEndpoint extends AbstractEndpoint<NioChannel> {
private Poller[] pollers = null;
private AtomicInteger pollerRotater = new AtomicInteger(0);
/**
- * Return an available poller in true round robin fashion
+ * Return an available poller in true round robin fashion.
+ *
+ * @return The next poller in sequence
*/
public Poller getPoller0() {
int idx = Math.abs(pollerRotater.incrementAndGet()) % pollers.length;
@@ -237,10 +166,6 @@ public class NioEndpoint extends AbstractEndpoint<NioChannel> {
this.socketProperties = socketProperties;
}
- public void setUseSendfile(boolean useSendfile) {
- this.useSendfile = useSendfile;
- }
-
/**
* Is deferAccept supported?
*/
@@ -250,20 +175,6 @@ public class NioEndpoint extends AbstractEndpoint<NioChannel> {
return false;
}
- public void setOomParachute(int oomParachute) {
- this.oomParachute = oomParachute;
- }
-
- public void setOomParachuteData(byte[] oomParachuteData) {
- this.oomParachuteData = oomParachuteData;
- }
-
-
- private SSLContext sslContext = null;
- public SSLContext getSSLContext() { return sslContext;}
- public void setSSLContext(SSLContext c) { sslContext = c;}
- private String[] enabledCiphers;
- private String[] enabledProtocols;
/**
* Port in use.
@@ -284,44 +195,12 @@ public class NioEndpoint extends AbstractEndpoint<NioChannel> {
}
- @Override
- public String[] getCiphersUsed() {
- return enabledCiphers;
- }
-
-
- // --------------------------------------------------------- OOM Parachute Methods
-
- protected void checkParachute() {
- boolean para = reclaimParachute(false);
- if (!para && (System.currentTimeMillis()-lastParachuteCheck)>10000) {
- try {
- log.fatal(oomParachuteMsg);
- }catch (Throwable t) {
- ExceptionUtils.handleThrowable(t);
- System.err.println(oomParachuteMsg);
- }
- lastParachuteCheck = System.currentTimeMillis();
- }
- }
-
- protected boolean reclaimParachute(boolean force) {
- if ( oomParachuteData != null ) return true;
- if ( oomParachute > 0 && ( force || (Runtime.getRuntime().freeMemory() > (oomParachute*2))) )
- oomParachuteData = new byte[oomParachute];
- return oomParachuteData != null;
- }
-
- protected void releaseCaches() {
- this.nioChannels.clear();
- this.processorCache.clear();
- if ( handler != null ) handler.recycle();
-
- }
-
// --------------------------------------------------------- Public Methods
/**
- * Number of keepalive sockets.
+ * 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
*/
public int getKeepAliveCount() {
if (pollers == null) {
@@ -338,7 +217,6 @@ public class NioEndpoint extends AbstractEndpoint<NioChannel> {
// ----------------------------------------------- Public Lifecycle Methods
-
/**
* Initialize the endpoint.
*/
@@ -364,46 +242,11 @@ public class NioEndpoint extends AbstractEndpoint<NioChannel> {
stopLatch = new CountDownLatch(pollerThreadCount);
// Initialize SSL if needed
- if (isSSLEnabled()) {
- SSLUtil sslUtil = handler.getSslImplementation().getSSLUtil(this);
-
- sslContext = sslUtil.createSSLContext();
- sslContext.init(wrap(sslUtil.getKeyManagers()),
- sslUtil.getTrustManagers(), null);
+ initialiseSsl();
- SSLSessionContext sessionContext =
- sslContext.getServerSessionContext();
- if (sessionContext != null) {
- sslUtil.configureSessionContext(sessionContext);
- }
- // Determine which cipher suites and protocols to enable
- enabledCiphers = sslUtil.getEnableableCiphers(sslContext);
- enabledProtocols = sslUtil.getEnableableProtocols(sslContext);
- }
-
- if (oomParachute>0) reclaimParachute(true);
selectorPool.open();
}
- public KeyManager[] wrap(KeyManager[] managers) {
- if (managers==null) return null;
- KeyManager[] result = new KeyManager[managers.length];
- for (int i=0; i<result.length; i++) {
- if (managers[i] instanceof X509KeyManager && getKeyAlias()!=null) {
- String keyAlias = getKeyAlias();
- // JKS keystores always convert the alias name to lower case
- if ("jks".equalsIgnoreCase(getKeystoreType())) {
- keyAlias = keyAlias.toLowerCase(Locale.ENGLISH);
- }
- result[i] = new NioX509KeyManager((X509KeyManager) managers[i], keyAlias);
- } else {
- result[i] = managers[i];
- }
- }
- return result;
- }
-
-
/**
* Start the NIO endpoint, creating acceptor, poller threads.
*/
@@ -488,8 +331,11 @@ public class NioEndpoint extends AbstractEndpoint<NioChannel> {
serverSock.socket().close();
serverSock.close();
serverSock = null;
- sslContext = null;
- releaseCaches();
+ destroySsl();
+ super.unbind();
+ if (getHandler() != null ) {
+ getHandler().recycle();
+ }
selectorPool.close();
if (log.isDebugEnabled()) {
log.debug("Destroy completed for "+new InetSocketAddress(getAddress(),getPort()));
@@ -512,19 +358,6 @@ public class NioEndpoint extends AbstractEndpoint<NioChannel> {
return selectorPool;
}
- @Override
- public boolean getUseSendfile() {
- return useSendfile;
- }
-
- public int getOomParachute() {
- return oomParachute;
- }
-
- public byte[] getOomParachuteData() {
- return oomParachuteData;
- }
-
@Override
protected AbstractEndpoint.Acceptor createAcceptor() {
@@ -534,6 +367,10 @@ public class NioEndpoint extends AbstractEndpoint<NioChannel> {
/**
* Process the specified connection.
+ * @param socket The socket channel
+ * @return <code>true</code> if the socket was correctly configured
+ * and processing may continue, <code>false</code> if the socket needs to be
+ * close immediately
*/
protected boolean setSocketOptions(SocketChannel socket) {
// Process the connection
@@ -544,31 +381,19 @@ public class NioEndpoint extends AbstractEndpoint<NioChannel> {
socketProperties.setProperties(sock);
NioChannel channel = nioChannels.pop();
- if ( channel == null ) {
- // SSL setup
- if (sslContext != null) {
- SSLEngine engine = createSSLEngine();
- int appbufsize = engine.getSession().getApplicationBufferSize();
- NioBufferHandler bufhandler = new NioBufferHandler(Math.max(appbufsize,socketProperties.getAppReadBufSize()),
- Math.max(appbufsize,socketProperties.getAppWriteBufSize()),
- socketProperties.getDirectBuffer());
- channel = new SecureNioChannel(socket, engine, bufhandler, selectorPool);
+ if (channel == null) {
+ SocketBufferHandler bufhandler = new SocketBufferHandler(
+ socketProperties.getAppReadBufSize(),
+ socketProperties.getAppWriteBufSize(),
+ socketProperties.getDirectBuffer());
+ if (isSSLEnabled()) {
+ channel = new SecureNioChannel(socket, bufhandler, selectorPool, this);
} else {
- // normal tcp setup
- NioBufferHandler bufhandler = new NioBufferHandler(socketProperties.getAppReadBufSize(),
- socketProperties.getAppWriteBufSize(),
- socketProperties.getDirectBuffer());
-
channel = new NioChannel(socket, bufhandler);
}
} else {
channel.setIOChannel(socket);
- if ( channel instanceof SecureNioChannel ) {
- SSLEngine engine = createSSLEngine();
- ((SecureNioChannel)channel).reset(engine);
- } else {
- channel.reset();
- }
+ channel.reset();
}
getPoller0().register(channel);
} catch (Throwable t) {
@@ -584,67 +409,6 @@ public class NioEndpoint extends AbstractEndpoint<NioChannel> {
return true;
}
- protected SSLEngine createSSLEngine() {
- SSLEngine engine = sslContext.createSSLEngine();
- if ("false".equals(getClientAuth())) {
- engine.setNeedClientAuth(false);
- engine.setWantClientAuth(false);
- } else if ("true".equals(getClientAuth()) || "yes".equals(getClientAuth())){
- engine.setNeedClientAuth(true);
- } else if ("want".equals(getClientAuth())) {
- engine.setWantClientAuth(true);
- }
- engine.setUseClientMode(false);
- engine.setEnabledCipherSuites(enabledCiphers);
- engine.setEnabledProtocols(enabledProtocols);
-
- configureUseServerCipherSuitesOrder(engine);
-
- return engine;
- }
-
-
- /**
- * Returns true if a worker thread is available for processing.
- * @return boolean
- */
- protected boolean isWorkerAvailable() {
- return true;
- }
-
-
- @Override
- public void processSocket(SocketWrapper<NioChannel> socketWrapper,
- SocketStatus socketStatus, boolean dispatch) {
- processSocket((KeyAttachment) socketWrapper, socketStatus, dispatch);
- }
-
- protected boolean processSocket(KeyAttachment attachment, SocketStatus status, boolean dispatch) {
- try {
- if (attachment == null) {
- return false;
- }
- SocketProcessor sc = processorCache.pop();
- if ( sc == null ) sc = new SocketProcessor(attachment, status);
- else sc.reset(attachment, status);
- Executor executor = getExecutor();
- if (dispatch && executor != null) {
- executor.execute(sc);
- } else {
- sc.run();
- }
- } catch (RejectedExecutionException ree) {
- log.warn(sm.getString("endpoint.executor.fail", attachment.getSocket()), ree);
- return false;
- } catch (Throwable t) {
- ExceptionUtils.handleThrowable(t);
- // This means we got an OOM or similar creating a thread, or that
- // the pool and its queue are full
- log.error(sm.getString("endpoint.process.fail"), t);
- return false;
- }
- return true;
- }
@Override
protected Log getLog() {
@@ -719,23 +483,6 @@ public class NioEndpoint extends AbstractEndpoint<NioChannel> {
if (running) {
log.error(sm.getString("endpoint.accept.fail"), x);
}
- } catch (OutOfMemoryError oom) {
- try {
- oomParachuteData = null;
- releaseCaches();
- log.error("", oom);
- }catch ( Throwable oomt ) {
- try {
- try {
- System.err.println(oomParachuteMsg);
- oomt.printStackTrace();
- }catch (Throwable letsHopeWeDontGetHere){
- ExceptionUtils.handleThrowable(letsHopeWeDontGetHere);
- }
- }catch (Throwable letsHopeWeDontGetHere){
- ExceptionUtils.handleThrowable(letsHopeWeDontGetHere);
- }
- }
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
log.error(sm.getString("endpoint.accept.fail"), t);
@@ -746,6 +493,33 @@ public class NioEndpoint extends AbstractEndpoint<NioChannel> {
}
+ @Override
+ protected SocketProcessorBase<NioChannel> createSocketProcessor(
+ SocketWrapperBase<NioChannel> socketWrapper, SocketEvent event) {
+ return new SocketProcessor(socketWrapper, event);
+ }
+
+
+ private void close(NioChannel socket, SelectionKey key) {
+ try {
+ if (socket.getPoller().cancelledKey(key) != null) {
+ // SocketWrapper (attachment) was removed from the
+ // key - recycle the key. This can only happen once
+ // per attempted closure so it is used to determine
+ // whether or not to return the key to the cache.
+ // We do NOT want to do this more than once - see BZ
+ // 57340 / 57943.
+ if (running && !paused) {
+ if (!nioChannels.push(socket)) {
+ socket.free();
+ }
+ }
+ }
+ } catch (Exception x) {
+ log.error("",x);
+ }
+ }
+
private void closeSocket(SocketChannel socket) {
try {
socket.socket().close();
@@ -774,16 +548,16 @@ public class NioEndpoint extends AbstractEndpoint<NioChannel> {
private NioChannel socket;
private int interestOps;
- private KeyAttachment key;
+ private NioSocketWrapper socketWrapper;
- public PollerEvent(NioChannel ch, KeyAttachment k, int intOps) {
- reset(ch, k, intOps);
+ public PollerEvent(NioChannel ch, NioSocketWrapper w, int intOps) {
+ reset(ch, w, intOps);
}
- public void reset(NioChannel ch, KeyAttachment k, int intOps) {
+ public void reset(NioChannel ch, NioSocketWrapper w, int intOps) {
socket = ch;
interestOps = intOps;
- key = k;
+ socketWrapper = w;
}
public void reset() {
@@ -792,11 +566,12 @@ public class NioEndpoint extends AbstractEndpoint<NioChannel> {
@Override
public void run() {
- if ( interestOps == OP_REGISTER ) {
+ if (interestOps == OP_REGISTER) {
try {
- socket.getIOChannel().register(socket.getPoller().getSelector(), SelectionKey.OP_READ, key);
+ socket.getIOChannel().register(
+ socket.getPoller().getSelector(), SelectionKey.OP_READ, socketWrapper);
} catch (Exception x) {
- log.error("", x);
+ log.error(sm.getString("endpoint.nio.registerFail"), x);
}
} else {
final SelectionKey key = socket.getIOChannel().keyFor(socket.getPoller().getSelector());
@@ -807,30 +582,30 @@ public class NioEndpoint extends AbstractEndpoint<NioChannel> {
// processed. Count down the connections at this point
// since it won't have been counted down when the socket
// closed.
- socket.getPoller().getEndpoint().countDownConnection();
+ socket.socketWrapper.getEndpoint().countDownConnection();
} else {
- final KeyAttachment att = (KeyAttachment) key.attachment();
- if ( att!=null ) {
- att.access();//to prevent timeout
+ final NioSocketWrapper socketWrapper = (NioSocketWrapper) key.attachment();
+ if (socketWrapper != null) {
//we are registering the key to start with, reset the fairness counter.
int ops = key.interestOps() | interestOps;
- att.interestOps(ops);
+ socketWrapper.interestOps(ops);
key.interestOps(ops);
} else {
- socket.getPoller().cancelledKey(key, SocketStatus.ERROR);
+ socket.getPoller().cancelledKey(key);
}
}
} catch (CancelledKeyException ckx) {
try {
- socket.getPoller().cancelledKey(key, SocketStatus.DISCONNECT);
+ socket.getPoller().cancelledKey(key);
} catch (Exception ignore) {}
}
- }//end if
- }//run
+ }
+ }
@Override
public String toString() {
- return super.toString()+"[intOps="+this.interestOps+"]";
+ return "Poller event: socket [" + socket + "], socketWrapper [" + socketWrapper +
+ "], interstOps [" + interestOps + "]";
}
}
@@ -851,22 +626,13 @@ public class NioEndpoint extends AbstractEndpoint<NioChannel> {
private volatile int keyCount = 0;
public Poller() throws IOException {
- synchronized (Selector.class) {
- // Selector.open() isn't thread safe
- // http://bugs.sun.com/view_bug.do?bug_id=6427854
- // Affects 1.6.0_29, fixed in 1.7.0_01
- this.selector = Selector.open();
- }
+ this.selector = Selector.open();
}
public int getKeyCount() { return keyCount; }
public Selector getSelector() { return selector;}
- NioEndpoint getEndpoint() {
- return NioEndpoint.this;
- }
-
/**
* Destroy the poller.
*/
@@ -890,19 +656,17 @@ public class NioEndpoint extends AbstractEndpoint<NioChannel> {
* however).
*
* @param socket to add to the poller
+ * @param interestOps Operations for which to register this socket with
+ * the Poller
*/
- public void add(final NioChannel socket) {
- add(socket,SelectionKey.OP_READ);
- }
-
public void add(final NioChannel socket, final int interestOps) {
PollerEvent r = eventCache.pop();
if ( r==null) r = new PollerEvent(socket,null,interestOps);
else r.reset(socket,null,interestOps);
addEvent(r);
if (close) {
- NioEndpoint.KeyAttachment ka = (NioEndpoint.KeyAttachment)socket.getAttachment();
- processSocket(ka, SocketStatus.STOP, false);
+ NioEndpoint.NioSocketWrapper ka = (NioEndpoint.NioSocketWrapper)socket.getAttachment();
+ processSocket(ka, SocketEvent.STOP, false);
}
}
@@ -939,11 +703,15 @@ public class NioEndpoint extends AbstractEndpoint<NioChannel> {
*/
public void register(final NioChannel socket) {
socket.setPoller(this);
- KeyAttachment ka = new KeyAttachment(socket);
+ NioSocketWrapper ka = new NioSocketWrapper(socket, NioEndpoint.this);
+ socket.setSocketWrapper(ka);
ka.setPoller(this);
- ka.setTimeout(getSocketProperties().getSoTimeout());
+ ka.setReadTimeout(getSocketProperties().getSoTimeout());
+ ka.setWriteTimeout(getSocketProperties().getSoTimeout());
ka.setKeepAliveLeft(NioEndpoint.this.getMaxKeepAliveRequests());
ka.setSecure(isSSLEnabled());
+ ka.setReadTimeout(getSoTimeout());
+ ka.setWriteTimeout(getSoTimeout());
PollerEvent r = eventCache.pop();
ka.interestOps(SelectionKey.OP_READ);//this is what OP_REGISTER turns into.
if ( r==null) r = new PollerEvent(socket,ka,OP_REGISTER);
@@ -951,25 +719,16 @@ public class NioEndpoint extends AbstractEndpoint<NioChannel> {
addEvent(r);
}
- public KeyAttachment cancelledKey(SelectionKey key, SocketStatus status) {
- KeyAttachment ka = null;
+ public NioSocketWrapper cancelledKey(SelectionKey key) {
+ NioSocketWrapper ka = null;
try {
if ( key == null ) return null;//nothing to do
- ka = (KeyAttachment) key.attachment();
- if (ka != null && ka.isComet() && status != null) {
- ka.setComet(false);//to avoid a loop
- if (status == SocketStatus.TIMEOUT ) {
- if (processSocket(ka, status, true)) {
- return null; // don't close on comet timeout
- }
- } else {
- // Don't dispatch if the lines below are cancelling the key
- processSocket(ka, status, false);
- }
+ ka = (NioSocketWrapper) key.attach(null);
+ if (ka != null) {
+ // If attachment is non-null then there may be a current
+ // connection with an associated processor.
+ getHandler().release(ka);
}
- ka = (KeyAttachment) key.attach(null);
- if (ka!=null) handler.release(ka);
- else handler.release((SocketChannel)key.channel());
if (key.isValid()) key.cancel();
if (key.channel().isOpen()) {
try {
@@ -1008,162 +767,118 @@ public class NioEndpoint extends AbstractEndpoint<NioChannel> {
}
return ka;
}
+
/**
- * The background thread that listens for incoming TCP/IP connections and
- * hands them off to an appropriate processor.
+ * 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() {
// Loop until destroy() is called
while (true) {
+
+ boolean hasEvents = false;
+
try {
- // Loop if endpoint is paused
- while (paused && (!close) ) {
- try {
- Thread.sleep(100);
- } catch (InterruptedException e) {
- // Ignore
+ if (!close) {
+ hasEvents = events();
+ if (wakeupCounter.getAndSet(-1) > 0) {
+ //if we are here, means we have other stuff to do
+ //do a non blocking select
+ keyCount = selector.selectNow();
+ } else {
+ keyCount = selector.select(selectorTimeout);
}
+ wakeupCounter.set(0);
}
-
- boolean hasEvents = false;
-
- // Time to terminate?
if (close) {
events();
timeout(0, false);
try {
selector.close();
} catch (IOException ioe) {
- log.error(sm.getString(
- "endpoint.nio.selectorCloseFail"), ioe);
+ log.error(sm.getString("endpoint.nio.selectorCloseFail"), ioe);
}
break;
- } else {
- hasEvents = events();
}
- try {
- if ( !close ) {
- if (wakeupCounter.getAndSet(-1) > 0) {
- //if we are here, means we have other stuff to do
- //do a non blocking select
- keyCount = selector.selectNow();
- } else {
- keyCount = selector.select(selectorTimeout);
- }
- wakeupCounter.set(0);
- }
- if (close) {
- events();
- timeout(0, false);
- try {
- selector.close();
- } catch (IOException ioe) {
- log.error(sm.getString(
- "endpoint.nio.selectorCloseFail"), ioe);
- }
- break;
- }
- } catch (Throwable x) {
- ExceptionUtils.handleThrowable(x);
- log.error("",x);
- continue;
+ } catch (Throwable x) {
+ ExceptionUtils.handleThrowable(x);
+ log.error("",x);
+ continue;
+ }
+ //either we timed out or we woke up, process events first
+ if ( keyCount == 0 ) hasEvents = (hasEvents | events());
+
+ Iterator<SelectionKey> iterator =
+ keyCount > 0 ? selector.selectedKeys().iterator() : null;
+ // Walk through the collection of ready keys and dispatch
+ // any active event.
+ while (iterator != null && iterator.hasNext()) {
+ SelectionKey sk = iterator.next();
+ NioSocketWrapper attachment = (NioSocketWrapper)sk.attachment();
+ // Attachment may be null if another thread has called
+ // cancelledKey()
+ if (attachment == null) {
+ iterator.remove();
+ } else {
+ iterator.remove();
+ processKey(sk, attachment);
}
- //either we timed out or we woke up, process events first
- if ( keyCount == 0 ) hasEvents = (hasEvents | events());
-
- Iterator<SelectionKey> iterator =
- keyCount > 0 ? selector.selectedKeys().iterator() : null;
- // Walk through the collection of ready keys and dispatch
- // any active event.
- while (iterator != null && iterator.hasNext()) {
- SelectionKey sk = iterator.next();
- KeyAttachment attachment = (KeyAttachment)sk.attachment();
- // Attachment may be null if another thread has called
- // cancelledKey()
- if (attachment == null) {
- iterator.remove();
- } else {
- attachment.access();
- iterator.remove();
- processKey(sk, attachment);
- }
- }//while
+ }//while
- //process timeouts
- timeout(keyCount,hasEvents);
- if ( oomParachute > 0 && oomParachuteData == null ) checkParachute();
- } catch (OutOfMemoryError oom) {
- try {
- oomParachuteData = null;
- releaseCaches();
- log.error("", oom);
- }catch ( Throwable oomt ) {
- try {
- System.err.println(oomParachuteMsg);
- oomt.printStackTrace();
- }catch (Throwable letsHopeWeDontGetHere){
- ExceptionUtils.handleThrowable(letsHopeWeDontGetHere);
- }
- }
- }
+ //process timeouts
+ timeout(keyCount,hasEvents);
}//while
stopLatch.countDown();
}
- protected boolean processKey(SelectionKey sk, KeyAttachment attachment) {
- boolean result = true;
+ protected void processKey(SelectionKey sk, NioSocketWrapper attachment) {
try {
if ( close ) {
- cancelledKey(sk, SocketStatus.STOP);
+ cancelledKey(sk);
} else if ( sk.isValid() && attachment != null ) {
- attachment.access();//make sure we don't time out valid sockets
if (sk.isReadable() || sk.isWritable() ) {
if ( attachment.getSendfileData() != null ) {
processSendfile(sk,attachment, false);
} else {
- if ( isWorkerAvailable() ) {
- unreg(sk, attachment, sk.readyOps());
- boolean closeSocket = false;
- // Read goes before write
- if (sk.isReadable()) {
- if (!processSocket(attachment, SocketStatus.OPEN_READ, true)) {
- closeSocket = true;
- }
- }
- if (!closeSocket && sk.isWritable()) {
- if (!processSocket(attachment, SocketStatus.OPEN_WRITE, true)) {
- closeSocket = true;
- }
+ unreg(sk, attachment, sk.readyOps());
+ boolean closeSocket = false;
+ // Read goes before write
+ if (sk.isReadable()) {
+ if (!processSocket(attachment, SocketEvent.OPEN_READ, true)) {
+ closeSocket = true;
}
- if (closeSocket) {
- cancelledKey(sk,SocketStatus.DISCONNECT);
+ }
+ if (!closeSocket && sk.isWritable()) {
+ if (!processSocket(attachment, SocketEvent.OPEN_WRITE, true)) {
+ closeSocket = true;
}
- } else {
- result = false;
+ }
+ if (closeSocket) {
+ cancelledKey(sk);
}
}
}
} else {
//invalid key
- cancelledKey(sk, SocketStatus.ERROR);
+ cancelledKey(sk);
}
} catch ( CancelledKeyException ckx ) {
- cancelledKey(sk, SocketStatus.ERROR);
+ cancelledKey(sk);
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
log.error("",t);
}
- return result;
}
- public SendfileState processSendfile(SelectionKey sk, KeyAttachment attachment,
+ public SendfileState processSendfile(SelectionKey sk, NioSocketWrapper socketWrapper,
boolean calledByProcessor) {
NioChannel sc = null;
try {
- unreg(sk, attachment, sk.readyOps());
- SendfileData sd = attachment.getSendfileData();
+ unreg(sk, socketWrapper, sk.readyOps());
+ SendfileData sd = socketWrapper.getSendfileData();
if (log.isTraceEnabled()) {
log.trace("Processing send file for: " + sd.fileName);
@@ -1173,7 +888,7 @@ public class NioEndpoint extends AbstractEndpoint<NioChannel> {
// Setup the file channel
File f = new File(sd.fileName);
if (!f.exists()) {
- cancelledKey(sk,SocketStatus.ERROR);
+ cancelledKey(sk);
return SendfileState.ERROR;
}
@SuppressWarnings("resource") // Closed when channel is closed
@@ -1182,21 +897,21 @@ public class NioEndpoint extends AbstractEndpoint<NioChannel> {
}
// Configure output channel
- sc = attachment.getSocket();
+ sc = socketWrapper.getSocket();
// TLS/SSL channel is slightly different
WritableByteChannel wc = ((sc instanceof SecureNioChannel)?sc:sc.getIOChannel());
// We still have data in the buffer
if (sc.getOutboundRemaining()>0) {
if (sc.flushOutbound()) {
- attachment.access();
+ socketWrapper.updateLastWrite();
}
} else {
long written = sd.fchannel.transferTo(sd.pos,sd.length,wc);
if (written > 0) {
sd.pos += written;
sd.length -= written;
- attachment.access();
+ socketWrapper.updateLastWrite();
} else {
// Unusual not to be able to transfer any bytes
// Check the length was set correctly
@@ -1210,7 +925,7 @@ public class NioEndpoint extends AbstractEndpoint<NioChannel> {
if (log.isDebugEnabled()) {
log.debug("Send file complete for: "+sd.fileName);
}
- attachment.setSendfileData(null);
+ socketWrapper.setSendfileData(null);
try {
sd.fchannel.close();
} catch (Exception ignore) {
@@ -1223,12 +938,12 @@ public class NioEndpoint extends AbstractEndpoint<NioChannel> {
if (log.isDebugEnabled()) {
log.debug("Connection is keep alive, registering back for OP_READ");
}
- reg(sk,attachment,SelectionKey.OP_READ);
+ reg(sk,socketWrapper,SelectionKey.OP_READ);
} else {
if (log.isDebugEnabled()) {
log.debug("Send file connection is being closed");
}
- cancelledKey(sk,SocketStatus.STOP);
+ close(sc, sk);
}
}
return SendfileState.DONE;
@@ -1237,29 +952,37 @@ public class NioEndpoint extends AbstractEndpoint<NioChannel> {
log.debug("OP_WRITE for sendfile: " + sd.fileName);
}
if (calledByProcessor) {
- add(attachment.getSocket(),SelectionKey.OP_WRITE);
+ add(socketWrapper.getSocket(),SelectionKey.OP_WRITE);
} else {
- reg(sk,attachment,SelectionKey.OP_WRITE);
+ reg(sk,socketWrapper,SelectionKey.OP_WRITE);
}
return SendfileState.PENDING;
}
- }catch ( IOException x ) {
- if ( log.isDebugEnabled() ) log.debug("Unable to complete sendfile request:", x);
- cancelledKey(sk,SocketStatus.ERROR);
+ } catch (IOException x) {
+ if (log.isDebugEnabled()) log.debug("Unable to complete sendfile request:", x);
+ if (!calledByProcessor && sc != null) {
+ close(sc, sk);
+ } else {
+ cancelledKey(sk);
+ }
return SendfileState.ERROR;
- }catch ( Throwable t ) {
- log.error("",t);
- cancelledKey(sk, SocketStatus.ERROR);
+ } catch (Throwable t) {
+ log.error("", t);
+ if (!calledByProcessor && sc != null) {
+ close(sc, sk);
+ } else {
+ cancelledKey(sk);
+ }
return SendfileState.ERROR;
}
}
- protected void unreg(SelectionKey sk, KeyAttachment attachment, int readyOps) {
+ protected void unreg(SelectionKey sk, NioSocketWrapper attachment, int readyOps) {
//this is a must, so that we don't have multiple threads messing with the socket
reg(sk,attachment,sk.interestOps()& (~readyOps));
}
- protected void reg(SelectionKey sk, KeyAttachment attachment, int intops) {
+ protected void reg(SelectionKey sk, NioSocketWrapper attachment, int intops) {
sk.interestOps(intops);
attachment.interestOps(intops);
}
@@ -1277,50 +1000,44 @@ public class NioEndpoint extends AbstractEndpoint<NioChannel> {
return;
}
//timeout
- Set<SelectionKey> keys = selector.keys();
int keycount = 0;
try {
- for (Iterator<SelectionKey> iter = keys.iterator(); iter.hasNext();) {
- SelectionKey key = iter.next();
+ for (SelectionKey key : selector.keys()) {
keycount++;
try {
- KeyAttachment ka = (KeyAttachment) key.attachment();
+ NioSocketWrapper ka = (NioSocketWrapper) key.attachment();
if ( ka == null ) {
- cancelledKey(key, SocketStatus.ERROR); //we don't support any keys without attachments
+ cancelledKey(key); //we don't support any keys without attachments
+ } else if (close) {
+ key.interestOps(0);
+ ka.interestOps(0); //avoid duplicate stop calls
+ processKey(key,ka);
} else if ((ka.interestOps()&SelectionKey.OP_READ) == SelectionKey.OP_READ ||
(ka.interestOps()&SelectionKey.OP_WRITE) == SelectionKey.OP_WRITE) {
- //only timeout sockets that we are waiting for a read from
- long delta = now - ka.getLastAccess();
- long timeout = ka.getTimeout();
- boolean isTimedout = timeout > 0 && delta > timeout;
- if ( close ) {
- key.interestOps(0);
- ka.interestOps(0); //avoid duplicate stop calls
- processKey(key,ka);
- } else if (isTimedout) {
- key.interestOps(0);
- ka.interestOps(0); //avoid duplicate timeout calls
- cancelledKey(key, SocketStatus.TIMEOUT);
+ boolean isTimedOut = false;
+ // Check for read timeout
+ if ((ka.interestOps() & SelectionKey.OP_READ) == SelectionKey.OP_READ) {
+ long delta = now - ka.getLastRead();
+ long timeout = ka.getReadTimeout();
+ isTimedOut = timeout > 0 && delta > timeout;
+ }
+ // Check for write timeout
+ if (!isTimedOut && (ka.interestOps() & SelectionKey.OP_WRITE) == SelectionKey.OP_WRITE) {
+ long delta = now - ka.getLastWrite();
+ long timeout = ka.getWriteTimeout();
+ isTimedOut = timeout > 0 && delta > timeout;
}
- } else if (ka.isAsync() || ka.isComet()) {
- if (close) {
+ if (isTimedOut) {
key.interestOps(0);
- ka.interestOps(0); //avoid duplicate stop calls
- processKey(key,ka);
- } else if (!ka.isAsync() || ka.getTimeout() > 0) {
- // Async requests with a timeout of 0 or less never timeout
- long delta = now - ka.getLastAccess();
- long timeout = (ka.getTimeout()==-1)?((long) socketProperties.getSoTimeout()):(ka.getTimeout());
- boolean isTimedout = delta > timeout;
- if (isTimedout) {
- // Prevent subsequent timeouts if the timeout event takes a while to process
- ka.access(Long.MAX_VALUE);
- processSocket(ka, SocketStatus.TIMEOUT, true);
+ ka.interestOps(0); //avoid duplicate timeout calls
+ ka.setError(new SocketTimeoutException());
+ if (!processSocket(ka, SocketEvent.ERROR, true)) {
+ cancelledKey(key);
}
}
- }//end if
- } catch ( CancelledKeyException ckx ) {
- cancelledKey(key, SocketStatus.ERROR);
+ }
+ }catch ( CancelledKeyException ckx ) {
+ cancelledKey(key);
}
}//for
} catch (ConcurrentModificationException cme) {
@@ -1340,19 +1057,27 @@ public class NioEndpoint extends AbstractEndpoint<NioChannel> {
}
}
-// ----------------------------------------------------- Key Attachment Class
- public static class KeyAttachment extends SocketWrapper<NioChannel> {
+ // ---------------------------------------------------- Key Attachment Class
+ public static class NioSocketWrapper extends SocketWrapperBase<NioChannel> {
- public KeyAttachment(NioChannel channel) {
- super(channel);
+ private final NioSelectorPool pool;
+
+ private Poller poller = null;
+ private int interestOps = 0;
+ private CountDownLatch readLatch = null;
+ private CountDownLatch writeLatch = null;
+ private volatile SendfileData sendfileData = null;
+ private volatile long lastRead = System.currentTimeMillis();
+ private volatile long lastWrite = lastRead;
+
+ public NioSocketWrapper(NioChannel channel, NioEndpoint endpoint) {
+ super(channel, endpoint);
+ pool = endpoint.getSelectorPool();
+ socketBufferHandler = channel.getBufHandler();
}
public Poller getPoller() { return poller;}
public void setPoller(Poller poller){this.poller = poller;}
- @Deprecated // Unused. NO-OP. Will be removed in Tomcat 8.5.x
- public void setCometNotify(@SuppressWarnings("unused") boolean notify) { /* NO-OP */ }
- @Deprecated // Unused. Always returns false. Will be removed in Tomcat 8.5.x
- public boolean getCometNotify() { return false; }
public int interestOps() { return interestOps;}
public int interestOps(int ops) { this.interestOps = ops; return ops; }
public CountDownLatch getReadLatch() { return readLatch; }
@@ -1386,100 +1111,307 @@ public class NioEndpoint extends AbstractEndpoint<NioChannel> {
public void setSendfileData(SendfileData sf) { this.sendfileData = sf;}
public SendfileData getSendfileData() { return this.sendfileData;}
- public void setWriteTimeout(long writeTimeout) {
- this.writeTimeout = writeTimeout;
+ 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 {
+ socketBufferHandler.configureReadBufferForRead();
+
+ if (socketBufferHandler.getReadBuffer().remaining() > 0) {
+ return true;
+ }
+
+ fillReadBuffer(false);
+
+ boolean isReady = socketBufferHandler.getReadBuffer().position() > 0;
+ return isReady;
}
- public long getWriteTimeout() {return this.writeTimeout;}
- private Poller poller = null;
- private int interestOps = 0;
- private CountDownLatch readLatch = null;
- private CountDownLatch writeLatch = null;
- private volatile SendfileData sendfileData = null;
- private long writeTimeout = -1;
- }
+ @Override
+ public int read(boolean block, byte[] b, int off, int len) throws IOException {
+ int nRead = populateReadBuffer(b, off, len);
+ 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.
+ */
+ }
+
+ // Fill the read buffer as best we can.
+ nRead = fillReadBuffer(block);
+ updateLastRead();
- // ------------------------------------------------ Application Buffer Handler
- public static class NioBufferHandler implements ApplicationBufferHandler {
- private ByteBuffer readbuf = null;
- private ByteBuffer writebuf = null;
-
- public NioBufferHandler(int readsize, int writesize, boolean direct) {
- if ( direct ) {
- readbuf = ByteBuffer.allocateDirect(readsize);
- writebuf = ByteBuffer.allocateDirect(writesize);
- }else {
- readbuf = ByteBuffer.allocate(readsize);
- writebuf = ByteBuffer.allocate(writesize);
+ // Fill as much of the remaining byte array as possible with the
+ // data that was just read
+ if (nRead > 0) {
+ socketBufferHandler.configureReadBufferForRead();
+ nRead = Math.min(nRead, len);
+ socketBufferHandler.getReadBuffer().get(b, off, nRead);
}
+ return nRead;
}
+
@Override
- public ByteBuffer expand(ByteBuffer buffer, int remaining) {return buffer;}
+ public int read(boolean block, ByteBuffer to) throws IOException {
+ int nRead = populateReadBuffer(to);
+ 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.
+ */
+ }
+
+ // The socket read buffer capacity is socket.appReadBufSize
+ int limit = socketBufferHandler.getReadBuffer().capacity();
+ if (to.remaining() >= limit) {
+ to.limit(to.position() + limit);
+ nRead = fillReadBuffer(block, to);
+ updateLastRead();
+ } else {
+ // Fill the read buffer as best we can.
+ nRead = fillReadBuffer(block);
+ updateLastRead();
+
+ // Fill as much of the remaining byte array as possible with the
+ // data that was just read
+ if (nRead > 0) {
+ nRead = populateReadBuffer(to);
+ }
+ }
+ return nRead;
+ }
+
+
@Override
- public ByteBuffer getReadBuffer() {return readbuf;}
+ public void close() throws IOException {
+ getSocket().close();
+ }
+
+
@Override
- public ByteBuffer getWriteBuffer() {return writebuf;}
+ public boolean isClosed() {
+ return !getSocket().isOpen();
+ }
- }
- // ------------------------------------------------ Handler Inner Interface
+ private int fillReadBuffer(boolean block) throws IOException {
+ socketBufferHandler.configureReadBufferForWrite();
+ return fillReadBuffer(block, socketBufferHandler.getReadBuffer());
+ }
- /**
- * Bare bones interface used for socket processing. Per thread data is to be
- * stored in the ThreadWithAttributes extra folders, or alternately in
- * thread local fields.
- */
- public interface Handler extends AbstractEndpoint.Handler {
- public SocketState process(SocketWrapper<NioChannel> socket,
- SocketStatus status);
- public void release(SocketWrapper<NioChannel> socket);
- public void release(SocketChannel socket);
- public SSLImplementation getSslImplementation();
- }
+ private int fillReadBuffer(boolean block, ByteBuffer to) throws IOException {
+ int nRead;
+ NioChannel channel = getSocket();
+ if (block) {
+ Selector selector = null;
+ try {
+ selector = pool.get();
+ } catch (IOException x) {
+ // Ignore
+ }
+ try {
+ NioEndpoint.NioSocketWrapper att = (NioEndpoint.NioSocketWrapper) channel
+ .getAttachment();
+ if (att == null) {
+ throw new IOException("Key must be cancelled.");
+ }
+ nRead = pool.read(to, channel, selector, att.getReadTimeout());
+ } finally {
+ if (selector != null) {
+ pool.put(selector);
+ }
+ }
+ } else {
+ nRead = channel.read(to);
+ if (nRead == -1) {
+ throw new EOFException();
+ }
+ }
+ return nRead;
+ }
- // ---------------------------------------------- SocketProcessor Inner Class
- /**
- * This class is the equivalent of the Worker, but will simply use in an
- * external Executor thread pool.
- */
- protected class SocketProcessor implements Runnable {
+ @Override
+ protected void doWrite(boolean block, ByteBuffer from) throws IOException {
+ long writeTimeout = getWriteTimeout();
+ Selector selector = null;
+ try {
+ selector = pool.get();
+ } catch (IOException x) {
+ // Ignore
+ }
+ try {
+ pool.write(from, getSocket(), selector, writeTimeout, block);
+ if (block) {
+ // Make sure we are flushed
+ do {
+ if (getSocket().flush(true, selector, writeTimeout)) {
+ break;
+ }
+ } while (true);
+ }
+ updateLastWrite();
+ } finally {
+ if (selector != null) {
+ pool.put(selector);
+ }
+ }
+ // If there is data left in the buffer the socket will be registered for
+ // write further up the stack. This is to ensure the socket is only
+ // registered for write once as both container and user code can trigger
+ // write registration.
+ }
- private KeyAttachment ka = null;
- private SocketStatus status = null;
- public SocketProcessor(KeyAttachment ka, SocketStatus status) {
- reset(ka, status);
+ @Override
+ public void registerReadInterest() {
+ getPoller().add(getSocket(), SelectionKey.OP_READ);
}
- public void reset(KeyAttachment ka, SocketStatus status) {
- this.ka = ka;
- this.status = status;
+
+ @Override
+ public void registerWriteInterest() {
+ getPoller().add(getSocket(), SelectionKey.OP_WRITE);
}
+
@Override
- public void run() {
- NioChannel socket = ka.getSocket();
-
- // Upgraded connections need to allow multiple threads to access the
- // connection at the same time to enable blocking IO to be used when
- // NIO has been configured
- if (ka.isUpgraded() && SocketStatus.OPEN_WRITE == status) {
- synchronized (ka.getWriteThreadLock()) {
- doRun();
+ public SendfileDataBase createSendfileData(String filename, long pos, long length) {
+ return new SendfileData(filename, pos, length);
+ }
+
+
+ @Override
+ public SendfileState processSendfile(SendfileDataBase sendfileData) {
+ setSendfileData((SendfileData) sendfileData);
+ SelectionKey key = getSocket().getIOChannel().keyFor(
+ getSocket().getPoller().getSelector());
+ // Might as well do the first write on this thread
+ return getSocket().getPoller().processSendfile(key, this, true);
+ }
+
+
+ @Override
+ protected void populateRemoteAddr() {
+ InetAddress inetAddr = getSocket().getIOChannel().socket().getInetAddress();
+ if (inetAddr != null) {
+ remoteAddr = inetAddr.getHostAddress();
+ }
+ }
+
+
+ @Override
+ protected void populateRemoteHost() {
+ InetAddress inetAddr = getSocket().getIOChannel().socket().getInetAddress();
+ if (inetAddr != null) {
+ remoteHost = inetAddr.getHostName();
+ if (remoteAddr == null) {
+ remoteAddr = inetAddr.getHostAddress();
}
+ }
+ }
+
+
+ @Override
+ protected void populateRemotePort() {
+ remotePort = getSocket().getIOChannel().socket().getPort();
+ }
+
+
+ @Override
+ protected void populateLocalName() {
+ InetAddress inetAddr = getSocket().getIOChannel().socket().getLocalAddress();
+ if (inetAddr != null) {
+ localName = inetAddr.getHostName();
+ }
+ }
+
+
+ @Override
+ protected void populateLocalAddr() {
+ InetAddress inetAddr = getSocket().getIOChannel().socket().getLocalAddress();
+ if (inetAddr != null) {
+ localAddr = inetAddr.getHostAddress();
+ }
+ }
+
+
+ @Override
+ protected void populateLocalPort() {
+ localPort = getSocket().getIOChannel().socket().getLocalPort();
+ }
+
+
+ /**
+ * {@inheritDoc}
+ * @param clientCertProvider Ignored for this implementation
+ */
+ @Override
+ public SSLSupport getSslSupport(String clientCertProvider) {
+ if (getSocket() instanceof SecureNioChannel) {
+ SecureNioChannel ch = (SecureNioChannel) getSocket();
+ SSLSession session = ch.getSslEngine().getSession();
+ return ((NioEndpoint) getEndpoint()).getSslImplementation().getSSLSupport(session);
} else {
- synchronized (socket) {
- doRun();
+ return null;
+ }
+ }
+
+
+ @Override
+ public void doClientAuth(SSLSupport sslSupport) {
+ SecureNioChannel sslChannel = (SecureNioChannel) getSocket();
+ SSLEngine engine = sslChannel.getSslEngine();
+ if (!engine.getNeedClientAuth()) {
+ // Need to re-negotiate SSL connection
+ engine.setNeedClientAuth(true);
+ try {
+ sslChannel.rehandshake(getEndpoint().getSoTimeout());
+ ((JSSESupport) sslSupport).setSession(engine.getSession());
+ } catch (IOException ioe) {
+ log.warn(sm.getString("socket.sslreneg",ioe));
}
}
}
- private void doRun() {
- NioChannel socket = ka.getSocket();
+
+ @Override
+ public void setAppReadBufHandler(ApplicationBufferHandler handler) {
+ getSocket().setAppReadBufHandler(handler);
+ }
+ }
+
+
+ // ---------------------------------------------- SocketProcessor Inner Class
+
+ /**
+ * This class is the equivalent of the Worker, but will simply use in an
+ * external Executor thread pool.
+ */
+ protected class SocketProcessor extends SocketProcessorBase<NioChannel> {
+
+ public SocketProcessor(SocketWrapperBase<NioChannel> socketWrapper, SocketEvent event) {
+ super(socketWrapper, event);
+ }
+
+ @Override
+ protected void doRun() {
+ NioChannel socket = socketWrapper.getSocket();
SelectionKey key = socket.getIOChannel().keyFor(socket.getPoller().getSelector());
try {
@@ -1487,14 +1419,17 @@ public class NioEndpoint extends AbstractEndpoint<NioChannel> {
try {
if (key != null) {
- // For STOP there is no point trying to handshake as the
- // Poller has been stopped.
- if (socket.isHandshakeComplete() ||
- status == SocketStatus.STOP) {
+ if (socket.isHandshakeComplete()) {
+ // No TLS handshaking required. Let the handler
+ // process this socket / event combination.
handshake = 0;
+ } else if (event == SocketEvent.STOP || event == SocketEvent.DISCONNECT ||
+ event == SocketEvent.ERROR) {
+ // Unable to complete the TLS handshake. Treat it as
+ // if the handshake failed.
+ handshake = -1;
} else {
- handshake = socket.handshake(
- key.isReadable(), key.isWritable());
+ handshake = socket.handshake(key.isReadable(), key.isWritable());
// The handshake process reads/writes from/to the
// socket. status may therefore be OPEN_WRITE once
// the handshake completes. However, the handshake
@@ -1502,7 +1437,7 @@ public class NioEndpoint extends AbstractEndpoint<NioChannel> {
// must always be OPEN_READ after it completes. It
// is OK to always set this as it is only used if
// the handshake completes.
- status = SocketStatus.OPEN_READ;
+ event = SocketEvent.OPEN_READ;
}
}
} catch (IOException x) {
@@ -1514,82 +1449,49 @@ public class NioEndpoint extends AbstractEndpoint<NioChannel> {
if (handshake == 0) {
SocketState state = SocketState.OPEN;
// Process the request from this socket
- if (status == null) {
- state = handler.process(ka, SocketStatus.OPEN_READ);
+ if (event == null) {
+ state = getHandler().process(socketWrapper, SocketEvent.OPEN_READ);
} else {
- state = handler.process(ka, status);
+ state = getHandler().process(socketWrapper, event);
}
if (state == SocketState.CLOSED) {
- close(socket, key, SocketStatus.ERROR);
+ close(socket, key);
}
} else if (handshake == -1 ) {
- close(socket, key, SocketStatus.DISCONNECT);
- } else {
- ka.getPoller().add(socket,handshake);
+ close(socket, key);
+ } else if (handshake == SelectionKey.OP_READ){
+ socketWrapper.registerReadInterest();
+ } else if (handshake == SelectionKey.OP_WRITE){
+ socketWrapper.registerWriteInterest();
}
} catch (CancelledKeyException cx) {
- socket.getPoller().cancelledKey(key, null);
- } catch (OutOfMemoryError oom) {
- try {
- oomParachuteData = null;
- log.error("", oom);
- socket.getPoller().cancelledKey(key,SocketStatus.ERROR);
- releaseCaches();
- } catch (Throwable oomt) {
- try {
- System.err.println(oomParachuteMsg);
- oomt.printStackTrace();
- } catch (Throwable letsHopeWeDontGetHere){
- ExceptionUtils.handleThrowable(letsHopeWeDontGetHere);
- }
- }
+ socket.getPoller().cancelledKey(key);
} catch (VirtualMachineError vme) {
ExceptionUtils.handleThrowable(vme);
} catch (Throwable t) {
log.error("", t);
- socket.getPoller().cancelledKey(key,SocketStatus.ERROR);
+ socket.getPoller().cancelledKey(key);
} finally {
- ka = null;
- status = null;
+ socketWrapper = null;
+ event = null;
//return to cache
if (running && !paused) {
processorCache.push(this);
}
}
}
-
- private void close(NioChannel socket, SelectionKey key, SocketStatus socketStatus) {
- // Close socket and pool
- try {
- ka.setComet(false);
- if (socket.getPoller().cancelledKey(key, socketStatus) != null) {
- // SocketWrapper (attachment) was removed from the
- // key - recycle the key. This can only happen once
- // per attempted closure so it is used to determine
- // whether or not to return the key to the cache.
- // We do NOT want to do this more than once - see BZ
- // 57340 / 57943.
- if (running && !paused) {
- nioChannels.push(socket);
- }
- }
- } catch (Exception x) {
- log.error("",x);
- }
- }
}
// ----------------------------------------------- SendfileData Inner Class
/**
* SendfileData class.
*/
- public static class SendfileData {
- // File
- public volatile String fileName;
- public volatile FileChannel fchannel;
- public volatile long pos;
- public volatile long length;
- // KeepAlive flag
- public volatile boolean keepAlive;
+ public static class SendfileData extends SendfileDataBase {
+
+ public SendfileData(String filename, long pos, long length) {
+ super(filename, pos, length);
+ }
+
+ protected volatile FileChannel fchannel;
}
}
diff --git a/java/org/apache/tomcat/util/net/NioSelectorPool.java b/java/org/apache/tomcat/util/net/NioSelectorPool.java
index 304d472..aa282cf 100644
--- a/java/org/apache/tomcat/util/net/NioSelectorPool.java
+++ b/java/org/apache/tomcat/util/net/NioSelectorPool.java
@@ -63,12 +63,7 @@ public class NioSelectorPool {
if (SHARED && SHARED_SELECTOR == null) {
synchronized ( NioSelectorPool.class ) {
if ( SHARED_SELECTOR == null ) {
- synchronized (Selector.class) {
- // Selector.open() isn't thread safe
- // http://bugs.sun.com/view_bug.do?bug_id=6427854
- // Affects 1.6.0_29, fixed in 1.7.0_01
- SHARED_SELECTOR = Selector.open();
- }
+ SHARED_SELECTOR = Selector.open();
log.info("Using a shared selector for servlet write/read");
}
}
@@ -88,23 +83,13 @@ public class NioSelectorPool {
try {
s = selectors.size()>0?selectors.poll():null;
if (s == null) {
- synchronized (Selector.class) {
- // Selector.open() isn't thread safe
- // http://bugs.sun.com/view_bug.do?bug_id=6427854
- // Affects 1.6.0_29, fixed in 1.7.0_01
- s = Selector.open();
- }
+ s = Selector.open();
}
else spare.decrementAndGet();
}catch (NoSuchElementException x ) {
try {
- synchronized (Selector.class) {
- // Selector.open() isn't thread safe
- // http://bugs.sun.com/view_bug.do?bug_id=6427854
- // Affects 1.6.0_29, fixed in 1.7.0_01
- s = Selector.open();
- }
+ s = Selector.open();
} catch (IOException iox) {
}
} finally {
@@ -259,7 +244,12 @@ public class NioSelectorPool {
int cnt = 0;
if ( keycount > 0 ) { //only read if we were registered for a read
cnt = socket.read(buf);
- if (cnt == -1) throw new EOFException();
+ if (cnt == -1) {
+ if (read == 0) {
+ read = -1;
+ }
+ break;
+ }
read += cnt;
if (cnt > 0) continue; //read some more
if (cnt==0 && (read>0 || (!block) ) ) break; //we are done reading
diff --git a/java/org/apache/tomcat/util/net/SSLContext.java b/java/org/apache/tomcat/util/net/SSLContext.java
new file mode 100644
index 0000000..57cacab
--- /dev/null
+++ b/java/org/apache/tomcat/util/net/SSLContext.java
@@ -0,0 +1,50 @@
+/*
+ * 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.KeyManagementException;
+import java.security.SecureRandom;
+
+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;
+
+/**
+ * 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 {
+
+ public void init(KeyManager[] kms, TrustManager[] tms,
+ SecureRandom sr) throws KeyManagementException;
+
+ public void destroy();
+
+ public SSLSessionContext getServerSessionContext();
+
+ public SSLEngine createSSLEngine();
+
+ public SSLServerSocketFactory getServerSocketFactory();
+
+ public SSLParameters getSupportedSSLParameters();
+
+}
diff --git a/java/org/apache/tomcat/util/net/SSLHostConfig.java b/java/org/apache/tomcat/util/net/SSLHostConfig.java
new file mode 100644
index 0000000..689462f
--- /dev/null
+++ b/java/org/apache/tomcat/util/net/SSLHostConfig.java
@@ -0,0 +1,787 @@
+/*
+ * 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.IOException;
+import java.io.Serializable;
+import java.security.KeyStore;
+import java.security.UnrecoverableKeyException;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.TrustManagerFactory;
+
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
+import org.apache.tomcat.util.compat.JreCompat;
+import org.apache.tomcat.util.net.openssl.ciphers.Cipher;
+import org.apache.tomcat.util.net.openssl.ciphers.OpenSSLCipherConfigurationParser;
+import org.apache.tomcat.util.res.StringManager;
+
+/**
+ * Represents the TLS configuration for a virtual host.
+ */
+public class SSLHostConfig implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ private static final Log log = LogFactory.getLog(SSLHostConfig.class);
+ private static final StringManager sm = StringManager.getManager(SSLHostConfig.class);
+
+ private static final String DEFAULT_CIPHERS = "HIGH:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!kRSA";
+
+ protected static final String DEFAULT_SSL_HOST_NAME = "_default_";
+ protected static final Set<String> SSL_PROTO_ALL_SET = new HashSet<>();
+
+ static {
+ /* Default used if protocols is not configured, also
+ used if protocols="All" */
+ /* If protocols is configured to be empty, the effective
+ value comes from
+ org.apache.tomcat.util.net.jsse.JSSESocketFactory.defaultServerProtocols
+ (JSSE) resp. org.apache.tomcat.jni.SSL.SSL_PROTOCOL_ALL (OpenSSL)*/
+ SSL_PROTO_ALL_SET.add(Constants.SSL_PROTO_SSLv2Hello);
+ SSL_PROTO_ALL_SET.add(Constants.SSL_PROTO_TLSv1);
+ SSL_PROTO_ALL_SET.add(Constants.SSL_PROTO_TLSv1_1);
+ SSL_PROTO_ALL_SET.add(Constants.SSL_PROTO_TLSv1_2);
+ }
+
+ private Type configType = null;
+ private Type currentConfigType = null;
+ private Map<Type,Set<String>> configuredProperties = new HashMap<>();
+
+ private String hostName = DEFAULT_SSL_HOST_NAME;
+
+ // OpenSSL can handle multiple certs in a single config so the reference to
+ // the context is here at the virtual host level. JSSE can't so the
+ // reference is held on the certificate.
+ private transient Long openSslContext;
+
+ // Configuration properties
+
+ // Internal
+ private String[] enabledCiphers;
+ private String[] enabledProtocols;
+ // Nested
+ private SSLHostConfigCertificate defaultCertificate = null;
+ private Set<SSLHostConfigCertificate> certificates = new HashSet<>(4);
+ // Common
+ private String certificateRevocationListFile;
+ private CertificateVerification certificateVerification = CertificateVerification.NONE;
+ private int certificateVerificationDepth = 10;
+ private String ciphers;
+ private LinkedHashSet<Cipher> cipherList = null;
+ private List<String> jsseCipherNames = null;
+ private String honorCipherOrder = null;
+ private Set<String> protocols = new HashSet<>();
+ // JSSE
+ private String keyManagerAlgorithm = KeyManagerFactory.getDefaultAlgorithm();
+ private int sessionCacheSize = 0;
+ private int sessionTimeout = 86400;
+ private String sslProtocol = Constants.SSL_PROTO_TLS;
+ private String trustManagerClassName;
+ private String truststoreAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
+ private String truststoreFile = System.getProperty("javax.net.ssl.trustStore");
+ private String truststorePassword = System.getProperty("javax.net.ssl.trustStorePassword");
+ private String truststoreProvider = System.getProperty("javax.net.ssl.trustStoreProvider");
+ private String truststoreType = System.getProperty("javax.net.ssl.trustStoreType");
+ private transient KeyStore truststore = null;
+ // OpenSSL
+ private String certificateRevocationListPath;
+ private String caCertificateFile;
+ private String caCertificatePath;
+ private boolean disableCompression = true;
+ private boolean disableSessionTickets = false;
+ private boolean insecureRenegotiation = false;
+
+ public SSLHostConfig() {
+ // Set defaults that can't be (easily) set when defining the fields.
+ setProtocols(Constants.SSL_PROTO_ALL);
+ }
+
+
+ public Long getOpenSslContext() {
+ return openSslContext;
+ }
+
+
+ public void setOpenSslContext(Long openSslContext) {
+ this.openSslContext = openSslContext;
+ }
+
+
+ public void setConfigType(Type configType) {
+ this.configType = configType;
+ if (configType == Type.EITHER) {
+ if (configuredProperties.remove(Type.JSSE) == null) {
+ configuredProperties.remove(Type.OPENSSL);
+ }
+ } else {
+ configuredProperties.remove(configType);
+ }
+ for (Map.Entry<Type,Set<String>> entry : configuredProperties.entrySet()) {
+ for (String property : entry.getValue()) {
+ log.warn(sm.getString("sslHostConfig.mismatch",
+ property, getHostName(), entry.getKey(), configType));
+ }
+ }
+ }
+
+
+ void setProperty(String name, Type configType) {
+ if (this.configType == null) {
+ Set<String> properties = configuredProperties.get(configType);
+ if (properties == null) {
+ properties = new HashSet<>();
+ configuredProperties.put(configType, properties);
+ }
+ properties.add(name);
+ } else if (this.configType == Type.EITHER) {
+ if (currentConfigType == null) {
+ currentConfigType = configType;
+ } else if (currentConfigType != configType) {
+ log.warn(sm.getString("sslHostConfig.mismatch",
+ name, getHostName(), configType, currentConfigType));
+ }
+ } else {
+ if (configType != this.configType) {
+ log.warn(sm.getString("sslHostConfig.mismatch",
+ name, getHostName(), configType, this.configType));
+ }
+ }
+ }
+
+
+ // ----------------------------------------------------- Internal properties
+
+ /**
+ * @see SSLUtil#getEnabledProtocols()
+ *
+ * @return The protocols enabled for this TLS virtual host
+ */
+ public String[] getEnabledProtocols() {
+ return enabledProtocols;
+ }
+
+
+ void setEnabledProtocols(String[] enabledProtocols) {
+ this.enabledProtocols = enabledProtocols;
+ }
+
+
+ /**
+ * @see SSLUtil#getEnabledCiphers()
+ *
+ * @return The ciphers enabled for this TLS virtual host
+ */
+ public String[] getEnabledCiphers() {
+ return enabledCiphers;
+ }
+
+
+ void setEnabledCiphers(String[] enabledCiphers) {
+ this.enabledCiphers = enabledCiphers;
+ }
+
+
+ // ------------------------------------------- Nested configuration elements
+
+ private void registerDefaultCertificate() {
+ if (defaultCertificate == null) {
+ defaultCertificate = new SSLHostConfigCertificate(
+ this, SSLHostConfigCertificate.Type.UNDEFINED);
+ certificates.add(defaultCertificate);
+ }
+ }
+
+
+ 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) {
+ certificates.add(certificate);
+ return;
+ }
+
+ if (certificates.size() == 1 &&
+ certificates.iterator().next().getType() == SSLHostConfigCertificate.Type.UNDEFINED ||
+ certificate.getType() == SSLHostConfigCertificate.Type.UNDEFINED) {
+ // Invalid config
+ throw new IllegalArgumentException(sm.getString("sslHostConfig.certificate.notype"));
+ }
+
+ certificates.add(certificate);
+ }
+
+
+ public Set<SSLHostConfigCertificate> getCertificates() {
+ return getCertificates(false);
+ }
+
+
+ public Set<SSLHostConfigCertificate> getCertificates(boolean createDefaultIfEmpty) {
+ if (certificates.size() == 0 && createDefaultIfEmpty) {
+ registerDefaultCertificate();
+ }
+ return certificates;
+ }
+
+
+ // ----------------------------------------- Common configuration properties
+
+ // TODO: This certificate setter can be removed once it is no longer
+ // necessary to support the old configuration attributes (Tomcat 10?).
+
+ public void setCertificateKeyPassword(String certificateKeyPassword) {
+ registerDefaultCertificate();
+ defaultCertificate.setCertificateKeyPassword(certificateKeyPassword);
+ }
+
+
+ public void setCertificateRevocationListFile(String certificateRevocationListFile) {
+ this.certificateRevocationListFile = certificateRevocationListFile;
+ }
+
+
+ public String getCertificateRevocationListFile() {
+ return certificateRevocationListFile;
+ }
+
+
+ public void setCertificateVerification(String certificateVerification) {
+ this.certificateVerification = CertificateVerification.fromString(certificateVerification);
+ }
+
+
+ public CertificateVerification getCertificateVerification() {
+ return certificateVerification;
+ }
+
+
+ public void setCertificateVerificationDepth(int certificateVerificationDepth) {
+ this.certificateVerificationDepth = certificateVerificationDepth;
+ }
+
+
+ public int getCertificateVerificationDepth() {
+ return certificateVerificationDepth;
+ }
+
+
+ /**
+ * Set the new cipher 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 (sb.length() > 0) {
+ sb.append(':');
+ }
+ sb.append(openSSLName);
+ }
+ }
+ this.ciphers = sb.toString();
+ } else {
+ this.ciphers = ciphersList;
+ }
+ this.cipherList = null;
+ this.jsseCipherNames = null;
+ }
+
+
+ /**
+ * @return An OpenSSL cipher string for the current configuration.
+ */
+ public String getCiphers() {
+ if (ciphers == null) {
+ if (!JreCompat.isJre8Available() && Type.JSSE.equals(configType)) {
+ ciphers = DEFAULT_CIPHERS + ":!DHE";
+ } else {
+ ciphers = DEFAULT_CIPHERS;
+ }
+
+ }
+ return ciphers;
+ }
+
+
+ public LinkedHashSet<Cipher> getCipherList() {
+ if (cipherList == null) {
+ cipherList = OpenSSLCipherConfigurationParser.parse(getCiphers());
+ }
+ return cipherList;
+ }
+
+
+ /**
+ * 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<String> getJsseCipherNames() {
+ if (jsseCipherNames == null) {
+ jsseCipherNames = OpenSSLCipherConfigurationParser.convertForJSSE(getCipherList());
+ }
+ return jsseCipherNames;
+ }
+
+
+ public void setHonorCipherOrder(String honorCipherOrder) {
+ this.honorCipherOrder = honorCipherOrder;
+ }
+
+
+ public String getHonorCipherOrder() {
+ return honorCipherOrder;
+ }
+
+
+ public void setHostName(String hostName) {
+ this.hostName = hostName;
+ }
+
+
+ public String getHostName() {
+ return hostName;
+ }
+
+
+ public void setProtocols(String input) {
+ protocols.clear();
+
+ // List of protocol names, separated by ",", "+" or "-".
+ // Semantics is adding ("+") or removing ("-") from left
+ // to right, starting with an empty protocol set.
+ // Tokens are individual protocol names or "all" for a
+ // default set of supported protocols.
+ // Separator "," is only kept for compatibility and has the
+ // same semantics as "+", except that it warns about a potentially
+ // missing "+" or "-".
+
+ // 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("(?=[-+,])")) {
+ String trimmed = value.trim();
+ // Ignore token which only consists of prefix character
+ if (trimmed.length() > 1) {
+ if (trimmed.charAt(0) == '+') {
+ trimmed = trimmed.substring(1).trim();
+ if (trimmed.equalsIgnoreCase(Constants.SSL_PROTO_ALL)) {
+ protocols.addAll(SSL_PROTO_ALL_SET);
+ } else {
+ protocols.add(trimmed);
+ }
+ } else if (trimmed.charAt(0) == '-') {
+ trimmed = trimmed.substring(1).trim();
+ if (trimmed.equalsIgnoreCase(Constants.SSL_PROTO_ALL)) {
+ protocols.removeAll(SSL_PROTO_ALL_SET);
+ } else {
+ protocols.remove(trimmed);
+ }
+ } else {
+ if (trimmed.charAt(0) == ',') {
+ trimmed = trimmed.substring(1).trim();
+ }
+ if (!protocols.isEmpty()) {
+ log.warn(sm.getString("sslHostConfig.prefix_missing",
+ trimmed, getHostName()));
+ }
+ if (trimmed.equalsIgnoreCase(Constants.SSL_PROTO_ALL)) {
+ protocols.addAll(SSL_PROTO_ALL_SET);
+ } else {
+ protocols.add(trimmed);
+ }
+ }
+ }
+ }
+ }
+
+
+ public Set<String> getProtocols() {
+ return protocols;
+ }
+
+
+ // ---------------------------------- JSSE specific configuration properties
+
+ // TODO: These certificate setters can be removed once it is no longer
+ // necessary to support the old configuration attributes (Tomcat 10?).
+
+ public void setCertificateKeyAlias(String certificateKeyAlias) {
+ registerDefaultCertificate();
+ defaultCertificate.setCertificateKeyAlias(certificateKeyAlias);
+ }
+
+
+ public void setCertificateKeystoreFile(String certificateKeystoreFile) {
+ registerDefaultCertificate();
+ defaultCertificate.setCertificateKeystoreFile(certificateKeystoreFile);
+ }
+
+
+ public void setCertificateKeystorePassword(String certificateKeystorePassword) {
+ registerDefaultCertificate();
+ defaultCertificate.setCertificateKeystorePassword(certificateKeystorePassword);
+ }
+
+
+ public void setCertificateKeystoreProvider(String certificateKeystoreProvider) {
+ registerDefaultCertificate();
+ defaultCertificate.setCertificateKeystoreProvider(certificateKeystoreProvider);
+ }
+
+
+ public void setCertificateKeystoreType(String certificateKeystoreType) {
+ registerDefaultCertificate();
+ defaultCertificate.setCertificateKeystoreType(certificateKeystoreType);
+ }
+
+
+ public void setKeyManagerAlgorithm(String keyManagerAlgorithm) {
+ setProperty("keyManagerAlgorithm", Type.JSSE);
+ this.keyManagerAlgorithm = keyManagerAlgorithm;
+ }
+
+
+ public String getKeyManagerAlgorithm() {
+ return keyManagerAlgorithm;
+ }
+
+
+ public void setSessionCacheSize(int sessionCacheSize) {
+ setProperty("sessionCacheSize", Type.JSSE);
+ this.sessionCacheSize = sessionCacheSize;
+ }
+
+
+ public int getSessionCacheSize() {
+ return sessionCacheSize;
+ }
+
+
+ public void setSessionTimeout(int sessionTimeout) {
+ setProperty("sessionTimeout", Type.JSSE);
+ this.sessionTimeout = sessionTimeout;
+ }
+
+
+ public int getSessionTimeout() {
+ return sessionTimeout;
+ }
+
+
+ public void setSslProtocol(String sslProtocol) {
+ setProperty("sslProtocol", Type.JSSE);
+ this.sslProtocol = sslProtocol;
+ }
+
+
+ public String getSslProtocol() {
+ return sslProtocol;
+ }
+
+
+ public void setTrustManagerClassName(String trustManagerClassName) {
+ setProperty("trustManagerClassName", Type.JSSE);
+ this.trustManagerClassName = trustManagerClassName;
+ }
+
+
+ public String getTrustManagerClassName() {
+ return trustManagerClassName;
+ }
+
+
+ public void setTruststoreAlgorithm(String truststoreAlgorithm) {
+ setProperty("truststoreAlgorithm", Type.JSSE);
+ this.truststoreAlgorithm = truststoreAlgorithm;
+ }
+
+
+ public String getTruststoreAlgorithm() {
+ return truststoreAlgorithm;
+ }
+
+
+ public void setTruststoreFile(String truststoreFile) {
+ setProperty("truststoreFile", Type.JSSE);
+ this.truststoreFile = truststoreFile;
+ }
+
+
+ public String getTruststoreFile() {
+ return truststoreFile;
+ }
+
+
+ public void setTruststorePassword(String truststorePassword) {
+ setProperty("truststorePassword", Type.JSSE);
+ this.truststorePassword = truststorePassword;
+ }
+
+
+ public String getTruststorePassword() {
+ return truststorePassword;
+ }
+
+
+ public void setTruststoreProvider(String truststoreProvider) {
+ setProperty("truststoreProvider", Type.JSSE);
+ this.truststoreProvider = truststoreProvider;
+ }
+
+
+ public String getTruststoreProvider() {
+ if (truststoreProvider == null) {
+ if (defaultCertificate == null) {
+ return SSLHostConfigCertificate.DEFAULT_KEYSTORE_PROVIDER;
+ } else {
+ return defaultCertificate.getCertificateKeystoreProvider();
+ }
+ } else {
+ return truststoreProvider;
+ }
+ }
+
+
+ public void setTruststoreType(String truststoreType) {
+ setProperty("truststoreType", Type.JSSE);
+ this.truststoreType = truststoreType;
+ }
+
+
+ public String getTruststoreType() {
+ if (truststoreType == null) {
+ if (defaultCertificate == null) {
+ return SSLHostConfigCertificate.DEFAULT_KEYSTORE_TYPE;
+ } else {
+ return defaultCertificate.getCertificateKeystoreType();
+ }
+ } else {
+ return truststoreType;
+ }
+ }
+
+
+ public void setTrustStore(KeyStore truststore) {
+ this.truststore = truststore;
+ }
+
+
+ public KeyStore getTruststore() throws IOException {
+ KeyStore result = truststore;
+ if (result == null) {
+ if (truststoreFile != null){
+ try {
+ result = SSLUtilBase.getStore(getTruststoreType(), getTruststoreProvider(),
+ getTruststoreFile(), getTruststorePassword());
+ } catch (IOException ioe) {
+ Throwable cause = ioe.getCause();
+ if (cause instanceof UnrecoverableKeyException) {
+ // Log a warning we had a password issue
+ log.warn(sm.getString("jsse.invalid_truststore_password"),
+ cause);
+ // Re-try
+ result = SSLUtilBase.getStore(getTruststoreType(), getTruststoreProvider(),
+ getTruststoreFile(), null);
+ } else {
+ // Something else went wrong - re-throw
+ throw ioe;
+ }
+ }
+ }
+ }
+ return result;
+ }
+
+
+ // ------------------------------- OpenSSL specific configuration properties
+
+ // TODO: These certificate setters can be removed once it is no longer
+ // necessary to support the old configuration attributes (Tomcat 10?).
+
+ public void setCertificateChainFile(String certificateChainFile) {
+ registerDefaultCertificate();
+ defaultCertificate.setCertificateChainFile(certificateChainFile);
+ }
+
+
+ public void setCertificateFile(String certificateFile) {
+ registerDefaultCertificate();
+ defaultCertificate.setCertificateFile(certificateFile);
+ }
+
+
+ public void setCertificateKeyFile(String certificateKeyFile) {
+ registerDefaultCertificate();
+ defaultCertificate.setCertificateKeyFile(certificateKeyFile);
+ }
+
+
+ public void setCertificateRevocationListPath(String certificateRevocationListPath) {
+ setProperty("certificateRevocationListPath", Type.OPENSSL);
+ this.certificateRevocationListPath = certificateRevocationListPath;
+ }
+
+
+ public String getCertificateRevocationListPath() {
+ return certificateRevocationListPath;
+ }
+
+
+ public void setCaCertificateFile(String caCertificateFile) {
+ setProperty("caCertificateFile", Type.OPENSSL);
+ this.caCertificateFile = caCertificateFile;
+ }
+
+
+ public String getCaCertificateFile() {
+ return caCertificateFile;
+ }
+
+
+ public void setCaCertificatePath(String caCertificatePath) {
+ setProperty("caCertificatePath", Type.OPENSSL);
+ this.caCertificatePath = caCertificatePath;
+ }
+
+
+ public String getCaCertificatePath() {
+ return caCertificatePath;
+ }
+
+
+ public void setDisableCompression(boolean disableCompression) {
+ setProperty("disableCompression", Type.OPENSSL);
+ this.disableCompression = disableCompression;
+ }
+
+
+ public boolean getDisableCompression() {
+ return disableCompression;
+ }
+
+
+ public void setDisableSessionTickets(boolean disableSessionTickets) {
+ setProperty("disableSessionTickets", Type.OPENSSL);
+ this.disableSessionTickets = disableSessionTickets;
+ }
+
+
+ public boolean getDisableSessionTickets() {
+ return disableSessionTickets;
+ }
+
+
+ public void setInsecureRenegotiation(boolean insecureRenegotiation) {
+ setProperty("insecureRenegotiation", Type.OPENSSL);
+ this.insecureRenegotiation = insecureRenegotiation;
+ }
+
+
+ public boolean getInsecureRenegotiation() {
+ return insecureRenegotiation;
+ }
+
+
+ // --------------------------------------------------------- Support methods
+
+ public static String adjustRelativePath(String path) {
+ // 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) {
+ return path;
+ }
+ String newPath = path;
+ File f = new File(newPath);
+ if ( !f.isAbsolute()) {
+ newPath = System.getProperty(Constants.CATALINA_BASE_PROP) + File.separator + newPath;
+ f = new File(newPath);
+ }
+ if (!f.exists()) {
+ // TODO i18n, sm
+ log.warn("configured file:["+newPath+"] does not exist.");
+ }
+ return newPath;
+ }
+
+
+ // ----------------------------------------------------------- Inner classes
+
+ public static enum Type {
+ JSSE,
+ OPENSSL,
+ EITHER
+ }
+
+
+ public static enum CertificateVerification {
+ NONE,
+ OPTIONAL_NO_CA,
+ OPTIONAL,
+ REQUIRED;
+
+ public static CertificateVerification fromString(String value) {
+ if ("true".equalsIgnoreCase(value) ||
+ "yes".equalsIgnoreCase(value) ||
+ "require".equalsIgnoreCase(value) ||
+ "required".equalsIgnoreCase(value)) {
+ return REQUIRED;
+ } else if ("optional".equalsIgnoreCase(value) ||
+ "want".equalsIgnoreCase(value)) {
+ return OPTIONAL;
+ } else if ("optionalNoCA".equalsIgnoreCase(value) ||
+ "optional_no_ca".equalsIgnoreCase(value)) {
+ return OPTIONAL_NO_CA;
+ } 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));
+ }
+ }
+ }
+}
diff --git a/java/org/apache/tomcat/util/net/SSLHostConfigCertificate.java b/java/org/apache/tomcat/util/net/SSLHostConfigCertificate.java
new file mode 100644
index 0000000..ec4217e
--- /dev/null
+++ b/java/org/apache/tomcat/util/net/SSLHostConfigCertificate.java
@@ -0,0 +1,269 @@
+/*
+ * 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.IOException;
+import java.io.Serializable;
+import java.security.KeyStore;
+import java.util.HashSet;
+import java.util.Set;
+
+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.res.StringManager;
+
+public class SSLHostConfigCertificate implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ private static final Log log = LogFactory.getLog(SSLHostConfigCertificate.class);
+ private static final StringManager sm = StringManager.getManager(SSLHostConfigCertificate.class);
+
+ public static final Type DEFAULT_TYPE = Type.UNDEFINED;
+
+ 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");
+
+ // 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.
+ private transient SSLContext sslContext;
+
+ // Common
+ private final SSLHostConfig sslHostConfig;
+ private final Type type;
+ private String certificateKeyPassword = null;
+
+ // JSSE
+ private String certificateKeyAlias;
+ private String certificateKeystorePassword = "changeit";
+ private String certificateKeystoreFile = System.getProperty("user.home")+"/.keystore";
+ private String certificateKeystoreProvider = DEFAULT_KEYSTORE_PROVIDER;
+ private String certificateKeystoreType = DEFAULT_KEYSTORE_TYPE;
+ private transient KeyStore certificateKeystore = null;
+
+ // OpenSSL
+ private String certificateChainFile;
+ private String certificateFile;
+ private String certificateKeyFile;
+
+ // Certificate store type
+ private StoreType storeType = null;
+
+ public SSLHostConfigCertificate() {
+ this(null, Type.UNDEFINED);
+ }
+
+ public SSLHostConfigCertificate(SSLHostConfig sslHostConfig, Type type) {
+ this.sslHostConfig = sslHostConfig;
+ this.type = type;
+ }
+
+
+ public SSLContext getSslContext() {
+ return sslContext;
+ }
+
+
+ public void setSslContext(SSLContext sslContext) {
+ this.sslContext = sslContext;
+ }
+
+
+ public SSLHostConfig getSSLHostConfig() {
+ return sslHostConfig;
+ }
+
+
+ // Common
+
+ public Type getType() {
+ return type;
+ }
+
+
+ public String getCertificateKeyPassword() {
+ return certificateKeyPassword;
+ }
+
+
+ public void setCertificateKeyPassword(String certificateKeyPassword) {
+ this.certificateKeyPassword = certificateKeyPassword;
+ }
+
+
+ // JSSE
+
+ public void setCertificateKeyAlias(String certificateKeyAlias) {
+ sslHostConfig.setProperty(
+ "Certificate.certificateKeyAlias", SSLHostConfig.Type.JSSE);
+ this.certificateKeyAlias = certificateKeyAlias;
+ }
+
+
+ public String getCertificateKeyAlias() {
+ return certificateKeyAlias;
+ }
+
+
+ public void setCertificateKeystoreFile(String certificateKeystoreFile) {
+ sslHostConfig.setProperty(
+ "Certificate.certificateKeystoreFile", SSLHostConfig.Type.JSSE);
+ setStoreType("Certificate.certificateKeystoreFile", StoreType.KEYSTORE);
+ this.certificateKeystoreFile = certificateKeystoreFile;
+ }
+
+
+ public String getCertificateKeystoreFile() {
+ return certificateKeystoreFile;
+ }
+
+
+ public void setCertificateKeystorePassword(String certificateKeystorePassword) {
+ sslHostConfig.setProperty(
+ "Certificate.certificateKeystorePassword", SSLHostConfig.Type.JSSE);
+ setStoreType("Certificate.certificateKeystorePassword", StoreType.KEYSTORE);
+ this.certificateKeystorePassword = certificateKeystorePassword;
+ }
+
+
+ public String getCertificateKeystorePassword() {
+ return certificateKeystorePassword;
+ }
+
+
+ public void setCertificateKeystoreProvider(String certificateKeystoreProvider) {
+ sslHostConfig.setProperty(
+ "Certificate.certificateKeystoreProvider", SSLHostConfig.Type.JSSE);
+ setStoreType("Certificate.certificateKeystoreProvider", StoreType.KEYSTORE);
+ this.certificateKeystoreProvider = certificateKeystoreProvider;
+ }
+
+
+ public String getCertificateKeystoreProvider() {
+ return certificateKeystoreProvider;
+ }
+
+
+ public void setCertificateKeystoreType(String certificateKeystoreType) {
+ sslHostConfig.setProperty(
+ "Certificate.certificateKeystoreType", SSLHostConfig.Type.JSSE);
+ setStoreType("Certificate.certificateKeystoreType", StoreType.KEYSTORE);
+ this.certificateKeystoreType = certificateKeystoreType;
+ }
+
+
+ public String getCertificateKeystoreType() {
+ return certificateKeystoreType;
+ }
+
+
+ public void setCertificateKeystore(KeyStore certificateKeystore) {
+ this.certificateKeystore = certificateKeystore;
+ }
+
+
+ public KeyStore getCertificateKeystore() throws IOException {
+ KeyStore result = certificateKeystore;
+
+ if (result == null && storeType == StoreType.KEYSTORE) {
+ result = SSLUtilBase.getStore(getCertificateKeystoreType(),
+ getCertificateKeystoreProvider(), getCertificateKeystoreFile(),
+ getCertificateKeystorePassword());
+ }
+
+ return result;
+ }
+
+
+ // OpenSSL
+
+ public void setCertificateChainFile(String certificateChainFile) {
+ setStoreType("Certificate.certificateChainFile", StoreType.PEM);
+ this.certificateChainFile = certificateChainFile;
+ }
+
+
+ public String getCertificateChainFile() {
+ return certificateChainFile;
+ }
+
+
+ public void setCertificateFile(String certificateFile) {
+ setStoreType("Certificate.certificateFile", StoreType.PEM);
+ this.certificateFile = certificateFile;
+ }
+
+
+ public String getCertificateFile() {
+ return certificateFile;
+ }
+
+
+ public void setCertificateKeyFile(String certificateKeyFile) {
+ setStoreType("Certificate.certificateKeyFile", StoreType.PEM);
+ this.certificateKeyFile = certificateKeyFile;
+ }
+
+
+ public String getCertificateKeyFile() {
+ return certificateKeyFile;
+ }
+
+
+ private void setStoreType(String name, StoreType type) {
+ if (storeType == null) {
+ storeType = type;
+ } else if (storeType != type) {
+ log.warn(sm.getString("sslHostConfigCertificate.mismatch",
+ name, sslHostConfig.getHostName(), type, this.storeType));
+ }
+ }
+
+ // Nested types
+
+ public static enum Type {
+
+ UNDEFINED,
+ RSA(Authentication.RSA),
+ DSA(Authentication.DSS),
+ EC(Authentication.ECDH, Authentication.ECDSA);
+
+ private final Set<Authentication> compatibleAuthentications;
+
+ private Type(Authentication... authentications) {
+ compatibleAuthentications = new HashSet<>();
+ if (authentications != null) {
+ for (Authentication authentication : authentications) {
+ compatibleAuthentications.add(authentication);
+ }
+ }
+ }
+
+ public boolean isCompatibleWith(Authentication au) {
+ return compatibleAuthentications.contains(au);
+ }
+ }
+
+ private enum StoreType {
+ KEYSTORE,
+ PEM
+ }
+}
diff --git a/java/org/apache/tomcat/util/net/SSLImplementation.java b/java/org/apache/tomcat/util/net/SSLImplementation.java
index fd9f976..7697224 100644
--- a/java/org/apache/tomcat/util/net/SSLImplementation.java
+++ b/java/org/apache/tomcat/util/net/SSLImplementation.java
@@ -17,75 +17,54 @@
package org.apache.tomcat.util.net;
-import java.net.Socket;
-
import javax.net.ssl.SSLSession;
-/* SSLImplementation:
-
- Abstract factory and base class for all SSL implementations.
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
+import org.apache.tomcat.util.net.jsse.JSSEImplementation;
+import org.apache.tomcat.util.res.StringManager;
- @author EKR
+/**
+ * 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 {
- private static final org.apache.juli.logging.Log logger = org.apache.juli.logging.LogFactory
- .getLog(SSLImplementation.class);
-
- // The default implementations in our search path
- private static final String JSSEImplementationClass =
- "org.apache.tomcat.util.net.jsse.JSSEImplementation";
-
- private static final String[] implementations = { JSSEImplementationClass };
-
- public static SSLImplementation getInstance() throws ClassNotFoundException {
- for (int i = 0; i < implementations.length; i++) {
- try {
- SSLImplementation impl = getInstance(implementations[i]);
- return impl;
- } catch (Exception e) {
- if (logger.isTraceEnabled())
- logger.trace("Error creating " + implementations[i], e);
- }
- }
-
- // If we can't instantiate any of these
- throw new ClassNotFoundException("Can't find any SSL implementation");
- }
+ private static final Log logger = LogFactory.getLog(SSLImplementation.class);
+ private static final StringManager sm = StringManager.getManager(SSLImplementation.class);
+
+ /**
+ * 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}.
+ *
+ * @return An instance of the required implementation
+ *
+ * @throws ClassNotFoundException If an instance of the requested class
+ * cannot be created
+ */
public static SSLImplementation getInstance(String className)
throws ClassNotFoundException {
if (className == null)
- return getInstance();
+ return new JSSEImplementation();
try {
- // Workaround for the J2SE 1.4.x classloading problem (under
- // Solaris).
- // Class.forName(..) fails without creating class using new.
- // This is an ugly workaround.
- if (JSSEImplementationClass.equals(className)) {
- return new org.apache.tomcat.util.net.jsse.JSSEImplementation();
- }
Class<?> clazz = Class.forName(className);
return (SSLImplementation) clazz.newInstance();
} catch (Exception e) {
- if (logger.isDebugEnabled())
- logger
- .debug("Error loading SSL Implementation " + className,
- e);
- throw new ClassNotFoundException(
- "Error loading SSL Implementation " + className + " :"
- + e.toString());
+ String msg = sm.getString("sslImplementation.cnfe", className);
+ if (logger.isDebugEnabled()) {
+ logger.debug(msg, e);
+ }
+ throw new ClassNotFoundException(msg, e);
}
}
- public abstract String getImplementationName();
-
- public abstract ServerSocketFactory getServerSocketFactory(
- AbstractEndpoint<?> endpoint);
-
- public abstract SSLSupport getSSLSupport(Socket sock);
public abstract SSLSupport getSSLSupport(SSLSession session);
- public abstract SSLUtil getSSLUtil(AbstractEndpoint<?> ep);
+ public abstract SSLUtil getSSLUtil(SSLHostConfigCertificate certificate);
}
diff --git a/java/org/apache/tomcat/util/net/SSLSupport.java b/java/org/apache/tomcat/util/net/SSLSupport.java
index 779e617..75740f9 100644
--- a/java/org/apache/tomcat/util/net/SSLSupport.java
+++ b/java/org/apache/tomcat/util/net/SSLSupport.java
@@ -64,17 +64,25 @@ public interface SSLSupport {
/**
* The cipher suite being used on this connection.
+ *
+ * @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
*/
public String getCipherSuite() throws IOException;
/**
* The client certificate chain (if any).
*
- * @param force If <code>true</code>, then re-negotiate the connection and
- * request a client certificate if a client certificate has not
- * already been requested.
+ * @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
*/
- public X509Certificate[] getPeerCertificateChain(boolean force) throws IOException;
+ public X509Certificate[] getPeerCertificateChain() throws IOException;
/**
* Get the keysize.
@@ -89,18 +97,28 @@ public interface SSLSupport {
* (d) The size of the signature key used by the server
*
* Unfortunately, all of these values are nonsensical.
- **/
- public Integer getKeySize()
- throws IOException;
+ *
+ * @return The effective key size for the current cipher suite
+ *
+ * @throws IOException If an error occurs trying to obtain the key size
+ */
+ public Integer getKeySize() throws IOException;
/**
* The current session Id.
+ *
+ * @return The current SSL/TLS session ID
+ *
+ * @throws IOException If an error occurs trying to obtain the session ID
*/
public String getSessionId() throws IOException;
/**
* @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
*/
public String getProtocol() throws IOException;
}
diff --git a/java/org/apache/tomcat/util/net/SSLUtil.java b/java/org/apache/tomcat/util/net/SSLUtil.java
index daa76c7..c65f7a2 100644
--- a/java/org/apache/tomcat/util/net/SSLUtil.java
+++ b/java/org/apache/tomcat/util/net/SSLUtil.java
@@ -16,14 +16,20 @@
*/
package org.apache.tomcat.util.net;
+import java.util.List;
+
import javax.net.ssl.KeyManager;
-import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSessionContext;
import javax.net.ssl.TrustManager;
+/**
+ * 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 {
- public SSLContext createSSLContext() throws Exception;
+ public SSLContext createSSLContext(List<String> negotiableProtocols) throws Exception;
public KeyManager[] getKeyManagers() throws Exception;
@@ -32,31 +38,45 @@ public interface SSLUtil {
public void configureSessionContext(SSLSessionContext sslSessionContext);
/**
- * Determines the SSL cipher suites that can be enabled, based on the
- * configuration of the endpoint and the ciphers supported by the SSL
- * implementation.
+ * 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.
*
- * @param context An initialized context to obtain the supported ciphers from.
+ * @return The protocols currently enabled and available for clients to
+ * select from for the associated connection
*
- * @return Array of SSL cipher suites that may be enabled (which may be
- * empty if none of the specified ciphers are supported), or
- * the defaults for the underlying SSL implementation if
- * the endpoint configuration does not specify any ciphers.
+ * @throws IllegalArgumentException If there is no intersection between the
+ * implemented and configured protocols
*/
- public String[] getEnableableCiphers(SSLContext context);
+ public String[] getEnabledProtocols() throws IllegalArgumentException;
/**
- * Determines the SSL protocol variants that can be enabled, based on the
- * configuration of the endpoint and the ciphers supported by the SSL
- * implementation.
+ * 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.
+ * <p>
+ * The ciphers used during the TLS handshake may be further restricted by
+ * the {@link #getEnabledProtocols()} and the certificates.
*
- * @param context An initialized context to obtain the supported protocols from.
+ * @return The ciphers currently enabled and available for clients to select
+ * from for the associated connection
*
- * @return Array of SSL protocol variants that may be enabled (which may be
- * empty if none of the specified protocols are supported), or
- * the defaults for the underlying SSL implementation if
- * the endpoint configuration does not specify any protocols.
+ * @throws IllegalArgumentException If there is no intersection between the
+ * implemented and configured ciphers
*/
- public String[] getEnableableProtocols(SSLContext context);
+ public 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.
+ */
+ public interface ProtocolInfo {
+ /**
+ * ALPN information.
+ * @return the protocol selected using ALPN
+ */
+ public String getNegotiatedProtocol();
+ }
}
diff --git a/java/org/apache/tomcat/util/net/SSLUtilBase.java b/java/org/apache/tomcat/util/net/SSLUtilBase.java
new file mode 100644
index 0000000..d311527
--- /dev/null
+++ b/java/org/apache/tomcat/util/net/SSLUtilBase.java
@@ -0,0 +1,177 @@
+/*
+ * 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.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.KeyStore;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
+import org.apache.tomcat.util.file.ConfigFileLoader;
+import org.apache.tomcat.util.res.StringManager;
+
+/**
+ * Common base class for {@link SSLUtil} implementations.
+ */
+public abstract class SSLUtilBase implements SSLUtil {
+
+ private static final Log log = LogFactory.getLog(SSLUtilBase.class);
+ private static final StringManager sm = StringManager.getManager(SSLUtilBase.class);
+
+ protected final SSLHostConfigCertificate certificate;
+
+ private final String[] enabledProtocols;
+ private final String[] enabledCiphers;
+
+
+ protected SSLUtilBase(SSLHostConfigCertificate certificate) {
+ this.certificate = certificate;
+ SSLHostConfig sslHostConfig = certificate.getSSLHostConfig();
+
+ // Calculate the enabled protocols
+ Set<String> configuredProtocols = sslHostConfig.getProtocols();
+ Set<String> implementedProtocols = getImplementedProtocols();
+ List<String> enabledProtocols =
+ getEnabled("protocols", getLog(), true, configuredProtocols, implementedProtocols);
+ this.enabledProtocols = enabledProtocols.toArray(new String[enabledProtocols.size()]);
+
+ // Calculate the enabled ciphers
+ List<String> configuredCiphers = sslHostConfig.getJsseCipherNames();
+ Set<String> implementedCiphers = getImplementedCiphers();
+ List<String> enabledCiphers =
+ getEnabled("ciphers", getLog(), false, configuredCiphers, implementedCiphers);
+ this.enabledCiphers = enabledCiphers.toArray(new String[enabledCiphers.size()]);
+ }
+
+
+ static <T> List<T> getEnabled(String name, Log log, boolean warnOnSkip, Collection<T> configured,
+ Collection<T> implemented) {
+
+ List<T> enabled = new ArrayList<>();
+
+ if (implemented.size() == 0) {
+ // 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
+ // will be generated when the list is used. Not ideal but no more
+ // can be done at this point.
+ enabled.addAll(configured);
+ } else {
+ enabled.addAll(configured);
+ enabled.retainAll(implemented);
+
+ if (enabled.isEmpty()) {
+ // 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));
+ }
+ if (log.isDebugEnabled()) {
+ log.debug(sm.getString("sslUtilBase.active", name, enabled));
+ }
+ if (log.isDebugEnabled() || warnOnSkip) {
+ if (enabled.size() != configured.size()) {
+ List<T> skipped = new ArrayList<>();
+ skipped.addAll(configured);
+ skipped.removeAll(enabled);
+ String msg = sm.getString("sslUtilBase.skipped", name, skipped);
+ if (warnOnSkip) {
+ log.warn(msg);
+ } else {
+ log.debug(msg);
+ }
+ }
+ }
+ }
+
+ return enabled;
+ }
+
+
+ /*
+ * Gets the key- or truststore with the specified type, path, and password.
+ */
+ static KeyStore getStore(String type, String provider, String path,
+ String pass) throws IOException {
+
+ KeyStore ks = null;
+ InputStream istream = null;
+ try {
+ if (provider == null) {
+ ks = KeyStore.getInstance(type);
+ } else {
+ ks = KeyStore.getInstance(type, provider);
+ }
+ if(!("PKCS11".equalsIgnoreCase(type) ||
+ "".equalsIgnoreCase(path)) ||
+ "NONE".equalsIgnoreCase(path)) {
+ istream = ConfigFileLoader.getInputStream(path);
+ }
+
+ char[] storePass = null;
+ if (pass != null && !"".equals(pass)) {
+ storePass = pass.toCharArray();
+ }
+ ks.load(istream, storePass);
+ } catch (FileNotFoundException fnfe) {
+ log.error(sm.getString("jsse.keystore_load_failed", type, path,
+ fnfe.getMessage()), fnfe);
+ throw fnfe;
+ } catch (IOException ioe) {
+ // 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("jsse.keystore_load_failed", type, path,
+ ex.getMessage());
+ log.error(msg, ex);
+ throw new IOException(msg);
+ } finally {
+ if (istream != null) {
+ try {
+ istream.close();
+ } catch (IOException ioe) {
+ // Do nothing
+ }
+ }
+ }
+
+ return ks;
+ }
+
+
+ @Override
+ public String[] getEnabledProtocols() {
+ return enabledProtocols;
+ }
+
+ @Override
+ public String[] getEnabledCiphers() {
+ return enabledCiphers;
+ }
+
+ protected abstract Set<String> getImplementedProtocols();
+ protected abstract Set<String> getImplementedCiphers();
+ protected abstract Log getLog();
+}
diff --git a/java/org/apache/tomcat/util/net/SecureNio2Channel.java b/java/org/apache/tomcat/util/net/SecureNio2Channel.java
index 87954b1..6ec625c 100644
--- a/java/org/apache/tomcat/util/net/SecureNio2Channel.java
+++ b/java/org/apache/tomcat/util/net/SecureNio2Channel.java
@@ -21,8 +21,9 @@ import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
-import java.nio.channels.ReadPendingException;
import java.nio.channels.WritePendingException;
+import java.util.Collections;
+import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
@@ -36,6 +37,9 @@ import javax.net.ssl.SSLException;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
+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.res.StringManager;
/**
@@ -43,8 +47,12 @@ import org.apache.tomcat.util.res.StringManager;
*/
public class SecureNio2Channel extends Nio2Channel {
- protected static final Log log = LogFactory.getLog(SecureNio2Channel.class);
- protected static final StringManager sm = StringManager.getManager("org.apache.tomcat.util.net.res");
+ private static final Log log = LogFactory.getLog(SecureNio2Channel.class);
+ private static final StringManager sm = StringManager.getManager(SecureNio2Channel.class);
+
+ // Value determined by observation of what the SSL Engine requested in
+ // various scenarios
+ private static final int DEFAULT_NET_BUFFER_SIZE = 16921;
protected ByteBuffer netInBuffer;
protected ByteBuffer netOutBuffer;
@@ -52,94 +60,93 @@ public class SecureNio2Channel extends Nio2Channel {
protected SSLEngine sslEngine;
protected final Nio2Endpoint endpoint;
+ protected boolean sniComplete = false;
+
private volatile boolean handshakeComplete;
private volatile HandshakeStatus handshakeStatus; //gets set by handshake
+ private volatile boolean unwrapBeforeRead = false;
+
protected boolean closed;
protected boolean closing;
- protected volatile boolean readPending;
- protected volatile boolean writePending;
- private final CompletionHandler<Integer, SocketWrapper<Nio2Channel>> handshakeReadCompletionHandler;
- private final CompletionHandler<Integer, SocketWrapper<Nio2Channel>> handshakeWriteCompletionHandler;
+ private final CompletionHandler<Integer, SocketWrapperBase<Nio2Channel>> handshakeReadCompletionHandler;
+ private final CompletionHandler<Integer, SocketWrapperBase<Nio2Channel>> handshakeWriteCompletionHandler;
- public SecureNio2Channel(SSLEngine engine, ApplicationBufferHandler bufHandler,
- Nio2Endpoint endpoint0) {
+ public SecureNio2Channel(SocketBufferHandler bufHandler, Nio2Endpoint endpoint) {
super(bufHandler);
- sslEngine = engine;
- endpoint = endpoint0;
- int netBufSize = sslEngine.getSession().getPacketBufferSize();
+ this.endpoint = endpoint;
if (endpoint.getSocketProperties().getDirectSslBuffer()) {
- netInBuffer = ByteBuffer.allocateDirect(netBufSize);
- netOutBuffer = ByteBuffer.allocateDirect(netBufSize);
+ netInBuffer = ByteBuffer.allocateDirect(DEFAULT_NET_BUFFER_SIZE);
+ netOutBuffer = ByteBuffer.allocateDirect(DEFAULT_NET_BUFFER_SIZE);
} else {
- netInBuffer = ByteBuffer.allocate(netBufSize);
- netOutBuffer = ByteBuffer.allocate(netBufSize);
+ netInBuffer = ByteBuffer.allocate(DEFAULT_NET_BUFFER_SIZE);
+ netOutBuffer = ByteBuffer.allocate(DEFAULT_NET_BUFFER_SIZE);
}
- handshakeReadCompletionHandler = new CompletionHandler<Integer, SocketWrapper<Nio2Channel>>() {
- @Override
- public void completed(Integer result, SocketWrapper<Nio2Channel> attachment) {
- if (result.intValue() < 0) {
- failed(new EOFException(), attachment);
- return;
- }
- endpoint.processSocket(attachment, SocketStatus.OPEN_READ, false);
- }
- @Override
- public void failed(Throwable exc, SocketWrapper<Nio2Channel> attachment) {
- endpoint.closeSocket(attachment, SocketStatus.ERROR);
- }
- };
- handshakeWriteCompletionHandler = new CompletionHandler<Integer, SocketWrapper<Nio2Channel>>() {
- @Override
- public void completed(Integer result, SocketWrapper<Nio2Channel> attachment) {
- if (result.intValue() < 0) {
- failed(new EOFException(), attachment);
- return;
- }
- endpoint.processSocket(attachment, SocketStatus.OPEN_WRITE, false);
- }
- @Override
- public void failed(Throwable exc, SocketWrapper<Nio2Channel> attachment) {
- endpoint.closeSocket(attachment, SocketStatus.ERROR);
+ handshakeReadCompletionHandler = new HandshakeReadCompletionHandler();
+ handshakeWriteCompletionHandler = new HandshakeWriteCompletionHandler();
+ }
+
+
+ private class HandshakeReadCompletionHandler
+ implements CompletionHandler<Integer, SocketWrapperBase<Nio2Channel>> {
+ @Override
+ public void completed(Integer result, SocketWrapperBase<Nio2Channel> attachment) {
+ if (result.intValue() < 0) {
+ failed(new EOFException(), attachment);
+ } else {
+ endpoint.processSocket(attachment, SocketEvent.OPEN_READ, false);
}
- };
+ }
+ @Override
+ public void failed(Throwable exc, SocketWrapperBase<Nio2Channel> attachment) {
+ endpoint.processSocket(attachment, SocketEvent.ERROR, false);
+ }
}
- public void setSSLEngine(SSLEngine engine) {
- this.sslEngine = engine;
+
+ private class HandshakeWriteCompletionHandler
+ implements CompletionHandler<Integer, SocketWrapperBase<Nio2Channel>> {
+ @Override
+ public void completed(Integer result, SocketWrapperBase<Nio2Channel> attachment) {
+ if (result.intValue() < 0) {
+ failed(new EOFException(), attachment);
+ } else {
+ endpoint.processSocket(attachment, SocketEvent.OPEN_WRITE, false);
+ }
+ }
+ @Override
+ public void failed(Throwable exc, SocketWrapperBase<Nio2Channel> attachment) {
+ endpoint.processSocket(attachment, SocketEvent.ERROR, false);
+ }
}
+
@Override
- public void reset(AsynchronousSocketChannel channel, SocketWrapper<Nio2Channel> socket)
+ public void reset(AsynchronousSocketChannel channel, SocketWrapperBase<Nio2Channel> socket)
throws IOException {
super.reset(channel, socket);
- netOutBuffer.position(0);
- netOutBuffer.limit(0);
- netInBuffer.position(0);
- netInBuffer.limit(0);
+ sslEngine = null;
+ sniComplete = false;
handshakeComplete = false;
closed = false;
closing = false;
- readPending = false;
- writePending = false;
- //initiate handshake
- sslEngine.beginHandshake();
- handshakeStatus = sslEngine.getHandshakeStatus();
+ netInBuffer.clear();
}
@Override
- public int getBufferSize() {
- int size = super.getBufferSize();
- size += netInBuffer!=null?netInBuffer.capacity():0;
- size += netOutBuffer!=null?netOutBuffer.capacity():0;
- return size;
+ public void free() {
+ super.free();
+ if (endpoint.getSocketProperties().getDirectSslBuffer()) {
+ ByteBufferUtils.cleanDirectBuffer(netInBuffer);
+ ByteBufferUtils.cleanDirectBuffer(netOutBuffer);
+ }
}
private class FutureFlush implements Future<Boolean> {
private Future<Integer> integer;
- protected FutureFlush(Future<Integer> integer) {
- this.integer = integer;
+ protected FutureFlush() {
+ integer = sc.write(netOutBuffer);
}
@Override
public boolean cancel(boolean mayInterruptIfRunning) {
@@ -156,23 +163,13 @@ public class SecureNio2Channel extends Nio2Channel {
@Override
public Boolean get() throws InterruptedException,
ExecutionException {
- try {
- int result = integer.get().intValue();
- return Boolean.valueOf(result >= 0);
- } finally {
- writePending = false;
- }
+ return Boolean.valueOf(integer.get().intValue() >= 0);
}
@Override
public Boolean get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException,
TimeoutException {
- try {
- int result = integer.get(timeout, unit).intValue();
- return Boolean.valueOf(result >= 0);
- } finally {
- writePending = false;
- }
+ return Boolean.valueOf(integer.get(timeout, unit).intValue() >= 0);
}
}
@@ -184,12 +181,7 @@ public class SecureNio2Channel extends Nio2Channel {
*/
@Override
public Future<Boolean> flush() {
- if (writePending) {
- throw new WritePendingException();
- } else {
- writePending = true;
- }
- return new FutureFlush(sc.write(netOutBuffer));
+ return new FutureFlush();
}
/**
@@ -205,7 +197,7 @@ public class SecureNio2Channel extends Nio2Channel {
* @return 0 if hand shake is complete, negative if the socket needs to
* close and positive if the handshake is incomplete
*
- * @throws IOException
+ * @throws IOException if an error occurs during the handshake
*/
@Override
public int handshake() throws IOException {
@@ -213,8 +205,18 @@ public class SecureNio2Channel extends Nio2Channel {
}
protected int handshakeInternal(boolean async) throws IOException {
- if (handshakeComplete)
+ if (handshakeComplete) {
return 0; //we have done our initial handshake
+ }
+
+ if (!sniComplete) {
+ int sniResult = processSNI();
+ if (sniResult == 0) {
+ sniComplete = true;
+ } else {
+ return sniResult;
+ }
+ }
SSLEngineResult handshake = null;
@@ -225,6 +227,9 @@ public class SecureNio2Channel extends Nio2Channel {
throw new IOException(sm.getString("channel.nio.ssl.notHandshaking"));
}
case FINISHED: {
+ if (endpoint.hasNegotiableProtocols() && sslEngine instanceof SSLUtil.ProtocolInfo) {
+ socket.setNegotiatedProtocol(((SSLUtil.ProtocolInfo) sslEngine).getNegotiatedProtocol());
+ }
//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
@@ -313,6 +318,87 @@ public class SecureNio2Channel extends Nio2Channel {
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.
+ */
+ 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, socket, handshakeReadCompletionHandler);
+ return 1;
+ }
+
+ TLSClientHelloExtractor extractor = new TLSClientHelloExtractor(netInBuffer);
+
+ while (extractor.getResult() == ExtractorResult.UNDERFLOW &&
+ netInBuffer.capacity() < endpoint.getSniParseLimit()) {
+ // extractor needed more data to process but netInBuffer was full so
+ // expand the buffer and read some more data.
+ int newLimit = Math.min(netInBuffer.capacity() * 2, endpoint.getSniParseLimit());
+ log.info(sm.getString("channel.nio.ssl.expandNetInBuffer",
+ Integer.toString(newLimit)));
+
+ netInBuffer = ByteBufferUtils.expand(netInBuffer, newLimit);
+ sc.read(netInBuffer);
+ extractor = new TLSClientHelloExtractor(netInBuffer);
+ }
+
+ String hostName = null;
+ List<Cipher> clientRequestedCiphers = null;
+ switch (extractor.getResult()) {
+ case COMPLETE:
+ hostName = extractor.getSNIValue();
+ //$FALL-THROUGH$ to set the client requested ciphers
+ case NOT_PRESENT:
+ clientRequestedCiphers = extractor.getClientRequestedCiphers();
+ break;
+ case NEED_READ:
+ sc.read(netInBuffer, socket, 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;
+ }
+
+ if (log.isDebugEnabled()) {
+ log.debug(sm.getString("channel.nio.ssl.sniHostName", hostName));
+ }
+
+ sslEngine = endpoint.createSSLEngine(hostName, clientRequestedCiphers);
+
+ // Ensure the application buffers (which have to be created earlier) are
+ // big enough.
+ getBufHandler().expand(sslEngine.getSession().getApplicationBufferSize());
+ if (netOutBuffer.capacity() < sslEngine.getSession().getApplicationBufferSize()) {
+ // Info for now as we may need to increase DEFAULT_NET_BUFFER_SIZE
+ log.info(sm.getString("channel.nio.ssl.expandNetOutBuffer",
+ Integer.toString(sslEngine.getSession().getApplicationBufferSize())));
+ }
+ netInBuffer = ByteBufferUtils.expand(netInBuffer, sslEngine.getSession().getPacketBufferSize());
+ netOutBuffer = ByteBufferUtils.expand(netOutBuffer, sslEngine.getSession().getPacketBufferSize());
+
+ // Set limit and position to expected values
+ netOutBuffer.position(0);
+ netOutBuffer.limit(0);
+
+ // Initiate handshake
+ sslEngine.beginHandshake();
+ handshakeStatus = sslEngine.getHandshakeStatus();
+
+ return 0;
+ }
+
+
/**
* 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
@@ -324,17 +410,14 @@ public class SecureNio2Channel extends Nio2Channel {
//validate the network buffers are empty
if (netInBuffer.position() > 0 && netInBuffer.position() < netInBuffer.limit()) throw new IOException(sm.getString("channel.nio.ssl.netInputNotEmpty"));
if (netOutBuffer.position() > 0 && netOutBuffer.position() < netOutBuffer.limit()) throw new IOException(sm.getString("channel.nio.ssl.netOutputNotEmpty"));
- ByteBuffer readBuffer = getBufHandler().getReadBuffer();
- ByteBuffer writeBuffer = getBufHandler().getWriteBuffer();
- if (readBuffer.position() > 0 && readBuffer.position() < readBuffer.limit()) throw new IOException(sm.getString("channel.nio.ssl.appInputNotEmpty"));
- if (writeBuffer.position() > 0 && writeBuffer.position() < writeBuffer.limit()) throw new IOException(sm.getString("channel.nio.ssl.appOutputNotEmpty"));
+ if (!getBufHandler().isReadBufferEmpty()) throw new IOException(sm.getString("channel.nio.ssl.appInputNotEmpty"));
+ if (!getBufHandler().isWriteBufferEmpty()) throw new IOException(sm.getString("channel.nio.ssl.appOutputNotEmpty"));
netOutBuffer.position(0);
netOutBuffer.limit(0);
netInBuffer.position(0);
netInBuffer.limit(0);
- readBuffer.clear();
- writeBuffer.clear();
+ getBufHandler().reset();
handshakeComplete = false;
//initiate handshake
@@ -362,7 +445,7 @@ public class SecureNio2Channel extends Nio2Channel {
/**
* Executes all the tasks needed on the same thread.
- * @return HandshakeStatus
+ * @return the status
*/
protected SSLEngineResult.HandshakeStatus tasks() {
Runnable r = null;
@@ -374,15 +457,16 @@ public class SecureNio2Channel extends Nio2Channel {
/**
* Performs the WRAP function
- * @return SSLEngineResult
- * @throws IOException
+ * @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.
netOutBuffer.clear();
//perform the wrap
- SSLEngineResult result = sslEngine.wrap(bufHandler.getWriteBuffer(), netOutBuffer);
+ getBufHandler().configureWriteBufferForRead();
+ SSLEngineResult result = sslEngine.wrap(getBufHandler().getWriteBuffer(), netOutBuffer);
//prepare the results to be written
netOutBuffer.flip();
//set the status
@@ -392,11 +476,10 @@ public class SecureNio2Channel extends Nio2Channel {
/**
* Perform handshake unwrap
- * @return SSLEngineResult
- * @throws IOException
+ * @return the result
+ * @throws IOException An IO error occurred
*/
protected SSLEngineResult handshakeUnwrap() throws IOException {
-
if (netInBuffer.position() == netInBuffer.limit()) {
//clear the buffer if we have emptied it out on data
netInBuffer.clear();
@@ -408,7 +491,8 @@ public class SecureNio2Channel extends Nio2Channel {
//prepare the buffer with the incoming data
netInBuffer.flip();
//call unwrap
- result = sslEngine.unwrap(netInBuffer, bufHandler.getReadBuffer());
+ getBufHandler().configureReadBufferForWrite();
+ result = sslEngine.unwrap(netInBuffer, getBufHandler().getReadBuffer());
//compact the buffer, this is an optional method, wonder what would happen if we didn't
netInBuffer.compact();
//read in the status
@@ -476,11 +560,6 @@ public class SecureNio2Channel extends Nio2Channel {
closed = (!netOutBuffer.hasRemaining() && (handshake.getHandshakeStatus() != HandshakeStatus.NEED_WRAP));
}
- /**
- * Force a close, can throw an IOException
- * @param force boolean
- * @throws IOException
- */
@Override
public void close(boolean force) throws IOException {
try {
@@ -494,11 +573,11 @@ public class SecureNio2Channel extends Nio2Channel {
}
private class FutureRead implements Future<Integer> {
- private final ByteBuffer dst;
+ private ByteBuffer dst;
private Future<Integer> integer;
- public FutureRead(ByteBuffer dst) {
+ private FutureRead(ByteBuffer dst) {
this.dst = dst;
- if (netInBuffer.position() > 0) {
+ if (unwrapBeforeRead || netInBuffer.position() > 0) {
this.integer = null;
} else {
this.integer = sc.read(netInBuffer);
@@ -506,7 +585,6 @@ public class SecureNio2Channel extends Nio2Channel {
}
@Override
public boolean cancel(boolean mayInterruptIfRunning) {
- readPending = false;
return (integer == null) ? false : integer.cancel(mayInterruptIfRunning);
}
@Override
@@ -524,21 +602,15 @@ public class SecureNio2Channel extends Nio2Channel {
} catch (TimeoutException e) {
// Cannot happen: no timeout
throw new ExecutionException(e);
- } finally {
- readPending = false;
}
}
@Override
public Integer get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException,
TimeoutException {
- try {
- return (integer == null) ? unwrap(netInBuffer.position(), timeout, unit) : unwrap(integer.get(timeout, unit).intValue(), timeout, unit);
- } finally {
- readPending = false;
- }
+ 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, InterruptedException, TimeoutException {
+ 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);
@@ -580,17 +652,40 @@ public class SecureNio2Channel extends Nio2Channel {
break;
}
}
- } 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
- break;
+ } else if (unwrap.getStatus() == Status.BUFFER_OVERFLOW) {
+ if (read > 0) {
+ // Buffer overflow can happen if we have read data. Return
+ // so the destination buffer can be emptied before another
+ // read is attempted
+ break;
+ } else {
+ // The SSL session has increased the required buffer size
+ // since the buffer was created.
+ if (dst == getBufHandler().getReadBuffer()) {
+ // This is the normal case for this code
+ getBufHandler()
+ .expand(sslEngine.getSession().getApplicationBufferSize());
+ dst = getBufHandler().getReadBuffer();
+ } else if (dst == getAppReadBufHandler().getByteBuffer()) {
+ 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())));
+ }
+ }
} else {
- //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
+ // Something else went wrong
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
+ } while (netInBuffer.position() != 0); //continue to unwrapping as long as the input buffer has stuff
+ if (!dst.hasRemaining()) {
+ unwrapBeforeRead = true;
+ } else {
+ unwrapBeforeRead = false;
+ }
return Integer.valueOf(read);
}
}
@@ -607,31 +702,25 @@ public class SecureNio2Channel extends Nio2Channel {
if (!handshakeComplete) {
throw new IllegalStateException(sm.getString("channel.nio.ssl.incompleteHandshake"));
}
- if (readPending) {
- throw new ReadPendingException();
- } else {
- readPending = true;
- }
return new FutureRead(dst);
}
private class FutureWrite implements Future<Integer> {
- private ByteBuffer src;
+ private final ByteBuffer src;
private Future<Integer> integer = null;
private int written = 0;
private Throwable t = null;
- protected FutureWrite(ByteBuffer src) {
+ private FutureWrite(ByteBuffer src) {
+ this.src = src;
//are we closing or closed?
if (closing || closed) {
t = new IOException(sm.getString("channel.nio.ssl.closing"));
- return;
+ } else {
+ wrap();
}
- this.src = src;
- wrap();
}
@Override
public boolean cancel(boolean mayInterruptIfRunning) {
- writePending = false;
return integer.cancel(mayInterruptIfRunning);
}
@Override
@@ -645,18 +734,15 @@ public class SecureNio2Channel extends Nio2Channel {
@Override
public Integer get() throws InterruptedException, ExecutionException {
if (t != null) {
- writePending = false;
throw new ExecutionException(t);
}
- integer.get();
- if (written == 0) {
+ if (integer.get().intValue() > 0 && written == 0) {
wrap();
return get();
} else if (netOutBuffer.hasRemaining()) {
integer = sc.write(netOutBuffer);
return get();
} else {
- writePending = false;
return Integer.valueOf(written);
}
}
@@ -665,33 +751,31 @@ public class SecureNio2Channel extends Nio2Channel {
throws InterruptedException, ExecutionException,
TimeoutException {
if (t != null) {
- writePending = false;
throw new ExecutionException(t);
}
- integer.get(timeout, unit);
- if (written == 0) {
+ if (integer.get(timeout, unit).intValue() > 0 && written == 0) {
wrap();
return get(timeout, unit);
} else if (netOutBuffer.hasRemaining()) {
integer = sc.write(netOutBuffer);
return get(timeout, unit);
} else {
- writePending = false;
return Integer.valueOf(written);
}
}
protected void wrap() {
- //The data buffer should be empty, we can reuse the entire buffer.
- netOutBuffer.clear();
try {
- SSLEngineResult result = sslEngine.wrap(src, netOutBuffer);
- written = result.bytesConsumed();
- netOutBuffer.flip();
- if (result.getStatus() == Status.OK) {
- if (result.getHandshakeStatus() == HandshakeStatus.NEED_TASK)
- tasks();
- } else {
- t = new IOException(sm.getString("channel.nio.ssl.wrapFail", result.getStatus()));
+ if (!netOutBuffer.hasRemaining()) {
+ netOutBuffer.clear();
+ SSLEngineResult result = sslEngine.wrap(src, netOutBuffer);
+ written = result.bytesConsumed();
+ netOutBuffer.flip();
+ if (result.getStatus() == Status.OK) {
+ if (result.getHandshakeStatus() == HandshakeStatus.NEED_TASK)
+ tasks();
+ } else {
+ t = new IOException(sm.getString("channel.nio.ssl.wrapFail", result.getStatus()));
+ }
}
integer = sc.write(netOutBuffer);
} catch (SSLException e) {
@@ -708,11 +792,6 @@ public class SecureNio2Channel extends Nio2Channel {
*/
@Override
public Future<Integer> write(ByteBuffer src) {
- if (writePending) {
- throw new WritePendingException();
- } else {
- writePending = true;
- }
return new FutureWrite(src);
}
@@ -728,10 +807,104 @@ public class SecureNio2Channel extends Nio2Channel {
if (!handshakeComplete) {
throw new IllegalStateException(sm.getString("channel.nio.ssl.incompleteHandshake"));
}
- if (readPending) {
- throw new ReadPendingException();
+ CompletionHandler<Integer, A> readCompletionHandler = new CompletionHandler<Integer, A>() {
+ @Override
+ public void completed(Integer nBytes, A attach) {
+ if (nBytes.intValue() < 0) {
+ failed(new EOFException(), attach);
+ } else {
+ try {
+ ByteBuffer dst2 = dst;
+ //the data read
+ int read = 0;
+ //the SSL engine result
+ SSLEngineResult unwrap;
+ do {
+ //prepare the buffer
+ netInBuffer.flip();
+ //unwrap the data
+ unwrap = sslEngine.unwrap(netInBuffer, dst2);
+ //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
+ read += unwrap.bytesProduced();
+ //perform any tasks if needed
+ if (unwrap.getHandshakeStatus() == HandshakeStatus.NEED_TASK)
+ tasks();
+ //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);
+ return;
+ } else {
+ break;
+ }
+ }
+ } else if (unwrap.getStatus() == Status.BUFFER_OVERFLOW) {
+ if (read > 0) {
+ // Buffer overflow can happen if we have read data. Return
+ // so the destination buffer can be emptied before another
+ // read is attempted
+ break;
+ } else {
+ // The SSL session has increased the required buffer size
+ // since the buffer was created.
+ if (dst2 == getBufHandler().getReadBuffer()) {
+ // This is the normal case for this code
+ getBufHandler().expand(
+ sslEngine.getSession().getApplicationBufferSize());
+ dst2 = getBufHandler().getReadBuffer();
+ } 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()));
+ }
+ }
+ } else {
+ // 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
+ } while (netInBuffer.position() != 0);
+ if (!dst2.hasRemaining()) {
+ unwrapBeforeRead = true;
+ } else {
+ unwrapBeforeRead = false;
+ }
+ // If everything is OK, so complete
+ handler.completed(Integer.valueOf(read), attach);
+ } catch (Exception e) {
+ failed(e, attach);
+ }
+ }
+ }
+ @Override
+ public void failed(Throwable exc, A attach) {
+ handler.failed(exc, attach);
+ }
+ };
+ if (unwrapBeforeRead || netInBuffer.position() > 0) {
+ readCompletionHandler.completed(Integer.valueOf(netInBuffer.position()), attachment);
} else {
- readPending = true;
+ sc.read(netInBuffer, timeout, unit, attachment, readCompletionHandler);
+ }
+ }
+
+ @Override
+ public <A> void read(final ByteBuffer[] dsts, final int offset, final int length,
+ final long timeout, final TimeUnit unit, final A attachment,
+ final CompletionHandler<Long, ? super A> handler) {
+ if (offset < 0 || dsts == null || (offset + length) > dsts.length) {
+ throw new IllegalArgumentException();
+ }
+ if (closing || closed) {
+ handler.completed(Long.valueOf(-1), attachment);
+ return;
+ }
+ if (!handshakeComplete) {
+ throw new IllegalStateException(sm.getString("channel.nio.ssl.incompleteHandshake"));
}
CompletionHandler<Integer, A> readCompletionHandler = new CompletionHandler<Integer, A>() {
@Override
@@ -741,14 +914,14 @@ public class SecureNio2Channel extends Nio2Channel {
} else {
try {
//the data read
- int read = 0;
+ long read = 0;
//the SSL engine result
SSLEngineResult unwrap;
do {
//prepare the buffer
netInBuffer.flip();
//unwrap the data
- unwrap = sslEngine.unwrap(netInBuffer, dst);
+ unwrap = sslEngine.unwrap(netInBuffer, dsts, offset, length);
//compact the buffer
netInBuffer.compact();
if (unwrap.getStatus() == Status.OK || unwrap.getStatus() == Status.BUFFER_UNDERFLOW) {
@@ -760,7 +933,7 @@ public class SecureNio2Channel extends Nio2Channel {
//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, attach, this);
+ sc.read(netInBuffer, timeout, unit, attachment, this);
return;
} else {
break;
@@ -776,10 +949,19 @@ public class SecureNio2Channel extends Nio2Channel {
//in the constructor
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 to unwrapping as long as the input buffer has stuff
+ int capacity = 0;
+ final int endOffset = offset + length;
+ for (int i = offset; i < endOffset; i++) {
+ capacity += dsts[i].remaining();
+ }
+ if (capacity == 0) {
+ unwrapBeforeRead = true;
+ } else {
+ unwrapBeforeRead = false;
+ }
// If everything is OK, so complete
- readPending = false;
- handler.completed(Integer.valueOf(read), attach);
+ handler.completed(Long.valueOf(read), attach);
} catch (Exception e) {
failed(e, attach);
}
@@ -787,11 +969,10 @@ public class SecureNio2Channel extends Nio2Channel {
}
@Override
public void failed(Throwable exc, A attach) {
- readPending = false;
handler.failed(exc, attach);
}
};
- if (netInBuffer.position() > 0) {
+ if (unwrapBeforeRead || netInBuffer.position() > 0) {
readCompletionHandler.completed(Integer.valueOf(netInBuffer.position()), attachment);
} else {
sc.read(netInBuffer, timeout, unit, attachment, readCompletionHandler);
@@ -806,12 +987,6 @@ public class SecureNio2Channel extends Nio2Channel {
handler.failed(new IOException(sm.getString("channel.nio.ssl.closing")), attachment);
return;
}
- if (writePending) {
- throw new WritePendingException();
- } else {
- writePending = true;
- }
-
try {
// Prepare the output buffer
netOutBuffer.clear();
@@ -834,18 +1009,15 @@ public class SecureNio2Channel extends Nio2Channel {
sc.write(netOutBuffer, timeout, unit, attachment, this);
} else if (written == 0) {
// Special case, start over to avoid code duplication
- writePending = false;
write(src, timeout, unit, attachment, handler);
} else {
// Call the handler completed method with the
// consumed bytes number
- writePending = false;
handler.completed(Integer.valueOf(written), attach);
}
}
@Override
public void failed(Throwable exc, A attach) {
- writePending = false;
handler.failed(exc, attach);
}
});
@@ -853,142 +1025,63 @@ public class SecureNio2Channel extends Nio2Channel {
throw new IOException(sm.getString("channel.nio.ssl.wrapFail", result.getStatus()));
}
} catch (Exception e) {
- writePending = false;
handler.failed(e, attachment);
}
}
- private static class GatherState<A> {
- public ByteBuffer[] srcs;
- public int offset;
- public int length;
- public A attachment;
- public long timeout;
- public TimeUnit unit;
- public CompletionHandler<Long, ? super A> handler;
- protected GatherState(ByteBuffer[] srcs, int offset, int length,
- long timeout, TimeUnit unit, A attachment,
- CompletionHandler<Long, ? super A> handler) {
- this.srcs = srcs;
- this.offset = offset;
- this.length = length;
- this.timeout = timeout;
- this.unit = unit;
- this.attachment = attachment;
- this.handler = handler;
- this.pos = offset;
- }
- public long writeCount = 0;
- public int pos;
- }
-
- private class GatherCompletionHandler<A> implements CompletionHandler<Integer, GatherState<A>> {
- protected GatherState<A> state;
- protected GatherCompletionHandler(GatherState<A> state) {
- this.state = state;
- }
- @Override
- public void completed(Integer nBytes, GatherState<A> attachment) {
- if (nBytes.intValue() < 0) {
- failed(new EOFException(), attachment);
- } else {
- if (state.pos == state.offset + state.length) {
- writePending = false;
- state.handler.completed(Long.valueOf(state.writeCount), state.attachment);
- } else if (netOutBuffer.hasRemaining()) {
- sc.write(netOutBuffer, state.timeout, state.unit, state, this);
- } else {
- try {
- // Prepare the output buffer
- netOutBuffer.clear();
- // Wrap the source data into the internal buffer
- SSLEngineResult result = sslEngine.wrap(state.srcs[state.pos], netOutBuffer);
- int written = result.bytesConsumed();
- state.writeCount += written;
- netOutBuffer.flip();
- if (result.getStatus() == Status.OK) {
- if (result.getHandshakeStatus() == HandshakeStatus.NEED_TASK) {
- tasks();
- }
- if (!state.srcs[state.pos].hasRemaining()) {
- state.pos++;
- }
- // Write data to the channel
- sc.write(netOutBuffer, state.timeout, state.unit, state, this);
- } else {
- throw new IOException(sm.getString("channel.nio.ssl.wrapFail", result.getStatus()));
- }
- } catch (Exception e) {
- failed(e, attachment);
- }
- }
- }
- }
- @Override
- public void failed(Throwable exc, GatherState<A> attachment) {
- writePending = false;
- state.handler.failed(exc, state.attachment);
- }
- }
-
@Override
- public <A> void write(ByteBuffer[] srcs, int offset, int length,
- long timeout, TimeUnit unit, A attachment,
- CompletionHandler<Long, ? super A> handler) {
- // Check state
+ public <A> void write(final ByteBuffer[] srcs, final int offset, final int length,
+ final long timeout, final TimeUnit unit, final A attachment,
+ final CompletionHandler<Long, ? super A> handler) {
if ((offset < 0) || (length < 0) || (offset > srcs.length - length)) {
throw new IndexOutOfBoundsException();
}
+ // Check state
if (closing || closed) {
handler.failed(new IOException(sm.getString("channel.nio.ssl.closing")), attachment);
return;
}
- if (writePending) {
- throw new WritePendingException();
- } else {
- writePending = true;
- }
try {
- GatherState<A> state = new GatherState<>(srcs, offset, length,
- timeout, unit, attachment, handler);
- // Prepare the output buffer
+ // Prepare the output buffer
netOutBuffer.clear();
// Wrap the source data into the internal buffer
- SSLEngineResult result = sslEngine.wrap(srcs[offset], netOutBuffer);
- state.writeCount += result.bytesConsumed();
+ SSLEngineResult result = sslEngine.wrap(srcs, offset, length, netOutBuffer);
+ final int written = result.bytesConsumed();
netOutBuffer.flip();
if (result.getStatus() == Status.OK) {
if (result.getHandshakeStatus() == HandshakeStatus.NEED_TASK) {
tasks();
}
- if (!srcs[offset].hasRemaining()) {
- state.pos++;
- }
// Write data to the channel
- sc.write(netOutBuffer, timeout, unit, state, new GatherCompletionHandler<>(state));
+ sc.write(netOutBuffer, timeout, unit, attachment, new CompletionHandler<Integer, A>() {
+ @Override
+ public void completed(Integer nBytes, A attach) {
+ if (nBytes.intValue() < 0) {
+ failed(new EOFException(), attach);
+ } else if (netOutBuffer.hasRemaining()) {
+ sc.write(netOutBuffer, timeout, unit, attachment, this);
+ } else if (written == 0) {
+ // Special case, start over to avoid code duplication
+ write(srcs, offset, length, timeout, unit, attachment, handler);
+ } else {
+ // Call the handler completed method with the
+ // consumed bytes number
+ handler.completed(Long.valueOf(written), attach);
+ }
+ }
+ @Override
+ public void failed(Throwable exc, A attach) {
+ handler.failed(exc, attach);
+ }
+ });
} else {
throw new IOException(sm.getString("channel.nio.ssl.wrapFail", result.getStatus()));
}
} catch (Exception e) {
- writePending = false;
handler.failed(e, attachment);
}
}
- /**
- * Callback interface to be able to expand buffers
- * when buffer overflow exceptions happen
- */
- public static interface ApplicationBufferHandler {
- public ByteBuffer getReadBuffer();
- public ByteBuffer getWriteBuffer();
- }
-
- @Override
- public ApplicationBufferHandler getBufHandler() {
- return bufHandler;
- }
-
@Override
public boolean isHandshakeComplete() {
return handshakeComplete;
@@ -1007,10 +1100,6 @@ public class SecureNio2Channel extends Nio2Channel {
return emptyBuf;
}
- public void setBufHandler(ApplicationBufferHandler bufHandler) {
- this.bufHandler = bufHandler;
- }
-
@Override
public AsynchronousSocketChannel getIOChannel() {
return sc;
diff --git a/java/org/apache/tomcat/util/net/SecureNioChannel.java b/java/org/apache/tomcat/util/net/SecureNioChannel.java
index 7fda2e7..543cdb9 100644
--- a/java/org/apache/tomcat/util/net/SecureNioChannel.java
+++ b/java/org/apache/tomcat/util/net/SecureNioChannel.java
@@ -23,6 +23,8 @@ import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
+import java.util.Collections;
+import java.util.List;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLEngineResult;
@@ -32,22 +34,30 @@ import javax.net.ssl.SSLException;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
+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.res.StringManager;
/**
- *
* Implementation of a secure socket channel
- * @version 1.0
*/
-
public class SecureNioChannel extends NioChannel {
- protected static final Log log = LogFactory.getLog(SecureNioChannel.class);
+ private static final Log log = LogFactory.getLog(SecureNioChannel.class);
+ private static final StringManager sm = StringManager.getManager(SecureNioChannel.class);
+
+ // Value determined by observation of what the SSL Engine requested in
+ // various scenarios
+ private static final int DEFAULT_NET_BUFFER_SIZE = 16921;
protected ByteBuffer netInBuffer;
protected ByteBuffer netOutBuffer;
protected SSLEngine sslEngine;
+ protected boolean sniComplete = false;
+
protected boolean handshakeComplete = false;
protected HandshakeStatus handshakeStatus; //gets set by handshake
@@ -55,75 +65,68 @@ public class SecureNioChannel extends NioChannel {
protected boolean closing = false;
protected NioSelectorPool pool;
+ private final NioEndpoint endpoint;
- public SecureNioChannel(SocketChannel channel, SSLEngine engine,
- ApplicationBufferHandler bufHandler, NioSelectorPool pool) throws IOException {
- super(channel,bufHandler);
- this.sslEngine = engine;
- int appBufSize = sslEngine.getSession().getApplicationBufferSize();
- int netBufSize = sslEngine.getSession().getPacketBufferSize();
- //allocate network buffers - TODO, add in optional direct non-direct buffers
- if ( netInBuffer == null ) netInBuffer = ByteBuffer.allocateDirect(netBufSize);
- if ( netOutBuffer == null ) netOutBuffer = ByteBuffer.allocateDirect(netBufSize);
-
- //selector pool for blocking operations
- this.pool = pool;
+ public SecureNioChannel(SocketChannel channel, SocketBufferHandler bufHandler,
+ NioSelectorPool pool, NioEndpoint endpoint) {
+ super(channel, bufHandler);
- //ensure that the application has a large enough read/write buffers
- //by doing this, we should not encounter any buffer overflow errors
- bufHandler.expand(bufHandler.getReadBuffer(), appBufSize);
- bufHandler.expand(bufHandler.getWriteBuffer(), appBufSize);
- reset();
- }
+ // Create the network buffers (these hold the encrypted data).
+ if (endpoint.getSocketProperties().getDirectSslBuffer()) {
+ netInBuffer = ByteBuffer.allocateDirect(DEFAULT_NET_BUFFER_SIZE);
+ netOutBuffer = ByteBuffer.allocateDirect(DEFAULT_NET_BUFFER_SIZE);
+ } else {
+ netInBuffer = ByteBuffer.allocate(DEFAULT_NET_BUFFER_SIZE);
+ netOutBuffer = ByteBuffer.allocate(DEFAULT_NET_BUFFER_SIZE);
+ }
- public void reset(SSLEngine engine) throws IOException {
- this.sslEngine = engine;
- reset();
+ // selector pool for blocking operations
+ this.pool = pool;
+ this.endpoint = endpoint;
}
+
@Override
public void reset() throws IOException {
super.reset();
- netOutBuffer.position(0);
- netOutBuffer.limit(0);
- netInBuffer.position(0);
- netInBuffer.limit(0);
+ sslEngine = null;
+ sniComplete = false;
handshakeComplete = false;
closed = false;
closing = false;
- //initiate handshake
- sslEngine.beginHandshake();
- handshakeStatus = sslEngine.getHandshakeStatus();
+ netInBuffer.clear();
}
@Override
- public int getBufferSize() {
- int size = super.getBufferSize();
- size += netInBuffer!=null?netInBuffer.capacity():0;
- size += netOutBuffer!=null?netOutBuffer.capacity():0;
- return size;
+ public void free() {
+ super.free();
+ if (endpoint.getSocketProperties().getDirectSslBuffer()) {
+ ByteBufferUtils.cleanDirectBuffer(netInBuffer);
+ ByteBufferUtils.cleanDirectBuffer(netOutBuffer);
+ }
}
-
//===========================================================================================
// NIO SSL METHODS
//===========================================================================================
+
/**
* Flush the channel.
*
* @param block Should a blocking write be used?
- * @param s
- * @param timeout
+ * @param s The selector to use for blocking, if null then a busy
+ * write will be initiated
+ * @param timeout The timeout for this write operation in milliseconds,
+ * -1 means no timeout
* @return <code>true</code> if the network buffer has been flushed out and
* is empty else <code>false</code>
- * @throws IOException
+ * @throws IOException If an I/O error occurs during the operation
*/
@Override
- public boolean flush(boolean block, Selector s, long timeout)
- throws IOException {
+ public boolean flush(boolean block, Selector s, long timeout) throws IOException {
if (!block) {
flush(netOutBuffer);
} else {
- pool.write(netOutBuffer, this, s, timeout,block);
+ pool.write(netOutBuffer, this, s, timeout, block);
}
return !netOutBuffer.hasRemaining();
}
@@ -132,7 +135,7 @@ public class SecureNioChannel extends NioChannel {
* 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
+ * @throws IOException An IO error occurred writing data
*/
protected boolean flush(ByteBuffer buf) throws IOException {
int remaining = buf.remaining();
@@ -145,19 +148,36 @@ public class SecureNioChannel extends NioChannel {
}
/**
- * Performs SSL handshake, non blocking, but performs NEED_TASK on the same thread.<br>
- * Hence, you should never call this method using your Acceptor thread, as you would slow down
- * your system significantly.<br>
- * 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, reregister the selection key for the return values interestOps.
+ * 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 write boolean - true if the underlying channel is writable
- * @return int - 0 if hand shake is complete, otherwise it returns a SelectionKey interestOps value
- * @throws IOException
+ *
+ * @return 0 if hand shake 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
*/
@Override
public int handshake(boolean read, boolean write) throws IOException {
- if ( handshakeComplete ) return 0; //we have done our initial handshake
+ if (handshakeComplete) {
+ return 0; //we have done our initial handshake
+ }
+
+ if (!sniComplete) {
+ int sniResult = processSNI();
+ if (sniResult == 0) {
+ sniComplete = true;
+ } else {
+ return sniResult;
+ }
+ }
if (!flush(netOutBuffer)) return SelectionKey.OP_WRITE; //we still have data to write
@@ -170,6 +190,10 @@ public class SecureNioChannel extends NioChannel {
throw new IOException(sm.getString("channel.nio.ssl.notHandshaking"));
}
case FINISHED: {
+ if (endpoint.hasNegotiableProtocols() && sslEngine instanceof SSLUtil.ProtocolInfo) {
+ socketWrapper.setNegotiatedProtocol(
+ ((SSLUtil.ProtocolInfo) sslEngine).getNegotiatedProtocol());
+ }
//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
@@ -212,6 +236,8 @@ public class SecureNioChannel extends NioChannel {
} else if ( handshake.getStatus() == Status.BUFFER_UNDERFLOW ){
//read more data, reregister for OP_READ
return SelectionKey.OP_READ;
+ } else if (handshake.getStatus() == Status.BUFFER_OVERFLOW) {
+ getBufHandler().configureReadBufferForWrite();
} else {
throw new IOException(sm.getString("channel.nio.ssl.unexpectedStatusDuringWrap", handshakeStatus));
}//switch
@@ -228,6 +254,90 @@ public class SecureNioChannel extends NioChannel {
return 0;
}
+
+ /*
+ * 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
+ */
+ private int processSNI() throws IOException {
+ // Read some data into the network input buffer so we can peek at it.
+ int bytesRead = sc.read(netInBuffer);
+ if (bytesRead == -1) {
+ // Reached end of stream before SNI could be processed.
+ return -1;
+ }
+ TLSClientHelloExtractor extractor = new TLSClientHelloExtractor(netInBuffer);
+
+ while (extractor.getResult() == ExtractorResult.UNDERFLOW &&
+ netInBuffer.capacity() < endpoint.getSniParseLimit()) {
+ // extractor needed more data to process but netInBuffer was full so
+ // expand the buffer and read some more data.
+ int newLimit = Math.min(netInBuffer.capacity() * 2, endpoint.getSniParseLimit());
+ log.info(sm.getString("channel.nio.ssl.expandNetInBuffer",
+ Integer.toString(newLimit)));
+
+ netInBuffer = ByteBufferUtils.expand(netInBuffer, newLimit);
+ sc.read(netInBuffer);
+ extractor = new TLSClientHelloExtractor(netInBuffer);
+ }
+
+ String hostName = null;
+ List<Cipher> clientRequestedCiphers = null;
+ switch (extractor.getResult()) {
+ case COMPLETE:
+ hostName = extractor.getSNIValue();
+ //$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;
+ }
+
+ if (log.isDebugEnabled()) {
+ log.debug(sm.getString("channel.nio.ssl.sniHostName", hostName));
+ }
+
+ sslEngine = endpoint.createSSLEngine(hostName, clientRequestedCiphers);
+
+ // Ensure the application buffers (which have to be created earlier) are
+ // big enough.
+ getBufHandler().expand(sslEngine.getSession().getApplicationBufferSize());
+ if (netOutBuffer.capacity() < sslEngine.getSession().getApplicationBufferSize()) {
+ // Info for now as we may need to increase DEFAULT_NET_BUFFER_SIZE
+ log.info(sm.getString("channel.nio.ssl.expandNetOutBuffer",
+ Integer.toString(sslEngine.getSession().getApplicationBufferSize())));
+ }
+ netInBuffer = ByteBufferUtils.expand(netInBuffer, sslEngine.getSession().getPacketBufferSize());
+ netOutBuffer = ByteBufferUtils.expand(netOutBuffer, sslEngine.getSession().getPacketBufferSize());
+
+ // Set limit and position to expected values
+ netOutBuffer.position(0);
+ netOutBuffer.limit(0);
+
+ // Initiate handshake
+ sslEngine.beginHandshake();
+ handshakeStatus = sslEngine.getHandshakeStatus();
+
+ return 0;
+ }
+
+
/**
* 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
@@ -241,15 +351,17 @@ public class SecureNioChannel extends NioChannel {
//validate the network buffers are empty
if (netInBuffer.position() > 0 && netInBuffer.position()<netInBuffer.limit()) throw new IOException(sm.getString("channel.nio.ssl.netInputNotEmpty"));
if (netOutBuffer.position() > 0 && netOutBuffer.position()<netOutBuffer.limit()) throw new IOException(sm.getString("channel.nio.ssl.netOutputNotEmpty"));
- if (getBufHandler().getReadBuffer().position()>0 && getBufHandler().getReadBuffer().position()<getBufHandler().getReadBuffer().limit()) throw new IOException(sm.getString("channel.nio.ssl.appInputNotEmpty"));
- if (getBufHandler().getWriteBuffer().position()>0 && getBufHandler().getWriteBuffer().position()<getBufHandler().getWriteBuffer().limit()) throw new IOException(sm.getString("channel.nio.ssl.appOutputNotEmpty"));
- reset();
- boolean isReadable = true;
- boolean isWriteable = true;
+ if (!getBufHandler().isReadBufferEmpty()) throw new IOException(sm.getString("channel.nio.ssl.appInputNotEmpty"));
+ if (!getBufHandler().isWriteBufferEmpty()) throw new IOException(sm.getString("channel.nio.ssl.appOutputNotEmpty"));
+ handshakeComplete = false;
+ boolean isReadable = false;
+ boolean isWriteable = false;
boolean handshaking = true;
Selector selector = null;
SelectionKey key = null;
try {
+ sslEngine.beginHandshake();
+ handshakeStatus = sslEngine.getHandshakeStatus();
while (handshaking) {
int hsStatus = this.handshake(isReadable, isWriteable);
switch (hsStatus) {
@@ -258,12 +370,7 @@ public class SecureNioChannel extends NioChannel {
default : {
long now = System.currentTimeMillis();
if (selector==null) {
- synchronized (Selector.class) {
- // Selector.open() isn't thread safe
- // http://bugs.sun.com/view_bug.do?bug_id=6427854
- // Affects 1.6.0_29, fixed in 1.7.0_01
- selector = Selector.open();
- }
+ selector = Selector.open();
key = getIOChannel().register(selector, hsStatus);
} else {
key.interestOps(hsStatus); // null warning supressed
@@ -292,7 +399,7 @@ public class SecureNioChannel extends NioChannel {
/**
* Executes all the tasks needed on the same thread.
- * @return HandshakeStatus
+ * @return the status
*/
protected SSLEngineResult.HandshakeStatus tasks() {
Runnable r = null;
@@ -305,15 +412,16 @@ public class SecureNioChannel extends NioChannel {
/**
* Performs the WRAP function
* @param doWrite boolean
- * @return SSLEngineResult
- * @throws IOException
+ * @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.
netOutBuffer.clear();
//perform the wrap
- SSLEngineResult result = sslEngine.wrap(bufHandler.getWriteBuffer(), netOutBuffer);
+ getBufHandler().configureWriteBufferForWrite();
+ SSLEngineResult result = sslEngine.wrap(getBufHandler().getWriteBuffer(), netOutBuffer);
//prepare the results to be written
netOutBuffer.flip();
//set the status
@@ -326,8 +434,8 @@ public class SecureNioChannel extends NioChannel {
/**
* Perform handshake unwrap
* @param doread boolean
- * @return SSLEngineResult
- * @throws IOException
+ * @return the result
+ * @throws IOException An IO error occurred
*/
protected SSLEngineResult handshakeUnwrap(boolean doread) throws IOException {
@@ -347,7 +455,8 @@ public class SecureNioChannel extends NioChannel {
//prepare the buffer with the incoming data
netInBuffer.flip();
//call unwrap
- result = sslEngine.unwrap(netInBuffer, bufHandler.getReadBuffer());
+ getBufHandler().configureReadBufferForWrite();
+ result = sslEngine.unwrap(netInBuffer, getBufHandler().getReadBuffer());
//compact the buffer, this is an optional method, wonder what would happen if we didn't
netInBuffer.compact();
//read in the status
@@ -402,11 +511,7 @@ public class SecureNioChannel extends NioChannel {
closed = (!netOutBuffer.hasRemaining() && (handshake.getHandshakeStatus() != HandshakeStatus.NEED_WRAP));
}
- /**
- * Force a close, can throw an IOException
- * @param force boolean
- * @throws IOException
- */
+
@Override
public void close(boolean force) throws IOException {
try {
@@ -428,12 +533,15 @@ public class SecureNioChannel extends NioChannel {
* channel has reached end-of-stream
* @throws IOException If some other I/O error occurs
* @throws IllegalArgumentException if the destination buffer is different
- * than bufHandler.getReadBuffer()
+ * than getBufHandler().getReadBuffer()
*/
@Override
public int read(ByteBuffer dst) throws IOException {
- //if we want to take advantage of the expand function, make sure we only use the ApplicationBufferHandler's buffers
- if ( dst != bufHandler.getReadBuffer() ) throw new IllegalArgumentException(sm.getString("channel.nio.ssl.invalidBuffer"));
+ // Make sure we only use the ApplicationBufferHandler's buffers
+ if (dst != getBufHandler().getReadBuffer() && (getAppReadBufHandler() == null
+ || dst != getAppReadBufHandler().getByteBuffer())) {
+ throw new IllegalArgumentException(sm.getString("channel.nio.ssl.invalidBuffer"));
+ }
//are we in the middle of closing or closed?
if ( closing || closed) return -1;
//did we finish our handshake?
@@ -456,25 +564,47 @@ public class SecureNioChannel extends NioChannel {
//compact the buffer
netInBuffer.compact();
- if ( unwrap.getStatus()==Status.OK || unwrap.getStatus()==Status.BUFFER_UNDERFLOW ) {
+ if (unwrap.getStatus() == Status.OK || unwrap.getStatus() == Status.BUFFER_UNDERFLOW) {
//we did receive some data, add it to our total
read += unwrap.bytesProduced();
//perform any tasks if needed
- if (unwrap.getHandshakeStatus() == HandshakeStatus.NEED_TASK) tasks();
+ if (unwrap.getHandshakeStatus() == HandshakeStatus.NEED_TASK) {
+ tasks();
+ }
//if we need more network data, then bail out for now.
- if ( unwrap.getStatus() == Status.BUFFER_UNDERFLOW ) break;
- }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
- break;
- }else {
- //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
+ if (unwrap.getStatus() == Status.BUFFER_UNDERFLOW) {
+ break;
+ }
+ } else if (unwrap.getStatus() == Status.BUFFER_OVERFLOW) {
+ if (read > 0) {
+ // Buffer overflow can happen if we have read data. Return
+ // so the destination buffer can be emptied before another
+ // read is attempted
+ break;
+ } else {
+ // The SSL session has increased the required buffer size
+ // since the buffer was created.
+ if (dst == getBufHandler().getReadBuffer()) {
+ // This is the normal case for this code
+ getBufHandler().expand(sslEngine.getSession().getApplicationBufferSize());
+ dst = getBufHandler().getReadBuffer();
+ } else if (dst == getAppReadBufHandler().getByteBuffer()) {
+ 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 IOException(
+ sm.getString("channel.nio.ssl.unwrapFailResize", unwrap.getStatus()));
+ }
+ }
+ } else {
+ // 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
- return (read);
+ } while (netInBuffer.position() != 0); //continue to unwrapping as long as the input buffer has stuff
+ return read;
}
/**
@@ -537,22 +667,6 @@ public class SecureNioChannel extends NioChannel {
return remaining2 < remaining;
}
-
- /**
- * Callback interface to be able to expand buffers
- * when buffer overflow exceptions happen
- */
- public static interface ApplicationBufferHandler {
- public ByteBuffer expand(ByteBuffer buffer, int remaining);
- public ByteBuffer getReadBuffer();
- public ByteBuffer getWriteBuffer();
- }
-
- @Override
- public ApplicationBufferHandler getBufHandler() {
- return bufHandler;
- }
-
@Override
public boolean isHandshakeComplete() {
return handshakeComplete;
@@ -571,12 +685,9 @@ public class SecureNioChannel extends NioChannel {
return emptyBuf;
}
- public void setBufHandler(ApplicationBufferHandler bufHandler) {
- this.bufHandler = bufHandler;
- }
-
@Override
public SocketChannel getIOChannel() {
return sc;
}
-}
+
+}
\ No newline at end of file
diff --git a/java/org/apache/tomcat/util/net/SendfileDataBase.java b/java/org/apache/tomcat/util/net/SendfileDataBase.java
new file mode 100644
index 0000000..fc89b11
--- /dev/null
+++ b/java/org/apache/tomcat/util/net/SendfileDataBase.java
@@ -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 org.apache.tomcat.util.net;
+
+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).
+ */
+ public boolean keepAlive;
+
+ /**
+ * 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.
+ */
+ 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.
+ */
+ public long length;
+
+ public SendfileDataBase(String filename, long pos, long length) {
+ this.fileName = filename;
+ this.pos = pos;
+ this.length = length;
+ }
+}
diff --git a/java/org/apache/tomcat/util/net/ServerSocketFactory.java b/java/org/apache/tomcat/util/net/ServerSocketFactory.java
deleted file mode 100644
index 1265bb1..0000000
--- a/java/org/apache/tomcat/util/net/ServerSocketFactory.java
+++ /dev/null
@@ -1,97 +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.net;
-
-import java.io.IOException;
-import java.net.InetAddress;
-import java.net.ServerSocket;
-import java.net.Socket;
-
-/**
- * The common interface through which the {@link JIoEndpoint} interacts with
- * both non-SSL and SSL sockets.
- */
-public interface ServerSocketFactory {
-
- /**
- * Returns a server socket which uses all network interfaces on the host,
- * and is bound to a the specified port. The socket is configured with the
- * socket options (such as accept timeout) given to this factory.
- *
- * @param port
- * the port to listen to
- * @exception IOException
- * for networking errors
- * @exception InstantiationException
- * for construction errors
- */
- ServerSocket createSocket(int port) throws IOException,
- InstantiationException;
-
- /**
- * Returns a server socket which uses all network interfaces on the host, is
- * bound to a the specified port, and uses the specified connection backlog.
- * The socket is configured with the socket options (such as accept timeout)
- * given to this factory.
- *
- * @param port
- * the port to listen to
- * @param backlog
- * how many connections are queued
- * @exception IOException
- * for networking errors
- * @exception InstantiationException
- * for construction errors
- */
- ServerSocket createSocket(int port, int backlog) throws IOException,
- InstantiationException;
-
- /**
- * Returns a server socket which uses only the specified network interface
- * on the local host, is bound to a the specified port, and uses the
- * specified connection backlog. The socket is configured with the socket
- * options (such as accept timeout) given to this factory.
- *
- * @param port
- * the port to listen to
- * @param backlog
- * how many connections are queued
- * @param ifAddress
- * the network interface address to use
- * @exception IOException
- * for networking errors
- * @exception InstantiationException
- * for construction errors
- */
- ServerSocket createSocket(int port, int backlog, InetAddress ifAddress)
- throws IOException, InstantiationException;
-
- /**
- * Wrapper function for accept(). This allows us to trap and translate
- * exceptions if necessary.
- *
- * @exception IOException
- */
- Socket acceptSocket(ServerSocket socket) throws IOException;
-
- /**
- * Triggers the SSL handshake. This will be a no-op for non-SSL sockets.
- *
- * @exception IOException
- */
- void handshake(Socket sock) throws IOException;
-}
diff --git a/java/org/apache/tomcat/util/net/SocketBufferHandler.java b/java/org/apache/tomcat/util/net/SocketBufferHandler.java
new file mode 100644
index 0000000..134ee1b
--- /dev/null
+++ b/java/org/apache/tomcat/util/net/SocketBufferHandler.java
@@ -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.tomcat.util.net;
+
+import java.nio.ByteBuffer;
+
+import org.apache.tomcat.util.buf.ByteBufferUtils;
+
+public class SocketBufferHandler {
+
+ private volatile boolean readBufferConfiguredForWrite = true;
+ private volatile ByteBuffer readBuffer;
+
+ private volatile boolean writeBufferConfiguredForWrite = true;
+ private volatile ByteBuffer writeBuffer;
+
+ private final boolean direct;
+
+ public SocketBufferHandler(int readBufferSize, int writeBufferSize,
+ boolean direct) {
+ this.direct = direct;
+ if (direct) {
+ readBuffer = ByteBuffer.allocateDirect(readBufferSize);
+ writeBuffer = ByteBuffer.allocateDirect(writeBufferSize);
+ } else {
+ readBuffer = ByteBuffer.allocate(readBufferSize);
+ writeBuffer = ByteBuffer.allocate(writeBufferSize);
+ }
+ }
+
+
+ public void configureReadBufferForWrite() {
+ setReadBufferConfiguredForWrite(true);
+ }
+
+
+ public void configureReadBufferForRead() {
+ setReadBufferConfiguredForWrite(false);
+ }
+
+
+ private void setReadBufferConfiguredForWrite(boolean readBufferConFiguredForWrite) {
+ // NO-OP if buffer is already in correct state
+ if (this.readBufferConfiguredForWrite != readBufferConFiguredForWrite) {
+ if (readBufferConFiguredForWrite) {
+ // Switching to write
+ int remaining = readBuffer.remaining();
+ if (remaining == 0) {
+ readBuffer.clear();
+ } else {
+ readBuffer.compact();
+ }
+ } else {
+ // Switching to read
+ readBuffer.flip();
+ }
+ this.readBufferConfiguredForWrite = readBufferConFiguredForWrite;
+ }
+ }
+
+
+ public ByteBuffer getReadBuffer() {
+ return readBuffer;
+ }
+
+
+ public boolean isReadBufferEmpty() {
+ if (readBufferConfiguredForWrite) {
+ return readBuffer.position() == 0;
+ } else {
+ return readBuffer.remaining() == 0;
+ }
+ }
+
+
+ public void configureWriteBufferForWrite() {
+ setWriteBufferConfiguredForWrite(true);
+ }
+
+
+ public void configureWriteBufferForRead() {
+ setWriteBufferConfiguredForWrite(false);
+ }
+
+
+ private void setWriteBufferConfiguredForWrite(boolean writeBufferConfiguredForWrite) {
+ // NO-OP if buffer is already in correct state
+ if (this.writeBufferConfiguredForWrite != writeBufferConfiguredForWrite) {
+ if (writeBufferConfiguredForWrite) {
+ // Switching to write
+ int remaining = writeBuffer.remaining();
+ if (remaining == 0) {
+ writeBuffer.clear();
+ } else {
+ writeBuffer.compact();
+ writeBuffer.position(remaining);
+ writeBuffer.limit(writeBuffer.capacity());
+ }
+ } else {
+ // Switching to read
+ writeBuffer.flip();
+ }
+ this.writeBufferConfiguredForWrite = writeBufferConfiguredForWrite;
+ }
+ }
+
+
+ public boolean isWriteBufferWritable() {
+ if (writeBufferConfiguredForWrite) {
+ return writeBuffer.hasRemaining();
+ } else {
+ return writeBuffer.remaining() == 0;
+ }
+ }
+
+
+ public ByteBuffer getWriteBuffer() {
+ return writeBuffer;
+ }
+
+
+ public boolean isWriteBufferEmpty() {
+ if (writeBufferConfiguredForWrite) {
+ return writeBuffer.position() == 0;
+ } else {
+ return writeBuffer.remaining() == 0;
+ }
+ }
+
+
+ public void reset() {
+ readBuffer.clear();
+ readBufferConfiguredForWrite = true;
+ writeBuffer.clear();
+ writeBufferConfiguredForWrite = true;
+ }
+
+
+ public void expand(int newSize) {
+ configureReadBufferForWrite();
+ readBuffer = ByteBufferUtils.expand(readBuffer, newSize);
+ configureWriteBufferForWrite();
+ writeBuffer = ByteBufferUtils.expand(writeBuffer, newSize);
+ }
+
+ public void free() {
+ if (direct) {
+ ByteBufferUtils.cleanDirectBuffer(readBuffer);
+ ByteBufferUtils.cleanDirectBuffer(writeBuffer);
+ }
+ }
+
+}
diff --git a/java/org/apache/tomcat/util/net/SocketEvent.java b/java/org/apache/tomcat/util/net/SocketEvent.java
new file mode 100644
index 0000000..9df1116
--- /dev/null
+++ b/java/org/apache/tomcat/util/net/SocketEvent.java
@@ -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.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.
+ */
+public enum SocketEvent {
+
+ /**
+ * Data is available to be read.
+ */
+ OPEN_READ,
+
+ /**
+ * The socket is ready to be written to.
+ */
+ OPEN_WRITE,
+
+ /**
+ * 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.
+ */
+ TIMEOUT,
+
+ /**
+ * The client has disconnected.
+ */
+ 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:
+ * <ul>
+ * <li>by NIO2 to signal the failure of a completion handler</li>
+ * <li>by the container to signal an I/O error on a non-container thread
+ * during Servlet 3.0 asynchronous processing.</li>
+ * </ul>
+ */
+ ERROR
+}
diff --git a/java/org/apache/tomcat/util/net/SocketProcessorBase.java b/java/org/apache/tomcat/util/net/SocketProcessorBase.java
new file mode 100644
index 0000000..1207ab0
--- /dev/null
+++ b/java/org/apache/tomcat/util/net/SocketProcessorBase.java
@@ -0,0 +1,55 @@
+/*
+ * 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.Objects;
+
+public abstract class SocketProcessorBase<S> implements Runnable {
+
+ protected SocketWrapperBase<S> socketWrapper;
+ protected SocketEvent event;
+
+ public SocketProcessorBase(SocketWrapperBase<S> socketWrapper, SocketEvent event) {
+ reset(socketWrapper, event);
+ }
+
+
+ public void reset(SocketWrapperBase<S> socketWrapper, SocketEvent event) {
+ Objects.requireNonNull(event);
+ this.socketWrapper = socketWrapper;
+ this.event = event;
+ }
+
+
+ @Override
+ public final void run() {
+ synchronized (socketWrapper) {
+ // It is possible that processing may be triggered for read and
+ // write at the same time. The sync above makes sure that processing
+ // does not occur in parallel. The test below ensures that if the
+ // first event to be processed results in the socket being closed,
+ // the subsequent events are not processed.
+ if (socketWrapper.isClosed()) {
+ return;
+ }
+ doRun();
+ }
+ }
+
+
+ protected abstract void doRun();
+}
diff --git a/java/org/apache/tomcat/util/net/SocketProperties.java b/java/org/apache/tomcat/util/net/SocketProperties.java
index 7225ee3..1d5f7f8 100644
--- a/java/org/apache/tomcat/util/net/SocketProperties.java
+++ b/java/org/apache/tomcat/util/net/SocketProperties.java
@@ -30,14 +30,6 @@ import java.nio.channels.AsynchronousSocketChannel;
* and are currently only working for the Nio connector
*/
public class SocketProperties {
- /**
- * Enable/disable socket wrapper cache, this bounded cache stores
- * SocketWrapper objects to reduce GC
- * Default is 500
- * -1 is unlimited
- * 0 is disabled
- */
- protected int socketWrapperCache = 500;
/**
* Enable/disable socket processor cache, this bounded cache stores
@@ -312,14 +304,6 @@ public class SocketProperties {
return eventCache;
}
- public int getKeyCache() {
- return socketWrapperCache;
- }
-
- public int getSocketWrapperCache() {
- return socketWrapperCache;
- }
-
public int getAppReadBufSize() {
return appReadBufSize;
}
@@ -409,14 +393,6 @@ public class SocketProperties {
this.eventCache = eventCache;
}
- public void setSocketWrapperCache(int socketWrapperCache) {
- this.socketWrapperCache = socketWrapperCache;
- }
-
- public void setKeyCache(int keyCache) {
- this.socketWrapperCache = keyCache;
- }
-
public void setAppReadBufSize(int appReadBufSize) {
this.appReadBufSize = appReadBufSize;
}
diff --git a/java/org/apache/tomcat/util/net/SocketStatus.java b/java/org/apache/tomcat/util/net/SocketStatus.java
deleted file mode 100644
index 4f0611e..0000000
--- a/java/org/apache/tomcat/util/net/SocketStatus.java
+++ /dev/null
@@ -1,35 +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.net;
-
-/**
- * Someone, please change the enum name.
- *
- * @author remm
- */
-public enum SocketStatus {
- OPEN_READ, OPEN_WRITE, STOP, TIMEOUT, DISCONNECT, ERROR,
-
- // All replaced by ERROR
- @Deprecated
- ASYNC_WRITE_ERROR,
- @Deprecated
- ASYNC_READ_ERROR,
- @Deprecated
- CLOSE_NOW
-}
diff --git a/java/org/apache/tomcat/util/net/SocketWrapper.java b/java/org/apache/tomcat/util/net/SocketWrapper.java
deleted file mode 100644
index 3163397..0000000
--- a/java/org/apache/tomcat/util/net/SocketWrapper.java
+++ /dev/null
@@ -1,182 +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.net;
-
-import java.util.Iterator;
-import java.util.Set;
-import java.util.concurrent.CopyOnWriteArraySet;
-import java.util.concurrent.locks.Lock;
-import java.util.concurrent.locks.ReentrantReadWriteLock;
-import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
-
-public class SocketWrapper<E> {
-
- private volatile E socket;
-
- // Volatile because I/O and setting the timeout values occurs on a different
- // thread to the thread checking the timeout.
- private volatile long lastAccess = System.currentTimeMillis();
- private volatile long timeout = -1;
-
- private volatile boolean error = false;
- private volatile int keepAliveLeft = 100;
- private volatile boolean comet = false;
- private volatile boolean async = false;
- private boolean keptAlive = false;
- private volatile boolean upgraded = false;
- private boolean secure = false;
- /*
- * Following cached for speed / reduced GC
- */
- private String localAddr = null;
- private String localName = null;
- private int localPort = -1;
- private String remoteAddr = null;
- private String remoteHost = null;
- private int remotePort = -1;
- /*
- * Used if block/non-blocking is set at the socket level. The client is
- * responsible for the thread-safe use of this field via the locks provided.
- */
- private volatile boolean blockingStatus = true;
- private final Lock blockingStatusReadLock;
- private final WriteLock blockingStatusWriteLock;
-
- /*
- * In normal servlet processing only one thread is allowed to access the
- * socket at a time. That is controlled by a lock on the socket for both
- * read and writes). When HTTP upgrade is used, one read thread and one
- * write thread are allowed to access the socket concurrently. In this case
- * the lock on the socket is used for reads and the lock below is used for
- * writes.
- */
- private final Object writeThreadLock = new Object();
-
- private Set<DispatchType> dispatches = new CopyOnWriteArraySet<>();
-
- public SocketWrapper(E socket) {
- this.socket = socket;
- ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
- this.blockingStatusReadLock = lock.readLock();
- this.blockingStatusWriteLock = lock.writeLock();
- }
-
- public E getSocket() {
- return socket;
- }
-
- public boolean isComet() { return comet; }
- public void setComet(boolean comet) { this.comet = comet; }
- public boolean isAsync() { return async; }
- public void setAsync(boolean async) { this.async = async; }
- public boolean isUpgraded() { return upgraded; }
- public void setUpgraded(boolean upgraded) { this.upgraded = upgraded; }
- public boolean isSecure() { return secure; }
- public void setSecure(boolean secure) { this.secure = secure; }
- public long getLastAccess() { return lastAccess; }
- public void access() {
- // Async timeouts are based on the time between the call to startAsync()
- // and complete() / dispatch() so don't update the last access time
- // (that drives the timeout) on every read and write when using async
- // processing.
- if (!isAsync()) {
- access(System.currentTimeMillis());
- }
- }
- public void access(long access) { lastAccess = access; }
- public void setTimeout(long timeout) {this.timeout = timeout;}
- public long getTimeout() {return this.timeout;}
- // error is used by NIO2 - will move to Nio2SocketWraper in Tomcat 9
- public boolean getError() { return error; }
- public void setError(boolean error) { this.error = error; }
- public void setKeepAliveLeft(int keepAliveLeft) { this.keepAliveLeft = keepAliveLeft;}
- public int decrementKeepAlive() { return (--keepAliveLeft);}
- public boolean isKeptAlive() {return keptAlive;}
- public void setKeptAlive(boolean keptAlive) {this.keptAlive = keptAlive;}
- public int getLocalPort() { return localPort; }
- public void setLocalPort(int localPort) {this.localPort = localPort; }
- public String getLocalName() { return localName; }
- public void setLocalName(String localName) {this.localName = localName; }
- public String getLocalAddr() { return localAddr; }
- public void setLocalAddr(String localAddr) {this.localAddr = localAddr; }
- public int getRemotePort() { return remotePort; }
- public void setRemotePort(int remotePort) {this.remotePort = remotePort; }
- public String getRemoteHost() { return remoteHost; }
- public void setRemoteHost(String remoteHost) {this.remoteHost = remoteHost; }
- public String getRemoteAddr() { return remoteAddr; }
- public void setRemoteAddr(String remoteAddr) {this.remoteAddr = remoteAddr; }
- public boolean getBlockingStatus() { return blockingStatus; }
- public void setBlockingStatus(boolean blockingStatus) {
- this.blockingStatus = blockingStatus;
- }
- public Lock getBlockingStatusReadLock() { return blockingStatusReadLock; }
- public WriteLock getBlockingStatusWriteLock() {
- return blockingStatusWriteLock;
- }
- public Object getWriteThreadLock() { return writeThreadLock; }
- public void addDispatch(DispatchType dispatchType) {
- synchronized (dispatches) {
- dispatches.add(dispatchType);
- }
- }
- public Iterator<DispatchType> getIteratorAndClearDispatches() {
- // Note: Logic in AbstractProtocol depends on this method only returning
- // a non-null value if the iterator is non-empty. i.e. it should never
- // return an empty iterator.
- Iterator<DispatchType> result;
- synchronized (dispatches) {
- // Synchronized as the generation of the iterator and the clearing
- // of dispatches needs to be an atomic operation.
- result = dispatches.iterator();
- if (result.hasNext()) {
- dispatches.clear();
- } else {
- result = null;
- }
- }
- return result;
- }
- public void clearDispatches() {
- synchronized (dispatches) {
- dispatches.clear();
- }
- }
-
- /**
- * Overridden for debug purposes. No guarantees are made about the format of
- * this message which may vary significantly between point releases.
- * <p>
- * {@inheritDoc}
- */
- @Override
- public String toString() {
- return super.toString() + ":" + String.valueOf(socket);
- }
-
-
- /**
- * Register the associated socket for the requested events.
- *
- * @param timeout The time to wait for the event(s) to occur
- * @param read Should the socket be register for read?
- * @param write Should the socket be register for write?
- */
- public void registerforEvent(int timeout, boolean read, boolean write) {
- // NO-OP by default.
- // Currently only implemented by APR.
- }
-}
diff --git a/java/org/apache/tomcat/util/net/SocketWrapperBase.java b/java/org/apache/tomcat/util/net/SocketWrapperBase.java
new file mode 100644
index 0000000..83e1fb8
--- /dev/null
+++ b/java/org/apache/tomcat/util/net/SocketWrapperBase.java
@@ -0,0 +1,1071 @@
+/*
+ * 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.IOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.CompletionHandler;
+import java.util.Iterator;
+import java.util.concurrent.LinkedBlockingDeque;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
+
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
+import org.apache.tomcat.util.buf.ByteBufferHolder;
+import org.apache.tomcat.util.res.StringManager;
+
+public abstract class SocketWrapperBase<E> {
+
+ private static final Log log = LogFactory.getLog(SocketWrapperBase.class);
+
+ protected static final StringManager sm = StringManager.getManager(SocketWrapperBase.class);
+
+ private final E socket;
+ private final AbstractEndpoint<E> endpoint;
+
+ // Volatile because I/O and setting the timeout values occurs on a different
+ // thread to the thread checking the timeout.
+ private volatile long readTimeout = -1;
+ private volatile long writeTimeout = -1;
+
+ private volatile int keepAliveLeft = 100;
+ private volatile boolean upgraded = false;
+ private boolean secure = false;
+ private String negotiatedProtocol = null;
+ /*
+ * Following cached for speed / reduced GC
+ */
+ protected String localAddr = null;
+ protected String localName = null;
+ protected int localPort = -1;
+ protected String remoteAddr = null;
+ protected String remoteHost = null;
+ protected int remotePort = -1;
+ /*
+ * Used if block/non-blocking is set at the socket level. The client is
+ * responsible for the thread-safe use of this field via the locks provided.
+ */
+ private volatile boolean blockingStatus = true;
+ private final Lock blockingStatusReadLock;
+ private final WriteLock blockingStatusWriteLock;
+ /*
+ * 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;
+
+ /**
+ * The buffers used for communicating with the socket.
+ */
+ protected volatile SocketBufferHandler socketBufferHandler = null;
+
+ /**
+ * For "non-blocking" writes use an external set of buffers. Although the
+ * API only allows one non-blocking write at a time, due to buffering and
+ * the possible need to write HTTP headers, there may be more than one write
+ * to the OutputBuffer.
+ */
+ protected final LinkedBlockingDeque<ByteBufferHolder> bufferedWrites = new LinkedBlockingDeque<>();
+
+ /**
+ * The max size of the buffered write buffer
+ */
+ protected int bufferedWriteSize = 64 * 1024; // 64k default write buffer
+
+ public SocketWrapperBase(E socket, AbstractEndpoint<E> endpoint) {
+ this.socket = socket;
+ this.endpoint = endpoint;
+ ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
+ this.blockingStatusReadLock = lock.readLock();
+ this.blockingStatusWriteLock = lock.writeLock();
+ }
+
+ public E getSocket() {
+ return socket;
+ }
+
+ public AbstractEndpoint<E> getEndpoint() {
+ return endpoint;
+ }
+
+ 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.
+ if (this.error != null) {
+ return;
+ }
+ this.error = error;
+ }
+ public void checkError() throws IOException {
+ if (error != null) {
+ throw error;
+ }
+ }
+
+ public boolean isUpgraded() { return upgraded; }
+ public void setUpgraded(boolean upgraded) { this.upgraded = upgraded; }
+ public boolean isSecure() { return secure; }
+ public void setSecure(boolean secure) { this.secure = secure; }
+ 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.
+ *
+ * @param readTimeout The timeout in milliseconds. A value of -1 indicates
+ * an infinite timeout.
+ */
+ public void setReadTimeout(long readTimeout) {
+ if (readTimeout > 0) {
+ this.readTimeout = readTimeout;
+ } else {
+ this.readTimeout = -1;
+ }
+ }
+
+ public long getReadTimeout() {
+ return this.readTimeout;
+ }
+
+ /**
+ * 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.
+ */
+ public void setWriteTimeout(long writeTimeout) {
+ if (writeTimeout > 0) {
+ this.writeTimeout = writeTimeout;
+ } else {
+ this.writeTimeout = -1;
+ }
+ }
+
+ public long getWriteTimeout() {
+ return this.writeTimeout;
+ }
+
+
+ public void setKeepAliveLeft(int keepAliveLeft) { this.keepAliveLeft = keepAliveLeft;}
+ public int decrementKeepAlive() { return (--keepAliveLeft);}
+
+ public String getRemoteHost() {
+ if (remoteHost == null) {
+ populateRemoteHost();
+ }
+ return remoteHost;
+ }
+ protected abstract void populateRemoteHost();
+
+ public String getRemoteAddr() {
+ if (remoteAddr == null) {
+ populateRemoteAddr();
+ }
+ return remoteAddr;
+ }
+ protected abstract void populateRemoteAddr();
+
+ public int getRemotePort() {
+ if (remotePort == -1) {
+ populateRemotePort();
+ }
+ return remotePort;
+ }
+ protected abstract void populateRemotePort();
+
+ public String getLocalName() {
+ if (localName == null) {
+ populateLocalName();
+ }
+ return localName;
+ }
+ protected abstract void populateLocalName();
+
+ public String getLocalAddr() {
+ if (localAddr == null) {
+ populateLocalAddr();
+ }
+ return localAddr;
+ }
+ protected abstract void populateLocalAddr();
+
+ public int getLocalPort() {
+ if (localPort == -1) {
+ populateLocalPort();
+ }
+ return localPort;
+ }
+ protected abstract void populateLocalPort();
+
+ public boolean getBlockingStatus() { return blockingStatus; }
+ public void setBlockingStatus(boolean blockingStatus) {
+ this.blockingStatus = blockingStatus;
+ }
+ public Lock getBlockingStatusReadLock() { return blockingStatusReadLock; }
+ public WriteLock getBlockingStatusWriteLock() {
+ return blockingStatusWriteLock;
+ }
+ public SocketBufferHandler getSocketBufferHandler() { return socketBufferHandler; }
+
+ public boolean hasDataToWrite() {
+ return !socketBufferHandler.isWriteBufferEmpty() || bufferedWrites.size() > 0;
+ }
+
+ /**
+ * 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.
+ * <p>
+ * Note: Once this method has returned <code>false</code> it <b>MUST NOT</b>
+ * 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 <code>true</code> if no writes are pending and data can be
+ * written otherwise <code>false</code>
+ */
+ public boolean isReadyForWrite() {
+ boolean result = canWrite();
+ if (!result) {
+ registerWriteInterest();
+ }
+ return result;
+ }
+
+
+ public boolean canWrite() {
+ if (socketBufferHandler == null) {
+ throw new IllegalStateException(sm.getString("socket.closed"));
+ }
+ return socketBufferHandler.isWriteBufferWritable() && bufferedWrites.size() == 0;
+ }
+
+
+ /**
+ * Overridden for debug purposes. No guarantees are made about the format of
+ * this message which may vary significantly between point releases.
+ * <p>
+ * {@inheritDoc}
+ */
+ @Override
+ public String toString() {
+ return super.toString() + ":" + String.valueOf(socket);
+ }
+
+
+ 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) {
+ socketBufferHandler.configureReadBufferForRead();
+ ByteBuffer readBuffer = socketBufferHandler.getReadBuffer();
+ int remaining = readBuffer.remaining();
+
+ // Is there enough data in the read buffer to satisfy this request?
+ // Copy what data there is in the read buffer to the byte array
+ if (remaining > 0) {
+ remaining = Math.min(remaining, len);
+ readBuffer.get(b, off, remaining);
+
+ if (log.isDebugEnabled()) {
+ log.debug("Socket: [" + this + "], Read from buffer: [" + remaining + "]");
+ }
+ }
+ return remaining;
+ }
+
+
+ protected int populateReadBuffer(ByteBuffer to) {
+ // Is there enough data in the read buffer to satisfy this request?
+ // Copy what data there is in the read buffer to the byte array
+ socketBufferHandler.configureReadBufferForRead();
+ int nRead = transfer(socketBufferHandler.getReadBuffer(), to);
+
+ if (log.isDebugEnabled()) {
+ log.debug("Socket: [" + this + "], Read from buffer: [" + nRead + "]");
+ }
+ return nRead;
+ }
+
+
+ /**
+ * 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.
+ */
+ public void unRead(ByteBuffer returnedInput) {
+ if (returnedInput != null) {
+ socketBufferHandler.configureReadBufferForWrite();
+ socketBufferHandler.getReadBuffer().put(returnedInput);
+ }
+ }
+
+
+ public abstract void close() throws IOException;
+ public abstract boolean isClosed();
+
+ /**
+ * Writes the provided data to the socket, buffering any remaining data if
+ * used in non-blocking mode.
+ *
+ * @param block <code>true</code> 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
+ *
+ * @throws IOException If an IO error occurs during the write
+ */
+ public final void write(boolean block, byte[] buf, int off, int len) throws IOException {
+ if (len == 0 || buf == null) {
+ return;
+ }
+
+ // 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.
+ if (block) {
+ writeBlocking(buf, off, len);
+ } else {
+ writeNonBlocking(buf, off, len);
+ }
+ }
+
+
+ /**
+ * Writes the provided data to the socket, buffering any remaining data if
+ * used in non-blocking mode.
+ *
+ * @param block <code>true</code> 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
+ */
+ public final void write(boolean block, ByteBuffer from) throws IOException {
+ if (from == null || from.remaining() == 0) {
+ return;
+ }
+
+ // 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.
+ if (block) {
+ writeBlocking(from);
+ } else {
+ writeNonBlocking(from);
+ }
+ }
+
+
+ /**
+ * Transfers the data to the socket write buffer (writing that data to the
+ * socket if the buffer fills up using a blocking write) until all the data
+ * has been transferred and space remains 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
+ */
+ protected void writeBlocking(byte[] buf, int off, int len) throws IOException {
+ // Note: There is an implementation assumption that if the switch from
+ // non-blocking to blocking has been made then any pending
+ // non-blocking writes were flushed at the time the switch
+ // occurred.
+
+ // Keep writing until all the data has been transferred to the socket
+ // write buffer and space remains in that buffer
+ socketBufferHandler.configureWriteBufferForWrite();
+ int thisTime = transfer(buf, off, len, socketBufferHandler.getWriteBuffer());
+ while (socketBufferHandler.getWriteBuffer().remaining() == 0) {
+ len = len - thisTime;
+ off = off + thisTime;
+ doWrite(true);
+ socketBufferHandler.configureWriteBufferForWrite();
+ thisTime = transfer(buf, off, len, socketBufferHandler.getWriteBuffer());
+ }
+ }
+
+
+ /**
+ * Write the data to the socket (writing that data to the socket using a
+ * blocking write) until all the data has been transferred and space remains
+ * in the socket write buffer. If it is possible use the provided buffer
+ * directly and do not transfer to the socket write buffer.
+ *
+ * @param from The ByteBuffer containing the data to be written
+ *
+ * @throws IOException If an IO error occurs during the write
+ */
+ protected void writeBlocking(ByteBuffer from) throws IOException {
+ // Note: There is an implementation assumption that if the switch from
+ // non-blocking to blocking has been made then any pending
+ // non-blocking writes were flushed at the time the switch
+ // occurred.
+
+ // If it is possible write the data to the socket directly from the
+ // provided buffer otherwise transfer it to the socket write buffer
+ if (socketBufferHandler.isWriteBufferEmpty()) {
+ writeByteBufferBlocking(from);
+ } else {
+ socketBufferHandler.configureWriteBufferForWrite();
+ transfer(from, socketBufferHandler.getWriteBuffer());
+ if (!socketBufferHandler.isWriteBufferWritable()) {
+ doWrite(true);
+ writeByteBufferBlocking(from);
+ }
+ }
+ }
+
+
+ protected void writeByteBufferBlocking(ByteBuffer from) throws IOException {
+ // The socket write buffer capacity is socket.appWriteBufSize
+ int limit = socketBufferHandler.getWriteBuffer().capacity();
+ int fromLimit = from.limit();
+ while (from.remaining() >= limit) {
+ from.limit(from.position() + limit);
+ doWrite(true, from);
+ from.limit(fromLimit);
+ }
+
+ if (from.remaining() > 0) {
+ socketBufferHandler.configureWriteBufferForWrite();
+ transfer(from, socketBufferHandler.getWriteBuffer());
+ }
+ }
+
+
+ /**
+ * 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.
+ *
+ * @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 (bufferedWrites.size() == 0 && socketBufferHandler.isWriteBufferWritable()) {
+ socketBufferHandler.configureWriteBufferForWrite();
+ int thisTime = transfer(buf, off, len, socketBufferHandler.getWriteBuffer());
+ len = len - thisTime;
+ while (!socketBufferHandler.isWriteBufferWritable()) {
+ off = off + thisTime;
+ doWrite(false);
+ if (len > 0 && 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
+ // else to do here. Exit the loop.
+ break;
+ }
+ len = len - thisTime;
+ }
+ }
+
+ if (len > 0) {
+ // Remaining data must be buffered
+ addToBuffers(buf, off, len);
+ }
+ }
+
+
+ /**
+ * Writes the data to the socket (writing that data to the socket 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. If it is possible use the provided
+ * buffer directly and do not transfer to the socket 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 {
+ if (bufferedWrites.size() == 0 && socketBufferHandler.isWriteBufferWritable()) {
+ writeNonBlockingInternal(from);
+ }
+
+ if (from.remaining() > 0) {
+ // Remaining data must be buffered
+ addToBuffers(from);
+ }
+ }
+
+
+ private boolean writeNonBlockingInternal(ByteBuffer from) throws IOException {
+ if (socketBufferHandler.isWriteBufferEmpty()) {
+ return writeByteBufferNonBlocking(from);
+ } else {
+ socketBufferHandler.configureWriteBufferForWrite();
+ transfer(from, socketBufferHandler.getWriteBuffer());
+ if (!socketBufferHandler.isWriteBufferWritable()) {
+ doWrite(false);
+ if (socketBufferHandler.isWriteBufferWritable()) {
+ return writeByteBufferNonBlocking(from);
+ }
+ }
+ }
+
+ return !socketBufferHandler.isWriteBufferWritable();
+ }
+
+
+ protected boolean writeByteBufferNonBlocking(ByteBuffer from) throws IOException {
+ // The socket write buffer capacity is socket.appWriteBufSize
+ int limit = socketBufferHandler.getWriteBuffer().capacity();
+ int fromLimit = from.limit();
+ while (from.remaining() >= limit) {
+ int newLimit = from.position() + limit;
+ from.limit(newLimit);
+ doWrite(false, from);
+ from.limit(fromLimit);
+ if (from.position() != newLimit) {
+ // Didn't write the whole amount of data in the last
+ // non-blocking write.
+ // Exit the loop.
+ return true;
+ }
+ }
+
+ if (from.remaining() > 0) {
+ socketBufferHandler.configureWriteBufferForWrite();
+ transfer(from, socketBufferHandler.getWriteBuffer());
+ }
+
+ return false;
+ }
+
+
+ /**
+ * Writes as much data as possible from any that remains in the buffers.
+ *
+ * @param block <code>true</code> if a blocking write should be used,
+ * otherwise a non-blocking write will be used
+ *
+ * @return <code>true</code> if data remains to be flushed after this method
+ * completes, otherwise <code>false</code>. In blocking mode
+ * therefore, the return value should always be <code>false</code>
+ *
+ * @throws IOException If an IO error occurs during the write
+ */
+ public boolean flush(boolean block) throws IOException {
+ boolean result = false;
+ if (block) {
+ // A blocking flush will always empty the buffer.
+ flushBlocking();
+ } else {
+ result = flushNonBlocking();
+ }
+
+ return result;
+ }
+
+
+ protected void flushBlocking() throws IOException {
+ doWrite(true);
+
+ if (bufferedWrites.size() > 0) {
+ Iterator<ByteBufferHolder> bufIter = bufferedWrites.iterator();
+ while (bufIter.hasNext()) {
+ ByteBufferHolder buffer = bufIter.next();
+ buffer.flip();
+ writeBlocking(buffer.getBuf());
+ if (buffer.getBuf().remaining() == 0) {
+ bufIter.remove();
+ }
+ }
+
+ if (!socketBufferHandler.isWriteBufferEmpty()) {
+ doWrite(true);
+ }
+ }
+
+ }
+
+
+ protected boolean flushNonBlocking() throws IOException {
+ boolean dataLeft = !socketBufferHandler.isWriteBufferEmpty();
+
+ // Write to the socket, if there is anything to write
+ if (dataLeft) {
+ doWrite(false);
+ dataLeft = !socketBufferHandler.isWriteBufferEmpty();
+ }
+
+ if (!dataLeft && bufferedWrites.size() > 0) {
+ Iterator<ByteBufferHolder> bufIter = bufferedWrites.iterator();
+ while (!dataLeft && bufIter.hasNext()) {
+ ByteBufferHolder buffer = bufIter.next();
+ buffer.flip();
+ dataLeft = writeNonBlockingInternal(buffer.getBuf());
+ if (buffer.getBuf().remaining() == 0) {
+ bufIter.remove();
+ }
+ }
+
+ if (!dataLeft && !socketBufferHandler.isWriteBufferEmpty()) {
+ doWrite(false);
+ dataLeft = !socketBufferHandler.isWriteBufferEmpty();
+ }
+ }
+
+ return dataLeft;
+ }
+
+
+ /**
+ * 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
+ */
+ protected void doWrite(boolean block) throws IOException {
+ socketBufferHandler.configureWriteBufferForRead();
+ doWrite(block, socketBufferHandler.getWriteBuffer());
+ }
+
+
+ /**
+ * 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
+ *
+ * @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;
+
+
+ protected void addToBuffers(byte[] buf, int offset, int length) {
+ ByteBufferHolder holder = getByteBufferHolder(length);
+ holder.getBuf().put(buf, offset, length);
+ }
+
+
+ protected void addToBuffers(ByteBuffer from) {
+ ByteBufferHolder holder = getByteBufferHolder(from.remaining());
+ holder.getBuf().put(from);
+ }
+
+
+ private ByteBufferHolder getByteBufferHolder(int capacity) {
+ ByteBufferHolder holder = bufferedWrites.peekLast();
+ if (holder == null || holder.isFlipped() || holder.getBuf().remaining() < capacity) {
+ ByteBuffer buffer = ByteBuffer.allocate(Math.max(bufferedWriteSize, capacity));
+ holder = new ByteBufferHolder(buffer, false);
+ bufferedWrites.add(holder);
+ }
+ return holder;
+ }
+
+
+ public void processSocket(SocketEvent socketStatus, boolean dispatch) {
+ endpoint.processSocket(this, socketStatus, dispatch);
+ }
+
+
+ public synchronized void executeNonBlockingDispatches(Iterator<DispatchType> dispatches) {
+ /*
+ * This method is called when non-blocking IO is initiated by defining
+ * a read and/or write listener in a non-container thread. It is called
+ * once the non-container thread completes so that the first calls to
+ * onWritePossible() and/or onDataAvailable() as appropriate are made by
+ * the container.
+ *
+ * Processing the dispatches requires (for APR/native at least)
+ * that the socket has been added to the waitingRequests queue. This may
+ * not have occurred by the time that the non-container thread completes
+ * triggering the call to this method. Therefore, the coded syncs on the
+ * SocketWrapper as the container thread that initiated this
+ * non-container thread holds a lock on the SocketWrapper. The container
+ * thread will add the socket to the waitingRequests queue before
+ * releasing the lock on the socketWrapper. Therefore, by obtaining the
+ * lock on socketWrapper before processing the dispatches, we can be
+ * sure that the socket has been added to the waitingRequests queue.
+ */
+ while (dispatches != null && dispatches.hasNext()) {
+ DispatchType dispatchType = dispatches.next();
+ processSocket(dispatchType.getSocketStatus(), false);
+ }
+ }
+
+
+ public abstract void registerReadInterest();
+
+ public abstract void registerWriteInterest();
+
+ 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 that the caller <b>will not</b> add
+ * the socket to the poller (or equivalent). That is the responsibility of
+ * this method.
+ *
+ * @param sendfileData Data representing the file to send
+ *
+ * @return The state of the sendfile process after the first write.
+ */
+ public abstract SendfileState processSendfile(SendfileDataBase sendfileData);
+
+ /**
+ * 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
+ */
+ public abstract void doClientAuth(SSLSupport sslSupport);
+
+ public abstract SSLSupport getSslSupport(String clientCertProvider);
+
+
+ // ------------------------------------------------------- NIO 2 style APIs
+
+
+ public enum CompletionState {
+ /**
+ * Operation is still pending.
+ */
+ PENDING,
+ /**
+ * The operation completed inline.
+ */
+ INLINE,
+ /**
+ * The operation completed inline but failed.
+ */
+ ERROR,
+ /**
+ * The operation completed, but not inline.
+ */
+ DONE
+ }
+
+ public enum CompletionHandlerCall {
+ /**
+ * Operation should continue, the completion handler shouldn't be
+ * called.
+ */
+ CONTINUE,
+ /**
+ * The operation completed but the completion handler shouldn't be
+ * called.
+ */
+ NONE,
+ /**
+ * 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.
+ *
+ * @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
+ */
+ public 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.
+ */
+ 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 < offset; i++) {
+ if (buffers[i].remaining() > 0) {
+ return CompletionHandlerCall.CONTINUE;
+ }
+ }
+ return (state == CompletionState.DONE) ? CompletionHandlerCall.DONE
+ : CompletionHandlerCall.NONE;
+ }
+ };
+
+ /**
+ * 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;
+ }
+ };
+
+ /**
+ * Allows using NIO2 style read/write only for connectors that can
+ * efficiently support it.
+ *
+ * @return This default implementation always returns {@code false}
+ */
+ public boolean hasAsyncIO() {
+ return false;
+ }
+
+ /**
+ * Allows checking if an asynchronous read operation is currently pending.
+ * @return <code>true</code> if the endpoint supports asynchronous IO and
+ * a read operation is being processed asynchronously
+ */
+ public boolean isReadPending() {
+ return false;
+ }
+
+ /**
+ * Allows checking if an asynchronous write operation is currently pending.
+ * @return <code>true</code> if the endpoint supports asynchronous IO and
+ * a write operation is being processed asynchronously
+ */
+ public boolean isWritePending() {
+ return false;
+ }
+
+ /**
+ * If an asynchronous read operation is pending, this method will block
+ * until the operation completes, or the specified amount of time
+ * has passed.
+ * @param timeout The maximum amount of time to wait
+ * @param unit The unit for the timeout
+ * @return <code>true</code> if the read operation is complete,
+ * <code>false</code> if the operation is still pending and
+ * the specified timeout has passed
+ */
+ public boolean awaitReadComplete(long timeout, TimeUnit unit) {
+ return true;
+ }
+
+ /**
+ * If an asynchronous write operation is pending, this method will block
+ * until the operation completes, or the specified amount of time
+ * has passed.
+ * @param timeout The maximum amount of time to wait
+ * @param unit The unit for the timeout
+ * @return <code>true</code> if the read operation is complete,
+ * <code>false</code> if the operation is still pending and
+ * the specified timeout has passed
+ */
+ public boolean awaitWriteComplete(long timeout, TimeUnit unit) {
+ return true;
+ }
+
+ /**
+ * 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 true to block until any pending read is done, if the
+ * timeout occurs and a read is still pending, a
+ * ReadPendingException will be thrown; false to
+ * not block but any pending read operation will cause
+ * a ReadPendingException
+ * @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 <A> The attachment type
+ * @return the completion state (done, done inline, or still pending)
+ */
+ public final <A> CompletionState read(boolean block, long timeout, TimeUnit unit, A attachment,
+ CompletionCheck check, CompletionHandler<Long, ? super A> handler, ByteBuffer... dsts) {
+ if (dsts == null) {
+ throw new IllegalArgumentException();
+ }
+ return read(dsts, 0, dsts.length, block, timeout, unit, attachment, check, handler);
+ }
+
+ /**
+ * 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 true to block until any pending read is done, if the
+ * timeout occurs and a read is still pending, a
+ * ReadPendingException will be thrown; false to
+ * not block but any pending read operation will cause
+ * a ReadPendingException
+ * @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 <A> The attachment type
+ * @return the completion state (done, done inline, or still pending)
+ */
+ public <A> CompletionState read(ByteBuffer[] dsts, int offset, int length, boolean block,
+ long timeout, TimeUnit unit, A attachment, CompletionCheck check,
+ CompletionHandler<Long, ? super A> handler) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * 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 true to block until any pending write is done, if the
+ * timeout occurs and a write is still pending, a
+ * WritePendingException will be thrown; false to
+ * not block but any pending write operation will cause
+ * a WritePendingException
+ * @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 <A> The attachment type
+ * @return the completion state (done, done inline, or still pending)
+ */
+ public final <A> CompletionState write(boolean block, long timeout, TimeUnit unit, A attachment,
+ CompletionCheck check, CompletionHandler<Long, ? super A> handler, ByteBuffer... srcs) {
+ if (srcs == null) {
+ throw new IllegalArgumentException();
+ }
+ return write(srcs, 0, srcs.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. 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 true to block until any pending write is done, if the
+ * timeout occurs and a write is still pending, a
+ * WritePendingException will be thrown; false to
+ * not block but any pending write operation will cause
+ * a WritePendingException
+ * @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 <A> The attachment type
+ * @return the completion state (done, done inline, or still pending)
+ */
+ public <A> CompletionState write(ByteBuffer[] srcs, int offset, int length, boolean block,
+ long timeout, TimeUnit unit, A attachment, CompletionCheck check,
+ CompletionHandler<Long, ? super A> handler) {
+ throw new UnsupportedOperationException();
+ }
+
+
+ // --------------------------------------------------------- Utility methods
+
+ protected static int transfer(byte[] from, int offset, int length, ByteBuffer to) {
+ int max = Math.min(length, to.remaining());
+ if (max > 0) {
+ to.put(from, offset, max);
+ }
+ return max;
+ }
+
+ protected static int transfer(ByteBuffer from, ByteBuffer to) {
+ int max = Math.min(from.remaining(), to.remaining());
+ if (max > 0) {
+ int fromLimit = from.limit();
+ from.limit(from.position() + max);
+ to.put(from);
+ from.limit(fromLimit);
+ }
+ return max;
+ }
+}
diff --git a/java/org/apache/tomcat/util/net/TLSClientHelloExtractor.java b/java/org/apache/tomcat/util/net/TLSClientHelloExtractor.java
new file mode 100644
index 0000000..77d793d
--- /dev/null
+++ b/java/org/apache/tomcat/util/net/TLSClientHelloExtractor.java
@@ -0,0 +1,252 @@
+/*
+ * 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.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
+import org.apache.tomcat.util.net.openssl.ciphers.Cipher;
+import org.apache.tomcat.util.res.StringManager;
+
+/**
+ * This class extracts the SNI host name from a TLS client-hello message.
+ */
+public class TLSClientHelloExtractor {
+
+ private static final Log log = LogFactory.getLog(TLSClientHelloExtractor.class);
+ private static final StringManager sm = StringManager.getManager(TLSClientHelloExtractor.class);
+
+ private final ExtractorResult result;
+ private final List<Cipher> clientRequestedCiphers;
+ private final String sniValue;
+
+ private static final int TLS_RECORD_HEADER_LEN = 5;
+
+
+ /**
+ * 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
+ */
+ public TLSClientHelloExtractor(ByteBuffer netInBuffer) {
+ // TODO: Detect use of http on a secure connection and provide a simple
+ // error page.
+
+ // Buffer is in write mode at this point. Record the current position so
+ // the buffer state can be restored at the end of this method.
+ int pos = netInBuffer.position();
+ int limit = netInBuffer.limit();
+ ExtractorResult result = ExtractorResult.NOT_PRESENT;
+ List<Cipher> clientRequestedCiphers = new ArrayList<>();
+ String sniValue = null;
+ try {
+ // Switch to read mode.
+ netInBuffer.flip();
+
+ // A complete TLS record header is required before we can figure out
+ // how many bytes there are in the record.
+ if (!isAvailable(netInBuffer, TLS_RECORD_HEADER_LEN)) {
+ result = handleIncompleteRead(netInBuffer);
+ return;
+ }
+
+ if (!isTLSHandshake(netInBuffer)) {
+ return;
+ }
+
+ if (!isAllRecordAvailable(netInBuffer)) {
+ result = handleIncompleteRead(netInBuffer);
+ return;
+ }
+
+ if (!isClientHello(netInBuffer)) {
+ return;
+ }
+
+ if (!isAllClientHelloAvailable(netInBuffer)) {
+ // Client hello didn't fit into single TLS record.
+ // Treat this as not present.
+ log.warn(sm.getString("sniExtractor.clientHelloTooBig"));
+ return;
+ }
+
+ // Protocol Version
+ skipBytes(netInBuffer, 2);
+ // Random
+ skipBytes(netInBuffer, 32);
+ // Session ID (single byte for length)
+ skipBytes(netInBuffer, (netInBuffer.get() & 0xFF));
+
+ // Cipher Suites
+ // (2 bytes for length, each cipher ID is 2 bytes)
+ int cipherCount = netInBuffer.getChar() / 2;
+ for (int i = 0; i < cipherCount; i++) {
+ int cipherId = netInBuffer.getChar();
+ clientRequestedCiphers.add(Cipher.valueOf(cipherId));
+ }
+
+ // Compression methods (single byte for length)
+ skipBytes(netInBuffer, (netInBuffer.get() & 0xFF));
+
+ if (!netInBuffer.hasRemaining()) {
+ // No more data means no extensions present
+ return;
+ }
+
+ // Extension length
+ skipBytes(netInBuffer, 2);
+ // Read the extensions until we run out of data or find the SNI
+ while (netInBuffer.hasRemaining() && sniValue == null) {
+ sniValue = readSniExtension(netInBuffer);
+ }
+ if (sniValue != null) {
+ result = ExtractorResult.COMPLETE;
+ }
+ } finally {
+ this.result = result;
+ this.clientRequestedCiphers = clientRequestedCiphers;
+ this.sniValue = sniValue;
+ // Whatever happens, return the buffer to its original state
+ netInBuffer.limit(limit);
+ netInBuffer.position(pos);
+ }
+ }
+
+
+ public ExtractorResult getResult() {
+ return result;
+ }
+
+
+ public String getSNIValue() {
+ if (result == ExtractorResult.COMPLETE) {
+ return sniValue;
+ } else {
+ throw new IllegalStateException();
+ }
+ }
+
+
+ public List<Cipher> getClientRequestedCiphers() {
+ if (result == ExtractorResult.COMPLETE || result == ExtractorResult.NOT_PRESENT) {
+ return clientRequestedCiphers;
+ } else {
+ throw new IllegalStateException();
+ }
+ }
+
+
+ private static ExtractorResult handleIncompleteRead(ByteBuffer bb) {
+ if (bb.limit() == bb.capacity()) {
+ // Buffer not big enough
+ return ExtractorResult.UNDERFLOW;
+ } else {
+ // Need to read more data into buffer
+ return ExtractorResult.NEED_READ;
+ }
+ }
+
+
+ private static boolean isAvailable(ByteBuffer bb, int size) {
+ if (bb.remaining() < size) {
+ bb.position(bb.limit());
+ return false;
+ }
+ return true;
+ }
+
+
+ private static boolean isTLSHandshake(ByteBuffer bb) {
+ // For a TLS client hello the first byte must be 22 - handshake
+ if (bb.get() != 22) {
+ return false;
+ }
+ // 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;
+ }
+
+
+ private static boolean isAllRecordAvailable(ByteBuffer bb) {
+ // Next two bytes (unsigned) are the size of the record. We need all of
+ // it.
+ int size = bb.getChar();
+ return isAvailable(bb, size);
+ }
+
+
+ private static boolean isClientHello(ByteBuffer bb) {
+ // Client hello is handshake type 1
+ if (bb.get() == 1) {
+ return true;
+ }
+ return false;
+ }
+
+
+ private static boolean isAllClientHelloAvailable(ByteBuffer bb) {
+ // Next three bytes (unsigned) are the size of the client hello. We need
+ // all of it.
+ int size = ((bb.get() & 0xFF) << 16) + ((bb.get() & 0xFF) << 8) + (bb.get() & 0xFF);
+ return isAvailable(bb, size);
+ }
+
+
+ private static void skipBytes(ByteBuffer bb, int size) {
+ bb.position(bb.position() + size);
+ }
+
+
+ private static String readSniExtension(ByteBuffer bb) {
+ // SNI extension is type 0
+ char extensionType = bb.getChar();
+ // Next byte is data size
+ char extensionDataSize = bb.getChar();
+ if (extensionType == 0) {
+ // First 2 bytes are size of server name list (only expecting one)
+ // Next byte is type (0 for hostname)
+ skipBytes(bb, 3);
+ // Next 2 bytes are length of host name
+ char serverNameSize = bb.getChar();
+ byte[] serverNameBytes = new byte[serverNameSize];
+ bb.get(serverNameBytes);
+ return new String(serverNameBytes, StandardCharsets.UTF_8);
+ } else {
+ skipBytes(bb, extensionDataSize);
+ }
+ return null;
+ }
+
+
+ public static enum ExtractorResult {
+ COMPLETE,
+ NOT_PRESENT,
+ UNDERFLOW,
+ NEED_READ
+ }
+}
diff --git a/java/org/apache/tomcat/util/net/URL.java b/java/org/apache/tomcat/util/net/URL.java
deleted file mode 100644
index 77deae3..0000000
--- a/java/org/apache/tomcat/util/net/URL.java
+++ /dev/null
@@ -1,661 +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.net;
-
-
-import java.io.Serializable;
-import java.net.MalformedURLException;
-import java.util.Locale;
-
-
-/**
- * <p><strong>URL</strong> is designed to provide public APIs for parsing
- * and synthesizing Uniform Resource Locators as similar as possible to the
- * APIs of <code>java.net.URL</code>, but without the ability to open a
- * stream or connection. One of the consequences of this is that you can
- * construct URLs for protocols for which a URLStreamHandler is not
- * available (such as an "https" URL when JSSE is not installed).</p>
- *
- * <p><strong>WARNING</strong> - This class assumes that the string
- * representation of a URL conforms to the <code>spec</code> argument
- * as described in RFC 2396 "Uniform Resource Identifiers: Generic Syntax":</p>
- * <pre>
- * <scheme>//<authority><path>?<query>#<fragment>
- * </pre>
- *
- * <p><strong>FIXME</strong> - This class really ought to end up in a Commons
- * package someplace.</p>
- *
- * @author Craig R. McClanahan
- *
- * @deprecated Will be removed in Tomcat 8.5.x. Use {@link java.net.URI} or
- * {@link org.apache.tomcat.util.buf.UriUtil}.
- */
- at Deprecated
-public final class URL implements Serializable {
-
- private static final long serialVersionUID = 1L;
-
-
- // ----------------------------------------------------------- Constructors
-
- /**
- * Create a URL object from the specified String representation.
- *
- * @param spec String representation of the URL
- *
- * @exception MalformedURLException if the string representation
- * cannot be parsed successfully
- */
- public URL(String spec) throws MalformedURLException {
-
- this(null, spec);
-
- }
-
-
- /**
- * Create a URL object by parsing a string representation relative
- * to a specified context. Based on logic from JDK 1.3.1's
- * <code>java.net.URL</code>.
- *
- * @param context URL against which the relative representation
- * is resolved
- * @param spec String representation of the URL (usually relative)
- *
- * @exception MalformedURLException if the string representation
- * cannot be parsed successfully
- */
- public URL(URL context, String spec) throws MalformedURLException {
-
- String original = spec;
- int i, limit, c;
- int start = 0;
- String newProtocol = null;
- boolean aRef = false;
-
- try {
-
- // Eliminate leading and trailing whitespace
- limit = spec.length();
- while ((limit > 0) && (spec.charAt(limit - 1) <= ' ')) {
- limit--;
- }
- while ((start < limit) && (spec.charAt(start) <= ' ')) {
- start++;
- }
-
- // If the string representation starts with "url:", skip it
- if (spec.regionMatches(true, start, "url:", 0, 4)) {
- start += 4;
- }
-
- // Is this a ref relative to the context URL?
- if ((start < spec.length()) && (spec.charAt(start) == '#')) {
- aRef = true;
- }
-
- // Parse out the new protocol
- for (i = start; !aRef && (i < limit) ; i++) {
- c = spec.charAt(i);
- if (c == ':') {
- String s = spec.substring(start, i).toLowerCase(Locale.ENGLISH);
- // Assume all protocols are valid
- newProtocol = s;
- start = i + 1;
- break;
- } else if( c == '#' ) {
- aRef = true;
- } else if( !isSchemeChar((char)c) ) {
- break;
- }
- }
-
- // Only use our context if the protocols match
- protocol = newProtocol;
- if ((context != null) && ((newProtocol == null) ||
- newProtocol.equalsIgnoreCase(context.getProtocol()))) {
- // If the context is a hierarchical URL scheme and the spec
- // contains a matching scheme then maintain backwards
- // compatibility and treat it as if the spec didn't contain
- // the scheme; see 5.2.3 of RFC2396
- if ((context.getPath() != null) &&
- (context.getPath().startsWith("/")))
- newProtocol = null;
- if (newProtocol == null) {
- protocol = context.getProtocol();
- authority = context.getAuthority();
- userInfo = context.getUserInfo();
- host = context.getHost();
- port = context.getPort();
- file = context.getFile();
- int question = file.lastIndexOf('?');
- if (question < 0)
- path = file;
- else
- path = file.substring(0, question);
- }
- }
-
- if (protocol == null)
- throw new MalformedURLException("no protocol: " + original);
-
- // Parse out any ref portion of the spec
- i = spec.indexOf('#', start);
- if (i >= 0) {
- ref = spec.substring(i + 1, limit);
- limit = i;
- }
-
- // Parse the remainder of the spec in a protocol-specific fashion
- parse(spec, start, limit);
- if (context != null)
- normalize();
-
-
- } catch (MalformedURLException e) {
- throw e;
- } catch (Exception e) {
- throw new MalformedURLException(e.toString());
- }
-
- }
-
-
- // ----------------------------------------------------- Instance Variables
-
-
- /**
- * The authority part of the URL.
- */
- private String authority = null;
-
-
- /**
- * The filename part of the URL.
- */
- private String file = null;
-
-
- /**
- * The host name part of the URL.
- */
- private String host = null;
-
-
- /**
- * The path part of the URL.
- */
- private String path = null;
-
-
- /**
- * The port number part of the URL.
- */
- private int port = -1;
-
-
- /**
- * The protocol name part of the URL.
- */
- private String protocol = null;
-
-
- /**
- * The query part of the URL.
- */
- private String query = null;
-
-
- /**
- * The reference part of the URL.
- */
- private String ref = null;
-
-
- /**
- * The user info part of the URL.
- */
- private String userInfo = null;
-
-
- // --------------------------------------------------------- Public Methods
-
-
- /**
- * Compare two URLs for equality. The result is <code>true</code> if and
- * only if the argument is not null, and is a <code>URL</code> object
- * that represents the same <code>URL</code> as this object. Two
- * <code>URLs</code> are equal if they have the same protocol and
- * reference the same host, the same port number on the host,
- * and the same file and anchor on the host.
- *
- * @param obj The URL to compare against
- */
- @Override
- public boolean equals(Object obj) {
-
- if (!(obj instanceof URL))
- return (false);
- URL other = (URL) obj;
- if (!sameFile(other))
- return (false);
- return (compare(ref, other.getRef()));
-
- }
-
-
- @Override
- public int hashCode() {
- final int prime = 31;
- int result = 1;
- result = prime * result + ((file == null) ? 0 : file.hashCode());
- result = prime * result + ((host == null) ? 0 : host.hashCode());
- result = prime * result + port;
- result = prime * result +
- ((protocol == null) ? 0 : protocol.hashCode());
- result = prime * result + ((ref == null) ? 0 : ref.hashCode());
- return result;
- }
-
-
- /**
- * Return the authority part of the URL.
- */
- public String getAuthority() {
-
- return (this.authority);
-
- }
-
-
- /**
- * Return the filename part of the URL. <strong>NOTE</strong> - For
- * compatibility with <code>java.net.URL</code>, this value includes
- * the query string if there was one. For just the path portion,
- * call <code>getPath()</code> instead.
- */
- public String getFile() {
-
- if (file == null)
- return ("");
- return (this.file);
-
- }
-
-
- /**
- * Return the host name part of the URL.
- */
- public String getHost() {
-
- return (this.host);
-
- }
-
-
- /**
- * Return the path part of the URL.
- */
- public String getPath() {
-
- if (this.path == null)
- return ("");
- return (this.path);
-
- }
-
-
- /**
- * Return the port number part of the URL.
- */
- public int getPort() {
-
- return (this.port);
-
- }
-
-
- /**
- * Return the protocol name part of the URL.
- */
- public String getProtocol() {
-
- return (this.protocol);
-
- }
-
-
- /**
- * Return the query part of the URL.
- */
- public String getQuery() {
-
- return (this.query);
-
- }
-
-
- /**
- * Return the reference part of the URL.
- */
- public String getRef() {
-
- return (this.ref);
-
- }
-
-
- /**
- * Return the user info part of the URL.
- */
- public String getUserInfo() {
-
- return (this.userInfo);
-
- }
-
-
- /**
- * Normalize the <code>path</code> (and therefore <code>file</code>)
- * portions of this URL.
- * <p>
- * <strong>NOTE</strong> - This method is not part of the public API
- * of <code>java.net.URL</code>, but is provided as a value added
- * service of this implementation.
- *
- * @exception MalformedURLException if a normalization error occurs,
- * such as trying to move about the hierarchical root
- */
- public void normalize() throws MalformedURLException {
-
- // Special case for null path
- if (path == null) {
- if (query != null)
- file = "?" + query;
- else
- file = "";
- return;
- }
-
- // Create a place for the normalized path
- String normalized = path;
- if (normalized.equals("/.")) {
- path = "/";
- if (query != null)
- file = path + "?" + query;
- else
- file = path;
- return;
- }
-
- // Normalize the slashes and add leading slash if necessary
- if (normalized.indexOf('\\') >= 0)
- normalized = normalized.replace('\\', '/');
- if (!normalized.startsWith("/"))
- normalized = "/" + normalized;
-
- // Resolve occurrences of "//" in the normalized path
- while (true) {
- int index = normalized.indexOf("//");
- if (index < 0)
- break;
- normalized = normalized.substring(0, index) +
- normalized.substring(index + 1);
- }
-
- // Resolve occurrences of "/./" in the normalized path
- while (true) {
- int index = normalized.indexOf("/./");
- if (index < 0)
- break;
- normalized = normalized.substring(0, index) +
- normalized.substring(index + 2);
- }
-
- // Resolve occurrences of "/../" in the normalized path
- while (true) {
- int index = normalized.indexOf("/../");
- if (index < 0)
- break;
- if (index == 0)
- throw new MalformedURLException
- ("Invalid relative URL reference");
- int index2 = normalized.lastIndexOf('/', index - 1);
- normalized = normalized.substring(0, index2) +
- normalized.substring(index + 3);
- }
-
- // Resolve occurrences of "/." at the end of the normalized path
- if (normalized.endsWith("/."))
- normalized = normalized.substring(0, normalized.length() - 1);
-
- // Resolve occurrences of "/.." at the end of the normalized path
- if (normalized.endsWith("/..")) {
- int index = normalized.length() - 3;
- int index2 = normalized.lastIndexOf('/', index - 1);
- if (index2 < 0)
- throw new MalformedURLException
- ("Invalid relative URL reference");
- normalized = normalized.substring(0, index2 + 1);
- }
-
- // Return the normalized path that we have completed
- path = normalized;
- if (query != null)
- file = path + "?" + query;
- else
- file = path;
-
- }
-
-
- /**
- * Compare two URLs, excluding the "ref" fields. Returns <code>true</code>
- * if this <code>URL</code> and the <code>other</code> argument both refer
- * to the same resource. The two <code>URLs</code> might not both contain
- * the same anchor.
- */
- public boolean sameFile(URL other) {
-
- if (!compare(protocol, other.getProtocol()))
- return (false);
- if (!compare(host, other.getHost()))
- return (false);
- if (port != other.getPort())
- return (false);
- if (!compare(file, other.getFile()))
- return (false);
- return (true);
-
- }
-
-
- /**
- * Return a string representation of this object.
- */
- @Override
- public String toString() {
-
- StringBuilder sb = new StringBuilder("URL[");
- sb.append("authority=");
- sb.append(authority);
- sb.append(", file=");
- sb.append(file);
- sb.append(", host=");
- sb.append(host);
- sb.append(", port=");
- sb.append(port);
- sb.append(", protocol=");
- sb.append(protocol);
- sb.append(", query=");
- sb.append(query);
- sb.append(", ref=");
- sb.append(ref);
- sb.append(", userInfo=");
- sb.append(userInfo);
- sb.append("]");
- return (sb.toString());
-
- // return (toExternalForm());
-
- }
-
-
- // -------------------------------------------------------- Private Methods
-
-
- /**
- * Compare to String values for equality, taking appropriate care if one
- * or both of the values are <code>null</code>.
- *
- * @param first First string
- * @param second Second string
- */
- private boolean compare(String first, String second) {
-
- if (first == null) {
- if (second == null)
- return (true);
- else
- return (false);
- } else {
- if (second == null)
- return (false);
- else
- return (first.equals(second));
- }
-
- }
-
-
- /**
- * Parse the specified portion of the string representation of a URL,
- * assuming that it has a format similar to that for <code>http</code>.
- *
- * <p><strong>FIXME</strong> - This algorithm can undoubtedly be optimized
- * for performance. However, that needs to wait until after sufficient
- * unit tests are implemented to guarantee correct behavior with no
- * regressions.</p>
- *
- * @param spec String representation being parsed
- * @param start Starting offset, which will be just after the ':' (if
- * there is one) that determined the protocol name
- * @param limit Ending position, which will be the position of the '#'
- * (if there is one) that delimited the anchor
- *
- * @exception MalformedURLException if a parsing error occurs
- */
- private void parse(String spec, int start, int limit)
- throws MalformedURLException {
-
- // Trim the query string (if any) off the tail end
- int question = spec.lastIndexOf('?', limit - 1);
- if ((question >= 0) && (question < limit)) {
- query = spec.substring(question + 1, limit);
- limit = question;
- } else {
- query = null;
- }
-
- // Parse the authority section
- if (spec.indexOf("//", start) == start) {
- int pathStart = spec.indexOf('/', start + 2);
- if ((pathStart >= 0) && (pathStart < limit)) {
- authority = spec.substring(start + 2, pathStart);
- start = pathStart;
- } else {
- authority = spec.substring(start + 2, limit);
- start = limit;
- }
- if (authority.length() > 0) {
- int at = authority.indexOf('@');
- if( at >= 0 ) {
- userInfo = authority.substring(0,at);
- }
- int ipv6 = authority.indexOf('[',at+1);
- int hStart = at+1;
- if( ipv6 >= 0 ) {
- hStart = ipv6;
- ipv6 = authority.indexOf(']', ipv6);
- if( ipv6 < 0 ) {
- throw new MalformedURLException(
- "Closing ']' not found in IPV6 address: " + authority);
- } else {
- at = ipv6-1;
- }
- }
-
- int colon = authority.indexOf(':', at+1);
- if (colon >= 0) {
- try {
- port =
- Integer.parseInt(authority.substring(colon + 1));
- } catch (NumberFormatException e) {
- throw new MalformedURLException(e.toString());
- }
- host = authority.substring(hStart, colon);
- } else {
- host = authority.substring(hStart);
- port = -1;
- }
- }
- }
-
- // Parse the path section
- if (spec.indexOf('/', start) == start) { // Absolute path
- path = spec.substring(start, limit);
- if (query != null)
- file = path + "?" + query;
- else
- file = path;
- return;
- }
-
- // Resolve relative path against our context's file
- if (path == null) {
- if (query != null)
- file = "?" + query;
- else
- file = null;
- return;
- }
- if (!path.startsWith("/"))
- throw new MalformedURLException
- ("Base path does not start with '/'");
- if (!path.endsWith("/"))
- path += "/../";
- path += spec.substring(start, limit);
- if (query != null)
- file = path + "?" + query;
- else
- file = path;
- return;
-
- }
-
- /**
- * Determine if the character is allowed in the scheme of a URI.
- * See RFC 2396, Section 3.1
- */
- public static boolean isSchemeChar(char c) {
- return Character.isLetterOrDigit(c) ||
- c == '+' || c == '-' || c == '.';
- }
-
-}
diff --git a/java/org/apache/tomcat/util/net/jsse/JSSEImplementation.java b/java/org/apache/tomcat/util/net/jsse/JSSEImplementation.java
index c9e446a..a2833d3 100644
--- a/java/org/apache/tomcat/util/net/jsse/JSSEImplementation.java
+++ b/java/org/apache/tomcat/util/net/jsse/JSSEImplementation.java
@@ -14,19 +14,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
package org.apache.tomcat.util.net.jsse;
-import java.net.Socket;
-
import javax.net.ssl.SSLSession;
-import javax.net.ssl.SSLSocket;
-import org.apache.tomcat.util.net.AbstractEndpoint;
+import org.apache.tomcat.util.net.SSLHostConfigCertificate;
import org.apache.tomcat.util.net.SSLImplementation;
import org.apache.tomcat.util.net.SSLSupport;
import org.apache.tomcat.util.net.SSLUtil;
-import org.apache.tomcat.util.net.ServerSocketFactory;
/* JSSEImplementation:
@@ -45,27 +40,12 @@ public class JSSEImplementation extends SSLImplementation {
}
@Override
- public String getImplementationName(){
- return "JSSE";
- }
-
- @Override
- public ServerSocketFactory getServerSocketFactory(AbstractEndpoint<?> endpoint) {
- return new JSSESocketFactory(endpoint);
- }
-
- @Override
- public SSLSupport getSSLSupport(Socket s) {
- return new JSSESupport((SSLSocket) s);
- }
-
- @Override
public SSLSupport getSSLSupport(SSLSession session) {
return new JSSESupport(session);
}
@Override
- public SSLUtil getSSLUtil(AbstractEndpoint<?> endpoint) {
- return new JSSESocketFactory(endpoint);
+ public SSLUtil getSSLUtil(SSLHostConfigCertificate certificate) {
+ return new JSSEUtil(certificate);
}
}
diff --git a/java/org/apache/tomcat/util/net/jsse/JSSEKeyManager.java b/java/org/apache/tomcat/util/net/jsse/JSSEKeyManager.java
index 5759944..e68e0df 100644
--- a/java/org/apache/tomcat/util/net/jsse/JSSEKeyManager.java
+++ b/java/org/apache/tomcat/util/net/jsse/JSSEKeyManager.java
@@ -38,6 +38,7 @@ public final class JSSEKeyManager extends X509ExtendedKeyManager {
private X509KeyManager delegate;
private String serverKeyAlias;
+
/**
* Constructor.
*
@@ -51,137 +52,72 @@ public final class JSSEKeyManager extends X509ExtendedKeyManager {
this.serverKeyAlias = serverKeyAlias;
}
+
/**
- * Choose an alias to authenticate the client side of a secure socket,
- * given the public key type and the list of certificate issuer authorities
- * recognized by the peer (if any).
- *
- * @param keyType The key algorithm type name(s), ordered with the
- * most-preferred key type first
- * @param issuers The list of acceptable CA issuer subject names, or null
- * if it does not matter which issuers are used
- * @param socket The socket to be used for this connection. This parameter
- * can be null, in which case this method will return the most generic
- * alias to use
- *
- * @return The alias name for the desired key, or null if there are no
- * matches
+ * 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 chooseClientAlias(String[] keyType, Principal[] issuers,
- Socket socket) {
- return delegate.chooseClientAlias(keyType, issuers, socket);
+ public String chooseServerAlias(String keyType, Principal[] issuers, Socket socket) {
+ if (serverKeyAlias != null) {
+ return serverKeyAlias;
+ }
+
+ return delegate.chooseServerAlias(keyType, issuers, socket);
}
+
/**
- * Returns this key manager's server key alias that was provided in the
- * constructor.
- *
- * @param keyType Ignored
- * @param issuers Ignored
- * @param socket Ignored
- *
- * @return Alias name for the desired key
+ * 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 chooseServerAlias(String keyType, Principal[] issuers,
+ public String chooseEngineServerAlias(String keyType, Principal[] issuers,
+ SSLEngine engine) {
+ if (serverKeyAlias!=null) {
+ return serverKeyAlias;
+ }
+
+ return super.chooseEngineServerAlias(keyType, issuers, engine);
+ }
+
+
+ @Override
+ public String chooseClientAlias(String[] keyType, Principal[] issuers,
Socket socket) {
- return serverKeyAlias;
+ return delegate.chooseClientAlias(keyType, issuers, socket);
}
- /**
- * Returns the certificate chain associated with the given alias.
- *
- * @param alias The alias name
- *
- * @return Certificate chain (ordered with the user's certificate first
- * and the root certificate authority last), or null if the alias can't be
- * found
- */
+
@Override
public X509Certificate[] getCertificateChain(String alias) {
return delegate.getCertificateChain(alias);
}
- /**
- * Get the matching aliases for authenticating the client side of a secure
- * socket, given the public key type and the list of certificate issuer
- * authorities recognized by the peer (if any).
- *
- * @param keyType The key algorithm type name
- * @param issuers The list of acceptable CA issuer subject names, or null
- * if it does not matter which issuers are used
- *
- * @return Array of the matching alias names, or null if there were no
- * matches
- */
+
@Override
public String[] getClientAliases(String keyType, Principal[] issuers) {
return delegate.getClientAliases(keyType, issuers);
}
- /**
- * Get the matching aliases for authenticating the server side of a secure
- * socket, given the public key type and the list of certificate issuer
- * authorities recognized by the peer (if any).
- *
- * @param keyType The key algorithm type name
- * @param issuers The list of acceptable CA issuer subject names, or null
- * if it does not matter which issuers are used
- *
- * @return Array of the matching alias names, or null if there were no
- * matches
- */
+
@Override
public String[] getServerAliases(String keyType, Principal[] issuers) {
return delegate.getServerAliases(keyType, issuers);
}
- /**
- * Returns the key associated with the given alias.
- *
- * @param alias The alias name
- *
- * @return The requested key, or null if the alias can't be found
- */
+
@Override
public PrivateKey getPrivateKey(String alias) {
return delegate.getPrivateKey(alias);
}
- /**
- * Choose an alias to authenticate the client side of a secure socket,
- * given the public key type and the list of certificate issuer authorities
- * recognized by the peer (if any).
- *
- * @param keyType The key algorithm type name(s), ordered with the
- * most-preferred key type first
- * @param issuers The list of acceptable CA issuer subject names, or null
- * if it does not matter which issuers are used
- * @param engine Ignored
- *
- * @return The alias name for the desired key, or null if there are no
- * matches
- */
+
@Override
public String chooseEngineClientAlias(String[] keyType, Principal[] issuers,
SSLEngine engine) {
return delegate.chooseClientAlias(keyType, issuers, null);
}
-
- /**
- * Returns this key manager's server key alias that was provided in the
- * constructor.
- *
- * @param keyType Ignored
- * @param issuers Ignored
- * @param engine Ignored
- *
- * @return Alias name for the desired key
- */
- @Override
- public String chooseEngineServerAlias(String keyType, Principal[] issuers,
- SSLEngine engine) {
- return serverKeyAlias;
- }
}
diff --git a/java/org/apache/tomcat/util/net/jsse/JSSESSLContext.java b/java/org/apache/tomcat/util/net/jsse/JSSESSLContext.java
new file mode 100644
index 0000000..c4ec83d
--- /dev/null
+++ b/java/org/apache/tomcat/util/net/jsse/JSSESSLContext.java
@@ -0,0 +1,70 @@
+/*
+ * 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.jsse;
+
+import java.security.KeyManagementException;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+
+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 org.apache.tomcat.util.net.SSLContext;
+
+class JSSESSLContext implements SSLContext {
+
+ private javax.net.ssl.SSLContext context;
+ JSSESSLContext(String protocol) throws NoSuchAlgorithmException {
+ context = javax.net.ssl.SSLContext.getInstance(protocol);
+ }
+
+ @Override
+ public void init(KeyManager[] kms, TrustManager[] tms, SecureRandom sr)
+ throws KeyManagementException {
+ context.init(kms, tms, sr);
+ }
+
+ @Override
+ public void destroy() {
+ }
+
+ @Override
+ public SSLSessionContext getServerSessionContext() {
+ return context.getServerSessionContext();
+ }
+
+ @Override
+ public SSLEngine createSSLEngine() {
+ return context.createSSLEngine();
+ }
+
+ @Override
+ public SSLServerSocketFactory getServerSocketFactory() {
+ return context.getServerSocketFactory();
+ }
+
+ @Override
+ public SSLParameters getSupportedSSLParameters() {
+ return context.getSupportedSSLParameters();
+ }
+
+}
diff --git a/java/org/apache/tomcat/util/net/jsse/JSSESocketFactory.java b/java/org/apache/tomcat/util/net/jsse/JSSESocketFactory.java
deleted file mode 100644
index e1d14f2..0000000
--- a/java/org/apache/tomcat/util/net/jsse/JSSESocketFactory.java
+++ /dev/null
@@ -1,858 +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.net.jsse;
-
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.InetAddress;
-import java.net.ServerSocket;
-import java.net.Socket;
-import java.net.SocketException;
-import java.security.KeyManagementException;
-import java.security.KeyStore;
-import java.security.NoSuchAlgorithmException;
-import java.security.UnrecoverableKeyException;
-import java.security.cert.CRL;
-import java.security.cert.CRLException;
-import java.security.cert.CertPathParameters;
-import java.security.cert.CertStore;
-import java.security.cert.CertStoreParameters;
-import java.security.cert.CertificateException;
-import java.security.cert.CertificateFactory;
-import java.security.cert.CollectionCertStoreParameters;
-import java.security.cert.PKIXBuilderParameters;
-import java.security.cert.X509CertSelector;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.List;
-import java.util.Locale;
-
-import javax.net.ssl.CertPathTrustManagerParameters;
-import javax.net.ssl.KeyManager;
-import javax.net.ssl.KeyManagerFactory;
-import javax.net.ssl.ManagerFactoryParameters;
-import javax.net.ssl.SSLContext;
-import javax.net.ssl.SSLException;
-import javax.net.ssl.SSLServerSocket;
-import javax.net.ssl.SSLServerSocketFactory;
-import javax.net.ssl.SSLSession;
-import javax.net.ssl.SSLSessionContext;
-import javax.net.ssl.SSLSocket;
-import javax.net.ssl.TrustManager;
-import javax.net.ssl.TrustManagerFactory;
-import javax.net.ssl.X509KeyManager;
-
-import org.apache.tomcat.util.compat.JreCompat;
-import org.apache.tomcat.util.compat.JreVendor;
-import org.apache.tomcat.util.file.ConfigFileLoader;
-import org.apache.tomcat.util.net.AbstractEndpoint;
-import org.apache.tomcat.util.net.Constants;
-import org.apache.tomcat.util.net.SSLUtil;
-import org.apache.tomcat.util.net.ServerSocketFactory;
-import org.apache.tomcat.util.net.jsse.openssl.OpenSSLCipherConfigurationParser;
-import org.apache.tomcat.util.res.StringManager;
-
-/**
- * SSL server socket factory. It <b>requires</b> a valid RSA key and
- * JSSE.<br>
- * keytool -genkey -alias tomcat -keyalg RSA<br>
- * Use "changeit" as password (this is the default we use).
- *
- * @author Harish Prabandham
- * @author Costin Manolache
- * @author Stefan Freyr Stefansson
- * @author EKR -- renamed to JSSESocketFactory
- * @author Jan Luehe
- */
-public class JSSESocketFactory implements ServerSocketFactory, SSLUtil {
-
- private static final org.apache.juli.logging.Log log =
- org.apache.juli.logging.LogFactory.getLog(JSSESocketFactory.class);
- private static final StringManager sm =
- StringManager.getManager("org.apache.tomcat.util.net.jsse.res");
-
- // Defaults - made public where re-used
- private static final String defaultProtocol = Constants.SSL_PROTO_TLS;
- private static final String defaultKeystoreType = "JKS";
- private static final String defaultKeystoreFile
- = System.getProperty("user.home") + "/.keystore";
- private static final int defaultSessionCacheSize = 0;
- private static final int defaultSessionTimeout = 86400;
- private static final String ALLOW_ALL_SUPPORTED_CIPHERS = "ALL";
- public static final String DEFAULT_KEY_PASS = "changeit";
-
- private AbstractEndpoint<?> endpoint;
-
- private final boolean rfc5746Supported;
- private final String[] defaultServerProtocols;
- private final String[] defaultServerCipherSuites;
-
- protected SSLServerSocketFactory sslProxy = null;
- protected String[] enabledCiphers;
- protected String[] enabledProtocols;
- protected boolean allowUnsafeLegacyRenegotiation = false;
-
- /**
- * Flag to state that we require client authentication.
- */
- protected boolean requireClientAuth = false;
-
- /**
- * Flag to state that we would like client authentication.
- */
- protected boolean wantClientAuth = false;
-
-
- public JSSESocketFactory (AbstractEndpoint<?> endpoint) {
- this.endpoint = endpoint;
-
- String sslProtocol = endpoint.getSslProtocol();
- if (sslProtocol == null) {
- sslProtocol = defaultProtocol;
- }
-
- SSLContext context;
- try {
- context = SSLContext.getInstance(sslProtocol);
- context.init(null, null, null);
- } catch (NoSuchAlgorithmException | KeyManagementException e) {
- // This is fatal for the connector so throw an exception to prevent
- // it from starting
- throw new IllegalArgumentException(e);
- }
-
- // Supported cipher suites aren't accessible directly from the
- // SSLContext so use the SSL server socket factory
- SSLServerSocketFactory ssf = context.getServerSocketFactory();
- String supportedCiphers[] = ssf.getSupportedCipherSuites();
- boolean found = false;
- for (String cipher : supportedCiphers) {
- if ("TLS_EMPTY_RENEGOTIATION_INFO_SCSV".equals(cipher)) {
- found = true;
- break;
- }
- }
- rfc5746Supported = found;
-
- // There is no standard way to determine the default protocols and
- // cipher suites so create a server socket to see what the defaults are
- SSLServerSocket socket;
- try {
- socket = (SSLServerSocket) ssf.createServerSocket();
- } catch (IOException e) {
- // This is very likely to be fatal but there is a slim chance that
- // the JSSE implementation just doesn't like creating unbound
- // sockets so allow the code to proceed.
- defaultServerCipherSuites = new String[0];
- defaultServerProtocols = new String[0];
- log.warn(sm.getString("jsse.noDefaultCiphers", endpoint.getName()));
- log.warn(sm.getString("jsse.noDefaultProtocols", endpoint.getName()));
- return;
- }
-
- try {
- defaultServerCipherSuites = socket.getEnabledCipherSuites();
- if (defaultServerCipherSuites.length == 0) {
- log.warn(sm.getString("jsse.noDefaultCiphers",
- endpoint.getName()));
- }
-
- // Filter out all the SSL protocols (SSLv2 and SSLv3) from the
- // defaults
- // since they are no longer considered secure
- List<String> filteredProtocols = new ArrayList<>();
- for (String protocol : socket.getEnabledProtocols()) {
- if (protocol.toUpperCase(Locale.ENGLISH).contains("SSL")) {
- log.debug(sm.getString("jsse.excludeDefaultProtocol",
- protocol));
- continue;
- }
- filteredProtocols.add(protocol);
- }
- defaultServerProtocols = filteredProtocols
- .toArray(new String[filteredProtocols.size()]);
- if (defaultServerProtocols.length == 0) {
- log.warn(sm.getString("jsse.noDefaultProtocols",
- endpoint.getName()));
- }
- } finally {
- try {
- socket.close();
- } catch (IOException e) {
- log.warn(sm.getString("jsse.exceptionOnClose"), e);
- }
- }
- }
-
-
- @Override
- public ServerSocket createSocket (int port)
- throws IOException
- {
- init();
- ServerSocket socket = sslProxy.createServerSocket(port);
- initServerSocket(socket);
- return socket;
- }
-
- @Override
- public ServerSocket createSocket (int port, int backlog)
- throws IOException
- {
- init();
- ServerSocket socket = sslProxy.createServerSocket(port, backlog);
- initServerSocket(socket);
- return socket;
- }
-
- @Override
- public ServerSocket createSocket (int port, int backlog,
- InetAddress ifAddress)
- throws IOException
- {
- init();
- ServerSocket socket = sslProxy.createServerSocket(port, backlog,
- ifAddress);
- initServerSocket(socket);
- return socket;
- }
-
- @Override
- public Socket acceptSocket(ServerSocket socket)
- throws IOException
- {
- SSLSocket asock = null;
- try {
- asock = (SSLSocket)socket.accept();
- } catch (SSLException e){
- throw new SocketException("SSL handshake error" + e.toString());
- }
- return asock;
- }
-
- @Override
- public void handshake(Socket sock) throws IOException {
- // We do getSession instead of startHandshake() so we can call this multiple times
- SSLSession session = ((SSLSocket)sock).getSession();
- if (session.getCipherSuite().equals("SSL_NULL_WITH_NULL_NULL"))
- throw new IOException("SSL handshake failed. Ciper suite in SSL Session is SSL_NULL_WITH_NULL_NULL");
-
- if (!allowUnsafeLegacyRenegotiation && !rfc5746Supported) {
- // Prevent further handshakes by removing all cipher suites
- ((SSLSocket) sock).setEnabledCipherSuites(new String[0]);
- }
- }
-
- @Override
- public String[] getEnableableCiphers(SSLContext context) {
- String requestedCiphersStr = endpoint.getCiphers();
-
- if (ALLOW_ALL_SUPPORTED_CIPHERS.equals(requestedCiphersStr)) {
- return context.getSupportedSSLParameters().getCipherSuites();
- }
- if ((requestedCiphersStr == null)
- || (requestedCiphersStr.trim().length() == 0)) {
- return defaultServerCipherSuites;
- }
-
- List<String> requestedCiphers = new ArrayList<>();
- if (requestedCiphersStr.indexOf(':') != -1) {
- requestedCiphers = OpenSSLCipherConfigurationParser.parseExpression(requestedCiphersStr);
- } else {
- for (String rc : requestedCiphersStr.split(",")) {
- final String cipher = rc.trim();
- if (cipher.length() > 0) {
- requestedCiphers.add(cipher);
- }
- }
- }
- if (requestedCiphers.isEmpty()) {
- return defaultServerCipherSuites;
- }
- List<String> ciphers = new ArrayList<>(requestedCiphers);
- String[] supportedCipherSuiteArray = 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
- // need to filter the requested cipher suites using both forms with an
- // IBM JRE.
- List<String> supportedCipherSuiteList;
- if (JreVendor.IS_IBM_JVM) {
- supportedCipherSuiteList = new ArrayList<>(supportedCipherSuiteArray.length * 2);
- for (String name : supportedCipherSuiteArray) {
- supportedCipherSuiteList.add(name);
- if (name.startsWith("SSL")) {
- supportedCipherSuiteList.add("TLS" + name.substring(3));
- }
- }
- } else {
- supportedCipherSuiteList = Arrays.asList(supportedCipherSuiteArray);
- }
- ciphers.retainAll(supportedCipherSuiteList);
-
- if (ciphers.isEmpty()) {
- log.warn(sm.getString("jsse.requested_ciphers_not_supported",
- requestedCiphersStr));
- }
- if (log.isDebugEnabled()) {
- log.debug(sm.getString("jsse.enableable_ciphers", ciphers));
- if (ciphers.size() != requestedCiphers.size()) {
- List<String> skipped = new ArrayList<>(requestedCiphers);
- skipped.removeAll(ciphers);
- log.debug(sm.getString("jsse.unsupported_ciphers", skipped));
- }
- }
-
- return ciphers.toArray(new String[ciphers.size()]);
- }
-
- public String[] getEnabledCiphers() {
- return enabledCiphers;
- }
-
- /*
- * Gets the SSL server's keystore password.
- */
- protected String getKeystorePassword() {
- String keystorePass = endpoint.getKeystorePass();
- if (keystorePass == null) {
- keystorePass = endpoint.getKeyPass();
- }
- if (keystorePass == null) {
- keystorePass = DEFAULT_KEY_PASS;
- }
- return keystorePass;
- }
-
- /*
- * Gets the SSL server's keystore.
- */
- protected KeyStore getKeystore(String type, String provider, String pass)
- throws IOException {
-
- String keystoreFile = endpoint.getKeystoreFile();
- if (keystoreFile == null)
- keystoreFile = defaultKeystoreFile;
-
- return getStore(type, provider, keystoreFile, pass);
- }
-
- /*
- * Gets the SSL server's truststore.
- */
- protected KeyStore getTrustStore(String keystoreType,
- String keystoreProvider) throws IOException {
- KeyStore trustStore = null;
-
- String truststoreFile = endpoint.getTruststoreFile();
- if(truststoreFile == null) {
- truststoreFile = System.getProperty("javax.net.ssl.trustStore");
- }
- if(log.isDebugEnabled()) {
- log.debug("Truststore = " + truststoreFile);
- }
-
- String truststorePassword = endpoint.getTruststorePass();
- if( truststorePassword == null) {
- truststorePassword =
- System.getProperty("javax.net.ssl.trustStorePassword");
- }
- if(log.isDebugEnabled()) {
- log.debug("TrustPass = " + truststorePassword);
- }
-
- String truststoreType = endpoint.getTruststoreType();
- if( truststoreType == null) {
- truststoreType = System.getProperty("javax.net.ssl.trustStoreType");
- }
- if(truststoreType == null) {
- truststoreType = keystoreType;
- }
- if(log.isDebugEnabled()) {
- log.debug("trustType = " + truststoreType);
- }
-
- String truststoreProvider = endpoint.getTruststoreProvider();
- if( truststoreProvider == null) {
- truststoreProvider =
- System.getProperty("javax.net.ssl.trustStoreProvider");
- }
- if (truststoreProvider == null) {
- truststoreProvider = keystoreProvider;
- }
- if(log.isDebugEnabled()) {
- log.debug("trustProvider = " + truststoreProvider);
- }
-
- if (truststoreFile != null){
- try {
- trustStore = getStore(truststoreType, truststoreProvider,
- truststoreFile, truststorePassword);
- } catch (IOException ioe) {
- Throwable cause = ioe.getCause();
- if (cause instanceof UnrecoverableKeyException) {
- // Log a warning we had a password issue
- log.warn(sm.getString("jsse.invalid_truststore_password"),
- cause);
- // Re-try
- trustStore = getStore(truststoreType, truststoreProvider,
- truststoreFile, null);
- } else {
- // Something else went wrong - re-throw
- throw ioe;
- }
- }
- }
-
- return trustStore;
- }
-
- /*
- * Gets the key- or truststore with the specified type, path, and password.
- */
- private KeyStore getStore(String type, String provider, String path,
- String pass) throws IOException {
-
- KeyStore ks = null;
- InputStream istream = null;
- try {
- if (provider == null) {
- ks = KeyStore.getInstance(type);
- } else {
- ks = KeyStore.getInstance(type, provider);
- }
- if(!("PKCS11".equalsIgnoreCase(type) ||
- "".equalsIgnoreCase(path))) {
- istream = ConfigFileLoader.getInputStream(path);
- }
-
- char[] storePass = null;
- if (pass != null && !"".equals(pass)) {
- storePass = pass.toCharArray();
- }
- ks.load(istream, storePass);
- } catch (FileNotFoundException fnfe) {
- log.error(sm.getString("jsse.keystore_load_failed", type, path,
- fnfe.getMessage()), fnfe);
- throw fnfe;
- } catch (IOException ioe) {
- // 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("jsse.keystore_load_failed", type, path,
- ex.getMessage());
- log.error(msg, ex);
- throw new IOException(msg);
- } finally {
- if (istream != null) {
- try {
- istream.close();
- } catch (IOException ioe) {
- // Do nothing
- }
- }
- }
-
- return ks;
- }
-
- /**
- * Reads the keystore and initializes the SSL socket factory.
- */
- void init() throws IOException {
- try {
-
- String clientAuthStr = endpoint.getClientAuth();
- if("true".equalsIgnoreCase(clientAuthStr) ||
- "yes".equalsIgnoreCase(clientAuthStr)) {
- requireClientAuth = true;
- } else if("want".equalsIgnoreCase(clientAuthStr)) {
- wantClientAuth = true;
- }
-
- SSLContext context = createSSLContext();
- context.init(getKeyManagers(), getTrustManagers(), null);
-
- // Configure SSL session cache
- SSLSessionContext sessionContext =
- context.getServerSessionContext();
- if (sessionContext != null) {
- configureSessionContext(sessionContext);
- }
-
- // create proxy
- sslProxy = context.getServerSocketFactory();
-
- // Determine which cipher suites to enable
- enabledCiphers = getEnableableCiphers(context);
- enabledProtocols = getEnableableProtocols(context);
-
- allowUnsafeLegacyRenegotiation = "true".equals(
- endpoint.getAllowUnsafeLegacyRenegotiation());
-
- // Check the SSL config is OK
- checkConfig();
-
- } catch(Exception e) {
- if( e instanceof IOException )
- throw (IOException)e;
- throw new IOException(e.getMessage(), e);
- }
- }
-
- @Override
- public SSLContext createSSLContext() throws Exception {
-
- // SSL protocol variant (e.g., TLS, SSL v3, etc.)
- String protocol = endpoint.getSslProtocol();
- if (protocol == null) {
- protocol = defaultProtocol;
- }
-
- SSLContext context = SSLContext.getInstance(protocol);
-
- return context;
- }
-
- @Override
- public KeyManager[] getKeyManagers() throws Exception {
- String keystoreType = endpoint.getKeystoreType();
- if (keystoreType == null) {
- keystoreType = defaultKeystoreType;
- }
-
- String algorithm = endpoint.getAlgorithm();
- if (algorithm == null) {
- algorithm = KeyManagerFactory.getDefaultAlgorithm();
- }
-
- return getKeyManagers(keystoreType, endpoint.getKeystoreProvider(),
- algorithm, endpoint.getKeyAlias());
- }
-
- @Override
- public TrustManager[] getTrustManagers() throws Exception {
- String truststoreType = endpoint.getTruststoreType();
- if (truststoreType == null) {
- truststoreType = System.getProperty("javax.net.ssl.trustStoreType");
- }
- if (truststoreType == null) {
- truststoreType = endpoint.getKeystoreType();
- }
- if (truststoreType == null) {
- truststoreType = defaultKeystoreType;
- }
-
- String algorithm = endpoint.getTruststoreAlgorithm();
- if (algorithm == null) {
- algorithm = TrustManagerFactory.getDefaultAlgorithm();
- }
-
- return getTrustManagers(truststoreType, endpoint.getKeystoreProvider(),
- algorithm);
- }
-
- @Override
- public void configureSessionContext(SSLSessionContext sslSessionContext) {
- int sessionCacheSize;
- if (endpoint.getSessionCacheSize() != null) {
- sessionCacheSize = Integer.parseInt(
- endpoint.getSessionCacheSize());
- } else {
- sessionCacheSize = defaultSessionCacheSize;
- }
-
- int sessionTimeout;
- if (endpoint.getSessionTimeout() != null) {
- sessionTimeout = Integer.parseInt(endpoint.getSessionTimeout());
- } else {
- sessionTimeout = defaultSessionTimeout;
- }
-
- sslSessionContext.setSessionCacheSize(sessionCacheSize);
- sslSessionContext.setSessionTimeout(sessionTimeout);
- }
-
- /**
- * Gets the initialized key managers.
- */
- protected KeyManager[] getKeyManagers(String keystoreType,
- String keystoreProvider,
- String algorithm,
- String keyAlias)
- throws Exception {
-
- KeyManager[] kms = null;
-
- String keystorePass = getKeystorePassword();
-
- KeyStore ks = getKeystore(keystoreType, keystoreProvider, keystorePass);
- if (keyAlias != null && !ks.isKeyEntry(keyAlias)) {
- throw new IOException(
- sm.getString("jsse.alias_no_key_entry", keyAlias));
- }
-
- KeyManagerFactory kmf = KeyManagerFactory.getInstance(algorithm);
- String keyPass = endpoint.getKeyPass();
- if (keyPass == null) {
- keyPass = keystorePass;
- }
- kmf.init(ks, keyPass.toCharArray());
-
- kms = kmf.getKeyManagers();
- if (keyAlias != null) {
- String alias = keyAlias;
- if ("JKS".equals(keystoreType)) {
- alias = alias.toLowerCase(Locale.ENGLISH);
- }
- for(int i=0; i<kms.length; i++) {
- kms[i] = new JSSEKeyManager((X509KeyManager)kms[i], alias);
- }
- }
-
- return kms;
- }
-
- /**
- * Gets the initialized trust managers.
- */
- protected TrustManager[] getTrustManagers(String keystoreType,
- String keystoreProvider, String algorithm)
- throws Exception {
- String crlf = endpoint.getCrlFile();
-
- String className = endpoint.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(
- "jsse.invalidTrustManagerClassName", className));
- }
- Object trustManagerObject = clazz.newInstance();
- TrustManager trustManager = (TrustManager) trustManagerObject;
- return new TrustManager[]{ trustManager };
- }
-
- TrustManager[] tms = null;
-
- KeyStore trustStore = getTrustStore(keystoreType, keystoreProvider);
- if (trustStore != null || endpoint.getTrustManagerClassName() != null) {
- if (crlf == null) {
- TrustManagerFactory tmf =
- TrustManagerFactory.getInstance(algorithm);
- tmf.init(trustStore);
- tms = tmf.getTrustManagers();
- } else {
- TrustManagerFactory tmf =
- TrustManagerFactory.getInstance(algorithm);
- CertPathParameters params =
- getParameters(algorithm, crlf, trustStore);
- ManagerFactoryParameters mfp =
- new CertPathTrustManagerParameters(params);
- tmf.init(mfp);
- tms = tmf.getTrustManagers();
- }
- }
-
- return tms;
- }
-
- /**
- * Return the initialization parameters for the TrustManager.
- * Currently, only the default <code>PKIX</code> is supported.
- *
- * @param algorithm The algorithm to get parameters for.
- * @param crlf The path to the CRL file.
- * @param trustStore The configured TrustStore.
- * @return The parameters including the CRLs and TrustStore.
- */
- protected CertPathParameters getParameters(String algorithm,
- String crlf,
- KeyStore trustStore)
- throws Exception {
- CertPathParameters params = null;
- if("PKIX".equalsIgnoreCase(algorithm)) {
- PKIXBuilderParameters xparams =
- new PKIXBuilderParameters(trustStore, new X509CertSelector());
- Collection<? extends CRL> crls = getCRLs(crlf);
- CertStoreParameters csp = new CollectionCertStoreParameters(crls);
- CertStore store = CertStore.getInstance("Collection", csp);
- xparams.addCertStore(store);
- xparams.setRevocationEnabled(true);
- String trustLength = endpoint.getTrustMaxCertLength();
- if(trustLength != null) {
- try {
- xparams.setMaxPathLength(Integer.parseInt(trustLength));
- } catch(Exception ex) {
- log.warn("Bad maxCertLength: "+trustLength);
- }
- }
-
- params = xparams;
- } else {
- throw new CRLException("CRLs not supported for type: "+algorithm);
- }
- return params;
- }
-
-
- /**
- * Load the collection of CRLs.
- *
- */
- protected Collection<? extends CRL> getCRLs(String crlf)
- throws IOException, CRLException, CertificateException {
-
- Collection<? extends CRL> crls = null;
- try {
- CertificateFactory cf = CertificateFactory.getInstance("X.509");
- try (InputStream is = ConfigFileLoader.getInputStream(crlf)) {
- crls = cf.generateCRLs(is);
- }
- } catch(IOException iex) {
- throw iex;
- } catch(CRLException crle) {
- throw crle;
- } catch(CertificateException ce) {
- throw ce;
- }
- return crls;
- }
-
- @Override
- public String[] getEnableableProtocols(SSLContext context) {
- String[] requestedProtocols = endpoint.getSslEnabledProtocolsArray();
- if ((requestedProtocols == null) || (requestedProtocols.length == 0)) {
- return defaultServerProtocols;
- }
-
- List<String> protocols = new ArrayList<>(
- Arrays.asList(requestedProtocols));
- protocols.retainAll(Arrays.asList(context.getSupportedSSLParameters()
- .getProtocols()));
-
- if (protocols.isEmpty()) {
- log.warn(sm.getString("jsse.requested_protocols_not_supported",
- Arrays.asList(requestedProtocols)));
- }
- if (log.isDebugEnabled()) {
- log.debug(sm.getString("jsse.enableable_protocols", protocols));
- if (protocols.size() != requestedProtocols.length) {
- List<String> skipped = new ArrayList<>(
- Arrays.asList(requestedProtocols));
- skipped.removeAll(protocols);
- log.debug(sm.getString("jsse.unsupported_protocols", skipped));
- }
- }
- return protocols.toArray(new String[protocols.size()]);
- }
-
- /**
- * Configure Client authentication for this version of JSSE. The
- * JSSE included in Java 1.4 supports the 'want' value. Prior
- * versions of JSSE will treat 'want' as 'false'.
- * @param socket the SSLServerSocket
- */
- protected void configureClientAuth(SSLServerSocket socket){
- if (wantClientAuth){
- socket.setWantClientAuth(wantClientAuth);
- } else {
- socket.setNeedClientAuth(requireClientAuth);
- }
- }
-
- /**
- * Configures SSLEngine to honor cipher suites ordering based upon
- * endpoint configuration.
- */
- protected void configureUseServerCipherSuitesOrder(SSLServerSocket socket) {
- String useServerCipherSuitesOrderStr = endpoint
- .getUseServerCipherSuitesOrder().trim();
-
- // Only use this feature if the user explicitly requested its use.
- if(!"".equals(useServerCipherSuitesOrderStr)) {
- boolean useServerCipherSuitesOrder =
- ("true".equalsIgnoreCase(useServerCipherSuitesOrderStr)
- || "yes".equalsIgnoreCase(useServerCipherSuitesOrderStr));
- JreCompat.getInstance().setUseServerCipherSuitesOrder(socket, useServerCipherSuitesOrder);
- }
- }
-
- /**
- * Configures the given SSL server socket with the requested cipher suites,
- * protocol versions, and need for client authentication
- */
- private void initServerSocket(ServerSocket ssocket) {
-
- SSLServerSocket socket = (SSLServerSocket) ssocket;
-
- socket.setEnabledCipherSuites(enabledCiphers);
- socket.setEnabledProtocols(enabledProtocols);
-
- // we don't know if client auth is needed -
- // after parsing the request we may re-handshake
- configureClientAuth(socket);
- configureUseServerCipherSuitesOrder(socket);
- }
-
- /**
- * Checks that the certificate is compatible with the enabled cipher suites.
- * If we don't check now, the JIoEndpoint can enter a nasty logging loop.
- * See bug 45528.
- */
- private void checkConfig() throws IOException {
- // Create an unbound server socket
- ServerSocket socket = sslProxy.createServerSocket();
- initServerSocket(socket);
-
- try {
- // Set the timeout to 1ms as all we care about is if it throws an
- // SSLException on accept.
- socket.setSoTimeout(1);
-
- socket.accept();
- // Will never get here - no client can connect to an unbound port
- } catch (SSLException ssle) {
- // SSL configuration is invalid. Possibly cert doesn't match ciphers
- IOException ioe = new IOException(sm.getString(
- "jsse.invalid_ssl_conf", ssle.getMessage()));
- ioe.initCause(ssle);
- throw ioe;
- } catch (Exception e) {
- /*
- * Possible ways of getting here
- * socket.accept() throws a SecurityException
- * socket.setSoTimeout() throws a SocketException
- * socket.accept() throws some other exception (after a JDK change)
- * In these cases the test won't work so carry on - essentially
- * the behaviour before this patch
- * socket.accept() throws a SocketTimeoutException
- * In this case all is well so carry on
- */
- } finally {
- // Should be open here but just in case
- if (!socket.isClosed()) {
- socket.close();
- }
- }
-
- }
-}
diff --git a/java/org/apache/tomcat/util/net/jsse/JSSESupport.java b/java/org/apache/tomcat/util/net/jsse/JSSESupport.java
index 2190077..bc84721 100644
--- a/java/org/apache/tomcat/util/net/jsse/JSSESupport.java
+++ b/java/org/apache/tomcat/util/net/jsse/JSSESupport.java
@@ -19,23 +19,18 @@ package org.apache.tomcat.util.net.jsse;
import java.io.ByteArrayInputStream;
import java.io.IOException;
-import java.io.InputStream;
-import java.net.SocketException;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.util.HashMap;
import java.util.Map;
-import javax.net.ssl.HandshakeCompletedEvent;
-import javax.net.ssl.HandshakeCompletedListener;
-import javax.net.ssl.SSLException;
import javax.net.ssl.SSLSession;
-import javax.net.ssl.SSLSocket;
-import javax.security.cert.X509Certificate;
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.net.SSLSessionManager;
import org.apache.tomcat.util.net.SSLSupport;
-import org.apache.tomcat.util.net.jsse.openssl.Cipher;
+import org.apache.tomcat.util.net.openssl.ciphers.Cipher;
import org.apache.tomcat.util.res.StringManager;
/** JSSESupport
@@ -51,14 +46,11 @@ import org.apache.tomcat.util.res.StringManager;
Parts cribbed from JSSECertCompat
Parts cribbed from CertificatesValve
*/
+public class JSSESupport implements SSLSupport, SSLSessionManager {
-class JSSESupport implements SSLSupport, SSLSessionManager {
+ private static final Log log = LogFactory.getLog(JSSESupport.class);
- private static final org.apache.juli.logging.Log log =
- org.apache.juli.logging.LogFactory.getLog(JSSESupport.class);
-
- private static final StringManager sm =
- StringManager.getManager("org.apache.tomcat.util.net.jsse.res");
+ private static final StringManager sm = StringManager.getManager(JSSESupport.class);
private static final Map<String,Integer> keySizeCache = new HashMap<>();
@@ -79,18 +71,10 @@ class JSSESupport implements SSLSupport, SSLSessionManager {
// NO-OP
}
- protected SSLSocket ssl;
- protected SSLSession session;
-
- Listener listener = new Listener();
+ private SSLSession session;
- JSSESupport(SSLSocket sock){
- ssl=sock;
- session = sock.getSession();
- sock.addHandshakeCompletedListener(listener);
- }
- JSSESupport(SSLSession session) {
+ public JSSESupport(SSLSession session) {
this.session = session;
}
@@ -102,8 +86,12 @@ class JSSESupport implements SSLSupport, SSLSessionManager {
return session.getCipherSuite();
}
- protected java.security.cert.X509Certificate [] getX509Certificates(
- SSLSession session) {
+ @Override
+ public java.security.cert.X509Certificate[] getPeerCertificateChain() throws IOException {
+ // Look up the current SSLSession
+ if (session == null)
+ return null;
+
Certificate [] certs=null;
try {
certs = session.getPeerCertificates();
@@ -142,84 +130,11 @@ class JSSESupport implements SSLSupport, SSLSessionManager {
return x509Certs;
}
- @Override
- public java.security.cert.X509Certificate[] getPeerCertificateChain(boolean force)
- throws IOException {
- // Look up the current SSLSession
- if (session == null)
- return null;
-
- // Convert JSSE's certificate format to the ones we need
- X509Certificate [] jsseCerts = null;
- try {
- jsseCerts = session.getPeerCertificateChain();
- } catch(Exception bex) {
- // ignore.
- }
- if (jsseCerts == null)
- jsseCerts = new X509Certificate[0];
- if(jsseCerts.length <= 0 && force && ssl != null) {
- session.invalidate();
- handShake();
- session = ssl.getSession();
- }
- return getX509Certificates(session);
- }
-
- protected void handShake() throws IOException {
- if( ssl.getWantClientAuth() ) {
- log.debug(sm.getString("jsseSupport.noCertWant"));
- } else {
- ssl.setNeedClientAuth(true);
- }
-
- if (ssl.getEnabledCipherSuites().length == 0) {
- // Handshake is never going to be successful.
- // Assume this is because handshakes are disabled
- log.warn(sm.getString("jsseSupport.serverRenegDisabled"));
- session.invalidate();
- ssl.close();
- return;
- }
-
- InputStream in = ssl.getInputStream();
- int oldTimeout = ssl.getSoTimeout();
- ssl.setSoTimeout(1000);
- byte[] b = new byte[1];
- listener.reset();
- ssl.startHandshake();
- int maxTries = 60; // 60 * 1000 = example 1 minute time out
- for (int i = 0; i < maxTries; i++) {
- if (log.isTraceEnabled())
- log.trace("Reading for try #" + i);
- try {
- int read = in.read(b);
- if (read > 0) {
- // Shouldn't happen as all input should have been swallowed
- // before trying to do the handshake. If it does, something
- // went wrong so lets bomb out now.
- throw new SSLException(
- sm.getString("jsseSupport.unexpectedData"));
- }
- } catch(SSLException sslex) {
- log.info(sm.getString("jsseSupport.clientCertError"), sslex);
- throw sslex;
- } catch (IOException e) {
- // ignore - presumably the timeout
- }
- if (listener.completed) {
- break;
- }
- }
- ssl.setSoTimeout(oldTimeout);
- if (listener.completed == false) {
- throw new SocketException("SSL Cert handshake timeout");
- }
-
- }
/**
- * Copied from <code>org.apache.catalina.valves.CertificateValve</code>
+ * {@inheritDoc}
+ * <p>
+ * This returns the effective bits for the current cipher suite.
*/
@Override
public Integer getKeySize() throws IOException {
@@ -252,17 +167,11 @@ class JSSESupport implements SSLSupport, SSLSessionManager {
}
- private static class Listener implements HandshakeCompletedListener {
- volatile boolean completed = false;
- @Override
- public void handshakeCompleted(HandshakeCompletedEvent event) {
- completed = true;
- }
- void reset() {
- completed = false;
- }
+ public void setSession(SSLSession session) {
+ this.session = session;
}
+
/**
* Invalidate the session this support object is associated with.
*/
diff --git a/java/org/apache/tomcat/util/net/jsse/JSSEUtil.java b/java/org/apache/tomcat/util/net/jsse/JSSEUtil.java
new file mode 100644
index 0000000..5965920
--- /dev/null
+++ b/java/org/apache/tomcat/util/net/jsse/JSSEUtil.java
@@ -0,0 +1,374 @@
+/*
+ * 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.jsse;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.KeyManagementException;
+import java.security.KeyStore;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.CRL;
+import java.security.cert.CRLException;
+import java.security.cert.CertPathParameters;
+import java.security.cert.CertStore;
+import java.security.cert.CertStoreParameters;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateExpiredException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.CertificateNotYetValidException;
+import java.security.cert.CollectionCertStoreParameters;
+import java.security.cert.PKIXBuilderParameters;
+import java.security.cert.X509CertSelector;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Locale;
+import java.util.Set;
+
+import javax.net.ssl.CertPathTrustManagerParameters;
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.ManagerFactoryParameters;
+import javax.net.ssl.SSLSessionContext;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
+import javax.net.ssl.X509KeyManager;
+
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
+import org.apache.tomcat.util.compat.JreVendor;
+import org.apache.tomcat.util.file.ConfigFileLoader;
+import org.apache.tomcat.util.net.Constants;
+import org.apache.tomcat.util.net.SSLContext;
+import org.apache.tomcat.util.net.SSLHostConfig;
+import org.apache.tomcat.util.net.SSLHostConfigCertificate;
+import org.apache.tomcat.util.net.SSLUtilBase;
+import org.apache.tomcat.util.res.StringManager;
+
+/**
+ * SSLUtil implementation for JSSE.
+ *
+ * @author Harish Prabandham
+ * @author Costin Manolache
+ * @author Stefan Freyr Stefansson
+ * @author EKR
+ * @author Jan Luehe
+ */
+public class JSSEUtil extends SSLUtilBase {
+
+ private static final Log log = LogFactory.getLog(JSSEUtil.class);
+ private static final StringManager sm = StringManager.getManager(JSSEUtil.class);
+
+ private static final Set<String> implementedProtocols;
+ private static final Set<String> implementedCiphers;
+
+ static {
+ SSLContext context;
+ try {
+ context = new JSSESSLContext(Constants.SSL_PROTO_TLS);
+ context.init(null, null, null);
+ } catch (NoSuchAlgorithmException | KeyManagementException e) {
+ // This is fatal for the connector so throw an exception to prevent
+ // it from starting
+ throw new IllegalArgumentException(e);
+ }
+
+ String[] implementedProtocolsArray = context.getSupportedSSLParameters().getProtocols();
+ implementedProtocols = new HashSet<>(implementedProtocolsArray.length);
+
+ // Filter out all the SSL protocols (SSLv2 and SSLv3) from the list of
+ // implemented protocols since they are no longer considered secure but
+ // allow SSLv2Hello. This has the effect of making it impossible to use
+ // SSLv2 or SSLv3 without source code changes.
+ for (String protocol : implementedProtocolsArray) {
+ String protocolUpper = protocol.toUpperCase(Locale.ENGLISH);
+ if (!"SSLV2HELLO".equals(protocolUpper)) {
+ if (protocolUpper.contains("SSL")) {
+ log.debug(sm.getString("jsse.excludeProtocol", protocol));
+ continue;
+ }
+ }
+ implementedProtocols.add(protocol);
+ }
+
+ if (implementedProtocols.size() == 0) {
+ log.warn(sm.getString("jsse.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
+ // need to filter the requested cipher suites using both forms with an
+ // IBM JRE.
+ if (JreVendor.IS_IBM_JVM) {
+ implementedCiphers = new HashSet<>(implementedCipherSuiteArray.length * 2);
+ for (String name : implementedCipherSuiteArray) {
+ implementedCiphers.add(name);
+ if (name.startsWith("SSL")) {
+ implementedCiphers.add("TLS" + name.substring(3));
+ }
+ }
+ } else {
+ implementedCiphers = new HashSet<>(implementedCipherSuiteArray.length);
+ implementedCiphers.addAll(Arrays.asList(implementedCipherSuiteArray));
+ }
+ }
+
+
+ private final SSLHostConfig sslHostConfig;
+
+
+ public JSSEUtil (SSLHostConfigCertificate certificate) {
+ super(certificate);
+ this.sslHostConfig = certificate.getSSLHostConfig();
+ }
+
+
+ @Override
+ protected Log getLog() {
+ return log;
+ }
+
+
+ @Override
+ protected Set<String> getImplementedProtocols() {
+ return implementedProtocols;
+ }
+
+
+ @Override
+ protected Set<String> getImplementedCiphers() {
+ return implementedCiphers;
+ }
+
+
+ @Override
+ public SSLContext createSSLContext(List<String> negotiableProtocols) throws NoSuchAlgorithmException {
+ return new JSSESSLContext(sslHostConfig.getSslProtocol());
+ }
+
+
+ @Override
+ public KeyManager[] getKeyManagers() throws Exception {
+ String keystoreType = certificate.getCertificateKeystoreType();
+ String keyAlias = certificate.getCertificateKeyAlias();
+ String algorithm = sslHostConfig.getKeyManagerAlgorithm();
+ String keyPass = certificate.getCertificateKeyPassword();
+ // This has to be here as it can't be moved to SSLHostConfig since the
+ // defaults vary between JSSE and OpenSSL.
+ if (keyPass == null) {
+ keyPass = certificate.getCertificateKeystorePassword();
+ }
+
+ KeyManager[] kms = null;
+
+ KeyStore ks = certificate.getCertificateKeystore();
+
+ if (ks == null) {
+ // create an in-memory keystore and import the private key
+ // and the certificate chain from the PEM files
+ ks = KeyStore.getInstance("JKS");
+ ks.load(null, null);
+
+ PEMFile privateKeyFile = new PEMFile(SSLHostConfig.adjustRelativePath
+ (certificate.getCertificateKeyFile() != null ? certificate.getCertificateKeyFile() : certificate.getCertificateFile()),
+ keyPass);
+ PEMFile certificateFile = new PEMFile(SSLHostConfig.adjustRelativePath(certificate.getCertificateFile()));
+
+ Collection<Certificate> chain = new ArrayList<>();
+ chain.addAll(certificateFile.getCertificates());
+ if (certificate.getCertificateChainFile() != null) {
+ PEMFile certificateChainFile = new PEMFile(SSLHostConfig.adjustRelativePath(certificate.getCertificateChainFile()));
+ chain.addAll(certificateChainFile.getCertificates());
+ }
+
+ if (keyAlias == null) {
+ keyAlias = "tomcat";
+ }
+ ks.setKeyEntry(keyAlias, privateKeyFile.getPrivateKey(), keyPass.toCharArray(), chain.toArray(new Certificate[chain.size()]));
+ }
+
+ if (keyAlias != null && !ks.isKeyEntry(keyAlias)) {
+ throw new IOException(sm.getString("jsse.alias_no_key_entry", keyAlias));
+ }
+
+ KeyManagerFactory kmf = KeyManagerFactory.getInstance(algorithm);
+ kmf.init(ks, keyPass.toCharArray());
+
+ kms = kmf.getKeyManagers();
+ if (kms == null) {
+ return kms;
+ }
+
+ if (keyAlias != null) {
+ String alias = keyAlias;
+ // JKS keystores always convert the alias name to lower case
+ if ("JKS".equals(keystoreType)) {
+ alias = alias.toLowerCase(Locale.ENGLISH);
+ }
+ for(int i = 0; i < kms.length; i++) {
+ kms[i] = new JSSEKeyManager((X509KeyManager)kms[i], alias);
+ }
+ }
+
+ return kms;
+ }
+
+
+ @Override
+ 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(
+ "jsse.invalidTrustManagerClassName", className));
+ }
+ Object trustManagerObject = clazz.newInstance();
+ TrustManager trustManager = (TrustManager) trustManagerObject;
+ return new TrustManager[]{ trustManager };
+ }
+
+ TrustManager[] tms = null;
+
+ KeyStore trustStore = sslHostConfig.getTruststore();
+ if (trustStore != null) {
+ checkTrustStoreEntries(trustStore);
+ String algorithm = sslHostConfig.getTruststoreAlgorithm();
+ String crlf = sslHostConfig.getCertificateRevocationListFile();
+
+ if ("PKIX".equalsIgnoreCase(algorithm)) {
+ TrustManagerFactory tmf = TrustManagerFactory.getInstance(algorithm);
+ CertPathParameters params = getParameters(crlf, trustStore);
+ 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) {
+ throw new CRLException(sm.getString("jsseUtil.noCrlSupport", algorithm));
+ }
+ log.warn(sm.getString("jsseUtil.noVerificationDepth"));
+ }
+ }
+
+ return tms;
+ }
+
+
+ private void checkTrustStoreEntries(KeyStore trustStore) throws Exception {
+ Enumeration<String> aliases = trustStore.aliases();
+ if (aliases != null) {
+ Date now = new Date();
+ while (aliases.hasMoreElements()) {
+ String alias = aliases.nextElement();
+ if (trustStore.isCertificateEntry(alias)) {
+ Certificate cert = trustStore.getCertificate(alias);
+ if (cert instanceof X509Certificate) {
+ try {
+ ((X509Certificate) cert).checkValidity(now);
+ } catch (CertificateExpiredException | CertificateNotYetValidException e) {
+ String msg = sm.getString("jsseUtil.trustedCertNotValid", alias,
+ ((X509Certificate) cert).getSubjectDN(), e.getMessage());
+ if (log.isDebugEnabled()) {
+ log.debug(msg, e);
+ } else {
+ log.warn(msg);
+ }
+ }
+ } else {
+ if (log.isDebugEnabled()) {
+ log.debug(sm.getString("jsseUtil.trustedCertNotChecked", alias));
+ }
+ }
+ }
+ }
+ }
+ }
+
+
+ @Override
+ public void configureSessionContext(SSLSessionContext sslSessionContext) {
+ sslSessionContext.setSessionCacheSize(sslHostConfig.getSessionCacheSize());
+ sslSessionContext.setSessionTimeout(sslHostConfig.getSessionTimeout());
+ }
+
+
+ /**
+ * Return the initialization parameters for the TrustManager.
+ * Currently, only the default <code>PKIX</code> is supported.
+ *
+ * @param crlf The path to the CRL file.
+ * @param trustStore The configured TrustStore.
+ * @return The parameters including the CRLs and TrustStore.
+ * @throws Exception An error occurred
+ */
+ protected CertPathParameters getParameters(String crlf, KeyStore trustStore) throws Exception {
+
+ PKIXBuilderParameters xparams =
+ new PKIXBuilderParameters(trustStore, new X509CertSelector());
+ if (crlf != null && crlf.length() > 0) {
+ Collection<? extends CRL> crls = getCRLs(crlf);
+ CertStoreParameters csp = new CollectionCertStoreParameters(crls);
+ CertStore store = CertStore.getInstance("Collection", csp);
+ xparams.addCertStore(store);
+ xparams.setRevocationEnabled(true);
+ } else {
+ xparams.setRevocationEnabled(false);
+ }
+ 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 CertificateException Error processing certificate
+ */
+ protected Collection<? extends CRL> getCRLs(String crlf)
+ throws IOException, CRLException, CertificateException {
+
+ Collection<? extends CRL> crls = null;
+ try {
+ CertificateFactory cf = CertificateFactory.getInstance("X.509");
+ try (InputStream is = ConfigFileLoader.getInputStream(crlf)) {
+ crls = cf.generateCRLs(is);
+ }
+ } catch(IOException iex) {
+ throw iex;
+ } catch(CRLException crle) {
+ throw crle;
+ } catch(CertificateException ce) {
+ throw ce;
+ }
+ return crls;
+ }
+}
diff --git a/java/org/apache/tomcat/util/net/jsse/LocalStrings.properties b/java/org/apache/tomcat/util/net/jsse/LocalStrings.properties
new file mode 100644
index 0000000..34733e9
--- /dev/null
+++ b/java/org/apache/tomcat/util/net/jsse/LocalStrings.properties
@@ -0,0 +1,40 @@
+# 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.
+
+jsse.alias_no_key_entry=Alias name {0} does not identify a key entry
+jsse.invalid_ssl_conf=SSL configuration is invalid due to {0}
+jsse.invalidTrustManagerClassName=The trustManagerClassName provided [{0}] does not implement javax.net.ssl.TrustManager
+jsse.requested_ciphers_not_supported=None of the ciphers specified are supported by the SSL engine : {0}
+jsse.enableable_ciphers=Specified SSL ciphers that are supported and enableable are : {0}
+jsse.unsupported_ciphers=Some specified SSL ciphers are not supported by the SSL engine : {0}
+jsse.excludeProtocol=The SSL protocol [{0}] which is supported in this JRE was excluded from the protocols available to Tomcat
+jsse.noDefaultCiphers=Unable to determine a default for ciphers for [{0}]. Set an explicit value to ensure the connector can start.
+jsse.noDefaultProtocols=Unable to determine a default for sslEnabledProtocols. Set an explicit value to ensure the connector can start.
+jsse.exceptionOnClose=Failure to close socket.
+jsse.pemParseError=Unable to parse the key from [{0}]
+
+jsseSupport.clientCertError=Error trying to obtain a certificate from the client
+jseeSupport.certTranslationError=Error translating certificate [{0}]
+jsseSupport.noCertWant=No client certificate sent for want
+jsseSupport.serverRenegDisabled=SSL server initiated renegotiation is disabled, closing connection
+jsseSupport.unexpectedData=Unexpected data read from input stream
+jsse.openssl.unknownElement=Unknown element in cipher string: {0}
+jsse.openssl.effectiveCiphers=Ciphers used: {0}
+
+jsseUtil.invalidTrustCert=The certificate for [{0}] in the trust store is not valid and has, therefore, been excluded in the list of certificates sent to the client
+jsseUtil.noCrlSupport=The truststoreProvider [{0}] does not support the certificateRevocationFile configuration option
+jsseUtil.noVerificationDepth=The truststoreProvider [{0}] does not support the certificateVerificationDepth configuration option
+jsseUtil.trustedCertNotChecked=The validity dates of the trusted certificate with alias [{0}] were not checked as the certificate was of an unknown type
+jsseUtil.trustedCertNotValid=The trusted certificate with alias [{0}] and DN [{1}] is not valid due to [{2}]. Certificates signed by this trusted certificate WILL be accepted
\ No newline at end of file
diff --git a/java/org/apache/tomcat/util/net/jsse/LocalStrings_es.properties b/java/org/apache/tomcat/util/net/jsse/LocalStrings_es.properties
new file mode 100644
index 0000000..28337f4
--- /dev/null
+++ b/java/org/apache/tomcat/util/net/jsse/LocalStrings_es.properties
@@ -0,0 +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.
+jsse.alias_no_key_entry = El nombre de Alias {0} no identifica una entrada de clave
+jsse.invalid_ssl_conf = La configuraci\u00F3n SSL no es v\u00E1lida debido a {0}
+jsse.invalidTrustManagerClassName = El trustManagerClassName suministrado [{0}] no implementa javax.net.ssl.TrustManager
diff --git a/java/org/apache/tomcat/util/net/jsse/res/LocalStrings_fr.properties b/java/org/apache/tomcat/util/net/jsse/LocalStrings_fr.properties
similarity index 100%
rename from java/org/apache/tomcat/util/net/jsse/res/LocalStrings_fr.properties
rename to java/org/apache/tomcat/util/net/jsse/LocalStrings_fr.properties
diff --git a/java/org/apache/tomcat/util/net/jsse/res/LocalStrings_ja.properties b/java/org/apache/tomcat/util/net/jsse/LocalStrings_ja.properties
similarity index 100%
rename from java/org/apache/tomcat/util/net/jsse/res/LocalStrings_ja.properties
rename to java/org/apache/tomcat/util/net/jsse/LocalStrings_ja.properties
diff --git a/java/org/apache/tomcat/util/net/jsse/NioX509KeyManager.java b/java/org/apache/tomcat/util/net/jsse/NioX509KeyManager.java
deleted file mode 100644
index 6acbad3..0000000
--- a/java/org/apache/tomcat/util/net/jsse/NioX509KeyManager.java
+++ /dev/null
@@ -1,92 +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.net.jsse;
-
-import java.net.Socket;
-import java.security.Principal;
-import java.security.PrivateKey;
-import java.security.cert.X509Certificate;
-
-import javax.net.ssl.SSLEngine;
-import javax.net.ssl.X509ExtendedKeyManager;
-import javax.net.ssl.X509KeyManager;
-
-public class NioX509KeyManager extends X509ExtendedKeyManager {
-
- private X509KeyManager delegate;
- private String serverKeyAlias;
-
- /**
- * Constructor.
- *
- * @param mgr The X509KeyManager used as a delegate
- * @param serverKeyAlias The alias name of the server's keypair and
- * supporting certificate chain
- */
- public NioX509KeyManager(X509KeyManager mgr, String serverKeyAlias) {
- this.delegate = mgr;
- this.serverKeyAlias = serverKeyAlias;
- }
-
- @Override
- public String chooseClientAlias(String[] keyType, Principal[] issuers,
- Socket socket) {
- return delegate.chooseClientAlias(keyType, issuers, socket);
- }
-
- @Override
- public String chooseServerAlias(String keyType, Principal[] issuers,
- Socket socket) {
- if (serverKeyAlias != null) {
- return serverKeyAlias;
- }
-
- return delegate.chooseServerAlias(keyType, issuers, socket);
- }
-
- @Override
- public X509Certificate[] getCertificateChain(String alias) {
- return delegate.getCertificateChain(alias);
- }
-
- @Override
- public String[] getClientAliases(String keyType, Principal[] issuers) {
- return delegate.getClientAliases(keyType, issuers);
- }
-
- @Override
- public PrivateKey getPrivateKey(String alias) {
- return delegate.getPrivateKey(alias);
- }
-
- @Override
- public String[] getServerAliases(String keyType, Principal[] issuers) {
- return delegate.getServerAliases(keyType, issuers);
- }
-
- @Override
- public String chooseEngineServerAlias(String keyType, Principal[] issuers,
- SSLEngine engine) {
- if (serverKeyAlias!=null) {
- return serverKeyAlias;
- }
-
- return super.chooseEngineServerAlias(keyType, issuers, engine);
- }
-
-}
diff --git a/java/org/apache/tomcat/util/net/jsse/PEMFile.java b/java/org/apache/tomcat/util/net/jsse/PEMFile.java
new file mode 100644
index 0000000..0087d9c
--- /dev/null
+++ b/java/org/apache/tomcat/util/net/jsse/PEMFile.java
@@ -0,0 +1,150 @@
+/*
+ * 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.jsse;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.FileReader;
+import java.io.IOException;
+import java.security.GeneralSecurityException;
+import java.security.InvalidKeyException;
+import java.security.KeyFactory;
+import java.security.PrivateKey;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.KeySpec;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.crypto.Cipher;
+import javax.crypto.EncryptedPrivateKeyInfo;
+import javax.crypto.SecretKey;
+import javax.crypto.SecretKeyFactory;
+import javax.crypto.spec.PBEKeySpec;
+
+import org.apache.tomcat.util.codec.binary.Base64;
+import org.apache.tomcat.util.res.StringManager;
+
+/**
+ * RFC 1421 PEM file containing X509 certificates or private keys (PKCS#8 only,
+ * i.e. with boundaries containing "BEGIN PRIVATE KEY" or "BEGIN ENCRYPTED PRIVATE KEY",
+ * not "BEGIN RSA PRIVATE KEY" or other variations).
+ */
+class PEMFile {
+
+ private static final StringManager sm = StringManager.getManager(PEMFile.class);
+
+ private String filename;
+ private List<X509Certificate> certificates = new ArrayList<>();
+ private PrivateKey privateKey;
+
+ public List<X509Certificate> getCertificates() {
+ return certificates;
+ }
+
+ public PrivateKey getPrivateKey() {
+ return privateKey;
+ }
+
+ public PEMFile(String filename) throws IOException, GeneralSecurityException {
+ this(filename, null);
+ }
+
+ public PEMFile(String filename, String password) throws IOException, GeneralSecurityException {
+ this.filename = filename;
+
+ List<Part> parts = new ArrayList<>();
+ try (BufferedReader in = new BufferedReader(new FileReader(filename))) {
+ Part part = null;
+ String line;
+ while ((line = in.readLine()) != null) {
+ if (line.startsWith(Part.BEGIN_BOUNDARY)) {
+ part = new Part();
+ part.type = line.substring(Part.BEGIN_BOUNDARY.length(), line.length() - 5).trim();
+ } else if (line.startsWith(Part.END_BOUNDARY)) {
+ parts.add(part);
+ part = null;
+ } else if (part != null && !line.contains(":") && !line.startsWith(" ")) {
+ part.content += line;
+ }
+ }
+ }
+
+ for (Part part : parts) {
+ switch (part.type) {
+ case "PRIVATE KEY":
+ privateKey = part.toPrivateKey(null);
+ break;
+ case "ENCRYPTED PRIVATE KEY":
+ privateKey = part.toPrivateKey(password);
+ break;
+ case "CERTIFICATE":
+ case "X509 CERTIFICATE":
+ certificates.add(part.toCertificate());
+ break;
+ }
+ }
+ }
+
+ private class Part {
+ public static final String BEGIN_BOUNDARY = "-----BEGIN ";
+ public static final String END_BOUNDARY = "-----END ";
+
+ public String type;
+ public String content = "";
+
+ private byte[] decode() {
+ return Base64.decodeBase64(content);
+ }
+
+ public X509Certificate toCertificate() throws CertificateException {
+ CertificateFactory factory = CertificateFactory.getInstance("X.509");
+ return (X509Certificate) factory.generateCertificate(new ByteArrayInputStream(decode()));
+ }
+
+ public PrivateKey toPrivateKey(String password) throws GeneralSecurityException, IOException {
+ KeySpec keySpec;
+
+ if (password == null) {
+ keySpec = new PKCS8EncodedKeySpec(decode());
+ } else {
+ EncryptedPrivateKeyInfo privateKeyInfo = new EncryptedPrivateKeyInfo(decode());
+ SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance(privateKeyInfo.getAlgName());
+ SecretKey secretKey = secretKeyFactory.generateSecret(new PBEKeySpec(password.toCharArray()));
+
+ Cipher cipher = Cipher.getInstance(privateKeyInfo.getAlgName());
+ cipher.init(Cipher.DECRYPT_MODE, secretKey, privateKeyInfo.getAlgParameters());
+
+ keySpec = privateKeyInfo.getKeySpec(cipher);
+ }
+
+ InvalidKeyException exception = new InvalidKeyException(sm.getString("jsse.pemParseError", filename));
+ for (String algorithm : new String[] {"RSA", "DSA", "EC"}) {
+ try {
+ return KeyFactory.getInstance(algorithm).generatePrivate(keySpec);
+ } catch (InvalidKeySpecException e) {
+ exception.addSuppressed(e);
+ }
+ }
+
+ throw exception;
+ }
+ }
+}
diff --git a/java/org/apache/tomcat/util/net/jsse/openssl/Authentication.java b/java/org/apache/tomcat/util/net/jsse/openssl/Authentication.java
deleted file mode 100644
index c1859d8..0000000
--- a/java/org/apache/tomcat/util/net/jsse/openssl/Authentication.java
+++ /dev/null
@@ -1,33 +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.net.jsse.openssl;
-
-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 */,
- GOST94 /* GOST R 34.10-94 signature auth */,
- GOST01 /* GOST R 34.10-2001 */,
- FZA /* Fortezza */,
- SRP
-}
diff --git a/java/org/apache/tomcat/util/net/jsse/openssl/Cipher.java b/java/org/apache/tomcat/util/net/jsse/openssl/Cipher.java
deleted file mode 100644
index c14d406..0000000
--- a/java/org/apache/tomcat/util/net/jsse/openssl/Cipher.java
+++ /dev/null
@@ -1,4746 +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.net.jsse.openssl;
-
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.LinkedHashSet;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * All the standard cipher suites for SSL/TSL.
- *
- * @see <a href="https://github.com/openssl/openssl/blob/master/ssl/s3_lib.c"
- * >OpenSSL cipher definitions</a>
- * @see <a href="http://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-4"
- * >The cipher suite registry</a>
- * @see <a href="https://www.thesprawl.org/research/tls-and-ssl-cipher-suites/"
- * >Another list of cipher suites with some non-standard IDs</a>
- * @see <a href="http://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#ciphersuites"
- * >Oracle standard names for cipher suites</a>
- * @see <a href="https://www.openssl.org/docs/apps/ciphers.html"
- * >Mapping of OpenSSL cipher suites names to registry names</a>
- * @see <a href="https://github.com/ssllabs/sslhaf/blob/0.1.x/suites.csv"
- * >SSL Labs tool - list of ciphers</a>
- * @see <a href="http://hg.openjdk.java.net/jdk9/jdk9/jdk/file/e30cd0d37abf/src/java.base/share/classes/sun/security/ssl/CipherSuite.java"
- * >OpenJDK source code</a>
- */
-public enum Cipher {
-
- /* Cipher 0
- * TLS_NULL_WITH_NULL_NULL
- * Must never be negotiated. Used internally to represent the initial
- * unprotected state of a connection.
- */
-
- /* The RSA ciphers */
- // Cipher 01
- TLS_RSA_WITH_NULL_MD5(
- 0x0001,
- "NULL-MD5",
- KeyExchange.RSA,
- Authentication.RSA,
- Encryption.eNULL,
- MessageDigest.MD5,
- Protocol.SSLv3,
- false,
- EncryptionLevel.STRONG_NONE,
- false,
- 0,
- 0,
- new String[] {"SSL_RSA_WITH_NULL_MD5"},
- null
- ),
- // Cipher 02
- TLS_RSA_WITH_NULL_SHA(
- 0x0002,
- "NULL-SHA",
- KeyExchange.RSA,
- Authentication.RSA,
- Encryption.eNULL,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.STRONG_NONE,
- true,
- 0,
- 0,
- new String[] {"SSL_RSA_WITH_NULL_SHA"},
- null
- ),
- // Cipher 03
- TLS_RSA_EXPORT_WITH_RC4_40_MD5(
- 0x0003,
- "EXP-RC4-MD5",
- KeyExchange.RSA,
- Authentication.RSA,
- Encryption.RC4,
- MessageDigest.MD5,
- Protocol.SSLv3,
- true,
- EncryptionLevel.EXP40,
- false,
- 40,
- 128,
- new String[] {"SSL_RSA_EXPORT_WITH_RC4_40_MD5"},
- null
- ),
- // Cipher 04
- TLS_RSA_WITH_RC4_128_MD5(
- 0x0004,
- "RC4-MD5",
- KeyExchange.RSA,
- Authentication.RSA,
- Encryption.RC4,
- MessageDigest.MD5,
- Protocol.SSLv3,
- false,
- EncryptionLevel.MEDIUM,
- false,
- 128,
- 128,
- new String[] {"SSL_RSA_WITH_RC4_128_MD5"},
- null
- ),
- // Cipher 05
- TLS_RSA_WITH_RC4_128_SHA(
- 0x0005,
- "RC4-SHA",
- KeyExchange.RSA,
- Authentication.RSA,
- Encryption.RC4,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.MEDIUM,
- false,
- 128,
- 128,
- new String[] {"SSL_RSA_WITH_RC4_128_SHA"},
- null
- ),
- // Cipher 06
- TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5(
- 0x0006,
- "EXP-RC2-CBC-MD5",
- KeyExchange.RSA,
- Authentication.RSA,
- Encryption.RC2,
- MessageDigest.MD5,
- Protocol.SSLv3,
- true,
- EncryptionLevel.EXP40,
- false,
- 40,
- 128,
- new String[] {"SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5"},
- null
- ),
- // Cipher 07
- TLS_RSA_WITH_IDEA_CBC_SHA(
- 0x0007,
- "IDEA-CBC-SHA",
- KeyExchange.RSA,
- Authentication.RSA,
- Encryption.IDEA,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.MEDIUM,
- false,
- 128,
- 128,
- new String[] {"SSL_RSA_WITH_IDEA_CBC_SHA"},
- null
- ),
- // Cipher 08
- TLS_RSA_EXPORT_WITH_DES40_CBC_SHA(
- 0x0008,
- "EXP-DES-CBC-SHA",
- KeyExchange.RSA,
- Authentication.RSA,
- Encryption.DES,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- true,
- EncryptionLevel.EXP40,
- false,
- 40,
- 56,
- new String[] {"SSL_RSA_EXPORT_WITH_DES40_CBC_SHA"},
- null
- ),
- // Cipher 09
- TLS_RSA_WITH_DES_CBC_SHA(
- 0x0009,
- "DES-CBC-SHA",
- KeyExchange.RSA,
- Authentication.RSA,
- Encryption.DES,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.LOW,
- false,
- 56,
- 56,
- new String[] {"SSL_RSA_WITH_DES_CBC_SHA"},
- null
- ),
- // Cipher 0A
- TLS_RSA_WITH_3DES_EDE_CBC_SHA(
- 0x000A,
- "DES-CBC3-SHA",
- KeyExchange.RSA,
- Authentication.RSA,
- Encryption.TRIPLE_DES,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.MEDIUM,
- true,
- 112,
- 168,
- new String[] {"SSL_RSA_WITH_3DES_EDE_CBC_SHA"},
- null
- ),
- /* The DH ciphers */
- // Cipher 0B
- TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA(
- 0x000B,
- "EXP-DH-DSS-DES-CBC-SHA",
- KeyExchange.DHd,
- Authentication.DH,
- Encryption.DES,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- true,
- EncryptionLevel.EXP40,
- false,
- 40,
- 56,
- new String[] {"SSL_DH_DSS_EXPORT_WITH_DES40_CBC_SHA"},
- null
- ),
- // Cipher 0C
- TLS_DH_DSS_WITH_DES_CBC_SHA(
- 0x000C,
- "DH-DSS-DES-CBC-SHA",
- KeyExchange.DHd,
- Authentication.DH,
- Encryption.DES,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.LOW,
- false,
- 56,
- 56,
- new String[] {"SSL_DH_DSS_WITH_DES_CBC_SHA"},
- null
- ),
- // Cipher 0D
- TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA(
- 0x000D,
- "DH-DSS-DES-CBC3-SHA",
- KeyExchange.DHd,
- Authentication.DH,
- Encryption.TRIPLE_DES,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.MEDIUM,
- true,
- 112,
- 168,
- new String[] {"SSL_DH_DSS_WITH_3DES_EDE_CBC_SHA"},
- null
- ),
- // Cipher 0E
- TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA(
- 0x000E,
- "EXP-DH-RSA-DES-CBC-SHA",
- KeyExchange.DHr,
- Authentication.DH,
- Encryption.DES,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- true,
- EncryptionLevel.EXP40,
- false,
- 40,
- 56,
- new String[] {"SSL_DH_RSA_EXPORT_WITH_DES40_CBC_SHA"},
- null
- ),
- // Cipher 0F
- TLS_DH_RSA_WITH_DES_CBC_SHA(
- 0x000F,
- "DH-RSA-DES-CBC-SHA",
- KeyExchange.DHr,
- Authentication.DH,
- Encryption.DES,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.LOW,
- false,
- 56,
- 56,
- new String[] {"SSL_DH_RSA_WITH_DES_CBC_SHA"},
- null
- ),
- // Cipher 10
- TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA(
- 0x0010,
- "DH-RSA-DES-CBC3-SHA",
- KeyExchange.DHr,
- Authentication.DH,
- Encryption.TRIPLE_DES,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.MEDIUM,
- true,
- 112,
- 168,
- new String[] {"SSL_DH_RSA_WITH_3DES_EDE_CBC_SHA"},
- null
- ),
- /* The Ephemeral DH ciphers */
- // Cipher 11
- TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA(
- 0x0011,
- "EXP-DHE-DSS-DES-CBC-SHA",
- KeyExchange.EDH,
- Authentication.DSS,
- Encryption.DES,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- true,
- EncryptionLevel.EXP40,
- false,
- 40,
- 56,
- new String[] {"SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA"},
- new String[] {"EXP-EDH-DSS-DES-CBC-SHA"}
- ),
- // Cipher 12
- TLS_DHE_DSS_WITH_DES_CBC_SHA(
- 0x0012,
- "DHE-DSS-DES-CBC-SHA",
- KeyExchange.EDH,
- Authentication.DSS,
- Encryption.DES,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.LOW,
- false,
- 56,
- 56,
- new String[] {"SSL_DHE_DSS_WITH_DES_CBC_SHA"},
- new String[] {"EDH-DSS-DES-CBC-SHA"}
- ),
- // Cipher 13
- TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA(
- 0x0013,
- "DHE-DSS-DES-CBC3-SHA",
- KeyExchange.EDH,
- Authentication.DSS,
- Encryption.TRIPLE_DES,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.MEDIUM,
- true,
- 112,
- 168,
- new String[] {"SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA"},
- new String[] {"EDH-DSS-DES-CBC3-SHA"}
- ),
- // Cipher 14
- TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA(
- 0x0014,
- "EXP-DHE-RSA-DES-CBC-SHA",
- KeyExchange.EDH,
- Authentication.RSA,
- Encryption.DES,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- true,
- EncryptionLevel.EXP40,
- false,
- 40,
- 56,
- new String[] {"SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA"},
- new String[] {"EXP-EDH-RSA-DES-CBC-SHA"}
- ),
- // Cipher 15
- TLS_DHE_RSA_WITH_DES_CBC_SHA(
- 0x0015,
- "DHE-RSA-DES-CBC-SHA",
- KeyExchange.EDH,
- Authentication.RSA,
- Encryption.DES,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.LOW,
- false,
- 56,
- 56,
- new String[] {"SSL_DHE_RSA_WITH_DES_CBC_SHA"},
- new String[] {"EDH-RSA-DES-CBC-SHA"}
- ),
- // Cipher 16
- TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA(
- 0x0016,
- "DHE-RSA-DES-CBC3-SHA",
- KeyExchange.EDH,
- Authentication.RSA,
- Encryption.TRIPLE_DES,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.MEDIUM,
- true,
- 112,
- 168,
- new String[] {"SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA"},
- new String[] {"EDH-RSA-DES-CBC3-SHA"}
- ),
- // Cipher 17
- TLS_DH_anon_EXPORT_WITH_RC4_40_MD5(
- 0x0017,
- "EXP-ADH-RC4-MD5",
- KeyExchange.EDH,
- Authentication.aNULL,
- Encryption.RC4,
- MessageDigest.MD5,
- Protocol.SSLv3,
- true,
- EncryptionLevel.EXP40,
- false,
- 40,
- 128,
- new String[] {"SSL_DH_anon_EXPORT_WITH_RC4_40_MD5"},
- null
- ),
- // Cipher 18
- TLS_DH_anon_WITH_RC4_128_MD5(
- 0x0018,
- "ADH-RC4-MD5",
- KeyExchange.EDH,
- Authentication.aNULL,
- Encryption.RC4,
- MessageDigest.MD5,
- Protocol.SSLv3,
- false,
- EncryptionLevel.MEDIUM,
- false,
- 128,
- 128,
- new String[] {"SSL_DH_anon_WITH_RC4_128_MD5"},
- null
- ),
- // Cipher 19
- TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA(
- 0x0019,
- "EXP-ADH-DES-CBC-SHA",
- KeyExchange.EDH,
- Authentication.aNULL,
- Encryption.DES,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- true,
- EncryptionLevel.EXP40,
- false,
- 40,
- 128,
- new String[] {"SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA"},
- null
- ),
- // Cipher 1A
- TLS_DH_anon_WITH_DES_CBC_SHA(
- 0x001A,
- "ADH-DES-CBC-SHA",
- KeyExchange.EDH,
- Authentication.aNULL,
- Encryption.DES,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.LOW,
- false,
- 56,
- 56,
- new String[] {"SSL_DH_anon_WITH_DES_CBC_SHA"},
- null
- ),
- // Cipher 1B
- TLS_DH_anon_WITH_3DES_EDE_CBC_SHA(
- 0x001B,
- "ADH-DES-CBC3-SHA",
- KeyExchange.EDH,
- Authentication.aNULL,
- Encryption.TRIPLE_DES,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.MEDIUM,
- true,
- 112,
- 168,
- new String[] {"SSL_DH_anon_WITH_3DES_EDE_CBC_SHA"},
- null
- ),
- /* Fortezza ciphersuite 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
- SSL_FORTEZZA_DMS_WITH_NULL_SHA(
- "FZA-NULL-SHA",
- KeyExchange.FZA,
- Authentication.FZA,
- Encryption.eNULL,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.STRONG_NONE,
- false,
- 0,
- 0,
- null,
- null
- ),
- // Cipher 1D
- SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA(
- "FZA-FZA-CBC-SHA",
- KeyExchange.FZA,
- Authentication.FZA,
- Encryption.FZA,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.STRONG_NONE,
- false,
- 0,
- 0,
- null,
- null
- ),
- // Cipher 1E - overlaps with Kerberos below
- SSL_FORTEZZA_DMS_WITH_RC4_128_SHA(
- "FZA-RC4-SHA",
- KeyExchange.FZA,
- Authentication.FZA,
- Encryption.RC4,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.MEDIUM,
- false,
- 128,
- 128,
- null,
- null
- ),
- */
- /* The Kerberos ciphers. OpenSSL doesn't support these. Java does but they
- * are used for Kerberos authentication.
- */
- // Cipher 1E - overlaps with Fortezza above
- /*TLS_KRB5_WITH_DES_CBC_SHA(
- "KRB5-DES-CBC-SHA",
- KeyExchange.KRB5,
- Authentication.KRB5,
- Encryption.DES,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.LOW,
- false,
- 56,
- 56,
- null,
- null
- ),
- // Cipher 1F
- TLS_KRB5_WITH_3DES_EDE_CBC_SHA(
- "KRB5-DES-CBC3-SHA",
- KeyExchange.KRB5,
- Authentication.KRB5,
- Encryption.TRIPLE_DES,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.HIGH,
- true,
- 112,
- 168,
- null,
- null
- ),
- // Cipher 20
- TLS_KRB5_WITH_RC4_128_SHA(
- "KRB5-RC4-SHA",
- KeyExchange.KRB5,
- Authentication.KRB5,
- Encryption.RC4,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.MEDIUM,
- false,
- 128,
- 128,
- null,
- null
- ),
- // Cipher 21
- TLS_KRB5_WITH_IDEA_CBC_SHA(
- "KRB5-IDEA-CBC-SHA",
- KeyExchange.KRB5,
- Authentication.KRB5,
- Encryption.IDEA,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.MEDIUM,
- false,
- 128,
- 128,
- null,
- null
- ),
- // Cipher 22
- TLS_KRB5_WITH_DES_CBC_MD5(
- "KRB5-DES-CBC-MD5",
- KeyExchange.KRB5,
- Authentication.KRB5,
- Encryption.DES,
- MessageDigest.MD5,
- Protocol.SSLv3,
- false,
- EncryptionLevel.LOW,
- false,
- 56,
- 56,
- null,
- null
- ),
- // Cipher 23
- TLS_KRB5_WITH_3DES_EDE_CBC_MD5(
- "KRB5-DES-CBC3-MD5",
- KeyExchange.KRB5,
- Authentication.KRB5,
- Encryption.TRIPLE_DES,
- MessageDigest.MD5,
- Protocol.SSLv3,
- false,
- EncryptionLevel.HIGH,
- false,
- 112,
- 168,
- null,
- null
- ),
- // Cipher 24
- TLS_KRB5_WITH_RC4_128_MD5(
- "KRB5-RC4-MD5",
- KeyExchange.KRB5,
- Authentication.KRB5,
- Encryption.RC4,
- MessageDigest.MD5,
- Protocol.SSLv3,
- false,
- EncryptionLevel.MEDIUM,
- false,
- 128,
- 128,
- null,
- null
- ),
- // Cipher 25
- TLS_KRB5_WITH_IDEA_CBC_MD5(
- "KRB5-IDEA-CBC-MD5",
- KeyExchange.KRB5,
- Authentication.KRB5,
- Encryption.IDEA,
- MessageDigest.MD5,
- Protocol.SSLv3,
- false,
- EncryptionLevel.MEDIUM,
- false,
- 128,
- 128,
- null,
- null
- ),
- // Cipher 26
- TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA(
- "EXP-KRB5-DES-CBC-SHA",
- KeyExchange.KRB5,
- Authentication.KRB5,
- Encryption.DES,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- true,
- EncryptionLevel.EXP40,
- false,
- 40,
- 56,
- null,
- null
- ),
- // Cipher 27
- TLS_KRB5_EXPORT_WITH_RC2_CBC_40_SHA(
- "EXP-KRB5-RC2-CBC-SHA",
- KeyExchange.KRB5,
- Authentication.KRB5,
- Encryption.RC2,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- true,
- EncryptionLevel.EXP40,
- false,
- 40,
- 128,
- null,
- null
- ),
- // Cipher 28
- TLS_KRB5_EXPORT_WITH_RC4_40_SHA(
- "EXP-KRB5-RC4-SHA",
- KeyExchange.KRB5,
- Authentication.KRB5,
- Encryption.RC4,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- true,
- EncryptionLevel.EXP40,
- false,
- 40,
- 128,
- null,
- null
- ),
- // Cipher 29
- TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5(
- "EXP-KRB5-DES-CBC-MD5",
- KeyExchange.KRB5,
- Authentication.KRB5,
- Encryption.DES,
- MessageDigest.MD5,
- Protocol.SSLv3,
- true,
- EncryptionLevel.EXP40,
- false,
- 40,
- 56,
- null,
- null
- ),
- // Cipher 2A
- TLS_KRB5_EXPORT_WITH_RC2_CBC_40_MD5(
- "EXP-KRB5-RC2-CBC-MD5",
- KeyExchange.KRB5,
- Authentication.KRB5,
- Encryption.RC2,
- MessageDigest.MD5,
- Protocol.SSLv3,
- true,
- EncryptionLevel.EXP40,
- false,
- 40,
- 128,
- null,
- null
- ),
- // Cipher 2B
- TLS_KRB5_EXPORT_WITH_RC4_40_MD5(
- "EXP-KRB5-RC4-MD5",
- KeyExchange.KRB5,
- Authentication.KRB5,
- Encryption.RC4,
- MessageDigest.MD5,
- Protocol.SSLv3,
- true,
- EncryptionLevel.EXP40,
- false,
- 40,
- 128,
- null,
- null
- ),*/
-
- /* PSK cipher suites from RFC 4785 */
- // Cipher 2C
- TLS_PSK_WITH_NULL_SHA(
- 0x002c,
- "PSK-NULL-SHA",
- KeyExchange.PSK,
- Authentication.PSK,
- Encryption.eNULL,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.STRONG_NONE,
- true,
- 0,
- 0,
- null,
- null
- ),
- // Cipher 2D
- TLS_DHE_PSK_WITH_NULL_SHA(
- 0x002d,
- "DHE-PSK-NULL-SHA",
- KeyExchange.DHEPSK,
- Authentication.PSK,
- Encryption.eNULL,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.STRONG_NONE,
- true,
- 0,
- 0,
- null,
- null
- ),
- // Cipher 2E
- TLS_RSA_PSK_WITH_NULL_SHA(
- 0x002e,
- "RSA-PSK-NULL-SHA",
- KeyExchange.RSAPSK,
- Authentication.RSA,
- Encryption.eNULL,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.STRONG_NONE,
- true,
- 0,
- 0,
- null,
- null
- ),
- /* New AES ciphersuites */
- // Cipher 2F
- TLS_RSA_WITH_AES_128_CBC_SHA(
- 0x002f,
- "AES128-SHA",
- KeyExchange.RSA,
- Authentication.RSA,
- Encryption.AES128,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.HIGH,
- true,
- 128,
- 128,
- null,
- null
- ),
- // Cipher 30
- TLS_DH_DSS_WITH_AES_128_CBC_SHA(
- 0x0030,
- "DH-DSS-AES128-SHA",
- KeyExchange.DHd,
- Authentication.DH,
- Encryption.AES128,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.HIGH,
- true,
- 128,
- 128,
- null,
- null
- ),
- // Cipher 31
- TLS_DH_RSA_WITH_AES_128_CBC_SHA(
- 0x0031,
- "DH-RSA-AES128-SHA",
- KeyExchange.DHr,
- Authentication.DH,
- Encryption.AES128,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.HIGH,
- true,
- 128,
- 128,
- null,
- null
- ),
- // Cipher 32
- TLS_DHE_DSS_WITH_AES_128_CBC_SHA(
- 0x0032,
- "DHE-DSS-AES128-SHA",
- KeyExchange.EDH,
- Authentication.DSS,
- Encryption.AES128,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.HIGH,
- true,
- 128,
- 128,
- null,
- null
- ),
- // Cipher 33
- TLS_DHE_RSA_WITH_AES_128_CBC_SHA(
- 0x0033,
- "DHE-RSA-AES128-SHA",
- KeyExchange.EDH,
- Authentication.RSA,
- Encryption.AES128,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.HIGH,
- true,
- 128,
- 128,
- null,
- null
- ),
- // Cipher 34
- TLS_DH_anon_WITH_AES_128_CBC_SHA(
- 0x0034,
- "ADH-AES128-SHA",
- KeyExchange.EDH,
- Authentication.aNULL,
- Encryption.AES128,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.HIGH,
- true,
- 128,
- 128,
- null,
- null
- ),
- // Cipher 35
- TLS_RSA_WITH_AES_256_CBC_SHA(
- 0x0035,
- "AES256-SHA",
- KeyExchange.RSA,
- Authentication.RSA,
- Encryption.AES256,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.HIGH,
- true,
- 256,
- 256,
- null,
- null
- ),
- // Cipher 36
- TLS_DH_DSS_WITH_AES_256_CBC_SHA(
- 0x0036,
- "DH-DSS-AES256-SHA",
- KeyExchange.DHd,
- Authentication.DH,
- Encryption.AES256,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.HIGH,
- true,
- 256,
- 256,
- null,
- null
- ),
- // Cipher 37
- TLS_DH_RSA_WITH_AES_256_CBC_SHA(
- 0x0037,
- "DH-RSA-AES256-SHA",
- KeyExchange.DHr,
- Authentication.DH,
- Encryption.AES256,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.HIGH,
- true,
- 256,
- 256,
- null,
- null
- ),
- // Cipher 38
- TLS_DHE_DSS_WITH_AES_256_CBC_SHA(
- 0x0038,
- "DHE-DSS-AES256-SHA",
- KeyExchange.EDH,
- Authentication.DSS,
- Encryption.AES256,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.HIGH,
- true,
- 256,
- 256,
- null,
- null
- ),
- // Cipher 39
- TLS_DHE_RSA_WITH_AES_256_CBC_SHA(
- 0x0039,
- "DHE-RSA-AES256-SHA",
- KeyExchange.EDH,
- Authentication.RSA,
- Encryption.AES256,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.HIGH,
- true,
- 256,
- 256,
- null,
- null
- ),
- // Cipher 3A
- TLS_DH_anon_WITH_AES_256_CBC_SHA(
- 0x003A,
- "ADH-AES256-SHA",
- KeyExchange.EDH,
- Authentication.aNULL,
- Encryption.AES256,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.HIGH,
- true,
- 256,
- 256,
- null,
- null
- ),
- /* TLS v1.2 ciphersuites */
- // Cipher 3B
- TLS_RSA_WITH_NULL_SHA256(
- 0x003B,
- "NULL-SHA256",
- KeyExchange.RSA,
- Authentication.RSA,
- Encryption.eNULL,
- MessageDigest.SHA256,
- Protocol.TLSv1_2,
- false,
- EncryptionLevel.STRONG_NONE,
- true,
- 0,
- 0,
- null,
- null
- ),
- // Cipher 3C
- TLS_RSA_WITH_AES_128_CBC_SHA256(
- 0x003C,
- "AES128-SHA256",
- KeyExchange.RSA,
- Authentication.RSA,
- Encryption.AES128,
- MessageDigest.SHA256,
- Protocol.TLSv1_2,
- false,
- EncryptionLevel.HIGH,
- true,
- 128,
- 128,
- null,
- null
- ),
- // Cipher 3D
- TLS_RSA_WITH_AES_256_CBC_SHA256(
- 0x003D,
- "AES256-SHA256",
- KeyExchange.RSA,
- Authentication.RSA,
- Encryption.AES256,
- MessageDigest.SHA256,
- Protocol.TLSv1_2,
- false,
- EncryptionLevel.HIGH,
- true,
- 256,
- 256,
- null,
- null
- ),
- // Cipher 3E
- TLS_DH_DSS_WITH_AES_128_CBC_SHA256(
- 0x003E,
- "DH-DSS-AES128-SHA256",
- KeyExchange.DHd,
- Authentication.DH,
- Encryption.AES128,
- MessageDigest.SHA256,
- Protocol.TLSv1_2,
- false,
- EncryptionLevel.HIGH,
- true,
- 128,
- 128,
- null,
- null
- ),
- // Cipher 3F
- TLS_DH_RSA_WITH_AES_128_CBC_SHA256(
- 0x003F,
- "DH-RSA-AES128-SHA256",
- KeyExchange.DHr,
- Authentication.DH,
- Encryption.AES128,
- MessageDigest.SHA256,
- Protocol.TLSv1_2,
- false,
- EncryptionLevel.HIGH,
- true,
- 128,
- 128,
- null,
- null
- ),
- // Cipher 40
- TLS_DHE_DSS_WITH_AES_128_CBC_SHA256(
- 0x0040,
- "DHE-DSS-AES128-SHA256",
- KeyExchange.EDH,
- Authentication.DSS,
- Encryption.AES128,
- MessageDigest.SHA256,
- Protocol.TLSv1_2,
- false,
- EncryptionLevel.HIGH,
- true,
- 128,
- 128,
- null,
- null
- ),
- /* Camellia ciphersuites from RFC4132 (
- 128-bit portion) */
- // Cipher 41
- TLS_RSA_WITH_CAMELLIA_128_CBC_SHA(
- 0x0041,
- "CAMELLIA128-SHA",
- KeyExchange.RSA,
- Authentication.RSA,
- Encryption.CAMELLIA128,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.HIGH,
- false,
- 128,
- 128,
- null,
- null
- ),
- // Cipher 42
- TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA(
- 0x0042,
- "DH-DSS-CAMELLIA128-SHA",
- KeyExchange.DHd,
- Authentication.DH,
- Encryption.CAMELLIA128,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.HIGH,
- false,
- 128,
- 128,
- null,
- null
- ),
- // Cipher 43
- TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA(
- 0x0043,
- "DH-RSA-CAMELLIA128-SHA",
- KeyExchange.DHr,
- Authentication.DH,
- Encryption.CAMELLIA128,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.HIGH,
- false,
- 128,
- 128,
- null,
- null
- ),
- // Cipher 44
- TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA(
- 0x0044,
- "DHE-DSS-CAMELLIA128-SHA",
- KeyExchange.EDH,
- Authentication.DSS,
- Encryption.CAMELLIA128,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.HIGH,
- false,
- 128,
- 128,
- null,
- null
- ),
- // Cipher 45
- TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA(
- 0x0045,
- "DHE-RSA-CAMELLIA128-SHA",
- KeyExchange.EDH,
- Authentication.RSA,
- Encryption.CAMELLIA128,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.HIGH,
- false,
- 128,
- 128,
- null,
- null
- ),
- // Cipher 46
- TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA(
- 0x0046,
- "ADH-CAMELLIA128-SHA",
- KeyExchange.EDH,
- Authentication.aNULL,
- Encryption.CAMELLIA128,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.HIGH,
- false,
- 128,
- 128,
- null,
- null
- ),
- /* Experimental (and now expired) TLSv1 versions of SSLv3 ciphers.
- * Unsupported by Java and OpenSSL 1.1.x onwards. Some earlier OpenSSL
- * versions do support these. */
- // Cipher 60
- TLS_RSA_EXPORT1024_WITH_RC4_56_MD5(
- 0x0060,
- "EXP1024-RC4-MD5",
- KeyExchange.RSA,
- Authentication.RSA,
- Encryption.RC4,
- MessageDigest.MD5,
- Protocol.TLSv1,
- true,
- EncryptionLevel.EXP56,
- false,
- 56,
- 128,
- new String[] {"SSL_RSA_EXPORT1024_WITH_RC4_56_MD5"},
- null
- ),
- // Cipher 61
- TLS_RSA_EXPORT1024_WITH_RC2_CBC_56_MD5(
- 0x0061,
- "EXP1024-RC2-CBC-MD5",
- KeyExchange.RSA,
- Authentication.RSA,
- Encryption.RC2,
- MessageDigest.MD5,
- Protocol.TLSv1,
- true,
- EncryptionLevel.EXP56,
- false,
- 56,
- 128,
- new String[] {"SSL_RSA_EXPORT1024_WITH_RC2_CBC_56_MD5"},
- null
- ),
- // Cipher 62
- TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA(
- 0x0062,
- "EXP1024-DES-CBC-SHA",
- KeyExchange.RSA,
- Authentication.RSA,
- Encryption.DES,
- MessageDigest.SHA1,
- Protocol.TLSv1,
- true,
- EncryptionLevel.EXP56,
- false,
- 56,
- 56,
- new String[] {"SSL_RSA_EXPORT1024_WITH_DES_CBC_SHA"},
- null
- ),
- // Cipher 63
- TLS_DHE_DSS_EXPORT1024_WITH_DES_CBC_SHA(
- 0x0063,
- "EXP1024-DHE-DSS-DES-CBC-SHA",
- KeyExchange.EDH,
- Authentication.DSS,
- Encryption.DES,
- MessageDigest.SHA1,
- Protocol.TLSv1,
- true,
- EncryptionLevel.EXP56,
- false,
- 56,
- 56,
- new String[] {"SSL_DHE_DSS_EXPORT1024_WITH_DES_CBC_SHA"},
- null
- ),
- // Cipher 64
- TLS_RSA_EXPORT1024_WITH_RC4_56_SHA(
- 0x0064,
- "EXP1024-RC4-SHA",
- KeyExchange.RSA,
- Authentication.RSA,
- Encryption.RC4,
- MessageDigest.SHA1,
- Protocol.TLSv1,
- true,
- EncryptionLevel.EXP56,
- false,
- 56,
- 128,
- new String[] {"SSL_RSA_EXPORT1024_WITH_RC4_56_SHA"},
- null
- ),
- // Cipher 65
- TLS_DHE_DSS_EXPORT1024_WITH_RC4_56_SHA(
- 0x0065,
- "EXP1024-DHE-DSS-RC4-SHA",
- KeyExchange.EDH,
- Authentication.DSS,
- Encryption.RC4,
- MessageDigest.SHA1,
- Protocol.TLSv1,
- true,
- EncryptionLevel.EXP56,
- false,
- 56,
- 128,
- new String[] {"SSL_DHE_DSS_EXPORT1024_WITH_RC4_56_SHA"},
- null
- ),
- // Cipher 66
- TLS_DHE_DSS_WITH_RC4_128_SHA(
- 0x0066,
- "DHE-DSS-RC4-SHA",
- KeyExchange.EDH,
- Authentication.DSS,
- Encryption.RC4,
- MessageDigest.SHA1,
- Protocol.TLSv1,
- false,
- EncryptionLevel.MEDIUM,
- false,
- 128,
- 128,
- new String[] {"SSL_DHE_DSS_WITH_RC4_128_SHA"},
- null
- ),
- /* TLS v1.2 ciphersuites */
- // Cipher 67
- TLS_DHE_RSA_WITH_AES_128_CBC_SHA256(
- 0x0067,
- "DHE-RSA-AES128-SHA256",
- KeyExchange.EDH,
- Authentication.RSA,
- Encryption.AES128,
- MessageDigest.SHA256,
- Protocol.TLSv1_2,
- false,
- EncryptionLevel.HIGH,
- true,
- 128,
- 128,
- null,
- null
- ),
- // Cipher 68
- TLS_DH_DSS_WITH_AES_256_CBC_SHA256(
- 0x0068,
- "DH-DSS-AES256-SHA256",
- KeyExchange.DHd,
- Authentication.DH,
- Encryption.AES256,
- MessageDigest.SHA256,
- Protocol.TLSv1_2,
- false,
- EncryptionLevel.HIGH,
- true,
- 256,
- 256,
- null,
- null
- ),
- // Cipher 69
- TLS_DH_RSA_WITH_AES_256_CBC_SHA256(
- 0x0069,
- "DH-RSA-AES256-SHA256",
- KeyExchange.DHr,
- Authentication.DH,
- Encryption.AES256,
- MessageDigest.SHA256,
- Protocol.TLSv1_2,
- false,
- EncryptionLevel.HIGH,
- true,
- 256,
- 256,
- null,
- null
- ),
- // Cipher 6A
- TLS_DHE_DSS_WITH_AES_256_CBC_SHA256(
- 0x006A,
- "DHE-DSS-AES256-SHA256",
- KeyExchange.EDH,
- Authentication.DSS,
- Encryption.AES256,
- MessageDigest.SHA256,
- Protocol.TLSv1_2,
- false,
- EncryptionLevel.HIGH,
- true,
- 256,
- 256,
- null,
- null
- ),
- // Cipher 6B
- TLS_DHE_RSA_WITH_AES_256_CBC_SHA256(
- 0x006B,
- "DHE-RSA-AES256-SHA256",
- KeyExchange.EDH,
- Authentication.RSA,
- Encryption.AES256,
- MessageDigest.SHA256,
- Protocol.TLSv1_2,
- false,
- EncryptionLevel.HIGH,
- true,
- 256,
- 256,
- null,
- null
- ),
- // Cipher 6C
- TLS_DH_anon_WITH_AES_128_CBC_SHA256(
- 0x006C,
- "ADH-AES128-SHA256",
- KeyExchange.EDH,
- Authentication.aNULL,
- Encryption.AES128,
- MessageDigest.SHA256,
- Protocol.TLSv1_2,
- false,
- EncryptionLevel.HIGH,
- true,
- 128,
- 128,
- null,
- null
- ),
- // Cipher 6D
- TLS_DH_anon_WITH_AES_256_CBC_SHA256(
- 0x006D,
- "ADH-AES256-SHA256",
- KeyExchange.EDH,
- Authentication.aNULL,
- Encryption.AES256,
- MessageDigest.SHA256,
- Protocol.TLSv1_2,
- false,
- EncryptionLevel.HIGH,
- true,
- 256,
- 256,
- null,
- null
- ),
- /* GOST Ciphersuites. Unsupported by Java. OpenSSl lists them with IDs
- * 0x3000080 to 0x3000083 */
- /*
- // Cipher 80
- TLS_GOSTR341094_WITH_28147_CNT_IMIT(
- "GOST94-GOST89-GOST89",
- KeyExchange.GOST,
- Authentication.GOST94,
- Encryption.eGOST2814789CNT,
- MessageDigest.GOST89MAC,
- Protocol.TLSv1,
- false,
- EncryptionLevel.HIGH,
- false,
- 256,
- 256,
- null,
- null
- ),
- // Cipher 81
- TLS_GOSTR341001_WITH_28147_CNT_IMIT(
- "GOST2001-GOST89-GOST89",
- KeyExchange.GOST,
- Authentication.GOST01,
- Encryption.eGOST2814789CNT,
- MessageDigest.GOST89MAC,
- Protocol.TLSv1,
- false,
- EncryptionLevel.HIGH,
- false,
- 256,
- 256,
- null,
- null
- ),
- // Cipher 82
- TLS_GOSTR341094_WITH_NULL_GOSTR3411(
- "GOST94-NULL-GOST94",
- KeyExchange.GOST,
- Authentication.GOST94,
- Encryption.eNULL,
- MessageDigest.GOST94,
- Protocol.TLSv1,
- false,
- EncryptionLevel.STRONG_NONE,
- false,
- 0,
- 0,
- null,
- null
- ),
- // Cipher 83
- TLS_GOSTR341001_WITH_NULL_GOSTR3411(
- "GOST2001-NULL-GOST94",
- KeyExchange.GOST,
- Authentication.GOST01,
- Encryption.eNULL,
- MessageDigest.GOST94,
- Protocol.TLSv1,
- false,
- EncryptionLevel.STRONG_NONE,
- false,
- 0,
- 0,
- null,
- null
- ),*/
- /* Camellia ciphersuites from RFC4132 (
- 256-bit portion) */
- // Cipher 84
- TLS_RSA_WITH_CAMELLIA_256_CBC_SHA(
- 0x0084,
- "CAMELLIA256-SHA",
- KeyExchange.RSA,
- Authentication.RSA,
- Encryption.CAMELLIA256,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.HIGH,
- false,
- 256,
- 256,
- null,
- null
- ),
- // Cipher 85
- TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA(
- 0x0085,
- "DH-DSS-CAMELLIA256-SHA",
- KeyExchange.DHd,
- Authentication.DH,
- Encryption.CAMELLIA256,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.HIGH,
- false,
- 256,
- 256,
- null,
- null
- ),
- // Cipher 86
- TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA(
- 0x0086,
- "DH-RSA-CAMELLIA256-SHA",
- KeyExchange.DHr,
- Authentication.DH,
- Encryption.CAMELLIA256,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.HIGH,
- false,
- 256,
- 256,
- null,
- null
- ),
- // Cipher 87
- TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA(
- 0x0087,
- "DHE-DSS-CAMELLIA256-SHA",
- KeyExchange.EDH,
- Authentication.DSS,
- Encryption.CAMELLIA256,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.HIGH,
- false,
- 256,
- 256,
- null,
- null
- ),
- // Cipher 88
- TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA(
- 0x0088,
- "DHE-RSA-CAMELLIA256-SHA",
- KeyExchange.EDH,
- Authentication.RSA,
- Encryption.CAMELLIA256,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.HIGH,
- false,
- 256,
- 256,
- null,
- null
- ),
- // Cipher 89
- TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA(
- 0x0089,
- "ADH-CAMELLIA256-SHA",
- KeyExchange.EDH,
- Authentication.aNULL,
- Encryption.CAMELLIA256,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.HIGH,
- false,
- 256,
- 256,
- null,
- null
- ),
- // Cipher 8A
- TLS_PSK_WITH_RC4_128_SHA(
- 0x008A,
- "PSK-RC4-SHA",
- KeyExchange.PSK,
- Authentication.PSK,
- Encryption.RC4,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.MEDIUM,
- false,
- 128,
- 128,
- null,
- null
- ),
- // Cipher 8B
- TLS_PSK_WITH_3DES_EDE_CBC_SHA(
- 0x008B,
- "PSK-3DES-EDE-CBC-SHA",
- KeyExchange.PSK,
- Authentication.PSK,
- Encryption.TRIPLE_DES,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.MEDIUM,
- true,
- 112,
- 168,
- null,
- null
- ),
- // Cipher 8C
- TLS_PSK_WITH_AES_128_CBC_SHA(
- 0x008C,
- "PSK-AES128-CBC-SHA",
- KeyExchange.PSK,
- Authentication.PSK,
- Encryption.AES128,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.HIGH,
- true,
- 128,
- 128,
- null,
- null
- ),
- // Cipher 8D
- TLS_PSK_WITH_AES_256_CBC_SHA(
- 0x008D,
- "PSK-AES256-CBC-SHA",
- KeyExchange.PSK,
- Authentication.PSK,
- Encryption.AES256,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.HIGH,
- true,
- 256,
- 256,
- null,
- null
- ),
- // Cipher 8E
- TLS_DHE_PSK_WITH_RC4_128_SHA(
- 0x008E,
- "DHE-PSK-RC4-SHA",
- KeyExchange.DHEPSK,
- Authentication.PSK,
- Encryption.RC4,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.MEDIUM,
- false,
- 128,
- 128,
- null,
- null
- ),
- // Cipher 8F
- TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA(
- 0x008F,
- "DHE-PSK-3DES-EDE-CBC-SHA",
- KeyExchange.DHEPSK,
- Authentication.PSK,
- Encryption.TRIPLE_DES,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.MEDIUM,
- true,
- 112,
- 168,
- null,
- null
- ),
- // Cipher 90
- TLS_DHE_PSK_WITH_AES_128_CBC_SHA(
- 0x0090,
- "DHE-PSK-AES128-CBC-SHA",
- KeyExchange.DHEPSK,
- Authentication.PSK,
- Encryption.AES128,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.HIGH,
- true,
- 128,
- 128,
- null,
- null
- ),
- // Cipher 91
- TLS_DHE_PSK_WITH_AES_256_CBC_SHA(
- 0x0091,
- "DHE-PSK-AES256-CBC-SHA",
- KeyExchange.DHEPSK,
- Authentication.PSK,
- Encryption.AES256,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.HIGH,
- true,
- 256,
- 256,
- null,
- null
- ),
- // Cipher 92
- TLS_RSA_PSK_WITH_RC4_128_SHA(
- 0x0092,
- "RSA-PSK-RC4-SHA",
- KeyExchange.RSAPSK,
- Authentication.RSA,
- Encryption.RC4,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.MEDIUM,
- false,
- 128,
- 128,
- null,
- null
- ),
- // Cipher 93
- TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA(
- 0x0093,
- "RSA-PSK-3DES-EDE-CBC-SHA",
- KeyExchange.RSAPSK,
- Authentication.RSA,
- Encryption.TRIPLE_DES,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.MEDIUM,
- true,
- 112,
- 168,
- null,
- null
- ),
- // Cipher 94
- TLS_RSA_PSK_WITH_AES_128_CBC_SHA(
- 0x0094,
- "RSA-PSK-AES128-CBC-SHA",
- KeyExchange.RSAPSK,
- Authentication.RSA,
- Encryption.AES128,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.HIGH,
- true,
- 128,
- 128,
- null,
- null
- ),
- // Cipher 95
- TLS_RSA_PSK_WITH_AES_256_CBC_SHA(
- 0x0095,
- "RSA-PSK-AES256-CBC-SHA",
- KeyExchange.RSAPSK,
- Authentication.RSA,
- Encryption.AES256,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.HIGH,
- true,
- 256,
- 256,
- null,
- null
- ),
- /* SEED ciphersuites from RFC4162 */
- // Cipher 96
- TLS_RSA_WITH_SEED_CBC_SHA(
- 0x0096,
- "SEED-SHA",
- KeyExchange.RSA,
- Authentication.RSA,
- Encryption.SEED,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.MEDIUM,
- false,
- 128,
- 128,
- null,
- null
- ),
- // Cipher 97
- TLS_DH_DSS_WITH_SEED_CBC_SHA(
- 0x0097,
- "DH-DSS-SEED-SHA",
- KeyExchange.DHd,
- Authentication.DH,
- Encryption.SEED,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.MEDIUM,
- false,
- 128,
- 128,
- null,
- null
- ),
- // Cipher 98
- TLS_DH_RSA_WITH_SEED_CBC_SHA(
- 0x0098,
- "DH-RSA-SEED-SHA",
- KeyExchange.DHr,
- Authentication.DH,
- Encryption.SEED,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.MEDIUM,
- false,
- 128,
- 128,
- null,
- null
- ),
- // Cipher 99
- TLS_DHE_DSS_WITH_SEED_CBC_SHA(
- 0x0099,
- "DHE-DSS-SEED-SHA",
- KeyExchange.EDH,
- Authentication.DSS,
- Encryption.SEED,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.MEDIUM,
- false,
- 128,
- 128,
- null,
- null
- ),
- // Cipher 9A
- TLS_DHE_RSA_WITH_SEED_CBC_SHA(
- 0x009A,
- "DHE-RSA-SEED-SHA",
- KeyExchange.EDH,
- Authentication.RSA,
- Encryption.SEED,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.MEDIUM,
- false,
- 128,
- 128,
- null,
- null
- ),
- // Cipher 9B
- TLS_DH_anon_WITH_SEED_CBC_SHA(
- 0x009B,
- "ADH-SEED-SHA",
- KeyExchange.EDH,
- Authentication.aNULL,
- Encryption.SEED,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.MEDIUM,
- false,
- 128,
- 128,
- null,
- null
- ),
- /* GCM ciphersuites from RFC5288 */
- // Cipher 9C
- TLS_RSA_WITH_AES_128_GCM_SHA256(
- 0x009C,
- "AES128-GCM-SHA256",
- KeyExchange.RSA,
- Authentication.RSA,
- Encryption.AES128GCM,
- MessageDigest.AEAD,
- Protocol.TLSv1_2,
- false,
- EncryptionLevel.HIGH,
- true,
- 128,
- 128,
- null,
- null
- ),
- // Cipher 9D
- TLS_RSA_WITH_AES_256_GCM_SHA384(
- 0x009D,
- "AES256-GCM-SHA384",
- KeyExchange.RSA,
- Authentication.RSA,
- Encryption.AES256GCM,
- MessageDigest.AEAD,
- Protocol.TLSv1_2,
- false,
- EncryptionLevel.HIGH,
- true,
- 256,
- 256,
- null,
- null
- ),
- // Cipher 9E
- TLS_DHE_RSA_WITH_AES_128_GCM_SHA256(
- 0x009E,
- "DHE-RSA-AES128-GCM-SHA256",
- KeyExchange.EDH,
- Authentication.RSA,
- Encryption.AES128GCM,
- MessageDigest.AEAD,
- Protocol.TLSv1_2,
- false,
- EncryptionLevel.HIGH,
- true,
- 128,
- 128,
- null,
- null
- ),
- // Cipher 9F
- TLS_DHE_RSA_WITH_AES_256_GCM_SHA384(
- 0x009F,
- "DHE-RSA-AES256-GCM-SHA384",
- KeyExchange.EDH,
- Authentication.RSA,
- Encryption.AES256GCM,
- MessageDigest.AEAD,
- Protocol.TLSv1_2,
- false,
- EncryptionLevel.HIGH,
- true,
- 256,
- 256,
- null,
- null
- ),
- // Cipher A0
- TLS_DH_RSA_WITH_AES_128_GCM_SHA256(
- 0x00A0,
- "DH-RSA-AES128-GCM-SHA256",
- KeyExchange.DHr,
- Authentication.DH,
- Encryption.AES128GCM,
- MessageDigest.AEAD,
- Protocol.TLSv1_2,
- false,
- EncryptionLevel.HIGH,
- true,
- 128,
- 128,
- null,
- null
- ),
- // Cipher A1
- TLS_DH_RSA_WITH_AES_256_GCM_SHA384(
- 0x00A1,
- "DH-RSA-AES256-GCM-SHA384",
- KeyExchange.DHr,
- Authentication.DH,
- Encryption.AES256GCM,
- MessageDigest.AEAD,
- Protocol.TLSv1_2,
- false,
- EncryptionLevel.HIGH,
- true,
- 256,
- 256,
- null,
- null
- ),
- // Cipher A2
- TLS_DHE_DSS_WITH_AES_128_GCM_SHA256(
- 0x00A2,
- "DHE-DSS-AES128-GCM-SHA256",
- KeyExchange.EDH,
- Authentication.DSS,
- Encryption.AES128GCM,
- MessageDigest.AEAD,
- Protocol.TLSv1_2,
- false,
- EncryptionLevel.HIGH,
- true,
- 128,
- 128,
- null,
- null
- ),
- // Cipher A3
- TLS_DHE_DSS_WITH_AES_256_GCM_SHA384(
- 0x00A3,
- "DHE-DSS-AES256-GCM-SHA384",
- KeyExchange.EDH,
- Authentication.DSS,
- Encryption.AES256GCM,
- MessageDigest.AEAD,
- Protocol.TLSv1_2,
- false,
- EncryptionLevel.HIGH,
- true,
- 256,
- 256,
- null,
- null
- ),
- // Cipher A4
- TLS_DH_DSS_WITH_AES_128_GCM_SHA256(
- 0x00A4,
- "DH-DSS-AES128-GCM-SHA256",
- KeyExchange.DHd,
- Authentication.DH,
- Encryption.AES128GCM,
- MessageDigest.AEAD,
- Protocol.TLSv1_2,
- false,
- EncryptionLevel.HIGH,
- true,
- 128,
- 128,
- null,
- null
- ),
- // Cipher A5
- TLS_DH_DSS_WITH_AES_256_GCM_SHA384(
- 0x00A5,
- "DH-DSS-AES256-GCM-SHA384",
- KeyExchange.DHd,
- Authentication.DH,
- Encryption.AES256GCM,
- MessageDigest.AEAD,
- Protocol.TLSv1_2,
- false,
- EncryptionLevel.HIGH,
- true,
- 256,
- 256,
- null,
- null
- ),
- // Cipher A6
- TLS_DH_anon_WITH_AES_128_GCM_SHA256(
- 0x00A6,
- "ADH-AES128-GCM-SHA256",
- KeyExchange.EDH,
- Authentication.aNULL,
- Encryption.AES128GCM,
- MessageDigest.AEAD,
- Protocol.TLSv1_2,
- false,
- EncryptionLevel.HIGH,
- true,
- 128,
- 128,
- null,
- null
- ),
- // Cipher A7
- TLS_DH_anon_WITH_AES_256_GCM_SHA384(
- 0x00A7,
- "ADH-AES256-GCM-SHA384",
- KeyExchange.EDH,
- Authentication.aNULL,
- Encryption.AES256GCM,
- MessageDigest.AEAD,
- Protocol.TLSv1_2,
- false,
- EncryptionLevel.HIGH,
- true,
- 256,
- 256,
- null,
- null
- ),
- // Cipher A8
- TLS_PSK_WITH_AES_128_GCM_SHA256(
- 0x00A8,
- "PSK-AES128-GCM-SHA256",
- KeyExchange.PSK,
- Authentication.PSK,
- Encryption.AES128GCM,
- MessageDigest.AEAD,
- Protocol.TLSv1_2,
- false,
- EncryptionLevel.HIGH,
- true,
- 128,
- 128,
- null,
- null
- ),
- // Cipher A9
- TLS_PSK_WITH_AES_256_GCM_SHA384(
- 0x00A9,
- "PSK-AES256-GCM-SHA384",
- KeyExchange.PSK,
- Authentication.PSK,
- Encryption.AES256GCM,
- MessageDigest.AEAD,
- Protocol.TLSv1_2,
- false,
- EncryptionLevel.HIGH,
- true,
- 256,
- 256,
- null,
- null
- ),
- // Cipher AA
- TLS_DHE_PSK_WITH_AES_128_GCM_SHA256(
- 0x00AA,
- "DHE-PSK-AES128-GCM-SHA256",
- KeyExchange.DHEPSK,
- Authentication.PSK,
- Encryption.AES128GCM,
- MessageDigest.AEAD,
- Protocol.TLSv1_2,
- false,
- EncryptionLevel.HIGH,
- true,
- 128,
- 128,
- null,
- null
- ),
- // Cipher AB
- TLS_DHE_PSK_WITH_AES_256_GCM_SHA384(
- 0x00AB,
- "DHE-PSK-AES256-GCM-SHA384",
- KeyExchange.DHEPSK,
- Authentication.PSK,
- Encryption.AES256GCM,
- MessageDigest.AEAD,
- Protocol.TLSv1_2,
- false,
- EncryptionLevel.HIGH,
- true,
- 256,
- 256,
- null,
- null
- ),
- // Cipher AC
- TLS_RSA_PSK_WITH_AES_128_GCM_SHA256(
- 0x00AC,
- "RSA-PSK-AES128-GCM-SHA256",
- KeyExchange.RSAPSK,
- Authentication.RSA,
- Encryption.AES128GCM,
- MessageDigest.AEAD,
- Protocol.TLSv1_2,
- false,
- EncryptionLevel.HIGH,
- true,
- 128,
- 128,
- null,
- null
- ),
- // Cipher AD
- TLS_RSA_PSK_WITH_AES_256_GCM_SHA384(
- 0x00AD,
- "RSA-PSK-AES256-GCM-SHA384",
- KeyExchange.RSAPSK,
- Authentication.RSA,
- Encryption.AES256GCM,
- MessageDigest.AEAD,
- Protocol.TLSv1_2,
- false,
- EncryptionLevel.HIGH,
- true,
- 256,
- 256,
- null,
- null
- ),
- // Cipher AE
- TLS_PSK_WITH_AES_128_CBC_SHA256 (
- 0x00AE,
- "PSK-AES128-CBC-SHA256",
- KeyExchange.PSK,
- Authentication.PSK,
- Encryption.AES128,
- MessageDigest.SHA256,
- Protocol.TLSv1,
- false,
- EncryptionLevel.HIGH,
- true,
- 128,
- 128,
- null,
- null
- ),
- // Cipher AF
- TLS_PSK_WITH_AES_256_CBC_SHA384 (
- 0x00AF,
- "PSK-AES256-CBC-SHA384",
- KeyExchange.PSK,
- Authentication.PSK,
- Encryption.AES256,
- MessageDigest.SHA384,
- Protocol.TLSv1,
- false,
- EncryptionLevel.HIGH,
- true,
- 256,
- 256,
- null,
- null
- ),
- // Cipher B0
- TLS_PSK_WITH_NULL_SHA256 (
- 0x00B0,
- "PSK-NULL-SHA256",
- KeyExchange.PSK,
- Authentication.PSK,
- Encryption.eNULL,
- MessageDigest.SHA256,
- Protocol.TLSv1,
- false,
- EncryptionLevel.STRONG_NONE,
- true,
- 0,
- 0,
- null,
- null
- ),
- // Cipher B1
- TLS_PSK_WITH_NULL_SHA384 (
- 0x00B1,
- "PSK-NULL-SHA384",
- KeyExchange.PSK,
- Authentication.PSK,
- Encryption.eNULL,
- MessageDigest.SHA384,
- Protocol.TLSv1,
- false,
- EncryptionLevel.STRONG_NONE,
- true,
- 0,
- 0,
- null,
- null
- ),
- // Cipher B2
- TLS_DHE_PSK_WITH_AES_128_CBC_SHA256(
- 0x00B2,
- "DHE-PSK-AES128-CBC-SHA256",
- KeyExchange.DHEPSK,
- Authentication.PSK,
- Encryption.AES128,
- MessageDigest.SHA256,
- Protocol.TLSv1,
- false,
- EncryptionLevel.HIGH,
- true,
- 128,
- 128,
- null,
- null
- ),
- // Cipher B3
- TLS_DHE_PSK_WITH_AES_256_CBC_SHA384(
- 0x00B3,
- "DHE-PSK-AES256-CBC-SHA384",
- KeyExchange.DHEPSK,
- Authentication.PSK,
- Encryption.AES256,
- MessageDigest.SHA384,
- Protocol.TLSv1,
- false,
- EncryptionLevel.HIGH,
- true,
- 256,
- 256,
- null,
- null
- ),
- // Cipher B4
- TLS_DHE_PSK_WITH_NULL_SHA256 (
- 0x00B4,
- "DHE-PSK-NULL-SHA256",
- KeyExchange.DHEPSK,
- Authentication.PSK,
- Encryption.eNULL,
- MessageDigest.SHA256,
- Protocol.TLSv1,
- false,
- EncryptionLevel.STRONG_NONE,
- true,
- 0,
- 0,
- null,
- null
- ),
- // Cipher B5
- TLS_DHE_PSK_WITH_NULL_SHA384 (
- 0x00B5,
- "DHE-PSK-NULL-SHA384",
- KeyExchange.DHEPSK,
- Authentication.PSK,
- Encryption.eNULL,
- MessageDigest.SHA384,
- Protocol.TLSv1,
- false,
- EncryptionLevel.STRONG_NONE,
- true,
- 0,
- 0,
- null,
- null
- ),
- // Cipher B6
- TLS_RSA_PSK_WITH_AES_128_CBC_SHA256(
- 0x00B6,
- "RSA-PSK-AES128-CBC-SHA256",
- KeyExchange.RSAPSK,
- Authentication.RSA,
- Encryption.AES128,
- MessageDigest.SHA256,
- Protocol.TLSv1,
- false,
- EncryptionLevel.HIGH,
- true,
- 128,
- 128,
- null,
- null
- ),
- // Cipher B7
- TLS_RSA_PSK_WITH_AES_256_CBC_SHA384(
- 0x00B7,
- "RSA-PSK-AES256-CBC-SHA384",
- KeyExchange.RSAPSK,
- Authentication.RSA,
- Encryption.AES256,
- MessageDigest.SHA384,
- Protocol.TLSv1,
- false,
- EncryptionLevel.HIGH,
- true,
- 256,
- 256,
- null,
- null
- ),
- // Cipher B8
- TLS_RSA_PSK_WITH_NULL_SHA256 (
- 0x00B8,
- "RSA-PSK-NULL-SHA256",
- KeyExchange.RSAPSK,
- Authentication.RSA,
- Encryption.eNULL,
- MessageDigest.SHA256,
- Protocol.TLSv1,
- false,
- EncryptionLevel.STRONG_NONE,
- true,
- 0,
- 0,
- null,
- null
- ),
- // Cipher B9
- TLS_RSA_PSK_WITH_NULL_SHA384 (
- 0x00B9,
- "RSA-PSK-NULL-SHA384",
- KeyExchange.RSAPSK,
- Authentication.RSA,
- Encryption.eNULL,
- MessageDigest.SHA384,
- Protocol.TLSv1,
- false,
- EncryptionLevel.STRONG_NONE,
- true,
- 0,
- 0,
- null,
- null
- ),
-
- // Cipher BA
- TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256(
- 0x00BA,
- "CAMELLIA128-SHA256",
- KeyExchange.RSA,
- Authentication.RSA,
- Encryption.CAMELLIA128,
- MessageDigest.SHA256,
- Protocol.TLSv1_2,
- false,
- EncryptionLevel.HIGH,
- false,
- 128,
- 128,
- null,
- null
- ),
- // Cipher BB
- TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256(
- 0x00BB,
- "DH-DSS-CAMELLIA128-SHA256",
- KeyExchange.DHd,
- Authentication.DH,
- Encryption.CAMELLIA128,
- MessageDigest.SHA256,
- Protocol.TLSv1_2,
- false,
- EncryptionLevel.HIGH,
- false,
- 128,
- 128,
- null,
- null
- ),
- // Cipher BC
- TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256(
- 0x00BC,
- "DH-RSA-CAMELLIA128-SHA256",
- KeyExchange.DHr,
- Authentication.DH,
- Encryption.CAMELLIA128,
- MessageDigest.SHA256,
- Protocol.TLSv1_2,
- false,
- EncryptionLevel.HIGH,
- false,
- 128,
- 128,
- null,
- null
- ),
- // Cipher BD
- TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256(
- 0x00BD,
- "DHE-DSS-CAMELLIA128-SHA256",
- KeyExchange.EDH,
- Authentication.DSS,
- Encryption.CAMELLIA128,
- MessageDigest.SHA256,
- Protocol.TLSv1_2,
- false,
- EncryptionLevel.HIGH,
- false,
- 128,
- 128,
- null,
- null
- ),
- // Cipher BE
- TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256(
- 0x00BE,
- "DHE-RSA-CAMELLIA128-SHA256",
- KeyExchange.EDH,
- Authentication.RSA,
- Encryption.CAMELLIA128,
- MessageDigest.SHA256,
- Protocol.TLSv1_2,
- false,
- EncryptionLevel.HIGH,
- false,
- 128,
- 128,
- null,
- null
- ),
- // Cipher BF
- TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA256(
- 0x00BF,
- "ADH-CAMELLIA128-SHA256",
- KeyExchange.EDH,
- Authentication.aNULL,
- Encryption.CAMELLIA128,
- MessageDigest.SHA256,
- Protocol.TLSv1_2,
- false,
- EncryptionLevel.HIGH,
- false,
- 128,
- 128,
- null,
- null
- ),
- // Cipher C0
- TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256(
- 0x00C0,
- "CAMELLIA256-SHA256",
- KeyExchange.RSA,
- Authentication.RSA,
- Encryption.CAMELLIA256,
- MessageDigest.SHA256,
- Protocol.TLSv1_2,
- false,
- EncryptionLevel.HIGH,
- false,
- 256,
- 256,
- null,
- null
- ),
- // Cipher C1
- TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256(
- 0x00C1,
- "DH-DSS-CAMELLIA256-SHA256",
- KeyExchange.DHd,
- Authentication.DH,
- Encryption.CAMELLIA256,
- MessageDigest.SHA256,
- Protocol.TLSv1_2,
- false,
- EncryptionLevel.HIGH,
- false,
- 256,
- 256,
- null,
- null
- ),
- // Cipher C2
- TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256(
- 0x00C2,
- "DH-RSA-CAMELLIA256-SHA256",
- KeyExchange.DHr,
- Authentication.DH,
- Encryption.CAMELLIA256,
- MessageDigest.SHA256,
- Protocol.TLSv1_2,
- false,
- EncryptionLevel.HIGH,
- false,
- 256,
- 256,
- null,
- null
- ),
- // Cipher C3
- TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256(
- 0x00C3,
- "DHE-DSS-CAMELLIA256-SHA256",
- KeyExchange.EDH,
- Authentication.DSS,
- Encryption.CAMELLIA256,
- MessageDigest.SHA256,
- Protocol.TLSv1_2,
- false,
- EncryptionLevel.HIGH,
- false,
- 256,
- 256,
- null,
- null
- ),
- // Cipher C4
- TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256(
- 0x00C4,
- "DHE-RSA-CAMELLIA256-SHA256",
- KeyExchange.EDH,
- Authentication.RSA,
- Encryption.CAMELLIA256,
- MessageDigest.SHA256,
- Protocol.TLSv1_2,
- false,
- EncryptionLevel.HIGH,
- false,
- 256,
- 256,
- null,
- null
- ),
- // Cipher C5
- TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA256(
- 0x00C5,
- "ADH-CAMELLIA256-SHA256",
- KeyExchange.EDH,
- Authentication.aNULL,
- Encryption.CAMELLIA256,
- MessageDigest.SHA256,
- Protocol.TLSv1_2,
- false,
- EncryptionLevel.HIGH,
- false,
- 256,
- 256,
- null,
- null
- ),
-
- /* Cipher 0x00FF TLS_EMPTY_RENEGOTIATION_INFO_SCSV
- * Cipher 0x5600 TLS_FALLBACK_SCSV
- *
- * No other ciphers defined until 0xC001 below
- */
-
- /* ECC ciphersuites from draft-ietf-tls-ecc-01.txt (
- Mar 15, 2001) */
- // Cipher C001
- TLS_ECDH_ECDSA_WITH_NULL_SHA(
- 0xC001,
- "ECDH-ECDSA-NULL-SHA",
- KeyExchange.ECDHe,
- Authentication.ECDH,
- Encryption.eNULL,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.STRONG_NONE,
- true,
- 0,
- 0,
- null,
- null
- ),
- // Cipher C002
- TLS_ECDH_ECDSA_WITH_RC4_128_SHA(
- 0xC002,
- "ECDH-ECDSA-RC4-SHA",
- KeyExchange.ECDHe,
- Authentication.ECDH,
- Encryption.RC4,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.MEDIUM,
- false,
- 128,
- 128,
- null,
- null
- ),
- // Cipher C003
- TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA(
- 0xC003,
- "ECDH-ECDSA-DES-CBC3-SHA",
- KeyExchange.ECDHe,
- Authentication.ECDH,
- Encryption.TRIPLE_DES,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.MEDIUM,
- true,
- 112,
- 168,
- null,
- null
- ),
- // Cipher C004
- TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA(
- 0xC004,
- "ECDH-ECDSA-AES128-SHA",
- KeyExchange.ECDHe,
- Authentication.ECDH,
- Encryption.AES128,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.HIGH,
- true,
- 128,
- 128,
- null,
- null
- ),
- // Cipher C005
- TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA(
- 0xC005,
- "ECDH-ECDSA-AES256-SHA",
- KeyExchange.ECDHe,
- Authentication.ECDH,
- Encryption.AES256,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.HIGH,
- true,
- 256,
- 256,
- null,
- null
- ),
- // Cipher C006
- TLS_ECDHE_ECDSA_WITH_NULL_SHA(
- 0xC006,
- "ECDHE-ECDSA-NULL-SHA",
- KeyExchange.EECDH,
- Authentication.ECDSA,
- Encryption.eNULL,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.STRONG_NONE,
- true,
- 0,
- 0,
- null,
- null
- ),
- // Cipher C007
- TLS_ECDHE_ECDSA_WITH_RC4_128_SHA(
- 0xC007,
- "ECDHE-ECDSA-RC4-SHA",
- KeyExchange.EECDH,
- Authentication.ECDSA,
- Encryption.RC4,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.MEDIUM,
- false,
- 128,
- 128,
- null,
- null
- ),
- // Cipher C008
- TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA(
- 0xC008,
- "ECDHE-ECDSA-DES-CBC3-SHA",
- KeyExchange.EECDH,
- Authentication.ECDSA,
- Encryption.TRIPLE_DES,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.MEDIUM,
- true,
- 112,
- 168,
- null,
- null
- ),
- // Cipher C009
- TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA(
- 0xC009,
- "ECDHE-ECDSA-AES128-SHA",
- KeyExchange.EECDH,
- Authentication.ECDSA,
- Encryption.AES128,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.HIGH,
- true,
- 128,
- 128,
- null,
- null
- ),
- // Cipher C00A
- TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA(
- 0xC00A,
- "ECDHE-ECDSA-AES256-SHA",
- KeyExchange.EECDH,
- Authentication.ECDSA,
- Encryption.AES256,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.HIGH,
- true,
- 256,
- 256,
- null,
- null
- ),
- // Cipher C00B
- TLS_ECDH_RSA_WITH_NULL_SHA(
- 0xC00B,
- "ECDH-RSA-NULL-SHA",
- KeyExchange.ECDHr,
- Authentication.ECDH,
- Encryption.eNULL,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.STRONG_NONE,
- true,
- 0,
- 0,
- null,
- null
- ),
- // Cipher C00C
- TLS_ECDH_RSA_WITH_RC4_128_SHA(
- 0xC00C,
- "ECDH-RSA-RC4-SHA",
- KeyExchange.ECDHr,
- Authentication.ECDH,
- Encryption.RC4,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.MEDIUM,
- false,
- 128,
- 128,
- null,
- null
- ),
- // Cipher C00D
- TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA(
- 0xC00D,
- "ECDH-RSA-DES-CBC3-SHA",
- KeyExchange.ECDHr,
- Authentication.ECDH,
- Encryption.TRIPLE_DES,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.MEDIUM,
- true,
- 112,
- 168,
- null,
- null
- ),
- // Cipher C00E
- TLS_ECDH_RSA_WITH_AES_128_CBC_SHA(
- 0xC00E,
- "ECDH-RSA-AES128-SHA",
- KeyExchange.ECDHr,
- Authentication.ECDH,
- Encryption.AES128,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.HIGH,
- true,
- 128,
- 128,
- null,
- null
- ),
- // Cipher C00F
- TLS_ECDH_RSA_WITH_AES_256_CBC_SHA(
- 0xC00F,
- "ECDH-RSA-AES256-SHA",
- KeyExchange.ECDHr,
- Authentication.ECDH,
- Encryption.AES256,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.HIGH,
- true,
- 256,
- 256,
- null,
- null
- ),
- // Cipher C010
- TLS_ECDHE_RSA_WITH_NULL_SHA(
- 0xC010,
- "ECDHE-RSA-NULL-SHA",
- KeyExchange.EECDH,
- Authentication.RSA,
- Encryption.eNULL,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.STRONG_NONE,
- true,
- 0,
- 0,
- null,
- null
- ),
- // Cipher C011
- TLS_ECDHE_RSA_WITH_RC4_128_SHA(
- 0xC011,
- "ECDHE-RSA-RC4-SHA",
- KeyExchange.EECDH,
- Authentication.RSA,
- Encryption.RC4,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.MEDIUM,
- false,
- 128,
- 128,
- null,
- null
- ),
- // Cipher C012
- TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA(
- 0xC012,
- "ECDHE-RSA-DES-CBC3-SHA",
- KeyExchange.EECDH,
- Authentication.RSA,
- Encryption.TRIPLE_DES,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.MEDIUM,
- true,
- 112,
- 168,
- null,
- null
- ),
- // Cipher C013
- TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA(
- 0xC013,
- "ECDHE-RSA-AES128-SHA",
- KeyExchange.EECDH,
- Authentication.RSA,
- Encryption.AES128,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.HIGH,
- true,
- 128,
- 128,
- null,
- null
- ),
- // Cipher C014
- TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA(
- 0xC014,
- "ECDHE-RSA-AES256-SHA",
- KeyExchange.EECDH,
- Authentication.RSA,
- Encryption.AES256,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.HIGH,
- true,
- 256,
- 256,
- null,
- null
- ),
- // Cipher C015
- TLS_ECDH_anon_WITH_NULL_SHA(
- 0xC015,
- "AECDH-NULL-SHA",
- KeyExchange.EECDH,
- Authentication.aNULL,
- Encryption.eNULL,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.STRONG_NONE,
- true,
- 0,
- 0,
- null,
- null
- ),
- // Cipher C016
- TLS_ECDH_anon_WITH_RC4_128_SHA(
- 0xC016,
- "AECDH-RC4-SHA",
- KeyExchange.EECDH,
- Authentication.aNULL,
- Encryption.RC4,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.MEDIUM,
- false,
- 128,
- 128,
- null,
- null
- ),
- // Cipher C017
- TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA(
- 0xC017,
- "AECDH-DES-CBC3-SHA",
- KeyExchange.EECDH,
- Authentication.aNULL,
- Encryption.TRIPLE_DES,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.MEDIUM,
- true,
- 112,
- 168,
- null,
- null
- ),
- // Cipher C018
- TLS_ECDH_anon_WITH_AES_128_CBC_SHA(
- 0xC018,
- "AECDH-AES128-SHA",
- KeyExchange.EECDH,
- Authentication.aNULL,
- Encryption.AES128,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.HIGH,
- true,
- 128,
- 128,
- null,
- null
- ),
- // Cipher C019
- TLS_ECDH_anon_WITH_AES_256_CBC_SHA(
- 0xC019,
- "AECDH-AES256-SHA",
- KeyExchange.EECDH,
- Authentication.aNULL,
- Encryption.AES256,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.HIGH,
- true,
- 256,
- 256,
- null,
- null
- ),
- /* SRP ciphersuite from RFC 5054 */
- // Cipher C01A
- TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA(
- 0xC01A,
- "SRP-3DES-EDE-CBC-SHA",
- KeyExchange.SRP,
- Authentication.SRP,
- Encryption.TRIPLE_DES,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.MEDIUM,
- false,
- 112,
- 168,
- null,
- null
- ),
- // Cipher C01B
- TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA(
- 0xC01B,
- "SRP-RSA-3DES-EDE-CBC-SHA",
- KeyExchange.SRP,
- Authentication.RSA,
- Encryption.TRIPLE_DES,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.MEDIUM,
- false,
- 112,
- 168,
- null,
- null
- ),
- // Cipher C01C
- TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA(
- 0xC01C,
- "SRP-DSS-3DES-EDE-CBC-SHA",
- KeyExchange.SRP,
- Authentication.DSS,
- Encryption.TRIPLE_DES,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.MEDIUM,
- false,
- 112,
- 168,
- null,
- null
- ),
- // Cipher C01D
- TLS_SRP_SHA_WITH_AES_128_CBC_SHA(
- 0xC01D,
- "SRP-AES-128-CBC-SHA",
- KeyExchange.SRP,
- Authentication.SRP,
- Encryption.AES128,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.HIGH,
- false,
- 128,
- 128,
- null,
- null
- ),
- // Cipher C01E
- TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA(
- 0xC01E,
- "SRP-RSA-AES-128-CBC-SHA",
- KeyExchange.SRP,
- Authentication.RSA,
- Encryption.AES128,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.HIGH,
- false,
- 128,
- 128,
- null,
- null
- ),
- // Cipher C01F
- TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA(
- 0xC01F,
- "SRP-DSS-AES-128-CBC-SHA",
- KeyExchange.SRP,
- Authentication.DSS,
- Encryption.AES128,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.HIGH,
- false,
- 128,
- 128,
- null,
- null
- ),
- // Cipher C020
- TLS_SRP_SHA_WITH_AES_256_CBC_SHA(
- 0xC020,
- "SRP-AES-256-CBC-SHA",
- KeyExchange.SRP,
- Authentication.SRP,
- Encryption.AES256,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.HIGH,
- false,
- 256,
- 256,
- null,
- null
- ),
- // Cipher C021
- TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA(
- 0xC021,
- "SRP-RSA-AES-256-CBC-SHA",
- KeyExchange.SRP,
- Authentication.RSA,
- Encryption.AES256,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.HIGH,
- false,
- 256,
- 256,
- null,
- null
- ),
- // Cipher C022
- TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA(
- 0xC022,
- "SRP-DSS-AES-256-CBC-SHA",
- KeyExchange.SRP,
- Authentication.DSS,
- Encryption.AES256,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.HIGH,
- false,
- 256,
- 256,
- null,
- null
- ),
- /* HMAC based TLS v1.2 ciphersuites from RFC5289 */
- // Cipher C023
- TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256(
- 0xC023,
- "ECDHE-ECDSA-AES128-SHA256",
- KeyExchange.EECDH,
- Authentication.ECDSA,
- Encryption.AES128,
- MessageDigest.SHA256,
- Protocol.TLSv1_2,
- false,
- EncryptionLevel.HIGH,
- true,
- 128,
- 128,
- null,
- null
- ),
- // Cipher C024
- TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384(
- 0xC024,
- "ECDHE-ECDSA-AES256-SHA384",
- KeyExchange.EECDH,
- Authentication.ECDSA,
- Encryption.AES256,
- MessageDigest.SHA384,
- Protocol.TLSv1_2,
- false,
- EncryptionLevel.HIGH,
- true,
- 256,
- 256,
- null,
- null
- ),
- // Cipher C025
- TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256(
- 0xC025,
- "ECDH-ECDSA-AES128-SHA256",
- KeyExchange.ECDHe,
- Authentication.ECDH,
- Encryption.AES128,
- MessageDigest.SHA256,
- Protocol.TLSv1_2,
- false,
- EncryptionLevel.HIGH,
- true,
- 128,
- 128,
- null,
- null
- ),
- // Cipher C026
- TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384(
- 0xC026,
- "ECDH-ECDSA-AES256-SHA384",
- KeyExchange.ECDHe,
- Authentication.ECDH,
- Encryption.AES256,
- MessageDigest.SHA384,
- Protocol.TLSv1_2,
- false,
- EncryptionLevel.HIGH,
- true,
- 256,
- 256,
- null,
- null
- ),
- // Cipher C027
- TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256(
- 0xC027,
- "ECDHE-RSA-AES128-SHA256",
- KeyExchange.EECDH,
- Authentication.RSA,
- Encryption.AES128,
- MessageDigest.SHA256,
- Protocol.TLSv1_2,
- false,
- EncryptionLevel.HIGH,
- true,
- 128,
- 128,
- null,
- null
- ),
- // Cipher C028
- TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384(
- 0xC028,
- "ECDHE-RSA-AES256-SHA384",
- KeyExchange.EECDH,
- Authentication.RSA,
- Encryption.AES256,
- MessageDigest.SHA384,
- Protocol.TLSv1_2,
- false,
- EncryptionLevel.HIGH,
- true,
- 256,
- 256,
- null,
- null
- ),
- // Cipher C029
- TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256(
- 0xC029,
- "ECDH-RSA-AES128-SHA256",
- KeyExchange.ECDHr,
- Authentication.ECDH,
- Encryption.AES128,
- MessageDigest.SHA256,
- Protocol.TLSv1_2,
- false,
- EncryptionLevel.HIGH,
- true,
- 128,
- 128,
- null,
- null
- ),
- // Cipher C02A
- TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384(
- 0xC02A,
- "ECDH-RSA-AES256-SHA384",
- KeyExchange.ECDHr,
- Authentication.ECDH,
- Encryption.AES256,
- MessageDigest.SHA384,
- Protocol.TLSv1_2,
- false,
- EncryptionLevel.HIGH,
- true,
- 256,
- 256,
- null,
- null
- ),
- /* GCM based TLS v1.2 ciphersuites from RFC5289 */
- // Cipher C02B
- TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256(
- 0xC02B,
- "ECDHE-ECDSA-AES128-GCM-SHA256",
- KeyExchange.EECDH,
- Authentication.ECDSA,
- Encryption.AES128GCM,
- MessageDigest.AEAD,
- Protocol.TLSv1_2,
- false,
- EncryptionLevel.HIGH,
- true,
- 128,
- 128,
- null,
- null
- ),
- // Cipher C02C
- TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384(
- 0xC02C,
- "ECDHE-ECDSA-AES256-GCM-SHA384",
- KeyExchange.EECDH,
- Authentication.ECDSA,
- Encryption.AES256GCM,
- MessageDigest.AEAD,
- Protocol.TLSv1_2,
- false,
- EncryptionLevel.HIGH,
- true,
- 256,
- 256,
- null,
- null
- ),
- // Cipher C02D
- TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256(
- 0xC02D,
- "ECDH-ECDSA-AES128-GCM-SHA256",
- KeyExchange.ECDHe,
- Authentication.ECDH,
- Encryption.AES128GCM,
- MessageDigest.AEAD,
- Protocol.TLSv1_2,
- false,
- EncryptionLevel.HIGH,
- true,
- 128,
- 128,
- null,
- null
- ),
- // Cipher C02E
- TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384(
- 0xC02E,
- "ECDH-ECDSA-AES256-GCM-SHA384",
- KeyExchange.ECDHe,
- Authentication.ECDH,
- Encryption.AES256GCM,
- MessageDigest.AEAD,
- Protocol.TLSv1_2,
- false,
- EncryptionLevel.HIGH,
- true,
- 256,
- 256,
- null,
- null
- ),
- // Cipher C02F
- TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256(
- 0xC02F,
- "ECDHE-RSA-AES128-GCM-SHA256",
- KeyExchange.EECDH,
- Authentication.RSA,
- Encryption.AES128GCM,
- MessageDigest.AEAD,
- Protocol.TLSv1_2,
- false,
- EncryptionLevel.HIGH,
- true,
- 128,
- 128,
- null,
- null
- ),
- // Cipher C030
- TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384(
- 0xC030,
- "ECDHE-RSA-AES256-GCM-SHA384",
- KeyExchange.EECDH,
- Authentication.RSA,
- Encryption.AES256GCM,
- MessageDigest.AEAD,
- Protocol.TLSv1_2,
- false,
- EncryptionLevel.HIGH,
- true,
- 256,
- 256,
- null,
- null
- ),
- // Cipher C031
- TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256(
- 0xC031,
- "ECDH-RSA-AES128-GCM-SHA256",
- KeyExchange.ECDHr,
- Authentication.ECDH,
- Encryption.AES128GCM,
- MessageDigest.AEAD,
- Protocol.TLSv1_2,
- false,
- EncryptionLevel.HIGH,
- true,
- 128,
- 128,
- null,
- null
- ),
- // Cipher C032
- TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384(
- 0xC032,
- "ECDH-RSA-AES256-GCM-SHA384",
- KeyExchange.ECDHr,
- Authentication.ECDH,
- Encryption.AES256GCM,
- MessageDigest.AEAD,
- Protocol.TLSv1_2,
- false,
- EncryptionLevel.HIGH,
- true,
- 256,
- 256,
- null,
- null
- ),
- // Cipher C033
- TLS_ECDHE_PSK_WITH_RC4_128_SHA(
- 0xC033,
- "ECDHE-PSK-RC4-SHA",
- KeyExchange.ECDHEPSK,
- Authentication.PSK,
- Encryption.RC4,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.MEDIUM,
- false,
- 128,
- 128,
- null,
- null
- ),
- // Cipher C034
- TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA(
- 0xC034,
- "ECDHE-PSK-3DES-EDE-CBC-SHA",
- KeyExchange.ECDHEPSK,
- Authentication.PSK,
- Encryption.TRIPLE_DES,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.MEDIUM,
- true,
- 112,
- 168,
- null,
- null
- ),
- // Cipher C035
- TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA(
- 0xC035,
- "ECDHE-PSK-AES128-CBC-SHA",
- KeyExchange.ECDHEPSK,
- Authentication.PSK,
- Encryption.AES128,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.HIGH,
- true,
- 128,
- 128,
- null,
- null
- ),
- // Cipher C036
- TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA(
- 0xC036,
- "ECDHE-PSK-AES256-CBC-SHA",
- KeyExchange.ECDHEPSK,
- Authentication.PSK,
- Encryption.AES256,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.HIGH,
- true,
- 256,
- 256,
- null,
- null
- ),
-
- TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256(
- 0xC037,
- "ECDHE-PSK-AES128-CBC-SHA256",
- KeyExchange.ECDHEPSK,
- Authentication.PSK,
- Encryption.AES128,
- MessageDigest.SHA256,
- Protocol.TLSv1,
- false,
- EncryptionLevel.HIGH,
- true,
- 128,
- 128,
- null,
- null
- ),
- TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384(
- 0xC038,
- "ECDHE-PSK-AES256-CBC-SHA384",
- KeyExchange.ECDHEPSK,
- Authentication.PSK,
- Encryption.AES256,
- MessageDigest.SHA384,
- Protocol.TLSv1,
- false,
- EncryptionLevel.HIGH,
- true,
- 256,
- 256,
- null,
- null
- ),
- TLS_ECDHE_PSK_WITH_NULL_SHA(
- 0xC039,
- "ECDHE-PSK-NULL-SHA",
- KeyExchange.ECDHEPSK,
- Authentication.PSK,
- Encryption.eNULL,
- MessageDigest.SHA1,
- Protocol.SSLv3,
- false,
- EncryptionLevel.STRONG_NONE,
- true,
- 0,
- 0,
- null,
- null
- ),
- TLS_ECDHE_PSK_WITH_NULL_SHA256(
- 0xC03A,
- "ECDHE-PSK-NULL-SHA256",
- KeyExchange.ECDHEPSK,
- Authentication.PSK,
- Encryption.eNULL,
- MessageDigest.SHA256,
- Protocol.TLSv1,
- false,
- EncryptionLevel.STRONG_NONE,
- true,
- 0,
- 0,
- null,
- null
- ),
- TLS_ECDHE_PSK_WITH_NULL_SHA384(
- 0xC03B,
- "ECDHE-PSK-NULL-SHA384",
- KeyExchange.ECDHEPSK,
- Authentication.PSK,
- Encryption.eNULL,
- MessageDigest.SHA384,
- Protocol.TLSv1,
- false,
- EncryptionLevel.STRONG_NONE,
- true,
- 0,
- 0,
- null,
- null
- ),
-
- /* ARIA ciphers 0xC03C to 0xC071
- * Unsupported by both Java and OpenSSL
- */
- // Cipher C072
- TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256(
- 0xC072,
- "ECDHE-ECDSA-CAMELLIA128-SHA256",
- KeyExchange.EECDH,
- Authentication.ECDSA,
- Encryption.CAMELLIA128,
- MessageDigest.SHA256,
- Protocol.TLSv1_2,
- false,
- EncryptionLevel.HIGH,
- true,
- 128,
- 128,
- null,
- null
- ),
- // Cipher C073
- TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384(
- 0xC073,
- "ECDHE-ECDSA-CAMELLIA256-SHA384",
- KeyExchange.EECDH,
- Authentication.ECDSA,
- Encryption.CAMELLIA256,
- MessageDigest.SHA384,
- Protocol.TLSv1_2,
- false,
- EncryptionLevel.HIGH,
- true,
- 256,
- 256,
- null,
- null
- ),
- // Cipher C074
- TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256(
- 0xC074,
- "ECDH-ECDSA-CAMELLIA128-SHA256",
- KeyExchange.ECDHe,
- Authentication.ECDH,
- Encryption.CAMELLIA128,
- MessageDigest.SHA256,
- Protocol.TLSv1_2,
- false,
- EncryptionLevel.HIGH,
- true,
- 128,
- 128,
- null,
- null
- ),
- // Cipher C075
- TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384(
- 0xC075,
- "ECDH-ECDSA-CAMELLIA256-SHA384",
- KeyExchange.ECDHe,
- Authentication.ECDH,
- Encryption.CAMELLIA256,
- MessageDigest.SHA384,
- Protocol.TLSv1_2,
- false,
- EncryptionLevel.HIGH,
- true,
- 256,
- 256,
- null,
- null
- ),
- // Cipher C076
- TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256(
- 0xC076,
- "ECDHE-RSA-CAMELLIA128-SHA256",
- KeyExchange.EECDH,
- Authentication.RSA,
- Encryption.CAMELLIA128,
- MessageDigest.SHA256,
- Protocol.TLSv1_2,
- false,
- EncryptionLevel.HIGH,
- true,
- 128,
- 128,
- null,
- null
- ),
- // Cipher C077
- TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384(
- 0xC077,
- "ECDHE-RSA-CAMELLIA256-SHA384",
- KeyExchange.EECDH,
- Authentication.RSA,
- Encryption.CAMELLIA256,
- MessageDigest.SHA384,
- Protocol.TLSv1_2,
- false,
- EncryptionLevel.HIGH,
- true,
- 256,
- 256,
- null,
- null
- ),
- // Cipher C078
- TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256(
- 0xC078,
- "ECDH-RSA-CAMELLIA128-SHA256",
- KeyExchange.ECDHr,
- Authentication.ECDH,
- Encryption.CAMELLIA128,
- MessageDigest.SHA256,
- Protocol.TLSv1_2,
- false,
- EncryptionLevel.HIGH,
- true,
- 128,
- 128,
- null,
- null
- ),
- // Cipher C079
- TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384(
- 0xC079,
- "ECDH-RSA-CAMELLIA256-SHA384",
- KeyExchange.ECDHr,
- Authentication.ECDH,
- Encryption.CAMELLIA256,
- MessageDigest.SHA384,
- Protocol.TLSv1_2,
- false,
- EncryptionLevel.HIGH,
- true,
- 256,
- 256,
- null,
- null
- ),
-
- // Cipher C094
- TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256(
- 0xC094,
- "PSK-CAMELLIA128-SHA256",
- KeyExchange.PSK,
- Authentication.PSK,
- Encryption.CAMELLIA128,
- MessageDigest.SHA256,
- Protocol.TLSv1,
- false,
- EncryptionLevel.HIGH,
- false,
- 128,
- 128,
- null,
- null
- ),
- // Cipher C095
- TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384(
- 0xC095,
- "PSK-CAMELLIA256-SHA384",
- KeyExchange.PSK,
- Authentication.PSK,
- Encryption.CAMELLIA256,
- MessageDigest.SHA384,
- Protocol.TLSv1,
- false,
- EncryptionLevel.HIGH,
- false,
- 256,
- 256,
- null,
- null
- ),
- // Cipher C096
- TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256(
- 0xC096,
- "DHE-PSK-CAMELLIA128-SHA256",
- KeyExchange.DHEPSK,
- Authentication.PSK,
- Encryption.CAMELLIA128,
- MessageDigest.SHA256,
- Protocol.TLSv1,
- false,
- EncryptionLevel.HIGH,
- false,
- 128,
- 128,
- null,
- null
- ),
- // Cipher C097
- TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384(
- 0xC097,
- "DHE-PSK-CAMELLIA256-SHA384",
- KeyExchange.DHEPSK,
- Authentication.PSK,
- Encryption.CAMELLIA256,
- MessageDigest.SHA384,
- Protocol.TLSv1,
- false,
- EncryptionLevel.HIGH,
- false,
- 256,
- 256,
- null,
- null
- ),
- // Cipher C098
- TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256(
- 0xC098,
- "RSA-PSK-CAMELLIA128-SHA256",
- KeyExchange.RSAPSK,
- Authentication.RSA,
- Encryption.CAMELLIA128,
- MessageDigest.SHA256,
- Protocol.TLSv1,
- false,
- EncryptionLevel.HIGH,
- false,
- 128,
- 128,
- null,
- null
- ),
- // Cipher C099
- TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384(
- 0xC099,
- "RSA-PSK-CAMELLIA256-SHA384",
- KeyExchange.RSAPSK,
- Authentication.RSA,
- Encryption.CAMELLIA256,
- MessageDigest.SHA384,
- Protocol.TLSv1,
- false,
- EncryptionLevel.HIGH,
- false,
- 256,
- 256,
- null,
- null
- ),
- // Cipher C09A
- TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256(
- 0xC09A,
- "ECDHE-PSK-CAMELLIA128-SHA256",
- KeyExchange.ECDHEPSK,
- Authentication.PSK,
- Encryption.CAMELLIA128,
- MessageDigest.SHA256,
- Protocol.TLSv1,
- false,
- EncryptionLevel.HIGH,
- false,
- 128,
- 128,
- null,
- null
- ),
- // Cipher C09B
- TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384(
- 0xC09B,
- "ECDHE-PSK-CAMELLIA256-SHA384",
- KeyExchange.ECDHEPSK,
- Authentication.PSK,
- Encryption.CAMELLIA256,
- MessageDigest.SHA384,
- Protocol.TLSv1,
- false,
- EncryptionLevel.HIGH,
- false,
- 256,
- 256,
- null,
- null
- ),
- // CCM ciphersuites from RFC6655
- // Cipher C09C
- TLS_RSA_WITH_AES_128_CCM(
- 0xC09C,
- "AES128-CCM",
- KeyExchange.RSA,
- Authentication.RSA,
- Encryption.AES128CCM,
- MessageDigest.AEAD,
- Protocol.TLSv1_2,
- false,
- EncryptionLevel.HIGH,
- false,
- 128,
- 128,
- null,
- null
- ),
- // Cipher C09D
- TLS_RSA_WITH_AES_256_CCM(
- 0xC09D,
- "AES256-CCM",
- KeyExchange.RSA,
- Authentication.RSA,
- Encryption.AES256CCM,
- MessageDigest.AEAD,
- Protocol.TLSv1_2,
- false,
- EncryptionLevel.HIGH,
- false,
- 256,
- 256,
- null,
- null
- ),
- // Cipher C09E
- TLS_DHE_RSA_WITH_AES_128_CCM(
- 0xC09E,
- "DHE-RSA-AES128-CCM",
- KeyExchange.EDH,
- Authentication.RSA,
- Encryption.AES128CCM,
- MessageDigest.AEAD,
- Protocol.TLSv1_2,
- false,
- EncryptionLevel.HIGH,
- false,
- 128,
- 128,
- null,
- null
- ),
- // Cipher C09F
- TLS_DHE_RSA_WITH_AES_256_CCM(
- 0xC09F,
- "DHE-RSA-AES256-CCM",
- KeyExchange.EDH,
- Authentication.RSA,
- Encryption.AES256CCM,
- MessageDigest.AEAD,
- Protocol.TLSv1_2,
- false,
- EncryptionLevel.HIGH,
- false,
- 256,
- 256,
- null,
- null
- ),
- // Cipher C0A0
- TLS_RSA_WITH_AES_128_CCM_8(
- 0xC0A0,
- "AES128-CCM8",
- KeyExchange.RSA,
- Authentication.RSA,
- Encryption.AES128CCM8,
- MessageDigest.AEAD,
- Protocol.TLSv1_2,
- false,
- EncryptionLevel.HIGH,
- false,
- 128,
- 128,
- null,
- null
- ),
- // Cipher C0A1
- TLS_RSA_WITH_AES_256_CCM_8(
- 0xC0A1,
- "AES256-CCM8",
- KeyExchange.RSA,
- Authentication.RSA,
- Encryption.AES256CCM8,
- MessageDigest.AEAD,
- Protocol.TLSv1_2,
- false,
- EncryptionLevel.HIGH,
- false,
- 256,
- 256,
- null,
- null
- ),
- // Cipher C0A2
- TLS_DHE_RSA_WITH_AES_128_CCM_8(
- 0xC0A2,
- "DHE-RSA-AES128-CCM8",
- KeyExchange.EDH,
- Authentication.RSA,
- Encryption.AES128CCM8,
- MessageDigest.AEAD,
- Protocol.TLSv1_2,
- false,
- EncryptionLevel.HIGH,
- false,
- 128,
- 128,
- null,
- null
- ),
- // Cipher C0A3
- TLS_DHE_RSA_WITH_AES_256_CCM_8(
- 0xC0A3,
- "DHE-RSA-AES256-CCM8",
- KeyExchange.EDH,
- Authentication.RSA,
- Encryption.AES256CCM8,
- MessageDigest.AEAD,
- Protocol.TLSv1_2,
- false,
- EncryptionLevel.HIGH,
- false,
- 256,
- 256,
- null,
- null
- ),
- // Cipher C0A4
- TLS_PSK_WITH_AES_128_CCM(
- 0xC0A4,
- "PSK-AES128-CCM",
- KeyExchange.PSK,
- Authentication.PSK,
- Encryption.AES128CCM,
- MessageDigest.AEAD,
- Protocol.TLSv1_2,
- false,
- EncryptionLevel.HIGH,
- false,
- 128,
- 128,
- null,
- null
- ),
- // Cipher C0A5
- TLS_PSK_WITH_AES_256_CCM(
- 0xC0A5,
- "PSK-AES256-CCM",
- KeyExchange.PSK,
- Authentication.PSK,
- Encryption.AES256CCM,
- MessageDigest.AEAD,
- Protocol.TLSv1_2,
- false,
- EncryptionLevel.HIGH,
- false,
- 256,
- 256,
- null,
- null
- ),
- // Cipher C0A6
- TLS_DHE_PSK_WITH_AES_128_CCM(
- 0xC0A6,
- "DHE-PSK-AES128-CCM",
- KeyExchange.DHEPSK,
- Authentication.PSK,
- Encryption.AES128CCM,
- MessageDigest.AEAD,
- Protocol.TLSv1_2,
- false,
- EncryptionLevel.HIGH,
- false,
- 128,
- 128,
- null,
- null
- ),
- // Cipher C0A7
- TLS_DHE_PSK_WITH_AES_256_CCM(
- 0xC0A7,
- "DHE-PSK-AES256-CCM",
- KeyExchange.DHEPSK,
- Authentication.PSK,
- Encryption.AES256CCM,
- MessageDigest.AEAD,
- Protocol.TLSv1_2,
- false,
- EncryptionLevel.HIGH,
- false,
- 256,
- 256,
- null,
- null
- ),
- // Cipher C0A8
- TLS_PSK_WITH_AES_128_CCM_8(
- 0xC0A8,
- "PSK-AES128-CCM8",
- KeyExchange.PSK,
- Authentication.PSK,
- Encryption.AES128CCM8,
- MessageDigest.AEAD,
- Protocol.TLSv1_2,
- false,
- EncryptionLevel.HIGH,
- false,
- 128,
- 128,
- null,
- null
- ),
- // Cipher C0A9
- TLS_PSK_WITH_AES_256_CCM_8(
- 0xC0A9,
- "PSK-AES256-CCM8",
- KeyExchange.PSK,
- Authentication.PSK,
- Encryption.AES256CCM8,
- MessageDigest.AEAD,
- Protocol.TLSv1_2,
- false,
- EncryptionLevel.HIGH,
- false,
- 256,
- 256,
- null,
- null
- ),
- // Cipher C0AA
- TLS_PSK_DHE_WITH_AES_128_CCM_8(
- 0xC0AA,
- "DHE-PSK-AES128-CCM8",
- KeyExchange.DHEPSK,
- Authentication.PSK,
- Encryption.AES128CCM8,
- MessageDigest.AEAD,
- Protocol.TLSv1_2,
- false,
- EncryptionLevel.HIGH,
- false,
- 128,
- 128,
- null,
- null
- ),
- // Cipher C0AB
- TLS_PSK_DHE_WITH_AES_256_CCM_8(
- 0xC0AB,
- "DHE-PSK-AES256-CCM8",
- KeyExchange.DHEPSK,
- Authentication.PSK,
- Encryption.AES256CCM8,
- MessageDigest.AEAD,
- Protocol.TLSv1_2,
- false,
- EncryptionLevel.HIGH,
- false,
- 256,
- 256,
- null,
- null
- ),
- // CCM ciphersuites from RFC7251
- // Cipher C0AC
- TLS_ECDHE_ECDSA_WITH_AES_128_CCM(
- 0xC0AC,
- "ECDHE-ECDSA-AES128-CCM",
- KeyExchange.EECDH,
- Authentication.ECDSA,
- Encryption.AES128CCM,
- MessageDigest.AEAD,
- Protocol.TLSv1_2,
- false,
- EncryptionLevel.HIGH,
- false,
- 128,
- 128,
- null,
- null
- ),
- // Cipher C0AD
- TLS_ECDHE_ECDSA_WITH_AES_256_CCM(
- 0xC0AD,
- "ECDHE-ECDSA-AES256-CCM",
- KeyExchange.EECDH,
- Authentication.ECDSA,
- Encryption.AES256CCM,
- MessageDigest.AEAD,
- Protocol.TLSv1_2,
- false,
- EncryptionLevel.HIGH,
- false,
- 256,
- 256,
- null,
- null
- ),
- // Cipher C0AE
- TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8(
- 0xC0AE,
- "ECDHE-ECDSA-AES128-CCM8",
- KeyExchange.EECDH,
- Authentication.ECDSA,
- Encryption.AES128CCM8,
- MessageDigest.AEAD,
- Protocol.TLSv1_2,
- false,
- EncryptionLevel.HIGH,
- false,
- 128,
- 128,
- null,
- null
- ),
- // Cipher C0AF
- TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8(
- 0xC0AF,
- "ECDHE-ECDSA-AES256-CCM8",
- KeyExchange.EECDH,
- Authentication.ECDSA,
- Encryption.AES256CCM8,
- MessageDigest.AEAD,
- Protocol.TLSv1_2,
- false,
- EncryptionLevel.HIGH,
- false,
- 256,
- 256,
- null,
- null
- ),
- // Draft: https://tools.ietf.org/html/draft-ietf-tls-chacha20-poly1305-04
- TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256(
- 0xCCA8,
- "ECDHE-RSA-CHACHA20-POLY1305",
- KeyExchange.EECDH,
- Authentication.RSA,
- Encryption.CHACHA20POLY1305,
- MessageDigest.AEAD,
- Protocol.TLSv1_2,
- false,
- EncryptionLevel.HIGH,
- false,
- 256,
- 256,
- null,
- null
- ),
- TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256(
- 0xCCA9,
- "ECDHE-ECDSA-CHACHA20-POLY1305",
- KeyExchange.EECDH,
- Authentication.ECDSA,
- Encryption.CHACHA20POLY1305,
- MessageDigest.AEAD,
- Protocol.TLSv1_2,
- false,
- EncryptionLevel.HIGH,
- false,
- 256,
- 256,
- null,
- null
- ),
- TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256(
- 0xCCAA,
- "DHE-RSA-CHACHA20-POLY1305",
- KeyExchange.EDH,
- Authentication.RSA,
- Encryption.CHACHA20POLY1305,
- MessageDigest.AEAD,
- Protocol.TLSv1_2,
- false,
- EncryptionLevel.HIGH,
- false,
- 256,
- 256,
- null,
- null
- ),
- TLS_PSK_WITH_CHACHA20_POLY1305_SHA256(
- 0xCCAB,
- "PSK-CHACHA20-POLY1305",
- KeyExchange.PSK,
- Authentication.PSK,
- Encryption.CHACHA20POLY1305,
- MessageDigest.AEAD,
- Protocol.TLSv1_2,
- false,
- EncryptionLevel.HIGH,
- false,
- 256,
- 256,
- null,
- null
- ),
- TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256(
- 0xCCAC,
- "ECDHE-PSK-CHACHA20-POLY1305",
- KeyExchange.ECDHEPSK,
- Authentication.PSK,
- Encryption.CHACHA20POLY1305,
- MessageDigest.AEAD,
- Protocol.TLSv1_2,
- false,
- EncryptionLevel.HIGH,
- false,
- 256,
- 256,
- null,
- null
- ),
- TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256(
- 0xCCAD,
- "DHE-PSK-CHACHA20-POLY1305",
- KeyExchange.DHEPSK,
- Authentication.PSK,
- Encryption.CHACHA20POLY1305,
- MessageDigest.AEAD,
- Protocol.TLSv1_2,
- false,
- EncryptionLevel.HIGH,
- false,
- 256,
- 256,
- null,
- null
- ),
- TLS_RSA_PSK_WITH_CHACHA20_POLY1305_SHA256(
- 0xCCAE,
- "RSA-PSK-CHACHA20-POLY1305",
- KeyExchange.RSAPSK,
- Authentication.RSA,
- Encryption.CHACHA20POLY1305,
- MessageDigest.AEAD,
- Protocol.TLSv1_2,
- false,
- EncryptionLevel.HIGH,
- false,
- 256,
- 256,
- null,
- null
- ),
-
- // Cipher 0x010080 (SSLv2)
- // RC4_128_WITH_MD5
- SSL_CK_RC4_128_WITH_MD5(
- -1,
- "RC4-MD5",
- KeyExchange.RSA,
- Authentication.RSA,
- Encryption.RC4,
- MessageDigest.MD5,
- Protocol.SSLv2,
- false,
- EncryptionLevel.MEDIUM,
- false,
- 128,
- 128,
- null,
- null
- ),
- // Cipher 0x020080 (SSLv2)
- SSL2_RC4_128_EXPORT40_WITH_MD5(
- -1,
- "EXP-RC4-MD5",
- KeyExchange.RSA,
- Authentication.RSA,
- Encryption.RC4,
- MessageDigest.MD5,
- Protocol.SSLv2,
- true,
- EncryptionLevel.EXP40,
- false,
- 40,
- 128,
- new String[] {"SSL_RC4_128_EXPORT40_WITH_MD5"},
- null
- ),
- // Cipher 0x030080 (SSLv2)
- // RC2_128_CBC_WITH_MD5
- SSL_CK_RC2_128_CBC_WITH_MD5(
- -1,
- "RC2-CBC-MD5",
- KeyExchange.RSA,
- Authentication.RSA,
- Encryption.RC2,
- MessageDigest.MD5,
- Protocol.SSLv2,
- false,
- EncryptionLevel.MEDIUM,
- false,
- 128,
- 128,
- null,
- null
- ),
- // Cipher 0x040080 (SSLv2)
- // RC2_128_CBC_EXPORT40_WITH_MD5
- SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5(
- -1,
- "EXP-RC2-CBC-MD5",
- KeyExchange.RSA,
- Authentication.RSA,
- Encryption.RC2,
- MessageDigest.MD5,
- Protocol.SSLv2,
- true,
- EncryptionLevel.EXP40,
- false,
- 40,
- 128,
- null,
- null
- ),
- // Cipher 0x050080 (SSLv2)
- // IDEA_128_CBC_WITH_MD5
- SSL2_IDEA_128_CBC_WITH_MD5(
- -1,
- "IDEA-CBC-MD5",
- KeyExchange.RSA,
- Authentication.RSA,
- Encryption.IDEA,
- MessageDigest.MD5,
- Protocol.SSLv2,
- false, EncryptionLevel.MEDIUM,
- false,
- 128,
- 128,
- new String[] {"SSL_CK_IDEA_128_CBC_WITH_MD5"},
- null
- ),
- // Cipher 0x060040 (SSLv2)
- // DES_64_CBC_WITH_MD5
- SSL2_DES_64_CBC_WITH_MD5(
- -1,
- "DES-CBC-MD5",
- KeyExchange.RSA,
- Authentication.RSA,
- Encryption.DES,
- MessageDigest.MD5,
- Protocol.SSLv2,
- false,
- EncryptionLevel.LOW,
- false,
- 56,
- 56,
- new String[] {"SSL_CK_DES_64_CBC_WITH_MD5"},
- null
- ),
- // Cipher 0x0700C0 (SSLv2)
- // DES_192_EDE3_CBC_WITH_MD5
- SSL2_DES_192_EDE3_CBC_WITH_MD5(
- -1,
- "DES-CBC3-MD5",
- KeyExchange.RSA,
- Authentication.RSA,
- Encryption.TRIPLE_DES,
- MessageDigest.MD5,
- Protocol.SSLv2,
- false,
- EncryptionLevel.MEDIUM,
- false,
- 112,
- 168,
- new String[] {"SSL_CK_DES_192_EDE3_CBC_WITH_MD5"},
- null
- );
-
- /* TEMP_GOST_TLS*/
- /*
- // Cipher FF00
- TLS_GOSTR341094_RSA_WITH_28147_CNT_MD5(
- "GOST-MD5",
- KeyExchange.RSA,
- Authentication.RSA,
- Encryption.eGOST2814789CNT,
- MessageDigest.MD5,
- Protocol.TLSv1,
- false,
- EncryptionLevel.HIGH,
- false,
- 256,
- 256
- ),
- TLS_RSA_WITH_28147_CNT_GOST94(
- "GOST-GOST94",
- KeyExchange.RSA,
- Authentication.RSA,
- Encryption.eGOST2814789CNT,
- MessageDigest.GOST94,
- Protocol.TLSv1,
- false, EncryptionLevel.HIGH,false,
- 256,
- 256
- ),
- {
- 1,
- "GOST-GOST89MAC",
- 0x0300ff02,
- KeyExchange.RSA,
- Authentication.RSA,
- Encryption.eGOST2814789CNT,
- MessageDigest.GOST89MAC,
- Protocol.TLSv1,
- false, EncryptionLevel.HIGH,false,
-
- 256,
- 256
- ),
- {
- 1,
- "GOST-GOST89STREAM",
- 0x0300ff03,
- KeyExchange.RSA,
- Authentication.RSA,
- Encryption.eGOST2814789CNT,
- MessageDigest.GOST89MAC,
- Protocol.TLSv1,
- false, EncryptionLevel.HIGH,false,
- 256,
- 256
- },*/
-
-
- private final int id;
- private final String openSSLAlias;
- private final Set<String> openSSLAltNames;
- private final Set<String> jsseNames;
- private final KeyExchange kx;
- private final Authentication au;
- private final Encryption enc;
- private final MessageDigest mac;
- private final Protocol protocol;
- private final boolean export;
- private final EncryptionLevel level;
- private final boolean fipsCompatible;
- /**
- * Number of bits really used
- */
- private final int strength_bits;
- /**
- * Number of bits for algorithm
- */
- private final int alg_bits;
-
- private 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) {
- Set<String> altNames = new HashSet<>();
- altNames.addAll(Arrays.asList(openSSlAltNames));
- this.openSSLAltNames = Collections.unmodifiableSet(altNames);
- } else {
- this.openSSLAltNames = Collections.emptySet();
- }
- Set<String> jsseNames = new LinkedHashSet<>();
- if (jsseAltNames != null && jsseAltNames.length != 0) {
- jsseNames.addAll(Arrays.asList(jsseAltNames));
- }
- jsseNames.add(name());
- this.jsseNames = Collections.unmodifiableSet(jsseNames);
- this.kx = kx;
- this.au = au;
- this.enc = enc;
- this.mac = mac;
- this.protocol = protocol;
- this.export = export;
- this.level = level;
- this.fipsCompatible = fipsCompatible;
- this.strength_bits = strength_bits;
- this.alg_bits = alg_bits;
- }
-
- public int getId() {
- return id;
- }
-
- public String getOpenSSLAlias() {
- return openSSLAlias;
- }
-
- public Set<String> getOpenSSLAltNames() {
- return openSSLAltNames;
- }
-
- public Set<String> getJsseNames() {
- return jsseNames;
- }
-
- public KeyExchange getKx() {
- return kx;
- }
-
- public Authentication getAu() {
- return au;
- }
-
- public Encryption getEnc() {
- return enc;
- }
-
- public MessageDigest getMac() {
- return mac;
- }
-
- public Protocol getProtocol() {
- return protocol;
- }
-
- public boolean isExport() {
- return export;
- }
-
- public EncryptionLevel getLevel() {
- return level;
- }
-
- public boolean isFipsCompatible() {
- return fipsCompatible;
- }
-
- public int getStrength_bits() {
- return strength_bits;
- }
-
- public int getAlg_bits() {
- return alg_bits;
- }
-
-
- private static final Map<Integer,Cipher> idMap = new HashMap<>();
-
- static {
- for (Cipher cipher : Cipher.values()) {
- int id = cipher.getId();
-
- if (id > 0 && id < 0xFFFF) {
- idMap.put(Integer.valueOf(id), cipher);
- }
- }
- }
-
-
- public static Cipher valueOf(int cipherId) {
- return idMap.get(Integer.valueOf(cipherId));
- }
-}
diff --git a/java/org/apache/tomcat/util/net/jsse/openssl/Encryption.java b/java/org/apache/tomcat/util/net/jsse/openssl/Encryption.java
deleted file mode 100644
index bb52b79..0000000
--- a/java/org/apache/tomcat/util/net/jsse/openssl/Encryption.java
+++ /dev/null
@@ -1,41 +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.net.jsse.openssl;
-
-enum Encryption {
- AES128,
- AES128CCM,
- AES128CCM8,
- AES128GCM,
- AES256,
- AES256CCM,
- AES256CCM8,
- AES256GCM,
- CAMELLIA256,
- CAMELLIA128,
- CHACHA20POLY1305,
- TRIPLE_DES,
- DES,
- IDEA,
- eGOST2814789CNT,
- SEED,
- FZA,
- RC4,
- RC2,
- eNULL;
-}
diff --git a/java/org/apache/tomcat/util/net/jsse/openssl/EncryptionLevel.java b/java/org/apache/tomcat/util/net/jsse/openssl/EncryptionLevel.java
deleted file mode 100644
index 25288b4..0000000
--- a/java/org/apache/tomcat/util/net/jsse/openssl/EncryptionLevel.java
+++ /dev/null
@@ -1,28 +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.net.jsse.openssl;
-
-enum EncryptionLevel {
- STRONG_NONE,
- EXP40,
- EXP56,
- LOW,
- MEDIUM,
- HIGH,
- FIPS;
-}
diff --git a/java/org/apache/tomcat/util/net/jsse/openssl/KeyExchange.java b/java/org/apache/tomcat/util/net/jsse/openssl/KeyExchange.java
deleted file mode 100644
index 569df6a..0000000
--- a/java/org/apache/tomcat/util/net/jsse/openssl/KeyExchange.java
+++ /dev/null
@@ -1,36 +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.net.jsse.openssl;
-
-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 */,
- 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 */,
- RSAPSK,
- ECDHEPSK,
- DHEPSK;
-}
diff --git a/java/org/apache/tomcat/util/net/jsse/openssl/MessageDigest.java b/java/org/apache/tomcat/util/net/jsse/openssl/MessageDigest.java
deleted file mode 100644
index 577d958..0000000
--- a/java/org/apache/tomcat/util/net/jsse/openssl/MessageDigest.java
+++ /dev/null
@@ -1,28 +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.net.jsse.openssl;
-
-enum MessageDigest {
- MD5,
- SHA1,
- GOST94,
- GOST89MAC,
- SHA256,
- SHA384,
- AEAD;
-}
diff --git a/java/org/apache/tomcat/util/net/jsse/openssl/OpenSSLCipherConfigurationParser.java b/java/org/apache/tomcat/util/net/jsse/openssl/OpenSSLCipherConfigurationParser.java
deleted file mode 100644
index b5454db..0000000
--- a/java/org/apache/tomcat/util/net/jsse/openssl/OpenSSLCipherConfigurationParser.java
+++ /dev/null
@@ -1,804 +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.net.jsse.openssl;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.LinkedHashMap;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import org.apache.juli.logging.Log;
-import org.apache.juli.logging.LogFactory;
-import org.apache.tomcat.util.net.Constants;
-import org.apache.tomcat.util.res.StringManager;
-
-/**
- * Class in charge with parsing openSSL expressions to define a list of ciphers.
- */
-public class OpenSSLCipherConfigurationParser {
-
- private static final Log log = LogFactory.getLog(OpenSSLCipherConfigurationParser.class);
- private static final StringManager sm =
- StringManager.getManager("org.apache.tomcat.util.net.jsse.res");
-
- private static boolean initialized = false;
-
- 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.
- */
- 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.
- */
- 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.
- */
- 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.
- */
- private static final String AND = "+";
- /**
- * All ciphers by their openssl alias name.
- */
- private static final Map<String, List<Cipher>> aliases = new LinkedHashMap<>();
-
- /**
- * 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.
- */
- 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.
- */
- private static final String HIGH = "HIGH";
- /**
- * '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.
- */
- private static final String LOW = "LOW";
- /**
- * Export encryption algorithms. Including 40 and 56 bits algorithms.
- */
- private static final String EXPORT = "EXPORT";
- /**
- * 40 bit export encryption algorithms.
- */
- private static final String EXPORT40 = "EXPORT40";
- /**
- * 56 bit export encryption algorithms.
- */
- private static final String EXPORT56 = "EXPORT56";
- /**
- * Cipher suites using RSA key exchange.
- */
- private static final String kRSA = "kRSA";
- /**
- * Cipher suites using RSA authentication.
- */
- private static final String aRSA = "aRSA";
- /**
- * Cipher suites using RSA for key exchange
- * Despite what the docs say, RSA is equivalent to kRSA.
- */
- private static final String RSA = "RSA";
- /**
- * Cipher suites using ephemeral DH key agreement.
- */
- private static final String kEDH = "kEDH";
- /**
- * Cipher suites using ephemeral DH key agreement.
- */
- private static final String kDHE = "kDHE";
- /**
- * Cipher suites using ephemeral DH key agreement. equivalent to kEDH:-ADH
- */
- private static final String EDH = "EDH";
- /**
- * Cipher suites using ephemeral DH key agreement. equivalent to kEDH:-ADH
- */
- private static final String DHE = "DHE";
- /**
- * Cipher suites using DH key agreement and DH certificates signed by CAs with RSA keys.
- */
- private static final String kDHr = "kDHr";
- /**
- * Cipher suites using DH key agreement and DH certificates signed by CAs with DSS keys.
- */
- private static final String kDHd = "kDHd";
- /**
- * Cipher suites using DH key agreement and DH certificates signed by CAs with RSA or DSS keys.
- */
- private static final String kDH = "kDH";
- /**
- * Cipher suites using fixed ECDH key agreement signed by CAs with RSA keys.
- */
- private static final String kECDHr = "kECDHr";
- /**
- * Cipher suites using fixed ECDH key agreement signed by CAs with ECDSA keys.
- */
- private static final String kECDHe = "kECDHe";
- /**
- * Cipher suites using fixed ECDH key agreement signed by CAs with RSA and ECDSA keys or either respectively.
- */
- private static final String kECDH = "kECDH";
- /**
- * Cipher suites using ephemeral ECDH key agreement, including anonymous cipher suites.
- */
- private static final String kEECDH = "kEECDH";
- /**
- * 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.
- */
- private static final String ECDH = "ECDH";
- /**
- * Cipher suites using ephemeral ECDH key agreement, including anonymous cipher suites.
- */
- private static final String kECDHE = "kECDHE";
- /**
- * Cipher suites using authenticated ephemeral ECDH key agreement
- */
- private static final String ECDHE = "ECDHE";
- /**
- * Cipher suites using authenticated ephemeral ECDH key agreement
- */
- private static final String EECDHE = "EECDHE";
- /**
- * Anonymous Elliptic Curve Diffie Hellman cipher suites.
- */
- private static final String AECDH = "AECDH";
- /**
- * Cipher suites using DSS for key exchange
- */
- private static final String DSS = "DSS";
- /**
- * Cipher suites using DSS authentication, i.e. the certificates carry DSS keys.
- */
- private static final String aDSS = "aDSS";
- /**
- * Cipher suites effectively using DH authentication, i.e. the certificates carry DH keys.
- */
- private static final String aDH = "aDH";
- /**
- * Cipher suites effectively using ECDH authentication, i.e. the certificates carry ECDH keys.
- */
- private static final String aECDH = "aECDH";
- /**
- * Cipher suites effectively using ECDSA authentication, i.e. the certificates carry ECDSA keys.
- */
- private static final String aECDSA = "aECDSA";
- /**
- * Cipher suites effectively using ECDSA authentication, i.e. the certificates carry ECDSA keys.
- */
- private static final String ECDSA = "ECDSA";
- /**
- * Ciphers suites using FORTEZZA key exchange algorithms.
- */
- private static final String kFZA = "kFZA";
- /**
- * Ciphers suites using FORTEZZA authentication algorithms.
- */
- private static final String aFZA = "aFZA";
- /**
- * Ciphers suites using FORTEZZA encryption algorithms.
- */
- private static final String eFZA = "eFZA";
- /**
- * Ciphers suites using all FORTEZZA algorithms.
- */
- private static final String FZA = "FZA";
- /**
- * Cipher suites using DH, including anonymous DH, ephemeral DH and fixed DH.
- */
- private static final String DH = "DH";
- /**
- * Anonymous DH cipher suites.
- */
- private static final String ADH = "ADH";
- /**
- * Cipher suites using 128 bit AES.
- */
- private static final String AES128 = "AES128";
- /**
- * Cipher suites using 256 bit AES.
- */
- private static final String AES256 = "AES256";
- /**
- * Cipher suites using either 128 or 256 bit AES.
- */
- private static final String AES = "AES";
- /**
- * AES in Galois Counter Mode (GCM): these cipher suites are only supported in TLS v1.2.
- */
- private static final String AESGCM = "AESGCM";
- /**
- * AES in Counter with CBC-MAC Mode (CCM).
- */
- private static final String AESCCM = "AESCCM";
- /**
- * AES in Counter with CBC-MAC Mode and 8-byte authentication (CCM8).
- */
- private static final String AESCCM8 = "AESCCM8";
- /**
- * Cipher suites using 128 bit CAMELLIA.
- */
- private static final String CAMELLIA128 = "CAMELLIA128";
- /**
- * Cipher suites using 256 bit CAMELLIA.
- */
- private static final String CAMELLIA256 = "CAMELLIA256";
- /**
- * Cipher suites using either 128 or 256 bit CAMELLIA.
- */
- private static final String CAMELLIA = "CAMELLIA";
- /**
- * Cipher suites using CHACHA20.
- */
- private static final String CHACHA20 = "CHACHA20";
- /**
- * Cipher suites using triple DES.
- */
- private static final String TRIPLE_DES = "3DES";
- /**
- * Cipher suites using DES (not triple DES).
- */
- private static final String DES = "DES";
- /**
- * Cipher suites using RC4.
- */
- private static final String RC4 = "RC4";
- /**
- * Cipher suites using RC2.
- */
- private static final String RC2 = "RC2";
- /**
- * Cipher suites using IDEA.
- */
- private static final String IDEA = "IDEA";
- /**
- * Cipher suites using SEED.
- */
- private static final String SEED = "SEED";
- /**
- * Cipher suites using MD5.
- */
- private static final String MD5 = "MD5";
- /**
- * Cipher suites using SHA1.
- */
- private static final String SHA1 = "SHA1";
- /**
- * Cipher suites using SHA1.
- */
- private static final String SHA = "SHA";
- /**
- * Cipher suites using SHA256.
- */
- private static final String SHA256 = "SHA256";
- /**
- * Cipher suites using SHA384.
- */
- private static final String SHA384 = "SHA384";
- /**
- * Cipher suites using KRB5.
- */
- private static final String KRB5 = "KRB5";
- /**
- * Cipher suites using GOST R 34.10 (either 2001 or 94) for authentication.
- */
- private static final String aGOST = "aGOST";
- /**
- * Cipher suites using GOST R 34.10-2001 for authentication.
- */
- private static final String aGOST01 = "aGOST01";
- /**
- * Cipher suites using GOST R 34.10-94 authentication (note that R 34.10-94 standard has been expired so use GOST R
- * 34.10-2001)
- */
- private static final String aGOST94 = "aGOST94";
- /**
- * Cipher suites using using VKO 34.10 key exchange, specified in the RFC 4357.
- */
- private static final String kGOST = "kGOST";
- /**
- * Cipher suites, using HMAC based on GOST R 34.11-94.
- */
- private static final String GOST94 = "GOST94";
- /**
- * Cipher suites using GOST 28147-89 MAC instead of HMAC.
- */
- private static final String GOST89MAC = "GOST89MAC";
- /**
- * Cipher suites using SRP authentication, specified in the RFC 5054.
- */
- private static final String aSRP = "aSRP";
- /**
- * Cipher suites using SRP key exchange, specified in the RFC 5054.
- */
- private static final String kSRP = "kSRP";
- /**
- * Same as kSRP
- */
- private static final String SRP = "SRP";
- /**
- * Cipher suites using pre-shared keys (PSK).
- */
- private static final String PSK = "PSK";
- /**
- * Cipher suites using PSK authentication.
- */
- private static final String aPSK = "aPSK";
- /**
- * Cipher suites using PSK key 'exchange'.
- */
- private static final String kPSK = "kPSK";
- private static final String kRSAPSK = "kRSAPSK";
- private static final String kECDHEPSK = "kECDHEPSK";
- private static final String kDHEPSK = "kDHEPSK";
-
- private static final String DEFAULT = "DEFAULT";
- private static final String COMPLEMENTOFDEFAULT = "COMPLEMENTOFDEFAULT";
-
- private static final String ALL = "ALL";
- private static final String COMPLEMENTOFALL = "COMPLEMENTOFALL";
-
- private static final Map<String,String> jsseToOpenSSL = new HashMap<>();
-
- private static final void init() {
-
- for (Cipher cipher : Cipher.values()) {
- String alias = cipher.getOpenSSLAlias();
- if (aliases.containsKey(alias)) {
- aliases.get(alias).add(cipher);
- } else {
- List<Cipher> list = new ArrayList<>();
- list.add(cipher);
- aliases.put(alias, list);
- }
- aliases.put(cipher.name(), Collections.singletonList(cipher));
-
- for (String openSSlAltName : cipher.getOpenSSLAltNames()) {
- if (aliases.containsKey(openSSlAltName)) {
- aliases.get(openSSlAltName).add(cipher);
- } else {
- List<Cipher> list = new ArrayList<>();
- list.add(cipher);
- aliases.put(openSSlAltName, list);
- }
-
- }
-
- jsseToOpenSSL.put(cipher.name(), cipher.getOpenSSLAlias());
- Set<String> jsseNames = cipher.getJsseNames();
- for (String jsseName : jsseNames) {
- jsseToOpenSSL.put(jsseName, cipher.getOpenSSLAlias());
- }
- }
- List<Cipher> allCiphersList = Arrays.asList(Cipher.values());
- Collections.reverse(allCiphersList);
- LinkedHashSet<Cipher> allCiphers = defaultSort(new LinkedHashSet<>(allCiphersList));
- addListAlias(eNULL, filterByEncryption(allCiphers, Collections.singleton(Encryption.eNULL)));
- LinkedHashSet<Cipher> all = new LinkedHashSet<>(allCiphers);
- remove(all, eNULL);
- addListAlias(ALL, all);
- 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))));
- aliases.put("EXP", aliases.get(EXPORT));
- addListAlias(EXPORT40, filterByEncryptionLevel(allCiphers, Collections.singleton(EncryptionLevel.EXP40)));
- addListAlias(EXPORT56, filterByEncryptionLevel(allCiphers, Collections.singleton(EncryptionLevel.EXP56)));
- aliases.put("NULL", aliases.get(eNULL));
- aliases.put(COMPLEMENTOFALL, aliases.get(eNULL));
- addListAlias(aNULL, filterByAuthentication(allCiphers, Collections.singleton(Authentication.aNULL)));
- addListAlias(kRSA, filterByKeyExchange(allCiphers, Collections.singleton(KeyExchange.RSA)));
- addListAlias(aRSA, filterByAuthentication(allCiphers, Collections.singleton(Authentication.RSA)));
- // Despite what the docs say, RSA is equivalent to kRSA
- aliases.put(RSA, aliases.get(kRSA));
- addListAlias(kEDH, filterByKeyExchange(allCiphers, Collections.singleton(KeyExchange.EDH)));
- addListAlias(kDHE, filterByKeyExchange(allCiphers, Collections.singleton(KeyExchange.EDH)));
- Set<Cipher> edh = filterByKeyExchange(allCiphers, Collections.singleton(KeyExchange.EDH));
- edh.removeAll(filterByAuthentication(allCiphers, Collections.singleton(Authentication.aNULL)));
- addListAlias(EDH, edh);
- 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(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(kECDHE, filterByKeyExchange(allCiphers, Collections.singleton(KeyExchange.EECDH)));
-
- Set<Cipher> ecdhe = filterByKeyExchange(allCiphers, Collections.singleton(KeyExchange.EECDH));
- remove(ecdhe, aNULL);
- addListAlias(ECDHE, ecdhe);
-
- addListAlias(kEECDH, filterByKeyExchange(allCiphers, Collections.singleton(KeyExchange.EECDH)));
- aliases.put(EECDHE, aliases.get(kEECDH));
- Set<Cipher> eecdh = filterByKeyExchange(allCiphers, Collections.singleton(KeyExchange.EECDH));
- eecdh.removeAll(filterByAuthentication(allCiphers, Collections.singleton(Authentication.aNULL)));
- addListAlias(EECDH, eecdh);
- addListAlias(aDSS, filterByAuthentication(allCiphers, Collections.singleton(Authentication.DSS)));
- aliases.put(DSS, aliases.get(aDSS));
- addListAlias(aDH, filterByAuthentication(allCiphers, Collections.singleton(Authentication.DH)));
- Set<Cipher> aecdh = filterByKeyExchange(allCiphers, Collections.singleton(KeyExchange.EECDH));
- addListAlias(AECDH, filterByAuthentication(aecdh, Collections.singleton(Authentication.aNULL)));
- addListAlias(aECDH, filterByAuthentication(allCiphers, Collections.singleton(Authentication.ECDH)));
- addListAlias(ECDSA, filterByAuthentication(allCiphers, Collections.singleton(Authentication.ECDSA)));
- aliases.put(aECDSA, aliases.get(ECDSA));
- 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(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))));
- Set<Cipher> 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(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)));
- addListAlias(TRIPLE_DES, filterByEncryption(allCiphers, Collections.singleton(Encryption.TRIPLE_DES)));
- addListAlias(DES, filterByEncryption(allCiphers, Collections.singleton(Encryption.DES)));
- addListAlias(RC4, filterByEncryption(allCiphers, Collections.singleton(Encryption.RC4)));
- addListAlias(RC2, filterByEncryption(allCiphers, Collections.singleton(Encryption.RC2)));
- addListAlias(IDEA, filterByEncryption(allCiphers, Collections.singleton(Encryption.IDEA)));
- addListAlias(SEED, filterByEncryption(allCiphers, Collections.singleton(Encryption.SEED)));
- addListAlias(MD5, filterByMessageDigest(allCiphers, Collections.singleton(MessageDigest.MD5)));
- addListAlias(SHA1, filterByMessageDigest(allCiphers, Collections.singleton(MessageDigest.SHA1)));
- 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(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(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(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"));
- // COMPLEMENTOFDEFAULT is also not exactly as defined by the docs
- LinkedHashSet<Cipher> complementOfDefault = filterByKeyExchange(all, new HashSet<>(Arrays.asList(KeyExchange.EDH,KeyExchange.EECDH)));
- complementOfDefault = filterByAuthentication(complementOfDefault, Collections.singleton(Authentication.aNULL));
- complementOfDefault.removeAll(aliases.get(eNULL));
- complementOfDefault.addAll(aliases.get(Constants.SSL_PROTO_SSLv2));
- complementOfDefault.addAll(aliases.get(EXPORT));
- complementOfDefault.addAll(aliases.get(DES));
- complementOfDefault.addAll(aliases.get(TRIPLE_DES));
- complementOfDefault.addAll(aliases.get(RC2));
- complementOfDefault.addAll(aliases.get(RC4));
- complementOfDefault.addAll(aliases.get(aDSS));
- complementOfDefault.addAll(aliases.get(SEED));
- complementOfDefault.addAll(aliases.get(IDEA));
- complementOfDefault.addAll(aliases.get(CAMELLIA));
- complementOfDefault.addAll(aliases.get(AESCCM));
- defaultSort(complementOfDefault);
- addListAlias(COMPLEMENTOFDEFAULT, complementOfDefault);
- }
-
- static void addListAlias(String alias, Set<Cipher> ciphers) {
- aliases.put(alias, new ArrayList<>(ciphers));
- }
-
- static void moveToEnd(final LinkedHashSet<Cipher> ciphers, final String alias) {
- moveToEnd(ciphers, aliases.get(alias));
- }
-
- static void moveToEnd(final LinkedHashSet<Cipher> ciphers, final Collection<Cipher> toBeMovedCiphers) {
- List<Cipher> movedCiphers = new ArrayList<>(toBeMovedCiphers);
- movedCiphers.retainAll(ciphers);
- ciphers.removeAll(movedCiphers);
- ciphers.addAll(movedCiphers);
- }
-
- static void moveToStart(final LinkedHashSet<Cipher> ciphers, final Collection<Cipher> toBeMovedCiphers) {
- List<Cipher> movedCiphers = new ArrayList<>(toBeMovedCiphers);
- List<Cipher> originalCiphers = new ArrayList<>(ciphers);
- movedCiphers.retainAll(ciphers);
- ciphers.clear();
- ciphers.addAll(movedCiphers);
- ciphers.addAll(originalCiphers);
- }
-
- static void add(final LinkedHashSet<Cipher> ciphers, final String alias) {
- ciphers.addAll(aliases.get(alias));
- }
-
- static void remove(final Set<Cipher> ciphers, final String alias) {
- ciphers.removeAll(aliases.get(alias));
- }
-
- static LinkedHashSet<Cipher> strengthSort(final LinkedHashSet<Cipher> 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.
- */
- Set<Integer> keySizes = new HashSet<>();
- for (Cipher cipher : ciphers) {
- keySizes.add(Integer.valueOf(cipher.getStrength_bits()));
- }
- List<Integer> strength_bits = new ArrayList<>(keySizes);
- Collections.sort(strength_bits);
- Collections.reverse(strength_bits);
- final LinkedHashSet<Cipher> result = new LinkedHashSet<>(ciphers);
- for (int strength : strength_bits) {
- moveToEnd(result, filterByStrengthBits(ciphers, strength));
- }
- return result;
- }
-
- /*
- * See
- * https://github.com/openssl/openssl/blob/7c96dbcdab959fef74c4caae63cdebaa354ab252/ssl/ssl_ciph.c#L1371
- */
- static LinkedHashSet<Cipher> defaultSort(final LinkedHashSet<Cipher> ciphers) {
- final LinkedHashSet<Cipher> result = new LinkedHashSet<>(ciphers.size());
- final LinkedHashSet<Cipher> ecdh = new LinkedHashSet<>(ciphers.size());
-
- /* Everything else being equal, prefer ephemeral ECDH over other key exchange mechanisms */
- ecdh.addAll(filterByKeyExchange(ciphers, Collections.singleton(KeyExchange.EECDH)));
-
- /* AES is our preferred symmetric cipher */
- Set<Encryption> 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));
- result.addAll(filterByEncryption(ciphers, aes));
-
- /* Add everything else */
- result.addAll(ecdh);
- result.addAll(ciphers);
-
- /* 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.) */
- moveToEnd(result, filterByAuthentication(result, Collections.singleton(Authentication.aNULL)));
-
- /* Move ciphers without forward secrecy to the end */
- moveToEnd(result, filterByAuthentication(result, Collections.singleton(Authentication.ECDH)));
- 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 */
- moveToEnd(result, filterByEncryption(result, Collections.singleton(Encryption.RC4)));
- return strengthSort(result);
- }
-
- static Set<Cipher> filterByStrengthBits(Set<Cipher> ciphers, int strength_bits) {
- Set<Cipher> result = new LinkedHashSet<>(ciphers.size());
- for (Cipher cipher : ciphers) {
- if (cipher.getStrength_bits() == strength_bits) {
- result.add(cipher);
- }
- }
- return result;
- }
-
- static Set<Cipher> filterByProtocol(Set<Cipher> ciphers, Set<Protocol> protocol) {
- return filter(ciphers, protocol, null, null, null, null, null);
- }
-
- static LinkedHashSet<Cipher> filterByKeyExchange(Set<Cipher> ciphers, Set<KeyExchange> kx) {
- return filter(ciphers, null, kx, null, null, null, null);
- }
-
- static LinkedHashSet<Cipher> filterByAuthentication(Set<Cipher> ciphers, Set<Authentication> au) {
- return filter(ciphers, null, null, au, null, null, null);
- }
-
- static Set<Cipher> filterByEncryption(Set<Cipher> ciphers, Set<Encryption> enc) {
- return filter(ciphers, null, null, null, enc, null, null);
- }
-
- static Set<Cipher> filterByEncryptionLevel(Set<Cipher> ciphers, Set<EncryptionLevel> level) {
- return filter(ciphers, null, null, null, null, level, null);
- }
-
- static Set<Cipher> filterByMessageDigest(Set<Cipher> ciphers, Set<MessageDigest> mac) {
- return filter(ciphers, null, null, null, null, null, mac);
- }
-
- static LinkedHashSet<Cipher> filter(Set<Cipher> ciphers, Set<Protocol> protocol, Set<KeyExchange> kx,
- Set<Authentication> au, Set<Encryption> enc, Set<EncryptionLevel> level, Set<MessageDigest> mac) {
- LinkedHashSet<Cipher> result = new LinkedHashSet<>(ciphers.size());
- for (Cipher cipher : ciphers) {
- if (protocol != null && protocol.contains(cipher.getProtocol())) {
- result.add(cipher);
- }
- if (kx != null && kx.contains(cipher.getKx())) {
- result.add(cipher);
- }
- if (au != null && au.contains(cipher.getAu())) {
- result.add(cipher);
- }
- if (enc != null && enc.contains(cipher.getEnc())) {
- result.add(cipher);
- }
- if (level != null && level.contains(cipher.getLevel())) {
- result.add(cipher);
- }
- if (mac != null && mac.contains(cipher.getMac())) {
- result.add(cipher);
- }
- }
- return result;
- }
-
- public static LinkedHashSet<Cipher> parse(String expression) {
- if (!initialized) {
- init();
- }
- String[] elements = expression.split(SEPARATOR);
- LinkedHashSet<Cipher> ciphers = new LinkedHashSet<>();
- Set<Cipher> removedCiphers = new HashSet<>();
- for (String element : elements) {
- if (element.startsWith(DELETE)) {
- String alias = element.substring(1);
- if (aliases.containsKey(alias)) {
- remove(ciphers, alias);
- }
- } else if (element.startsWith(EXCLUDE)) {
- String alias = element.substring(1);
- if (aliases.containsKey(alias)) {
- removedCiphers.addAll(aliases.get(alias));
- } else {
- log.warn(sm.getString("jsse.openssl.unknownElement", alias));
- }
- } else if (element.startsWith(TO_END)) {
- String alias = element.substring(1);
- if (aliases.containsKey(alias)) {
- moveToEnd(ciphers, alias);
- }
- } else if ("@STRENGTH".equals(element)) {
- strengthSort(ciphers);
- break;
- } else if (aliases.containsKey(element)) {
- add(ciphers, element);
- } else if (element.contains(AND)) {
- String[] intersections = element.split("\\" + AND);
- if(intersections.length > 0 && aliases.containsKey(intersections[0])) {
- List<Cipher> result = new ArrayList<>(aliases.get(intersections[0]));
- for(int i = 1; i < intersections.length; i++) {
- if(aliases.containsKey(intersections[i])) {
- result.retainAll(aliases.get(intersections[i]));
- }
- }
- ciphers.addAll(result);
- }
- }
- }
- ciphers.removeAll(removedCiphers);
- return ciphers;
- }
-
- public static List<String> convertForJSSE(Collection<Cipher> ciphers) {
- List<String> result = new ArrayList<>(ciphers.size());
- for (Cipher cipher : ciphers) {
- result.addAll(cipher.getJsseNames());
- }
- if (log.isDebugEnabled()) {
- log.debug(sm.getString("jsse.openssl.effectiveCiphers", displayResult(ciphers, true, ",")));
- }
- return result;
- }
-
- /**
- * 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<String> parseExpression(String expression) {
- return convertForJSSE(parse(expression));
- }
-
-
- /**
- * Converts a JSSE cipher name to an OpenSSL cipher name.
- *
- * @param jsseCipherName The JSSE name for a cipher
- *
- * @return The OpenSSL name for the specified JSSE cipher
- */
- public static String jsseToOpenSSL(String jsseCipherName) {
- if (!initialized) {
- init();
- }
- return jsseToOpenSSL.get(jsseCipherName);
- }
-
-
- static String displayResult(Collection<Cipher> ciphers, boolean useJSSEFormat, String separator) {
- if (ciphers.isEmpty()) {
- return "";
- }
- StringBuilder builder = new StringBuilder(ciphers.size() * 16);
- for (Cipher cipher : ciphers) {
- if (useJSSEFormat) {
- for (String name : cipher.getJsseNames()) {
- builder.append(name);
- builder.append(separator);
- }
- } else {
- builder.append(cipher.getOpenSSLAlias());
- }
- builder.append(separator);
- }
- return builder.toString().substring(0, builder.length() - 1);
- }
-}
diff --git a/java/org/apache/tomcat/util/net/jsse/openssl/Protocol.java b/java/org/apache/tomcat/util/net/jsse/openssl/Protocol.java
deleted file mode 100644
index 772f39d..0000000
--- a/java/org/apache/tomcat/util/net/jsse/openssl/Protocol.java
+++ /dev/null
@@ -1,43 +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.net.jsse.openssl;
-
-import org.apache.tomcat.util.net.Constants;
-
-enum Protocol {
-
- SSLv3(Constants.SSL_PROTO_SSLv3),
- SSLv2(Constants.SSL_PROTO_SSLv2),
- TLSv1(Constants.SSL_PROTO_TLSv1),
- TLSv1_2(Constants.SSL_PROTO_TLSv1_2);
-
- private final String openSSLName;
-
- private Protocol(String openSSLName) {
- this.openSSLName = openSSLName;
- }
-
- /**
- * The name returned by OpenSSL in the protocol column when using
- * <code>openssl ciphers -v</code>. This is currently only used by the unit
- * tests hence it is package private.
- */
- String getOpenSSLName() {
- return openSSLName;
- }
-}
diff --git a/java/org/apache/tomcat/util/net/jsse/res/LocalStrings.properties b/java/org/apache/tomcat/util/net/jsse/res/LocalStrings.properties
deleted file mode 100644
index 38968d6..0000000
--- a/java/org/apache/tomcat/util/net/jsse/res/LocalStrings.properties
+++ /dev/null
@@ -1,37 +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.
-
-jsse.alias_no_key_entry=Alias name {0} does not identify a key entry
-jsse.keystore_load_failed=Failed to load keystore type {0} with path {1} due to {2}
-jsse.invalid_ssl_conf=SSL configuration is invalid due to {0}
-jsse.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.
-jsse.invalidTrustManagerClassName=The trustManagerClassName provided [{0}] does not implement javax.net.ssl.TrustManager
-jsse.requested_ciphers_not_supported=None of the ciphers specified are supported by the SSL engine : {0}
-jsse.enableable_ciphers=Specified SSL ciphers that are supported and enableable are : {0}
-jsse.unsupported_ciphers=Some specified SSL ciphers are not supported by the SSL engine : {0}
-jsse.requested_protocols_not_supported=None of the SSL protocols specified are supported by the SSL engine : {0}
-jsse.enableable_protocols=Specified SSL protocols that are supported and enableable are : {0}
-jsse.unsupported_protocols=Some specified SSL protocols are not supported by the SSL engine : {0}
-jsse.excludeDefaultProtocol=The SSL protocol [{0}] which is enabled by default in this JRE was excluded from the defaults used by Tomcat
-jsse.noDefaultCiphers=Unable to determine a default for ciphers for [{0}]. Set an explicit value to ensure the connector can start.
-jsse.noDefaultProtocols=Unable to determine a default for sslEnabledProtocols for [{0}]. Set an explicit value to ensure the connector can start.
-jsse.exceptionOnClose=Failure to close socket.
-jsseSupport.clientCertError=Error trying to obtain a certificate from the client
-jseeSupport.certTranslationError=Error translating certificate [{0}]
-jsseSupport.noCertWant=No client certificate sent for want
-jsseSupport.serverRenegDisabled=SSL server initiated renegotiation is disabled, closing connection
-jsseSupport.unexpectedData=Unexpected data read from input stream
-jsse.openssl.unknownElement=Unknown element in cipher string: {0}
-jsse.openssl.effectiveCiphers=Ciphers used: {0}
diff --git a/java/org/apache/tomcat/util/net/jsse/res/LocalStrings_es.properties b/java/org/apache/tomcat/util/net/jsse/res/LocalStrings_es.properties
deleted file mode 100644
index cdf93f2..0000000
--- a/java/org/apache/tomcat/util/net/jsse/res/LocalStrings_es.properties
+++ /dev/null
@@ -1,19 +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.
-jsse.alias_no_key_entry = El nombre de Alias {0} no identifica una entrada de clave
-jsse.keystore_load_failed = No pude cargar almac\u00E9n de claves de tipo {0} con ruta {1} debido a {2}
-jsse.invalid_ssl_conf = La configuraci\u00F3n SSL no es v\u00E1lida debido a {0}
-jsse.invalid_truststore_password = La clave del almac\u00E9n de confianza suministrada no se pudo usar para desbloquear y/o validar el almac\u00E9n de confianza. Reintentando acceder el almac\u00E9n de confianza con una clave nula que se saltar\u00E1 la validaci\u00F3n.
-jsse.invalidTrustManagerClassName = El trustManagerClassName suministrado [{0}] no implementa javax.net.ssl.TrustManager
diff --git a/java/org/apache/tomcat/util/net/openssl/LocalStrings.properties b/java/org/apache/tomcat/util/net/openssl/LocalStrings.properties
new file mode 100644
index 0000000..04ca1ba
--- /dev/null
+++ b/java/org/apache/tomcat/util/net/openssl/LocalStrings.properties
@@ -0,0 +1,48 @@
+# 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.
+
+openssl.X509FactoryError=Error getting X509 factory instance
+openssl.errorSSLCtxInit=Error initializing SSL context
+openssl.doubleInit=SSL context already initialized, ignoring
+openssl.certificateVerificationFailed=Certificate verification failed
+openssl.keyManagerMissing=No key manager found
+openssl.trustManagerMissing=No trust manager found
+
+engine.engineClosed=Engine is closed
+engine.renegociationUnsupported=Renegociation is not supported
+engine.oversizedPacket=Encrypted packet is oversized
+engine.ciphersFailure=Failed getting cipher list
+engine.noSSLContext=No SSL context
+engine.writeToSSLFailed=Failed writing to SSL, returned: {0}
+engine.invalidBufferArray=offset: {0}, length: {1} (expected: offset <= offset + length <= srcs.length ({2}))
+engine.nullBufferInArray=Null buffer in array
+engine.nullBuffer=Null buffer
+engine.openSSLError=OpenSSL error: {0} message: {1}
+engine.inboundClose=Inbound closed before receiving peer's close_notify
+engine.nullCipherSuite=Null cipher suite
+engine.unsupportedCipher=Unsupported cipher suite: {0} ({1})
+engine.emptyCipherSuite=Empty cipher suite
+engine.failedCipherSuite=Failed to enable cipher suite {0}
+engine.unsupportedProtocol=Protocol {0} is not supported
+engine.unverifiedPeer=Peer unverified
+engine.noSession=SSL session ID not available
+engine.nullName=Null value name
+engine.nullValue=Null value
+engine.handshakeFailure=Failed handshake: {0}
+
+keyManager.nullCertificateChain=Null certificate chain
+keyManager.nullPrivateKey=Null private key
+
+sessionContext.nullTicketKeys=Null keys
diff --git a/java/org/apache/tomcat/util/net/openssl/OpenSSLContext.java b/java/org/apache/tomcat/util/net/openssl/OpenSSLContext.java
new file mode 100644
index 0000000..72b35e2
--- /dev/null
+++ b/java/org/apache/tomcat/util/net/openssl/OpenSSLContext.java
@@ -0,0 +1,397 @@
+/*
+ * 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;
+
+import java.nio.charset.StandardCharsets;
+import java.security.PrivateKey;
+import java.security.SecureRandom;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLException;
+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;
+
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
+import org.apache.tomcat.jni.CertificateVerifier;
+import org.apache.tomcat.jni.Pool;
+import org.apache.tomcat.jni.SSL;
+import org.apache.tomcat.jni.SSLContext;
+import org.apache.tomcat.util.codec.binary.Base64;
+import org.apache.tomcat.util.net.AbstractEndpoint;
+import org.apache.tomcat.util.net.Constants;
+import org.apache.tomcat.util.net.SSLHostConfig;
+import org.apache.tomcat.util.net.SSLHostConfigCertificate;
+import org.apache.tomcat.util.net.jsse.JSSEKeyManager;
+import org.apache.tomcat.util.net.openssl.ciphers.OpenSSLCipherConfigurationParser;
+import org.apache.tomcat.util.res.StringManager;
+
+public class OpenSSLContext implements org.apache.tomcat.util.net.SSLContext {
+
+ private static final Base64 BASE64_ENCODER = new Base64(64, new byte[] {'\n'});
+
+ private static final Log log = LogFactory.getLog(OpenSSLContext.class);
+
+ // Note: this uses the main "net" package strings as many are common with APR
+ private static final StringManager netSm = StringManager.getManager(AbstractEndpoint.class);
+ private static final StringManager sm = StringManager.getManager(OpenSSLContext.class);
+
+ private static final String defaultProtocol = "TLS";
+
+ private final SSLHostConfig sslHostConfig;
+ private final SSLHostConfigCertificate certificate;
+ private OpenSSLSessionContext sessionContext;
+
+ private final List<String> negotiableProtocols;
+
+ private List<String> jsseCipherNames = new ArrayList<>();
+
+ public List<String> getJsseCipherNames() {
+ return jsseCipherNames;
+ }
+
+ private String enabledProtocol;
+
+ public String getEnabledProtocol() {
+ return enabledProtocol;
+ }
+
+ public void setEnabledProtocol(String protocol) {
+ enabledProtocol = (protocol == null) ? defaultProtocol : protocol;
+ }
+
+ private final long aprPool;
+ private final AtomicInteger aprPoolDestroyed = new AtomicInteger(0);
+
+ protected final long ctx;
+
+ static final CertificateFactory X509_CERT_FACTORY;
+
+ private static final String BEGIN_KEY = "-----BEGIN RSA PRIVATE KEY-----\n";
+
+ private static final Object END_KEY = "\n-----END RSA PRIVATE KEY-----";
+ private boolean initialized = false;
+
+ static {
+ try {
+ X509_CERT_FACTORY = CertificateFactory.getInstance("X.509");
+ } catch (CertificateException e) {
+ throw new IllegalStateException(sm.getString("openssl.X509FactoryError"), e);
+ }
+ }
+
+ public OpenSSLContext(SSLHostConfigCertificate certificate, List<String> negotiableProtocols)
+ throws SSLException {
+ this.sslHostConfig = certificate.getSSLHostConfig();
+ this.certificate = certificate;
+ aprPool = Pool.create(0);
+ boolean success = false;
+ try {
+ // SSL protocol
+ int value = SSL.SSL_PROTOCOL_NONE;
+ if (sslHostConfig.getProtocols().size() == 0) {
+ value = SSL.SSL_PROTOCOL_ALL;
+ } else {
+ for (String protocol : sslHostConfig.getEnabledProtocols()) {
+ if (Constants.SSL_PROTO_SSLv2Hello.equalsIgnoreCase(protocol)) {
+ // NO-OP. OpenSSL always supports SSLv2Hello
+ } else if (Constants.SSL_PROTO_SSLv2.equalsIgnoreCase(protocol)) {
+ value |= SSL.SSL_PROTOCOL_SSLV2;
+ } else if (Constants.SSL_PROTO_SSLv3.equalsIgnoreCase(protocol)) {
+ value |= SSL.SSL_PROTOCOL_SSLV3;
+ } else if (Constants.SSL_PROTO_TLSv1.equalsIgnoreCase(protocol)) {
+ value |= SSL.SSL_PROTOCOL_TLSV1;
+ } else if (Constants.SSL_PROTO_TLSv1_1.equalsIgnoreCase(protocol)) {
+ value |= SSL.SSL_PROTOCOL_TLSV1_1;
+ } else if (Constants.SSL_PROTO_TLSv1_2.equalsIgnoreCase(protocol)) {
+ value |= SSL.SSL_PROTOCOL_TLSV1_2;
+ } else if (Constants.SSL_PROTO_ALL.equalsIgnoreCase(protocol)) {
+ value |= SSL.SSL_PROTOCOL_ALL;
+ } else {
+ // Protocol not recognized, fail to start as it is safer than
+ // continuing with the default which might enable more than the
+ // is required
+ throw new Exception(netSm.getString(
+ "endpoint.apr.invalidSslProtocol", protocol));
+ }
+ }
+ }
+
+ // Create SSL Context
+ try {
+ ctx = SSLContext.make(aprPool, value, SSL.SSL_MODE_SERVER);
+ } catch (Exception e) {
+ // If the sslEngine is disabled on the AprLifecycleListener
+ // there will be an Exception here but there is no way to check
+ // the AprLifecycleListener settings from here
+ throw new Exception(
+ netSm.getString("endpoint.apr.failSslContextMake"), e);
+ }
+
+ this.negotiableProtocols = negotiableProtocols;
+
+ success = true;
+ } catch(Exception e) {
+ throw new SSLException(sm.getString("openssl.errorSSLCtxInit"), e);
+ } finally {
+ if (!success) {
+ destroy();
+ }
+ }
+ }
+
+ @Override
+ public synchronized void destroy() {
+ // Guard against multiple destroyPools() calls triggered by construction exception and finalize() later
+ if (aprPoolDestroyed.compareAndSet(0, 1)) {
+ if (ctx != 0) {
+ SSLContext.free(ctx);
+ }
+ if (aprPool != 0) {
+ Pool.destroy(aprPool);
+ }
+ }
+ }
+
+ /**
+ * 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.
+ */
+ @Override
+ public synchronized void init(KeyManager[] kms, TrustManager[] tms, SecureRandom sr) {
+ if (initialized) {
+ log.warn(sm.getString("openssl.doubleInit"));
+ return;
+ }
+ try {
+ if (sslHostConfig.getInsecureRenegotiation()) {
+ SSLContext.setOptions(ctx, SSL.SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION);
+ } else {
+ SSLContext.clearOptions(ctx, SSL.SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION);
+ }
+
+ // Use server's preference order for ciphers (rather than
+ // client's)
+ String honorCipherOrderStr = sslHostConfig.getHonorCipherOrder();
+ if (honorCipherOrderStr != null) {
+ if (Boolean.parseBoolean(honorCipherOrderStr)) {
+ SSLContext.setOptions(ctx, SSL.SSL_OP_CIPHER_SERVER_PREFERENCE);
+ } else {
+ SSLContext.clearOptions(ctx, SSL.SSL_OP_CIPHER_SERVER_PREFERENCE);
+ }
+ }
+
+ // Disable compression if requested
+ if (sslHostConfig.getDisableCompression()) {
+ SSLContext.setOptions(ctx, SSL.SSL_OP_NO_COMPRESSION);
+ } else {
+ SSLContext.clearOptions(ctx, SSL.SSL_OP_NO_COMPRESSION);
+ }
+
+ // Disable TLS Session Tickets (RFC4507) to protect perfect forward secrecy
+ if (sslHostConfig.getDisableSessionTickets()) {
+ SSLContext.setOptions(ctx, SSL.SSL_OP_NO_TICKET);
+ } else {
+ SSLContext.clearOptions(ctx, SSL.SSL_OP_NO_TICKET);
+ }
+
+ // Set session cache size, if specified
+ if (sslHostConfig.getSessionCacheSize() > 0) {
+ SSLContext.setSessionCacheSize(ctx, sslHostConfig.getSessionCacheSize());
+ } else {
+ // Get the default session cache size using SSLContext.setSessionCacheSize()
+ long sessionCacheSize = SSLContext.setSessionCacheSize(ctx, 20480);
+ // Revert the session cache size to the default value.
+ SSLContext.setSessionCacheSize(ctx, sessionCacheSize);
+ }
+
+ // Set session timeout, if specified
+ if (sslHostConfig.getSessionTimeout() > 0) {
+ SSLContext.setSessionCacheTimeout(ctx, sslHostConfig.getSessionTimeout());
+ } else {
+ // Get the default session timeout using SSLContext.setSessionCacheTimeout()
+ long sessionTimeout = SSLContext.setSessionCacheTimeout(ctx, 300);
+ // Revert the session timeout to the default value.
+ SSLContext.setSessionCacheTimeout(ctx, sessionTimeout);
+ }
+
+ // List the ciphers that the client is permitted to negotiate
+ String opensslCipherConfig = sslHostConfig.getCiphers();
+ this.jsseCipherNames = OpenSSLCipherConfigurationParser.parseExpression(opensslCipherConfig);
+ SSLContext.setCipherSuite(ctx, opensslCipherConfig);
+ // Load Server key and certificate
+ if (certificate.getCertificateFile() != null) {
+ // Set certificate
+ SSLContext.setCertificate(ctx,
+ SSLHostConfig.adjustRelativePath(certificate.getCertificateFile()),
+ SSLHostConfig.adjustRelativePath(certificate.getCertificateKeyFile()),
+ certificate.getCertificateKeyPassword(), SSL.SSL_AIDX_RSA);
+ // Set certificate chain file
+ SSLContext.setCertificateChainFile(ctx,
+ SSLHostConfig.adjustRelativePath(certificate.getCertificateChainFile()), false);
+ // Support Client Certificates
+ SSLContext.setCACertificate(ctx,
+ SSLHostConfig.adjustRelativePath(sslHostConfig.getCaCertificateFile()),
+ SSLHostConfig.adjustRelativePath(sslHostConfig.getCaCertificatePath()));
+ // Set revocation
+ SSLContext.setCARevocation(ctx,
+ SSLHostConfig.adjustRelativePath(
+ sslHostConfig.getCertificateRevocationListFile()),
+ SSLHostConfig.adjustRelativePath(
+ sslHostConfig.getCertificateRevocationListPath()));
+ } else {
+ X509KeyManager keyManager = chooseKeyManager(kms);
+ String alias = certificate.getCertificateKeyAlias();
+ if (alias == null) {
+ alias = "tomcat";
+ }
+ X509Certificate[] chain = keyManager.getCertificateChain(alias);
+ PrivateKey key = keyManager.getPrivateKey(alias);
+ StringBuilder sb = new StringBuilder(BEGIN_KEY);
+ String encoded = BASE64_ENCODER.encodeToString(key.getEncoded());
+ if (encoded.endsWith("\n")) {
+ encoded = encoded.substring(0, encoded.length() - 1);
+ }
+ sb.append(encoded);
+ sb.append(END_KEY);
+ SSLContext.setCertificateRaw(ctx, chain[0].getEncoded(), sb.toString().getBytes(StandardCharsets.US_ASCII), SSL.SSL_AIDX_RSA);
+ for (int i = 1; i < chain.length; i++) {
+ SSLContext.addChainCertificateRaw(ctx, chain[i].getEncoded());
+ }
+ }
+ // 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;
+ }
+ SSLContext.setVerify(ctx, value, sslHostConfig.getCertificateVerificationDepth());
+
+ if (tms != null) {
+ final X509TrustManager manager = chooseTrustManager(tms);
+ SSLContext.setCertVerifyCallback(ctx, new CertificateVerifier() {
+ @Override
+ public boolean verify(long ssl, byte[][] chain, String auth) {
+ X509Certificate[] peerCerts = certificates(chain);
+ try {
+ manager.checkClientTrusted(peerCerts, auth);
+ return true;
+ } catch (Exception e) {
+ log.debug(sm.getString("openssl.certificateVerificationFailed"), e);
+ }
+ return false;
+ }
+ });
+ }
+
+ if (negotiableProtocols != null && negotiableProtocols.size() > 0) {
+ ArrayList<String> protocols = new ArrayList<>();
+ protocols.addAll(negotiableProtocols);
+ protocols.add("http/1.1");
+ String[] protocolsArray = protocols.toArray(new String[0]);
+ SSLContext.setAlpnProtos(ctx, protocolsArray, SSL.SSL_SELECTOR_FAILURE_NO_ADVERTISE);
+ SSLContext.setNpnProtos(ctx, protocolsArray, SSL.SSL_SELECTOR_FAILURE_NO_ADVERTISE);
+ }
+
+ sessionContext = new OpenSSLSessionContext(ctx);
+ sslHostConfig.setOpenSslContext(Long.valueOf(ctx));
+ initialized = true;
+ } catch (Exception e) {
+ log.warn(sm.getString("openssl.errorSSLCtxInit"), e);
+ destroy();
+ }
+ }
+
+ private static X509KeyManager chooseKeyManager(KeyManager[] managers) throws Exception {
+ for (KeyManager manager : managers) {
+ if (manager instanceof JSSEKeyManager) {
+ return (JSSEKeyManager) manager;
+ }
+ }
+ for (KeyManager manager : managers) {
+ if (manager instanceof X509KeyManager) {
+ return (X509KeyManager) manager;
+ }
+ }
+ throw new IllegalStateException(sm.getString("openssl.keyManagerMissing"));
+ }
+
+ private static X509TrustManager chooseTrustManager(TrustManager[] managers) {
+ for (TrustManager m : managers) {
+ if (m instanceof X509TrustManager) {
+ return (X509TrustManager) m;
+ }
+ }
+ 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;
+ }
+
+ @Override
+ public SSLSessionContext getServerSessionContext() {
+ return sessionContext;
+ }
+
+ @Override
+ public SSLEngine createSSLEngine() {
+ return new OpenSSLEngine(ctx, defaultProtocol, false, sessionContext,
+ (negotiableProtocols != null && negotiableProtocols.size() > 0));
+ }
+
+ @Override
+ public SSLServerSocketFactory getServerSocketFactory() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public SSLParameters getSupportedSSLParameters() {
+ throw new UnsupportedOperationException();
+ }
+
+}
diff --git a/java/org/apache/tomcat/util/net/openssl/OpenSSLEngine.java b/java/org/apache/tomcat/util/net/openssl/OpenSSLEngine.java
new file mode 100644
index 0000000..5e1e11b
--- /dev/null
+++ b/java/org/apache/tomcat/util/net/openssl/OpenSSLEngine.java
@@ -0,0 +1,1298 @@
+/*
+ * 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;
+
+import java.nio.ByteBuffer;
+import java.nio.ReadOnlyBufferException;
+import java.security.Principal;
+import java.security.cert.Certificate;
+import java.util.ArrayList;
+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;
+import java.util.Set;
+
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLEngineResult;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLPeerUnverifiedException;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSessionBindingEvent;
+import javax.net.ssl.SSLSessionBindingListener;
+import javax.net.ssl.SSLSessionContext;
+import javax.security.cert.CertificateException;
+import javax.security.cert.X509Certificate;
+
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
+import org.apache.tomcat.jni.Buffer;
+import org.apache.tomcat.jni.Pool;
+import org.apache.tomcat.jni.SSL;
+import org.apache.tomcat.jni.SSLContext;
+import org.apache.tomcat.util.buf.ByteBufferUtils;
+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.res.StringManager;
+
+/**
+ * Implements a {@link SSLEngine} using
+ * <a href="https://www.openssl.org/docs/crypto/BIO_s_bio.html#EXAMPLE">OpenSSL
+ * BIO abstractions</a>.
+ */
+public final class OpenSSLEngine extends SSLEngine implements SSLUtil.ProtocolInfo {
+
+ private static final Log logger = LogFactory.getLog(OpenSSLEngine.class);
+ private static final StringManager sm = StringManager.getManager(OpenSSLEngine.class);
+
+ private static final Certificate[] EMPTY_CERTIFICATES = new Certificate[0];
+
+ public static final Set<String> AVAILABLE_CIPHER_SUITES;
+
+ static {
+ final Set<String> availableCipherSuites = new LinkedHashSet<>(128);
+ final long aprPool = Pool.create(0);
+ try {
+ final long sslCtx = SSLContext.make(aprPool, SSL.SSL_PROTOCOL_ALL, SSL.SSL_MODE_SERVER);
+ try {
+ SSLContext.setOptions(sslCtx, SSL.SSL_OP_ALL);
+ SSLContext.setCipherSuite(sslCtx, "ALL");
+ final long ssl = SSL.newSSL(sslCtx, true);
+ try {
+ for (String c: SSL.getCiphers(ssl)) {
+ // Filter out bad input.
+ if (c == null || c.length() == 0 || availableCipherSuites.contains(c)) {
+ continue;
+ }
+ availableCipherSuites.add(OpenSSLCipherConfigurationParser.openSSLToJsse(c));
+ }
+ } finally {
+ SSL.freeSSL(ssl);
+ }
+ } finally {
+ SSLContext.free(sslCtx);
+ }
+ } catch (Exception e) {
+ logger.warn(sm.getString("engine.ciphersFailure"), e);
+ } finally {
+ Pool.destroy(aprPool);
+ }
+ AVAILABLE_CIPHER_SUITES = Collections.unmodifiableSet(availableCipherSuites);
+ }
+
+ 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;
+
+ // Protocols
+ static final int VERIFY_DEPTH = 10;
+
+ private static final String[] IMPLEMENTED_PROTOCOLS = {
+ 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
+ };
+ public static final Set<String> IMPLEMENTED_PROTOCOLS_SET =
+ Collections.unmodifiableSet(new HashSet<>(Arrays.asList(IMPLEMENTED_PROTOCOLS)));
+
+ // Header (5) + Data (2^14) + Compression (1024) + Encryption (1024) + MAC (20) + Padding (256)
+ static final int MAX_ENCRYPTED_PACKET_LENGTH = MAX_CIPHERTEXT_LENGTH + 5 + 20 + 256;
+
+ static final int MAX_ENCRYPTION_OVERHEAD_LENGTH = MAX_ENCRYPTED_PACKET_LENGTH - MAX_PLAINTEXT_LENGTH;
+
+ enum ClientAuthMode {
+ NONE,
+ OPTIONAL,
+ REQUIRE,
+ }
+
+ private static final String INVALID_CIPHER = "SSL_NULL_WITH_NULL_NULL";
+
+ private static final long EMPTY_ADDR = Buffer.address(ByteBuffer.allocate(0));
+
+ // OpenSSL state
+ private long ssl;
+ private long networkBIO;
+
+ /**
+ * 0 - not accepted, 1 - accepted implicitly via wrap()/unwrap(), 2 -
+ * accepted explicitly via beginHandshake() call
+ */
+ private int accepted;
+ private boolean handshakeFinished;
+ private int currentHandshake;
+ private boolean receivedShutdown;
+ private volatile boolean destroyed;
+
+ // Use an invalid cipherSuite until the handshake is completed
+ // See http://docs.oracle.com/javase/7/docs/api/javax/net/ssl/SSLEngine.html#getSession()
+ private volatile String cipher;
+ private volatile String applicationProtocol;
+
+ private volatile Certificate[] peerCerts;
+ private volatile X509Certificate[] x509PeerCerts;
+ private volatile ClientAuthMode clientAuth = ClientAuthMode.NONE;
+
+ // SSL Engine status variables
+ private boolean isInboundDone;
+ private boolean isOutboundDone;
+ private boolean engineClosed;
+ private boolean sendHandshakeError = false;
+
+ private final boolean clientMode;
+ private final String fallbackApplicationProtocol;
+ private final OpenSSLSessionContext sessionContext;
+ private final boolean alpn;
+
+ private String selectedProtocol = null;
+
+ private final OpenSSLSession session;
+
+ /**
+ * Creates a new instance
+ *
+ * @param sslCtx an OpenSSL {@code SSL_CTX} object
+ * @param alloc the {@link ByteBufAllocator} that will be used by this
+ * engine
+ * @param clientMode {@code true} if this is used for clients, {@code false}
+ * otherwise
+ * @param sessionContext the {@link OpenSslSessionContext} this
+ * {@link SSLEngine} belongs to.
+ */
+ OpenSSLEngine(long sslCtx, String fallbackApplicationProtocol,
+ boolean clientMode, OpenSSLSessionContext sessionContext, boolean alpn) {
+ if (sslCtx == 0) {
+ throw new IllegalArgumentException(sm.getString("engine.noSSLContext"));
+ }
+ session = new OpenSSLSession();
+ destroyed = true;
+ ssl = SSL.newSSL(sslCtx, !clientMode);
+ networkBIO = SSL.makeNetworkBIO(ssl);
+ destroyed = false;
+ this.fallbackApplicationProtocol = fallbackApplicationProtocol;
+ this.clientMode = clientMode;
+ this.sessionContext = sessionContext;
+ this.alpn = alpn;
+ }
+
+ @Override
+ public String getNegotiatedProtocol() {
+ return selectedProtocol;
+ }
+
+ /**
+ * Destroys this engine.
+ */
+ public synchronized void shutdown() {
+ if (!destroyed) {
+ destroyed = true;
+ SSL.freeBIO(networkBIO);
+ SSL.freeSSL(ssl);
+ ssl = networkBIO = 0;
+
+ // internal errors can cause shutdown without marking the engine closed
+ isInboundDone = isOutboundDone = engineClosed = true;
+ }
+ }
+
+ /**
+ * Write plain text data to the OpenSSL internal BIO
+ *
+ * Calling this function with src.remaining == 0 is undefined.
+ */
+ private int writePlaintextData(final ByteBuffer src) {
+ final int pos = src.position();
+ final int limit = src.limit();
+ final int len = Math.min(limit - pos, MAX_PLAINTEXT_LENGTH);
+ final int sslWrote;
+
+ if (src.isDirect()) {
+ final long addr = Buffer.address(src) + pos;
+ sslWrote = SSL.writeToSSL(ssl, addr, len);
+ if (sslWrote >= 0) {
+ src.position(pos + sslWrote);
+ return sslWrote;
+ }
+ } else {
+ ByteBuffer buf = ByteBuffer.allocateDirect(len);
+ try {
+ final long addr = memoryAddress(buf);
+
+ src.limit(pos + len);
+
+ buf.put(src);
+ src.limit(limit);
+
+ sslWrote = SSL.writeToSSL(ssl, addr, len);
+ if (sslWrote >= 0) {
+ src.position(pos + sslWrote);
+ return sslWrote;
+ } else {
+ src.position(pos);
+ }
+ } finally {
+ buf.clear();
+ ByteBufferUtils.cleanDirectBuffer(buf);
+ }
+ }
+
+ throw new IllegalStateException(
+ sm.getString("engine.writeToSSLFailed", Integer.toString(sslWrote)));
+ }
+
+ /**
+ * Write encrypted data to the OpenSSL network BIO.
+ */
+ private int writeEncryptedData(final ByteBuffer src) {
+ final int pos = src.position();
+ final int len = src.remaining();
+ if (src.isDirect()) {
+ final long addr = Buffer.address(src) + pos;
+ final int netWrote = SSL.writeToBIO(networkBIO, addr, len);
+ if (netWrote >= 0) {
+ src.position(pos + netWrote);
+ return netWrote;
+ }
+ } else {
+ ByteBuffer buf = ByteBuffer.allocateDirect(len);
+ try {
+ final long addr = memoryAddress(buf);
+
+ buf.put(src);
+
+ final int netWrote = SSL.writeToBIO(networkBIO, addr, len);
+ if (netWrote >= 0) {
+ src.position(pos + netWrote);
+ return netWrote;
+ } else {
+ src.position(pos);
+ }
+ } finally {
+ buf.clear();
+ ByteBufferUtils.cleanDirectBuffer(buf);
+ }
+ }
+
+ return -1;
+ }
+
+ /**
+ * Read plain text data from the OpenSSL internal BIO
+ */
+ private int readPlaintextData(final ByteBuffer dst) {
+ if (dst.isDirect()) {
+ final int pos = dst.position();
+ final long addr = Buffer.address(dst) + pos;
+ final int len = dst.limit() - pos;
+ final int sslRead = SSL.readFromSSL(ssl, addr, len);
+ if (sslRead > 0) {
+ dst.position(pos + sslRead);
+ return sslRead;
+ }
+ } else {
+ final int pos = dst.position();
+ final int limit = dst.limit();
+ final int len = Math.min(MAX_ENCRYPTED_PACKET_LENGTH, limit - pos);
+ final ByteBuffer buf = ByteBuffer.allocateDirect(len);
+ try {
+ final long addr = memoryAddress(buf);
+
+ final int sslRead = SSL.readFromSSL(ssl, addr, len);
+ if (sslRead > 0) {
+ buf.limit(sslRead);
+ dst.limit(pos + sslRead);
+ dst.put(buf);
+ dst.limit(limit);
+ return sslRead;
+ }
+ } finally {
+ buf.clear();
+ ByteBufferUtils.cleanDirectBuffer(buf);
+ }
+ }
+
+ return 0;
+ }
+
+ /**
+ * Read encrypted data from the OpenSSL network BIO
+ */
+ private int readEncryptedData(final ByteBuffer dst, final int pending) {
+ if (dst.isDirect() && dst.remaining() >= pending) {
+ final int pos = dst.position();
+ final long addr = Buffer.address(dst) + pos;
+ final int bioRead = SSL.readFromBIO(networkBIO, addr, pending);
+ if (bioRead > 0) {
+ dst.position(pos + bioRead);
+ return bioRead;
+ }
+ } else {
+ final ByteBuffer buf = ByteBuffer.allocateDirect(pending);
+ try {
+ final long addr = memoryAddress(buf);
+
+ final int bioRead = SSL.readFromBIO(networkBIO, addr, pending);
+ if (bioRead > 0) {
+ buf.limit(bioRead);
+ int oldLimit = dst.limit();
+ dst.limit(dst.position() + bioRead);
+ dst.put(buf);
+ dst.limit(oldLimit);
+ return bioRead;
+ }
+ } finally {
+ buf.clear();
+ ByteBufferUtils.cleanDirectBuffer(buf);
+ }
+ }
+
+ return 0;
+ }
+
+ @Override
+ 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);
+ }
+
+ // Throw required runtime exceptions
+ if (srcs == null || dst == null) {
+ 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)));
+ }
+ if (dst.isReadOnly()) {
+ throw new ReadOnlyBufferException();
+ }
+
+ // Prepare OpenSSL to work in server mode and receive handshake
+ if (accepted == 0) {
+ beginHandshakeImplicitly();
+ }
+
+ // In handshake or close_notify stages, check if call to wrap was made
+ // without regard to the handshake status.
+ SSLEngineResult.HandshakeStatus handshakeStatus = getHandshakeStatus();
+
+ if ((!handshakeFinished || engineClosed) && handshakeStatus == SSLEngineResult.HandshakeStatus.NEED_UNWRAP) {
+ return new SSLEngineResult(getEngineStatus(), SSLEngineResult.HandshakeStatus.NEED_UNWRAP, 0, 0);
+ }
+
+ int bytesProduced = 0;
+ int pendingNet;
+
+ // Check for pending data in the network BIO
+ pendingNet = SSL.pendingWrittenBytesInBIO(networkBIO);
+ if (pendingNet > 0) {
+ // Do we have enough room in destination to write encrypted data?
+ int capacity = dst.remaining();
+ if (capacity < pendingNet) {
+ return new SSLEngineResult(SSLEngineResult.Status.BUFFER_OVERFLOW, handshakeStatus, 0, 0);
+ }
+
+ // Write the pending data from the network BIO into the dst buffer
+ try {
+ bytesProduced = readEncryptedData(dst, pendingNet);
+ } catch (Exception e) {
+ throw new SSLException(e);
+ }
+
+ // If isOuboundDone is set, then the data from the network BIO
+ // was the close_notify message -- we are not required to wait
+ // for the receipt the peer's close_notify message -- shutdown.
+ if (isOutboundDone) {
+ shutdown();
+ }
+
+ return new SSLEngineResult(getEngineStatus(), getHandshakeStatus(), 0, bytesProduced);
+ }
+
+ // There was no pending data in the network BIO -- encrypt any application data
+ int bytesConsumed = 0;
+ int endOffset = offset + length;
+ for (int i = offset; i < endOffset; ++i) {
+ final ByteBuffer src = srcs[i];
+ if (src == null) {
+ throw new IllegalArgumentException(sm.getString("engine.nullBufferInArray"));
+ }
+ while (src.hasRemaining()) {
+
+ // Write plain text application data to the SSL engine
+ try {
+ bytesConsumed += writePlaintextData(src);
+ } catch (Exception e) {
+ throw new SSLException(e);
+ }
+
+ // Check to see if the engine wrote data into the network BIO
+ pendingNet = SSL.pendingWrittenBytesInBIO(networkBIO);
+ if (pendingNet > 0) {
+ // 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);
+ }
+
+ // Write the pending data from the network BIO into the dst buffer
+ try {
+ bytesProduced += readEncryptedData(dst, pendingNet);
+ } catch (Exception e) {
+ throw new SSLException(e);
+ }
+
+ return new SSLEngineResult(getEngineStatus(), getHandshakeStatus(), bytesConsumed, bytesProduced);
+ }
+ }
+ }
+ return new SSLEngineResult(getEngineStatus(), getHandshakeStatus(), bytesConsumed, bytesProduced);
+ }
+
+ @Override
+ 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);
+ }
+
+ // Throw required runtime exceptions
+ if (src == null || dsts == null) {
+ 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)));
+ }
+ int capacity = 0;
+ final int endOffset = offset + length;
+ for (int i = offset; i < endOffset; i++) {
+ ByteBuffer dst = dsts[i];
+ if (dst == null) {
+ throw new IllegalArgumentException(sm.getString("engine.nullBufferInArray"));
+ }
+ if (dst.isReadOnly()) {
+ throw new ReadOnlyBufferException();
+ }
+ capacity += dst.remaining();
+ }
+
+ // Prepare OpenSSL to work in server mode and receive handshake
+ if (accepted == 0) {
+ beginHandshakeImplicitly();
+ }
+
+ // In handshake or close_notify stages, check if call to unwrap was made
+ // without regard to the handshake status.
+ SSLEngineResult.HandshakeStatus handshakeStatus = getHandshakeStatus();
+ if ((!handshakeFinished || engineClosed) && handshakeStatus == SSLEngineResult.HandshakeStatus.NEED_WRAP) {
+ return new SSLEngineResult(getEngineStatus(), SSLEngineResult.HandshakeStatus.NEED_WRAP, 0, 0);
+ }
+
+ int len = src.remaining();
+
+ // protect against protocol overflow attack vector
+ if (len > MAX_ENCRYPTED_PACKET_LENGTH) {
+ isInboundDone = true;
+ isOutboundDone = true;
+ engineClosed = true;
+ shutdown();
+ throw new SSLException(sm.getString("engine.oversizedPacket"));
+ }
+
+ // Write encrypted data to network BIO
+ int written = -1;
+ try {
+ written = writeEncryptedData(src);
+ } catch (Exception e) {
+ throw new SSLException(e);
+ }
+ // OpenSSL can return 0 or -1 to these calls if nothing was written
+ if (written < 0) {
+ written = 0;
+ }
+
+ // There won't be any application data until we're done handshaking
+ //
+ // We first check handshakeFinished to eliminate the overhead of extra JNI call if possible.
+ int pendingApp = pendingReadableBytesInSSL();
+ if (!handshakeFinished) {
+ pendingApp = 0;
+ }
+ int bytesProduced = 0;
+ int idx = offset;
+ // Do we have enough room in dsts to write decrypted data?
+ if (capacity < pendingApp) {
+ return new SSLEngineResult(SSLEngineResult.Status.BUFFER_OVERFLOW, getHandshakeStatus(), written, 0);
+ }
+
+ while (pendingApp > 0) {
+ // Write decrypted data to dsts buffers
+ while (idx < endOffset) {
+ ByteBuffer dst = dsts[idx];
+ if (!dst.hasRemaining()) {
+ idx++;
+ continue;
+ }
+
+ if (pendingApp <= 0) {
+ break;
+ }
+
+ int bytesRead;
+ try {
+ bytesRead = readPlaintextData(dst);
+ } catch (Exception e) {
+ throw new SSLException(e);
+ }
+
+ if (bytesRead == 0) {
+ break;
+ }
+
+ bytesProduced += bytesRead;
+ pendingApp -= bytesRead;
+ capacity -= bytesRead;
+
+ if (!dst.hasRemaining()) {
+ idx++;
+ }
+ }
+ if (capacity == 0) {
+ break;
+ } else if (pendingApp == 0) {
+ pendingApp = pendingReadableBytesInSSL();
+ }
+ }
+
+ // Check to see if we received a close_notify message from the peer
+ if (!receivedShutdown && (SSL.getShutdown(ssl) & SSL.SSL_RECEIVED_SHUTDOWN) == SSL.SSL_RECEIVED_SHUTDOWN) {
+ receivedShutdown = true;
+ closeOutbound();
+ closeInbound();
+ }
+ if (bytesProduced == 0 && written == 0) {
+ return new SSLEngineResult(SSLEngineResult.Status.BUFFER_UNDERFLOW, getHandshakeStatus(), 0, 0);
+ } else {
+ return new SSLEngineResult(getEngineStatus(), getHandshakeStatus(), written, bytesProduced);
+ }
+ }
+
+ 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/ssl/SSL_pending.html
+ int lastPrimingReadResult = SSL.readFromSSL(ssl, EMPTY_ADDR, 0); // priming read
+ // check if SSL_read returned <= 0. In this case we need to check the error and see if it was something
+ // fatal.
+ if (lastPrimingReadResult <= 0) {
+ checkLastError();
+ }
+ return SSL.pendingReadableBytesInSSL(ssl);
+ }
+
+ @Override
+ public Runnable getDelegatedTask() {
+ // Currently, we do not delegate SSL computation tasks
+ return null;
+ }
+
+ @Override
+ public synchronized void closeInbound() throws SSLException {
+ if (isInboundDone) {
+ return;
+ }
+
+ isInboundDone = true;
+ engineClosed = true;
+
+ shutdown();
+
+ if (accepted != 0 && !receivedShutdown) {
+ throw new SSLException(sm.getString("engine.inboundClose"));
+ }
+ }
+
+ @Override
+ public synchronized boolean isInboundDone() {
+ return isInboundDone || engineClosed;
+ }
+
+ @Override
+ public synchronized void closeOutbound() {
+ if (isOutboundDone) {
+ return;
+ }
+
+ isOutboundDone = true;
+ engineClosed = true;
+
+ if (accepted != 0 && !destroyed) {
+ int mode = SSL.getShutdown(ssl);
+ if ((mode & SSL.SSL_SENT_SHUTDOWN) != SSL.SSL_SENT_SHUTDOWN) {
+ SSL.shutdownSSL(ssl);
+ }
+ } else {
+ // engine closing before initial handshake
+ shutdown();
+ }
+ }
+
+ @Override
+ public synchronized boolean isOutboundDone() {
+ return isOutboundDone;
+ }
+
+ @Override
+ public String[] getSupportedCipherSuites() {
+ Set<String> availableCipherSuites = AVAILABLE_CIPHER_SUITES;
+ return availableCipherSuites.toArray(new String[availableCipherSuites.size()]);
+ }
+
+ @Override
+ public String[] getEnabledCipherSuites() {
+ String[] enabled = SSL.getCiphers(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;
+ }
+ }
+ return enabled;
+ }
+ }
+
+ @Override
+ public void setEnabledCipherSuites(String[] cipherSuites) {
+ if (cipherSuites == null) {
+ throw new IllegalArgumentException(sm.getString("engine.nullCipherSuite"));
+ }
+ final StringBuilder buf = new StringBuilder();
+ for (String cipherSuite : cipherSuites) {
+ if (cipherSuite == null) {
+ break;
+ }
+ String converted = OpenSSLCipherConfigurationParser.jsseToOpenSSL(cipherSuite);
+ if (converted != null) {
+ cipherSuite = converted;
+ }
+ if (!AVAILABLE_CIPHER_SUITES.contains(cipherSuite)) {
+ logger.debug(sm.getString("engine.unsupportedCipher", cipherSuite, converted));
+ }
+
+ buf.append(cipherSuite);
+ buf.append(':');
+ }
+
+ if (buf.length() == 0) {
+ throw new IllegalArgumentException(sm.getString("engine.emptyCipherSuite"));
+ }
+ buf.setLength(buf.length() - 1);
+
+ final String cipherSuiteSpec = buf.toString();
+ try {
+ SSL.setCipherSuites(ssl, cipherSuiteSpec);
+ } catch (Exception e) {
+ throw new IllegalStateException(sm.getString("engine.failedCipherSuite", cipherSuiteSpec), e);
+ }
+ }
+
+ @Override
+ public String[] getSupportedProtocols() {
+ return IMPLEMENTED_PROTOCOLS.clone();
+ }
+
+ @Override
+ public String[] getEnabledProtocols() {
+ List<String> enabled = new ArrayList<>();
+ // Seems like there is no way to explicitly disable SSLv2Hello in OpenSSL so it is always enabled
+ enabled.add(Constants.SSL_PROTO_SSLv2Hello);
+ int opts = SSL.getOptions(ssl);
+ if ((opts & SSL.SSL_OP_NO_TLSv1) == 0) {
+ enabled.add(Constants.SSL_PROTO_TLSv1);
+ }
+ if ((opts & SSL.SSL_OP_NO_TLSv1_1) == 0) {
+ enabled.add(Constants.SSL_PROTO_TLSv1_1);
+ }
+ if ((opts & SSL.SSL_OP_NO_TLSv1_2) == 0) {
+ enabled.add(Constants.SSL_PROTO_TLSv1_2);
+ }
+ if ((opts & SSL.SSL_OP_NO_SSLv2) == 0) {
+ enabled.add(Constants.SSL_PROTO_SSLv2);
+ }
+ if ((opts & SSL.SSL_OP_NO_SSLv3) == 0) {
+ enabled.add(Constants.SSL_PROTO_SSLv3);
+ }
+ int size = enabled.size();
+ if (size == 0) {
+ return new String[0];
+ } else {
+ return enabled.toArray(new String[size]);
+ }
+ }
+
+ @Override
+ public void setEnabledProtocols(String[] protocols) {
+ if (protocols == null) {
+ // This is correct from the API docs
+ throw new IllegalArgumentException();
+ }
+ boolean sslv2 = false;
+ boolean sslv3 = false;
+ boolean tlsv1 = false;
+ boolean tlsv1_1 = false;
+ boolean tlsv1_2 = false;
+ for (String p : protocols) {
+ 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;
+ }
+ }
+ // Enable all and then disable what we not want
+ SSL.setOptions(ssl, SSL.SSL_OP_ALL);
+
+ if (!sslv2) {
+ SSL.setOptions(ssl, SSL.SSL_OP_NO_SSLv2);
+ }
+ if (!sslv3) {
+ SSL.setOptions(ssl, SSL.SSL_OP_NO_SSLv3);
+ }
+ if (!tlsv1) {
+ SSL.setOptions(ssl, SSL.SSL_OP_NO_TLSv1);
+ }
+ if (!tlsv1_1) {
+ SSL.setOptions(ssl, SSL.SSL_OP_NO_TLSv1_1);
+ }
+ if (!tlsv1_2) {
+ SSL.setOptions(ssl, SSL.SSL_OP_NO_TLSv1_2);
+ }
+ }
+
+ @Override
+ public SSLSession getSession() {
+ return session;
+ }
+
+ @Override
+ public synchronized void beginHandshake() throws SSLException {
+ if (engineClosed || destroyed) {
+ throw new SSLException(sm.getString("engine.engineClosed"));
+ }
+ switch (accepted) {
+ case 0:
+ handshake();
+ accepted = 2;
+ break;
+ case 1:
+ // A user did not start handshake by calling this method by him/herself,
+ // 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, he or she never asked
+ // for renegotiation.
+
+ accepted = 2; // Next time this method is invoked by the user, we should raise an exception.
+ break;
+ case 2:
+ renegotiate();
+ break;
+ default:
+ throw new Error();
+ }
+ }
+
+ private void beginHandshakeImplicitly() throws SSLException {
+ handshake();
+ accepted = 1;
+ }
+
+ private void handshake() throws SSLException {
+ currentHandshake = SSL.getHandshakeCount(ssl);
+ int code = SSL.doHandshake(ssl);
+ if (code <= 0) {
+ checkLastError();
+ } else {
+ if (alpn) {
+ selectedProtocol = SSL.getAlpnSelected(ssl);
+ if (selectedProtocol == null) {
+ selectedProtocol = SSL.getNextProtoNegotiated(ssl);
+ }
+ }
+ session.lastAccessedTime = System.currentTimeMillis();
+ // if SSL_do_handshake returns > 0 it means the handshake was finished. This means we can update
+ // handshakeFinished directly and so eliminate unnecessary calls to SSL.isInInit(...)
+ handshakeFinished = true;
+ }
+ }
+
+ private synchronized void renegotiate() throws SSLException {
+ int code = SSL.renegotiate(ssl);
+ if (code <= 0) {
+ checkLastError();
+ }
+ handshakeFinished = false;
+ peerCerts = null;
+ x509PeerCerts = null;
+ currentHandshake = SSL.getHandshakeCount(ssl);
+ int code2 = SSL.doHandshake(ssl);
+ if (code2 <= 0) {
+ checkLastError();
+ }
+ }
+
+ private void checkLastError() throws SSLException {
+ long error = SSL.getLastErrorNumber();
+ if (error != SSL.SSL_ERROR_NONE) {
+ String err = SSL.getErrorString(error);
+ if (logger.isDebugEnabled()) {
+ logger.debug(sm.getString("engine.openSSLError", Long.toString(error), err));
+ }
+ // Many errors can occur during handshake and need to be reported
+ if (!handshakeFinished) {
+ sendHandshakeError = true;
+ } else {
+ throw new SSLException(err);
+ }
+ }
+ }
+
+ private static long memoryAddress(ByteBuffer buf) {
+ return Buffer.address(buf);
+ }
+
+ private SSLEngineResult.Status getEngineStatus() {
+ return engineClosed ? SSLEngineResult.Status.CLOSED : SSLEngineResult.Status.OK;
+ }
+
+ @Override
+ public synchronized SSLEngineResult.HandshakeStatus getHandshakeStatus() {
+ if (accepted == 0 || destroyed) {
+ return SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING;
+ }
+
+ // Check if we are in the initial handshake phase
+ if (!handshakeFinished) {
+
+ // There is pending data in the network BIO -- call wrap
+ if (sendHandshakeError || SSL.pendingWrittenBytesInBIO(networkBIO) != 0) {
+ if (sendHandshakeError) {
+ // After a last wrap, consider it is going to be done
+ sendHandshakeError = false;
+ currentHandshake++;
+ }
+ return SSLEngineResult.HandshakeStatus.NEED_WRAP;
+ }
+
+ // No pending data to be sent to the peer
+ // Check to see if we have finished handshaking
+ int handshakeCount = SSL.getHandshakeCount(ssl);
+ if (handshakeCount != currentHandshake) {
+ if (alpn) {
+ selectedProtocol = SSL.getAlpnSelected(ssl);
+ if (selectedProtocol == null) {
+ selectedProtocol = SSL.getNextProtoNegotiated(ssl);
+ }
+ }
+ session.lastAccessedTime = System.currentTimeMillis();
+ handshakeFinished = true;
+ return SSLEngineResult.HandshakeStatus.FINISHED;
+ }
+
+ // No pending data and still handshaking
+ // Must be waiting on the peer to send more data
+ return SSLEngineResult.HandshakeStatus.NEED_UNWRAP;
+ }
+
+ // Check if we are in the shutdown phase
+ if (engineClosed) {
+ // Waiting to send the close_notify message
+ if (SSL.pendingWrittenBytesInBIO(networkBIO) != 0) {
+ return SSLEngineResult.HandshakeStatus.NEED_WRAP;
+ }
+
+ // Must be waiting to receive the close_notify message
+ return SSLEngineResult.HandshakeStatus.NEED_UNWRAP;
+ }
+
+ return SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING;
+ }
+
+ @Override
+ public void setUseClientMode(boolean clientMode) {
+ if (clientMode != this.clientMode) {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ @Override
+ public boolean getUseClientMode() {
+ return clientMode;
+ }
+
+ @Override
+ public void setNeedClientAuth(boolean b) {
+ setClientAuth(b ? ClientAuthMode.REQUIRE : ClientAuthMode.NONE);
+ }
+
+ @Override
+ public boolean getNeedClientAuth() {
+ return clientAuth == ClientAuthMode.REQUIRE;
+ }
+
+ @Override
+ public void setWantClientAuth(boolean b) {
+ setClientAuth(b ? ClientAuthMode.OPTIONAL : ClientAuthMode.NONE);
+ }
+
+ @Override
+ public boolean getWantClientAuth() {
+ return clientAuth == ClientAuthMode.OPTIONAL;
+ }
+
+ private void setClientAuth(ClientAuthMode mode) {
+ if (clientMode) {
+ return;
+ }
+ synchronized (this) {
+ if (clientAuth == mode) {
+ // No need to issue any JNI calls if the mode is the same
+ return;
+ }
+ switch (mode) {
+ case NONE:
+ SSL.setVerify(ssl, SSL.SSL_CVERIFY_NONE, VERIFY_DEPTH);
+ break;
+ case REQUIRE:
+ SSL.setVerify(ssl, SSL.SSL_CVERIFY_REQUIRE, VERIFY_DEPTH);
+ break;
+ case OPTIONAL:
+ SSL.setVerify(ssl, SSL.SSL_CVERIFY_OPTIONAL, VERIFY_DEPTH);
+ break;
+ }
+ clientAuth = mode;
+ }
+ }
+
+ @Override
+ public void setEnableSessionCreation(boolean b) {
+ if (b) {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ @Override
+ public boolean getEnableSessionCreation() {
+ return false;
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ super.finalize();
+ // Call shutdown as the user may have created the OpenSslEngine and not used it at all.
+ shutdown();
+ }
+
+ private class OpenSSLSession implements SSLSession {
+
+ // lazy init for memory reasons
+ private Map<String, Object> values;
+
+ // Last accessed time
+ private long lastAccessedTime = -1;
+
+ @Override
+ public byte[] getId() {
+ // We don't cache that to keep memory usage to a minimum.
+ byte[] id = SSL.getSessionId(ssl);
+ if (id == null) {
+ // The id should never be null, if it was null then the SESSION itself was not valid.
+ throw new IllegalStateException(sm.getString("engine.noSession"));
+ }
+ return id;
+ }
+
+ @Override
+ public SSLSessionContext getSessionContext() {
+ return sessionContext;
+ }
+
+ @Override
+ public long getCreationTime() {
+ // We need to multiply by 1000 as OpenSSL uses seconds and we need milliseconds.
+ return SSL.getTime(ssl) * 1000L;
+ }
+
+ @Override
+ public long getLastAccessedTime() {
+ return (lastAccessedTime > 0) ? lastAccessedTime : getCreationTime();
+ }
+
+ @Override
+ public void invalidate() {
+ // NOOP
+ }
+
+ @Override
+ public boolean isValid() {
+ return false;
+ }
+
+ @Override
+ public void putValue(String name, Object value) {
+ if (name == null) {
+ throw new IllegalArgumentException(sm.getString("engine.nullName"));
+ }
+ if (value == null) {
+ throw new IllegalArgumentException(sm.getString("engine.nullValue"));
+ }
+ Map<String, Object> values = this.values;
+ if (values == null) {
+ // Use size of 2 to keep the memory overhead small
+ values = this.values = new HashMap<>(2);
+ }
+ Object old = values.put(name, value);
+ if (value instanceof SSLSessionBindingListener) {
+ ((SSLSessionBindingListener) value).valueBound(new SSLSessionBindingEvent(this, name));
+ }
+ notifyUnbound(old, name);
+ }
+
+ @Override
+ public Object getValue(String name) {
+ if (name == null) {
+ throw new IllegalArgumentException(sm.getString("engine.nullName"));
+ }
+ if (values == null) {
+ return null;
+ }
+ return values.get(name);
+ }
+
+ @Override
+ public void removeValue(String name) {
+ if (name == null) {
+ throw new IllegalArgumentException(sm.getString("engine.nullName"));
+ }
+ Map<String, Object> values = this.values;
+ if (values == null) {
+ return;
+ }
+ Object old = values.remove(name);
+ notifyUnbound(old, name);
+ }
+
+ @Override
+ public String[] getValueNames() {
+ Map<String, Object> values = this.values;
+ if (values == null || values.isEmpty()) {
+ return new String[0];
+ }
+ return values.keySet().toArray(new String[values.size()]);
+ }
+
+ private void notifyUnbound(Object value, String name) {
+ if (value instanceof SSLSessionBindingListener) {
+ ((SSLSessionBindingListener) value).valueUnbound(new SSLSessionBindingEvent(this, name));
+ }
+ }
+
+ @Override
+ public Certificate[] getPeerCertificates() throws SSLPeerUnverifiedException {
+ // these are lazy created to reduce memory overhead
+ Certificate[] c = peerCerts;
+ if (c == null) {
+ if (SSL.isInInit(ssl) != 0) {
+ throw new SSLPeerUnverifiedException(sm.getString("engine.unverifiedPeer"));
+ }
+ byte[][] chain = SSL.getPeerCertChain(ssl);
+ byte[] clientCert;
+ if (!clientMode) {
+ // 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 = SSL.getPeerCertificate(ssl);
+ } else {
+ clientCert = null;
+ }
+ if (chain == null && clientCert == null) {
+ return null;
+ }
+ int len = 0;
+ if (chain != null) {
+ len += chain.length;
+ }
+
+ int i = 0;
+ Certificate[] certificates;
+ if (clientCert != null) {
+ len++;
+ certificates = new Certificate[len];
+ certificates[i++] = new OpenSslX509Certificate(clientCert);
+ } else {
+ certificates = new Certificate[len];
+ }
+ if (chain != null) {
+ int a = 0;
+ for (; i < certificates.length; i++) {
+ certificates[i] = new OpenSslX509Certificate(chain[a++]);
+ }
+ }
+ c = peerCerts = certificates;
+ }
+ return c;
+ }
+
+ @Override
+ public Certificate[] getLocalCertificates() {
+ // FIXME (if possible): Not available in the OpenSSL API
+ return EMPTY_CERTIFICATES;
+ }
+
+ @Override
+ public X509Certificate[] getPeerCertificateChain() throws SSLPeerUnverifiedException {
+ // these are lazy created to reduce memory overhead
+ X509Certificate[] c = x509PeerCerts;
+ if (c == null) {
+ if (SSL.isInInit(ssl) != 0) {
+ throw new SSLPeerUnverifiedException(sm.getString("engine.unverifiedPeer"));
+ }
+ byte[][] chain = SSL.getPeerCertChain(ssl);
+ if (chain == null) {
+ throw new SSLPeerUnverifiedException(sm.getString("engine.unverifiedPeer"));
+ }
+ X509Certificate[] peerCerts = new X509Certificate[chain.length];
+ for (int i = 0; i < peerCerts.length; i++) {
+ try {
+ peerCerts[i] = X509Certificate.getInstance(chain[i]);
+ } catch (CertificateException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+ c = x509PeerCerts = peerCerts;
+ }
+ return c;
+ }
+
+ @Override
+ public Principal getPeerPrincipal() throws SSLPeerUnverifiedException {
+ Certificate[] peer = getPeerCertificates();
+ if (peer == null || peer.length == 0) {
+ return null;
+ }
+ return principal(peer);
+ }
+
+ @Override
+ public Principal getLocalPrincipal() {
+ Certificate[] local = getLocalCertificates();
+ if (local == null || local.length == 0) {
+ return null;
+ }
+ return principal(local);
+ }
+
+ private Principal principal(Certificate[] certs) {
+ return ((java.security.cert.X509Certificate) certs[0]).getIssuerX500Principal();
+ }
+
+ @Override
+ public String getCipherSuite() {
+ if (!handshakeFinished) {
+ return INVALID_CIPHER;
+ }
+ if (cipher == null) {
+ String c = OpenSSLCipherConfigurationParser.openSSLToJsse(SSL.getCipherForSSL(ssl));
+ if (c != null) {
+ cipher = c;
+ }
+ }
+ return cipher;
+ }
+
+ @Override
+ public String getProtocol() {
+ String applicationProtocol = OpenSSLEngine.this.applicationProtocol;
+ if (applicationProtocol == null) {
+ applicationProtocol = SSL.getNextProtoNegotiated(ssl);
+ if (applicationProtocol == null) {
+ applicationProtocol = fallbackApplicationProtocol;
+ }
+ if (applicationProtocol != null) {
+ OpenSSLEngine.this.applicationProtocol = applicationProtocol.replace(':', '_');
+ } else {
+ OpenSSLEngine.this.applicationProtocol = applicationProtocol = "";
+ }
+ }
+ String version = SSL.getVersion(ssl);
+ if (applicationProtocol.isEmpty()) {
+ return version;
+ } else {
+ return version + ':' + applicationProtocol;
+ }
+ }
+
+ @Override
+ public String getPeerHost() {
+ // Not available for now in Tomcat (needs to be passed during engine creation)
+ return null;
+ }
+
+ @Override
+ public int getPeerPort() {
+ // Not available for now in Tomcat (needs to be passed during engine creation)
+ return 0;
+ }
+
+ @Override
+ public int getPacketBufferSize() {
+ return MAX_ENCRYPTED_PACKET_LENGTH;
+ }
+
+ @Override
+ public int getApplicationBufferSize() {
+ return MAX_PLAINTEXT_LENGTH;
+ }
+
+ }
+
+}
diff --git a/java/org/apache/tomcat/util/net/openssl/OpenSSLImplementation.java b/java/org/apache/tomcat/util/net/openssl/OpenSSLImplementation.java
new file mode 100644
index 0000000..6f2c3bf
--- /dev/null
+++ b/java/org/apache/tomcat/util/net/openssl/OpenSSLImplementation.java
@@ -0,0 +1,39 @@
+/*
+ * 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;
+
+import javax.net.ssl.SSLSession;
+
+import org.apache.tomcat.util.net.SSLHostConfigCertificate;
+import org.apache.tomcat.util.net.SSLImplementation;
+import org.apache.tomcat.util.net.SSLSupport;
+import org.apache.tomcat.util.net.SSLUtil;
+import org.apache.tomcat.util.net.jsse.JSSESupport;
+
+public class OpenSSLImplementation extends SSLImplementation {
+
+ @Override
+ public SSLSupport getSSLSupport(SSLSession session) {
+ return new JSSESupport(session);
+ }
+
+ @Override
+ public SSLUtil getSSLUtil(SSLHostConfigCertificate certificate) {
+ return new OpenSSLUtil(certificate);
+ }
+
+}
diff --git a/java/org/apache/tomcat/util/net/openssl/OpenSSLKeyManager.java b/java/org/apache/tomcat/util/net/openssl/OpenSSLKeyManager.java
new file mode 100644
index 0000000..4b11104
--- /dev/null
+++ b/java/org/apache/tomcat/util/net/openssl/OpenSSLKeyManager.java
@@ -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.net.openssl;
+
+import java.io.File;
+
+import javax.net.ssl.KeyManager;
+
+public class OpenSSLKeyManager implements KeyManager{
+
+ private File certificateChain;
+ public File getCertificateChain() { return certificateChain; }
+ public void setCertificateChain(File certificateChain) { this.certificateChain = certificateChain; }
+
+ private File privateKey;
+ public File getPrivateKey() { return privateKey; }
+ public void setPrivateKey(File privateKey) { this.privateKey = privateKey; }
+
+ OpenSSLKeyManager(String certChainFile, String keyFile) {
+ if (certChainFile == null) {
+ return;
+ }
+ if (keyFile == null) {
+ return;
+ }
+ this.certificateChain = new File(certChainFile);
+ this.privateKey = new File(keyFile);
+ }
+}
diff --git a/java/org/apache/tomcat/util/net/openssl/OpenSSLProtocols.java b/java/org/apache/tomcat/util/net/openssl/OpenSSLProtocols.java
new file mode 100644
index 0000000..4a892a7
--- /dev/null
+++ b/java/org/apache/tomcat/util/net/openssl/OpenSSLProtocols.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tomcat.util.net.openssl;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.tomcat.util.net.Constants;
+
+/**
+ * Get SSL protocols in the right preference order
+ */
+public class OpenSSLProtocols {
+
+ private List<String> openSSLProtocols = new ArrayList<>();
+
+ public OpenSSLProtocols(String preferredJSSEProto) {
+ Collections.addAll(openSSLProtocols, Constants.SSL_PROTO_TLSv1_2,
+ Constants.SSL_PROTO_TLSv1_1, Constants.SSL_PROTO_TLSv1,
+ Constants.SSL_PROTO_SSLv3, Constants.SSL_PROTO_SSLv2);
+ if(openSSLProtocols.contains(preferredJSSEProto)) {
+ openSSLProtocols.remove(preferredJSSEProto);
+ openSSLProtocols.add(0, preferredJSSEProto);
+ }
+ }
+
+ public String[] getProtocols() {
+ return openSSLProtocols.toArray(new String[openSSLProtocols.size()]);
+ }
+}
diff --git a/java/org/apache/tomcat/util/net/openssl/OpenSSLSessionContext.java b/java/org/apache/tomcat/util/net/openssl/OpenSSLSessionContext.java
new file mode 100644
index 0000000..ef52079
--- /dev/null
+++ b/java/org/apache/tomcat/util/net/openssl/OpenSSLSessionContext.java
@@ -0,0 +1,141 @@
+/*
+ * 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;
+
+import java.util.Enumeration;
+import java.util.NoSuchElementException;
+
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSessionContext;
+
+import org.apache.tomcat.jni.SSL;
+import org.apache.tomcat.jni.SSLContext;
+import org.apache.tomcat.util.res.StringManager;
+
+/**
+ * OpenSSL specific {@link SSLSessionContext} implementation.
+ */
+public class OpenSSLSessionContext implements SSLSessionContext {
+ private static final StringManager sm = StringManager.getManager(OpenSSLSessionContext.class);
+ private static final Enumeration<byte[]> EMPTY = new EmptyEnumeration();
+
+ private final OpenSSLSessionStats stats;
+ private final long context;
+
+ OpenSSLSessionContext(long context) {
+ this.context = context;
+ stats = new OpenSSLSessionStats(context);
+ }
+
+ @Override
+ public SSLSession getSession(byte[] bytes) {
+ return null;
+ }
+
+ @Override
+ public Enumeration<byte[]> getIds() {
+ return EMPTY;
+ }
+
+ /**
+ * Sets the SSL session ticket keys of this context.
+ *
+ * @param keys The session ticket keys
+ */
+ public void setTicketKeys(byte[] keys) {
+ if (keys == null) {
+ throw new IllegalArgumentException(sm.getString("sessionContext.nullTicketKeys"));
+ }
+ SSLContext.setSessionTicketKeys(context, keys);
+ }
+
+ /**
+ * Enable or disable caching of SSL sessions.
+ *
+ * @param enabled {@code true} to enable caching, {@code false} to disable
+ */
+ public void setSessionCacheEnabled(boolean enabled) {
+ long mode = enabled ? SSL.SSL_SESS_CACHE_SERVER : SSL.SSL_SESS_CACHE_OFF;
+ SSLContext.setSessionCacheMode(context, mode);
+ }
+
+ /**
+ * @return {@code true} if caching of SSL sessions is enabled, {@code false}
+ * otherwise.
+ */
+ public boolean isSessionCacheEnabled() {
+ return SSLContext.getSessionCacheMode(context) == SSL.SSL_SESS_CACHE_SERVER;
+ }
+
+ /**
+ * @return The statistics for this context.
+ */
+ public OpenSSLSessionStats stats() {
+ return stats;
+ }
+
+ @Override
+ public void setSessionTimeout(int seconds) {
+ if (seconds < 0) {
+ throw new IllegalArgumentException();
+ }
+ SSLContext.setSessionCacheTimeout(context, seconds);
+ }
+
+ @Override
+ public int getSessionTimeout() {
+ return (int) SSLContext.getSessionCacheTimeout(context);
+ }
+
+ @Override
+ public void setSessionCacheSize(int size) {
+ if (size < 0) {
+ throw new IllegalArgumentException();
+ }
+ SSLContext.setSessionCacheSize(context, size);
+ }
+
+ @Override
+ public int getSessionCacheSize() {
+ return (int) SSLContext.getSessionCacheSize(context);
+ }
+
+ /**
+ * Set the context within which session be reused (server side only)
+ * See <a href="http://www.openssl.org/docs/ssl/SSL_CTX_set_session_id_context.html">
+ * man SSL_CTX_set_session_id_context</a>
+ *
+ * @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) {
+ return SSLContext.setSessionIdContext(context, sidCtx);
+ }
+
+ private static final class EmptyEnumeration implements Enumeration<byte[]> {
+ @Override
+ public boolean hasMoreElements() {
+ return false;
+ }
+
+ @Override
+ public byte[] nextElement() {
+ throw new NoSuchElementException();
+ }
+ }
+}
diff --git a/java/org/apache/tomcat/util/net/openssl/OpenSSLSessionStats.java b/java/org/apache/tomcat/util/net/openssl/OpenSSLSessionStats.java
new file mode 100644
index 0000000..12c44f7
--- /dev/null
+++ b/java/org/apache/tomcat/util/net/openssl/OpenSSLSessionStats.java
@@ -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 org.apache.tomcat.util.net.openssl;
+
+import org.apache.tomcat.jni.SSLContext;
+
+/**
+ * Stats exposed by an OpenSSL session context.
+ *
+ * @see <a href="https://www.openssl.org/docs/ssl/SSL_CTX_sess_number.html"><code>SSL_CTX_sess_number</code></a>
+ */
+public final class OpenSSLSessionStats {
+
+ private final long context;
+
+ OpenSSLSessionStats(long context) {
+ this.context = context;
+ }
+
+ /**
+ * @return The current number of sessions in the internal session cache.
+ */
+ public long number() {
+ return SSLContext.sessionNumber(context);
+ }
+
+ /**
+ * @return The number of started SSL/TLS handshakes in client mode.
+ */
+ public long connect() {
+ return SSLContext.sessionConnect(context);
+ }
+
+ /**
+ * @return The number of successfully established SSL/TLS sessions in client mode.
+ */
+ public long connectGood() {
+ return SSLContext.sessionConnectGood(context);
+ }
+
+ /**
+ * @return The number of start renegotiations in client mode.
+ */
+ public long connectRenegotiate() {
+ return SSLContext.sessionConnectRenegotiate(context);
+ }
+
+ /**
+ * @return The number of started SSL/TLS handshakes in server mode.
+ */
+ public long accept() {
+ return SSLContext.sessionAccept(context);
+ }
+
+ /**
+ * @return The number of successfully established SSL/TLS sessions in server mode.
+ */
+ public long acceptGood() {
+ return SSLContext.sessionAcceptGood(context);
+ }
+
+ /**
+ * @return The number of start renegotiations in server mode.
+ */
+ public long acceptRenegotiate() {
+ return SSLContext.sessionAcceptRenegotiate(context);
+ }
+
+ /**
+ * @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.
+ */
+ 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.
+ */
+ 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.
+ */
+ public long timeouts() {
+ return SSLContext.sessionTimeouts(context);
+ }
+
+ /**
+ * @return The number of sessions that were removed because the maximum
+ * session cache size was exceeded.
+ */
+ public long cacheFull() {
+ return SSLContext.sessionCacheFull(context);
+ }
+}
diff --git a/java/org/apache/tomcat/util/net/openssl/OpenSSLUtil.java b/java/org/apache/tomcat/util/net/openssl/OpenSSLUtil.java
new file mode 100644
index 0000000..2dd465e
--- /dev/null
+++ b/java/org/apache/tomcat/util/net/openssl/OpenSSLUtil.java
@@ -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.tomcat.util.net.openssl;
+
+import java.util.List;
+import java.util.Set;
+
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.SSLSessionContext;
+import javax.net.ssl.TrustManager;
+
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
+import org.apache.tomcat.util.net.SSLContext;
+import org.apache.tomcat.util.net.SSLHostConfig;
+import org.apache.tomcat.util.net.SSLHostConfigCertificate;
+import org.apache.tomcat.util.net.SSLUtilBase;
+import org.apache.tomcat.util.net.jsse.JSSEUtil;
+
+public class OpenSSLUtil extends SSLUtilBase {
+
+ private static final Log log = LogFactory.getLog(OpenSSLUtil.class);
+
+ private final JSSEUtil jsseUtil;
+
+ public OpenSSLUtil(SSLHostConfigCertificate certificate) {
+ super(certificate);
+
+ if (certificate.getCertificateFile() == null) {
+ // Using JSSE configuration for keystore and truststore
+ jsseUtil = new JSSEUtil(certificate);
+ } else {
+ // Use OpenSSL configuration for certificates
+ jsseUtil = null;
+ }
+ }
+
+
+ @Override
+ protected Log getLog() {
+ return log;
+ }
+
+
+ @Override
+ protected Set<String> getImplementedProtocols() {
+ return OpenSSLEngine.IMPLEMENTED_PROTOCOLS_SET;
+ }
+
+
+ @Override
+ protected Set<String> getImplementedCiphers() {
+ return OpenSSLEngine.AVAILABLE_CIPHER_SUITES;
+ }
+
+
+ @Override
+ public SSLContext createSSLContext(List<String> negotiableProtocols) throws Exception {
+ return new OpenSSLContext(certificate, negotiableProtocols);
+ }
+
+ @Override
+ public KeyManager[] getKeyManagers() throws Exception {
+ if (jsseUtil != null) {
+ return jsseUtil.getKeyManagers();
+ } else {
+ // Return something although it is not actually used
+ KeyManager[] managers = {
+ new OpenSSLKeyManager(SSLHostConfig.adjustRelativePath(certificate.getCertificateFile()),
+ SSLHostConfig.adjustRelativePath(certificate.getCertificateKeyFile()))
+ };
+ return managers;
+ }
+ }
+
+ @Override
+ public TrustManager[] getTrustManagers() throws Exception {
+ if (jsseUtil != null) {
+ return jsseUtil.getTrustManagers();
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public void configureSessionContext(SSLSessionContext sslSessionContext) {
+ if (jsseUtil != null) {
+ jsseUtil.configureSessionContext(sslSessionContext);
+ }
+ }
+}
diff --git a/java/org/apache/tomcat/util/net/openssl/OpenSSLX509Certificate.java b/java/org/apache/tomcat/util/net/openssl/OpenSSLX509Certificate.java
new file mode 100644
index 0000000..54f8307
--- /dev/null
+++ b/java/org/apache/tomcat/util/net/openssl/OpenSSLX509Certificate.java
@@ -0,0 +1,190 @@
+/*
+ * 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;
+
+import java.io.ByteArrayInputStream;
+import java.math.BigInteger;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.Principal;
+import java.security.PublicKey;
+import java.security.SignatureException;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateExpiredException;
+import java.security.cert.CertificateNotYetValidException;
+import java.security.cert.X509Certificate;
+import java.util.Date;
+import java.util.Set;
+
+final class OpenSslX509Certificate extends X509Certificate {
+
+ private final byte[] bytes;
+ private X509Certificate wrapped;
+
+ public OpenSslX509Certificate(byte[] bytes) {
+ this.bytes = bytes;
+ }
+
+ @Override
+ public void checkValidity() throws CertificateExpiredException, CertificateNotYetValidException {
+ unwrap().checkValidity();
+ }
+
+ @Override
+ public void checkValidity(Date date) throws CertificateExpiredException, CertificateNotYetValidException {
+ unwrap().checkValidity(date);
+ }
+
+ @Override
+ public int getVersion() {
+ return unwrap().getVersion();
+ }
+
+ @Override
+ public BigInteger getSerialNumber() {
+ return unwrap().getSerialNumber();
+ }
+
+ @Override
+ public Principal getIssuerDN() {
+ return unwrap().getIssuerDN();
+ }
+
+ @Override
+ public Principal getSubjectDN() {
+ return unwrap().getSubjectDN();
+ }
+
+ @Override
+ public Date getNotBefore() {
+ return unwrap().getNotBefore();
+ }
+
+ @Override
+ public Date getNotAfter() {
+ return unwrap().getNotAfter();
+ }
+
+ @Override
+ public byte[] getTBSCertificate() throws CertificateEncodingException {
+ return unwrap().getTBSCertificate();
+ }
+
+ @Override
+ public byte[] getSignature() {
+ return unwrap().getSignature();
+ }
+
+ @Override
+ public String getSigAlgName() {
+ return unwrap().getSigAlgName();
+ }
+
+ @Override
+ public String getSigAlgOID() {
+ return unwrap().getSigAlgOID();
+ }
+
+ @Override
+ public byte[] getSigAlgParams() {
+ return unwrap().getSigAlgParams();
+ }
+
+ @Override
+ public boolean[] getIssuerUniqueID() {
+ return unwrap().getIssuerUniqueID();
+ }
+
+ @Override
+ public boolean[] getSubjectUniqueID() {
+ return unwrap().getSubjectUniqueID();
+ }
+
+ @Override
+ public boolean[] getKeyUsage() {
+ return unwrap().getKeyUsage();
+ }
+
+ @Override
+ public int getBasicConstraints() {
+ return unwrap().getBasicConstraints();
+ }
+
+ @Override
+ public byte[] getEncoded() {
+ return bytes.clone();
+ }
+
+ @Override
+ 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 {
+ unwrap().verify(key, sigProvider);
+ }
+
+ @Override
+ public String toString() {
+ return unwrap().toString();
+ }
+
+ @Override
+ public PublicKey getPublicKey() {
+ return unwrap().getPublicKey();
+ }
+
+ @Override
+ public boolean hasUnsupportedCriticalExtension() {
+ return unwrap().hasUnsupportedCriticalExtension();
+ }
+
+ @Override
+ public Set<String> getCriticalExtensionOIDs() {
+ return unwrap().getCriticalExtensionOIDs();
+ }
+
+ @Override
+ public Set<String> getNonCriticalExtensionOIDs() {
+ return unwrap().getNonCriticalExtensionOIDs();
+ }
+
+ @Override
+ public byte[] getExtensionValue(String oid) {
+ return unwrap().getExtensionValue(oid);
+ }
+
+ private X509Certificate unwrap() {
+ X509Certificate wrapped = this.wrapped;
+ if (wrapped == null) {
+ try {
+ wrapped = this.wrapped = (X509Certificate) OpenSSLContext.X509_CERT_FACTORY.generateCertificate(
+ new ByteArrayInputStream(bytes));
+ } catch (CertificateException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+ return wrapped;
+ }
+}
diff --git a/java/org/apache/tomcat/util/net/openssl/ciphers/Authentication.java b/java/org/apache/tomcat/util/net/openssl/ciphers/Authentication.java
new file mode 100644
index 0000000..658a916
--- /dev/null
+++ b/java/org/apache/tomcat/util/net/openssl/ciphers/Authentication.java
@@ -0,0 +1,33 @@
+/*
+ * 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;
+
+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 */,
+ GOST94 /* GOST R 34.10-94 signature auth */,
+ GOST01 /* GOST R 34.10-2001 */,
+ FZA /* Fortezza */,
+ SRP
+}
diff --git a/java/org/apache/tomcat/util/net/openssl/ciphers/Cipher.java b/java/org/apache/tomcat/util/net/openssl/ciphers/Cipher.java
new file mode 100644
index 0000000..717b715
--- /dev/null
+++ b/java/org/apache/tomcat/util/net/openssl/ciphers/Cipher.java
@@ -0,0 +1,4747 @@
+/*
+ * 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.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * All the standard cipher suites for SSL/TSL.
+ *
+ * @see <a href="https://github.com/openssl/openssl/blob/master/ssl/s3_lib.c"
+ * >OpenSSL cipher definitions</a>
+ * @see <a href="http://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-4"
+ * >The cipher suite registry</a>
+ * @see <a href="https://www.thesprawl.org/research/tls-and-ssl-cipher-suites/"
+ * >Another list of cipher suites with some non-standard IDs</a>
+ * @see <a href="http://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#ciphersuites"
+ * >Oracle standard names for cipher suites</a>
+ * @see <a href="https://www.openssl.org/docs/apps/ciphers.html"
+ * >Mapping of OpenSSL cipher suites names to registry names</a>
+ * @see <a href="https://github.com/ssllabs/sslhaf/blob/0.1.x/suites.csv"
+ * >SSL Labs tool - list of ciphers</a>
+ * @see <a href="http://hg.openjdk.java.net/jdk9/jdk9/jdk/file/e30cd0d37abf/src/java.base/share/classes/sun/security/ssl/CipherSuite.java"
+ * >OpenJDK source code</a>
+ */
+public enum Cipher {
+
+ /* Cipher 0
+ * TLS_NULL_WITH_NULL_NULL
+ * Must never be negotiated. Used internally to represent the initial
+ * unprotected state of a connection.
+ */
+
+ /* The RSA ciphers */
+ // Cipher 01
+ TLS_RSA_WITH_NULL_MD5(
+ 0x0001,
+ "NULL-MD5",
+ KeyExchange.RSA,
+ Authentication.RSA,
+ Encryption.eNULL,
+ MessageDigest.MD5,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.STRONG_NONE,
+ false,
+ 0,
+ 0,
+ new String[] {"SSL_RSA_WITH_NULL_MD5"},
+ null
+ ),
+ // Cipher 02
+ TLS_RSA_WITH_NULL_SHA(
+ 0x0002,
+ "NULL-SHA",
+ KeyExchange.RSA,
+ Authentication.RSA,
+ Encryption.eNULL,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.STRONG_NONE,
+ true,
+ 0,
+ 0,
+ new String[] {"SSL_RSA_WITH_NULL_SHA"},
+ null
+ ),
+ // Cipher 03
+ TLS_RSA_EXPORT_WITH_RC4_40_MD5(
+ 0x0003,
+ "EXP-RC4-MD5",
+ KeyExchange.RSA,
+ Authentication.RSA,
+ Encryption.RC4,
+ MessageDigest.MD5,
+ Protocol.SSLv3,
+ true,
+ EncryptionLevel.EXP40,
+ false,
+ 40,
+ 128,
+ new String[] {"SSL_RSA_EXPORT_WITH_RC4_40_MD5"},
+ null
+ ),
+ // Cipher 04
+ TLS_RSA_WITH_RC4_128_MD5(
+ 0x0004,
+ "RC4-MD5",
+ KeyExchange.RSA,
+ Authentication.RSA,
+ Encryption.RC4,
+ MessageDigest.MD5,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.MEDIUM,
+ false,
+ 128,
+ 128,
+ new String[] {"SSL_RSA_WITH_RC4_128_MD5"},
+ null
+ ),
+ // Cipher 05
+ TLS_RSA_WITH_RC4_128_SHA(
+ 0x0005,
+ "RC4-SHA",
+ KeyExchange.RSA,
+ Authentication.RSA,
+ Encryption.RC4,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.MEDIUM,
+ false,
+ 128,
+ 128,
+ new String[] {"SSL_RSA_WITH_RC4_128_SHA"},
+ null
+ ),
+ // Cipher 06
+ TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5(
+ 0x0006,
+ "EXP-RC2-CBC-MD5",
+ KeyExchange.RSA,
+ Authentication.RSA,
+ Encryption.RC2,
+ MessageDigest.MD5,
+ Protocol.SSLv3,
+ true,
+ EncryptionLevel.EXP40,
+ false,
+ 40,
+ 128,
+ new String[] {"SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5"},
+ null
+ ),
+ // Cipher 07
+ TLS_RSA_WITH_IDEA_CBC_SHA(
+ 0x0007,
+ "IDEA-CBC-SHA",
+ KeyExchange.RSA,
+ Authentication.RSA,
+ Encryption.IDEA,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.MEDIUM,
+ false,
+ 128,
+ 128,
+ new String[] {"SSL_RSA_WITH_IDEA_CBC_SHA"},
+ null
+ ),
+ // Cipher 08
+ TLS_RSA_EXPORT_WITH_DES40_CBC_SHA(
+ 0x0008,
+ "EXP-DES-CBC-SHA",
+ KeyExchange.RSA,
+ Authentication.RSA,
+ Encryption.DES,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ true,
+ EncryptionLevel.EXP40,
+ false,
+ 40,
+ 56,
+ new String[] {"SSL_RSA_EXPORT_WITH_DES40_CBC_SHA"},
+ null
+ ),
+ // Cipher 09
+ TLS_RSA_WITH_DES_CBC_SHA(
+ 0x0009,
+ "DES-CBC-SHA",
+ KeyExchange.RSA,
+ Authentication.RSA,
+ Encryption.DES,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.LOW,
+ false,
+ 56,
+ 56,
+ new String[] {"SSL_RSA_WITH_DES_CBC_SHA"},
+ null
+ ),
+ // Cipher 0A
+ TLS_RSA_WITH_3DES_EDE_CBC_SHA(
+ 0x000A,
+ "DES-CBC3-SHA",
+ KeyExchange.RSA,
+ Authentication.RSA,
+ Encryption.TRIPLE_DES,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.MEDIUM,
+ true,
+ 112,
+ 168,
+ new String[] {"SSL_RSA_WITH_3DES_EDE_CBC_SHA"},
+ null
+ ),
+ /* The DH ciphers */
+ // Cipher 0B
+ TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA(
+ 0x000B,
+ "EXP-DH-DSS-DES-CBC-SHA",
+ KeyExchange.DHd,
+ Authentication.DH,
+ Encryption.DES,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ true,
+ EncryptionLevel.EXP40,
+ false,
+ 40,
+ 56,
+ new String[] {"SSL_DH_DSS_EXPORT_WITH_DES40_CBC_SHA"},
+ null
+ ),
+ // Cipher 0C
+ TLS_DH_DSS_WITH_DES_CBC_SHA(
+ 0x000C,
+ "DH-DSS-DES-CBC-SHA",
+ KeyExchange.DHd,
+ Authentication.DH,
+ Encryption.DES,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.LOW,
+ false,
+ 56,
+ 56,
+ new String[] {"SSL_DH_DSS_WITH_DES_CBC_SHA"},
+ null
+ ),
+ // Cipher 0D
+ TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA(
+ 0x000D,
+ "DH-DSS-DES-CBC3-SHA",
+ KeyExchange.DHd,
+ Authentication.DH,
+ Encryption.TRIPLE_DES,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.MEDIUM,
+ true,
+ 112,
+ 168,
+ new String[] {"SSL_DH_DSS_WITH_3DES_EDE_CBC_SHA"},
+ null
+ ),
+ // Cipher 0E
+ TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA(
+ 0x000E,
+ "EXP-DH-RSA-DES-CBC-SHA",
+ KeyExchange.DHr,
+ Authentication.DH,
+ Encryption.DES,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ true,
+ EncryptionLevel.EXP40,
+ false,
+ 40,
+ 56,
+ new String[] {"SSL_DH_RSA_EXPORT_WITH_DES40_CBC_SHA"},
+ null
+ ),
+ // Cipher 0F
+ TLS_DH_RSA_WITH_DES_CBC_SHA(
+ 0x000F,
+ "DH-RSA-DES-CBC-SHA",
+ KeyExchange.DHr,
+ Authentication.DH,
+ Encryption.DES,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.LOW,
+ false,
+ 56,
+ 56,
+ new String[] {"SSL_DH_RSA_WITH_DES_CBC_SHA"},
+ null
+ ),
+ // Cipher 10
+ TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA(
+ 0x0010,
+ "DH-RSA-DES-CBC3-SHA",
+ KeyExchange.DHr,
+ Authentication.DH,
+ Encryption.TRIPLE_DES,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.MEDIUM,
+ true,
+ 112,
+ 168,
+ new String[] {"SSL_DH_RSA_WITH_3DES_EDE_CBC_SHA"},
+ null
+ ),
+ /* The Ephemeral DH ciphers */
+ // Cipher 11
+ TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA(
+ 0x0011,
+ "EXP-DHE-DSS-DES-CBC-SHA",
+ KeyExchange.EDH,
+ Authentication.DSS,
+ Encryption.DES,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ true,
+ EncryptionLevel.EXP40,
+ false,
+ 40,
+ 56,
+ new String[] {"SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA"},
+ new String[] {"EXP-EDH-DSS-DES-CBC-SHA"}
+ ),
+ // Cipher 12
+ TLS_DHE_DSS_WITH_DES_CBC_SHA(
+ 0x0012,
+ "DHE-DSS-DES-CBC-SHA",
+ KeyExchange.EDH,
+ Authentication.DSS,
+ Encryption.DES,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.LOW,
+ false,
+ 56,
+ 56,
+ new String[] {"SSL_DHE_DSS_WITH_DES_CBC_SHA"},
+ new String[] {"EDH-DSS-DES-CBC-SHA"}
+ ),
+ // Cipher 13
+ TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA(
+ 0x0013,
+ "DHE-DSS-DES-CBC3-SHA",
+ KeyExchange.EDH,
+ Authentication.DSS,
+ Encryption.TRIPLE_DES,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.MEDIUM,
+ true,
+ 112,
+ 168,
+ new String[] {"SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA"},
+ new String[] {"EDH-DSS-DES-CBC3-SHA"}
+ ),
+ // Cipher 14
+ TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA(
+ 0x0014,
+ "EXP-DHE-RSA-DES-CBC-SHA",
+ KeyExchange.EDH,
+ Authentication.RSA,
+ Encryption.DES,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ true,
+ EncryptionLevel.EXP40,
+ false,
+ 40,
+ 56,
+ new String[] {"SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA"},
+ new String[] {"EXP-EDH-RSA-DES-CBC-SHA"}
+ ),
+ // Cipher 15
+ TLS_DHE_RSA_WITH_DES_CBC_SHA(
+ 0x0015,
+ "DHE-RSA-DES-CBC-SHA",
+ KeyExchange.EDH,
+ Authentication.RSA,
+ Encryption.DES,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.LOW,
+ false,
+ 56,
+ 56,
+ new String[] {"SSL_DHE_RSA_WITH_DES_CBC_SHA"},
+ new String[] {"EDH-RSA-DES-CBC-SHA"}
+ ),
+ // Cipher 16
+ TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA(
+ 0x0016,
+ "DHE-RSA-DES-CBC3-SHA",
+ KeyExchange.EDH,
+ Authentication.RSA,
+ Encryption.TRIPLE_DES,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.MEDIUM,
+ true,
+ 112,
+ 168,
+ new String[] {"SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA"},
+ new String[] {"EDH-RSA-DES-CBC3-SHA"}
+ ),
+ // Cipher 17
+ TLS_DH_anon_EXPORT_WITH_RC4_40_MD5(
+ 0x0017,
+ "EXP-ADH-RC4-MD5",
+ KeyExchange.EDH,
+ Authentication.aNULL,
+ Encryption.RC4,
+ MessageDigest.MD5,
+ Protocol.SSLv3,
+ true,
+ EncryptionLevel.EXP40,
+ false,
+ 40,
+ 128,
+ new String[] {"SSL_DH_anon_EXPORT_WITH_RC4_40_MD5"},
+ null
+ ),
+ // Cipher 18
+ TLS_DH_anon_WITH_RC4_128_MD5(
+ 0x0018,
+ "ADH-RC4-MD5",
+ KeyExchange.EDH,
+ Authentication.aNULL,
+ Encryption.RC4,
+ MessageDigest.MD5,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.MEDIUM,
+ false,
+ 128,
+ 128,
+ new String[] {"SSL_DH_anon_WITH_RC4_128_MD5"},
+ null
+ ),
+ // Cipher 19
+ TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA(
+ 0x0019,
+ "EXP-ADH-DES-CBC-SHA",
+ KeyExchange.EDH,
+ Authentication.aNULL,
+ Encryption.DES,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ true,
+ EncryptionLevel.EXP40,
+ false,
+ 40,
+ 128,
+ new String[] {"SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA"},
+ null
+ ),
+ // Cipher 1A
+ TLS_DH_anon_WITH_DES_CBC_SHA(
+ 0x001A,
+ "ADH-DES-CBC-SHA",
+ KeyExchange.EDH,
+ Authentication.aNULL,
+ Encryption.DES,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.LOW,
+ false,
+ 56,
+ 56,
+ new String[] {"SSL_DH_anon_WITH_DES_CBC_SHA"},
+ null
+ ),
+ // Cipher 1B
+ TLS_DH_anon_WITH_3DES_EDE_CBC_SHA(
+ 0x001B,
+ "ADH-DES-CBC3-SHA",
+ KeyExchange.EDH,
+ Authentication.aNULL,
+ Encryption.TRIPLE_DES,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.MEDIUM,
+ true,
+ 112,
+ 168,
+ new String[] {"SSL_DH_anon_WITH_3DES_EDE_CBC_SHA"},
+ null
+ ),
+ /* Fortezza ciphersuite 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
+ SSL_FORTEZZA_DMS_WITH_NULL_SHA(
+ "FZA-NULL-SHA",
+ KeyExchange.FZA,
+ Authentication.FZA,
+ Encryption.eNULL,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.STRONG_NONE,
+ false,
+ 0,
+ 0,
+ null,
+ null
+ ),
+ // Cipher 1D
+ SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA(
+ "FZA-FZA-CBC-SHA",
+ KeyExchange.FZA,
+ Authentication.FZA,
+ Encryption.FZA,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.STRONG_NONE,
+ false,
+ 0,
+ 0,
+ null,
+ null
+ ),
+ // Cipher 1E - overlaps with Kerberos below
+ SSL_FORTEZZA_DMS_WITH_RC4_128_SHA(
+ "FZA-RC4-SHA",
+ KeyExchange.FZA,
+ Authentication.FZA,
+ Encryption.RC4,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.MEDIUM,
+ false,
+ 128,
+ 128,
+ null,
+ null
+ ),
+ */
+ /* The Kerberos ciphers. OpenSSL doesn't support these. Java does but they
+ * are used for Kerberos authentication.
+ */
+ // Cipher 1E - overlaps with Fortezza above
+ /*TLS_KRB5_WITH_DES_CBC_SHA(
+ "KRB5-DES-CBC-SHA",
+ KeyExchange.KRB5,
+ Authentication.KRB5,
+ Encryption.DES,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.LOW,
+ false,
+ 56,
+ 56,
+ null,
+ null
+ ),
+ // Cipher 1F
+ TLS_KRB5_WITH_3DES_EDE_CBC_SHA(
+ "KRB5-DES-CBC3-SHA",
+ KeyExchange.KRB5,
+ Authentication.KRB5,
+ Encryption.TRIPLE_DES,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.HIGH,
+ true,
+ 112,
+ 168,
+ null,
+ null
+ ),
+ // Cipher 20
+ TLS_KRB5_WITH_RC4_128_SHA(
+ "KRB5-RC4-SHA",
+ KeyExchange.KRB5,
+ Authentication.KRB5,
+ Encryption.RC4,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.MEDIUM,
+ false,
+ 128,
+ 128,
+ null,
+ null
+ ),
+ // Cipher 21
+ TLS_KRB5_WITH_IDEA_CBC_SHA(
+ "KRB5-IDEA-CBC-SHA",
+ KeyExchange.KRB5,
+ Authentication.KRB5,
+ Encryption.IDEA,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.MEDIUM,
+ false,
+ 128,
+ 128,
+ null,
+ null
+ ),
+ // Cipher 22
+ TLS_KRB5_WITH_DES_CBC_MD5(
+ "KRB5-DES-CBC-MD5",
+ KeyExchange.KRB5,
+ Authentication.KRB5,
+ Encryption.DES,
+ MessageDigest.MD5,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.LOW,
+ false,
+ 56,
+ 56,
+ null,
+ null
+ ),
+ // Cipher 23
+ TLS_KRB5_WITH_3DES_EDE_CBC_MD5(
+ "KRB5-DES-CBC3-MD5",
+ KeyExchange.KRB5,
+ Authentication.KRB5,
+ Encryption.TRIPLE_DES,
+ MessageDigest.MD5,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.HIGH,
+ false,
+ 112,
+ 168,
+ null,
+ null
+ ),
+ // Cipher 24
+ TLS_KRB5_WITH_RC4_128_MD5(
+ "KRB5-RC4-MD5",
+ KeyExchange.KRB5,
+ Authentication.KRB5,
+ Encryption.RC4,
+ MessageDigest.MD5,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.MEDIUM,
+ false,
+ 128,
+ 128,
+ null,
+ null
+ ),
+ // Cipher 25
+ TLS_KRB5_WITH_IDEA_CBC_MD5(
+ "KRB5-IDEA-CBC-MD5",
+ KeyExchange.KRB5,
+ Authentication.KRB5,
+ Encryption.IDEA,
+ MessageDigest.MD5,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.MEDIUM,
+ false,
+ 128,
+ 128,
+ null,
+ null
+ ),
+ // Cipher 26
+ TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA(
+ "EXP-KRB5-DES-CBC-SHA",
+ KeyExchange.KRB5,
+ Authentication.KRB5,
+ Encryption.DES,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ true,
+ EncryptionLevel.EXP40,
+ false,
+ 40,
+ 56,
+ null,
+ null
+ ),
+ // Cipher 27
+ TLS_KRB5_EXPORT_WITH_RC2_CBC_40_SHA(
+ "EXP-KRB5-RC2-CBC-SHA",
+ KeyExchange.KRB5,
+ Authentication.KRB5,
+ Encryption.RC2,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ true,
+ EncryptionLevel.EXP40,
+ false,
+ 40,
+ 128,
+ null,
+ null
+ ),
+ // Cipher 28
+ TLS_KRB5_EXPORT_WITH_RC4_40_SHA(
+ "EXP-KRB5-RC4-SHA",
+ KeyExchange.KRB5,
+ Authentication.KRB5,
+ Encryption.RC4,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ true,
+ EncryptionLevel.EXP40,
+ false,
+ 40,
+ 128,
+ null,
+ null
+ ),
+ // Cipher 29
+ TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5(
+ "EXP-KRB5-DES-CBC-MD5",
+ KeyExchange.KRB5,
+ Authentication.KRB5,
+ Encryption.DES,
+ MessageDigest.MD5,
+ Protocol.SSLv3,
+ true,
+ EncryptionLevel.EXP40,
+ false,
+ 40,
+ 56,
+ null,
+ null
+ ),
+ // Cipher 2A
+ TLS_KRB5_EXPORT_WITH_RC2_CBC_40_MD5(
+ "EXP-KRB5-RC2-CBC-MD5",
+ KeyExchange.KRB5,
+ Authentication.KRB5,
+ Encryption.RC2,
+ MessageDigest.MD5,
+ Protocol.SSLv3,
+ true,
+ EncryptionLevel.EXP40,
+ false,
+ 40,
+ 128,
+ null,
+ null
+ ),
+ // Cipher 2B
+ TLS_KRB5_EXPORT_WITH_RC4_40_MD5(
+ "EXP-KRB5-RC4-MD5",
+ KeyExchange.KRB5,
+ Authentication.KRB5,
+ Encryption.RC4,
+ MessageDigest.MD5,
+ Protocol.SSLv3,
+ true,
+ EncryptionLevel.EXP40,
+ false,
+ 40,
+ 128,
+ null,
+ null
+ ),*/
+
+ /* PSK cipher suites from RFC 4785 */
+ // Cipher 2C
+ TLS_PSK_WITH_NULL_SHA(
+ 0x002c,
+ "PSK-NULL-SHA",
+ KeyExchange.PSK,
+ Authentication.PSK,
+ Encryption.eNULL,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.STRONG_NONE,
+ true,
+ 0,
+ 0,
+ null,
+ null
+ ),
+ // Cipher 2D
+ TLS_DHE_PSK_WITH_NULL_SHA(
+ 0x002d,
+ "DHE-PSK-NULL-SHA",
+ KeyExchange.DHEPSK,
+ Authentication.PSK,
+ Encryption.eNULL,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.STRONG_NONE,
+ true,
+ 0,
+ 0,
+ null,
+ null
+ ),
+ // Cipher 2E
+ TLS_RSA_PSK_WITH_NULL_SHA(
+ 0x002e,
+ "RSA-PSK-NULL-SHA",
+ KeyExchange.RSAPSK,
+ Authentication.RSA,
+ Encryption.eNULL,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.STRONG_NONE,
+ true,
+ 0,
+ 0,
+ null,
+ null
+ ),
+ /* New AES ciphersuites */
+ // Cipher 2F
+ TLS_RSA_WITH_AES_128_CBC_SHA(
+ 0x002f,
+ "AES128-SHA",
+ KeyExchange.RSA,
+ Authentication.RSA,
+ Encryption.AES128,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.HIGH,
+ true,
+ 128,
+ 128,
+ null,
+ null
+ ),
+ // Cipher 30
+ TLS_DH_DSS_WITH_AES_128_CBC_SHA(
+ 0x0030,
+ "DH-DSS-AES128-SHA",
+ KeyExchange.DHd,
+ Authentication.DH,
+ Encryption.AES128,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.HIGH,
+ true,
+ 128,
+ 128,
+ null,
+ null
+ ),
+ // Cipher 31
+ TLS_DH_RSA_WITH_AES_128_CBC_SHA(
+ 0x0031,
+ "DH-RSA-AES128-SHA",
+ KeyExchange.DHr,
+ Authentication.DH,
+ Encryption.AES128,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.HIGH,
+ true,
+ 128,
+ 128,
+ null,
+ null
+ ),
+ // Cipher 32
+ TLS_DHE_DSS_WITH_AES_128_CBC_SHA(
+ 0x0032,
+ "DHE-DSS-AES128-SHA",
+ KeyExchange.EDH,
+ Authentication.DSS,
+ Encryption.AES128,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.HIGH,
+ true,
+ 128,
+ 128,
+ null,
+ null
+ ),
+ // Cipher 33
+ TLS_DHE_RSA_WITH_AES_128_CBC_SHA(
+ 0x0033,
+ "DHE-RSA-AES128-SHA",
+ KeyExchange.EDH,
+ Authentication.RSA,
+ Encryption.AES128,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.HIGH,
+ true,
+ 128,
+ 128,
+ null,
+ null
+ ),
+ // Cipher 34
+ TLS_DH_anon_WITH_AES_128_CBC_SHA(
+ 0x0034,
+ "ADH-AES128-SHA",
+ KeyExchange.EDH,
+ Authentication.aNULL,
+ Encryption.AES128,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.HIGH,
+ true,
+ 128,
+ 128,
+ null,
+ null
+ ),
+ // Cipher 35
+ TLS_RSA_WITH_AES_256_CBC_SHA(
+ 0x0035,
+ "AES256-SHA",
+ KeyExchange.RSA,
+ Authentication.RSA,
+ Encryption.AES256,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.HIGH,
+ true,
+ 256,
+ 256,
+ null,
+ null
+ ),
+ // Cipher 36
+ TLS_DH_DSS_WITH_AES_256_CBC_SHA(
+ 0x0036,
+ "DH-DSS-AES256-SHA",
+ KeyExchange.DHd,
+ Authentication.DH,
+ Encryption.AES256,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.HIGH,
+ true,
+ 256,
+ 256,
+ null,
+ null
+ ),
+ // Cipher 37
+ TLS_DH_RSA_WITH_AES_256_CBC_SHA(
+ 0x0037,
+ "DH-RSA-AES256-SHA",
+ KeyExchange.DHr,
+ Authentication.DH,
+ Encryption.AES256,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.HIGH,
+ true,
+ 256,
+ 256,
+ null,
+ null
+ ),
+ // Cipher 38
+ TLS_DHE_DSS_WITH_AES_256_CBC_SHA(
+ 0x0038,
+ "DHE-DSS-AES256-SHA",
+ KeyExchange.EDH,
+ Authentication.DSS,
+ Encryption.AES256,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.HIGH,
+ true,
+ 256,
+ 256,
+ null,
+ null
+ ),
+ // Cipher 39
+ TLS_DHE_RSA_WITH_AES_256_CBC_SHA(
+ 0x0039,
+ "DHE-RSA-AES256-SHA",
+ KeyExchange.EDH,
+ Authentication.RSA,
+ Encryption.AES256,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.HIGH,
+ true,
+ 256,
+ 256,
+ null,
+ null
+ ),
+ // Cipher 3A
+ TLS_DH_anon_WITH_AES_256_CBC_SHA(
+ 0x003A,
+ "ADH-AES256-SHA",
+ KeyExchange.EDH,
+ Authentication.aNULL,
+ Encryption.AES256,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.HIGH,
+ true,
+ 256,
+ 256,
+ null,
+ null
+ ),
+ /* TLS v1.2 ciphersuites */
+ // Cipher 3B
+ TLS_RSA_WITH_NULL_SHA256(
+ 0x003B,
+ "NULL-SHA256",
+ KeyExchange.RSA,
+ Authentication.RSA,
+ Encryption.eNULL,
+ MessageDigest.SHA256,
+ Protocol.TLSv1_2,
+ false,
+ EncryptionLevel.STRONG_NONE,
+ true,
+ 0,
+ 0,
+ null,
+ null
+ ),
+ // Cipher 3C
+ TLS_RSA_WITH_AES_128_CBC_SHA256(
+ 0x003C,
+ "AES128-SHA256",
+ KeyExchange.RSA,
+ Authentication.RSA,
+ Encryption.AES128,
+ MessageDigest.SHA256,
+ Protocol.TLSv1_2,
+ false,
+ EncryptionLevel.HIGH,
+ true,
+ 128,
+ 128,
+ null,
+ null
+ ),
+ // Cipher 3D
+ TLS_RSA_WITH_AES_256_CBC_SHA256(
+ 0x003D,
+ "AES256-SHA256",
+ KeyExchange.RSA,
+ Authentication.RSA,
+ Encryption.AES256,
+ MessageDigest.SHA256,
+ Protocol.TLSv1_2,
+ false,
+ EncryptionLevel.HIGH,
+ true,
+ 256,
+ 256,
+ null,
+ null
+ ),
+ // Cipher 3E
+ TLS_DH_DSS_WITH_AES_128_CBC_SHA256(
+ 0x003E,
+ "DH-DSS-AES128-SHA256",
+ KeyExchange.DHd,
+ Authentication.DH,
+ Encryption.AES128,
+ MessageDigest.SHA256,
+ Protocol.TLSv1_2,
+ false,
+ EncryptionLevel.HIGH,
+ true,
+ 128,
+ 128,
+ null,
+ null
+ ),
+ // Cipher 3F
+ TLS_DH_RSA_WITH_AES_128_CBC_SHA256(
+ 0x003F,
+ "DH-RSA-AES128-SHA256",
+ KeyExchange.DHr,
+ Authentication.DH,
+ Encryption.AES128,
+ MessageDigest.SHA256,
+ Protocol.TLSv1_2,
+ false,
+ EncryptionLevel.HIGH,
+ true,
+ 128,
+ 128,
+ null,
+ null
+ ),
+ // Cipher 40
+ TLS_DHE_DSS_WITH_AES_128_CBC_SHA256(
+ 0x0040,
+ "DHE-DSS-AES128-SHA256",
+ KeyExchange.EDH,
+ Authentication.DSS,
+ Encryption.AES128,
+ MessageDigest.SHA256,
+ Protocol.TLSv1_2,
+ false,
+ EncryptionLevel.HIGH,
+ true,
+ 128,
+ 128,
+ null,
+ null
+ ),
+ /* Camellia ciphersuites from RFC4132 (
+ 128-bit portion) */
+ // Cipher 41
+ TLS_RSA_WITH_CAMELLIA_128_CBC_SHA(
+ 0x0041,
+ "CAMELLIA128-SHA",
+ KeyExchange.RSA,
+ Authentication.RSA,
+ Encryption.CAMELLIA128,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.HIGH,
+ false,
+ 128,
+ 128,
+ null,
+ null
+ ),
+ // Cipher 42
+ TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA(
+ 0x0042,
+ "DH-DSS-CAMELLIA128-SHA",
+ KeyExchange.DHd,
+ Authentication.DH,
+ Encryption.CAMELLIA128,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.HIGH,
+ false,
+ 128,
+ 128,
+ null,
+ null
+ ),
+ // Cipher 43
+ TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA(
+ 0x0043,
+ "DH-RSA-CAMELLIA128-SHA",
+ KeyExchange.DHr,
+ Authentication.DH,
+ Encryption.CAMELLIA128,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.HIGH,
+ false,
+ 128,
+ 128,
+ null,
+ null
+ ),
+ // Cipher 44
+ TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA(
+ 0x0044,
+ "DHE-DSS-CAMELLIA128-SHA",
+ KeyExchange.EDH,
+ Authentication.DSS,
+ Encryption.CAMELLIA128,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.HIGH,
+ false,
+ 128,
+ 128,
+ null,
+ null
+ ),
+ // Cipher 45
+ TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA(
+ 0x0045,
+ "DHE-RSA-CAMELLIA128-SHA",
+ KeyExchange.EDH,
+ Authentication.RSA,
+ Encryption.CAMELLIA128,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.HIGH,
+ false,
+ 128,
+ 128,
+ null,
+ null
+ ),
+ // Cipher 46
+ TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA(
+ 0x0046,
+ "ADH-CAMELLIA128-SHA",
+ KeyExchange.EDH,
+ Authentication.aNULL,
+ Encryption.CAMELLIA128,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.HIGH,
+ false,
+ 128,
+ 128,
+ null,
+ null
+ ),
+ /* Experimental (and now expired) TLSv1 versions of SSLv3 ciphers.
+ * Unsupported by Java and OpenSSL 1.1.x onwards. Some earlier OpenSSL
+ * versions do support these. */
+ // Cipher 60
+ TLS_RSA_EXPORT1024_WITH_RC4_56_MD5(
+ 0x0060,
+ "EXP1024-RC4-MD5",
+ KeyExchange.RSA,
+ Authentication.RSA,
+ Encryption.RC4,
+ MessageDigest.MD5,
+ Protocol.TLSv1,
+ true,
+ EncryptionLevel.EXP56,
+ false,
+ 56,
+ 128,
+ new String[] {"SSL_RSA_EXPORT1024_WITH_RC4_56_MD5"},
+ null
+ ),
+ // Cipher 61
+ TLS_RSA_EXPORT1024_WITH_RC2_CBC_56_MD5(
+ 0x0061,
+ "EXP1024-RC2-CBC-MD5",
+ KeyExchange.RSA,
+ Authentication.RSA,
+ Encryption.RC2,
+ MessageDigest.MD5,
+ Protocol.TLSv1,
+ true,
+ EncryptionLevel.EXP56,
+ false,
+ 56,
+ 128,
+ new String[] {"SSL_RSA_EXPORT1024_WITH_RC2_CBC_56_MD5"},
+ null
+ ),
+ // Cipher 62
+ TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA(
+ 0x0062,
+ "EXP1024-DES-CBC-SHA",
+ KeyExchange.RSA,
+ Authentication.RSA,
+ Encryption.DES,
+ MessageDigest.SHA1,
+ Protocol.TLSv1,
+ true,
+ EncryptionLevel.EXP56,
+ false,
+ 56,
+ 56,
+ new String[] {"SSL_RSA_EXPORT1024_WITH_DES_CBC_SHA"},
+ null
+ ),
+ // Cipher 63
+ TLS_DHE_DSS_EXPORT1024_WITH_DES_CBC_SHA(
+ 0x0063,
+ "EXP1024-DHE-DSS-DES-CBC-SHA",
+ KeyExchange.EDH,
+ Authentication.DSS,
+ Encryption.DES,
+ MessageDigest.SHA1,
+ Protocol.TLSv1,
+ true,
+ EncryptionLevel.EXP56,
+ false,
+ 56,
+ 56,
+ new String[] {"SSL_DHE_DSS_EXPORT1024_WITH_DES_CBC_SHA"},
+ null
+ ),
+ // Cipher 64
+ TLS_RSA_EXPORT1024_WITH_RC4_56_SHA(
+ 0x0064,
+ "EXP1024-RC4-SHA",
+ KeyExchange.RSA,
+ Authentication.RSA,
+ Encryption.RC4,
+ MessageDigest.SHA1,
+ Protocol.TLSv1,
+ true,
+ EncryptionLevel.EXP56,
+ false,
+ 56,
+ 128,
+ new String[] {"SSL_RSA_EXPORT1024_WITH_RC4_56_SHA"},
+ null
+ ),
+ // Cipher 65
+ TLS_DHE_DSS_EXPORT1024_WITH_RC4_56_SHA(
+ 0x0065,
+ "EXP1024-DHE-DSS-RC4-SHA",
+ KeyExchange.EDH,
+ Authentication.DSS,
+ Encryption.RC4,
+ MessageDigest.SHA1,
+ Protocol.TLSv1,
+ true,
+ EncryptionLevel.EXP56,
+ false,
+ 56,
+ 128,
+ new String[] {"SSL_DHE_DSS_EXPORT1024_WITH_RC4_56_SHA"},
+ null
+ ),
+ // Cipher 66
+ TLS_DHE_DSS_WITH_RC4_128_SHA(
+ 0x0066,
+ "DHE-DSS-RC4-SHA",
+ KeyExchange.EDH,
+ Authentication.DSS,
+ Encryption.RC4,
+ MessageDigest.SHA1,
+ Protocol.TLSv1,
+ false,
+ EncryptionLevel.MEDIUM,
+ false,
+ 128,
+ 128,
+ new String[] {"SSL_DHE_DSS_WITH_RC4_128_SHA"},
+ null
+ ),
+
+ /* TLS v1.2 ciphersuites */
+ // Cipher 67
+ TLS_DHE_RSA_WITH_AES_128_CBC_SHA256(
+ 0x0067,
+ "DHE-RSA-AES128-SHA256",
+ KeyExchange.EDH,
+ Authentication.RSA,
+ Encryption.AES128,
+ MessageDigest.SHA256,
+ Protocol.TLSv1_2,
+ false,
+ EncryptionLevel.HIGH,
+ true,
+ 128,
+ 128,
+ null,
+ null
+ ),
+ // Cipher 68
+ TLS_DH_DSS_WITH_AES_256_CBC_SHA256(
+ 0x0068,
+ "DH-DSS-AES256-SHA256",
+ KeyExchange.DHd,
+ Authentication.DH,
+ Encryption.AES256,
+ MessageDigest.SHA256,
+ Protocol.TLSv1_2,
+ false,
+ EncryptionLevel.HIGH,
+ true,
+ 256,
+ 256,
+ null,
+ null
+ ),
+ // Cipher 69
+ TLS_DH_RSA_WITH_AES_256_CBC_SHA256(
+ 0x0069,
+ "DH-RSA-AES256-SHA256",
+ KeyExchange.DHr,
+ Authentication.DH,
+ Encryption.AES256,
+ MessageDigest.SHA256,
+ Protocol.TLSv1_2,
+ false,
+ EncryptionLevel.HIGH,
+ true,
+ 256,
+ 256,
+ null,
+ null
+ ),
+ // Cipher 6A
+ TLS_DHE_DSS_WITH_AES_256_CBC_SHA256(
+ 0x006A,
+ "DHE-DSS-AES256-SHA256",
+ KeyExchange.EDH,
+ Authentication.DSS,
+ Encryption.AES256,
+ MessageDigest.SHA256,
+ Protocol.TLSv1_2,
+ false,
+ EncryptionLevel.HIGH,
+ true,
+ 256,
+ 256,
+ null,
+ null
+ ),
+ // Cipher 6B
+ TLS_DHE_RSA_WITH_AES_256_CBC_SHA256(
+ 0x006B,
+ "DHE-RSA-AES256-SHA256",
+ KeyExchange.EDH,
+ Authentication.RSA,
+ Encryption.AES256,
+ MessageDigest.SHA256,
+ Protocol.TLSv1_2,
+ false,
+ EncryptionLevel.HIGH,
+ true,
+ 256,
+ 256,
+ null,
+ null
+ ),
+ // Cipher 6C
+ TLS_DH_anon_WITH_AES_128_CBC_SHA256(
+ 0x006C,
+ "ADH-AES128-SHA256",
+ KeyExchange.EDH,
+ Authentication.aNULL,
+ Encryption.AES128,
+ MessageDigest.SHA256,
+ Protocol.TLSv1_2,
+ false,
+ EncryptionLevel.HIGH,
+ true,
+ 128,
+ 128,
+ null,
+ null
+ ),
+ // Cipher 6D
+ TLS_DH_anon_WITH_AES_256_CBC_SHA256(
+ 0x006D,
+ "ADH-AES256-SHA256",
+ KeyExchange.EDH,
+ Authentication.aNULL,
+ Encryption.AES256,
+ MessageDigest.SHA256,
+ Protocol.TLSv1_2,
+ false,
+ EncryptionLevel.HIGH,
+ true,
+ 256,
+ 256,
+ null,
+ null
+ ),
+ /* GOST Ciphersuites. Unsupported by Java. OpenSSl lists them with IDs
+ * 0x3000080 to 0x3000083 */
+ /*
+ // Cipher 80
+ TLS_GOSTR341094_WITH_28147_CNT_IMIT(
+ "GOST94-GOST89-GOST89",
+ KeyExchange.GOST,
+ Authentication.GOST94,
+ Encryption.eGOST2814789CNT,
+ MessageDigest.GOST89MAC,
+ Protocol.TLSv1,
+ false,
+ EncryptionLevel.HIGH,
+ false,
+ 256,
+ 256,
+ null,
+ null
+ ),
+ // Cipher 81
+ TLS_GOSTR341001_WITH_28147_CNT_IMIT(
+ "GOST2001-GOST89-GOST89",
+ KeyExchange.GOST,
+ Authentication.GOST01,
+ Encryption.eGOST2814789CNT,
+ MessageDigest.GOST89MAC,
+ Protocol.TLSv1,
+ false,
+ EncryptionLevel.HIGH,
+ false,
+ 256,
+ 256,
+ null,
+ null
+ ),
+ // Cipher 82
+ TLS_GOSTR341094_WITH_NULL_GOSTR3411(
+ "GOST94-NULL-GOST94",
+ KeyExchange.GOST,
+ Authentication.GOST94,
+ Encryption.eNULL,
+ MessageDigest.GOST94,
+ Protocol.TLSv1,
+ false,
+ EncryptionLevel.STRONG_NONE,
+ false,
+ 0,
+ 0,
+ null,
+ null
+ ),
+ // Cipher 83
+ TLS_GOSTR341001_WITH_NULL_GOSTR3411(
+ "GOST2001-NULL-GOST94",
+ KeyExchange.GOST,
+ Authentication.GOST01,
+ Encryption.eNULL,
+ MessageDigest.GOST94,
+ Protocol.TLSv1,
+ false,
+ EncryptionLevel.STRONG_NONE,
+ false,
+ 0,
+ 0,
+ null,
+ null
+ ),*/
+ /* Camellia ciphersuites from RFC4132 (
+ 256-bit portion) */
+ // Cipher 84
+ TLS_RSA_WITH_CAMELLIA_256_CBC_SHA(
+ 0x0084,
+ "CAMELLIA256-SHA",
+ KeyExchange.RSA,
+ Authentication.RSA,
+ Encryption.CAMELLIA256,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.HIGH,
+ false,
+ 256,
+ 256,
+ null,
+ null
+ ),
+ // Cipher 85
+ TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA(
+ 0x0085,
+ "DH-DSS-CAMELLIA256-SHA",
+ KeyExchange.DHd,
+ Authentication.DH,
+ Encryption.CAMELLIA256,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.HIGH,
+ false,
+ 256,
+ 256,
+ null,
+ null
+ ),
+ // Cipher 86
+ TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA(
+ 0x0086,
+ "DH-RSA-CAMELLIA256-SHA",
+ KeyExchange.DHr,
+ Authentication.DH,
+ Encryption.CAMELLIA256,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.HIGH,
+ false,
+ 256,
+ 256,
+ null,
+ null
+ ),
+ // Cipher 87
+ TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA(
+ 0x0087,
+ "DHE-DSS-CAMELLIA256-SHA",
+ KeyExchange.EDH,
+ Authentication.DSS,
+ Encryption.CAMELLIA256,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.HIGH,
+ false,
+ 256,
+ 256,
+ null,
+ null
+ ),
+ // Cipher 88
+ TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA(
+ 0x0088,
+ "DHE-RSA-CAMELLIA256-SHA",
+ KeyExchange.EDH,
+ Authentication.RSA,
+ Encryption.CAMELLIA256,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.HIGH,
+ false,
+ 256,
+ 256,
+ null,
+ null
+ ),
+ // Cipher 89
+ TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA(
+ 0x0089,
+ "ADH-CAMELLIA256-SHA",
+ KeyExchange.EDH,
+ Authentication.aNULL,
+ Encryption.CAMELLIA256,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.HIGH,
+ false,
+ 256,
+ 256,
+ null,
+ null
+ ),
+ // Cipher 8A
+ TLS_PSK_WITH_RC4_128_SHA(
+ 0x008A,
+ "PSK-RC4-SHA",
+ KeyExchange.PSK,
+ Authentication.PSK,
+ Encryption.RC4,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.MEDIUM,
+ false,
+ 128,
+ 128,
+ null,
+ null
+ ),
+ // Cipher 8B
+ TLS_PSK_WITH_3DES_EDE_CBC_SHA(
+ 0x008B,
+ "PSK-3DES-EDE-CBC-SHA",
+ KeyExchange.PSK,
+ Authentication.PSK,
+ Encryption.TRIPLE_DES,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.MEDIUM,
+ true,
+ 112,
+ 168,
+ null,
+ null
+ ),
+ // Cipher 8C
+ TLS_PSK_WITH_AES_128_CBC_SHA(
+ 0x008C,
+ "PSK-AES128-CBC-SHA",
+ KeyExchange.PSK,
+ Authentication.PSK,
+ Encryption.AES128,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.HIGH,
+ true,
+ 128,
+ 128,
+ null,
+ null
+ ),
+ // Cipher 8D
+ TLS_PSK_WITH_AES_256_CBC_SHA(
+ 0x008D,
+ "PSK-AES256-CBC-SHA",
+ KeyExchange.PSK,
+ Authentication.PSK,
+ Encryption.AES256,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.HIGH,
+ true,
+ 256,
+ 256,
+ null,
+ null
+ ),
+ // Cipher 8E
+ TLS_DHE_PSK_WITH_RC4_128_SHA(
+ 0x008E,
+ "DHE-PSK-RC4-SHA",
+ KeyExchange.DHEPSK,
+ Authentication.PSK,
+ Encryption.RC4,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.MEDIUM,
+ false,
+ 128,
+ 128,
+ null,
+ null
+ ),
+ // Cipher 8F
+ TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA(
+ 0x008F,
+ "DHE-PSK-3DES-EDE-CBC-SHA",
+ KeyExchange.DHEPSK,
+ Authentication.PSK,
+ Encryption.TRIPLE_DES,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.MEDIUM,
+ true,
+ 112,
+ 168,
+ null,
+ null
+ ),
+ // Cipher 90
+ TLS_DHE_PSK_WITH_AES_128_CBC_SHA(
+ 0x0090,
+ "DHE-PSK-AES128-CBC-SHA",
+ KeyExchange.DHEPSK,
+ Authentication.PSK,
+ Encryption.AES128,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.HIGH,
+ true,
+ 128,
+ 128,
+ null,
+ null
+ ),
+ // Cipher 91
+ TLS_DHE_PSK_WITH_AES_256_CBC_SHA(
+ 0x0091,
+ "DHE-PSK-AES256-CBC-SHA",
+ KeyExchange.DHEPSK,
+ Authentication.PSK,
+ Encryption.AES256,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.HIGH,
+ true,
+ 256,
+ 256,
+ null,
+ null
+ ),
+ // Cipher 92
+ TLS_RSA_PSK_WITH_RC4_128_SHA(
+ 0x0092,
+ "RSA-PSK-RC4-SHA",
+ KeyExchange.RSAPSK,
+ Authentication.RSA,
+ Encryption.RC4,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.MEDIUM,
+ false,
+ 128,
+ 128,
+ null,
+ null
+ ),
+ // Cipher 93
+ TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA(
+ 0x0093,
+ "RSA-PSK-3DES-EDE-CBC-SHA",
+ KeyExchange.RSAPSK,
+ Authentication.RSA,
+ Encryption.TRIPLE_DES,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.MEDIUM,
+ true,
+ 112,
+ 168,
+ null,
+ null
+ ),
+ // Cipher 94
+ TLS_RSA_PSK_WITH_AES_128_CBC_SHA(
+ 0x0094,
+ "RSA-PSK-AES128-CBC-SHA",
+ KeyExchange.RSAPSK,
+ Authentication.RSA,
+ Encryption.AES128,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.HIGH,
+ true,
+ 128,
+ 128,
+ null,
+ null
+ ),
+ // Cipher 95
+ TLS_RSA_PSK_WITH_AES_256_CBC_SHA(
+ 0x0095,
+ "RSA-PSK-AES256-CBC-SHA",
+ KeyExchange.RSAPSK,
+ Authentication.RSA,
+ Encryption.AES256,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.HIGH,
+ true,
+ 256,
+ 256,
+ null,
+ null
+ ),
+ /* SEED ciphersuites from RFC4162 */
+ // Cipher 96
+ TLS_RSA_WITH_SEED_CBC_SHA(
+ 0x0096,
+ "SEED-SHA",
+ KeyExchange.RSA,
+ Authentication.RSA,
+ Encryption.SEED,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.MEDIUM,
+ false,
+ 128,
+ 128,
+ null,
+ null
+ ),
+ // Cipher 97
+ TLS_DH_DSS_WITH_SEED_CBC_SHA(
+ 0x0097,
+ "DH-DSS-SEED-SHA",
+ KeyExchange.DHd,
+ Authentication.DH,
+ Encryption.SEED,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.MEDIUM,
+ false,
+ 128,
+ 128,
+ null,
+ null
+ ),
+ // Cipher 98
+ TLS_DH_RSA_WITH_SEED_CBC_SHA(
+ 0x0098,
+ "DH-RSA-SEED-SHA",
+ KeyExchange.DHr,
+ Authentication.DH,
+ Encryption.SEED,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.MEDIUM,
+ false,
+ 128,
+ 128,
+ null,
+ null
+ ),
+ // Cipher 99
+ TLS_DHE_DSS_WITH_SEED_CBC_SHA(
+ 0x0099,
+ "DHE-DSS-SEED-SHA",
+ KeyExchange.EDH,
+ Authentication.DSS,
+ Encryption.SEED,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.MEDIUM,
+ false,
+ 128,
+ 128,
+ null,
+ null
+ ),
+ // Cipher 9A
+ TLS_DHE_RSA_WITH_SEED_CBC_SHA(
+ 0x009A,
+ "DHE-RSA-SEED-SHA",
+ KeyExchange.EDH,
+ Authentication.RSA,
+ Encryption.SEED,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.MEDIUM,
+ false,
+ 128,
+ 128,
+ null,
+ null
+ ),
+ // Cipher 9B
+ TLS_DH_anon_WITH_SEED_CBC_SHA(
+ 0x009B,
+ "ADH-SEED-SHA",
+ KeyExchange.EDH,
+ Authentication.aNULL,
+ Encryption.SEED,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.MEDIUM,
+ false,
+ 128,
+ 128,
+ null,
+ null
+ ),
+ /* GCM ciphersuites from RFC5288 */
+ // Cipher 9C
+ TLS_RSA_WITH_AES_128_GCM_SHA256(
+ 0x009C,
+ "AES128-GCM-SHA256",
+ KeyExchange.RSA,
+ Authentication.RSA,
+ Encryption.AES128GCM,
+ MessageDigest.AEAD,
+ Protocol.TLSv1_2,
+ false,
+ EncryptionLevel.HIGH,
+ true,
+ 128,
+ 128,
+ null,
+ null
+ ),
+ // Cipher 9D
+ TLS_RSA_WITH_AES_256_GCM_SHA384(
+ 0x009D,
+ "AES256-GCM-SHA384",
+ KeyExchange.RSA,
+ Authentication.RSA,
+ Encryption.AES256GCM,
+ MessageDigest.AEAD,
+ Protocol.TLSv1_2,
+ false,
+ EncryptionLevel.HIGH,
+ true,
+ 256,
+ 256,
+ null,
+ null
+ ),
+ // Cipher 9E
+ TLS_DHE_RSA_WITH_AES_128_GCM_SHA256(
+ 0x009E,
+ "DHE-RSA-AES128-GCM-SHA256",
+ KeyExchange.EDH,
+ Authentication.RSA,
+ Encryption.AES128GCM,
+ MessageDigest.AEAD,
+ Protocol.TLSv1_2,
+ false,
+ EncryptionLevel.HIGH,
+ true,
+ 128,
+ 128,
+ null,
+ null
+ ),
+ // Cipher 9F
+ TLS_DHE_RSA_WITH_AES_256_GCM_SHA384(
+ 0x009F,
+ "DHE-RSA-AES256-GCM-SHA384",
+ KeyExchange.EDH,
+ Authentication.RSA,
+ Encryption.AES256GCM,
+ MessageDigest.AEAD,
+ Protocol.TLSv1_2,
+ false,
+ EncryptionLevel.HIGH,
+ true,
+ 256,
+ 256,
+ null,
+ null
+ ),
+ // Cipher A0
+ TLS_DH_RSA_WITH_AES_128_GCM_SHA256(
+ 0x00A0,
+ "DH-RSA-AES128-GCM-SHA256",
+ KeyExchange.DHr,
+ Authentication.DH,
+ Encryption.AES128GCM,
+ MessageDigest.AEAD,
+ Protocol.TLSv1_2,
+ false,
+ EncryptionLevel.HIGH,
+ true,
+ 128,
+ 128,
+ null,
+ null
+ ),
+ // Cipher A1
+ TLS_DH_RSA_WITH_AES_256_GCM_SHA384(
+ 0x00A1,
+ "DH-RSA-AES256-GCM-SHA384",
+ KeyExchange.DHr,
+ Authentication.DH,
+ Encryption.AES256GCM,
+ MessageDigest.AEAD,
+ Protocol.TLSv1_2,
+ false,
+ EncryptionLevel.HIGH,
+ true,
+ 256,
+ 256,
+ null,
+ null
+ ),
+ // Cipher A2
+ TLS_DHE_DSS_WITH_AES_128_GCM_SHA256(
+ 0x00A2,
+ "DHE-DSS-AES128-GCM-SHA256",
+ KeyExchange.EDH,
+ Authentication.DSS,
+ Encryption.AES128GCM,
+ MessageDigest.AEAD,
+ Protocol.TLSv1_2,
+ false,
+ EncryptionLevel.HIGH,
+ true,
+ 128,
+ 128,
+ null,
+ null
+ ),
+ // Cipher A3
+ TLS_DHE_DSS_WITH_AES_256_GCM_SHA384(
+ 0x00A3,
+ "DHE-DSS-AES256-GCM-SHA384",
+ KeyExchange.EDH,
+ Authentication.DSS,
+ Encryption.AES256GCM,
+ MessageDigest.AEAD,
+ Protocol.TLSv1_2,
+ false,
+ EncryptionLevel.HIGH,
+ true,
+ 256,
+ 256,
+ null,
+ null
+ ),
+ // Cipher A4
+ TLS_DH_DSS_WITH_AES_128_GCM_SHA256(
+ 0x00A4,
+ "DH-DSS-AES128-GCM-SHA256",
+ KeyExchange.DHd,
+ Authentication.DH,
+ Encryption.AES128GCM,
+ MessageDigest.AEAD,
+ Protocol.TLSv1_2,
+ false,
+ EncryptionLevel.HIGH,
+ true,
+ 128,
+ 128,
+ null,
+ null
+ ),
+ // Cipher A5
+ TLS_DH_DSS_WITH_AES_256_GCM_SHA384(
+ 0x00A5,
+ "DH-DSS-AES256-GCM-SHA384",
+ KeyExchange.DHd,
+ Authentication.DH,
+ Encryption.AES256GCM,
+ MessageDigest.AEAD,
+ Protocol.TLSv1_2,
+ false,
+ EncryptionLevel.HIGH,
+ true,
+ 256,
+ 256,
+ null,
+ null
+ ),
+ // Cipher A6
+ TLS_DH_anon_WITH_AES_128_GCM_SHA256(
+ 0x00A6,
+ "ADH-AES128-GCM-SHA256",
+ KeyExchange.EDH,
+ Authentication.aNULL,
+ Encryption.AES128GCM,
+ MessageDigest.AEAD,
+ Protocol.TLSv1_2,
+ false,
+ EncryptionLevel.HIGH,
+ true,
+ 128,
+ 128,
+ null,
+ null
+ ),
+ // Cipher A7
+ TLS_DH_anon_WITH_AES_256_GCM_SHA384(
+ 0x00A7,
+ "ADH-AES256-GCM-SHA384",
+ KeyExchange.EDH,
+ Authentication.aNULL,
+ Encryption.AES256GCM,
+ MessageDigest.AEAD,
+ Protocol.TLSv1_2,
+ false,
+ EncryptionLevel.HIGH,
+ true,
+ 256,
+ 256,
+ null,
+ null
+ ),
+ // Cipher A8
+ TLS_PSK_WITH_AES_128_GCM_SHA256(
+ 0x00A8,
+ "PSK-AES128-GCM-SHA256",
+ KeyExchange.PSK,
+ Authentication.PSK,
+ Encryption.AES128GCM,
+ MessageDigest.AEAD,
+ Protocol.TLSv1_2,
+ false,
+ EncryptionLevel.HIGH,
+ true,
+ 128,
+ 128,
+ null,
+ null
+ ),
+ // Cipher A9
+ TLS_PSK_WITH_AES_256_GCM_SHA384(
+ 0x00A9,
+ "PSK-AES256-GCM-SHA384",
+ KeyExchange.PSK,
+ Authentication.PSK,
+ Encryption.AES256GCM,
+ MessageDigest.AEAD,
+ Protocol.TLSv1_2,
+ false,
+ EncryptionLevel.HIGH,
+ true,
+ 256,
+ 256,
+ null,
+ null
+ ),
+ // Cipher AA
+ TLS_DHE_PSK_WITH_AES_128_GCM_SHA256(
+ 0x00AA,
+ "DHE-PSK-AES128-GCM-SHA256",
+ KeyExchange.DHEPSK,
+ Authentication.PSK,
+ Encryption.AES128GCM,
+ MessageDigest.AEAD,
+ Protocol.TLSv1_2,
+ false,
+ EncryptionLevel.HIGH,
+ true,
+ 128,
+ 128,
+ null,
+ null
+ ),
+ // Cipher AB
+ TLS_DHE_PSK_WITH_AES_256_GCM_SHA384(
+ 0x00AB,
+ "DHE-PSK-AES256-GCM-SHA384",
+ KeyExchange.DHEPSK,
+ Authentication.PSK,
+ Encryption.AES256GCM,
+ MessageDigest.AEAD,
+ Protocol.TLSv1_2,
+ false,
+ EncryptionLevel.HIGH,
+ true,
+ 256,
+ 256,
+ null,
+ null
+ ),
+ // Cipher AC
+ TLS_RSA_PSK_WITH_AES_128_GCM_SHA256(
+ 0x00AC,
+ "RSA-PSK-AES128-GCM-SHA256",
+ KeyExchange.RSAPSK,
+ Authentication.RSA,
+ Encryption.AES128GCM,
+ MessageDigest.AEAD,
+ Protocol.TLSv1_2,
+ false,
+ EncryptionLevel.HIGH,
+ true,
+ 128,
+ 128,
+ null,
+ null
+ ),
+ // Cipher AD
+ TLS_RSA_PSK_WITH_AES_256_GCM_SHA384(
+ 0x00AD,
+ "RSA-PSK-AES256-GCM-SHA384",
+ KeyExchange.RSAPSK,
+ Authentication.RSA,
+ Encryption.AES256GCM,
+ MessageDigest.AEAD,
+ Protocol.TLSv1_2,
+ false,
+ EncryptionLevel.HIGH,
+ true,
+ 256,
+ 256,
+ null,
+ null
+ ),
+ // Cipher AE
+ TLS_PSK_WITH_AES_128_CBC_SHA256 (
+ 0x00AE,
+ "PSK-AES128-CBC-SHA256",
+ KeyExchange.PSK,
+ Authentication.PSK,
+ Encryption.AES128,
+ MessageDigest.SHA256,
+ Protocol.TLSv1,
+ false,
+ EncryptionLevel.HIGH,
+ true,
+ 128,
+ 128,
+ null,
+ null
+ ),
+ // Cipher AF
+ TLS_PSK_WITH_AES_256_CBC_SHA384 (
+ 0x00AF,
+ "PSK-AES256-CBC-SHA384",
+ KeyExchange.PSK,
+ Authentication.PSK,
+ Encryption.AES256,
+ MessageDigest.SHA384,
+ Protocol.TLSv1,
+ false,
+ EncryptionLevel.HIGH,
+ true,
+ 256,
+ 256,
+ null,
+ null
+ ),
+ // Cipher B0
+ TLS_PSK_WITH_NULL_SHA256 (
+ 0x00B0,
+ "PSK-NULL-SHA256",
+ KeyExchange.PSK,
+ Authentication.PSK,
+ Encryption.eNULL,
+ MessageDigest.SHA256,
+ Protocol.TLSv1,
+ false,
+ EncryptionLevel.STRONG_NONE,
+ true,
+ 0,
+ 0,
+ null,
+ null
+ ),
+ // Cipher B1
+ TLS_PSK_WITH_NULL_SHA384 (
+ 0x00B1,
+ "PSK-NULL-SHA384",
+ KeyExchange.PSK,
+ Authentication.PSK,
+ Encryption.eNULL,
+ MessageDigest.SHA384,
+ Protocol.TLSv1,
+ false,
+ EncryptionLevel.STRONG_NONE,
+ true,
+ 0,
+ 0,
+ null,
+ null
+ ),
+ // Cipher B2
+ TLS_DHE_PSK_WITH_AES_128_CBC_SHA256(
+ 0x00B2,
+ "DHE-PSK-AES128-CBC-SHA256",
+ KeyExchange.DHEPSK,
+ Authentication.PSK,
+ Encryption.AES128,
+ MessageDigest.SHA256,
+ Protocol.TLSv1,
+ false,
+ EncryptionLevel.HIGH,
+ true,
+ 128,
+ 128,
+ null,
+ null
+ ),
+ // Cipher B3
+ TLS_DHE_PSK_WITH_AES_256_CBC_SHA384(
+ 0x00B3,
+ "DHE-PSK-AES256-CBC-SHA384",
+ KeyExchange.DHEPSK,
+ Authentication.PSK,
+ Encryption.AES256,
+ MessageDigest.SHA384,
+ Protocol.TLSv1,
+ false,
+ EncryptionLevel.HIGH,
+ true,
+ 256,
+ 256,
+ null,
+ null
+ ),
+ // Cipher B4
+ TLS_DHE_PSK_WITH_NULL_SHA256 (
+ 0x00B4,
+ "DHE-PSK-NULL-SHA256",
+ KeyExchange.DHEPSK,
+ Authentication.PSK,
+ Encryption.eNULL,
+ MessageDigest.SHA256,
+ Protocol.TLSv1,
+ false,
+ EncryptionLevel.STRONG_NONE,
+ true,
+ 0,
+ 0,
+ null,
+ null
+ ),
+ // Cipher B5
+ TLS_DHE_PSK_WITH_NULL_SHA384 (
+ 0x00B5,
+ "DHE-PSK-NULL-SHA384",
+ KeyExchange.DHEPSK,
+ Authentication.PSK,
+ Encryption.eNULL,
+ MessageDigest.SHA384,
+ Protocol.TLSv1,
+ false,
+ EncryptionLevel.STRONG_NONE,
+ true,
+ 0,
+ 0,
+ null,
+ null
+ ),
+ // Cipher B6
+ TLS_RSA_PSK_WITH_AES_128_CBC_SHA256(
+ 0x00B6,
+ "RSA-PSK-AES128-CBC-SHA256",
+ KeyExchange.RSAPSK,
+ Authentication.RSA,
+ Encryption.AES128,
+ MessageDigest.SHA256,
+ Protocol.TLSv1,
+ false,
+ EncryptionLevel.HIGH,
+ true,
+ 128,
+ 128,
+ null,
+ null
+ ),
+ // Cipher B7
+ TLS_RSA_PSK_WITH_AES_256_CBC_SHA384(
+ 0x00B7,
+ "RSA-PSK-AES256-CBC-SHA384",
+ KeyExchange.RSAPSK,
+ Authentication.RSA,
+ Encryption.AES256,
+ MessageDigest.SHA384,
+ Protocol.TLSv1,
+ false,
+ EncryptionLevel.HIGH,
+ true,
+ 256,
+ 256,
+ null,
+ null
+ ),
+ // Cipher B8
+ TLS_RSA_PSK_WITH_NULL_SHA256 (
+ 0x00B8,
+ "RSA-PSK-NULL-SHA256",
+ KeyExchange.RSAPSK,
+ Authentication.RSA,
+ Encryption.eNULL,
+ MessageDigest.SHA256,
+ Protocol.TLSv1,
+ false,
+ EncryptionLevel.STRONG_NONE,
+ true,
+ 0,
+ 0,
+ null,
+ null
+ ),
+ // Cipher B9
+ TLS_RSA_PSK_WITH_NULL_SHA384 (
+ 0x00B9,
+ "RSA-PSK-NULL-SHA384",
+ KeyExchange.RSAPSK,
+ Authentication.RSA,
+ Encryption.eNULL,
+ MessageDigest.SHA384,
+ Protocol.TLSv1,
+ false,
+ EncryptionLevel.STRONG_NONE,
+ true,
+ 0,
+ 0,
+ null,
+ null
+ ),
+
+ // Cipher BA
+ TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256(
+ 0x00BA,
+ "CAMELLIA128-SHA256",
+ KeyExchange.RSA,
+ Authentication.RSA,
+ Encryption.CAMELLIA128,
+ MessageDigest.SHA256,
+ Protocol.TLSv1_2,
+ false,
+ EncryptionLevel.HIGH,
+ false,
+ 128,
+ 128,
+ null,
+ null
+ ),
+ // Cipher BB
+ TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256(
+ 0x00BB,
+ "DH-DSS-CAMELLIA128-SHA256",
+ KeyExchange.DHd,
+ Authentication.DH,
+ Encryption.CAMELLIA128,
+ MessageDigest.SHA256,
+ Protocol.TLSv1_2,
+ false,
+ EncryptionLevel.HIGH,
+ false,
+ 128,
+ 128,
+ null,
+ null
+ ),
+ // Cipher BC
+ TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256(
+ 0x00BC,
+ "DH-RSA-CAMELLIA128-SHA256",
+ KeyExchange.DHr,
+ Authentication.DH,
+ Encryption.CAMELLIA128,
+ MessageDigest.SHA256,
+ Protocol.TLSv1_2,
+ false,
+ EncryptionLevel.HIGH,
+ false,
+ 128,
+ 128,
+ null,
+ null
+ ),
+ // Cipher BD
+ TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256(
+ 0x00BD,
+ "DHE-DSS-CAMELLIA128-SHA256",
+ KeyExchange.EDH,
+ Authentication.DSS,
+ Encryption.CAMELLIA128,
+ MessageDigest.SHA256,
+ Protocol.TLSv1_2,
+ false,
+ EncryptionLevel.HIGH,
+ false,
+ 128,
+ 128,
+ null,
+ null
+ ),
+ // Cipher BE
+ TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256(
+ 0x00BE,
+ "DHE-RSA-CAMELLIA128-SHA256",
+ KeyExchange.EDH,
+ Authentication.RSA,
+ Encryption.CAMELLIA128,
+ MessageDigest.SHA256,
+ Protocol.TLSv1_2,
+ false,
+ EncryptionLevel.HIGH,
+ false,
+ 128,
+ 128,
+ null,
+ null
+ ),
+ // Cipher BF
+ TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA256(
+ 0x00BF,
+ "ADH-CAMELLIA128-SHA256",
+ KeyExchange.EDH,
+ Authentication.aNULL,
+ Encryption.CAMELLIA128,
+ MessageDigest.SHA256,
+ Protocol.TLSv1_2,
+ false,
+ EncryptionLevel.HIGH,
+ false,
+ 128,
+ 128,
+ null,
+ null
+ ),
+ // Cipher C0
+ TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256(
+ 0x00C0,
+ "CAMELLIA256-SHA256",
+ KeyExchange.RSA,
+ Authentication.RSA,
+ Encryption.CAMELLIA256,
+ MessageDigest.SHA256,
+ Protocol.TLSv1_2,
+ false,
+ EncryptionLevel.HIGH,
+ false,
+ 256,
+ 256,
+ null,
+ null
+ ),
+ // Cipher C1
+ TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256(
+ 0x00C1,
+ "DH-DSS-CAMELLIA256-SHA256",
+ KeyExchange.DHd,
+ Authentication.DH,
+ Encryption.CAMELLIA256,
+ MessageDigest.SHA256,
+ Protocol.TLSv1_2,
+ false,
+ EncryptionLevel.HIGH,
+ false,
+ 256,
+ 256,
+ null,
+ null
+ ),
+ // Cipher C2
+ TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256(
+ 0x00C2,
+ "DH-RSA-CAMELLIA256-SHA256",
+ KeyExchange.DHr,
+ Authentication.DH,
+ Encryption.CAMELLIA256,
+ MessageDigest.SHA256,
+ Protocol.TLSv1_2,
+ false,
+ EncryptionLevel.HIGH,
+ false,
+ 256,
+ 256,
+ null,
+ null
+ ),
+ // Cipher C3
+ TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256(
+ 0x00C3,
+ "DHE-DSS-CAMELLIA256-SHA256",
+ KeyExchange.EDH,
+ Authentication.DSS,
+ Encryption.CAMELLIA256,
+ MessageDigest.SHA256,
+ Protocol.TLSv1_2,
+ false,
+ EncryptionLevel.HIGH,
+ false,
+ 256,
+ 256,
+ null,
+ null
+ ),
+ // Cipher C4
+ TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256(
+ 0x00C4,
+ "DHE-RSA-CAMELLIA256-SHA256",
+ KeyExchange.EDH,
+ Authentication.RSA,
+ Encryption.CAMELLIA256,
+ MessageDigest.SHA256,
+ Protocol.TLSv1_2,
+ false,
+ EncryptionLevel.HIGH,
+ false,
+ 256,
+ 256,
+ null,
+ null
+ ),
+ // Cipher C5
+ TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA256(
+ 0x00C5,
+ "ADH-CAMELLIA256-SHA256",
+ KeyExchange.EDH,
+ Authentication.aNULL,
+ Encryption.CAMELLIA256,
+ MessageDigest.SHA256,
+ Protocol.TLSv1_2,
+ false,
+ EncryptionLevel.HIGH,
+ false,
+ 256,
+ 256,
+ null,
+ null
+ ),
+
+ /* Cipher 0x00FF TLS_EMPTY_RENEGOTIATION_INFO_SCSV
+ * Cipher 0x5600 TLS_FALLBACK_SCSV
+ *
+ * No other ciphers defined until 0xC001 below
+ */
+
+ /* ECC ciphersuites from draft-ietf-tls-ecc-01.txt (
+ Mar 15, 2001) */
+ // Cipher C001
+ TLS_ECDH_ECDSA_WITH_NULL_SHA(
+ 0xC001,
+ "ECDH-ECDSA-NULL-SHA",
+ KeyExchange.ECDHe,
+ Authentication.ECDH,
+ Encryption.eNULL,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.STRONG_NONE,
+ true,
+ 0,
+ 0,
+ null,
+ null
+ ),
+ // Cipher C002
+ TLS_ECDH_ECDSA_WITH_RC4_128_SHA(
+ 0xC002,
+ "ECDH-ECDSA-RC4-SHA",
+ KeyExchange.ECDHe,
+ Authentication.ECDH,
+ Encryption.RC4,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.MEDIUM,
+ false,
+ 128,
+ 128,
+ null,
+ null
+ ),
+ // Cipher C003
+ TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA(
+ 0xC003,
+ "ECDH-ECDSA-DES-CBC3-SHA",
+ KeyExchange.ECDHe,
+ Authentication.ECDH,
+ Encryption.TRIPLE_DES,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.MEDIUM,
+ true,
+ 112,
+ 168,
+ null,
+ null
+ ),
+ // Cipher C004
+ TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA(
+ 0xC004,
+ "ECDH-ECDSA-AES128-SHA",
+ KeyExchange.ECDHe,
+ Authentication.ECDH,
+ Encryption.AES128,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.HIGH,
+ true,
+ 128,
+ 128,
+ null,
+ null
+ ),
+ // Cipher C005
+ TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA(
+ 0xC005,
+ "ECDH-ECDSA-AES256-SHA",
+ KeyExchange.ECDHe,
+ Authentication.ECDH,
+ Encryption.AES256,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.HIGH,
+ true,
+ 256,
+ 256,
+ null,
+ null
+ ),
+ // Cipher C006
+ TLS_ECDHE_ECDSA_WITH_NULL_SHA(
+ 0xC006,
+ "ECDHE-ECDSA-NULL-SHA",
+ KeyExchange.EECDH,
+ Authentication.ECDSA,
+ Encryption.eNULL,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.STRONG_NONE,
+ true,
+ 0,
+ 0,
+ null,
+ null
+ ),
+ // Cipher C007
+ TLS_ECDHE_ECDSA_WITH_RC4_128_SHA(
+ 0xC007,
+ "ECDHE-ECDSA-RC4-SHA",
+ KeyExchange.EECDH,
+ Authentication.ECDSA,
+ Encryption.RC4,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.MEDIUM,
+ false,
+ 128,
+ 128,
+ null,
+ null
+ ),
+ // Cipher C008
+ TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA(
+ 0xC008,
+ "ECDHE-ECDSA-DES-CBC3-SHA",
+ KeyExchange.EECDH,
+ Authentication.ECDSA,
+ Encryption.TRIPLE_DES,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.MEDIUM,
+ true,
+ 112,
+ 168,
+ null,
+ null
+ ),
+ // Cipher C009
+ TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA(
+ 0xC009,
+ "ECDHE-ECDSA-AES128-SHA",
+ KeyExchange.EECDH,
+ Authentication.ECDSA,
+ Encryption.AES128,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.HIGH,
+ true,
+ 128,
+ 128,
+ null,
+ null
+ ),
+ // Cipher C00A
+ TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA(
+ 0xC00A,
+ "ECDHE-ECDSA-AES256-SHA",
+ KeyExchange.EECDH,
+ Authentication.ECDSA,
+ Encryption.AES256,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.HIGH,
+ true,
+ 256,
+ 256,
+ null,
+ null
+ ),
+ // Cipher C00B
+ TLS_ECDH_RSA_WITH_NULL_SHA(
+ 0xC00B,
+ "ECDH-RSA-NULL-SHA",
+ KeyExchange.ECDHr,
+ Authentication.ECDH,
+ Encryption.eNULL,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.STRONG_NONE,
+ true,
+ 0,
+ 0,
+ null,
+ null
+ ),
+ // Cipher C00C
+ TLS_ECDH_RSA_WITH_RC4_128_SHA(
+ 0xC00C,
+ "ECDH-RSA-RC4-SHA",
+ KeyExchange.ECDHr,
+ Authentication.ECDH,
+ Encryption.RC4,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.MEDIUM,
+ false,
+ 128,
+ 128,
+ null,
+ null
+ ),
+ // Cipher C00D
+ TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA(
+ 0xC00D,
+ "ECDH-RSA-DES-CBC3-SHA",
+ KeyExchange.ECDHr,
+ Authentication.ECDH,
+ Encryption.TRIPLE_DES,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.MEDIUM,
+ true,
+ 112,
+ 168,
+ null,
+ null
+ ),
+ // Cipher C00E
+ TLS_ECDH_RSA_WITH_AES_128_CBC_SHA(
+ 0xC00E,
+ "ECDH-RSA-AES128-SHA",
+ KeyExchange.ECDHr,
+ Authentication.ECDH,
+ Encryption.AES128,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.HIGH,
+ true,
+ 128,
+ 128,
+ null,
+ null
+ ),
+ // Cipher C00F
+ TLS_ECDH_RSA_WITH_AES_256_CBC_SHA(
+ 0xC00F,
+ "ECDH-RSA-AES256-SHA",
+ KeyExchange.ECDHr,
+ Authentication.ECDH,
+ Encryption.AES256,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.HIGH,
+ true,
+ 256,
+ 256,
+ null,
+ null
+ ),
+ // Cipher C010
+ TLS_ECDHE_RSA_WITH_NULL_SHA(
+ 0xC010,
+ "ECDHE-RSA-NULL-SHA",
+ KeyExchange.EECDH,
+ Authentication.RSA,
+ Encryption.eNULL,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.STRONG_NONE,
+ true,
+ 0,
+ 0,
+ null,
+ null
+ ),
+ // Cipher C011
+ TLS_ECDHE_RSA_WITH_RC4_128_SHA(
+ 0xC011,
+ "ECDHE-RSA-RC4-SHA",
+ KeyExchange.EECDH,
+ Authentication.RSA,
+ Encryption.RC4,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.MEDIUM,
+ false,
+ 128,
+ 128,
+ null,
+ null
+ ),
+ // Cipher C012
+ TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA(
+ 0xC012,
+ "ECDHE-RSA-DES-CBC3-SHA",
+ KeyExchange.EECDH,
+ Authentication.RSA,
+ Encryption.TRIPLE_DES,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.MEDIUM,
+ true,
+ 112,
+ 168,
+ null,
+ null
+ ),
+ // Cipher C013
+ TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA(
+ 0xC013,
+ "ECDHE-RSA-AES128-SHA",
+ KeyExchange.EECDH,
+ Authentication.RSA,
+ Encryption.AES128,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.HIGH,
+ true,
+ 128,
+ 128,
+ null,
+ null
+ ),
+ // Cipher C014
+ TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA(
+ 0xC014,
+ "ECDHE-RSA-AES256-SHA",
+ KeyExchange.EECDH,
+ Authentication.RSA,
+ Encryption.AES256,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.HIGH,
+ true,
+ 256,
+ 256,
+ null,
+ null
+ ),
+ // Cipher C015
+ TLS_ECDH_anon_WITH_NULL_SHA(
+ 0xC015,
+ "AECDH-NULL-SHA",
+ KeyExchange.EECDH,
+ Authentication.aNULL,
+ Encryption.eNULL,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.STRONG_NONE,
+ true,
+ 0,
+ 0,
+ null,
+ null
+ ),
+ // Cipher C016
+ TLS_ECDH_anon_WITH_RC4_128_SHA(
+ 0xC016,
+ "AECDH-RC4-SHA",
+ KeyExchange.EECDH,
+ Authentication.aNULL,
+ Encryption.RC4,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.MEDIUM,
+ false,
+ 128,
+ 128,
+ null,
+ null
+ ),
+ // Cipher C017
+ TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA(
+ 0xC017,
+ "AECDH-DES-CBC3-SHA",
+ KeyExchange.EECDH,
+ Authentication.aNULL,
+ Encryption.TRIPLE_DES,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.MEDIUM,
+ true,
+ 112,
+ 168,
+ null,
+ null
+ ),
+ // Cipher C018
+ TLS_ECDH_anon_WITH_AES_128_CBC_SHA(
+ 0xC018,
+ "AECDH-AES128-SHA",
+ KeyExchange.EECDH,
+ Authentication.aNULL,
+ Encryption.AES128,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.HIGH,
+ true,
+ 128,
+ 128,
+ null,
+ null
+ ),
+ // Cipher C019
+ TLS_ECDH_anon_WITH_AES_256_CBC_SHA(
+ 0xC019,
+ "AECDH-AES256-SHA",
+ KeyExchange.EECDH,
+ Authentication.aNULL,
+ Encryption.AES256,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.HIGH,
+ true,
+ 256,
+ 256,
+ null,
+ null
+ ),
+ /* SRP ciphersuite from RFC 5054 */
+ // Cipher C01A
+ TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA(
+ 0xC01A,
+ "SRP-3DES-EDE-CBC-SHA",
+ KeyExchange.SRP,
+ Authentication.SRP,
+ Encryption.TRIPLE_DES,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.MEDIUM,
+ false,
+ 112,
+ 168,
+ null,
+ null
+ ),
+ // Cipher C01B
+ TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA(
+ 0xC01B,
+ "SRP-RSA-3DES-EDE-CBC-SHA",
+ KeyExchange.SRP,
+ Authentication.RSA,
+ Encryption.TRIPLE_DES,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.MEDIUM,
+ false,
+ 112,
+ 168,
+ null,
+ null
+ ),
+ // Cipher C01C
+ TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA(
+ 0xC01C,
+ "SRP-DSS-3DES-EDE-CBC-SHA",
+ KeyExchange.SRP,
+ Authentication.DSS,
+ Encryption.TRIPLE_DES,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.MEDIUM,
+ false,
+ 112,
+ 168,
+ null,
+ null
+ ),
+ // Cipher C01D
+ TLS_SRP_SHA_WITH_AES_128_CBC_SHA(
+ 0xC01D,
+ "SRP-AES-128-CBC-SHA",
+ KeyExchange.SRP,
+ Authentication.SRP,
+ Encryption.AES128,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.HIGH,
+ false,
+ 128,
+ 128,
+ null,
+ null
+ ),
+ // Cipher C01E
+ TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA(
+ 0xC01E,
+ "SRP-RSA-AES-128-CBC-SHA",
+ KeyExchange.SRP,
+ Authentication.RSA,
+ Encryption.AES128,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.HIGH,
+ false,
+ 128,
+ 128,
+ null,
+ null
+ ),
+ // Cipher C01F
+ TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA(
+ 0xC01F,
+ "SRP-DSS-AES-128-CBC-SHA",
+ KeyExchange.SRP,
+ Authentication.DSS,
+ Encryption.AES128,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.HIGH,
+ false,
+ 128,
+ 128,
+ null,
+ null
+ ),
+ // Cipher C020
+ TLS_SRP_SHA_WITH_AES_256_CBC_SHA(
+ 0xC020,
+ "SRP-AES-256-CBC-SHA",
+ KeyExchange.SRP,
+ Authentication.SRP,
+ Encryption.AES256,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.HIGH,
+ false,
+ 256,
+ 256,
+ null,
+ null
+ ),
+ // Cipher C021
+ TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA(
+ 0xC021,
+ "SRP-RSA-AES-256-CBC-SHA",
+ KeyExchange.SRP,
+ Authentication.RSA,
+ Encryption.AES256,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.HIGH,
+ false,
+ 256,
+ 256,
+ null,
+ null
+ ),
+ // Cipher C022
+ TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA(
+ 0xC022,
+ "SRP-DSS-AES-256-CBC-SHA",
+ KeyExchange.SRP,
+ Authentication.DSS,
+ Encryption.AES256,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.HIGH,
+ false,
+ 256,
+ 256,
+ null,
+ null
+ ),
+ /* HMAC based TLS v1.2 ciphersuites from RFC5289 */
+ // Cipher C023
+ TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256(
+ 0xC023,
+ "ECDHE-ECDSA-AES128-SHA256",
+ KeyExchange.EECDH,
+ Authentication.ECDSA,
+ Encryption.AES128,
+ MessageDigest.SHA256,
+ Protocol.TLSv1_2,
+ false,
+ EncryptionLevel.HIGH,
+ true,
+ 128,
+ 128,
+ null,
+ null
+ ),
+ // Cipher C024
+ TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384(
+ 0xC024,
+ "ECDHE-ECDSA-AES256-SHA384",
+ KeyExchange.EECDH,
+ Authentication.ECDSA,
+ Encryption.AES256,
+ MessageDigest.SHA384,
+ Protocol.TLSv1_2,
+ false,
+ EncryptionLevel.HIGH,
+ true,
+ 256,
+ 256,
+ null,
+ null
+ ),
+ // Cipher C025
+ TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256(
+ 0xC025,
+ "ECDH-ECDSA-AES128-SHA256",
+ KeyExchange.ECDHe,
+ Authentication.ECDH,
+ Encryption.AES128,
+ MessageDigest.SHA256,
+ Protocol.TLSv1_2,
+ false,
+ EncryptionLevel.HIGH,
+ true,
+ 128,
+ 128,
+ null,
+ null
+ ),
+ // Cipher C026
+ TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384(
+ 0xC026,
+ "ECDH-ECDSA-AES256-SHA384",
+ KeyExchange.ECDHe,
+ Authentication.ECDH,
+ Encryption.AES256,
+ MessageDigest.SHA384,
+ Protocol.TLSv1_2,
+ false,
+ EncryptionLevel.HIGH,
+ true,
+ 256,
+ 256,
+ null,
+ null
+ ),
+ // Cipher C027
+ TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256(
+ 0xC027,
+ "ECDHE-RSA-AES128-SHA256",
+ KeyExchange.EECDH,
+ Authentication.RSA,
+ Encryption.AES128,
+ MessageDigest.SHA256,
+ Protocol.TLSv1_2,
+ false,
+ EncryptionLevel.HIGH,
+ true,
+ 128,
+ 128,
+ null,
+ null
+ ),
+ // Cipher C028
+ TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384(
+ 0xC028,
+ "ECDHE-RSA-AES256-SHA384",
+ KeyExchange.EECDH,
+ Authentication.RSA,
+ Encryption.AES256,
+ MessageDigest.SHA384,
+ Protocol.TLSv1_2,
+ false,
+ EncryptionLevel.HIGH,
+ true,
+ 256,
+ 256,
+ null,
+ null
+ ),
+ // Cipher C029
+ TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256(
+ 0xC029,
+ "ECDH-RSA-AES128-SHA256",
+ KeyExchange.ECDHr,
+ Authentication.ECDH,
+ Encryption.AES128,
+ MessageDigest.SHA256,
+ Protocol.TLSv1_2,
+ false,
+ EncryptionLevel.HIGH,
+ true,
+ 128,
+ 128,
+ null,
+ null
+ ),
+ // Cipher C02A
+ TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384(
+ 0xC02A,
+ "ECDH-RSA-AES256-SHA384",
+ KeyExchange.ECDHr,
+ Authentication.ECDH,
+ Encryption.AES256,
+ MessageDigest.SHA384,
+ Protocol.TLSv1_2,
+ false,
+ EncryptionLevel.HIGH,
+ true,
+ 256,
+ 256,
+ null,
+ null
+ ),
+ /* GCM based TLS v1.2 ciphersuites from RFC5289 */
+ // Cipher C02B
+ TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256(
+ 0xC02B,
+ "ECDHE-ECDSA-AES128-GCM-SHA256",
+ KeyExchange.EECDH,
+ Authentication.ECDSA,
+ Encryption.AES128GCM,
+ MessageDigest.AEAD,
+ Protocol.TLSv1_2,
+ false,
+ EncryptionLevel.HIGH,
+ true,
+ 128,
+ 128,
+ null,
+ null
+ ),
+ // Cipher C02C
+ TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384(
+ 0xC02C,
+ "ECDHE-ECDSA-AES256-GCM-SHA384",
+ KeyExchange.EECDH,
+ Authentication.ECDSA,
+ Encryption.AES256GCM,
+ MessageDigest.AEAD,
+ Protocol.TLSv1_2,
+ false,
+ EncryptionLevel.HIGH,
+ true,
+ 256,
+ 256,
+ null,
+ null
+ ),
+ // Cipher C02D
+ TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256(
+ 0xC02D,
+ "ECDH-ECDSA-AES128-GCM-SHA256",
+ KeyExchange.ECDHe,
+ Authentication.ECDH,
+ Encryption.AES128GCM,
+ MessageDigest.AEAD,
+ Protocol.TLSv1_2,
+ false,
+ EncryptionLevel.HIGH,
+ true,
+ 128,
+ 128,
+ null,
+ null
+ ),
+ // Cipher C02E
+ TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384(
+ 0xC02E,
+ "ECDH-ECDSA-AES256-GCM-SHA384",
+ KeyExchange.ECDHe,
+ Authentication.ECDH,
+ Encryption.AES256GCM,
+ MessageDigest.AEAD,
+ Protocol.TLSv1_2,
+ false,
+ EncryptionLevel.HIGH,
+ true,
+ 256,
+ 256,
+ null,
+ null
+ ),
+ // Cipher C02F
+ TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256(
+ 0xC02F,
+ "ECDHE-RSA-AES128-GCM-SHA256",
+ KeyExchange.EECDH,
+ Authentication.RSA,
+ Encryption.AES128GCM,
+ MessageDigest.AEAD,
+ Protocol.TLSv1_2,
+ false,
+ EncryptionLevel.HIGH,
+ true,
+ 128,
+ 128,
+ null,
+ null
+ ),
+ // Cipher C030
+ TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384(
+ 0xC030,
+ "ECDHE-RSA-AES256-GCM-SHA384",
+ KeyExchange.EECDH,
+ Authentication.RSA,
+ Encryption.AES256GCM,
+ MessageDigest.AEAD,
+ Protocol.TLSv1_2,
+ false,
+ EncryptionLevel.HIGH,
+ true,
+ 256,
+ 256,
+ null,
+ null
+ ),
+ // Cipher C031
+ TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256(
+ 0xC031,
+ "ECDH-RSA-AES128-GCM-SHA256",
+ KeyExchange.ECDHr,
+ Authentication.ECDH,
+ Encryption.AES128GCM,
+ MessageDigest.AEAD,
+ Protocol.TLSv1_2,
+ false,
+ EncryptionLevel.HIGH,
+ true,
+ 128,
+ 128,
+ null,
+ null
+ ),
+ // Cipher C032
+ TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384(
+ 0xC032,
+ "ECDH-RSA-AES256-GCM-SHA384",
+ KeyExchange.ECDHr,
+ Authentication.ECDH,
+ Encryption.AES256GCM,
+ MessageDigest.AEAD,
+ Protocol.TLSv1_2,
+ false,
+ EncryptionLevel.HIGH,
+ true,
+ 256,
+ 256,
+ null,
+ null
+ ),
+ // Cipher C033
+ TLS_ECDHE_PSK_WITH_RC4_128_SHA(
+ 0xC033,
+ "ECDHE-PSK-RC4-SHA",
+ KeyExchange.ECDHEPSK,
+ Authentication.PSK,
+ Encryption.RC4,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.MEDIUM,
+ false,
+ 128,
+ 128,
+ null,
+ null
+ ),
+ // Cipher C034
+ TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA(
+ 0xC034,
+ "ECDHE-PSK-3DES-EDE-CBC-SHA",
+ KeyExchange.ECDHEPSK,
+ Authentication.PSK,
+ Encryption.TRIPLE_DES,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.MEDIUM,
+ true,
+ 112,
+ 168,
+ null,
+ null
+ ),
+ // Cipher C035
+ TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA(
+ 0xC035,
+ "ECDHE-PSK-AES128-CBC-SHA",
+ KeyExchange.ECDHEPSK,
+ Authentication.PSK,
+ Encryption.AES128,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.HIGH,
+ true,
+ 128,
+ 128,
+ null,
+ null
+ ),
+ // Cipher C036
+ TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA(
+ 0xC036,
+ "ECDHE-PSK-AES256-CBC-SHA",
+ KeyExchange.ECDHEPSK,
+ Authentication.PSK,
+ Encryption.AES256,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.HIGH,
+ true,
+ 256,
+ 256,
+ null,
+ null
+ ),
+
+ TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256(
+ 0xC037,
+ "ECDHE-PSK-AES128-CBC-SHA256",
+ KeyExchange.ECDHEPSK,
+ Authentication.PSK,
+ Encryption.AES128,
+ MessageDigest.SHA256,
+ Protocol.TLSv1,
+ false,
+ EncryptionLevel.HIGH,
+ true,
+ 128,
+ 128,
+ null,
+ null
+ ),
+ TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384(
+ 0xC038,
+ "ECDHE-PSK-AES256-CBC-SHA384",
+ KeyExchange.ECDHEPSK,
+ Authentication.PSK,
+ Encryption.AES256,
+ MessageDigest.SHA384,
+ Protocol.TLSv1,
+ false,
+ EncryptionLevel.HIGH,
+ true,
+ 256,
+ 256,
+ null,
+ null
+ ),
+ TLS_ECDHE_PSK_WITH_NULL_SHA(
+ 0xC039,
+ "ECDHE-PSK-NULL-SHA",
+ KeyExchange.ECDHEPSK,
+ Authentication.PSK,
+ Encryption.eNULL,
+ MessageDigest.SHA1,
+ Protocol.SSLv3,
+ false,
+ EncryptionLevel.STRONG_NONE,
+ true,
+ 0,
+ 0,
+ null,
+ null
+ ),
+ TLS_ECDHE_PSK_WITH_NULL_SHA256(
+ 0xC03A,
+ "ECDHE-PSK-NULL-SHA256",
+ KeyExchange.ECDHEPSK,
+ Authentication.PSK,
+ Encryption.eNULL,
+ MessageDigest.SHA256,
+ Protocol.TLSv1,
+ false,
+ EncryptionLevel.STRONG_NONE,
+ true,
+ 0,
+ 0,
+ null,
+ null
+ ),
+ TLS_ECDHE_PSK_WITH_NULL_SHA384(
+ 0xC03B,
+ "ECDHE-PSK-NULL-SHA384",
+ KeyExchange.ECDHEPSK,
+ Authentication.PSK,
+ Encryption.eNULL,
+ MessageDigest.SHA384,
+ Protocol.TLSv1,
+ false,
+ EncryptionLevel.STRONG_NONE,
+ true,
+ 0,
+ 0,
+ null,
+ null
+ ),
+
+ /* ARIA ciphers 0xC03C to 0xC071
+ * Unsupported by both Java and OpenSSL
+ */
+ // Cipher C072
+ TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256(
+ 0xC072,
+ "ECDHE-ECDSA-CAMELLIA128-SHA256",
+ KeyExchange.EECDH,
+ Authentication.ECDSA,
+ Encryption.CAMELLIA128,
+ MessageDigest.SHA256,
+ Protocol.TLSv1_2,
+ false,
+ EncryptionLevel.HIGH,
+ true,
+ 128,
+ 128,
+ null,
+ null
+ ),
+ // Cipher C073
+ TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384(
+ 0xC073,
+ "ECDHE-ECDSA-CAMELLIA256-SHA384",
+ KeyExchange.EECDH,
+ Authentication.ECDSA,
+ Encryption.CAMELLIA256,
+ MessageDigest.SHA384,
+ Protocol.TLSv1_2,
+ false,
+ EncryptionLevel.HIGH,
+ true,
+ 256,
+ 256,
+ null,
+ null
+ ),
+ // Cipher C074
+ TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256(
+ 0xC074,
+ "ECDH-ECDSA-CAMELLIA128-SHA256",
+ KeyExchange.ECDHe,
+ Authentication.ECDH,
+ Encryption.CAMELLIA128,
+ MessageDigest.SHA256,
+ Protocol.TLSv1_2,
+ false,
+ EncryptionLevel.HIGH,
+ true,
+ 128,
+ 128,
+ null,
+ null
+ ),
+ // Cipher C075
+ TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384(
+ 0xC075,
+ "ECDH-ECDSA-CAMELLIA256-SHA384",
+ KeyExchange.ECDHe,
+ Authentication.ECDH,
+ Encryption.CAMELLIA256,
+ MessageDigest.SHA384,
+ Protocol.TLSv1_2,
+ false,
+ EncryptionLevel.HIGH,
+ true,
+ 256,
+ 256,
+ null,
+ null
+ ),
+ // Cipher C076
+ TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256(
+ 0xC076,
+ "ECDHE-RSA-CAMELLIA128-SHA256",
+ KeyExchange.EECDH,
+ Authentication.RSA,
+ Encryption.CAMELLIA128,
+ MessageDigest.SHA256,
+ Protocol.TLSv1_2,
+ false,
+ EncryptionLevel.HIGH,
+ true,
+ 128,
+ 128,
+ null,
+ null
+ ),
+ // Cipher C077
+ TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384(
+ 0xC077,
+ "ECDHE-RSA-CAMELLIA256-SHA384",
+ KeyExchange.EECDH,
+ Authentication.RSA,
+ Encryption.CAMELLIA256,
+ MessageDigest.SHA384,
+ Protocol.TLSv1_2,
+ false,
+ EncryptionLevel.HIGH,
+ true,
+ 256,
+ 256,
+ null,
+ null
+ ),
+ // Cipher C078
+ TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256(
+ 0xC078,
+ "ECDH-RSA-CAMELLIA128-SHA256",
+ KeyExchange.ECDHr,
+ Authentication.ECDH,
+ Encryption.CAMELLIA128,
+ MessageDigest.SHA256,
+ Protocol.TLSv1_2,
+ false,
+ EncryptionLevel.HIGH,
+ true,
+ 128,
+ 128,
+ null,
+ null
+ ),
+ // Cipher C079
+ TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384(
+ 0xC079,
+ "ECDH-RSA-CAMELLIA256-SHA384",
+ KeyExchange.ECDHr,
+ Authentication.ECDH,
+ Encryption.CAMELLIA256,
+ MessageDigest.SHA384,
+ Protocol.TLSv1_2,
+ false,
+ EncryptionLevel.HIGH,
+ true,
+ 256,
+ 256,
+ null,
+ null
+ ),
+
+ // Cipher C094
+ TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256(
+ 0xC094,
+ "PSK-CAMELLIA128-SHA256",
+ KeyExchange.PSK,
+ Authentication.PSK,
+ Encryption.CAMELLIA128,
+ MessageDigest.SHA256,
+ Protocol.TLSv1,
+ false,
+ EncryptionLevel.HIGH,
+ false,
+ 128,
+ 128,
+ null,
+ null
+ ),
+ // Cipher C095
+ TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384(
+ 0xC095,
+ "PSK-CAMELLIA256-SHA384",
+ KeyExchange.PSK,
+ Authentication.PSK,
+ Encryption.CAMELLIA256,
+ MessageDigest.SHA384,
+ Protocol.TLSv1,
+ false,
+ EncryptionLevel.HIGH,
+ false,
+ 256,
+ 256,
+ null,
+ null
+ ),
+ // Cipher C096
+ TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256(
+ 0xC096,
+ "DHE-PSK-CAMELLIA128-SHA256",
+ KeyExchange.DHEPSK,
+ Authentication.PSK,
+ Encryption.CAMELLIA128,
+ MessageDigest.SHA256,
+ Protocol.TLSv1,
+ false,
+ EncryptionLevel.HIGH,
+ false,
+ 128,
+ 128,
+ null,
+ null
+ ),
+ // Cipher C097
+ TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384(
+ 0xC097,
+ "DHE-PSK-CAMELLIA256-SHA384",
+ KeyExchange.DHEPSK,
+ Authentication.PSK,
+ Encryption.CAMELLIA256,
+ MessageDigest.SHA384,
+ Protocol.TLSv1,
+ false,
+ EncryptionLevel.HIGH,
+ false,
+ 256,
+ 256,
+ null,
+ null
+ ),
+ // Cipher C098
+ TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256(
+ 0xC098,
+ "RSA-PSK-CAMELLIA128-SHA256",
+ KeyExchange.RSAPSK,
+ Authentication.RSA,
+ Encryption.CAMELLIA128,
+ MessageDigest.SHA256,
+ Protocol.TLSv1,
+ false,
+ EncryptionLevel.HIGH,
+ false,
+ 128,
+ 128,
+ null,
+ null
+ ),
+ // Cipher C099
+ TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384(
+ 0xC099,
+ "RSA-PSK-CAMELLIA256-SHA384",
+ KeyExchange.RSAPSK,
+ Authentication.RSA,
+ Encryption.CAMELLIA256,
+ MessageDigest.SHA384,
+ Protocol.TLSv1,
+ false,
+ EncryptionLevel.HIGH,
+ false,
+ 256,
+ 256,
+ null,
+ null
+ ),
+ // Cipher C09A
+ TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256(
+ 0xC09A,
+ "ECDHE-PSK-CAMELLIA128-SHA256",
+ KeyExchange.ECDHEPSK,
+ Authentication.PSK,
+ Encryption.CAMELLIA128,
+ MessageDigest.SHA256,
+ Protocol.TLSv1,
+ false,
+ EncryptionLevel.HIGH,
+ false,
+ 128,
+ 128,
+ null,
+ null
+ ),
+ // Cipher C09B
+ TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384(
+ 0xC09B,
+ "ECDHE-PSK-CAMELLIA256-SHA384",
+ KeyExchange.ECDHEPSK,
+ Authentication.PSK,
+ Encryption.CAMELLIA256,
+ MessageDigest.SHA384,
+ Protocol.TLSv1,
+ false,
+ EncryptionLevel.HIGH,
+ false,
+ 256,
+ 256,
+ null,
+ null
+ ),
+ // CCM ciphersuites from RFC6655
+ // Cipher C09C
+ TLS_RSA_WITH_AES_128_CCM(
+ 0xC09C,
+ "AES128-CCM",
+ KeyExchange.RSA,
+ Authentication.RSA,
+ Encryption.AES128CCM,
+ MessageDigest.AEAD,
+ Protocol.TLSv1_2,
+ false,
+ EncryptionLevel.HIGH,
+ false,
+ 128,
+ 128,
+ null,
+ null
+ ),
+ // Cipher C09D
+ TLS_RSA_WITH_AES_256_CCM(
+ 0xC09D,
+ "AES256-CCM",
+ KeyExchange.RSA,
+ Authentication.RSA,
+ Encryption.AES256CCM,
+ MessageDigest.AEAD,
+ Protocol.TLSv1_2,
+ false,
+ EncryptionLevel.HIGH,
+ false,
+ 256,
+ 256,
+ null,
+ null
+ ),
+ // Cipher C09E
+ TLS_DHE_RSA_WITH_AES_128_CCM(
+ 0xC09E,
+ "DHE-RSA-AES128-CCM",
+ KeyExchange.EDH,
+ Authentication.RSA,
+ Encryption.AES128CCM,
+ MessageDigest.AEAD,
+ Protocol.TLSv1_2,
+ false,
+ EncryptionLevel.HIGH,
+ false,
+ 128,
+ 128,
+ null,
+ null
+ ),
+ // Cipher C09F
+ TLS_DHE_RSA_WITH_AES_256_CCM(
+ 0xC09F,
+ "DHE-RSA-AES256-CCM",
+ KeyExchange.EDH,
+ Authentication.RSA,
+ Encryption.AES256CCM,
+ MessageDigest.AEAD,
+ Protocol.TLSv1_2,
+ false,
+ EncryptionLevel.HIGH,
+ false,
+ 256,
+ 256,
+ null,
+ null
+ ),
+ // Cipher C0A0
+ TLS_RSA_WITH_AES_128_CCM_8(
+ 0xC0A0,
+ "AES128-CCM8",
+ KeyExchange.RSA,
+ Authentication.RSA,
+ Encryption.AES128CCM8,
+ MessageDigest.AEAD,
+ Protocol.TLSv1_2,
+ false,
+ EncryptionLevel.HIGH,
+ false,
+ 128,
+ 128,
+ null,
+ null
+ ),
+ // Cipher C0A1
+ TLS_RSA_WITH_AES_256_CCM_8(
+ 0xC0A1,
+ "AES256-CCM8",
+ KeyExchange.RSA,
+ Authentication.RSA,
+ Encryption.AES256CCM8,
+ MessageDigest.AEAD,
+ Protocol.TLSv1_2,
+ false,
+ EncryptionLevel.HIGH,
+ false,
+ 256,
+ 256,
+ null,
+ null
+ ),
+ // Cipher C0A2
+ TLS_DHE_RSA_WITH_AES_128_CCM_8(
+ 0xC0A2,
+ "DHE-RSA-AES128-CCM8",
+ KeyExchange.EDH,
+ Authentication.RSA,
+ Encryption.AES128CCM8,
+ MessageDigest.AEAD,
+ Protocol.TLSv1_2,
+ false,
+ EncryptionLevel.HIGH,
+ false,
+ 128,
+ 128,
+ null,
+ null
+ ),
+ // Cipher C0A3
+ TLS_DHE_RSA_WITH_AES_256_CCM_8(
+ 0xC0A3,
+ "DHE-RSA-AES256-CCM8",
+ KeyExchange.EDH,
+ Authentication.RSA,
+ Encryption.AES256CCM8,
+ MessageDigest.AEAD,
+ Protocol.TLSv1_2,
+ false,
+ EncryptionLevel.HIGH,
+ false,
+ 256,
+ 256,
+ null,
+ null
+ ),
+ // Cipher C0A4
+ TLS_PSK_WITH_AES_128_CCM(
+ 0xC0A4,
+ "PSK-AES128-CCM",
+ KeyExchange.PSK,
+ Authentication.PSK,
+ Encryption.AES128CCM,
+ MessageDigest.AEAD,
+ Protocol.TLSv1_2,
+ false,
+ EncryptionLevel.HIGH,
+ false,
+ 128,
+ 128,
+ null,
+ null
+ ),
+ // Cipher C0A5
+ TLS_PSK_WITH_AES_256_CCM(
+ 0xC0A5,
+ "PSK-AES256-CCM",
+ KeyExchange.PSK,
+ Authentication.PSK,
+ Encryption.AES256CCM,
+ MessageDigest.AEAD,
+ Protocol.TLSv1_2,
+ false,
+ EncryptionLevel.HIGH,
+ false,
+ 256,
+ 256,
+ null,
+ null
+ ),
+ // Cipher C0A6
+ TLS_DHE_PSK_WITH_AES_128_CCM(
+ 0xC0A6,
+ "DHE-PSK-AES128-CCM",
+ KeyExchange.DHEPSK,
+ Authentication.PSK,
+ Encryption.AES128CCM,
+ MessageDigest.AEAD,
+ Protocol.TLSv1_2,
+ false,
+ EncryptionLevel.HIGH,
+ false,
+ 128,
+ 128,
+ null,
+ null
+ ),
+ // Cipher C0A7
+ TLS_DHE_PSK_WITH_AES_256_CCM(
+ 0xC0A7,
+ "DHE-PSK-AES256-CCM",
+ KeyExchange.DHEPSK,
+ Authentication.PSK,
+ Encryption.AES256CCM,
+ MessageDigest.AEAD,
+ Protocol.TLSv1_2,
+ false,
+ EncryptionLevel.HIGH,
+ false,
+ 256,
+ 256,
+ null,
+ null
+ ),
+ // Cipher C0A8
+ TLS_PSK_WITH_AES_128_CCM_8(
+ 0xC0A8,
+ "PSK-AES128-CCM8",
+ KeyExchange.PSK,
+ Authentication.PSK,
+ Encryption.AES128CCM8,
+ MessageDigest.AEAD,
+ Protocol.TLSv1_2,
+ false,
+ EncryptionLevel.HIGH,
+ false,
+ 128,
+ 128,
+ null,
+ null
+ ),
+ // Cipher C0A9
+ TLS_PSK_WITH_AES_256_CCM_8(
+ 0xC0A9,
+ "PSK-AES256-CCM8",
+ KeyExchange.PSK,
+ Authentication.PSK,
+ Encryption.AES256CCM8,
+ MessageDigest.AEAD,
+ Protocol.TLSv1_2,
+ false,
+ EncryptionLevel.HIGH,
+ false,
+ 256,
+ 256,
+ null,
+ null
+ ),
+ // Cipher C0AA
+ TLS_PSK_DHE_WITH_AES_128_CCM_8(
+ 0xC0AA,
+ "DHE-PSK-AES128-CCM8",
+ KeyExchange.DHEPSK,
+ Authentication.PSK,
+ Encryption.AES128CCM8,
+ MessageDigest.AEAD,
+ Protocol.TLSv1_2,
+ false,
+ EncryptionLevel.HIGH,
+ false,
+ 128,
+ 128,
+ null,
+ null
+ ),
+ // Cipher C0AB
+ TLS_PSK_DHE_WITH_AES_256_CCM_8(
+ 0xC0AB,
+ "DHE-PSK-AES256-CCM8",
+ KeyExchange.DHEPSK,
+ Authentication.PSK,
+ Encryption.AES256CCM8,
+ MessageDigest.AEAD,
+ Protocol.TLSv1_2,
+ false,
+ EncryptionLevel.HIGH,
+ false,
+ 256,
+ 256,
+ null,
+ null
+ ),
+ // CCM ciphersuites from RFC7251
+ // Cipher C0AC
+ TLS_ECDHE_ECDSA_WITH_AES_128_CCM(
+ 0xC0AC,
+ "ECDHE-ECDSA-AES128-CCM",
+ KeyExchange.EECDH,
+ Authentication.ECDSA,
+ Encryption.AES128CCM,
+ MessageDigest.AEAD,
+ Protocol.TLSv1_2,
+ false,
+ EncryptionLevel.HIGH,
+ false,
+ 128,
+ 128,
+ null,
+ null
+ ),
+ // Cipher C0AD
+ TLS_ECDHE_ECDSA_WITH_AES_256_CCM(
+ 0xC0AD,
+ "ECDHE-ECDSA-AES256-CCM",
+ KeyExchange.EECDH,
+ Authentication.ECDSA,
+ Encryption.AES256CCM,
+ MessageDigest.AEAD,
+ Protocol.TLSv1_2,
+ false,
+ EncryptionLevel.HIGH,
+ false,
+ 256,
+ 256,
+ null,
+ null
+ ),
+ // Cipher C0AE
+ TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8(
+ 0xC0AE,
+ "ECDHE-ECDSA-AES128-CCM8",
+ KeyExchange.EECDH,
+ Authentication.ECDSA,
+ Encryption.AES128CCM8,
+ MessageDigest.AEAD,
+ Protocol.TLSv1_2,
+ false,
+ EncryptionLevel.HIGH,
+ false,
+ 128,
+ 128,
+ null,
+ null
+ ),
+ // Cipher C0AF
+ TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8(
+ 0xC0AF,
+ "ECDHE-ECDSA-AES256-CCM8",
+ KeyExchange.EECDH,
+ Authentication.ECDSA,
+ Encryption.AES256CCM8,
+ MessageDigest.AEAD,
+ Protocol.TLSv1_2,
+ false,
+ EncryptionLevel.HIGH,
+ false,
+ 256,
+ 256,
+ null,
+ null
+ ),
+ // Draft: https://tools.ietf.org/html/draft-ietf-tls-chacha20-poly1305-04
+ TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256(
+ 0xCCA8,
+ "ECDHE-RSA-CHACHA20-POLY1305",
+ KeyExchange.EECDH,
+ Authentication.RSA,
+ Encryption.CHACHA20POLY1305,
+ MessageDigest.AEAD,
+ Protocol.TLSv1_2,
+ false,
+ EncryptionLevel.HIGH,
+ false,
+ 256,
+ 256,
+ null,
+ null
+ ),
+ TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256(
+ 0xCCA9,
+ "ECDHE-ECDSA-CHACHA20-POLY1305",
+ KeyExchange.EECDH,
+ Authentication.ECDSA,
+ Encryption.CHACHA20POLY1305,
+ MessageDigest.AEAD,
+ Protocol.TLSv1_2,
+ false,
+ EncryptionLevel.HIGH,
+ false,
+ 256,
+ 256,
+ null,
+ null
+ ),
+ TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256(
+ 0xCCAA,
+ "DHE-RSA-CHACHA20-POLY1305",
+ KeyExchange.EDH,
+ Authentication.RSA,
+ Encryption.CHACHA20POLY1305,
+ MessageDigest.AEAD,
+ Protocol.TLSv1_2,
+ false,
+ EncryptionLevel.HIGH,
+ false,
+ 256,
+ 256,
+ null,
+ null
+ ),
+ TLS_PSK_WITH_CHACHA20_POLY1305_SHA256(
+ 0xCCAB,
+ "PSK-CHACHA20-POLY1305",
+ KeyExchange.PSK,
+ Authentication.PSK,
+ Encryption.CHACHA20POLY1305,
+ MessageDigest.AEAD,
+ Protocol.TLSv1_2,
+ false,
+ EncryptionLevel.HIGH,
+ false,
+ 256,
+ 256,
+ null,
+ null
+ ),
+ TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256(
+ 0xCCAC,
+ "ECDHE-PSK-CHACHA20-POLY1305",
+ KeyExchange.ECDHEPSK,
+ Authentication.PSK,
+ Encryption.CHACHA20POLY1305,
+ MessageDigest.AEAD,
+ Protocol.TLSv1_2,
+ false,
+ EncryptionLevel.HIGH,
+ false,
+ 256,
+ 256,
+ null,
+ null
+ ),
+ TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256(
+ 0xCCAD,
+ "DHE-PSK-CHACHA20-POLY1305",
+ KeyExchange.DHEPSK,
+ Authentication.PSK,
+ Encryption.CHACHA20POLY1305,
+ MessageDigest.AEAD,
+ Protocol.TLSv1_2,
+ false,
+ EncryptionLevel.HIGH,
+ false,
+ 256,
+ 256,
+ null,
+ null
+ ),
+ TLS_RSA_PSK_WITH_CHACHA20_POLY1305_SHA256(
+ 0xCCAE,
+ "RSA-PSK-CHACHA20-POLY1305",
+ KeyExchange.RSAPSK,
+ Authentication.RSA,
+ Encryption.CHACHA20POLY1305,
+ MessageDigest.AEAD,
+ Protocol.TLSv1_2,
+ false,
+ EncryptionLevel.HIGH,
+ false,
+ 256,
+ 256,
+ null,
+ null
+ ),
+
+ // Cipher 0x010080 (SSLv2)
+ // RC4_128_WITH_MD5
+ SSL_CK_RC4_128_WITH_MD5(
+ -1,
+ "RC4-MD5",
+ KeyExchange.RSA,
+ Authentication.RSA,
+ Encryption.RC4,
+ MessageDigest.MD5,
+ Protocol.SSLv2,
+ false,
+ EncryptionLevel.MEDIUM,
+ false,
+ 128,
+ 128,
+ null,
+ null
+ ),
+ // Cipher 0x020080 (SSLv2)
+ SSL2_RC4_128_EXPORT40_WITH_MD5(
+ -1,
+ "EXP-RC4-MD5",
+ KeyExchange.RSA,
+ Authentication.RSA,
+ Encryption.RC4,
+ MessageDigest.MD5,
+ Protocol.SSLv2,
+ true,
+ EncryptionLevel.EXP40,
+ false,
+ 40,
+ 128,
+ new String[] {"SSL_RC4_128_EXPORT40_WITH_MD5"},
+ null
+ ),
+ // Cipher 0x030080 (SSLv2)
+ // RC2_128_CBC_WITH_MD5
+ SSL_CK_RC2_128_CBC_WITH_MD5(
+ -1,
+ "RC2-CBC-MD5",
+ KeyExchange.RSA,
+ Authentication.RSA,
+ Encryption.RC2,
+ MessageDigest.MD5,
+ Protocol.SSLv2,
+ false,
+ EncryptionLevel.MEDIUM,
+ false,
+ 128,
+ 128,
+ null,
+ null
+ ),
+ // Cipher 0x040080 (SSLv2)
+ // RC2_128_CBC_EXPORT40_WITH_MD5
+ SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5(
+ -1,
+ "EXP-RC2-CBC-MD5",
+ KeyExchange.RSA,
+ Authentication.RSA,
+ Encryption.RC2,
+ MessageDigest.MD5,
+ Protocol.SSLv2,
+ true,
+ EncryptionLevel.EXP40,
+ false,
+ 40,
+ 128,
+ null,
+ null
+ ),
+ // Cipher 0x050080 (SSLv2)
+ // IDEA_128_CBC_WITH_MD5
+ SSL2_IDEA_128_CBC_WITH_MD5(
+ -1,
+ "IDEA-CBC-MD5",
+ KeyExchange.RSA,
+ Authentication.RSA,
+ Encryption.IDEA,
+ MessageDigest.MD5,
+ Protocol.SSLv2,
+ false, EncryptionLevel.MEDIUM,
+ false,
+ 128,
+ 128,
+ new String[] {"SSL_CK_IDEA_128_CBC_WITH_MD5"},
+ null
+ ),
+ // Cipher 0x060040 (SSLv2)
+ // DES_64_CBC_WITH_MD5
+ SSL2_DES_64_CBC_WITH_MD5(
+ -1,
+ "DES-CBC-MD5",
+ KeyExchange.RSA,
+ Authentication.RSA,
+ Encryption.DES,
+ MessageDigest.MD5,
+ Protocol.SSLv2,
+ false,
+ EncryptionLevel.LOW,
+ false,
+ 56,
+ 56,
+ new String[] {"SSL_CK_DES_64_CBC_WITH_MD5"},
+ null
+ ),
+ // Cipher 0x0700C0 (SSLv2)
+ // DES_192_EDE3_CBC_WITH_MD5
+ SSL2_DES_192_EDE3_CBC_WITH_MD5(
+ -1,
+ "DES-CBC3-MD5",
+ KeyExchange.RSA,
+ Authentication.RSA,
+ Encryption.TRIPLE_DES,
+ MessageDigest.MD5,
+ Protocol.SSLv2,
+ false,
+ EncryptionLevel.MEDIUM,
+ false,
+ 112,
+ 168,
+ new String[] {"SSL_CK_DES_192_EDE3_CBC_WITH_MD5"},
+ null
+ );
+
+ /* TEMP_GOST_TLS*/
+ /*
+ // Cipher FF00
+ TLS_GOSTR341094_RSA_WITH_28147_CNT_MD5(
+ "GOST-MD5",
+ KeyExchange.RSA,
+ Authentication.RSA,
+ Encryption.eGOST2814789CNT,
+ MessageDigest.MD5,
+ Protocol.TLSv1,
+ false,
+ EncryptionLevel.HIGH,
+ false,
+ 256,
+ 256
+ ),
+ TLS_RSA_WITH_28147_CNT_GOST94(
+ "GOST-GOST94",
+ KeyExchange.RSA,
+ Authentication.RSA,
+ Encryption.eGOST2814789CNT,
+ MessageDigest.GOST94,
+ Protocol.TLSv1,
+ false, EncryptionLevel.HIGH,false,
+ 256,
+ 256
+ ),
+ {
+ 1,
+ "GOST-GOST89MAC",
+ 0x0300ff02,
+ KeyExchange.RSA,
+ Authentication.RSA,
+ Encryption.eGOST2814789CNT,
+ MessageDigest.GOST89MAC,
+ Protocol.TLSv1,
+ false, EncryptionLevel.HIGH,false,
+
+ 256,
+ 256
+ ),
+ {
+ 1,
+ "GOST-GOST89STREAM",
+ 0x0300ff03,
+ KeyExchange.RSA,
+ Authentication.RSA,
+ Encryption.eGOST2814789CNT,
+ MessageDigest.GOST89MAC,
+ Protocol.TLSv1,
+ false, EncryptionLevel.HIGH,false,
+ 256,
+ 256
+ },*/
+
+
+ private final int id;
+ private final String openSSLAlias;
+ private final Set<String> openSSLAltNames;
+ private final Set<String> jsseNames;
+ private final KeyExchange kx;
+ private final Authentication au;
+ private final Encryption enc;
+ private final MessageDigest mac;
+ private final Protocol protocol;
+ private final boolean export;
+ private final EncryptionLevel level;
+ private final boolean fipsCompatible;
+ /**
+ * Number of bits really used
+ */
+ private final int strength_bits;
+ /**
+ * Number of bits for algorithm
+ */
+ private final int alg_bits;
+
+ private 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) {
+ Set<String> altNames = new HashSet<>();
+ altNames.addAll(Arrays.asList(openSSlAltNames));
+ this.openSSLAltNames = Collections.unmodifiableSet(altNames);
+ } else {
+ this.openSSLAltNames = Collections.emptySet();
+ }
+ Set<String> jsseNames = new LinkedHashSet<>();
+ if (jsseAltNames != null && jsseAltNames.length != 0) {
+ jsseNames.addAll(Arrays.asList(jsseAltNames));
+ }
+ jsseNames.add(name());
+ this.jsseNames = Collections.unmodifiableSet(jsseNames);
+ this.kx = kx;
+ this.au = au;
+ this.enc = enc;
+ this.mac = mac;
+ this.protocol = protocol;
+ this.export = export;
+ this.level = level;
+ this.fipsCompatible = fipsCompatible;
+ this.strength_bits = strength_bits;
+ this.alg_bits = alg_bits;
+ }
+
+ public int getId() {
+ return id;
+ }
+
+ public String getOpenSSLAlias() {
+ return openSSLAlias;
+ }
+
+ public Set<String> getOpenSSLAltNames() {
+ return openSSLAltNames;
+ }
+
+ public Set<String> getJsseNames() {
+ return jsseNames;
+ }
+
+ public KeyExchange getKx() {
+ return kx;
+ }
+
+ public Authentication getAu() {
+ return au;
+ }
+
+ public Encryption getEnc() {
+ return enc;
+ }
+
+ public MessageDigest getMac() {
+ return mac;
+ }
+
+ public Protocol getProtocol() {
+ return protocol;
+ }
+
+ public boolean isExport() {
+ return export;
+ }
+
+ public EncryptionLevel getLevel() {
+ return level;
+ }
+
+ public boolean isFipsCompatible() {
+ return fipsCompatible;
+ }
+
+ public int getStrength_bits() {
+ return strength_bits;
+ }
+
+ public int getAlg_bits() {
+ return alg_bits;
+ }
+
+
+ private static final Map<Integer,Cipher> idMap = new HashMap<>();
+
+ static {
+ for (Cipher cipher : Cipher.values()) {
+ int id = cipher.getId();
+
+ if (id > 0 && id < 0xFFFF) {
+ idMap.put(Integer.valueOf(id), cipher);
+ }
+ }
+ }
+
+
+ public static Cipher valueOf(int cipherId) {
+ return idMap.get(Integer.valueOf(cipherId));
+ }
+}
diff --git a/java/org/apache/tomcat/util/net/openssl/ciphers/Encryption.java b/java/org/apache/tomcat/util/net/openssl/ciphers/Encryption.java
new file mode 100644
index 0000000..56d6bbc
--- /dev/null
+++ b/java/org/apache/tomcat/util/net/openssl/ciphers/Encryption.java
@@ -0,0 +1,41 @@
+/*
+ * 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;
+
+enum Encryption {
+ AES128,
+ AES128CCM,
+ AES128CCM8,
+ AES128GCM,
+ AES256,
+ AES256CCM,
+ AES256CCM8,
+ AES256GCM,
+ CAMELLIA256,
+ CAMELLIA128,
+ CHACHA20POLY1305,
+ TRIPLE_DES,
+ DES,
+ IDEA,
+ eGOST2814789CNT,
+ SEED,
+ FZA,
+ RC4,
+ RC2,
+ eNULL;
+}
diff --git a/java/org/apache/tomcat/util/net/openssl/ciphers/EncryptionLevel.java b/java/org/apache/tomcat/util/net/openssl/ciphers/EncryptionLevel.java
new file mode 100644
index 0000000..f779df2
--- /dev/null
+++ b/java/org/apache/tomcat/util/net/openssl/ciphers/EncryptionLevel.java
@@ -0,0 +1,28 @@
+/*
+ * 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;
+
+enum EncryptionLevel {
+ STRONG_NONE,
+ EXP40,
+ EXP56,
+ LOW,
+ MEDIUM,
+ HIGH,
+ FIPS;
+}
diff --git a/java/org/apache/tomcat/util/net/openssl/ciphers/KeyExchange.java b/java/org/apache/tomcat/util/net/openssl/ciphers/KeyExchange.java
new file mode 100644
index 0000000..70ab0b7
--- /dev/null
+++ b/java/org/apache/tomcat/util/net/openssl/ciphers/KeyExchange.java
@@ -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.tomcat.util.net.openssl.ciphers;
+
+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 */,
+ 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 */,
+ RSAPSK,
+ ECDHEPSK,
+ DHEPSK;
+}
diff --git a/java/org/apache/tomcat/util/net/openssl/ciphers/LocalStrings.properties b/java/org/apache/tomcat/util/net/openssl/ciphers/LocalStrings.properties
new file mode 100644
index 0000000..4d0a321
--- /dev/null
+++ b/java/org/apache/tomcat/util/net/openssl/ciphers/LocalStrings.properties
@@ -0,0 +1,16 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+converter.mapping=Cipher suite mapping: [{0}] => [{1}]
diff --git a/java/org/apache/tomcat/util/net/openssl/ciphers/MessageDigest.java b/java/org/apache/tomcat/util/net/openssl/ciphers/MessageDigest.java
new file mode 100644
index 0000000..718a002
--- /dev/null
+++ b/java/org/apache/tomcat/util/net/openssl/ciphers/MessageDigest.java
@@ -0,0 +1,28 @@
+/*
+ * 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;
+
+enum MessageDigest {
+ MD5,
+ SHA1,
+ GOST94,
+ GOST89MAC,
+ SHA256,
+ SHA384,
+ AEAD;
+}
diff --git a/java/org/apache/tomcat/util/net/openssl/ciphers/OpenSSLCipherConfigurationParser.java b/java/org/apache/tomcat/util/net/openssl/ciphers/OpenSSLCipherConfigurationParser.java
new file mode 100644
index 0000000..80cbf48
--- /dev/null
+++ b/java/org/apache/tomcat/util/net/openssl/ciphers/OpenSSLCipherConfigurationParser.java
@@ -0,0 +1,827 @@
+/*
+ * 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.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
+import org.apache.tomcat.util.net.Constants;
+import org.apache.tomcat.util.res.StringManager;
+
+/**
+ * Class in charge with parsing openSSL expressions to define a list of ciphers.
+ */
+public class OpenSSLCipherConfigurationParser {
+
+ private static final Log log = LogFactory.getLog(OpenSSLCipherConfigurationParser.class);
+ private static final StringManager sm =
+ StringManager.getManager("org.apache.tomcat.util.net.jsse.res");
+
+ private static boolean initialized = false;
+
+ 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.
+ */
+ 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.
+ */
+ 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.
+ */
+ 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.
+ */
+ private static final String AND = "+";
+ /**
+ * All ciphers by their openssl alias name.
+ */
+ private static final Map<String, List<Cipher>> aliases = new LinkedHashMap<>();
+
+ /**
+ * 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.
+ */
+ 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.
+ */
+ private static final String HIGH = "HIGH";
+ /**
+ * '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.
+ */
+ private static final String LOW = "LOW";
+ /**
+ * Export encryption algorithms. Including 40 and 56 bits algorithms.
+ */
+ private static final String EXPORT = "EXPORT";
+ /**
+ * 40 bit export encryption algorithms.
+ */
+ private static final String EXPORT40 = "EXPORT40";
+ /**
+ * 56 bit export encryption algorithms.
+ */
+ private static final String EXPORT56 = "EXPORT56";
+ /**
+ * Cipher suites using RSA key exchange.
+ */
+ private static final String kRSA = "kRSA";
+ /**
+ * Cipher suites using RSA authentication.
+ */
+ private static final String aRSA = "aRSA";
+ /**
+ * Cipher suites using RSA for key exchange
+ * Despite what the docs say, RSA is equivalent to kRSA.
+ */
+ private static final String RSA = "RSA";
+ /**
+ * Cipher suites using ephemeral DH key agreement.
+ */
+ private static final String kEDH = "kEDH";
+ /**
+ * Cipher suites using ephemeral DH key agreement.
+ */
+ private static final String kDHE = "kDHE";
+ /**
+ * Cipher suites using ephemeral DH key agreement. equivalent to kEDH:-ADH
+ */
+ private static final String EDH = "EDH";
+ /**
+ * Cipher suites using ephemeral DH key agreement. equivalent to kEDH:-ADH
+ */
+ private static final String DHE = "DHE";
+ /**
+ * Cipher suites using DH key agreement and DH certificates signed by CAs with RSA keys.
+ */
+ private static final String kDHr = "kDHr";
+ /**
+ * Cipher suites using DH key agreement and DH certificates signed by CAs with DSS keys.
+ */
+ private static final String kDHd = "kDHd";
+ /**
+ * Cipher suites using DH key agreement and DH certificates signed by CAs with RSA or DSS keys.
+ */
+ private static final String kDH = "kDH";
+ /**
+ * Cipher suites using fixed ECDH key agreement signed by CAs with RSA keys.
+ */
+ private static final String kECDHr = "kECDHr";
+ /**
+ * Cipher suites using fixed ECDH key agreement signed by CAs with ECDSA keys.
+ */
+ private static final String kECDHe = "kECDHe";
+ /**
+ * Cipher suites using fixed ECDH key agreement signed by CAs with RSA and ECDSA keys or either respectively.
+ */
+ private static final String kECDH = "kECDH";
+ /**
+ * Cipher suites using ephemeral ECDH key agreement, including anonymous cipher suites.
+ */
+ private static final String kEECDH = "kEECDH";
+ /**
+ * 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.
+ */
+ private static final String ECDH = "ECDH";
+ /**
+ * Cipher suites using ephemeral ECDH key agreement, including anonymous cipher suites.
+ */
+ private static final String kECDHE = "kECDHE";
+ /**
+ * Cipher suites using authenticated ephemeral ECDH key agreement
+ */
+ private static final String ECDHE = "ECDHE";
+ /**
+ * Cipher suites using authenticated ephemeral ECDH key agreement
+ */
+ private static final String EECDHE = "EECDHE";
+ /**
+ * Anonymous Elliptic Curve Diffie Hellman cipher suites.
+ */
+ private static final String AECDH = "AECDH";
+ /**
+ * Cipher suites using DSS for key exchange
+ */
+ private static final String DSS = "DSS";
+ /**
+ * Cipher suites using DSS authentication, i.e. the certificates carry DSS keys.
+ */
+ private static final String aDSS = "aDSS";
+ /**
+ * Cipher suites effectively using DH authentication, i.e. the certificates carry DH keys.
+ */
+ private static final String aDH = "aDH";
+ /**
+ * Cipher suites effectively using ECDH authentication, i.e. the certificates carry ECDH keys.
+ */
+ private static final String aECDH = "aECDH";
+ /**
+ * Cipher suites effectively using ECDSA authentication, i.e. the certificates carry ECDSA keys.
+ */
+ private static final String aECDSA = "aECDSA";
+ /**
+ * Cipher suites effectively using ECDSA authentication, i.e. the certificates carry ECDSA keys.
+ */
+ private static final String ECDSA = "ECDSA";
+ /**
+ * Ciphers suites using FORTEZZA key exchange algorithms.
+ */
+ private static final String kFZA = "kFZA";
+ /**
+ * Ciphers suites using FORTEZZA authentication algorithms.
+ */
+ private static final String aFZA = "aFZA";
+ /**
+ * Ciphers suites using FORTEZZA encryption algorithms.
+ */
+ private static final String eFZA = "eFZA";
+ /**
+ * Ciphers suites using all FORTEZZA algorithms.
+ */
+ private static final String FZA = "FZA";
+ /**
+ * Cipher suites using DH, including anonymous DH, ephemeral DH and fixed DH.
+ */
+ private static final String DH = "DH";
+ /**
+ * Anonymous DH cipher suites.
+ */
+ private static final String ADH = "ADH";
+ /**
+ * Cipher suites using 128 bit AES.
+ */
+ private static final String AES128 = "AES128";
+ /**
+ * Cipher suites using 256 bit AES.
+ */
+ private static final String AES256 = "AES256";
+ /**
+ * Cipher suites using either 128 or 256 bit AES.
+ */
+ private static final String AES = "AES";
+ /**
+ * AES in Galois Counter Mode (GCM): these cipher suites are only supported in TLS v1.2.
+ */
+ private static final String AESGCM = "AESGCM";
+ /**
+ * AES in Counter with CBC-MAC Mode (CCM).
+ */
+ private static final String AESCCM = "AESCCM";
+ /**
+ * AES in Counter with CBC-MAC Mode and 8-byte authentication (CCM8).
+ */
+ private static final String AESCCM8 = "AESCCM8";
+ /**
+ * Cipher suites using 128 bit CAMELLIA.
+ */
+ private static final String CAMELLIA128 = "CAMELLIA128";
+ /**
+ * Cipher suites using 256 bit CAMELLIA.
+ */
+ private static final String CAMELLIA256 = "CAMELLIA256";
+ /**
+ * Cipher suites using either 128 or 256 bit CAMELLIA.
+ */
+ private static final String CAMELLIA = "CAMELLIA";
+ /**
+ * Cipher suites using CHACHA20.
+ */
+ private static final String CHACHA20 = "CHACHA20";
+ /**
+ * Cipher suites using triple DES.
+ */
+ private static final String TRIPLE_DES = "3DES";
+ /**
+ * Cipher suites using DES (not triple DES).
+ */
+ private static final String DES = "DES";
+ /**
+ * Cipher suites using RC4.
+ */
+ private static final String RC4 = "RC4";
+ /**
+ * Cipher suites using RC2.
+ */
+ private static final String RC2 = "RC2";
+ /**
+ * Cipher suites using IDEA.
+ */
+ private static final String IDEA = "IDEA";
+ /**
+ * Cipher suites using SEED.
+ */
+ private static final String SEED = "SEED";
+ /**
+ * Cipher suites using MD5.
+ */
+ private static final String MD5 = "MD5";
+ /**
+ * Cipher suites using SHA1.
+ */
+ private static final String SHA1 = "SHA1";
+ /**
+ * Cipher suites using SHA1.
+ */
+ private static final String SHA = "SHA";
+ /**
+ * Cipher suites using SHA256.
+ */
+ private static final String SHA256 = "SHA256";
+ /**
+ * Cipher suites using SHA384.
+ */
+ private static final String SHA384 = "SHA384";
+ /**
+ * Cipher suites using KRB5.
+ */
+ private static final String KRB5 = "KRB5";
+ /**
+ * Cipher suites using GOST R 34.10 (either 2001 or 94) for authentication.
+ */
+ private static final String aGOST = "aGOST";
+ /**
+ * Cipher suites using GOST R 34.10-2001 for authentication.
+ */
+ private static final String aGOST01 = "aGOST01";
+ /**
+ * Cipher suites using GOST R 34.10-94 authentication (note that R 34.10-94 standard has been expired so use GOST R
+ * 34.10-2001)
+ */
+ private static final String aGOST94 = "aGOST94";
+ /**
+ * Cipher suites using using VKO 34.10 key exchange, specified in the RFC 4357.
+ */
+ private static final String kGOST = "kGOST";
+ /**
+ * Cipher suites, using HMAC based on GOST R 34.11-94.
+ */
+ private static final String GOST94 = "GOST94";
+ /**
+ * Cipher suites using GOST 28147-89 MAC instead of HMAC.
+ */
+ private static final String GOST89MAC = "GOST89MAC";
+ /**
+ * Cipher suites using SRP authentication, specified in the RFC 5054.
+ */
+ private static final String aSRP = "aSRP";
+ /**
+ * Cipher suites using SRP key exchange, specified in the RFC 5054.
+ */
+ private static final String kSRP = "kSRP";
+ /**
+ * Same as kSRP
+ */
+ private static final String SRP = "SRP";
+ /**
+ * Cipher suites using pre-shared keys (PSK).
+ */
+ private static final String PSK = "PSK";
+ /**
+ * Cipher suites using PSK authentication.
+ */
+ private static final String aPSK = "aPSK";
+ /**
+ * Cipher suites using PSK key 'exchange'.
+ */
+ private static final String kPSK = "kPSK";
+ private static final String kRSAPSK = "kRSAPSK";
+ private static final String kECDHEPSK = "kECDHEPSK";
+ private static final String kDHEPSK = "kDHEPSK";
+
+ private static final String DEFAULT = "DEFAULT";
+ private static final String COMPLEMENTOFDEFAULT = "COMPLEMENTOFDEFAULT";
+
+ private static final String ALL = "ALL";
+ private static final String COMPLEMENTOFALL = "COMPLEMENTOFALL";
+
+ private static final Map<String,String> jsseToOpenSSL = new HashMap<>();
+
+ private static final void init() {
+
+ for (Cipher cipher : Cipher.values()) {
+ String alias = cipher.getOpenSSLAlias();
+ if (aliases.containsKey(alias)) {
+ aliases.get(alias).add(cipher);
+ } else {
+ List<Cipher> list = new ArrayList<>();
+ list.add(cipher);
+ aliases.put(alias, list);
+ }
+ aliases.put(cipher.name(), Collections.singletonList(cipher));
+
+ for (String openSSlAltName : cipher.getOpenSSLAltNames()) {
+ if (aliases.containsKey(openSSlAltName)) {
+ aliases.get(openSSlAltName).add(cipher);
+ } else {
+ List<Cipher> list = new ArrayList<>();
+ list.add(cipher);
+ aliases.put(openSSlAltName, list);
+ }
+
+ }
+
+ jsseToOpenSSL.put(cipher.name(), cipher.getOpenSSLAlias());
+ Set<String> jsseNames = cipher.getJsseNames();
+ for (String jsseName : jsseNames) {
+ jsseToOpenSSL.put(jsseName, cipher.getOpenSSLAlias());
+ }
+ }
+ List<Cipher> allCiphersList = Arrays.asList(Cipher.values());
+ Collections.reverse(allCiphersList);
+ LinkedHashSet<Cipher> allCiphers = defaultSort(new LinkedHashSet<>(allCiphersList));
+ addListAlias(eNULL, filterByEncryption(allCiphers, Collections.singleton(Encryption.eNULL)));
+ LinkedHashSet<Cipher> all = new LinkedHashSet<>(allCiphers);
+ remove(all, eNULL);
+ addListAlias(ALL, all);
+ 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))));
+ aliases.put("EXP", aliases.get(EXPORT));
+ addListAlias(EXPORT40, filterByEncryptionLevel(allCiphers, Collections.singleton(EncryptionLevel.EXP40)));
+ addListAlias(EXPORT56, filterByEncryptionLevel(allCiphers, Collections.singleton(EncryptionLevel.EXP56)));
+ aliases.put("NULL", aliases.get(eNULL));
+ aliases.put(COMPLEMENTOFALL, aliases.get(eNULL));
+ addListAlias(aNULL, filterByAuthentication(allCiphers, Collections.singleton(Authentication.aNULL)));
+ addListAlias(kRSA, filterByKeyExchange(allCiphers, Collections.singleton(KeyExchange.RSA)));
+ addListAlias(aRSA, filterByAuthentication(allCiphers, Collections.singleton(Authentication.RSA)));
+ // Despite what the docs say, RSA is equivalent to kRSA
+ aliases.put(RSA, aliases.get(kRSA));
+ addListAlias(kEDH, filterByKeyExchange(allCiphers, Collections.singleton(KeyExchange.EDH)));
+ addListAlias(kDHE, filterByKeyExchange(allCiphers, Collections.singleton(KeyExchange.EDH)));
+ Set<Cipher> edh = filterByKeyExchange(allCiphers, Collections.singleton(KeyExchange.EDH));
+ edh.removeAll(filterByAuthentication(allCiphers, Collections.singleton(Authentication.aNULL)));
+ addListAlias(EDH, edh);
+ 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(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(kECDHE, filterByKeyExchange(allCiphers, Collections.singleton(KeyExchange.EECDH)));
+
+ Set<Cipher> ecdhe = filterByKeyExchange(allCiphers, Collections.singleton(KeyExchange.EECDH));
+ remove(ecdhe, aNULL);
+ addListAlias(ECDHE, ecdhe);
+
+ addListAlias(kEECDH, filterByKeyExchange(allCiphers, Collections.singleton(KeyExchange.EECDH)));
+ aliases.put(EECDHE, aliases.get(kEECDH));
+ Set<Cipher> eecdh = filterByKeyExchange(allCiphers, Collections.singleton(KeyExchange.EECDH));
+ eecdh.removeAll(filterByAuthentication(allCiphers, Collections.singleton(Authentication.aNULL)));
+ addListAlias(EECDH, eecdh);
+ addListAlias(aDSS, filterByAuthentication(allCiphers, Collections.singleton(Authentication.DSS)));
+ aliases.put(DSS, aliases.get(aDSS));
+ addListAlias(aDH, filterByAuthentication(allCiphers, Collections.singleton(Authentication.DH)));
+ Set<Cipher> aecdh = filterByKeyExchange(allCiphers, Collections.singleton(KeyExchange.EECDH));
+ addListAlias(AECDH, filterByAuthentication(aecdh, Collections.singleton(Authentication.aNULL)));
+ addListAlias(aECDH, filterByAuthentication(allCiphers, Collections.singleton(Authentication.ECDH)));
+ addListAlias(ECDSA, filterByAuthentication(allCiphers, Collections.singleton(Authentication.ECDSA)));
+ aliases.put(aECDSA, aliases.get(ECDSA));
+ 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(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))));
+ Set<Cipher> 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(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)));
+ addListAlias(TRIPLE_DES, filterByEncryption(allCiphers, Collections.singleton(Encryption.TRIPLE_DES)));
+ addListAlias(DES, filterByEncryption(allCiphers, Collections.singleton(Encryption.DES)));
+ addListAlias(RC4, filterByEncryption(allCiphers, Collections.singleton(Encryption.RC4)));
+ addListAlias(RC2, filterByEncryption(allCiphers, Collections.singleton(Encryption.RC2)));
+ addListAlias(IDEA, filterByEncryption(allCiphers, Collections.singleton(Encryption.IDEA)));
+ addListAlias(SEED, filterByEncryption(allCiphers, Collections.singleton(Encryption.SEED)));
+ addListAlias(MD5, filterByMessageDigest(allCiphers, Collections.singleton(MessageDigest.MD5)));
+ addListAlias(SHA1, filterByMessageDigest(allCiphers, Collections.singleton(MessageDigest.SHA1)));
+ 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(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(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(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"));
+ // COMPLEMENTOFDEFAULT is also not exactly as defined by the docs
+ LinkedHashSet<Cipher> complementOfDefault = filterByKeyExchange(all, new HashSet<>(Arrays.asList(KeyExchange.EDH,KeyExchange.EECDH)));
+ complementOfDefault = filterByAuthentication(complementOfDefault, Collections.singleton(Authentication.aNULL));
+ complementOfDefault.removeAll(aliases.get(eNULL));
+ complementOfDefault.addAll(aliases.get(Constants.SSL_PROTO_SSLv2));
+ complementOfDefault.addAll(aliases.get(EXPORT));
+ complementOfDefault.addAll(aliases.get(DES));
+ complementOfDefault.addAll(aliases.get(TRIPLE_DES));
+ complementOfDefault.addAll(aliases.get(RC2));
+ complementOfDefault.addAll(aliases.get(RC4));
+ complementOfDefault.addAll(aliases.get(aDSS));
+ complementOfDefault.addAll(aliases.get(SEED));
+ complementOfDefault.addAll(aliases.get(IDEA));
+ complementOfDefault.addAll(aliases.get(CAMELLIA));
+ complementOfDefault.addAll(aliases.get(AESCCM));
+ defaultSort(complementOfDefault);
+ addListAlias(COMPLEMENTOFDEFAULT, complementOfDefault);
+ }
+
+ static void addListAlias(String alias, Set<Cipher> ciphers) {
+ aliases.put(alias, new ArrayList<>(ciphers));
+ }
+
+ static void moveToEnd(final LinkedHashSet<Cipher> ciphers, final String alias) {
+ moveToEnd(ciphers, aliases.get(alias));
+ }
+
+ static void moveToEnd(final LinkedHashSet<Cipher> ciphers, final Collection<Cipher> toBeMovedCiphers) {
+ List<Cipher> movedCiphers = new ArrayList<>(toBeMovedCiphers);
+ movedCiphers.retainAll(ciphers);
+ ciphers.removeAll(movedCiphers);
+ ciphers.addAll(movedCiphers);
+ }
+
+ static void moveToStart(final LinkedHashSet<Cipher> ciphers, final Collection<Cipher> toBeMovedCiphers) {
+ List<Cipher> movedCiphers = new ArrayList<>(toBeMovedCiphers);
+ List<Cipher> originalCiphers = new ArrayList<>(ciphers);
+ movedCiphers.retainAll(ciphers);
+ ciphers.clear();
+ ciphers.addAll(movedCiphers);
+ ciphers.addAll(originalCiphers);
+ }
+
+ static void add(final LinkedHashSet<Cipher> ciphers, final String alias) {
+ ciphers.addAll(aliases.get(alias));
+ }
+
+ static void remove(final Set<Cipher> ciphers, final String alias) {
+ ciphers.removeAll(aliases.get(alias));
+ }
+
+ static LinkedHashSet<Cipher> strengthSort(final LinkedHashSet<Cipher> 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.
+ */
+ Set<Integer> keySizes = new HashSet<>();
+ for (Cipher cipher : ciphers) {
+ keySizes.add(Integer.valueOf(cipher.getStrength_bits()));
+ }
+ List<Integer> strength_bits = new ArrayList<>(keySizes);
+ Collections.sort(strength_bits);
+ Collections.reverse(strength_bits);
+ final LinkedHashSet<Cipher> result = new LinkedHashSet<>(ciphers);
+ for (int strength : strength_bits) {
+ moveToEnd(result, filterByStrengthBits(ciphers, strength));
+ }
+ return result;
+ }
+
+ /*
+ * See
+ * https://github.com/openssl/openssl/blob/7c96dbcdab959fef74c4caae63cdebaa354ab252/ssl/ssl_ciph.c#L1371
+ */
+ static LinkedHashSet<Cipher> defaultSort(final LinkedHashSet<Cipher> ciphers) {
+ final LinkedHashSet<Cipher> result = new LinkedHashSet<>(ciphers.size());
+ final LinkedHashSet<Cipher> ecdh = new LinkedHashSet<>(ciphers.size());
+
+ /* Everything else being equal, prefer ephemeral ECDH over other key exchange mechanisms */
+ ecdh.addAll(filterByKeyExchange(ciphers, Collections.singleton(KeyExchange.EECDH)));
+
+ /* AES is our preferred symmetric cipher */
+ Set<Encryption> 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));
+ result.addAll(filterByEncryption(ciphers, aes));
+
+ /* Add everything else */
+ result.addAll(ecdh);
+ result.addAll(ciphers);
+
+ /* 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.) */
+ moveToEnd(result, filterByAuthentication(result, Collections.singleton(Authentication.aNULL)));
+
+ /* Move ciphers without forward secrecy to the end */
+ moveToEnd(result, filterByAuthentication(result, Collections.singleton(Authentication.ECDH)));
+ 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 */
+ moveToEnd(result, filterByEncryption(result, Collections.singleton(Encryption.RC4)));
+ return strengthSort(result);
+ }
+
+ static Set<Cipher> filterByStrengthBits(Set<Cipher> ciphers, int strength_bits) {
+ Set<Cipher> result = new LinkedHashSet<>(ciphers.size());
+ for (Cipher cipher : ciphers) {
+ if (cipher.getStrength_bits() == strength_bits) {
+ result.add(cipher);
+ }
+ }
+ return result;
+ }
+
+ static Set<Cipher> filterByProtocol(Set<Cipher> ciphers, Set<Protocol> protocol) {
+ return filter(ciphers, protocol, null, null, null, null, null);
+ }
+
+ static LinkedHashSet<Cipher> filterByKeyExchange(Set<Cipher> ciphers, Set<KeyExchange> kx) {
+ return filter(ciphers, null, kx, null, null, null, null);
+ }
+
+ static LinkedHashSet<Cipher> filterByAuthentication(Set<Cipher> ciphers, Set<Authentication> au) {
+ return filter(ciphers, null, null, au, null, null, null);
+ }
+
+ static Set<Cipher> filterByEncryption(Set<Cipher> ciphers, Set<Encryption> enc) {
+ return filter(ciphers, null, null, null, enc, null, null);
+ }
+
+ static Set<Cipher> filterByEncryptionLevel(Set<Cipher> ciphers, Set<EncryptionLevel> level) {
+ return filter(ciphers, null, null, null, null, level, null);
+ }
+
+ static Set<Cipher> filterByMessageDigest(Set<Cipher> ciphers, Set<MessageDigest> mac) {
+ return filter(ciphers, null, null, null, null, null, mac);
+ }
+
+ static LinkedHashSet<Cipher> filter(Set<Cipher> ciphers, Set<Protocol> protocol, Set<KeyExchange> kx,
+ Set<Authentication> au, Set<Encryption> enc, Set<EncryptionLevel> level, Set<MessageDigest> mac) {
+ LinkedHashSet<Cipher> result = new LinkedHashSet<>(ciphers.size());
+ for (Cipher cipher : ciphers) {
+ if (protocol != null && protocol.contains(cipher.getProtocol())) {
+ result.add(cipher);
+ }
+ if (kx != null && kx.contains(cipher.getKx())) {
+ result.add(cipher);
+ }
+ if (au != null && au.contains(cipher.getAu())) {
+ result.add(cipher);
+ }
+ if (enc != null && enc.contains(cipher.getEnc())) {
+ result.add(cipher);
+ }
+ if (level != null && level.contains(cipher.getLevel())) {
+ result.add(cipher);
+ }
+ if (mac != null && mac.contains(cipher.getMac())) {
+ result.add(cipher);
+ }
+ }
+ return result;
+ }
+
+ public static LinkedHashSet<Cipher> parse(String expression) {
+ if (!initialized) {
+ init();
+ }
+ String[] elements = expression.split(SEPARATOR);
+ LinkedHashSet<Cipher> ciphers = new LinkedHashSet<>();
+ Set<Cipher> removedCiphers = new HashSet<>();
+ for (String element : elements) {
+ if (element.startsWith(DELETE)) {
+ String alias = element.substring(1);
+ if (aliases.containsKey(alias)) {
+ remove(ciphers, alias);
+ }
+ } else if (element.startsWith(EXCLUDE)) {
+ String alias = element.substring(1);
+ if (aliases.containsKey(alias)) {
+ removedCiphers.addAll(aliases.get(alias));
+ } else {
+ log.warn(sm.getString("jsse.openssl.unknownElement", alias));
+ }
+ } else if (element.startsWith(TO_END)) {
+ String alias = element.substring(1);
+ if (aliases.containsKey(alias)) {
+ moveToEnd(ciphers, alias);
+ }
+ } else if ("@STRENGTH".equals(element)) {
+ strengthSort(ciphers);
+ break;
+ } else if (aliases.containsKey(element)) {
+ add(ciphers, element);
+ } else if (element.contains(AND)) {
+ String[] intersections = element.split("\\" + AND);
+ if(intersections.length > 0 && aliases.containsKey(intersections[0])) {
+ List<Cipher> result = new ArrayList<>(aliases.get(intersections[0]));
+ for(int i = 1; i < intersections.length; i++) {
+ if(aliases.containsKey(intersections[i])) {
+ result.retainAll(aliases.get(intersections[i]));
+ }
+ }
+ ciphers.addAll(result);
+ }
+ }
+ }
+ ciphers.removeAll(removedCiphers);
+ return ciphers;
+ }
+
+ public static List<String> convertForJSSE(Collection<Cipher> ciphers) {
+ List<String> result = new ArrayList<>(ciphers.size());
+ for (Cipher cipher : ciphers) {
+ result.addAll(cipher.getJsseNames());
+ }
+ if (log.isDebugEnabled()) {
+ log.debug(sm.getString("jsse.openssl.effectiveCiphers", displayResult(ciphers, true, ",")));
+ }
+ return result;
+ }
+
+ /**
+ * 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<String> parseExpression(String expression) {
+ return convertForJSSE(parse(expression));
+ }
+
+
+ /**
+ * Converts a JSSE cipher name to an OpenSSL cipher name.
+ *
+ * @param jsseCipherName The JSSE name for a cipher
+ *
+ * @return The OpenSSL name for the specified JSSE cipher
+ */
+ public static String jsseToOpenSSL(String jsseCipherName) {
+ if (!initialized) {
+ init();
+ }
+ return jsseToOpenSSL.get(jsseCipherName);
+ }
+
+
+ /**
+ * Converts an OpenSSL cipher name to a JSSE cipher name.
+ *
+ * @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
+ */
+ public static String openSSLToJsse(String opensslCipherName) {
+ if (!initialized) {
+ init();
+ }
+ List<Cipher> ciphers = aliases.get(opensslCipherName);
+ if (ciphers == null || ciphers.size() != 1) {
+ // Not an OpenSSL cipher name
+ return null;
+ }
+ Cipher cipher = ciphers.get(0);
+ // Each Cipher always has at least one JSSE name
+ return cipher.getJsseNames().iterator().next();
+ }
+
+
+ static String displayResult(Collection<Cipher> ciphers, boolean useJSSEFormat, String separator) {
+ if (ciphers.isEmpty()) {
+ return "";
+ }
+ StringBuilder builder = new StringBuilder(ciphers.size() * 16);
+ for (Cipher cipher : ciphers) {
+ if (useJSSEFormat) {
+ for (String name : cipher.getJsseNames()) {
+ builder.append(name);
+ builder.append(separator);
+ }
+ } else {
+ builder.append(cipher.getOpenSSLAlias());
+ }
+ builder.append(separator);
+ }
+ return builder.toString().substring(0, builder.length() - 1);
+ }
+}
diff --git a/java/org/apache/tomcat/util/net/openssl/ciphers/Protocol.java b/java/org/apache/tomcat/util/net/openssl/ciphers/Protocol.java
new file mode 100644
index 0000000..416e02d
--- /dev/null
+++ b/java/org/apache/tomcat/util/net/openssl/ciphers/Protocol.java
@@ -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.net.openssl.ciphers;
+
+import org.apache.tomcat.util.net.Constants;
+
+enum Protocol {
+
+ SSLv3(Constants.SSL_PROTO_SSLv3),
+ SSLv2(Constants.SSL_PROTO_SSLv2),
+ TLSv1(Constants.SSL_PROTO_TLSv1),
+ TLSv1_2(Constants.SSL_PROTO_TLSv1_2);
+
+ private final String openSSLName;
+
+ private Protocol(String openSSLName) {
+ this.openSSLName = openSSLName;
+ }
+
+ /**
+ * The name returned by OpenSSL in the protocol column when using
+ * <code>openssl ciphers -v</code>. This is currently only used by the unit
+ * tests hence it is package private.
+ */
+ String getOpenSSLName() {
+ return openSSLName;
+ }
+}
diff --git a/java/org/apache/tomcat/util/net/res/LocalStrings.properties b/java/org/apache/tomcat/util/net/res/LocalStrings.properties
deleted file mode 100644
index 1bedc84..0000000
--- a/java/org/apache/tomcat/util/net/res/LocalStrings.properties
+++ /dev/null
@@ -1,90 +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.
-
-# net resources
-endpoint.err.close=Caught exception trying to close socket
-endpoint.err.handshake=Handshake failed
-endpoint.err.unexpected=Unexpected error processing socket
-endpoint.warn.noExector=Failed to process socket [{0}] in state [{1}] because the executor had already been shutdown
-endpoint.warn.noDisableCompression='Disable compression' option is not supported by the SSL library {0}
-endpoint.warn.noDisableSessionTickets='Disable TLS Session Tickets' option is not supported by the SSL library {0}
-endpoint.warn.noHonorCipherOrder='Honor cipher order' option is not supported by the SSL library {0}
-endpoint.warn.noInsecureReneg=Secure re-negotiation is not supported by the SSL library {0}
-endpoint.warn.unlockAcceptorFailed=Acceptor thread [{0}] failed to unlock. Forcing hard socket shutdown.
-endpoint.warn.executorShutdown=The executor associated with thread pool [{0}] has not fully shutdown. Some application threads may still be running.
-endpoint.debug.channelCloseFail=Failed to close channel
-endpoint.debug.destroySocket=Destroying socket [{0}]
-endpoint.debug.pollerAdd=Add to addList socket [{0}], timeout [{1}], flags [{2}]
-endpoint.debug.pollerAddDo=Add to poller socket [{0}]
-endpoint.debug.pollerProcess=Processing socket [{0}] for event(s) [{1}]
-endpoint.debug.pollerRemove=Attempting to remove [{0}] from poller
-endpoint.debug.pollerRemoved=Removed [{0}] from poller
-endpoint.debug.socket=socket [{0}]
-endpoint.debug.socketCloseFail=Failed to close socket
-endpoint.debug.socketTimeout=Timing out [{0}]
-endpoint.debug.unlock=Caught exception trying to unlock accept on port {0}
-endpoint.executor.fail=Executor rejected socket [{0}] for processing
-endpoint.init.bind=Socket bind failed: [{0}] {1}
-endpoint.init.listen=Socket listen failed: [{0}] {1}
-endpoint.init.notavail=APR not available
-endpoint.launch.fail=Failed to launch new runnable
-endpoint.accept.fail=Socket accept failed
-endpoint.getAttribute=[{0}] is [{1}]
-endpoint.poll.limitedpollsize=Failed to create poller with specified size of {0}
-endpoint.poll.initfail=Poller creation failed
-endpoint.poll.fail=Critical poller failure (restarting poller): [{0}] {1}
-endpoint.poll.error=Unexpected poller error
-endpoint.process.fail=Error allocating socket processor
-endpoint.processing.fail=Error running socket processor
-endpoint.sendfile.error=Unexpected sendfile error
-endpoint.sendfile.addfail=Sendfile failure: [{0}] {1}
-endpoint.setAttribute=Set [{0}] to [{1}]
-endpoint.timeout.err=Error processing socket timeout
-endpoint.apr.failSslContextMake=Unable to create SSLContext. Check that SSLEngine is enabled in the AprLifecycleListener, the AprLifecycleListener has initialised correctly and that a valid SSLProtocol has been specified
-endpoint.apr.invalidSslProtocol=An invalid value [{0}] was provided for the SSLProtocol attribute
-endpoint.apr.maxConnections.running=The APR endpoint does not support the setting of maxConnections while it is running. The existing value of [{0}] will continue to be used.
-endpoint.apr.maxConnections.unlimited=The APR endpoint does not support unlimited connections. The existing value of [{0}] will continue to be used.
-endpoint.apr.noSendfileWithSSL=Sendfile is not supported for the APR/native connector when SSL is enabled
-endpoint.apr.noSslCertFile=Connector attribute SSLCertificateFile must be defined when using SSL with APR
-endpoint.apr.pollAddInvalid=Invalid attempted to add a socket [{0}] to the poller
-endpoint.apr.pollError=Poller failed with error [{0}] : [{1}]
-endpoint.apr.pollMergeEvents=Merge poller events [{1}] and [{2}] for socket [{0}]
-endpoint.apr.pollUnknownEvent=A socket was returned from the poller with an unrecognized event [{0}]
-endpoint.apr.remoteport=APR socket [{0}] opened with remote port [{1}]
-endpoint.nio.selectorCloseFail=Failed to close selector when closing the poller
-endpoint.nio.timeoutCme=Exception during processing of timeouts. The code has been checked repeatedly and no concurrent modification has been found. If you are able to repeat this error please open a Tomcat bug and provide the steps to reproduce.
-endpoint.nio2.exclusiveExecutor=The NIO2 connector requires an exclusive executor to operate properly on shutdown
-
-channel.nio.interrupted=The current thread was interrupted
-channel.nio.ssl.notHandshaking=NOT_HANDSHAKING during handshake
-channel.nio.ssl.handhakeError=Handshake error
-channel.nio.ssl.unexpectedStatusDuringWrap=Unexpected status [{0}] during handshake WRAP.
-channel.nio.ssl.unexpectedStatusDuringUnwrap=Unexpected status [{0}] during handshake UNWRAP.
-channel.nio.ssl.invalidStatus=Unexpected status [{0}].
-channel.nio.ssl.netInputNotEmpty=Network input buffer still contains data. Handshake will fail.
-channel.nio.ssl.netOutputNotEmpty=Network output buffer still contains data. Handshake will fail.
-channel.nio.ssl.appInputNotEmpty=Application input buffer still contains data. Data would have been lost.
-channel.nio.ssl.appOutputNotEmpty=Application output buffer still contains data. Data would have been lost.
-channel.nio.ssl.eofDuringHandshake=EOF during handshake.
-channel.nio.ssl.timeoutDuringHandshake=Timeout during handshake.
-channel.nio.ssl.remainingDataDuringClose=Remaining data in the network buffer, can't send SSL close message, force a close with close(true) instead
-channel.nio.ssl.pendingWriteDuringClose=Pending write, so remaining data in the network buffer, can't send SSL close message, force a close with close(true) instead
-channel.nio.ssl.invalidCloseState=Invalid close state, will not send network data.
-channel.nio.ssl.unwrapFail=Unable to unwrap data, invalid status [{0}]
-channel.nio.ssl.wrapException=Handshake failed during wrap
-channel.nio.ssl.wrapFail=Unable to wrap data, invalid status [{0}]
-channel.nio.ssl.incompleteHandshake=Handshake incomplete, you must complete handshake before reading data.
-channel.nio.ssl.closing=Channel is in closing state.
-channel.nio.ssl.invalidBuffer=You can only read using the application read buffer provided by the handler.
diff --git a/java/org/apache/tomcat/util/net/res/LocalStrings_es.properties b/java/org/apache/tomcat/util/net/res/LocalStrings_es.properties
deleted file mode 100644
index 505cdf6..0000000
--- a/java/org/apache/tomcat/util/net/res/LocalStrings_es.properties
+++ /dev/null
@@ -1,36 +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.
-# net resources
-endpoint.err.handshake = Acuerdo fallido
-endpoint.err.unexpected = Error inesperado al procesar conector
-endpoint.debug.unlock = Excepci\u00F3n cogida intentando desbloquear aceptaci\u00F3n en puerto {0}
-endpoint.err.close = Excepci\u00F3n cogida intentando cerrar conector
-endpoint.init.bind = Ligado de conector fall\u00F3\: [{0}] {1}
-endpoint.init.listen = Escucha de conector fall\u00F3\: [{0}] {1}
-endpoint.init.notavail = APR no disponible
-endpoint.accept.fail = Aceptaci\u00F3n de conector fall\u00F3
-endpoint.poll.limitedpollsize = No pude crear encuestador de medida espec\u00EDfica de {0}
-endpoint.poll.initfail = Fall\u00F3 la creaci\u00F3n del encuestador
-endpoint.poll.fail = Fallo cr\u00EDtico de encuestador (reiniciando encuestador)\: [{0}] {1}
-endpoint.poll.error = Error inesperado de encuestador
-endpoint.process.fail = Error reservando procesador de conector
-endpoint.sendfile.error = Error inesperado de env\u00EDo de fichero
-endpoint.sendfile.addfail = Fallo en Sednfile\: [{0}] {1}
-endpoint.warn.noInsecureReneg = La renegociaci\u00F3n segura no est\u00E1 soportada por la biblioteca SSL {0}
-endpoint.warn.unlockAcceptorFailed = El hilo aceptador [{0}] fall\u00F3 al desbloquear. Forzando apagado de enchufe (socket).
-endpoint.debug.channelCloseFail = No puede cerrar el canal
-endpoint.debug.socketCloseFail = No pude cerrar el enchufe (socket)
-endpoint.apr.noSslCertFile = El atribiuto del conector SSLCertificateFile debe de ser definido al usar SSL con APR
-endpoint.apr.invalidSslProtocol = Se ha proporcionado un valor inv\u00E1lido [{0}] para el atributo SSLProtocol
diff --git a/java/org/apache/tomcat/util/scan/Constants.java b/java/org/apache/tomcat/util/scan/Constants.java
index cf8bc30..68911bb 100644
--- a/java/org/apache/tomcat/util/scan/Constants.java
+++ b/java/org/apache/tomcat/util/scan/Constants.java
@@ -34,11 +34,4 @@ public final class Constants {
public static final String JAR_EXT = ".jar";
public static final String WEB_INF_LIB = "/WEB-INF/lib/";
public static final String WEB_INF_CLASSES = "/WEB-INF/classes";
-
- /* Context attributes */
- @Deprecated // Third party components should use standard Servlet API
- // mechanisms such as a ServletContainerInitializer to access
- // configuration information
- public static final String MERGED_WEB_XML =
- "org.apache.tomcat.util.scan.MergedWebXml";
}
diff --git a/java/org/apache/tomcat/util/scan/Jar.java b/java/org/apache/tomcat/util/scan/Jar.java
deleted file mode 100644
index 37ffe21..0000000
--- a/java/org/apache/tomcat/util/scan/Jar.java
+++ /dev/null
@@ -1,33 +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.scan;
-
-/**
- * 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.
- *
- * @deprecated Use {@link org.apache.tomcat.Jar} instead.
- * This class will be removed from Tomcat 8.5.x onwards.
- */
- at Deprecated
-public interface Jar extends org.apache.tomcat.Jar {
-}
diff --git a/java/org/apache/tomcat/util/scan/StandardJarScanner.java b/java/org/apache/tomcat/util/scan/StandardJarScanner.java
index 4236ca9..5238d3c 100644
--- a/java/org/apache/tomcat/util/scan/StandardJarScanner.java
+++ b/java/org/apache/tomcat/util/scan/StandardJarScanner.java
@@ -265,6 +265,10 @@ public class StandardJarScanner implements JarScanner {
continue;
}
+ // TODO: Java 9 support. Details are TBD. It will depend
+ // on the extent to which Java 8 supports the
+ // Java 9 file formats since this code MUST run on
+ // Java 8.
ClassPathEntry cpe = new ClassPathEntry(url);
// JARs are scanned unless the filter says not to.
diff --git a/java/org/apache/tomcat/util/threads/Constants.java b/java/org/apache/tomcat/util/threads/Constants.java
index 63970c7..5dcacce 100644
--- a/java/org/apache/tomcat/util/threads/Constants.java
+++ b/java/org/apache/tomcat/util/threads/Constants.java
@@ -21,8 +21,6 @@ package org.apache.tomcat.util.threads;
*/
public final class Constants {
- public static final String Package = "org.apache.tomcat.util.threads";
-
public static final long DEFAULT_THREAD_RENEWAL_DELAY = 1000L;
/**
diff --git a/java/org/apache/tomcat/util/threads/LimitLatch.java b/java/org/apache/tomcat/util/threads/LimitLatch.java
index 8293c4e..e868405 100644
--- a/java/org/apache/tomcat/util/threads/LimitLatch.java
+++ b/java/org/apache/tomcat/util/threads/LimitLatch.java
@@ -82,6 +82,7 @@ public class LimitLatch {
/**
* Obtain the current limit.
+ * @return the limit
*/
public long getLimit() {
return limit;
@@ -107,6 +108,7 @@ public class LimitLatch {
/**
* 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.isDebugEnabled()) {
@@ -131,6 +133,7 @@ public class LimitLatch {
/**
* Releases all waiting threads and causes the {@link #limit} to be ignored
* until {@link #reset()} is called.
+ * @return <code>true</code> if release was done
*/
public boolean releaseAll() {
released = true;
@@ -149,6 +152,7 @@ public class LimitLatch {
/**
* Returns <code>true</code> if there is at least one thread waiting to
* acquire the shared lock, otherwise returns <code>false</code>.
+ * @return <code>true</code> if threads are waiting
*/
public boolean hasQueuedThreads() {
return sync.hasQueuedThreads();
@@ -157,6 +161,7 @@ public class LimitLatch {
/**
* Provide access to the list of threads waiting to acquire this limited
* shared latch.
+ * @return a collection of threads
*/
public Collection<Thread> getQueuedThreads() {
return sync.getQueuedThreads();
diff --git a/java/org/apache/tomcat/util/threads/ThreadPoolExecutor.java b/java/org/apache/tomcat/util/threads/ThreadPoolExecutor.java
index 99d9977..179126b 100644
--- a/java/org/apache/tomcat/util/threads/ThreadPoolExecutor.java
+++ b/java/org/apache/tomcat/util/threads/ThreadPoolExecutor.java
@@ -151,6 +151,8 @@ public class ThreadPoolExecutor extends java.util.concurrent.ThreadPoolExecutor
* full after that.
*
* @param command the runnable task
+ * @param timeout A timeout for the completion of the task
+ * @param unit The timeout time unit
* @throws RejectedExecutionException if this task cannot be
* accepted for execution - the queue is full
* @throws NullPointerException if command or unit is null
diff --git a/java/org/apache/tomcat/websocket/AsyncChannelGroupUtil.java b/java/org/apache/tomcat/websocket/AsyncChannelGroupUtil.java
index 0e81011..fe0136e 100644
--- a/java/org/apache/tomcat/websocket/AsyncChannelGroupUtil.java
+++ b/java/org/apache/tomcat/websocket/AsyncChannelGroupUtil.java
@@ -37,7 +37,7 @@ import org.apache.tomcat.util.threads.ThreadPoolExecutor;
public class AsyncChannelGroupUtil {
private static final StringManager sm =
- StringManager.getManager(Constants.PACKAGE_NAME);
+ StringManager.getManager(AsyncChannelGroupUtil.class);
private static AsynchronousChannelGroup group = null;
private static int usageCount = 0;
diff --git a/java/org/apache/tomcat/websocket/AsyncChannelWrapperSecure.java b/java/org/apache/tomcat/websocket/AsyncChannelWrapperSecure.java
index 778a62e..1a92fd0 100644
--- a/java/org/apache/tomcat/websocket/AsyncChannelWrapperSecure.java
+++ b/java/org/apache/tomcat/websocket/AsyncChannelWrapperSecure.java
@@ -51,7 +51,7 @@ public class AsyncChannelWrapperSecure implements AsyncChannelWrapper {
private static final Log log =
LogFactory.getLog(AsyncChannelWrapperSecure.class);
private static final StringManager sm =
- StringManager.getManager(Constants.PACKAGE_NAME);
+ StringManager.getManager(AsyncChannelWrapperSecure.class);
private static final ByteBuffer DUMMY = ByteBuffer.allocate(8192);
private final AsynchronousSocketChannel socketChannel;
@@ -403,8 +403,9 @@ public class AsyncChannelWrapperSecure implements AsyncChannelWrapper {
handshaking = false;
break;
}
- default: {
- throw new SSLException("TODO");
+ case NOT_HANDSHAKING: {
+ throw new SSLException(
+ sm.getString("asyncChannelWrapperSecure.notHandshaking"));
}
}
}
@@ -424,13 +425,14 @@ public class AsyncChannelWrapperSecure implements AsyncChannelWrapper {
if (resultStatus != Status.OK &&
(wrap || resultStatus != Status.BUFFER_UNDERFLOW)) {
- throw new SSLException("TODO");
+ throw new SSLException(
+ sm.getString("asyncChannelWrapperSecure.check.notOk", resultStatus));
}
if (wrap && result.bytesConsumed() != 0) {
- throw new SSLException("TODO");
+ throw new SSLException(sm.getString("asyncChannelWrapperSecure.check.wrap"));
}
if (!wrap && result.bytesProduced() != 0) {
- throw new SSLException("TODO");
+ throw new SSLException(sm.getString("asyncChannelWrapperSecure.check.unwrap"));
}
}
}
diff --git a/java/org/apache/tomcat/websocket/BackgroundProcessManager.java b/java/org/apache/tomcat/websocket/BackgroundProcessManager.java
index 983eafb..14c27b5 100644
--- a/java/org/apache/tomcat/websocket/BackgroundProcessManager.java
+++ b/java/org/apache/tomcat/websocket/BackgroundProcessManager.java
@@ -34,7 +34,7 @@ public class BackgroundProcessManager {
private static final Log log =
LogFactory.getLog(BackgroundProcessManager.class);
private static final StringManager sm =
- StringManager.getManager(Constants.PACKAGE_NAME);
+ StringManager.getManager(BackgroundProcessManager.class);
private static final BackgroundProcessManager instance;
diff --git a/java/org/apache/tomcat/websocket/CaseInsensitiveKeyMap.java b/java/org/apache/tomcat/websocket/CaseInsensitiveKeyMap.java
deleted file mode 100644
index a3004b8..0000000
--- a/java/org/apache/tomcat/websocket/CaseInsensitiveKeyMap.java
+++ /dev/null
@@ -1,208 +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.websocket;
-
-import java.util.AbstractMap;
-import java.util.AbstractSet;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Set;
-
-import org.apache.tomcat.util.res.StringManager;
-
-/**
- * A Map implementation that uses case-insensitive (using {@link
- * Locale#ENGLISH}) strings as keys.
- * <p>
- * Keys must be instances of {@link String}. Note that this means that
- * <code>null</code> keys are not permitted.
- * <p>
- * This implementation is not thread-safe.
- *
- * @param <V> Type of values placed in this Map.
- */
-public class CaseInsensitiveKeyMap<V> extends AbstractMap<String,V> {
-
- private static final StringManager sm =
- StringManager.getManager(Constants.PACKAGE_NAME);
-
- private final Map<Key,V> map = new HashMap<>();
-
-
- @Override
- public V get(Object key) {
- return map.get(Key.getInstance(key));
- }
-
-
- @Override
- public V put(String key, V value) {
- Key caseInsensitiveKey = Key.getInstance(key);
- if (caseInsensitiveKey == null) {
- throw new NullPointerException(sm.getString("caseInsensitiveKeyMap.nullKey"));
- }
- return map.put(caseInsensitiveKey, value);
- }
-
-
- /**
- * {@inheritDoc}
- * <p>
- * <b>Use this method with caution</b>. 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<? extends String, ? extends V> m) {
- super.putAll(m);
- }
-
-
- @Override
- public boolean containsKey(Object key) {
- return map.containsKey(Key.getInstance(key));
- }
-
-
- @Override
- public V remove(Object key) {
- return map.remove(Key.getInstance(key));
- }
-
-
- @Override
- public Set<Entry<String, V>> entrySet() {
- return new EntrySet<>(map.entrySet());
- }
-
-
- private static class EntrySet<V> extends AbstractSet<Entry<String,V>> {
-
- private final Set<Entry<Key,V>> entrySet;
-
- public EntrySet(Set<Map.Entry<Key,V>> entrySet) {
- this.entrySet = entrySet;
- }
-
- @Override
- public Iterator<Entry<String,V>> iterator() {
- return new EntryIterator<>(entrySet.iterator());
- }
-
- @Override
- public int size() {
- return entrySet.size();
- }
- }
-
-
- private static class EntryIterator<V> implements Iterator<Entry<String,V>> {
-
- private final Iterator<Entry<Key,V>> iterator;
-
- public EntryIterator(Iterator<Entry<Key,V>> iterator) {
- this.iterator = iterator;
- }
-
- @Override
- public boolean hasNext() {
- return iterator.hasNext();
- }
-
- @Override
- public Entry<String,V> next() {
- Entry<Key,V> entry = iterator.next();
- return new EntryImpl<>(entry.getKey().getKey(), entry.getValue());
- }
-
- @Override
- public void remove() {
- iterator.remove();
- }
- }
-
-
- private static class EntryImpl<V> implements Entry<String,V> {
-
- private final String key;
- private final V value;
-
- public EntryImpl(String key, V value) {
- this.key = key;
- this.value = value;
- }
-
- @Override
- public String getKey() {
- return key;
- }
-
- @Override
- public V getValue() {
- return value;
- }
-
- @Override
- public V setValue(V value) {
- throw new UnsupportedOperationException();
- }
- }
-
- private static class Key {
-
- private final String key;
- private final String lcKey;
-
- private Key(String key) {
- this.key = key;
- this.lcKey = key.toLowerCase(Locale.ENGLISH);
- }
-
- public String getKey() {
- return key;
- }
-
- @Override
- public int hashCode() {
- return lcKey.hashCode();
- }
-
- @Override
- public boolean equals(Object obj) {
- if (this == obj) {
- return true;
- }
- if (obj == null) {
- return false;
- }
- if (getClass() != obj.getClass()) {
- return false;
- }
- Key other = (Key) obj;
- return lcKey.equals(other.lcKey);
- }
-
- public static Key getInstance(Object o) {
- if (o instanceof String) {
- return new Key((String) o);
- }
- return null;
- }
- }
-}
diff --git a/java/org/apache/tomcat/websocket/Constants.java b/java/org/apache/tomcat/websocket/Constants.java
index e055894..6e75d7f 100644
--- a/java/org/apache/tomcat/websocket/Constants.java
+++ b/java/org/apache/tomcat/websocket/Constants.java
@@ -27,8 +27,6 @@ import javax.websocket.Extension;
*/
public class Constants {
- protected static final String PACKAGE_NAME =
- Constants.class.getPackage().getName();
// OP Codes
public static final byte OPCODE_CONTINUATION = 0x00;
public static final byte OPCODE_TEXT = 0x01;
@@ -48,6 +46,34 @@ public class Constants {
.intValue();
// Client connection
+ /**
+ * Property name to set to configure the value that is passed to
+ * {@link javax.net.ssl.SSLEngine#setEnabledProtocols(String[])}. The value
+ * should be a comma separated string.
+ */
+ public static final String SSL_PROTOCOLS_PROPERTY =
+ "org.apache.tomcat.websocket.SSL_PROTOCOLS";
+ public static final String SSL_TRUSTSTORE_PROPERTY =
+ "org.apache.tomcat.websocket.SSL_TRUSTSTORE";
+ public static final String SSL_TRUSTSTORE_PWD_PROPERTY =
+ "org.apache.tomcat.websocket.SSL_TRUSTSTORE_PWD";
+ public static final String SSL_TRUSTSTORE_PWD_DEFAULT = "changeit";
+ /**
+ * Property name to set to configure used SSLContext. The value should be an
+ * instance of SSLContext. If this property is present, the SSL_TRUSTSTORE*
+ * properties are ignored.
+ */
+ public static final String SSL_CONTEXT_PROPERTY =
+ "org.apache.tomcat.websocket.SSL_CONTEXT";
+ /**
+ * Property name to set to configure the timeout (in milliseconds) when
+ * establishing a WebSocket connection to server. The default is
+ * {@link #IO_TIMEOUT_MS_DEFAULT}.
+ */
+ public static final String IO_TIMEOUT_MS_PROPERTY =
+ "org.apache.tomcat.websocket.IO_TIMEOUT_MS";
+ public static final long IO_TIMEOUT_MS_DEFAULT = 5000;
+ // HTTP upgrade header names and values
public static final String HOST_HEADER_NAME = "Host";
public static final String UPGRADE_HEADER_NAME = "Upgrade";
public static final String UPGRADE_HEADER_VALUE = "websocket";
@@ -57,15 +83,19 @@ public class Constants {
public static final String WS_VERSION_HEADER_NAME = "Sec-WebSocket-Version";
public static final String WS_VERSION_HEADER_VALUE = "13";
public static final String WS_KEY_HEADER_NAME = "Sec-WebSocket-Key";
- public static final String WS_PROTOCOL_HEADER_NAME =
- "Sec-WebSocket-Protocol";
- public static final String WS_EXTENSIONS_HEADER_NAME =
- "Sec-WebSocket-Extensions";
+ public static final String WS_PROTOCOL_HEADER_NAME = "Sec-WebSocket-Protocol";
+ public static final String WS_EXTENSIONS_HEADER_NAME = "Sec-WebSocket-Extensions";
// Configuration for Origin header in client
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";
+ // Milliseconds so this is 20 seconds
+ public static final long DEFAULT_BLOCKING_SEND_TIMEOUT = 20 * 1000;
+
// Configuration for background processing checks intervals
static final int DEFAULT_PROCESS_PERIOD = Integer.getInteger(
"org.apache.tomcat.websocket.DEFAULT_PROCESS_PERIOD", 10)
diff --git a/java/org/apache/tomcat/websocket/FutureToSendHandler.java b/java/org/apache/tomcat/websocket/FutureToSendHandler.java
index dbeebe7..ce59ade 100644
--- a/java/org/apache/tomcat/websocket/FutureToSendHandler.java
+++ b/java/org/apache/tomcat/websocket/FutureToSendHandler.java
@@ -33,26 +33,14 @@ import org.apache.tomcat.util.res.StringManager;
*/
class FutureToSendHandler implements Future<Void>, SendHandler {
- private static final StringManager sm = StringManager.getManager(Constants.PACKAGE_NAME);
+ private static final StringManager sm = StringManager.getManager(FutureToSendHandler.class);
private final CountDownLatch latch = new CountDownLatch(1);
private final WsSession wsSession;
- private final boolean closeMessage;
private volatile SendResult result = null;
public FutureToSendHandler(WsSession wsSession) {
- this(wsSession, false);
- }
-
-
- public FutureToSendHandler(WsSession wsSession, boolean closeMessage) {
this.wsSession = wsSession;
- this.closeMessage = closeMessage;
- }
-
-
- public boolean isCloseMessage() {
- return closeMessage;
}
diff --git a/java/org/apache/tomcat/websocket/LocalStrings.properties b/java/org/apache/tomcat/websocket/LocalStrings.properties
index 2a3e3cf..e3bebe4 100644
--- a/java/org/apache/tomcat/websocket/LocalStrings.properties
+++ b/java/org/apache/tomcat/websocket/LocalStrings.properties
@@ -16,9 +16,13 @@
asyncChannelGroup.createFail=Unable to create dedicated AsynchronousChannelGroup for WebSocket clients which is required to prevent memory leaks in complex class loader environments like JavaEE containers
asyncChannelWrapperSecure.closeFail=Failed to close channel cleanly
+asyncChannelWrapperSecure.check.notOk=TLS handshake returned an unexpected status [{0}]
+asyncChannelWrapperSecure.check.unwrap=Bytes were written to the output during a read
+asyncChannelWrapperSecure.check.wrap=Bytes were consumed from the input during a write
asyncChannelWrapperSecure.concurrentRead=Concurrent read operations are not permitted
asyncChannelWrapperSecure.concurrentWrite=Concurrent write operations are not permitted
asyncChannelWrapperSecure.eof=Unexpected end of stream
+asyncChannelWrapperSecure.notHandshaking=Unexpected state [NOT_HANDSHAKING] during TLS handshake
asyncChannelWrapperSecure.readOverflow=Buffer overflow. [{0}] bytes to write into a [{1}] byte buffer that already contained [{2}] bytes.
asyncChannelWrapperSecure.statusUnwrap=Unexpected Status of SSLEngineResult after an unwrap() operation
asyncChannelWrapperSecure.statusWrap=Unexpected Status of SSLEngineResult after a wrap() operation
@@ -69,6 +73,7 @@ wsFrame.wrongRsv=The client frame set the reserved bits to [{0}] for a message w
wsFrameClient.ioe=Failure while reading data sent by server
+wsRemoteEndpoint.acquireTimeout=The current message was not fully sent within the specified timeout
wsRemoteEndpoint.closed=Message will not be sent because the WebSocket session has been closed
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
@@ -78,16 +83,17 @@ wsRemoteEndpoint.concurrentMessageSend=Messages may not be sent concurrently eve
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
wsRemoteEndpoint.noEncoder=No encoder specified for object of class [{0}]
-wsRemoteEndpoint.wrongState=The remote endpoint was in state [{0}] which is an invalid state for called method
wsRemoteEndpoint.nullData=Invalid null data argument
wsRemoteEndpoint.nullHandler=Invalid null handler argument
+wsRemoteEndpoint.sendInterupt=The current thread was interrupted while waiting for a blocking send to complete
wsRemoteEndpoint.tooMuchData=Ping or pong may not send more than 125 bytes
+wsRemoteEndpoint.wrongState=The remote endpoint was in state [{0}] which is an invalid state for called method
# Note the following message is used as a close reason in a WebSocket control
# frame and therefore must be 123 bytes (not characters) or less in length.
# Messages are encoded using UTF-8 where a single character may be encoded in
# as many as 4 bytes.
-wsSession.timeout=The WebSocket session timeout expired
+wsSession.timeout=The WebSocket session [{0}] timeout expired
wsSession.closed=The WebSocket session [{0}] has been closed and no method (apart from close()) may be called on a closed session
wsSession.created=Created WebSocket session [{0}]
diff --git a/java/org/apache/tomcat/websocket/MessagePart.java b/java/org/apache/tomcat/websocket/MessagePart.java
index c44b33c..f51c007 100644
--- a/java/org/apache/tomcat/websocket/MessagePart.java
+++ b/java/org/apache/tomcat/websocket/MessagePart.java
@@ -27,15 +27,18 @@ class MessagePart {
private final ByteBuffer payload;
private final SendHandler intermediateHandler;
private volatile SendHandler endHandler;
+ private final long blockingWriteTimeoutExpiry;
public MessagePart( boolean fin, int rsv, byte opCode, ByteBuffer payload,
- SendHandler intermediateHandler, SendHandler endHandler) {
+ SendHandler intermediateHandler, SendHandler endHandler,
+ long blockingWriteTimeoutExpiry) {
this.fin = fin;
this.rsv = rsv;
this.opCode = opCode;
this.payload = payload;
this.intermediateHandler = intermediateHandler;
this.endHandler = endHandler;
+ this.blockingWriteTimeoutExpiry = blockingWriteTimeoutExpiry;
}
@@ -71,6 +74,10 @@ class MessagePart {
public void setEndHandler(SendHandler endHandler) {
this.endHandler = endHandler;
}
+
+ public long getBlockingWriteTimeoutExpiry() {
+ return blockingWriteTimeoutExpiry;
+ }
}
diff --git a/java/org/apache/tomcat/websocket/PerMessageDeflate.java b/java/org/apache/tomcat/websocket/PerMessageDeflate.java
index 099133f..513cf08 100644
--- a/java/org/apache/tomcat/websocket/PerMessageDeflate.java
+++ b/java/org/apache/tomcat/websocket/PerMessageDeflate.java
@@ -32,7 +32,7 @@ import org.apache.tomcat.util.res.StringManager;
public class PerMessageDeflate implements Transformation {
- private static final StringManager sm = StringManager.getManager(Constants.PACKAGE_NAME);
+ private static final StringManager sm = StringManager.getManager(PerMessageDeflate.class);
private static final String SERVER_NO_CONTEXT_TAKEOVER = "server_no_context_takeover";
private static final String CLIENT_NO_CONTEXT_TAKEOVER = "client_no_context_takeover";
@@ -367,13 +367,14 @@ public class PerMessageDeflate implements Transformation {
boolean fin = uncompressedPart.isFin();
boolean full = compressedPayload.limit() == compressedPayload.capacity();
boolean needsInput = deflater.needsInput();
+ long blockingWriteTimeoutExpiry = uncompressedPart.getBlockingWriteTimeoutExpiry();
if (fin && !full && needsInput) {
// End of compressed message. Drop EOM bytes and output.
compressedPayload.limit(compressedPayload.limit() - EOM_BYTES.length);
compressedPart = new MessagePart(true, getRsv(uncompressedPart),
opCode, compressedPayload, uncompressedIntermediateHandler,
- uncompressedIntermediateHandler);
+ uncompressedIntermediateHandler, blockingWriteTimeoutExpiry);
deflateRequired = false;
startNewMessage();
} else if (full && !needsInput) {
@@ -381,13 +382,13 @@ public class PerMessageDeflate implements Transformation {
// Output and start new compressed part.
compressedPart = new MessagePart(false, getRsv(uncompressedPart),
opCode, compressedPayload, uncompressedIntermediateHandler,
- uncompressedIntermediateHandler);
+ uncompressedIntermediateHandler, blockingWriteTimeoutExpiry);
} else if (!fin && full && needsInput) {
// Write buffer full and input message not fully read.
// Output and get more data.
compressedPart = new MessagePart(false, getRsv(uncompressedPart),
opCode, compressedPayload, uncompressedIntermediateHandler,
- uncompressedIntermediateHandler);
+ uncompressedIntermediateHandler, blockingWriteTimeoutExpiry);
deflateRequired = false;
} else if (fin && full && needsInput) {
// Write buffer full. Input fully read. Deflater may be
@@ -403,7 +404,8 @@ public class PerMessageDeflate implements Transformation {
compressedPayload.limit(compressedPayload.limit() - EOM_BYTES.length + eomBufferWritten);
compressedPart = new MessagePart(true,
getRsv(uncompressedPart), opCode, compressedPayload,
- uncompressedIntermediateHandler, uncompressedIntermediateHandler);
+ uncompressedIntermediateHandler, uncompressedIntermediateHandler,
+ blockingWriteTimeoutExpiry);
deflateRequired = false;
startNewMessage();
} else {
@@ -412,7 +414,8 @@ public class PerMessageDeflate implements Transformation {
writeBuffer.put(EOM_BUFFER, 0, eomBufferWritten);
compressedPart = new MessagePart(false,
getRsv(uncompressedPart), opCode, compressedPayload,
- uncompressedIntermediateHandler, uncompressedIntermediateHandler);
+ uncompressedIntermediateHandler, uncompressedIntermediateHandler,
+ blockingWriteTimeoutExpiry);
}
} else {
throw new IllegalStateException("Should never happen");
diff --git a/java/org/apache/tomcat/websocket/SendHandlerToCompletionHandler.java b/java/org/apache/tomcat/websocket/SendHandlerToCompletionHandler.java
deleted file mode 100644
index d910a93..0000000
--- a/java/org/apache/tomcat/websocket/SendHandlerToCompletionHandler.java
+++ /dev/null
@@ -1,42 +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.websocket;
-
-import java.nio.channels.CompletionHandler;
-
-import javax.websocket.SendHandler;
-import javax.websocket.SendResult;
-
-public class SendHandlerToCompletionHandler
- implements CompletionHandler<Long,Void> {
-
- private SendHandler handler;
-
- public SendHandlerToCompletionHandler(SendHandler handler) {
- this.handler = handler;
- }
-
- @Override
- public void completed(Long result, Void attachment) {
- handler.onResult(new SendResult());
- }
-
- @Override
- public void failed(Throwable exc, Void attachment) {
- handler.onResult(new SendResult(exc));
- }
-}
diff --git a/java/org/apache/tomcat/websocket/TransformationFactory.java b/java/org/apache/tomcat/websocket/TransformationFactory.java
index fed13f9..643be94 100644
--- a/java/org/apache/tomcat/websocket/TransformationFactory.java
+++ b/java/org/apache/tomcat/websocket/TransformationFactory.java
@@ -24,7 +24,7 @@ import org.apache.tomcat.util.res.StringManager;
public class TransformationFactory {
- private static final StringManager sm = StringManager.getManager(Constants.PACKAGE_NAME);
+ private static final StringManager sm = StringManager.getManager(TransformationFactory.class);
private static final TransformationFactory factory = new TransformationFactory();
diff --git a/java/org/apache/tomcat/websocket/Util.java b/java/org/apache/tomcat/websocket/Util.java
index 0288868..228ac8c 100644
--- a/java/org/apache/tomcat/websocket/Util.java
+++ b/java/org/apache/tomcat/websocket/Util.java
@@ -59,8 +59,7 @@ import org.apache.tomcat.websocket.pojo.PojoMessageHandlerWholeText;
*/
public class Util {
- private static final StringManager sm =
- StringManager.getManager(Constants.PACKAGE_NAME);
+ private static final StringManager sm = StringManager.getManager(Util.class);
private static final Queue<SecureRandom> randoms =
new ConcurrentLinkedQueue<>();
diff --git a/java/org/apache/tomcat/websocket/WsFrameBase.java b/java/org/apache/tomcat/websocket/WsFrameBase.java
index ef97a25..84e8132 100644
--- a/java/org/apache/tomcat/websocket/WsFrameBase.java
+++ b/java/org/apache/tomcat/websocket/WsFrameBase.java
@@ -43,11 +43,11 @@ import org.apache.tomcat.util.res.StringManager;
public abstract class WsFrameBase {
private static final StringManager sm =
- StringManager.getManager(Constants.PACKAGE_NAME);
+ StringManager.getManager(WsFrameBase.class);
// Connection level attributes
protected final WsSession wsSession;
- protected final byte[] inputBuffer;
+ protected final ByteBuffer inputBuffer;
private final Transformation transformation;
// Attributes for control messages
@@ -84,11 +84,10 @@ public abstract class WsFrameBase {
// Attributes tracking state
private volatile State state = State.NEW_FRAME;
private volatile boolean open = true;
- private volatile int readPos = 0;
- protected volatile int writePos = 0;
public WsFrameBase(WsSession wsSession, Transformation transformation) {
- inputBuffer = new byte[Constants.DEFAULT_BUFFER_SIZE];
+ inputBuffer = ByteBuffer.allocate(Constants.DEFAULT_BUFFER_SIZE);
+ inputBuffer.position(0).limit(0);
messageBufferBinary =
ByteBuffer.allocate(wsSession.getMaxBinaryMessageBufferSize());
messageBufferText =
@@ -142,10 +141,10 @@ public abstract class WsFrameBase {
*/
private boolean processInitialHeader() throws IOException {
// Need at least two bytes of data to do this
- if (writePos - readPos < 2) {
+ if (inputBuffer.remaining() < 2) {
return false;
}
- int b = inputBuffer[readPos++];
+ int b = inputBuffer.get();
fin = (b & 0x80) > 0;
rsv = (b & 0x70) >>> 4;
opCode = (byte) (b & 0x0F);
@@ -212,7 +211,7 @@ public abstract class WsFrameBase {
}
continuationExpected = !fin;
}
- b = inputBuffer[readPos++];
+ b = inputBuffer.get();
// Client data must be masked
if ((b & 0x80) == 0 && isMasked()) {
throw new WsIOException(new CloseReason(
@@ -251,16 +250,18 @@ public abstract class WsFrameBase {
} else if (payloadLength == 127) {
headerLength += 8;
}
- if (writePos - readPos < headerLength) {
+ if (inputBuffer.remaining() < headerLength) {
return false;
}
// Calculate new payload length if necessary
if (payloadLength == 126) {
- payloadLength = byteArrayToLong(inputBuffer, readPos, 2);
- readPos += 2;
+ payloadLength = byteArrayToLong(inputBuffer.array(),
+ inputBuffer.arrayOffset() + inputBuffer.position(), 2);
+ inputBuffer.position(inputBuffer.position() + 2);
} else if (payloadLength == 127) {
- payloadLength = byteArrayToLong(inputBuffer, readPos, 8);
- readPos += 8;
+ payloadLength = byteArrayToLong(inputBuffer.array(),
+ inputBuffer.arrayOffset() + inputBuffer.position(), 8);
+ inputBuffer.position(inputBuffer.position() + 8);
}
if (Util.isControl(opCode)) {
if (payloadLength > 125) {
@@ -276,8 +277,7 @@ public abstract class WsFrameBase {
}
}
if (isMasked()) {
- System.arraycopy(inputBuffer, readPos, mask, 0, 4);
- readPos += 4;
+ inputBuffer.get(mask, 0, 4);
}
state = State.DATA;
return true;
@@ -377,7 +377,7 @@ public abstract class WsFrameBase {
@SuppressWarnings("unchecked")
- private void sendMessageText(boolean last) throws WsIOException {
+ protected void sendMessageText(boolean last) throws WsIOException {
if (textMsgHandler instanceof WrappedMessageHandler) {
long maxMessageSize =
((WrappedMessageHandler) textMsgHandler).getMaxMessageSize();
@@ -572,7 +572,7 @@ public abstract class WsFrameBase {
@SuppressWarnings("unchecked")
- private void sendMessageBinary(ByteBuffer msg, boolean last)
+ protected void sendMessageBinary(ByteBuffer msg, boolean last)
throws WsIOException {
if (binaryMsgHandler instanceof WrappedMessageHandler) {
long maxMessageSize =
@@ -607,9 +607,8 @@ public abstract class WsFrameBase {
private void newFrame() {
- if (readPos == writePos) {
- readPos = 0;
- writePos = 0;
+ if (inputBuffer.remaining() == 0) {
+ inputBuffer.position(0).limit(0);
}
maskIndex = 0;
@@ -626,7 +625,7 @@ public abstract class WsFrameBase {
private void checkRoomHeaders() {
// Is the start of the current frame too near the end of the input
// buffer?
- if (inputBuffer.length - readPos < 131) {
+ if (inputBuffer.capacity() - inputBuffer.position() < 131) {
// Limit based on a control frame with a full payload
makeRoom();
}
@@ -634,17 +633,15 @@ public abstract class WsFrameBase {
private void checkRoomPayload() {
- if (inputBuffer.length - readPos - payloadLength + payloadWritten < 0) {
+ if (inputBuffer.capacity() - inputBuffer.position() - payloadLength + payloadWritten < 0) {
makeRoom();
}
}
private void makeRoom() {
- System.arraycopy(inputBuffer, readPos, inputBuffer, 0,
- writePos - readPos);
- writePos = writePos - readPos;
- readPos = 0;
+ inputBuffer.compact();
+ inputBuffer.flip();
}
@@ -661,8 +658,8 @@ public abstract class WsFrameBase {
private boolean swallowInput() {
- long toSkip = Math.min(payloadLength - payloadWritten, writePos - readPos);
- readPos += toSkip;
+ long toSkip = Math.min(payloadLength - payloadWritten, inputBuffer.remaining());
+ inputBuffer.position(inputBuffer.position() + (int) toSkip);
payloadWritten += toSkip;
if (payloadWritten == payloadLength) {
if (continuationExpected) {
@@ -758,16 +755,18 @@ public abstract class WsFrameBase {
// opCodes
// rsv is ignored as it known to be zero at this point
long toWrite = Math.min(
- payloadLength - payloadWritten, writePos - readPos);
+ payloadLength - payloadWritten, inputBuffer.remaining());
toWrite = Math.min(toWrite, dest.remaining());
- dest.put(inputBuffer, readPos, (int) toWrite);
- readPos += toWrite;
+ int orgLimit = inputBuffer.limit();
+ inputBuffer.limit(inputBuffer.position() + (int) toWrite);
+ dest.put(inputBuffer);
+ inputBuffer.limit(orgLimit);
payloadWritten += toWrite;
if (payloadWritten == payloadLength) {
return TransformationResult.END_OF_FRAME;
- } else if (readPos == writePos) {
+ } else if (inputBuffer.remaining() == 0) {
return TransformationResult.UNDERFLOW;
} else {
// !dest.hasRemaining()
@@ -797,20 +796,19 @@ public abstract class WsFrameBase {
// opCode is ignored as the transformation is the same for all
// opCodes
// rsv is ignored as it known to be zero at this point
- while (payloadWritten < payloadLength && readPos < writePos &&
+ while (payloadWritten < payloadLength && inputBuffer.remaining() > 0 &&
dest.hasRemaining()) {
- byte b = (byte) ((inputBuffer[readPos] ^ mask[maskIndex]) & 0xFF);
+ byte b = (byte) ((inputBuffer.get() ^ mask[maskIndex]) & 0xFF);
maskIndex++;
if (maskIndex == 4) {
maskIndex = 0;
}
- readPos++;
payloadWritten++;
dest.put(b);
}
if (payloadWritten == payloadLength) {
return TransformationResult.END_OF_FRAME;
- } else if (readPos == writePos) {
+ } else if (inputBuffer.remaining() == 0) {
return TransformationResult.UNDERFLOW;
} else {
// !dest.hasRemaining()
diff --git a/java/org/apache/tomcat/websocket/WsFrameClient.java b/java/org/apache/tomcat/websocket/WsFrameClient.java
index e408bd0..109c326 100644
--- a/java/org/apache/tomcat/websocket/WsFrameClient.java
+++ b/java/org/apache/tomcat/websocket/WsFrameClient.java
@@ -32,7 +32,7 @@ public class WsFrameClient extends WsFrameBase {
private final Log log = LogFactory.getLog(WsFrameClient.class);
private static final StringManager sm =
- StringManager.getManager(Constants.PACKAGE_NAME);
+ StringManager.getManager(WsFrameClient.class);
private final AsyncChannelWrapper channel;
private final CompletionHandler<Integer,Void> handler;
@@ -60,14 +60,20 @@ public class WsFrameClient extends WsFrameBase {
private void processSocketRead() throws IOException {
while (response.hasRemaining()) {
- int remaining = response.remaining();
+ inputBuffer.mark();
+ inputBuffer.position(inputBuffer.limit()).limit(inputBuffer.capacity());
- int toCopy = Math.min(remaining, inputBuffer.length - writePos);
+ int toCopy = Math.min(response.remaining(), inputBuffer.remaining());
// Copy remaining bytes read in HTTP phase to input buffer used by
// frame processing
- response.get(inputBuffer, writePos, toCopy);
- writePos += toCopy;
+
+ int orgLimit = response.limit();
+ response.limit(response.position() + toCopy);
+ inputBuffer.put(response);
+ response.limit(orgLimit);
+
+ inputBuffer.limit(inputBuffer.position()).reset();
// Process the data we have
processInputBuffer();
@@ -136,7 +142,7 @@ public class WsFrameClient extends WsFrameBase {
// continuing to send a message after the server sent a close
// control message.
if (isOpen()) {
- log.debug(sm.getString("wsFrameClient.ioe", e));
+ log.debug(sm.getString("wsFrameClient.ioe"), e);
close(e);
}
}
diff --git a/java/org/apache/tomcat/websocket/WsHandshakeResponse.java b/java/org/apache/tomcat/websocket/WsHandshakeResponse.java
index 13af6bb..ecb39e4 100644
--- a/java/org/apache/tomcat/websocket/WsHandshakeResponse.java
+++ b/java/org/apache/tomcat/websocket/WsHandshakeResponse.java
@@ -23,6 +23,8 @@ import java.util.Map.Entry;
import javax.websocket.HandshakeResponse;
+import org.apache.tomcat.util.collections.CaseInsensitiveKeyMap;
+
/**
* Represents the response to a WebSocket handshake.
*/
diff --git a/java/org/apache/tomcat/websocket/WsRemoteEndpointImplBase.java b/java/org/apache/tomcat/websocket/WsRemoteEndpointImplBase.java
index a59efbc..826e9a5 100644
--- a/java/org/apache/tomcat/websocket/WsRemoteEndpointImplBase.java
+++ b/java/org/apache/tomcat/websocket/WsRemoteEndpointImplBase.java
@@ -19,6 +19,7 @@ package org.apache.tomcat.websocket;
import java.io.IOException;
import java.io.OutputStream;
import java.io.Writer;
+import java.net.SocketTimeoutException;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.CharsetEncoder;
@@ -27,10 +28,9 @@ import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.List;
import java.util.Queue;
-import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
+import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.websocket.CloseReason;
@@ -51,13 +51,9 @@ import org.apache.tomcat.util.res.StringManager;
public abstract class WsRemoteEndpointImplBase implements RemoteEndpoint {
private static final StringManager sm =
- StringManager.getManager(Constants.PACKAGE_NAME);
+ StringManager.getManager(WsRemoteEndpointImplBase.class);
- // Milliseconds so this is 20 seconds
- private static final long DEFAULT_BLOCKING_SEND_TIMEOUT = 20 * 1000;
-
- public static final String BLOCKING_SEND_TIMEOUT_PROPERTY =
- "org.apache.tomcat.websocket.BLOCKING_SEND_TIMEOUT";
+ protected static final SendResult SENDRESULT_OK = new SendResult();
private final Log log = LogFactory.getLog(WsRemoteEndpointImplBase.class);
@@ -67,7 +63,7 @@ public abstract class WsRemoteEndpointImplBase implements RemoteEndpoint {
new IntermediateMessageHandler(this);
private Transformation transformation = null;
- private boolean messagePartInProgress = false;
+ private final Semaphore messagePartInProgress = new Semaphore(1);
private final Queue<MessagePart> messagePartQueue = new ArrayDeque<>();
private final Object messagePartLock = new Object();
@@ -122,7 +118,7 @@ public abstract class WsRemoteEndpointImplBase implements RemoteEndpoint {
@Override
public void flushBatch() throws IOException {
- startMessageBlock(Constants.INTERNAL_OPCODE_FLUSH, null, true);
+ sendMessageBlock(Constants.INTERNAL_OPCODE_FLUSH, null, true);
}
@@ -131,7 +127,7 @@ public abstract class WsRemoteEndpointImplBase implements RemoteEndpoint {
throw new IllegalArgumentException(sm.getString("wsRemoteEndpoint.nullData"));
}
stateMachine.binaryStart();
- startMessageBlock(Constants.OPCODE_BINARY, data, true);
+ sendMessageBlock(Constants.OPCODE_BINARY, data, true);
stateMachine.complete(true);
}
@@ -150,7 +146,7 @@ public abstract class WsRemoteEndpointImplBase implements RemoteEndpoint {
if (handler == null) {
throw new IllegalArgumentException(sm.getString("wsRemoteEndpoint.nullHandler"));
}
- StateUpdateSendHandler sush = new StateUpdateSendHandler(handler);
+ StateUpdateSendHandler sush = new StateUpdateSendHandler(handler, stateMachine);
stateMachine.binaryStart();
startMessage(Constants.OPCODE_BINARY, data, true, sush);
}
@@ -162,7 +158,7 @@ public abstract class WsRemoteEndpointImplBase implements RemoteEndpoint {
throw new IllegalArgumentException(sm.getString("wsRemoteEndpoint.nullData"));
}
stateMachine.binaryPartialStart();
- startMessageBlock(Constants.OPCODE_BINARY, partialByte, last);
+ sendMessageBlock(Constants.OPCODE_BINARY, partialByte, last);
stateMachine.complete(last);
}
@@ -173,7 +169,7 @@ public abstract class WsRemoteEndpointImplBase implements RemoteEndpoint {
if (applicationData.remaining() > 125) {
throw new IllegalArgumentException(sm.getString("wsRemoteEndpoint.tooMuchData"));
}
- startMessageBlock(Constants.OPCODE_PING, applicationData, true);
+ sendMessageBlock(Constants.OPCODE_PING, applicationData, true);
}
@@ -183,7 +179,7 @@ public abstract class WsRemoteEndpointImplBase implements RemoteEndpoint {
if (applicationData.remaining() > 125) {
throw new IllegalArgumentException(sm.getString("wsRemoteEndpoint.tooMuchData"));
}
- startMessageBlock(Constants.OPCODE_PONG, applicationData, true);
+ sendMessageBlock(Constants.OPCODE_PONG, applicationData, true);
}
@@ -192,7 +188,7 @@ public abstract class WsRemoteEndpointImplBase implements RemoteEndpoint {
throw new IllegalArgumentException(sm.getString("wsRemoteEndpoint.nullData"));
}
stateMachine.textStart();
- sendPartialString(CharBuffer.wrap(text), true);
+ sendMessageBlock(CharBuffer.wrap(text), true);
}
@@ -224,7 +220,7 @@ public abstract class WsRemoteEndpointImplBase implements RemoteEndpoint {
throw new IllegalArgumentException(sm.getString("wsRemoteEndpoint.nullData"));
}
stateMachine.textPartialStart();
- sendPartialString(CharBuffer.wrap(fragment), isLast);
+ sendMessageBlock(CharBuffer.wrap(fragment), isLast);
}
@@ -240,50 +236,95 @@ public abstract class WsRemoteEndpointImplBase implements RemoteEndpoint {
}
- void sendPartialString(CharBuffer part, boolean last) throws IOException {
- try {
- // 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.
- long timeout = getBlockingSendTimeout();
- FutureToSendHandler f2sh = new FutureToSendHandler(wsSession);
- TextMessageSendHandler tmsh = new TextMessageSendHandler(f2sh, part,
- last, encoder, encoderBuffer, this);
- tmsh.write();
- if (timeout == -1) {
- f2sh.get();
- } else {
- f2sh.get(timeout, TimeUnit.MILLISECONDS);
+ void sendMessageBlock(CharBuffer part, boolean last) throws IOException {
+ long timeoutExpiry = getTimeoutExpiry();
+ boolean isDone = false;
+ while (!isDone) {
+ encoderBuffer.clear();
+ CoderResult cr = encoder.encode(part, encoderBuffer, true);
+ if (cr.isError()) {
+ throw new IllegalArgumentException(cr.toString());
}
- } catch (InterruptedException | ExecutionException |
- TimeoutException e) {
- handleSendFailure(e);
+ isDone = !cr.isOverflow();
+ encoderBuffer.flip();
+ sendMessageBlock(Constants.OPCODE_TEXT, encoderBuffer, last && isDone, timeoutExpiry);
}
+ stateMachine.complete(last);
}
- void startMessageBlock(byte opCode, ByteBuffer payload, boolean last)
+ void sendMessageBlock(byte opCode, ByteBuffer payload, boolean last)
throws IOException {
+ sendMessageBlock(opCode, payload, last, getTimeoutExpiry());
+ }
+
+
+ private long getTimeoutExpiry() {
// 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.
long timeout = getBlockingSendTimeout();
- FutureToSendHandler f2sh =
- new FutureToSendHandler(wsSession, opCode == Constants.OPCODE_CLOSE);
- startMessage(opCode, payload, last, f2sh);
+ if (timeout < 0) {
+ return Long.MAX_VALUE;
+ } else {
+ return System.currentTimeMillis() + timeout;
+ }
+ }
+
+
+ private void sendMessageBlock(byte opCode, ByteBuffer payload, boolean last,
+ long timeoutExpiry) throws IOException {
+ wsSession.updateLastActive();
+
+ BlockingSendHandler bsh = new BlockingSendHandler();
+
+ List<MessagePart> messageParts = new ArrayList<>();
+ messageParts.add(new MessagePart(last, 0, opCode, payload, bsh, bsh, timeoutExpiry));
+
+ messageParts = transformation.sendMessagePart(messageParts);
+
+ // 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) {
+ return;
+ }
+
+ long timeout = timeoutExpiry - System.currentTimeMillis();
try {
- if (timeout == -1) {
- f2sh.get();
- } else {
- f2sh.get(timeout, TimeUnit.MILLISECONDS);
+ if (!messagePartInProgress.tryAcquire(timeout, TimeUnit.MILLISECONDS)) {
+ String msg = sm.getString("wsRemoteEndpoint.acquireTimeout");
+ wsSession.doClose(new CloseReason(CloseCodes.GOING_AWAY, msg),
+ new CloseReason(CloseCodes.CLOSED_ABNORMALLY, msg));
+ throw new SocketTimeoutException(msg);
}
- if (payload != null) {
- payload.clear();
+ } catch (InterruptedException e) {
+ String msg = sm.getString("wsRemoteEndpoint.sendInterupt");
+ wsSession.doClose(new CloseReason(CloseCodes.GOING_AWAY, msg),
+ new CloseReason(CloseCodes.CLOSED_ABNORMALLY, msg));
+ throw new IOException(msg, e);
+ }
+
+ for (MessagePart mp : messageParts) {
+ writeMessagePart(mp);
+ if (!bsh.getSendResult().isOK()) {
+ messagePartInProgress.release();
+ Throwable t = bsh.getSendResult().getException();
+ wsSession.doClose(new CloseReason(CloseCodes.GOING_AWAY, t.getMessage()),
+ new CloseReason(CloseCodes.CLOSED_ABNORMALLY, t.getMessage()));
+ throw new IOException (t);
}
- } catch (InterruptedException | ExecutionException |
- TimeoutException e) {
- handleSendFailure(e);
+ // The BlockingSendHandler doesn't call end message so update the
+ // flags.
+ fragmented = nextFragmented;
+ text = nextText;
+ }
+
+ if (payload != null) {
+ payload.clear();
}
+
+ endMessage(null, null);
}
@@ -295,13 +336,13 @@ public abstract class WsRemoteEndpointImplBase implements RemoteEndpoint {
List<MessagePart> messageParts = new ArrayList<>();
messageParts.add(new MessagePart(last, 0, opCode, payload,
intermediateMessageHandler,
- new EndMessageHandler(this, handler)));
+ new EndMessageHandler(this, handler), -1));
messageParts = transformation.sendMessagePart(messageParts);
// Some extensions/transformations may buffer messages so it is possible
// that no message parts will be returned. If this is the case the
- // trigger the suppler SendHandler
+ // trigger the supplied SendHandler
if (messageParts.size() == 0) {
handler.onResult(new SendResult());
return;
@@ -316,7 +357,9 @@ public abstract class WsRemoteEndpointImplBase implements RemoteEndpoint {
// the session has been closed. Complain loudly.
log.warn(sm.getString("wsRemoteEndpoint.flushOnCloseFailed"));
}
- if (messagePartInProgress) {
+ if (messagePartInProgress.tryAcquire()) {
+ doWrite = true;
+ } else {
// When a control message is sent while another message is being
// sent, the control message is queued. Chances are the
// subsequent data message part will end up queued while the
@@ -327,9 +370,6 @@ public abstract class WsRemoteEndpointImplBase implements RemoteEndpoint {
// Add it to the queue
messagePartQueue.add(mp);
- } else {
- messagePartInProgress = true;
- doWrite = true;
}
// Add any remaining messages to the queue
messagePartQueue.addAll(messageParts);
@@ -353,7 +393,7 @@ public abstract class WsRemoteEndpointImplBase implements RemoteEndpoint {
mpNext = messagePartQueue.poll();
if (mpNext == null) {
- messagePartInProgress = false;
+ messagePartInProgress.release();
} else if (!closed){
// Session may have been closed unexpectedly in the middle of
// sending a fragmented message closing the endpoint. If this
@@ -391,7 +431,7 @@ public abstract class WsRemoteEndpointImplBase implements RemoteEndpoint {
outputBuffer.flip();
SendHandler flushHandler = new OutputBufferFlushSendHandler(
outputBuffer, mp.getEndHandler());
- doWrite(flushHandler, outputBuffer);
+ doWrite(flushHandler, mp.getBlockingWriteTimeoutExpiry(), outputBuffer);
return;
}
@@ -445,25 +485,26 @@ public abstract class WsRemoteEndpointImplBase implements RemoteEndpoint {
if (getBatchingAllowed() || isMasked()) {
// Need to write via output buffer
OutputBufferSendHandler obsh = new OutputBufferSendHandler(
- mp.getEndHandler(), headerBuffer, mp.getPayload(), mask,
+ mp.getEndHandler(), mp.getBlockingWriteTimeoutExpiry(),
+ headerBuffer, mp.getPayload(), mask,
outputBuffer, !getBatchingAllowed(), this);
obsh.write();
} else {
// Can write directly
- doWrite(mp.getEndHandler(), headerBuffer, mp.getPayload());
+ doWrite(mp.getEndHandler(), mp.getBlockingWriteTimeoutExpiry(),
+ headerBuffer, mp.getPayload());
}
}
private long getBlockingSendTimeout() {
- Object obj = wsSession.getUserProperties().get(
- BLOCKING_SEND_TIMEOUT_PROPERTY);
+ Object obj = wsSession.getUserProperties().get(Constants.BLOCKING_SEND_TIMEOUT_PROPERTY);
Long userTimeout = null;
if (obj instanceof Long) {
userTimeout = (Long) obj;
}
if (userTimeout == null) {
- return DEFAULT_BLOCKING_SEND_TIMEOUT;
+ return Constants.DEFAULT_BLOCKING_SEND_TIMEOUT;
} else {
return userTimeout.longValue();
}
@@ -518,44 +559,46 @@ public abstract class WsRemoteEndpointImplBase implements RemoteEndpoint {
}
+ @SuppressWarnings({"unchecked", "rawtypes"})
public void sendObject(Object obj) throws IOException, EncodeException {
- Future<Void> f = sendObjectByFuture(obj);
- try {
- f.get();
- } catch (InterruptedException | ExecutionException e) {
- handleSendFailureWithEncode(e);
+ if (obj == null) {
+ throw new IllegalArgumentException(sm.getString("wsRemoteEndpoint.nullData"));
}
- }
-
-
- private void handleSendFailure(Throwable t) throws IOException {
- try {
- handleSendFailureWithEncode(t);
- } catch (EncodeException e) {
- // Should never happen. But in case it does...
- throw new IOException(e);
+ /*
+ * Note that the implementation will convert primitives and their object
+ * equivalents by default but that users are free to specify their own
+ * encoders and decoders for this if they wish.
+ */
+ Encoder encoder = findEncoder(obj);
+ if (encoder == null && Util.isPrimitive(obj.getClass())) {
+ String msg = obj.toString();
+ sendString(msg);
+ return;
}
- }
-
-
- private void handleSendFailureWithEncode(Throwable t) throws IOException, EncodeException {
- // First, unwrap any execution exception
- if (t instanceof ExecutionException && t.getCause() != null) {
- t = t.getCause();
+ if (encoder == null && byte[].class.isAssignableFrom(obj.getClass())) {
+ ByteBuffer msg = ByteBuffer.wrap((byte[]) obj);
+ sendBytes(msg);
+ return;
}
- // Close the session
- wsSession.doClose(new CloseReason(CloseCodes.GOING_AWAY, t.getMessage()),
- new CloseReason(CloseCodes.CLOSED_ABNORMALLY, t.getMessage()));
-
- // Rethrow the exception
- if (t instanceof EncodeException) {
- throw (EncodeException) t;
- }
- if (t instanceof IOException) {
- throw (IOException) t;
+ if (encoder instanceof Encoder.Text) {
+ String msg = ((Encoder.Text) encoder).encode(obj);
+ sendString(msg);
+ } else if (encoder instanceof Encoder.TextStream) {
+ try (Writer w = getSendWriter()) {
+ ((Encoder.TextStream) encoder).encode(obj, w);
+ }
+ } else if (encoder instanceof Encoder.Binary) {
+ ByteBuffer msg = ((Encoder.Binary) encoder).encode(obj);
+ sendBytes(msg);
+ } else if (encoder instanceof Encoder.BinaryStream) {
+ try (OutputStream os = getSendStream()) {
+ ((Encoder.BinaryStream) encoder).encode(obj, os);
+ }
+ } else {
+ throw new EncodeException(obj, sm.getString(
+ "wsRemoteEndpoint.noEncoder", obj.getClass()));
}
- throw new IOException(t);
}
@@ -668,7 +711,8 @@ public abstract class WsRemoteEndpointImplBase implements RemoteEndpoint {
}
- protected abstract void doWrite(SendHandler handler, ByteBuffer... data);
+ protected abstract void doWrite(SendHandler handler, long blockingWrieTimeoutExpiry,
+ ByteBuffer... data);
protected abstract boolean isMasked();
protected abstract void doClose();
@@ -785,6 +829,7 @@ public abstract class WsRemoteEndpointImplBase implements RemoteEndpoint {
private static class OutputBufferSendHandler implements SendHandler {
private final SendHandler handler;
+ private final long blockingWriteTimeoutExpiry;
private final ByteBuffer headerBuffer;
private final ByteBuffer payload;
private final byte[] mask;
@@ -794,9 +839,11 @@ public abstract class WsRemoteEndpointImplBase implements RemoteEndpoint {
private int maskIndex = 0;
public OutputBufferSendHandler(SendHandler completion,
+ long blockingWriteTimeoutExpiry,
ByteBuffer headerBuffer, ByteBuffer payload, byte[] mask,
ByteBuffer outputBuffer, boolean flushRequired,
WsRemoteEndpointImplBase endpoint) {
+ this.blockingWriteTimeoutExpiry = blockingWriteTimeoutExpiry;
this.handler = completion;
this.headerBuffer = headerBuffer;
this.payload = payload;
@@ -814,7 +861,7 @@ public abstract class WsRemoteEndpointImplBase implements RemoteEndpoint {
if (headerBuffer.hasRemaining()) {
// Still more headers to write, need to flush
outputBuffer.flip();
- endpoint.doWrite(this, outputBuffer);
+ endpoint.doWrite(this, blockingWriteTimeoutExpiry, outputBuffer);
return;
}
@@ -846,21 +893,21 @@ public abstract class WsRemoteEndpointImplBase implements RemoteEndpoint {
if (payloadLeft > outputSpace) {
// Restore the original limit
payload.limit(payloadLimit);
- // Still more headers to write, need to flush
+ // Still more data to write, need to flush
outputBuffer.flip();
- endpoint.doWrite(this, outputBuffer);
+ endpoint.doWrite(this, blockingWriteTimeoutExpiry, outputBuffer);
return;
}
if (flushRequired) {
outputBuffer.flip();
if (outputBuffer.remaining() == 0) {
- handler.onResult(new SendResult());
+ handler.onResult(SENDRESULT_OK);
} else {
- endpoint.doWrite(this, outputBuffer);
+ endpoint.doWrite(this, blockingWriteTimeoutExpiry, outputBuffer);
}
} else {
- handler.onResult(new SendResult());
+ handler.onResult(SENDRESULT_OK);
}
}
@@ -869,7 +916,7 @@ public abstract class WsRemoteEndpointImplBase implements RemoteEndpoint {
public void onResult(SendResult result) {
if (result.isOK()) {
if (outputBuffer.hasRemaining()) {
- endpoint.doWrite(this, outputBuffer);
+ endpoint.doWrite(this, blockingWriteTimeoutExpiry, outputBuffer);
} else {
outputBuffer.clear();
write();
@@ -882,7 +929,7 @@ public abstract class WsRemoteEndpointImplBase implements RemoteEndpoint {
/**
- * Ensures that tne output buffer is cleared after it has been flushed.
+ * Ensures that the output buffer is cleared after it has been flushed.
*/
private static class OutputBufferFlushSendHandler implements SendHandler {
@@ -989,7 +1036,7 @@ public abstract class WsRemoteEndpointImplBase implements RemoteEndpoint {
private void doWrite(boolean last) throws IOException {
if (!Constants.STREAMS_DROP_EMPTY_MESSAGES || used) {
buffer.flip();
- endpoint.startMessageBlock(Constants.OPCODE_BINARY, buffer, last);
+ endpoint.sendMessageBlock(Constants.OPCODE_BINARY, buffer, last);
}
endpoint.stateMachine.complete(last);
buffer.clear();
@@ -1066,7 +1113,7 @@ public abstract class WsRemoteEndpointImplBase implements RemoteEndpoint {
private void doWrite(boolean last) throws IOException {
if (!Constants.STREAMS_DROP_EMPTY_MESSAGES || used) {
buffer.flip();
- endpoint.sendPartialString(buffer, last);
+ endpoint.sendMessageBlock(buffer, last);
buffer.clear();
} else {
endpoint.stateMachine.complete(last);
@@ -1180,12 +1227,14 @@ public abstract class WsRemoteEndpointImplBase implements RemoteEndpoint {
}
- private class StateUpdateSendHandler implements SendHandler {
+ private static class StateUpdateSendHandler implements SendHandler {
private final SendHandler handler;
+ private final StateMachine stateMachine;
- public StateUpdateSendHandler(SendHandler handler) {
+ public StateUpdateSendHandler(SendHandler handler, StateMachine stateMachine) {
this.handler = handler;
+ this.stateMachine = stateMachine;
}
@Override
@@ -1196,4 +1245,19 @@ public abstract class WsRemoteEndpointImplBase implements RemoteEndpoint {
handler.onResult(result);
}
}
+
+
+ private static class BlockingSendHandler implements SendHandler {
+
+ private SendResult sendResult = null;
+
+ @Override
+ public void onResult(SendResult result) {
+ sendResult = result;
+ }
+
+ public SendResult getSendResult() {
+ return sendResult;
+ }
+ }
}
diff --git a/java/org/apache/tomcat/websocket/WsRemoteEndpointImplClient.java b/java/org/apache/tomcat/websocket/WsRemoteEndpointImplClient.java
index 8ee4313..160b77d 100644
--- a/java/org/apache/tomcat/websocket/WsRemoteEndpointImplClient.java
+++ b/java/org/apache/tomcat/websocket/WsRemoteEndpointImplClient.java
@@ -16,10 +16,14 @@
*/
package org.apache.tomcat.websocket;
+import java.io.IOException;
import java.nio.ByteBuffer;
+import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
import javax.websocket.SendHandler;
+import javax.websocket.SendResult;
public class WsRemoteEndpointImplClient extends WsRemoteEndpointImplBase {
@@ -37,20 +41,31 @@ public class WsRemoteEndpointImplClient extends WsRemoteEndpointImplBase {
@Override
- protected void doWrite(SendHandler handler, ByteBuffer... data) {
- long timeout = getSendTimeout();
- if (timeout < 1) {
- timeout = Long.MAX_VALUE;
+ protected void doWrite(SendHandler handler, long blockingWriteTimeoutExpiry,
+ ByteBuffer... data) {
+ long timeout;
+ for (ByteBuffer byteBuffer : data) {
+ if (blockingWriteTimeoutExpiry == -1) {
+ timeout = getSendTimeout();
+ if (timeout < 1) {
+ timeout = Long.MAX_VALUE;
+ }
+ } else {
+ timeout = blockingWriteTimeoutExpiry - System.currentTimeMillis();
+ if (timeout < 0) {
+ SendResult sr = new SendResult(new IOException("Blocking write timeout"));
+ handler.onResult(sr);
+ }
+ }
+ try {
+ channel.write(byteBuffer).get(timeout, TimeUnit.MILLISECONDS);
+ } catch (InterruptedException | ExecutionException | TimeoutException e) {
+ handler.onResult(new SendResult(e));
+ return;
+ }
}
- SendHandlerToCompletionHandler sh2ch =
- new SendHandlerToCompletionHandler(handler);
- try {
- channel.write(data, 0, data.length, timeout, TimeUnit.MILLISECONDS,
- null, sh2ch);
- } catch (IllegalStateException ise) {
- sh2ch.failed(ise, null);
- }
+ handler.onResult(SENDRESULT_OK);
}
@Override
diff --git a/java/org/apache/tomcat/websocket/WsSession.java b/java/org/apache/tomcat/websocket/WsSession.java
index 3942da4..14fb41f 100644
--- a/java/org/apache/tomcat/websocket/WsSession.java
+++ b/java/org/apache/tomcat/websocket/WsSession.java
@@ -49,6 +49,7 @@ import javax.websocket.WebSocketContainer;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.InstanceManager;
+import org.apache.tomcat.InstanceManagerBindings;
import org.apache.tomcat.util.ExceptionUtils;
import org.apache.tomcat.util.res.StringManager;
@@ -182,6 +183,9 @@ public class WsSession implements Session {
this.id = Long.toHexString(ids.getAndIncrement());
InstanceManager instanceManager = webSocketContainer.getInstanceManager();
+ if (instanceManager == null) {
+ instanceManager = InstanceManagerBindings.get(applicationClassLoader);
+ }
if (instanceManager != null) {
try {
instanceManager.newInstance(localEndpoint);
@@ -377,7 +381,7 @@ public class WsSession implements Session {
@Override
public boolean isOpen() {
- return state == State.OPEN || state == State.SENDING_CLOSED;
+ return state == State.OPEN;
}
@@ -475,8 +479,6 @@ public class WsSession implements Session {
return;
}
- state = State.CLOSED_SENT;
-
if (log.isDebugEnabled()) {
log.debug(sm.getString("wsSession.doClose", id));
}
@@ -487,6 +489,8 @@ public class WsSession implements Session {
fireEndpointOnError(e);
}
+ state = State.OUTPUT_CLOSED;
+
sendCloseMessage(closeReasonMessage);
fireEndpointOnClose(closeReasonLocal);
}
@@ -518,9 +522,8 @@ public class WsSession implements Session {
fireEndpointOnError(e);
}
if (state == State.OPEN) {
- state = State.SENDING_CLOSED;
+ state = State.OUTPUT_CLOSED;
sendCloseMessage(closeReason);
- state = State.CLOSED_SENT;
fireEndpointOnClose(closeReason);
}
state = State.CLOSED;
@@ -536,6 +539,9 @@ public class WsSession implements Session {
// Fire the onClose event
Throwable throwable = null;
InstanceManager instanceManager = webSocketContainer.getInstanceManager();
+ if (instanceManager == null) {
+ instanceManager = InstanceManagerBindings.get(applicationClassLoader);
+ }
Thread t = Thread.currentThread();
ClassLoader cl = t.getContextClassLoader();
t.setContextClassLoader(applicationClassLoader);
@@ -597,7 +603,7 @@ public class WsSession implements Session {
}
msg.flip();
try {
- wsRemoteEndpoint.startMessageBlock(Constants.OPCODE_CLOSE, msg, true);
+ wsRemoteEndpoint.sendMessageBlock(Constants.OPCODE_CLOSE, msg, true);
} catch (IOException | WritePendingException e) {
// Failed to send close message. Close the socket and let the caller
// deal with the Exception
@@ -656,6 +662,7 @@ public class WsSession implements Session {
* Make the session aware of a {@link FutureToSendHandler} that will need to
* be forcibly closed if the session closes before the
* {@link FutureToSendHandler} completes.
+ * @param f2sh The handler
*/
protected void registerFuture(FutureToSendHandler f2sh) {
boolean fail = false;
@@ -663,8 +670,7 @@ public class WsSession implements Session {
// If the session has already been closed the any registered futures
// will have been processed so the failure result for this future
// needs to be set here.
- if (isOpen() || f2sh.isCloseMessage()) {
- // WebSocket session is open or this is the close message
+ if (state == State.OPEN) {
futures.put(f2sh, f2sh);
} else if (f2sh.isDone()) {
// NO-OP. The future completed before the session closed so no
@@ -778,7 +784,10 @@ public class WsSession implements Session {
}
if (System.currentTimeMillis() - lastActive > timeout) {
- String msg = sm.getString("wsSession.timeout");
+ String msg = sm.getString("wsSession.timeout", getId());
+ if (log.isDebugEnabled()) {
+ log.debug(msg);
+ }
doClose(new CloseReason(CloseCodes.GOING_AWAY, msg),
new CloseReason(CloseCodes.CLOSED_ABNORMALLY, msg));
}
@@ -797,8 +806,7 @@ public class WsSession implements Session {
private static enum State {
OPEN,
- SENDING_CLOSED,
- CLOSED_SENT,
+ OUTPUT_CLOSED,
CLOSED
}
}
diff --git a/java/org/apache/tomcat/websocket/WsWebSocketContainer.java b/java/org/apache/tomcat/websocket/WsWebSocketContainer.java
index c862279..6656221 100644
--- a/java/org/apache/tomcat/websocket/WsWebSocketContainer.java
+++ b/java/org/apache/tomcat/websocket/WsWebSocketContainer.java
@@ -68,44 +68,13 @@ import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.InstanceManager;
import org.apache.tomcat.util.codec.binary.Base64;
+import org.apache.tomcat.util.collections.CaseInsensitiveKeyMap;
import org.apache.tomcat.util.res.StringManager;
import org.apache.tomcat.websocket.pojo.PojoEndpointClient;
-public class WsWebSocketContainer
- implements WebSocketContainer, BackgroundProcess {
+public class WsWebSocketContainer implements WebSocketContainer, BackgroundProcess {
- /**
- * Property name to set to configure the value that is passed to
- * {@link SSLEngine#setEnabledProtocols(String[])}. The value should be a
- * comma separated string.
- */
- public static final String SSL_PROTOCOLS_PROPERTY =
- "org.apache.tomcat.websocket.SSL_PROTOCOLS";
- public static final String SSL_TRUSTSTORE_PROPERTY =
- "org.apache.tomcat.websocket.SSL_TRUSTSTORE";
- public static final String SSL_TRUSTSTORE_PWD_PROPERTY =
- "org.apache.tomcat.websocket.SSL_TRUSTSTORE_PWD";
- public static final String SSL_TRUSTSTORE_PWD_DEFAULT = "changeit";
- /**
- * Property name to set to configure used SSLContext. The value should be an
- * instance of SSLContext. If this property is present, the SSL_TRUSTSTORE*
- * properties are ignored.
- */
- public static final String SSL_CONTEXT_PROPERTY =
- "org.apache.tomcat.websocket.SSL_CONTEXT";
-
- /**
- * Property name to set to configure the timeout (in milliseconds) when
- * establishing a WebSocket connection to server. The default is
- * {@link #IO_TIMEOUT_MS_DEFAULT}.
- */
- public static final String IO_TIMEOUT_MS_PROPERTY =
- "org.apache.tomcat.websocket.IO_TIMEOUT_MS";
-
- public static final long IO_TIMEOUT_MS_DEFAULT = 5000;
-
- private static final StringManager sm =
- StringManager.getManager(Constants.PACKAGE_NAME);
+ private static final StringManager sm = StringManager.getManager(WsWebSocketContainer.class);
private static final Random random = new Random();
private static final byte[] crlf = new byte[] {13, 10};
@@ -217,7 +186,7 @@ public class WsWebSocketContainer
}
- @SuppressWarnings("resource") // socketChannel is closed with channel
+ @SuppressWarnings("resource") // socketChannel is closed
@Override
public Session connectToServer(Endpoint endpoint,
ClientEndpointConfig clientEndpointConfiguration, URI path)
@@ -306,9 +275,9 @@ public class WsWebSocketContainer
}
// Get the connection timeout
- long timeout = IO_TIMEOUT_MS_DEFAULT;
+ long timeout = Constants.IO_TIMEOUT_MS_DEFAULT;
String timeoutValue = (String) clientEndpointConfiguration.getUserProperties().get(
- IO_TIMEOUT_MS_PROPERTY);
+ Constants.IO_TIMEOUT_MS_PROPERTY);
if (timeoutValue != null) {
timeout = Long.valueOf(timeoutValue).intValue();
}
@@ -630,9 +599,11 @@ public class WsWebSocketContainer
// Request line
result.put(GET_BYTES);
- byte[] path = (null == uri.getPath() || "".equals(uri.getPath()))
- ? ROOT_URI_BYTES : uri.getRawPath().getBytes(StandardCharsets.ISO_8859_1);
- result.put(path);
+ if (null == uri.getPath() || "".equals(uri.getPath())) {
+ result.put(ROOT_URI_BYTES);
+ } else {
+ result.put(uri.getRawPath().getBytes(StandardCharsets.ISO_8859_1));
+ }
String query = uri.getRawQuery();
if (query != null) {
result.put((byte) '?');
@@ -641,8 +612,7 @@ public class WsWebSocketContainer
result.put(HTTP_VERSION_BYTES);
// Headers
- Iterator<Entry<String,List<String>>> iter =
- reqHeaders.entrySet().iterator();
+ Iterator<Entry<String,List<String>>> iter = reqHeaders.entrySet().iterator();
while (iter.hasNext()) {
Entry<String,List<String>> entry = iter.next();
addHeader(result, entry.getKey(), entry.getValue());
@@ -793,7 +763,7 @@ public class WsWebSocketContainer
try {
// See if a custom SSLContext has been provided
SSLContext sslContext =
- (SSLContext) userProperties.get(SSL_CONTEXT_PROPERTY);
+ (SSLContext) userProperties.get(Constants.SSL_CONTEXT_PROPERTY);
if (sslContext == null) {
// Create the SSL Context
@@ -801,12 +771,12 @@ public class WsWebSocketContainer
// Trust store
String sslTrustStoreValue =
- (String) userProperties.get(SSL_TRUSTSTORE_PROPERTY);
+ (String) userProperties.get(Constants.SSL_TRUSTSTORE_PROPERTY);
if (sslTrustStoreValue != null) {
String sslTrustStorePwdValue = (String) userProperties.get(
- SSL_TRUSTSTORE_PWD_PROPERTY);
+ Constants.SSL_TRUSTSTORE_PWD_PROPERTY);
if (sslTrustStorePwdValue == null) {
- sslTrustStorePwdValue = SSL_TRUSTSTORE_PWD_DEFAULT;
+ sslTrustStorePwdValue = Constants.SSL_TRUSTSTORE_PWD_DEFAULT;
}
File keyStoreFile = new File(sslTrustStoreValue);
@@ -828,7 +798,7 @@ public class WsWebSocketContainer
SSLEngine engine = sslContext.createSSLEngine();
String sslProtocolsValue =
- (String) userProperties.get(SSL_PROTOCOLS_PROPERTY);
+ (String) userProperties.get(Constants.SSL_PROTOCOLS_PROPERTY);
if (sslProtocolsValue != null) {
engine.setEnabledProtocols(sslProtocolsValue.split(","));
}
diff --git a/java/org/apache/tomcat/websocket/pojo/Constants.java b/java/org/apache/tomcat/websocket/pojo/Constants.java
index bcc9978..686443a 100644
--- a/java/org/apache/tomcat/websocket/pojo/Constants.java
+++ b/java/org/apache/tomcat/websocket/pojo/Constants.java
@@ -21,8 +21,10 @@ package org.apache.tomcat.websocket.pojo;
*/
public class Constants {
- protected static final String PACKAGE_NAME =
- Constants.class.getPackage().getName();
+ public static final String POJO_PATH_PARAM_KEY =
+ "org.apache.tomcat.websocket.pojo.PojoEndpoint.pathParams";
+ public static final String POJO_METHOD_MAPPING_KEY =
+ "org.apache.tomcat.websocket.pojo.PojoEndpoint.methodMapping";
private Constants() {
// Hide default constructor
diff --git a/java/org/apache/tomcat/websocket/pojo/PojoEndpointBase.java b/java/org/apache/tomcat/websocket/pojo/PojoEndpointBase.java
index dce9c37..47f7603 100644
--- a/java/org/apache/tomcat/websocket/pojo/PojoEndpointBase.java
+++ b/java/org/apache/tomcat/websocket/pojo/PojoEndpointBase.java
@@ -40,8 +40,7 @@ import org.apache.tomcat.util.res.StringManager;
public abstract class PojoEndpointBase extends Endpoint {
private static final Log log = LogFactory.getLog(PojoEndpointBase.class);
- private static final StringManager sm =
- StringManager.getManager(Constants.PACKAGE_NAME);
+ private static final StringManager sm = StringManager.getManager(PojoEndpointBase.class);
private Object pojo;
private Map<String,String> pathParameters;
diff --git a/java/org/apache/tomcat/websocket/pojo/PojoEndpointServer.java b/java/org/apache/tomcat/websocket/pojo/PojoEndpointServer.java
index 77ef83d..48af737 100644
--- a/java/org/apache/tomcat/websocket/pojo/PojoEndpointServer.java
+++ b/java/org/apache/tomcat/websocket/pojo/PojoEndpointServer.java
@@ -32,13 +32,7 @@ import org.apache.tomcat.util.res.StringManager;
public class PojoEndpointServer extends PojoEndpointBase {
private static final StringManager sm =
- StringManager.getManager(Constants.PACKAGE_NAME);
-
- public static final String POJO_PATH_PARAM_KEY =
- "org.apache.tomcat.websocket.pojo.PojoEndpoint.pathParams";
- public static final String POJO_METHOD_MAPPING_KEY =
- "org.apache.tomcat.websocket.pojo.PojoEndpoint.methodMapping";
-
+ StringManager.getManager(PojoEndpointServer.class);
@Override
public void onOpen(Session session, EndpointConfig endpointConfig) {
@@ -59,12 +53,12 @@ public class PojoEndpointServer extends PojoEndpointBase {
@SuppressWarnings("unchecked")
Map<String,String> pathParameters =
(Map<String, String>) sec.getUserProperties().get(
- POJO_PATH_PARAM_KEY);
+ Constants.POJO_PATH_PARAM_KEY);
setPathParameters(pathParameters);
PojoMethodMapping methodMapping =
(PojoMethodMapping) sec.getUserProperties().get(
- POJO_METHOD_MAPPING_KEY);
+ Constants.POJO_METHOD_MAPPING_KEY);
setMethodMapping(methodMapping);
doOnOpen(session, endpointConfig);
diff --git a/java/org/apache/tomcat/websocket/pojo/PojoMessageHandlerWholeBinary.java b/java/org/apache/tomcat/websocket/pojo/PojoMessageHandlerWholeBinary.java
index 2af4944..d91f5f1 100644
--- a/java/org/apache/tomcat/websocket/pojo/PojoMessageHandlerWholeBinary.java
+++ b/java/org/apache/tomcat/websocket/pojo/PojoMessageHandlerWholeBinary.java
@@ -39,7 +39,7 @@ public class PojoMessageHandlerWholeBinary
extends PojoMessageHandlerWholeBase<ByteBuffer> {
private static final StringManager sm =
- StringManager.getManager(Constants.PACKAGE_NAME);
+ StringManager.getManager(PojoMessageHandlerWholeBinary.class);
private final List<Decoder> decoders = new ArrayList<>();
diff --git a/java/org/apache/tomcat/websocket/pojo/PojoMessageHandlerWholeText.java b/java/org/apache/tomcat/websocket/pojo/PojoMessageHandlerWholeText.java
index 33ded7e..27e1c54 100644
--- a/java/org/apache/tomcat/websocket/pojo/PojoMessageHandlerWholeText.java
+++ b/java/org/apache/tomcat/websocket/pojo/PojoMessageHandlerWholeText.java
@@ -40,7 +40,7 @@ public class PojoMessageHandlerWholeText
extends PojoMessageHandlerWholeBase<String> {
private static final StringManager sm =
- StringManager.getManager(Constants.PACKAGE_NAME);
+ StringManager.getManager(PojoMessageHandlerWholeText.class);
private final List<Decoder> decoders = new ArrayList<>();
private final Class<?> primitiveType;
diff --git a/java/org/apache/tomcat/websocket/pojo/PojoMethodMapping.java b/java/org/apache/tomcat/websocket/pojo/PojoMethodMapping.java
index b42fcd8..e7b8bd1 100644
--- a/java/org/apache/tomcat/websocket/pojo/PojoMethodMapping.java
+++ b/java/org/apache/tomcat/websocket/pojo/PojoMethodMapping.java
@@ -58,7 +58,7 @@ import org.apache.tomcat.websocket.Util.DecoderMatch;
public class PojoMethodMapping {
private static final StringManager sm =
- StringManager.getManager(Constants.PACKAGE_NAME);
+ StringManager.getManager(PojoMethodMapping.class);
private final Method onOpen;
private final Method onClose;
diff --git a/java/org/apache/tomcat/websocket/server/Constants.java b/java/org/apache/tomcat/websocket/server/Constants.java
index 94b6e19..876f1b8 100644
--- a/java/org/apache/tomcat/websocket/server/Constants.java
+++ b/java/org/apache/tomcat/websocket/server/Constants.java
@@ -21,9 +21,6 @@ package org.apache.tomcat.websocket.server;
*/
public class Constants {
- protected static final String PACKAGE_NAME =
- Constants.class.getPackage().getName();
-
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 =
@@ -31,18 +28,6 @@ public class Constants {
public static final String ENFORCE_NO_ADD_AFTER_HANDSHAKE_CONTEXT_INIT_PARAM =
"org.apache.tomcat.websocket.noAddAfterHandshake";
- // Executor configuration
- public static final String EXECUTOR_CORE_SIZE_INIT_PARAM =
- "org.apache.tomcat.websocket.executorCoreSize";
- /**
- * @deprecated No longer used and will be removed in Tomcat 8.5.x
- */
- @Deprecated
- public static final String EXECUTOR_MAX_SIZE_INIT_PARAM =
- "org.apache.tomcat.websocket.executorMaxSize";
- public static final String EXECUTOR_KEEPALIVETIME_SECONDS_INIT_PARAM =
- "org.apache.tomcat.websocket.executorKeepAliveTimeSeconds";
-
public static final String SERVER_CONTAINER_SERVLET_CONTEXT_ATTRIBUTE =
"javax.websocket.server.ServerContainer";
diff --git a/java/org/apache/tomcat/websocket/server/LocalStrings.properties b/java/org/apache/tomcat/websocket/server/LocalStrings.properties
index cd78efc..8b8ff21 100644
--- a/java/org/apache/tomcat/websocket/server/LocalStrings.properties
+++ b/java/org/apache/tomcat/websocket/server/LocalStrings.properties
@@ -22,7 +22,6 @@ serverContainer.missingEndpoint=An Endpoint instance has been request for path [
serverContainer.pojoDeploy=POJO class [{0}] deploying to path [{1}] in ServletContext [{2}]
serverContainer.servletContextMismatch=Attempted to register a POJO annotated for WebSocket at path [{0}] in the ServletContext with context path [{1}] when the WebSocket ServerContainer is allocated to the ServletContext with context path [{2}]
serverContainer.servletContextMissing=No ServletContext was specified
-serverContainer.threadGroupNotDestroyed=Unable to destroy WebSocket thread group [{0}] as [{1}] threads were still running when the web application was stopped. The thread group will be destroyed once the threads terminate.
upgradeUtil.incompatibleRsv=Extensions were specified that have incompatible RSV bit usage
@@ -34,7 +33,9 @@ uriTemplate.invalidSegment=The segment [{0}] is not valid in the provided path [
wsFrameServer.bytesRead=Read [{0}] bytes into input buffer ready for processing
wsFrameServer.onDataAvailable=Method entry
+wsHttpUpgradeHandler.closeOnError=Closing WebSocket connection due to an error
wsHttpUpgradeHandler.destroyFailed=Failed to close WebConnection while destroying the WebSocket HttpUpgradeHandler
wsHttpUpgradeHandler.noPreInit=The preInit() method must be called to configure the WebSocket HttpUpgradeHandler before the container calls init(). Usually, this means the Servlet that created the WsHttpUpgradeHandler instance should also call preInit()
+wsHttpUpgradeHandler.serverStop=The server is stopping
wsRemoteEndpointServer.closeFailed=Failed to close the ServletOutputStream connection cleanly
\ No newline at end of file
diff --git a/java/org/apache/tomcat/websocket/server/UpgradeUtil.java b/java/org/apache/tomcat/websocket/server/UpgradeUtil.java
index ef225c8..b1cf697 100644
--- a/java/org/apache/tomcat/websocket/server/UpgradeUtil.java
+++ b/java/org/apache/tomcat/websocket/server/UpgradeUtil.java
@@ -225,7 +225,7 @@ public class UpgradeUtil {
ep = new PojoEndpointServer();
// Need to make path params available to POJO
perSessionServerEndpointConfig.getUserProperties().put(
- PojoEndpointServer.POJO_PATH_PARAM_KEY, pathParams);
+ org.apache.tomcat.websocket.pojo.Constants.POJO_PATH_PARAM_KEY, pathParams);
}
} catch (InstantiationException e) {
throw new ServletException(e);
diff --git a/java/org/apache/tomcat/websocket/server/UriTemplate.java b/java/org/apache/tomcat/websocket/server/UriTemplate.java
index 4d82c11..a9b8eb9 100644
--- a/java/org/apache/tomcat/websocket/server/UriTemplate.java
+++ b/java/org/apache/tomcat/websocket/server/UriTemplate.java
@@ -34,8 +34,7 @@ import org.apache.tomcat.util.res.StringManager;
*/
public class UriTemplate {
- private static final StringManager sm =
- StringManager.getManager(Constants.PACKAGE_NAME);
+ private static final StringManager sm = StringManager.getManager(UriTemplate.class);
private final String normalized;
private final List<Segment> segments = new ArrayList<>();
diff --git a/java/org/apache/tomcat/websocket/server/WsFilter.java b/java/org/apache/tomcat/websocket/server/WsFilter.java
index f4ee916..58a1bf7 100644
--- a/java/org/apache/tomcat/websocket/server/WsFilter.java
+++ b/java/org/apache/tomcat/websocket/server/WsFilter.java
@@ -83,6 +83,4 @@ public class WsFilter implements Filter {
public void destroy() {
// NO-OP
}
-
-
}
diff --git a/java/org/apache/tomcat/websocket/server/WsFrameServer.java b/java/org/apache/tomcat/websocket/server/WsFrameServer.java
index edb241d..70c0406 100644
--- a/java/org/apache/tomcat/websocket/server/WsFrameServer.java
+++ b/java/org/apache/tomcat/websocket/server/WsFrameServer.java
@@ -16,31 +16,32 @@
*/
package org.apache.tomcat.websocket.server;
-import java.io.EOFException;
import java.io.IOException;
-
-import javax.servlet.ServletInputStream;
+import java.nio.ByteBuffer;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
+import org.apache.tomcat.util.net.SocketWrapperBase;
import org.apache.tomcat.util.res.StringManager;
import org.apache.tomcat.websocket.Transformation;
import org.apache.tomcat.websocket.WsFrameBase;
+import org.apache.tomcat.websocket.WsIOException;
import org.apache.tomcat.websocket.WsSession;
public class WsFrameServer extends WsFrameBase {
private static final Log log = LogFactory.getLog(WsFrameServer.class);
- private static final StringManager sm = StringManager.getManager(Constants.PACKAGE_NAME);
+ private static final StringManager sm = StringManager.getManager(WsFrameServer.class);
- private final ServletInputStream sis;
- private final Object connectionReadLock = new Object();
+ private final SocketWrapperBase<?> socketWrapper;
+ private final ClassLoader applicationClassLoader;
- public WsFrameServer(ServletInputStream sis, WsSession wsSession,
- Transformation transformation) {
+ public WsFrameServer(SocketWrapperBase<?> socketWrapper, WsSession wsSession,
+ Transformation transformation, ClassLoader applicationClassLoader) {
super(wsSession, transformation);
- this.sis = sis;
+ this.socketWrapper = socketWrapper;
+ this.applicationClassLoader = applicationClassLoader;
}
@@ -54,22 +55,19 @@ public class WsFrameServer extends WsFrameBase {
if (log.isDebugEnabled()) {
log.debug("wsFrameServer.onDataAvailable");
}
- synchronized (connectionReadLock) {
- while (isOpen() && sis.isReady()) {
- // Fill up the input buffer with as much data as we can
- int read = sis.read(inputBuffer, writePos, inputBuffer.length - writePos);
- if (log.isDebugEnabled()) {
- log.debug(sm.getString("wsFrameServer.bytesRead", Integer.toString(read)));
- }
- if (read == 0) {
- return;
- }
- if (read == -1) {
- throw new EOFException();
- }
- writePos += read;
- processInputBuffer();
+ while (isOpen()) {
+ // Fill up the input buffer with as much data as we can
+ inputBuffer.mark();
+ inputBuffer.position(inputBuffer.limit()).limit(inputBuffer.capacity());
+ int read = socketWrapper.read(false, inputBuffer);
+ inputBuffer.limit(inputBuffer.position()).reset();
+ if (read <= 0) {
+ return;
+ }
+ if (log.isDebugEnabled()) {
+ log.debug(sm.getString("wsFrameServer.bytesRead", Integer.toString(read)));
}
+ processInputBuffer();
}
}
@@ -89,7 +87,38 @@ public class WsFrameServer extends WsFrameBase {
@Override
+ protected boolean isOpen() {
+ // Overridden to make it visible to other classes in this package
+ return super.isOpen();
+ }
+
+
+ @Override
protected Log getLog() {
return log;
}
+
+
+ @Override
+ protected void sendMessageText(boolean last) throws WsIOException {
+ ClassLoader cl = Thread.currentThread().getContextClassLoader();
+ try {
+ Thread.currentThread().setContextClassLoader(applicationClassLoader);
+ super.sendMessageText(last);
+ } finally {
+ Thread.currentThread().setContextClassLoader(cl);
+ }
+ }
+
+
+ @Override
+ protected void sendMessageBinary(ByteBuffer msg, boolean last) throws WsIOException {
+ ClassLoader cl = Thread.currentThread().getContextClassLoader();
+ try {
+ Thread.currentThread().setContextClassLoader(applicationClassLoader);
+ super.sendMessageBinary(msg, last);
+ } finally {
+ Thread.currentThread().setContextClassLoader(cl);
+ }
+ }
}
diff --git a/java/org/apache/tomcat/websocket/server/WsHandshakeRequest.java b/java/org/apache/tomcat/websocket/server/WsHandshakeRequest.java
index 05dd794..4bc7079 100644
--- a/java/org/apache/tomcat/websocket/server/WsHandshakeRequest.java
+++ b/java/org/apache/tomcat/websocket/server/WsHandshakeRequest.java
@@ -30,7 +30,7 @@ import java.util.Map.Entry;
import javax.servlet.http.HttpServletRequest;
import javax.websocket.server.HandshakeRequest;
-import org.apache.tomcat.websocket.CaseInsensitiveKeyMap;
+import org.apache.tomcat.util.collections.CaseInsensitiveKeyMap;
/**
* Represents the request that this session was opened under.
diff --git a/java/org/apache/tomcat/websocket/server/WsHttpUpgradeHandler.java b/java/org/apache/tomcat/websocket/server/WsHttpUpgradeHandler.java
index 34d3e18..4b0622c 100644
--- a/java/org/apache/tomcat/websocket/server/WsHttpUpgradeHandler.java
+++ b/java/org/apache/tomcat/websocket/server/WsHttpUpgradeHandler.java
@@ -20,12 +20,7 @@ import java.io.IOException;
import java.util.List;
import java.util.Map;
-import javax.servlet.ReadListener;
-import javax.servlet.ServletInputStream;
-import javax.servlet.ServletOutputStream;
-import javax.servlet.WriteListener;
import javax.servlet.http.HttpSession;
-import javax.servlet.http.HttpUpgradeHandler;
import javax.servlet.http.WebConnection;
import javax.websocket.CloseReason;
import javax.websocket.CloseReason.CloseCodes;
@@ -34,8 +29,13 @@ import javax.websocket.Endpoint;
import javax.websocket.EndpointConfig;
import javax.websocket.Extension;
+import org.apache.coyote.http11.upgrade.InternalHttpUpgradeHandler;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
+import org.apache.tomcat.util.net.AbstractEndpoint.Handler.SocketState;
+import org.apache.tomcat.util.net.SSLSupport;
+import org.apache.tomcat.util.net.SocketEvent;
+import org.apache.tomcat.util.net.SocketWrapperBase;
import org.apache.tomcat.util.res.StringManager;
import org.apache.tomcat.websocket.Transformation;
import org.apache.tomcat.websocket.WsIOException;
@@ -44,12 +44,14 @@ import org.apache.tomcat.websocket.WsSession;
/**
* Servlet 3.1 HTTP upgrade handler for WebSocket connections.
*/
-public class WsHttpUpgradeHandler implements HttpUpgradeHandler {
+public class WsHttpUpgradeHandler implements InternalHttpUpgradeHandler {
- private static final Log log =
- LogFactory.getLog(WsHttpUpgradeHandler.class);
- private static final StringManager sm =
- StringManager.getManager(Constants.PACKAGE_NAME);
+ private static final Log log = LogFactory.getLog(WsHttpUpgradeHandler.class);
+ private static final StringManager sm = StringManager.getManager(WsHttpUpgradeHandler.class);
+
+ private final ClassLoader applicationClassLoader;
+
+ private SocketWrapperBase<?> socketWrapper;
private Endpoint ep;
private EndpointConfig endpointConfig;
@@ -62,9 +64,22 @@ public class WsHttpUpgradeHandler implements HttpUpgradeHandler {
private boolean secure;
private WebConnection connection;
+ private WsRemoteEndpointImplServer wsRemoteEndpointServer;
+ private WsFrameServer wsFrame;
private WsSession wsSession;
+ public WsHttpUpgradeHandler() {
+ applicationClassLoader = Thread.currentThread().getContextClassLoader();
+ }
+
+
+ @Override
+ public void setSocketWrapper(SocketWrapperBase<?> socketWrapper) {
+ this.socketWrapper = socketWrapper;
+ }
+
+
public void preInit(Endpoint ep, EndpointConfig endpointConfig,
WsServerContainer wsc, WsHandshakeRequest handshakeRequest,
List<Extension> negotiatedExtensionsPhase2, String subProtocol,
@@ -89,17 +104,6 @@ public class WsHttpUpgradeHandler implements HttpUpgradeHandler {
sm.getString("wsHttpUpgradeHandler.noPreInit"));
}
- this.connection = connection;
-
- ServletInputStream sis;
- ServletOutputStream sos;
- try {
- sis = connection.getInputStream();
- sos = connection.getOutputStream();
- } catch (IOException e) {
- throw new IllegalStateException(e);
- }
-
String httpSessionId = null;
Object session = handshakeRequest.getHttpSession();
if (session != null ) {
@@ -109,9 +113,11 @@ public class WsHttpUpgradeHandler implements HttpUpgradeHandler {
// Need to call onOpen using the web application's class loader
// Create the frame using the application's class loader so it can pick
// up application specific config from the ServerContainerImpl
+ Thread t = Thread.currentThread();
+ ClassLoader cl = t.getContextClassLoader();
+ t.setContextClassLoader(applicationClassLoader);
try {
- WsRemoteEndpointImplServer wsRemoteEndpointServer =
- new WsRemoteEndpointImplServer(sos, webSocketContainer);
+ wsRemoteEndpointServer = new WsRemoteEndpointImplServer(socketWrapper, webSocketContainer);
wsSession = new WsSession(ep, wsRemoteEndpointServer,
webSocketContainer, handshakeRequest.getRequestURI(),
handshakeRequest.getParameterMap(),
@@ -119,21 +125,78 @@ public class WsHttpUpgradeHandler implements HttpUpgradeHandler {
handshakeRequest.getUserPrincipal(), httpSessionId,
negotiatedExtensions, subProtocol, pathParameters, secure,
endpointConfig);
- WsFrameServer wsFrame = new WsFrameServer(sis, wsSession, transformation);
- sos.setWriteListener(new WsWriteListener(this, wsRemoteEndpointServer));
+ wsFrame = new WsFrameServer(socketWrapper, wsSession, transformation,
+ applicationClassLoader);
// WsFrame adds the necessary final transformations. Copy the
// completed transformation chain to the remote end point.
wsRemoteEndpointServer.setTransformation(wsFrame.getTransformation());
ep.onOpen(wsSession, endpointConfig);
webSocketContainer.registerSession(ep, wsSession);
- sis.setReadListener(new WsReadListener(this, wsFrame));
} catch (DeploymentException e) {
throw new IllegalArgumentException(e);
+ } finally {
+ t.setContextClassLoader(cl);
+ }
+ }
+
+
+ @Override
+ public SocketState upgradeDispatch(SocketEvent status) {
+ switch (status) {
+ case OPEN_READ:
+ try {
+ wsFrame.onDataAvailable();
+ } catch (WsIOException ws) {
+ close(ws.getCloseReason());
+ } catch (IOException ioe) {
+ onError(ioe);
+ CloseReason cr = new CloseReason(
+ CloseCodes.CLOSED_ABNORMALLY, ioe.getMessage());
+ close(cr);
+ return SocketState.CLOSED;
+ }
+ break;
+ case OPEN_WRITE:
+ wsRemoteEndpointServer.onWritePossible(false);
+ break;
+ case STOP:
+ CloseReason cr = new CloseReason(CloseCodes.GOING_AWAY,
+ sm.getString("wsHttpUpgradeHandler.serverStop"));
+ try {
+ wsSession.close(cr);
+ } catch (IOException ioe) {
+ onError(ioe);
+ cr = new CloseReason(
+ CloseCodes.CLOSED_ABNORMALLY, ioe.getMessage());
+ close(cr);
+ return SocketState.CLOSED;
+ }
+ break;
+ case ERROR:
+ String msg = sm.getString("wsHttpUpgradeHandler.closeOnError");
+ wsSession.doClose(new CloseReason(CloseCodes.GOING_AWAY, msg),
+ new CloseReason(CloseCodes.CLOSED_ABNORMALLY, msg));
+ //$FALL-THROUGH$
+ case DISCONNECT:
+ case TIMEOUT:
+ return SocketState.CLOSED;
+
+ }
+ if (wsFrame.isOpen()) {
+ return SocketState.UPGRADED;
+ } else {
+ return SocketState.CLOSED;
}
}
@Override
+ public void pause() {
+ // NO-OP
+ }
+
+
+ @Override
public void destroy() {
if (connection != null) {
try {
@@ -146,10 +209,15 @@ public class WsHttpUpgradeHandler implements HttpUpgradeHandler {
private void onError(Throwable throwable) {
- wsSession.doClose(new CloseReason(CloseCodes.GOING_AWAY, throwable.getMessage()),
- new CloseReason(CloseCodes.CLOSED_ABNORMALLY, throwable.getMessage()));
-
- ep.onError(wsSession, throwable);
+ // Need to call onError using the web application's class loader
+ Thread t = Thread.currentThread();
+ ClassLoader cl = t.getContextClassLoader();
+ t.setContextClassLoader(applicationClassLoader);
+ try {
+ ep.onError(wsSession, throwable);
+ } finally {
+ t.setContextClassLoader(cl);
+ }
}
@@ -166,72 +234,9 @@ public class WsHttpUpgradeHandler implements HttpUpgradeHandler {
}
- private static class WsReadListener implements ReadListener {
-
- private final WsHttpUpgradeHandler wsProtocolHandler;
- private final WsFrameServer wsFrame;
-
-
- private WsReadListener(WsHttpUpgradeHandler wsProtocolHandler,
- WsFrameServer wsFrame) {
- this.wsProtocolHandler = wsProtocolHandler;
- this.wsFrame = wsFrame;
- }
-
-
- @Override
- public void onDataAvailable() {
- try {
- wsFrame.onDataAvailable();
- } catch (WsIOException ws) {
- wsProtocolHandler.close(ws.getCloseReason());
- } catch (IOException ioe) {
- onError(ioe);
- CloseReason cr = new CloseReason(
- CloseCodes.CLOSED_ABNORMALLY, ioe.getMessage());
- wsProtocolHandler.close(cr);
- }
- }
-
-
- @Override
- public void onAllDataRead() {
- // Will never happen with WebSocket
- throw new IllegalStateException();
- }
-
-
- @Override
- public void onError(Throwable throwable) {
- wsProtocolHandler.onError(throwable);
- }
- }
-
-
- private static class WsWriteListener implements WriteListener {
-
- private final WsHttpUpgradeHandler wsProtocolHandler;
- private final WsRemoteEndpointImplServer wsRemoteEndpointServer;
-
- private WsWriteListener(WsHttpUpgradeHandler wsProtocolHandler,
- WsRemoteEndpointImplServer wsRemoteEndpointServer) {
- this.wsProtocolHandler = wsProtocolHandler;
- this.wsRemoteEndpointServer = wsRemoteEndpointServer;
- }
-
-
- @Override
- public void onWritePossible() {
- // Triggered by the poller so this isn't the same thread that
- // triggered the write so no need for a dispatch
- wsRemoteEndpointServer.onWritePossible(false);
- }
-
-
- @Override
- public void onError(Throwable throwable) {
- wsProtocolHandler.onError(throwable);
- wsRemoteEndpointServer.close();
- }
+ @Override
+ public void setSslSupport(SSLSupport sslSupport) {
+ // NO-OP. WebSocket has no requirement to access the TLS information
+ // associated with the underlying connection.
}
}
diff --git a/java/org/apache/tomcat/websocket/server/WsRemoteEndpointImplServer.java b/java/org/apache/tomcat/websocket/server/WsRemoteEndpointImplServer.java
index 5264309..404533e 100644
--- a/java/org/apache/tomcat/websocket/server/WsRemoteEndpointImplServer.java
+++ b/java/org/apache/tomcat/websocket/server/WsRemoteEndpointImplServer.java
@@ -20,47 +20,41 @@ import java.io.EOFException;
import java.io.IOException;
import java.net.SocketTimeoutException;
import java.nio.ByteBuffer;
-import java.util.Queue;
-import java.util.concurrent.ConcurrentLinkedQueue;
-import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executor;
-import javax.servlet.ServletOutputStream;
import javax.websocket.SendHandler;
import javax.websocket.SendResult;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
+import org.apache.tomcat.util.net.AbstractEndpoint;
+import org.apache.tomcat.util.net.SocketWrapperBase;
import org.apache.tomcat.util.res.StringManager;
import org.apache.tomcat.websocket.Transformation;
import org.apache.tomcat.websocket.WsRemoteEndpointImplBase;
/**
* This is the server side {@link javax.websocket.RemoteEndpoint} implementation
- * - i.e. what the server uses to send data to the client. Communication is over
- * a {@link ServletOutputStream}.
+ * - i.e. what the server uses to send data to the client.
*/
public class WsRemoteEndpointImplServer extends WsRemoteEndpointImplBase {
private static final StringManager sm =
- StringManager.getManager(Constants.PACKAGE_NAME);
+ StringManager.getManager(WsRemoteEndpointImplServer.class);
private static final Log log = LogFactory.getLog(WsRemoteEndpointImplServer.class);
- private static final Queue<OnResultRunnable> onResultRunnables =
- new ConcurrentLinkedQueue<>();
-
- private final ServletOutputStream sos;
+ private final SocketWrapperBase<?> socketWrapper;
private final WsWriteTimeout wsWriteTimeout;
- private final ExecutorService executorService;
private volatile SendHandler handler = null;
private volatile ByteBuffer[] buffers = null;
private volatile long timeoutExpiry = -1;
+ private volatile boolean close;
-
- public WsRemoteEndpointImplServer(ServletOutputStream sos, WsServerContainer serverContainer) {
- this.sos = sos;
+ public WsRemoteEndpointImplServer(SocketWrapperBase<?> socketWrapper,
+ WsServerContainer serverContainer) {
+ this.socketWrapper = socketWrapper;
this.wsWriteTimeout = serverContainer.getTimeout();
- this.executorService = serverContainer.getExecutorService();
}
@@ -71,12 +65,41 @@ public class WsRemoteEndpointImplServer extends WsRemoteEndpointImplBase {
@Override
- protected void doWrite(SendHandler handler, ByteBuffer... buffers) {
- this.handler = handler;
- this.buffers = buffers;
- // This is definitely the same thread that triggered the write so a
- // dispatch will be required.
- onWritePossible(true);
+ protected void doWrite(SendHandler handler, long blockingWriteTimeoutExpiry,
+ ByteBuffer... buffers) {
+ if (blockingWriteTimeoutExpiry == -1) {
+ this.handler = handler;
+ this.buffers = buffers;
+ // This is definitely the same thread that triggered the write so a
+ // dispatch will be required.
+ onWritePossible(true);
+ } else {
+ // Blocking
+ for (ByteBuffer buffer : buffers) {
+ long timeout = blockingWriteTimeoutExpiry - System.currentTimeMillis();
+ if (timeout < 0) {
+ SendResult sr = new SendResult(new SocketTimeoutException());
+ handler.onResult(sr);
+ return;
+ }
+ socketWrapper.setWriteTimeout(timeout);
+ try {
+ socketWrapper.write(true, buffer);
+ timeout = blockingWriteTimeoutExpiry - System.currentTimeMillis();
+ if (timeout < 0) {
+ SendResult sr = new SendResult(new SocketTimeoutException());
+ handler.onResult(sr);
+ return;
+ }
+ socketWrapper.setWriteTimeout(timeout);
+ socketWrapper.flush(true);
+ handler.onResult(SENDRESULT_OK);
+ } catch (IOException e) {
+ SendResult sr = new SendResult(e);
+ handler.onResult(sr);
+ }
+ }
+ }
}
@@ -87,35 +110,40 @@ public class WsRemoteEndpointImplServer extends WsRemoteEndpointImplBase {
// was written
return;
}
- boolean complete = true;
+ boolean complete = false;
try {
+ socketWrapper.flush(false);
// If this is false there will be a call back when it is true
- while (sos.isReady()) {
+ while (socketWrapper.isReadyForWrite()) {
complete = true;
for (ByteBuffer buffer : buffers) {
if (buffer.hasRemaining()) {
complete = false;
- sos.write(buffer.array(), buffer.arrayOffset(),
- buffer.limit());
- buffer.position(buffer.limit());
+ socketWrapper.write(false, buffer);
break;
}
}
if (complete) {
- wsWriteTimeout.unregister(this);
- clearHandler(null, useDispatch);
+ socketWrapper.flush(false);
+ complete = socketWrapper.isReadyForWrite();
+ if (complete) {
+ wsWriteTimeout.unregister(this);
+ clearHandler(null, useDispatch);
+ if (close) {
+ close();
+ }
+ }
break;
}
}
-
- } catch (IOException ioe) {
+ } catch (IOException | IllegalStateException e) {
wsWriteTimeout.unregister(this);
- clearHandler(ioe, useDispatch);
+ clearHandler(e, useDispatch);
close();
}
+
if (!complete) {
// Async write is in progress
-
long timeout = getSendTimeout();
if (timeout > 0) {
// Register with timeout thread
@@ -136,7 +164,7 @@ public class WsRemoteEndpointImplServer extends WsRemoteEndpointImplBase {
clearHandler(new EOFException(), true);
}
try {
- sos.close();
+ socketWrapper.close();
} catch (IOException e) {
if (log.isInfoEnabled()) {
log.info(sm.getString("wsRemoteEndpointServer.closeFailed"), e);
@@ -193,12 +221,12 @@ public class WsRemoteEndpointImplServer extends WsRemoteEndpointImplBase {
buffers = null;
if (sh != null) {
if (useDispatch) {
- OnResultRunnable r = onResultRunnables.poll();
- if (r == null) {
- r = new OnResultRunnable(onResultRunnables);
- }
- r.init(sh, t);
- if (executorService == null || executorService.isShutdown()) {
+ OnResultRunnable r = new OnResultRunnable(sh, t);
+ AbstractEndpoint<?> endpoint = socketWrapper.getEndpoint();
+ Executor containerExecutor = endpoint.getExecutor();
+ if (endpoint.isRunning() && containerExecutor != null) {
+ containerExecutor.execute(r);
+ } else {
// Can't use the executor so call the runnable directly.
// This may not be strictly specification compliant in all
// cases but during shutdown only close messages are going
@@ -207,8 +235,6 @@ public class WsRemoteEndpointImplServer extends WsRemoteEndpointImplBase {
// 55715. The issues with nested calls was the reason for
// the separate thread requirement in the specification.
r.run();
- } else {
- executorService.execute(r);
}
} else {
if (t == null) {
@@ -223,16 +249,10 @@ public class WsRemoteEndpointImplServer extends WsRemoteEndpointImplBase {
private static class OnResultRunnable implements Runnable {
- private final Queue<OnResultRunnable> queue;
-
- private volatile SendHandler sh;
- private volatile Throwable t;
-
- private OnResultRunnable(Queue<OnResultRunnable> queue) {
- this.queue = queue;
- }
+ private final SendHandler sh;
+ private final Throwable t;
- private void init(SendHandler sh, Throwable t) {
+ private OnResultRunnable(SendHandler sh, Throwable t) {
this.sh = sh;
this.t = t;
}
@@ -244,12 +264,6 @@ public class WsRemoteEndpointImplServer extends WsRemoteEndpointImplBase {
} else {
sh.onResult(new SendResult(t));
}
- t = null;
- sh = null;
- // Return the Runnable to the queue when it has been finished with
- // Note if this method takes an age to finish there shouldn't be any
- // thread safety issues as the fields are cleared above.
- queue.add(this);
}
}
}
diff --git a/java/org/apache/tomcat/websocket/server/WsServerContainer.java b/java/org/apache/tomcat/websocket/server/WsServerContainer.java
index 1cae0cf..fde8b9f 100644
--- a/java/org/apache/tomcat/websocket/server/WsServerContainer.java
+++ b/java/org/apache/tomcat/websocket/server/WsServerContainer.java
@@ -27,12 +27,6 @@ import java.util.SortedSet;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.SynchronousQueue;
-import java.util.concurrent.ThreadFactory;
-import java.util.concurrent.ThreadPoolExecutor;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicLong;
import javax.servlet.DispatcherType;
import javax.servlet.FilterRegistration;
@@ -50,13 +44,10 @@ import javax.websocket.server.ServerEndpoint;
import javax.websocket.server.ServerEndpointConfig;
import javax.websocket.server.ServerEndpointConfig.Configurator;
-import org.apache.juli.logging.Log;
-import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.InstanceManager;
import org.apache.tomcat.util.res.StringManager;
import org.apache.tomcat.websocket.WsSession;
import org.apache.tomcat.websocket.WsWebSocketContainer;
-import org.apache.tomcat.websocket.pojo.PojoEndpointServer;
import org.apache.tomcat.websocket.pojo.PojoMethodMapping;
/**
@@ -72,9 +63,7 @@ import org.apache.tomcat.websocket.pojo.PojoMethodMapping;
public class WsServerContainer extends WsWebSocketContainer
implements ServerContainer {
- private static final StringManager sm =
- StringManager.getManager(Constants.PACKAGE_NAME);
- private static final Log log = LogFactory.getLog(WsServerContainer.class);
+ private static final StringManager sm = StringManager.getManager(WsServerContainer.class);
private static final CloseReason AUTHENTICATED_HTTP_SESSION_CLOSED =
new CloseReason(CloseCodes.VIOLATED_POLICY,
@@ -93,8 +82,6 @@ public class WsServerContainer extends WsWebSocketContainer
private volatile boolean addAllowed = true;
private final ConcurrentMap<String,Set<WsSession>> authenticatedSessions =
new ConcurrentHashMap<>();
- private final ExecutorService executorService;
- private final ThreadGroup threadGroup;
private volatile boolean endpointsRegistered = false;
WsServerContainer(ServletContext servletContext) {
@@ -120,19 +107,6 @@ public class WsServerContainer extends WsWebSocketContainer
if (value != null) {
setEnforceNoAddAfterHandshake(Boolean.parseBoolean(value));
}
- // Executor config
- int executorCoreSize = 0;
- long executorKeepAliveTimeSeconds = 60;
- value = servletContext.getInitParameter(
- Constants.EXECUTOR_CORE_SIZE_INIT_PARAM);
- if (value != null) {
- executorCoreSize = Integer.parseInt(value);
- }
- value = servletContext.getInitParameter(
- Constants.EXECUTOR_KEEPALIVETIME_SECONDS_INIT_PARAM);
- if (value != null) {
- executorKeepAliveTimeSeconds = Long.parseLong(value);
- }
FilterRegistration.Dynamic fr = servletContext.addFilter(
"Tomcat WebSocket (JSR356) Filter", new WsFilter());
@@ -142,24 +116,6 @@ public class WsServerContainer extends WsWebSocketContainer
DispatcherType.FORWARD);
fr.addMappingForUrlPatterns(types, true, "/*");
-
- // Use a per web application executor for any threads that the WebSocket
- // server code needs to create. Group all of the threads under a single
- // ThreadGroup.
- StringBuffer threadGroupName = new StringBuffer("WebSocketServer-");
- threadGroupName.append(servletContext.getVirtualServerName());
- threadGroupName.append('-');
- if ("".equals(servletContext.getContextPath())) {
- threadGroupName.append("ROOT");
- } else {
- threadGroupName.append(servletContext.getContextPath());
- }
- threadGroup = new ThreadGroup(threadGroupName.toString());
- WsThreadFactory wsThreadFactory = new WsThreadFactory(threadGroup);
-
- executorService = new ThreadPoolExecutor(executorCoreSize,
- Integer.MAX_VALUE, executorKeepAliveTimeSeconds, TimeUnit.SECONDS,
- new SynchronousQueue<Runnable>(), wsThreadFactory);
}
@@ -169,7 +125,7 @@ public class WsServerContainer extends WsWebSocketContainer
* must be called before calling this method.
*
* @param sec The configuration to use when creating endpoint instances
- * @throws DeploymentException if the endpoint can not be published as
+ * @throws DeploymentException if the endpoint cannot be published as
* requested
*/
@Override
@@ -192,8 +148,7 @@ public class WsServerContainer extends WsWebSocketContainer
sec.getDecoders(), path);
if (methodMapping.getOnClose() != null || methodMapping.getOnOpen() != null
|| methodMapping.getOnError() != null || methodMapping.hasMessageHandlers()) {
- sec.getUserProperties().put(
- PojoEndpointServer.POJO_METHOD_MAPPING_KEY,
+ sec.getUserProperties().put(org.apache.tomcat.websocket.pojo.Constants.POJO_METHOD_MAPPING_KEY,
methodMapping);
}
@@ -280,50 +235,6 @@ public class WsServerContainer extends WsWebSocketContainer
}
- @Override
- public void destroy() {
- shutdownExecutor();
- super.destroy();
- // If the executor hasn't fully shutdown it won't be possible to
- // destroy this thread group as there will still be threads running.
- // Mark the thread group as daemon one, so that it destroys itself
- // when thread count reaches zero.
- // Synchronization on threadGroup is needed, as there is a race between
- // destroy() call from termination of the last thread in thread group
- // marked as daemon versus the explicit destroy() call.
- int threadCount = threadGroup.activeCount();
- boolean success = false;
- try {
- while (true) {
- int oldThreadCount = threadCount;
- synchronized (threadGroup) {
- if (threadCount > 0) {
- Thread.yield();
- threadCount = threadGroup.activeCount();
- }
- if (threadCount > 0 && threadCount != oldThreadCount) {
- // Value not stabilized. Retry.
- continue;
- }
- if (threadCount > 0) {
- threadGroup.setDaemon(true);
- } else {
- threadGroup.destroy();
- success = true;
- }
- break;
- }
- }
- } catch (IllegalThreadStateException exception) {
- // Fall-through
- }
- if (!success) {
- log.warn(sm.getString("serverContainer.threadGroupNotDestroyed",
- threadGroup.getName(), Integer.valueOf(threadCount)));
- }
- }
-
-
boolean areEndpointsRegistered() {
return endpointsRegistered;
}
@@ -498,23 +409,6 @@ public class WsServerContainer extends WsWebSocketContainer
}
- ExecutorService getExecutorService() {
- return executorService;
- }
-
-
- private void shutdownExecutor() {
- if (executorService == null) {
- return;
- }
- executorService.shutdown();
- try {
- executorService.awaitTermination(10, TimeUnit.SECONDS);
- } catch (InterruptedException e) {
- // Ignore the interruption and carry on
- }
- }
-
private static void validateEncoders(Class<? extends Encoder>[] encoders)
throws DeploymentException {
@@ -579,22 +473,4 @@ public class WsServerContainer extends WsWebSocketContainer
tpm2.getUriTemplate().getNormalizedPath());
}
}
-
-
- private static class WsThreadFactory implements ThreadFactory {
-
- private final ThreadGroup tg;
- private final AtomicLong count = new AtomicLong(0);
-
- private WsThreadFactory(ThreadGroup tg) {
- this.tg = tg;
- }
-
- @Override
- public Thread newThread(Runnable r) {
- Thread t = new Thread(tg, r);
- t.setName(tg.getName() + "-" + count.incrementAndGet());
- return t;
- }
- }
}
diff --git a/modules/bayeux/.classpath b/modules/bayeux/.classpath
deleted file mode 100644
index 889b127..0000000
--- a/modules/bayeux/.classpath
+++ /dev/null
@@ -1,9 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<classpath>
- <classpathentry kind="src" path="java"/>
- <classpathentry kind="src" path="test"/>
- <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
- <classpathentry combineaccessrules="false" kind="src" path="/tomcat-trunk"/>
- <classpathentry kind="var" path="TOMCAT_LIBS_BASE/json-20080701/json.jar"/>
- <classpathentry kind="output" path="bin"/>
-</classpath>
diff --git a/modules/bayeux/.project b/modules/bayeux/.project
deleted file mode 100644
index 872599e..0000000
--- a/modules/bayeux/.project
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<projectDescription>
- <name>tomcat-bayeux</name>
- <comment></comment>
- <projects>
- </projects>
- <buildSpec>
- <buildCommand>
- <name>org.eclipse.jdt.core.javabuilder</name>
- <arguments>
- </arguments>
- </buildCommand>
- </buildSpec>
- <natures>
- <nature>org.eclipse.jdt.core.javanature</nature>
- </natures>
-</projectDescription>
diff --git a/modules/bayeux/build.xml b/modules/bayeux/build.xml
deleted file mode 100644
index d30ba7f..0000000
--- a/modules/bayeux/build.xml
+++ /dev/null
@@ -1,228 +0,0 @@
-<?xml version="1.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.
--->
-<project name="Tomcat 6.0" default="bayeux" basedir="../..">
-
-
- <!-- ===================== Initialize Property Values =================== -->
-
- <!-- See "build.properties.sample" in the top level directory for all -->
- <!-- property values you must customize for successful building!!! -->
- <property file="${user.home}/build.properties"/>
- <property file="${basedir}/build.properties"/>
- <property file="${basedir}/build.properties.default"/>
-
- <!-- Project Properties -->
- <property name="project" value="apache-tomcat" />
- <property name="name" value="Apache Tomcat" />
- <property name="year" value="2008" />
- <property name="version.major" value="6" />
- <property name="version.minor" value="0" />
- <property name="version.build" value="0" />
- <property name="version.patch" value="0" />
- <property name="version.suffix" value="-dev" />
-
- <property name="version" value="${version.major}.${version.minor}.${version.build}${version.suffix}" />
- <property name="version.number" value="${version.major}.${version.minor}.${version.build}.${version.patch}" />
- <property name="version.major.minor" value="${version.major}.${version.minor}" />
-
- <property name="final.name" value="${project}-${version}" />
- <property name="final-src.name" value="${project}-${version}-src" />
-
- <!-- Build Defaults -->
- <property name="tomcat.build" value="${basedir}/output/build"/>
- <property name="tomcat.classes" value="${basedir}/output/classes"/>
- <property name="tomcat.dist" value="${basedir}/output/dist"/>
- <property name="tomcat.extras" value="${basedir}/output/extras"/>
- <property name="tomcat.deployer" value="${basedir}/output/deployer"/>
- <property name="tomcat.release" value="${basedir}/output/release"/>
- <property name="test.failonerror" value="true"/>
- <property name="test.runner" value="junit.textui.TestRunner"/>
-
- <!-- Can't be lower - jsp uses templates -->
- <property name="compile.source" value="1.5"/>
-
- <!-- JAR artifacts -->
- <property name="cometd-api.jar" value="${tomcat.extras}/cometd-api.jar"/>
- <property name="tomcat-bayeux.jar" value="${tomcat.extras}/tomcat-bayeux.jar"/>
- <property name="cometd.war" value="${tomcat.extras}/cometd.war"/>
- <property name="tomcat-bayeux-samples.jar" value="${tomcat.extras}/tomcat-bayeux-samples.jar"/>
-
- <!-- Classpath -->
- <path id="tomcat.classpath">
- <pathelement location="${tomcat.classes}"/>
- </path>
-
- <target name="prepare">
- <mkdir dir="${tomcat.extras}"/>
- </target>
-
- <target name="clean">
- <delete dir="${tomcat.extras}"/>
- </target>
-
-
- <target name="bayeux">
- <mkdir dir="${tomcat.extras}"/>
-
- <antcall target="downloadfile">
- <param name="sourcefile" value="${json-lib.lib}"/>
- <param name="destfile" value="${json-lib.home}/${json-lib.jar}"/>
- <param name="destdir" value="${json-lib.home}"/>
- </antcall>
-
- <antcall target="downloadgz">
- <param name="sourcefile" value="${dojo-js.loc}"/>
- <param name="destfile" value="${dojo-js.jar}"/>
- </antcall>
-
- <copy todir="${tomcat.extras}" file="${json-lib.home}/${json-lib.jar}"/>
- <!-- Classpath -->
- <path id="tomcat.bayeux.classpath">
- <pathelement path="${tomcat.classpath}"/>
- <pathelement path="${json-lib.home}/${json-lib.jar}"/>
- </path>
-
- <!-- compile org.apache.tomcat.bayeux -->
- <!-- compile org.apache.cometd -->
- <javac srcdir="modules/bayeux/java" destdir="${tomcat.classes}"
- debug="${compile.debug}"
- deprecation="${compile.deprecation}"
- source="${compile.source}"
- encoding="ISO-8859-1">
- <classpath refid="tomcat.bayeux.classpath" />
- <include name="org/apache/tomcat/bayeux/**" />
- <include name="org/apache/cometd/**" />
- </javac>
-
- <!-- Cometd API JAR File -->
- <jar jarfile="${cometd-api.jar}">
- <fileset dir="${tomcat.classes}">
- <exclude name="**/package.html" />
- <exclude name="**/LocalStrings_*" />
- <include name="org/apache/cometd/**" />
- </fileset>
- </jar>
- <!-- Cometd API JAR File -->
- <jar jarfile="${tomcat-bayeux.jar}">
- <fileset dir="${tomcat.classes}">
- <exclude name="**/package.html" />
- <exclude name="**/LocalStrings_*" />
- <include name="org/apache/tomcat/bayeux/**" />
- </fileset>
- </jar>
-
- <!-- cometd samples application -->
- <javac srcdir="modules/bayeux/test" destdir="${tomcat.classes}"
- debug="${compile.debug}"
- deprecation="${compile.deprecation}"
- source="${compile.source}"
- encoding="ISO-8859-1">
- <classpath refid="tomcat.bayeux.classpath" />
- <include name="org/apache/tomcat/bayeux/**" />
- <include name="org/apache/cometd/**" />
- </javac>
-
- <!-- Cometd samples JAR File -->
- <jar jarfile="${tomcat-bayeux-samples.jar}">
- <fileset dir="${tomcat.classes}">
- <exclude name="**/package.html" />
- <exclude name="**/LocalStrings_*" />
- <include name="org/apache/cometd/bayeux/samples/**" />
- </fileset>
- </jar>
-
- <!-- build samples webapplication /cometd -->
- <property name="cometd-app" value="${base.path}/cometd"/>
- <mkdir dir="${cometd-app}"/>
-
- <copy todir="${cometd-app}">
- <fileset dir="${basedir}/modules/bayeux/webapps/cometd">
- <include name="**/**"/>
- </fileset>
- <fileset dir="${dojo-js.home}">
- <include name="dojo/**"/>
- <include name="dojox/**"/>
- </fileset>
- </copy>
- <mkdir dir="${cometd-app}/WEB-INF/lib"/>
- <copy todir="${cometd-app}/WEB-INF/lib" file="${tomcat-bayeux-samples.jar}"/>
-
- <zip zipfile="${cometd.war}">
- <fileset dir="${cometd-app}">
- <include name="**/**"/>
- </fileset>
- </zip>
-
- <delete dir="${cometd-app}"/>
-
- <!-- create checksums -->
- <checksum file="${cometd-api.jar}" forceOverwrite="yes" fileext=".md5" />
- <checksum file="${tomcat-bayeux.jar}" forceOverwrite="yes" fileext=".md5" />
- <checksum file="${cometd.war}" forceOverwrite="yes" fileext=".md5" />
- <checksum file="${tomcat.extras}/${json-lib.jar}" forceOverwrite="yes" fileext=".md5" />
-
- <!-- print out how to -->
- <echo>You've built the Tomcat Bayeux libraries, simply add the following libraries to your CATALINA_HOME/lib directory:
- ${cometd-api.jar}
- ${tomcat-bayeux.jar}
- ${tomcat.extras}/${json-lib.jar}
-To run the sample application, copy the following applications into your CATALINA_BASE/webapps directory
- ${cometd.war}
- </echo>
- </target>
-
-
-
- <!-- Download and dependency building -->
- <target name="proxyflags">
- <!-- check proxy parameters. -->
- <condition property="useproxy">
- <equals arg1="${proxy.use}" arg2="on" />
- </condition>
- </target>
-
- <target name="setproxy" depends="proxyflags" if="useproxy">
- <taskdef name="setproxy"
- classname="org.apache.tools.ant.taskdefs.optional.net.SetProxy" />
- <setproxy proxyhost="${proxy.host}" proxyport="${proxy.port}"
- proxyuser="${proxy.user}" proxypassword="${proxy.password}" />
- <echo message="Using ${proxy.host}:${proxy.port} to download ${sourcefile}"/>
- </target>
-
- <target name="testexist">
- <echo message="Testing for ${destfile}"/>
- <available file="${destfile}" property="exist"/>
- </target>
-
- <target name="downloadfile" unless="exist" depends="setproxy,testexist">
- <!-- Download extract the file -->
- <mkdir dir="${destdir}" />
- <get src="${sourcefile}" dest="${destfile}" />
- </target>
-
- <target name="downloadgz" unless="exist" depends="setproxy,testexist">
- <!-- Download and extract the package -->
- <get src="${sourcefile}" dest="${base.path}/file.tar.gz" />
- <gunzip src="${base.path}/file.tar.gz" dest="${base.path}/file.tar"/>
- <untar src="${base.path}/file.tar" dest="${base.path}"/>
- <delete file="${base.path}/file.tar"/>
- <delete file="${base.path}/file.tar.gz"/>
- </target>
-
-
-</project>
diff --git a/modules/bayeux/java/org/apache/cometd/bayeux/Bayeux.java b/modules/bayeux/java/org/apache/cometd/bayeux/Bayeux.java
deleted file mode 100644
index 6c54fa6..0000000
--- a/modules/bayeux/java/org/apache/cometd/bayeux/Bayeux.java
+++ /dev/null
@@ -1,241 +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.cometd.bayeux;
-
-import java.util.List;
-
-/** Bayeux Interface.<br>
- * This interface represents the server side API for the Bayeux messaging protocol.
- * Bayeux is a simple subscribe/publish/receive methodology, not far from JMS, but much simplified.<br>
- * It is used both by the actual implementation and by server side clients.<br>
- * Server side clients use this to create, retrieve and subscribe to channels.
- * Server side clients are represented, just like remote clients, through the Client interface.
- * <br>
- * The Bayeux implementations is intended to be thread safe and multiple threads may simultaneously call Bayeux methods.
- * <br>
- * The Bayeux object, is the starting point for any cometd application relying on the Bayeux object.
- * Dependent on the container, the Bayeux object will be stored in the <code>javax.servlet.ServletContext</code> object
- * as an attribute under the name <code>Bayeux.DOJOX_COMETD_BAYEUX</code><br>
- * To retrieve this object, one would simply call<br>
- * <code>Bayeux bx = (Bayeux)getServletContext().getAttribute(Bayeux.DOJOX_COMETD_BAYEUX);
- * <br><br>
- * The Bayeux protocol is pretty straight forward and includes a bunch of messaging that is not needed to be known to clients,
- * both server side and remote clients.
- * This object gets initialized by a container dependent servlet, and the servlet then handles all Bayeux communication from the client.
- * Remote messsages are delivered to channels, and to server side clients using the <code>Listener</code> interface.<br>
- * <br>
- * A <code>Bayeux session</code> is active as long as the webapp hosting the Bayeux object is active.<br>
- * When the webapplication shuts down, the Bayeux object will unsubscribe all clients and remove all the active channels.
- *
- * @author Greg Wilkins
- */
-public interface Bayeux {
-
- /**Meta definitions for channels*/
- public static final String META="/meta";
- /**Meta definitions for channels*/
- public static final String META_SLASH="/meta/";
- /**Meta definitions for channels - connect message*/
- public static final String META_CONNECT="/meta/connect";
- /**Meta definitions for channels - client messsage*/
- public static final String META_CLIENT="/meta/client";
- /**Meta definitions for channels - disconnect messsage*/
- public static final String META_DISCONNECT="/meta/disconnect";
- /**Meta definitions for channels - handshake messsage*/
- public static final String META_HANDSHAKE="/meta/handshake";
- /**Meta definitions for channels - ping messsage*/
- public static final String META_PING="/meta/ping";
- /**Meta definitions for channels - reconnect messsage
- * @deprecated
- */
- public static final String META_RECONNECT="/meta/reconnect";
- /**Meta definitions for channels - status messsage*/
- public static final String META_STATUS="/meta/status";
- /**Meta definitions for channels - subscribe messsage*/
- public static final String META_SUBSCRIBE="/meta/subscribe";
- /**Meta definitions for channels - unsubscribe messsage*/
- public static final String META_UNSUBSCRIBE="/meta/unsubscribe";
- /*Field names inside Bayeux messages*/
- /**Field names inside Bayeux messages - clientId field*/
- public static final String CLIENT_FIELD="clientId";
- /**Field names inside Bayeux messages - data field*/
- public static final String DATA_FIELD="data";
- /**Field names inside Bayeux messages - channel field*/
- public static final String CHANNEL_FIELD="channel";
- /**Field names inside Bayeux messages - id field*/
- public static final String ID_FIELD="id";
- /**Field names inside Bayeux messages - error field*/
- public static final String ERROR_FIELD="error";
- /**Field names inside Bayeux messages - timestamp field*/
- public static final String TIMESTAMP_FIELD="timestamp";
- /**Field names inside Bayeux messages - transport field*/
- public static final String TRANSPORT_FIELD="transport";
- /**Field names inside Bayeux messages - advice field*/
- public static final String ADVICE_FIELD="advice";
- /**Field names inside Bayeux messages - successful field*/
- public static final String SUCCESSFUL_FIELD="successful";
- /**Field names inside Bayeux messages - subscription field*/
- public static final String SUBSCRIPTION_FIELD="subscription";
- /**Field names inside Bayeux messages - ext field*/
- public static final String EXT_FIELD="ext";
- /**Field names inside Bayeux messages - connectionType field*/
- public static final String CONNECTION_TYPE_FIELD="connectionType";
- /**Field names inside Bayeux messages - version field*/
- public static final String VERSION_FIELD="version";
- /**Field names inside Bayeux messages - minimumVersion field*/
- public static final String MIN_VERSION_FIELD="minimumVersion";
- /**Field names inside Bayeux messages - supportedConnectionTypes field*/
- public static final String SUPP_CONNECTION_TYPE_FIELD="supportedConnectionTypes";
- /**Field names inside Bayeux messages - json-comment-filtered field*/
- public static final String JSON_COMMENT_FILTERED_FIELD="json-comment-filtered";
- /**Field names inside Bayeux messages - reconnect field*/
- public static final String RECONNECT_FIELD = "reconnect";
- /**Field names inside Bayeux messages - interval field*/
- public static final String INTERVAL_FIELD = "interval";
- /**Field values inside Bayeux messages - retry response*/
- public static final String RETRY_RESPONSE = "retry";
- /**Field values inside Bayeux messages - handshake response*/
- public static final String HANDSHAKE_RESPONSE = "handshake";
- /**Field values inside Bayeux messages - none response*/
- public static final String NONE_RESPONSE = "none";
- /**Service channel names-starts with*/
- public static final String SERVICE="/service";
- /**Service channel names-trailing slash*/
- public static final String SERVICE_SLASH="/service/";
- /*Transport types*/
- /**Transport types - long polling*/
- public static final String TRANSPORT_LONG_POLL="long-polling";
- /**Transport types - callback polling*/
- public static final String TRANSPORT_CALLBACK_POLL="callback-polling";
- /**Transport types - iframe*/
- public static final String TRANSPORT_IFRAME="iframe";
- /**Transport types - flash*/
- public static final String TRANSPORT_FLASH="flash";
- /** ServletContext attribute name used to obtain the Bayeux object */
- public static final String DOJOX_COMETD_BAYEUX="dojox.cometd.bayeux";
- /*http field names*/
- /**http helpers - text/json content type*/
- public static final String JSON_CONTENT_TYPE="text/json";
- /**http helpers - parameter name for json message*/
- public static final String MESSAGE_PARAMETER="message";
- /**http helpers - name of the jsonp parameter*/
- public static final String JSONP_PARAMETER="jsonp";
- /**http helpers - default name of the jsonp callback function*/
- public static final String JSONP_DEFAULT_NAME="jsonpcallback";
-
- /*--Client----------------------------------------------------------- */
- /**
- * Creates a new server side client. This method is to be invoked
- * by server side objects only. You cannot create a remote client by using this method.
- * A client represents an entity that can subscribe to channels and publish and receive messages
- * through these channels
- * @param idprefix String - the prefix string for the id generated, can be null
- * @param listener Listener - a callback object to be called when messages are to be delivered to the new client
- * @return Client - returns an implementation of the client interface.
- */
- public Client newClient(String idprefix, Listener listener);
-
- /**
- * retrieve a client based on an ID. Will return null if the client doesn't exist.
- * @param clientid String
- * @return Client-null if the client doesn't exist.returns the client if it does.
- */
- public Client getClient(String clientid);
-
- /**
- * Returns a non modifiable list of all the clients that are currently active
- * in this Bayeux session
- * @return List<Client> - a list containing all clients. The List can not be modified.
- */
- public List<Client> getClients();
-
- /**
- * Returns true if a client with the given id exists.<br>
- * Same as executing <code>getClient(id)!=null</code>.
- * @param clientId String
- * @return boolean - true if the client exists
- */
- public boolean hasClient(String clientId);
-
- /**
- * Removes the client all together.
- * This will unsubscribe the client to any channels it may be subscribed to
- * and remove it from the list.
- * @param client Client
- * @return Client - returns the client that was removed, or null if no client was removed.
- */
- public Client remove(Client client);
-
-
- /*--Channel---------------------------------------------------------- */
- /**
- * Returns the channel for a given channel id.
- * If the channel doesn't exist, and the <code>create</code> parameter is set to true,
- * the channel will be created and added to the list of active channels.<br>
- * if <code>create</code> is set to false, and the channel doesn't exist, null will be returned.
- * @param channelId String - the id of the channel to be retrieved or created
- * @param create boolean - true if the Bayeux impl should create the channel
- * @return Channel - null if <code>create</code> is set to false and the channel doesn't exist,
- * otherwise it returns a channel object.
- */
- public Channel getChannel(String channelId, boolean create);
-
- /**
- * Returns a list of currently active channels in this Bayeux session.
- * @return List<Channel>
- */
- public List<Channel> getChannels();
-
- /**
- * Removes a channel from the Bayeux object.
- * This will also unsubscribe all the clients currently subscribed to the
- * the channel.
- * @param channel Channel - the channel to be removed
- * @return Channel - returns the channel that was removed, or null if no channel was removed.
- */
- public Channel remove(Channel channel);
-
- /**
- * returns true if a channel with the given channelId exists.
- * <br>Same as executing <code>Bayeux.getChannel(channelId,false)!=null</code>
- * @param channelId String
- * @return boolean - true if the channel exists.
- */
- public boolean hasChannel(String channelId);
-
- /* --Message---------------------------------------------------------- */
- /**
- * Creates a new message to be sent by a server side client.
- * @return Message - returns a new Message object, that has a unique id.
- */
- public Message newMessage(Client from);
-
-
- /*--Security policy----------------------------------------------------------- */
- /**
- * Returns the security policy associated with this Bayeux session
- * @return SecurityPolicy
- */
- public SecurityPolicy getSecurityPolicy();
-
- /**
- * Sets the security policy to be used in this Bayeux session
- * @param securityPolicy SecurityPolicy
- */
- public void setSecurityPolicy(SecurityPolicy securityPolicy);
-
-}
\ No newline at end of file
diff --git a/modules/bayeux/java/org/apache/cometd/bayeux/Channel.java b/modules/bayeux/java/org/apache/cometd/bayeux/Channel.java
deleted file mode 100644
index e5e45f9..0000000
--- a/modules/bayeux/java/org/apache/cometd/bayeux/Channel.java
+++ /dev/null
@@ -1,102 +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.cometd.bayeux;
-
-import java.util.List;
-
-/**
- * A Bayeux Channel represents a channel used to receive messages from and to publish messages to.
- * In order to publish messages to or receive messages from, one must subscribe to the channel.
- * This is easily done by invoking the <code>subscribe</code> method.
- * A channel is created by calling the <code>Bayeux.getChannel(channelId,true)</code> method.
- * A channel can be created either server side by invoking the getChannel, or client side
- * by using the /meta/subscribe message without a wildcard.
- * @author Greg Wilkins
- */
-public interface Channel
-{
- /**
- * Returns the id for this channel. The id is unique within bayeux session.
- * @return String - will never be null.
- */
- public String getId();
-
- /**
- * Publishes a message to all the subscribers of this channel.
- * The <code>from</code> is contained within the message, by calling
- * <code>msg.getClient()</code>
- * @param data - the message to be published, can not be null.
- */
- public void publish(Message msg);
-
- /**
- * Publishes more than one message to all the subscribers of this channel.
- * The <code>from</code> is contained within the message, by calling
- * <code>msg[x].getClient()</code>
- * @param data - the message to be published, can not be null.
- */
- public void publish(Message[] msgs);
-
- /**
- * Non persistent channels are removed when the last subscription is
- * removed. Persistent channels survive periods without any subscribers.
- * @return true if the Channel will persist without any subscription.
- */
- public boolean isPersistent();
-
- /**
- * @param persistent true if the Channel will persist without any subscription.
- * @see isPersistent
- */
- public void setPersistent(boolean persistent);
-
- /**
- * Subscribes a client to a channel.
- * @param subscriber - the client to be subscribed. If the client
- * already is subscribed, this call will not create a duplicate subscription.
- */
- public void subscribe(Client subscriber);
-
- /**
- * Unsubscribes a client from a channel
- * @param subscriber - the client to be subscribed.
- * @return - returns the client that was unsubscribed, or null if the client wasn't subscribed.
- */
- public Client unsubscribe(Client subscriber);
-
- /**
- * returns a non modifiable list of all the subscribers to this
- * channel.
- * @return a list of subscribers
- */
- public List<Client> getSubscribers();
-
- /**
- * Adds a data filter to this channel. All messages received by this channel
- * will run through this filter.
- * @param filter Filter
- */
- public void addFilter(DataFilter filter);
-
- /**
- * Removes a filter from this channel.
- * returns the filter that was removed, or null if the filter wasn't in the channel.
- * @param filter Filter
- * @return Filter - null if no filter was removed otherwise it returns the filter that was removed.
- */
- public DataFilter removeFilter(DataFilter filter);
-}
\ No newline at end of file
diff --git a/modules/bayeux/java/org/apache/cometd/bayeux/Client.java b/modules/bayeux/java/org/apache/cometd/bayeux/Client.java
deleted file mode 100644
index 36da967..0000000
--- a/modules/bayeux/java/org/apache/cometd/bayeux/Client.java
+++ /dev/null
@@ -1,90 +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.cometd.bayeux;
-
-
-
-/** A Bayeux Client.
- * <p>
- * A client may subscribe to channels and publish messages to channels.
- * Client instances should not be directly created by uses, but should
- * be obtained via the {@link Bayeux#getClient(String)} or {@link Bayeux#newClient(String, Listener)}
- * methods.
- * </p>
- * <p>
- * Three types of client may be represented by this interface:<nl>
- * <li>The server representation of a remote client connected via HTTP,
- * automatically created by the Bayeux server when a connect message comes in</li>
- * <li>A server side client, created by the application using the {@link Bayeux#newClient(String, Listener)} method</li>
- * <li>A java client connected to a remote Bayeux server - not implemented</li>
- * </nl>
- * @author Greg Wilkins
- */
-public interface Client
-{
- /**
- * Returns a unique id for this client. The id is unique within this Bayeux session.
- * @return String - will not be null
- */
- public String getId();
-
- /**
- * Returns true if this client is holding messages to be delivered to the remote client.
- * This method always returns false for local clients, since messages are delivered instantly using the
- * Listener(callback) object
- * @return boolean
- */
- public boolean hasMessages();
-
- /**
- * Deliver a message to this client only
- * Deliver a message directly to the client. The message is not
- * filtered or published to a channel.
- * @param message
- */
- public void deliver(Message message);
-
- /**
- * Deliver a batch of messages to this client only
- * Deliver a batch messages directly to the client. The messages are not
- * filtered or published to a channel.
- * @param message
- */
- public void deliver(Message[] message);
-
- /**
- * @return True if the client is local. False if this client is either a remote HTTP client or
- * a java client to a remote server.
- */
- public boolean isLocal();
-
- /**
- * Starts a batch, no messages will be delivered until endBatch is called.
- * Batches can be nested, and messages will only be delivered after
- * the last endBatch has been called.
- */
- public void startBatch();
-
- /**
- * Ends a batch. since batches can be nested, messages will only be delivered
- * after the endBatch has been called as many times as startBatch has.
- */
- public void endBatch();
-
-
-}
\ No newline at end of file
diff --git a/modules/bayeux/java/org/apache/cometd/bayeux/DataFilter.java b/modules/bayeux/java/org/apache/cometd/bayeux/DataFilter.java
deleted file mode 100644
index 8111a9c..0000000
--- a/modules/bayeux/java/org/apache/cometd/bayeux/DataFilter.java
+++ /dev/null
@@ -1,37 +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.cometd.bayeux;
-
-/**
- * Data Filter<br>
- * Data filters are used to transform data as it is sent to a Channel.
- * Messages are filtered as the message is published to a channel, invoking the
- * {@link Channel#publish(Message)} method.<br>
- * This method gets invoked in two different scenarios, the first being when a message is received from
- * a remote client, and the Bayeux implementation invokes the publish method directly.
- * The second scenario is when a local client invokes {@link Channel#publish(Message)} directly in the local JVM.
- * @author Greg Wilkins
- *
- */
-public interface DataFilter
-{
- /**
- * Runs a message through the filter. Filtering can only modify an existing object, it can not replace it.
- * @param data Message - the message to be filtered, may not be null
- */
- public void filter(Message data);
-}
diff --git a/modules/bayeux/java/org/apache/cometd/bayeux/Listener.java b/modules/bayeux/java/org/apache/cometd/bayeux/Listener.java
deleted file mode 100644
index 089a8bd..0000000
--- a/modules/bayeux/java/org/apache/cometd/bayeux/Listener.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.cometd.bayeux;
-
-/**
- * Cometd Listener interface.<br>
- * For local clients, in order to receive messages, they pass in a callback object
- * when the local client is created using the {@link Bayeux#newClient(String,Listener)} method.
- * This callback object, implementing the Listener interface, is used to deliver messages to local, in JVM, clients.
- * @author Greg Wilkins
- *
- */
-public interface Listener
-{
- /**
- * This method is called when the client is removed (explicitly or from a timeout)
- * @param timeout - true if the client was removed from a timeout
- * false if it was removed explicitly.
- */
- public void removed(boolean timeout);
-
- /**
- * Invoked when a message is delivered to the client.
- * The message contains the message itself, as well as what channel this message came through
- * and who the sender is. If someone invoked {@link Client#deliver(Message)} then the channel reference will
- * be null.
- * @param msg
- */
- public void deliver(Message[] msg);
-}
diff --git a/modules/bayeux/java/org/apache/cometd/bayeux/Message.java b/modules/bayeux/java/org/apache/cometd/bayeux/Message.java
deleted file mode 100644
index 3544561..0000000
--- a/modules/bayeux/java/org/apache/cometd/bayeux/Message.java
+++ /dev/null
@@ -1,67 +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.cometd.bayeux;
-
-import java.util.Map;
-
-/**
- * A Bayeux Message<br>
- * A Bayeux message is a Map of String/Object key value pairs representing the data in the message.
- * The message contains information about the channel it was published through and who the sender was
- *
- * @author Greg Wilkins
- */
-public interface Message extends Map<String,Object>
-{
- /**
- * Returns a reference to the client that sent this message
- * @return Client - may be null
- */
- public Client getClient();
- /**
- * Returns a reference to the channel that this message was published throuhg
- * @return Channel - may be null
- */
- public Channel getChannel();
- /**
- * Returns the unique id of this message
- * @return String
- */
- public String getId();
-
- /**
- * Sets the time to live in milliseconds. If the message hasn't been delivered
- * when the time passed after the creation time is longer than the TTL the message will
- * expire and removed from any delivery queues.
- * @param ttl long
- */
- public void setTTL(long ttl);
-
- /**
- * Returns the time to live (in milliseconds) for this message
- * @return long
- */
- public long getTTL();
-
- /**
- * returns the timestamp in milliseconds(System.currentTimeMillis()) of when this message was created.
- * @return long
- */
- public long getCreationTime();
-}
-
-
diff --git a/modules/bayeux/java/org/apache/cometd/bayeux/SecurityPolicy.java b/modules/bayeux/java/org/apache/cometd/bayeux/SecurityPolicy.java
deleted file mode 100644
index e7bc38a..0000000
--- a/modules/bayeux/java/org/apache/cometd/bayeux/SecurityPolicy.java
+++ /dev/null
@@ -1,28 +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.cometd.bayeux;
-
-/**
- * @author Greg Wilkins
- */
-public interface SecurityPolicy
-{
- boolean canHandshake(Message message);
- boolean canCreate(Client client,String channel,Message message);
- boolean canSubscribe(Client client,String channel,Message messsage);
- boolean canPublish(Client client,String channel,Message messsage);
-}
diff --git a/modules/bayeux/java/org/apache/tomcat/bayeux/BayeuxException.java b/modules/bayeux/java/org/apache/tomcat/bayeux/BayeuxException.java
deleted file mode 100644
index 8794e01..0000000
--- a/modules/bayeux/java/org/apache/tomcat/bayeux/BayeuxException.java
+++ /dev/null
@@ -1,38 +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.bayeux;
-/**
- *
- * @version 1.0
- */
-public class BayeuxException extends Exception {
- public BayeuxException() {
- super();
- }
-
- public BayeuxException(String message) {
- super(message);
- }
-
- public BayeuxException(String message, Throwable cause) {
- super(message, cause);
- }
-
- public BayeuxException(Throwable cause) {
- super(cause);
- }
-}
\ No newline at end of file
diff --git a/modules/bayeux/java/org/apache/tomcat/bayeux/BayeuxRequest.java b/modules/bayeux/java/org/apache/tomcat/bayeux/BayeuxRequest.java
deleted file mode 100644
index 43e2b4e..0000000
--- a/modules/bayeux/java/org/apache/tomcat/bayeux/BayeuxRequest.java
+++ /dev/null
@@ -1,53 +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.bayeux;
-
-import org.apache.tomcat.bayeux.HttpError;
-
-/**
- * An interface that defines methods for managing Bayeux request meta
- * messages.
- *
- * @author Guy A. Molinari
- * @version 0.9
- */
-public interface BayeuxRequest {
-
- public static final String LAST_REQ_ATTR = "org.apache.cometd.bayeux.last_request";
- public static final String CURRENT_REQ_ATTR = "org.apache.cometd.bayeux.current_request";
- public static final String JSON_MSG_ARRAY = "org.apache.cometd.bayeux.json_msg_array";
-
- /**
- * Validates a specific request.
- * This method must be called prior to process()
- * as a request can do pre processing in the validate method.
- * <br>
- * Should the validation fail, an error object is returned
- * containing an error message, and potentially a stack trace
- * if an exception was generated
- * @return HttpError - null if no error was detected, an HttpError object containing information about the error.
- */
- public HttpError validate();
-
- /**
- * processes a remote client Bayeux message
- * @param prevops - the operation requested by the previous request, in case of chained requests.
- * @return int - returns the interest operation for a CometEvent. Currently not used
- * @throws BayeuxException - if an error was detected, and the appropriate error response couldn't be delivered to the client.
- */
- public int process(int prevops) throws BayeuxException;
-}
diff --git a/modules/bayeux/java/org/apache/tomcat/bayeux/BayeuxServlet.java b/modules/bayeux/java/org/apache/tomcat/bayeux/BayeuxServlet.java
deleted file mode 100644
index 36fd065..0000000
--- a/modules/bayeux/java/org/apache/tomcat/bayeux/BayeuxServlet.java
+++ /dev/null
@@ -1,235 +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.bayeux;
-
-import java.io.IOException;
-import javax.servlet.ServletConfig;
-import javax.servlet.ServletContext;
-import javax.servlet.ServletException;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
-import javax.servlet.http.HttpServletResponse;
-
-import org.apache.catalina.comet.CometEvent;
-import org.apache.catalina.comet.CometProcessor;
-import org.apache.juli.logging.Log;
-import org.apache.juli.logging.LogFactory;
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
-import org.apache.cometd.bayeux.Bayeux;
-
-/**
- *
- * @author Guy Molinari
- * @version 1.0
- */
-public class BayeuxServlet implements CometProcessor {
-
- /**
- * Attribute to hold the TomcatBayeux object in the servlet context
- */
- public static final String TOMCAT_BAYEUX_ATTR = Bayeux.DOJOX_COMETD_BAYEUX;
-
- /**
- * Logger object
- */
- private static final Log log = LogFactory.getLog(BayeuxServlet.class);
-
- /**
- * Servlet config - for future use
- */
- protected ServletConfig servletConfig;
-
- /**
- * Reference to the global TomcatBayeux object
- */
- protected TomcatBayeux tb;
-
- /**
- * Upon servlet destruction, the servlet will clean up the
- * TomcatBayeux object and terminate any outstanding events.
- */
- public void destroy() {
- servletConfig = null;
- //to do, close all outstanding comet events
- //tb.destroy();
- tb = null;//TO DO, close everything down
-
- }
-
- /**
- * Returns the preconfigured connection timeout.
- * If no timeout has been configured as a servlet init parameter named <code>timeout</code>
- * then the default of 2min will be used.
- * @return int - the timeout for a connection in milliseconds
- */
- protected int getTimeout() {
- String timeoutS = servletConfig.getInitParameter("timeout");
- int timeout = 120*1000; //2 min
- try {
- timeout = Integer.parseInt(timeoutS);
- }catch (NumberFormatException nfe) {
- //ignore, we have a default value
- }
- return timeout;
- }
-
- protected int getReconnectInterval() {
- String rs = servletConfig.getInitParameter("reconnectInterval");
- int rct = 1000; //1 seconds
- try {
- rct = Integer.parseInt(rs);
- }catch (NumberFormatException nfe) {
- //ignore, we have a default value
- }
- return rct;
- }
-
-
- public void event(CometEvent cometEvent) throws IOException, ServletException {
- CometEvent.EventType type = cometEvent.getEventType();
- if (log.isDebugEnabled()) {
- log.debug("["+Thread.currentThread().getName()+"] Received Comet Event type="+type+" subtype:"+cometEvent.getEventSubType());
- }
- synchronized (cometEvent) {
- if (type==CometEvent.EventType.BEGIN) {
- //begin event, set the timeout
- cometEvent.setTimeout(getTimeout());
- //checkBayeux(cometEvent); - READ event should always come
- } else if (type==CometEvent.EventType.READ) {
- checkBayeux(cometEvent);
- } else if (type==CometEvent.EventType.ERROR) {
- tb.remove(cometEvent);
- cometEvent.close();
- } else if (type==CometEvent.EventType.END) {
- tb.remove(cometEvent);
- cometEvent.close();
- }//end if
-
- }//synchronized
- }//event
-
- /**
- *
- * @param cometEvent CometEvent
- * @return boolean - true if we comet event stays open
- * @throws IOException
- * @throws UnsupportedOperationException
- */
- protected void checkBayeux(CometEvent cometEvent) throws IOException, UnsupportedOperationException {
- //we actually have data.
- //data can be text/json or
- if (Bayeux.JSON_CONTENT_TYPE.equals(cometEvent.getHttpServletRequest().getContentType())) {
- //read and decode the bytes according to content length
- log.warn("["+Thread.currentThread().getName()+"] JSON encoding not supported, will throw an exception and abort the request.");
- int contentlength = cometEvent.getHttpServletRequest().getContentLength();
- throw new UnsupportedOperationException("Decoding "+Bayeux.JSON_CONTENT_TYPE+" not yet implemented.");
- } else { //GET method or application/x-www-form-urlencoded
- String message = cometEvent.getHttpServletRequest().getParameter(Bayeux.MESSAGE_PARAMETER);
- if (log.isTraceEnabled()) {
- log.trace("["+Thread.currentThread().getName()+"] Received JSON message:"+message);
- }
- try {
- int action = handleBayeux(message, cometEvent);
- if (log.isDebugEnabled()) {
- log.debug("["+Thread.currentThread().getName()+"] Bayeux handling complete, action result="+action);
- }
- if (action<=0) {
- cometEvent.close();
- }
- }catch (Exception x) {
- x.printStackTrace();
- tb.remove(cometEvent);
- log.error(x);
- cometEvent.close();
- }
- }
- }
-
- protected int handleBayeux(String message, CometEvent event) throws IOException, ServletException {
- int result = 0;
- if (message==null || message.length()==0) return result;
- try {
- BayeuxRequest request = null;
- //a message can be an array of messages
- JSONArray jsArray = new JSONArray(message);
- for (int i = 0; i < jsArray.length(); i++) {
- JSONObject msg = jsArray.getJSONObject(i);
-
- if (log.isDebugEnabled()) {
- log.debug("["+Thread.currentThread().getName()+"] Processing bayeux message:"+msg);
- }
- request = RequestFactory.getRequest(tb,event,msg);
- if (log.isDebugEnabled()) {
- log.debug("["+Thread.currentThread().getName()+"] Processing bayeux message using request:"+request);
- }
- result = request.process(result);
- if (log.isDebugEnabled()) {
- log.debug("["+Thread.currentThread().getName()+"] Processing bayeux message result:"+result);
- }
- }
- if (result>0 && request!=null) {
- event.getHttpServletRequest().setAttribute(BayeuxRequest.LAST_REQ_ATTR, request);
- ClientImpl ci = (ClientImpl)tb.getClient(((RequestBase)request).getClientId());
- ci.addCometEvent(event);
- if (log.isDebugEnabled()) {
- log.debug("["+Thread.currentThread().getName()+"] Done bayeux message added to request attribute");
- }
- } else if (result == 0 && request!=null) {
- RequestBase.deliver(event,(ClientImpl)tb.getClient(((RequestBase)request).getClientId()));
- if (log.isDebugEnabled()) {
- log.debug("["+Thread.currentThread().getName()+"] Done bayeux message, delivered to client");
- }
- }
-
- }catch (JSONException x) {
- log.error(x);//to do impl error handling
- result = -1;
- }catch (BayeuxException x) {
- log.error(x); //to do impl error handling
- result = -1;
- }
- return result;
- }
-
- public ServletConfig getServletConfig() {
- return servletConfig;
- }
-
- public String getServletInfo() {
- return "Tomcat/BayeuxServlet/1.0";
- }
-
- public void init(ServletConfig servletConfig) throws ServletException {
-
- this.servletConfig = servletConfig;
- ServletContext ctx = servletConfig.getServletContext();
- if (ctx.getAttribute(TOMCAT_BAYEUX_ATTR)==null)
- ctx.setAttribute(TOMCAT_BAYEUX_ATTR,new TomcatBayeux());
- this.tb = (TomcatBayeux)ctx.getAttribute(TOMCAT_BAYEUX_ATTR);
- tb.setReconnectInterval(getReconnectInterval());
- }
-
- public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
- if (servletResponse instanceof HttpServletResponse) {
- ( (HttpServletResponse) servletResponse).sendError(500, "Misconfigured Tomcat server, must be configured to support Comet operations.");
- } else {
- throw new ServletException("Misconfigured Tomcat server, must be configured to support Comet operations for the Bayeux protocol.");
- }
- }
-}
diff --git a/modules/bayeux/java/org/apache/tomcat/bayeux/ChannelImpl.java b/modules/bayeux/java/org/apache/tomcat/bayeux/ChannelImpl.java
deleted file mode 100644
index f8c1a69..0000000
--- a/modules/bayeux/java/org/apache/tomcat/bayeux/ChannelImpl.java
+++ /dev/null
@@ -1,188 +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.bayeux;
-
-import java.util.LinkedList;
-
-import org.apache.cometd.bayeux.Channel;
-import org.apache.cometd.bayeux.Client;
-import org.apache.cometd.bayeux.DataFilter;
-import java.util.Collections;
-import java.util.List;
-import org.apache.cometd.bayeux.Message;
-import java.util.Iterator;
-import org.apache.juli.logging.Log;
-import org.apache.juli.logging.LogFactory;
-/**
- *
- * @version 1.0
- */
-public class ChannelImpl implements Channel {
-
- private static final Log log = LogFactory.getLog(ChannelImpl.class);
-
- /**
- * The unique id of this channel
- */
- protected String id = null;
-
- /**
- * A list of the current subscribers
- */
- protected LinkedList<Client> subscribers = new LinkedList<Client>();
-
- /**
- * A list of the current filters
- */
- protected LinkedList<DataFilter> filters = new LinkedList<DataFilter>();
-
- /**
- * Is this channel persistent, default value is true
- */
- protected boolean persistent = true;
-
- /**
- * Creates a new channel
- * @param id String - the id of the channel, can not be null
- */
- protected ChannelImpl(String id) {
- assert id != null;
- this.id = id;
- }
-
- /**
- * returns the id of this channel
- * @return String
- */
- public String getId() {
- return id;
- }
-
- /**
- * Returns true if this channel matches the pattern to its id.
- * The channel pattern can be a complete name like <code>/service/mychannel</code>
- * or it can be a wild card pattern like <code>/service/app2/**</code>
- * @param pattern String according to the Bayeux specification section 2.2.1 Channel Globbing, can not be null.
- * @return boolean true if the id of this channel matches the pattern
- */
- public boolean matches(String pattern) {
- if (pattern == null)
- throw new NullPointerException("Channel pattern must not be null.");
- if (getId().equals(pattern))
- return true;
- int wildcardPos = pattern.indexOf("/*");
- if (wildcardPos == -1)
- return false;
- boolean multiSegment = pattern.indexOf("**") != -1;
- String leadSubstring = pattern.substring(0, wildcardPos);
- if (leadSubstring == null)
- return false;
- if (multiSegment)
- return getId().startsWith(leadSubstring);
- else {
- if (getId().length() <= wildcardPos + 2)
- return false;
- return !(getId().substring(wildcardPos + 2).contains("/"));
- }
- }
-
-
-
- /**
- * @return returns a non modifiable list of the subscribers for this channel.
- */
- public List<Client> getSubscribers() {
- return Collections.unmodifiableList(subscribers);
- }
-
- /**
- * @return true if the Channel will persist without any subscription.
- */
- public boolean isPersistent() {
- return persistent;
- }
-
- public void publish(Message msg) {
- publish(new Message[] {msg});
- }
-
- public void publish(Message[] msgs) {
- if (msgs==null) return;
- MessageImpl[] imsgs = new MessageImpl[msgs.length];
- for (int i=0; msgs!=null && i<msgs.length; i++) {
- Message data = msgs[i];
-
- if (!(data instanceof MessageImpl))
- throw new IllegalArgumentException("Invalid message class, you can only publish messages "+
- "created through the Bayeux.newMessage() method");
- if (log.isDebugEnabled()) {
- log.debug("Publishing message:"+data+" to channel:"+this);
- }
- //clone it so that we can set this channel as a reference
- MessageImpl msg = (MessageImpl)((MessageImpl)data).clone();
- //this is the channel it was delivered through
- msg.setChannel(this);
- //pass through filters
- for (Iterator<DataFilter> it = filters.iterator(); it.hasNext(); ) {
- it.next().filter(msg);
- }
- imsgs[i] = msg;
- }
- //deliver it to the clients
- for (Iterator<Client> it = subscribers.iterator(); it.hasNext(); ) {
- ClientImpl c = (ClientImpl)it.next();
- c.deliverInternal(this,imsgs);
- }
-
- }
-
- public void setPersistent(boolean persistent) {
- this.persistent = persistent;
- }
-
- public void subscribe(Client subscriber) {
- if (!subscribers.contains((subscriber))) {
- subscribers.addLast(subscriber);
- ((ClientImpl)subscriber).subscribed(this);
- }
- }
-
- public Client unsubscribe(Client subscriber) {
- if (subscribers.remove(subscriber)) {
- ((ClientImpl)subscriber).unsubscribed(this);
- return subscriber;
- } else
- return null;
- }
-
- public void addFilter(DataFilter filter) {
- if (!filters.contains(filter))
- filters.addLast(filter);
- }
-
- public DataFilter removeFilter(DataFilter filter) {
- if ( filters.remove(filter) ) return filter;
- else return null;
- }
-
- public String toString() {
- StringBuilder buf = new StringBuilder(super.toString());
- buf.append("; channelId=").append(getId());
- return buf.toString();
- }
-
-}
\ No newline at end of file
diff --git a/modules/bayeux/java/org/apache/tomcat/bayeux/ClientImpl.java b/modules/bayeux/java/org/apache/tomcat/bayeux/ClientImpl.java
deleted file mode 100644
index b18e0fa..0000000
--- a/modules/bayeux/java/org/apache/tomcat/bayeux/ClientImpl.java
+++ /dev/null
@@ -1,279 +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.bayeux;
-
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.Queue;
-import java.util.concurrent.ConcurrentLinkedQueue;
-
-import org.apache.catalina.comet.CometEvent;
-import org.json.JSONObject;
-import org.apache.cometd.bayeux.Bayeux;
-import org.apache.cometd.bayeux.Client;
-import org.apache.cometd.bayeux.Listener;
-import org.apache.cometd.bayeux.Message;
-import org.apache.juli.logging.Log;
-import org.apache.juli.logging.LogFactory;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.HashMap;
-import java.util.ArrayList;
-
-public class ClientImpl implements Client {
-
- public static final int SUPPORT_CALLBACK_POLL = 0x1;
- public static final int SUPPORT_LONG_POLL = 0x2;
-
- public static final String COMET_EVENT_ATTR = "org.apache.cometd.bayeux.client";
-
- private static final Log log = LogFactory.getLog(ClientImpl.class);
-
- protected static LinkedList<Message> EMPTY_LIST = new LinkedList<Message>();
- /**
- * queued message for remote clients.
- */
- protected LinkedList<Message> messages = null;
-
- /**
- *
- */
- protected Queue<CometEvent> events = new LinkedList<CometEvent>();
-
- /**
- * Unique id representing this client
- */
- protected String id;
-
- /**
- * supported connection types, defaults to long-polling
- */
- protected int supportedConnTypes = SUPPORT_LONG_POLL | SUPPORT_CALLBACK_POLL;
-
- /**
- * The desired connection type
- */
- protected int desirectConnType = SUPPORT_LONG_POLL;
-
- /**
- * Does this client use json-comment-filtered messages
- */
- protected boolean useJsonFiltered = false;
-
- /**
- * Same JVM clients, get local=true
- */
- protected boolean local;
-
- /**
- * The callback object for local clients
- */
- protected Listener listener;
-
- protected AtomicInteger nrofsubscriptions = new AtomicInteger(0);
-
- protected ClientImpl(String id, boolean local) {
- this.id = id;
- this.local = local;
- if (!local) messages = new LinkedList<Message>();
- }
-
- protected ClientImpl(String id, CometEvent event) {
- this(id,false);
- events = new ConcurrentLinkedQueue<CometEvent>();
- addCometEvent(event);
- }
-
- public synchronized void deliver(Message message) {
- deliverInternal(null,new MessageImpl[] {(MessageImpl)message});
- }
-
- public synchronized void deliver(Message[] message) {
- deliverInternal(null,message);
- }
-
- protected synchronized void deliverInternal(ChannelImpl channel, MessageImpl message) {
- deliverInternal(channel,new MessageImpl[] {message});
- }
-
- protected synchronized void deliverInternal(ChannelImpl channel, Message[] msgs) {
- if (isLocal()) {
- //local clients must have a listener
- ArrayList<Message> list = new ArrayList<Message>();
- for (int i=0; msgs!=null && i<msgs.length; i++) {
- //dont deliver to ourselves
- if (this!=msgs[i].getClient()) list.add(msgs[i]);
- }
- if (getListener() != null && list.size()>0) {
- getListener().deliver(list.toArray(new Message[0]));
- }
- } else {
- for (int i=0; msgs!=null && i<msgs.length; i++) {
- MessageImpl message = (MessageImpl)msgs[i];
- if (this==message.getClient()) {
- //dont deliver to ourself
- continue;
- }
- //we are not implementing forever responses, if the client is connected
- //then we will fire off the message
- //first we check to see if we have any existing connections we can piggy back on
- CometEvent event = events.poll();
- boolean delivered = false;
- //TODO TODO - check on thread safety, for writing and for getting last request.
- if (event!=null) {
- synchronized (event) {
- RequestBase rq = (RequestBase)event.getHttpServletRequest().getAttribute(RequestBase.LAST_REQ_ATTR);
- if (rq!=null) {
- Map map = new HashMap();
- try {
- map.put(Bayeux.CHANNEL_FIELD,message.getChannel().getId());
- map.put(Bayeux.DATA_FIELD,message);
- JSONObject json = new JSONObject(map);
- if (log.isDebugEnabled()) {
- log.debug("Message instantly delivered to remote client["+this+"] message:"+json);
- }
- rq.addToDeliveryQueue(this, json);
- //deliver the batch
- if (i==(msgs.length-1)) {
- rq.deliver(event, this);
- event.close(); //todo, figure out a better way, this means only one message gets delivered
- removeCometEvent(event); //and delivered instantly
- }
- delivered = true;
- } catch (Exception x) {
- log.error(x);
- }
- }
- }
- }
- if (!delivered) {
- if (log.isDebugEnabled()) {
- log.debug("Message added to queue for remote client["+this+"] message:"+message);
- }
- //queue the message for the next round
- messages.add(message);
- }
- }
- }
- }
-
- public String getId() {
- return this.id;
- }
-
- protected Listener getListener() {
- return listener;
- }
-
- public boolean hasMessages() {
- if (isLocal()) return false;
- else {
- return messages.size() > 0;
- }
- }
-
- public boolean isLocal() {
- return local;
- }
-
- public int getSupportedConnTypes() {
- return supportedConnTypes;
- }
-
- public int getDesirectConnType() {
- return desirectConnType;
- }
-
- public boolean useJsonFiltered() {
- return useJsonFiltered;
- }
-
- public void setListener(Listener listener) {
- this.listener = listener;
- }
-
- public void setSupportedConnTypes(int supportedConnTypes) {
- this.supportedConnTypes = supportedConnTypes;
- }
-
- public void setUseJsonFiltered(boolean useJsonFiltered) {
- this.useJsonFiltered = useJsonFiltered;
- }
-
- public void setDesirectConnType(int desirectConnType) {
- this.desirectConnType = desirectConnType;
- }
-
- public boolean supportsCallbackPoll() {
- return (supportedConnTypes & SUPPORT_CALLBACK_POLL) == SUPPORT_CALLBACK_POLL;
- }
-
- public boolean supportsLongPoll() {
- return (supportedConnTypes & SUPPORT_LONG_POLL) == SUPPORT_LONG_POLL;
- }
-
- public synchronized List<Message> takeMessages() {
- if (isLocal()) return null;
- if (messages.size()==0) return EMPTY_LIST;
- List result = new LinkedList(messages);
- messages.clear();
- return result;
- }
-
- public String toString() {
- StringBuilder buf = new StringBuilder(super.toString());
- buf.append(" id=").append(getId());
- return buf.toString();
- }
-
- public boolean isSubscribed() {
- return nrofsubscriptions.get()>0;
- }
-
- protected synchronized boolean addCometEvent(CometEvent event) {
- boolean result = false;
- if (!events.contains(event)) {
- events.add(event);
- result = true;
- }
- event.getHttpServletRequest().setAttribute(COMET_EVENT_ATTR,this);
- return result;
- }
-
- protected synchronized boolean removeCometEvent(CometEvent event) {
- boolean result = events.remove(event);
- event.getHttpServletRequest().removeAttribute(COMET_EVENT_ATTR);
- return result;
- }
-
-
- protected void subscribed(ChannelImpl ch) {
- nrofsubscriptions.addAndGet(1);
- }
-
- protected void unsubscribed(ChannelImpl ch) {
- nrofsubscriptions.addAndGet(-1);
- }
-
- public void startBatch(){
- //noop until improved
- }
- public void endBatch() {
- //noop until improved
- }
-
-}
diff --git a/modules/bayeux/java/org/apache/tomcat/bayeux/HttpError.java b/modules/bayeux/java/org/apache/tomcat/bayeux/HttpError.java
deleted file mode 100644
index 57d5636..0000000
--- a/modules/bayeux/java/org/apache/tomcat/bayeux/HttpError.java
+++ /dev/null
@@ -1,60 +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.bayeux;
-
-public class HttpError {
- private int code;
- private String status;
- private Throwable cause;
- public HttpError(int code, String status, Throwable cause) {
- this.code = code;
- this.status = status;
- this.cause = cause;
- }
-
- public void setCode(int code) {
- this.code = code;
- }
-
- public void setStatus(String status) {
- this.status = status;
- }
-
- public void setCause(Throwable exception) {
- this.cause = exception;
- }
-
- public int getCode() {
- return code;
- }
-
- public String getStatus() {
- return status;
- }
-
- public Throwable getCause() {
- return cause;
- }
-
- public String toString() {
- if (cause != null)
- return code + ":" + status + " - [" + cause + "]";
- else
- return code + ":" + status;
- }
-}
diff --git a/modules/bayeux/java/org/apache/tomcat/bayeux/MessageImpl.java b/modules/bayeux/java/org/apache/tomcat/bayeux/MessageImpl.java
deleted file mode 100644
index bf54652..0000000
--- a/modules/bayeux/java/org/apache/tomcat/bayeux/MessageImpl.java
+++ /dev/null
@@ -1,80 +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.bayeux;
-
-import java.util.HashMap;
-
-import org.apache.cometd.bayeux.Channel;
-import org.apache.cometd.bayeux.Client;
-import org.apache.cometd.bayeux.Message;
-
-public class MessageImpl extends HashMap<String,Object> implements Message {
-
- protected Channel channel;
- protected Client client;
- protected String id;
- private long TTL = 1000*60*5; //5min is the default TTL for a message
- protected long creationTime = System.currentTimeMillis();
-
- public Object clone() {
- MessageImpl copy = new MessageImpl(id);
- copy.putAll(this);
- copy.channel = channel;
- copy.client = client;
- copy.id = id;
- copy.creationTime = creationTime;
- copy.TTL = TTL;
- return copy;
- }
-
- protected MessageImpl(String id) {
- assert id != null;
- this.id = id;
- }
-
- public Channel getChannel() {
- return channel;
- }
-
- public Client getClient() {
- return client;
- }
-
- public long getCreationTime() {
- return creationTime;
- }
-
- public long getTTL() {
- return TTL;
- }
-
- public String getId() {
- return id;
- }
-
- protected void setChannel(Channel channel) {
- this.channel = channel;
- }
-
- protected void setClient(Client client) {
- this.client = client;
- }
-
- public void setTTL(long TTL) {
- this.TTL = TTL;
- }
-}
\ No newline at end of file
diff --git a/modules/bayeux/java/org/apache/tomcat/bayeux/RequestBase.java b/modules/bayeux/java/org/apache/tomcat/bayeux/RequestBase.java
deleted file mode 100644
index f2ccfb1..0000000
--- a/modules/bayeux/java/org/apache/tomcat/bayeux/RequestBase.java
+++ /dev/null
@@ -1,258 +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.bayeux;
-
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.TimeZone;
-import java.util.Date;
-import java.text.SimpleDateFormat;
-import javax.servlet.ServletException;
-
-import org.apache.catalina.comet.CometEvent;
-import org.apache.tomcat.bayeux.HttpError;
-
-import org.apache.juli.logging.Log;
-import org.apache.juli.logging.LogFactory;
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
-import org.apache.cometd.bayeux.Bayeux;
-import org.apache.cometd.bayeux.Message;
-
-/**
- * Common functionality and member variables for all Bayeux requests.
- *
- * @author Guy A. Molinari
- * @version 0.9
- *
- */
-public abstract class RequestBase implements BayeuxRequest {
-
- protected static final SimpleDateFormat timestampFmt =
- new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
- static {
- timestampFmt.setTimeZone(TimeZone.getTimeZone("GMT"));
- }
- //message properties, combined for all messages
- protected TomcatBayeux tomcatBayeux;
- protected String channel;
- protected String id;
- protected String clientId;
- protected String version = null;
- protected String[] suppConnTypes = null;
- protected int suppConnTypesFlag = 0;
- protected int desiredConnTypeFlag = 0;
- protected String minVersion = null;
- protected String subscription = null;
- protected String data = null;
- protected String conType = null;
- protected LinkedHashMap<String, Object> ext = new LinkedHashMap<String, Object> ();
-
-
- protected CometEvent event;
-
- protected HashMap<String, Object> response = null;
-
- private static final Log log = LogFactory.getLog(RequestBase.class);
-
- protected int reconnectInterval = 1000;
-
- protected RequestBase(TomcatBayeux tb, CometEvent event, JSONObject jsReq) throws JSONException {
- this.tomcatBayeux = tb;
- this.event = event;
- channel = jsReq.optString(Bayeux.CHANNEL_FIELD);
- id = jsReq.optString(Bayeux.ID_FIELD);
- clientId = jsReq.optString(Bayeux.CLIENT_FIELD);
- version = jsReq.optString(Bayeux.VERSION_FIELD);
- minVersion = jsReq.optString(Bayeux.MIN_VERSION_FIELD);
- conType = jsReq.optString(Bayeux.CONNECTION_TYPE_FIELD);
- subscription = jsReq.optString(Bayeux.SUBSCRIPTION_FIELD);
- data = jsReq.optString(Bayeux.DATA_FIELD);
- reconnectInterval = tb.getReconnectInterval();
- if (jsReq.has(Bayeux.EXT_FIELD)) {
- JSONObject jext = jsReq.getJSONObject(Bayeux.EXT_FIELD);
- for (Iterator<String> i = jext.keys(); i.hasNext(); ) {
- String key = i.next();
- ext.put(key, jext.get(key));
- }//for
- }//end if
-
- if (jsReq.has(Bayeux.SUPP_CONNECTION_TYPE_FIELD)) {
- JSONArray types = jsReq.getJSONArray(Bayeux.SUPP_CONNECTION_TYPE_FIELD);
- suppConnTypes = new String[types.length()];
- for (int i = 0; i < types.length(); i++) {
- suppConnTypes[i] = types.getString(i);
- if (Bayeux.TRANSPORT_CALLBACK_POLL.equals(suppConnTypes[i]))
- suppConnTypesFlag = suppConnTypesFlag|ClientImpl.SUPPORT_CALLBACK_POLL;
- else if (Bayeux.TRANSPORT_LONG_POLL.equals(suppConnTypes[i]))
- suppConnTypesFlag = suppConnTypesFlag|ClientImpl.SUPPORT_LONG_POLL;
- }//for
- }//end if
-
- if (conType!=null) {
- if (Bayeux.TRANSPORT_CALLBACK_POLL.equals(conType))
- desiredConnTypeFlag = ClientImpl.SUPPORT_CALLBACK_POLL;
- else if (Bayeux.TRANSPORT_LONG_POLL.equals(conType))
- desiredConnTypeFlag = ClientImpl.SUPPORT_LONG_POLL;
- }//end if
-
- //due to the fact that the javascript doesn't send up a required field
- //we have to fake it
- suppConnTypesFlag = ClientImpl.SUPPORT_CALLBACK_POLL | ClientImpl.SUPPORT_LONG_POLL;
-
- }
-
- public HttpError validate() {
- HttpError result = null;
-// if (clientId == null) {
-// result = new HttpError(401,"No Client ID.", null);
-// }
- return result;
- }
-
- public TomcatBayeux getTomcatBayeux() {
- return tomcatBayeux;
- }
-
- public String getChannel() {
- return channel;
- }
-
- public String getId() {
- return id;
- }
-
- public String getClientId() {
- return clientId;
- }
-
- public LinkedHashMap getExt() {
- return ext;
- }
-
- public CometEvent getEvent() {
- return event;
- }
-
- protected static void deliver(CometEvent event, ClientImpl to) throws IOException, ServletException, BayeuxException {
- JSONArray jarray = getJSONArray(event,true);
- if ( jarray == null ) throw new BayeuxException("No message to send!");
- String jsonstring = jarray.toString();
- if (log.isDebugEnabled()) {
- log.debug("["+Thread.currentThread().getName()+"] Delivering message to[" + to + "] message:" + jsonstring);
- }
-
- if (to!=null) {
- if (to.useJsonFiltered()) {
- if (!event.getHttpServletResponse().isCommitted()) event.getHttpServletResponse().setContentType("text/json-comment-filtered");
- }else {
- if (!event.getHttpServletResponse().isCommitted()) event.getHttpServletResponse().setContentType("text/json");
- }
- }
-
- PrintWriter out = event.getHttpServletResponse().getWriter();
- if (to==null) {
- //do nothing
- }else if ( (to.getDesirectConnType() == 0 && to.supportsLongPoll()) || to.getDesirectConnType() == ClientImpl.SUPPORT_LONG_POLL) {
- if (to.useJsonFiltered())
- out.print("/*");
- } else if ( (to.getDesirectConnType() == 0 && to.supportsCallbackPoll()) || to.getDesirectConnType() == ClientImpl.SUPPORT_CALLBACK_POLL) {
- String jsonp = event.getHttpServletRequest().getParameter(Bayeux.JSONP_PARAMETER);
- if (jsonp == null)
- jsonp = Bayeux.JSONP_DEFAULT_NAME;
- out.print(jsonp);
- out.print('(');
- } else {
- throw new BayeuxException("Client doesn't support any appropriate connection type.");
- }
- out.print(jsonstring);
- if ( to == null ) {
- //do nothing
- } else if ( (to.getDesirectConnType() == 0 && to.supportsLongPoll()) || to.getDesirectConnType() == ClientImpl.SUPPORT_LONG_POLL) {
- if (to.useJsonFiltered())
- out.print("*/");
- } else if ( (to.getDesirectConnType() == 0 && to.supportsCallbackPoll()) || to.getDesirectConnType() == ClientImpl.SUPPORT_CALLBACK_POLL) {
- out.print(");");
- }
- out.flush();
- event.getHttpServletResponse().flushBuffer();
-
-
- }
-
- protected static JSONArray getJSONArray(CometEvent event, boolean nullok) {
- synchronized(event) {
- JSONArray jarray = (JSONArray) event.getHttpServletRequest().getAttribute(JSON_MSG_ARRAY);
- if (jarray == null && (!nullok)) {
- jarray = new JSONArray();
- event.getHttpServletRequest().setAttribute(JSON_MSG_ARRAY, jarray);
- }
- return jarray;
- }
- }
-
- protected JSONArray getJSONArray() {
- return getJSONArray(event,false);
- }
-
- protected void addToDeliveryQueue(ClientImpl to, JSONObject msg) throws IOException, ServletException, BayeuxException {
- synchronized (event) {
- getJSONArray().put(msg);
- }
- }
-
- protected void flushMessages(ClientImpl client) throws BayeuxException {
- List<Message> msgs = client.takeMessages();
- synchronized (event) {
- try {
- for (Iterator<Message> it = msgs.iterator(); it.hasNext(); ){
- MessageImpl msg = (MessageImpl)it.next();
- Map map = new HashMap();
- map.put(Bayeux.CHANNEL_FIELD,msg.getChannel().getId());
- if (msg.getClient()!=null) map.put(Bayeux.CLIENT_FIELD,msg.getClient().getId());
- map.put(Bayeux.DATA_FIELD,msg);
- JSONObject obj = new JSONObject(map);
- addToDeliveryQueue(client, obj);
- }
- } catch (ServletException x) {
- throw new BayeuxException(x);
- } catch (IOException x) {
- throw new BayeuxException(x);
- }
- }
- }
-
- public int process(int prevops) throws BayeuxException {
- event.getHttpServletRequest().setAttribute(CURRENT_REQ_ATTR,this);
- return prevops;
- }
-
- public int getReconnectInterval() {
- return reconnectInterval;
- }
-
- public String getTimeStamp() {
- return timestampFmt.format(new Date(System.currentTimeMillis()));
- }
-
-}
diff --git a/modules/bayeux/java/org/apache/tomcat/bayeux/RequestFactory.java b/modules/bayeux/java/org/apache/tomcat/bayeux/RequestFactory.java
deleted file mode 100644
index 3c4cd3f..0000000
--- a/modules/bayeux/java/org/apache/tomcat/bayeux/RequestFactory.java
+++ /dev/null
@@ -1,48 +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.bayeux;
-
-import org.json.JSONObject;
-import org.apache.tomcat.bayeux.request.MetaHandshakeRequest;
-import org.apache.catalina.comet.CometEvent;
-import org.json.JSONException;
-import org.apache.tomcat.bayeux.request.MetaConnectRequest;
-import org.apache.tomcat.bayeux.request.MetaDisconnectRequest;
-import org.apache.tomcat.bayeux.request.MetaSubscribeRequest;
-import org.apache.tomcat.bayeux.request.MetaUnsubscribeRequest;
-import org.apache.tomcat.bayeux.request.PublishRequest;
-import org.apache.cometd.bayeux.Bayeux;
-
-public class RequestFactory {
-
- public static BayeuxRequest getRequest(TomcatBayeux tomcatBayeux, CometEvent event, JSONObject msg) throws JSONException {
- String channel = msg.optString(Bayeux.CHANNEL_FIELD);
- if (Bayeux.META_HANDSHAKE.equals(channel)) {
- return new MetaHandshakeRequest(tomcatBayeux,event,msg);
- }else if (Bayeux.META_CONNECT.equals(channel)) {
- return new MetaConnectRequest(tomcatBayeux,event,msg);
- }else if (Bayeux.META_DISCONNECT.equals(channel)) {
- return new MetaDisconnectRequest(tomcatBayeux,event,msg);
- }else if (Bayeux.META_SUBSCRIBE.equals(channel)) {
- return new MetaSubscribeRequest(tomcatBayeux,event,msg);
- }else if (Bayeux.META_UNSUBSCRIBE.equals(channel)) {
- return new MetaUnsubscribeRequest(tomcatBayeux,event,msg);
- } else {
- return new PublishRequest(tomcatBayeux,event,msg);
- }
- }
-}
\ No newline at end of file
diff --git a/modules/bayeux/java/org/apache/tomcat/bayeux/TomcatBayeux.java b/modules/bayeux/java/org/apache/tomcat/bayeux/TomcatBayeux.java
deleted file mode 100644
index 3ea636e..0000000
--- a/modules/bayeux/java/org/apache/tomcat/bayeux/TomcatBayeux.java
+++ /dev/null
@@ -1,175 +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.bayeux;
-
-import java.util.HashMap;
-import java.util.LinkedHashMap;
-import java.util.List;
-
-import org.apache.catalina.comet.CometEvent;
-import org.apache.catalina.tribes.util.Arrays;
-import org.apache.catalina.tribes.util.UUIDGenerator;
-import org.apache.cometd.bayeux.Bayeux;
-import org.apache.cometd.bayeux.Channel;
-import org.apache.cometd.bayeux.Client;
-import org.apache.cometd.bayeux.Listener;
-import org.apache.cometd.bayeux.Message;
-import org.apache.cometd.bayeux.SecurityPolicy;
-/**
- *
- * @version 1.0
- */
-public class TomcatBayeux implements Bayeux {
-
-
- protected int reconnectInterval = 5000;
- /**
- * a list of all active clients
- */
- protected HashMap<String,Client> clients = new HashMap<String,Client>();
-
- /**
- * a list of all active channels
- */
- protected LinkedHashMap<String, Channel> channels = new LinkedHashMap<String,Channel>();
-
- /**
- * security policy to be used.
- */
- protected SecurityPolicy securityPolicy = null;
- /**
- * default client to use when we need to send an error message but don't have a client valid reference
- */
- protected static ClientImpl errorClient = new ClientImpl("error-no-client",false);
-
- /**
- * returns the default error client
- * @return ClientImpl
- */
- public static ClientImpl getErrorClient() {
- return errorClient;
- }
-
- protected TomcatBayeux() {
- }
-
- /**
- * should be invoked when the servlet is destroyed or when the context shuts down
- */
- public void destroy() {
- throw new UnsupportedOperationException("TomcatBayeux.destroy() not yet implemented");
- }
-
- public Channel getChannel(String channelId, boolean create) {
- Channel result = channels.get(channelId);
- if (result==null && create) {
- result = new ChannelImpl(channelId);
- channels.put(channelId,result);
- }
- return result;
- }
-
- public Channel remove(Channel channel) {
- return channels.remove(channel.getId());
- }
-
- public Client remove(Client client) {
- if (client==null) return null;
- for (Channel ch : getChannels()) {
- ch.unsubscribe(client);
- }
- return clients.remove(client.getId());
- }
-
- public Client getClient(String clientId) {
- return clients.get(clientId);
- }
-
- public boolean hasClient(String clientId) {
- return clients.containsKey(clientId);
- }
-
- public List<Client> getClients() {
- return java.util.Arrays.asList(clients.values().toArray(new Client[0]));
- }
-
- public SecurityPolicy getSecurityPolicy() {
- return securityPolicy;
- }
-
- public int getReconnectInterval() {
- return reconnectInterval;
- }
-
- public boolean hasChannel(String channel) {
- return channels.containsKey(channel);
- }
-
- public Client newClient(String idprefix, Listener listener, boolean local, CometEvent event) {
- String id = createUUID(idprefix);
- ClientImpl client = new ClientImpl(id, local);
- client.setListener(listener);
- clients.put(id, client);
- return client;
- }
-
- public Client newClient(String idprefix, Listener listener) {
- assert listener!=null;
- //if this method gets called, someone is using the API inside
- //the JVM, this is a local client
- return newClient(idprefix,listener,true, null);
- }
-
- protected ClientImpl getClientImpl(CometEvent event) {
- return (ClientImpl)event.getHttpServletRequest().getAttribute(ClientImpl.COMET_EVENT_ATTR);
- }
-
- protected void remove(CometEvent event) {
- ClientImpl client = getClientImpl(event);
- if (client!=null) {
- client.removeCometEvent(event);
- }
- }
-
- public String createUUID(String idprefix) {
- if (idprefix==null) idprefix="";
- return idprefix + Arrays.toString(UUIDGenerator.randomUUID(false));
- }
-
- public List<Channel> getChannels() {
- return java.util.Arrays.asList(channels.entrySet().toArray(new Channel[0]));
- }
-
- protected Message newMessage() {
- String id = createUUID("msg-");
- return new MessageImpl(id);
- }
-
- public Message newMessage(Client from) {
- MessageImpl msg = (MessageImpl)newMessage();
- msg.setClient(from);
- return msg;
- }
- public void setSecurityPolicy(SecurityPolicy securityPolicy) {
- this.securityPolicy = securityPolicy;
- }
-
- public void setReconnectInterval(int reconnectTimeout) {
- this.reconnectInterval = reconnectTimeout;
- }
-
-}
diff --git a/modules/bayeux/java/org/apache/tomcat/bayeux/request/MetaConnectRequest.java b/modules/bayeux/java/org/apache/tomcat/bayeux/request/MetaConnectRequest.java
deleted file mode 100644
index 9fcd397..0000000
--- a/modules/bayeux/java/org/apache/tomcat/bayeux/request/MetaConnectRequest.java
+++ /dev/null
@@ -1,124 +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.bayeux.request;
-
-import java.io.IOException;
-import java.util.HashMap;
-import javax.servlet.ServletException;
-
-import org.apache.catalina.comet.CometEvent;
-import org.apache.tomcat.bayeux.HttpError;
-import org.apache.tomcat.bayeux.BayeuxException;
-import org.apache.tomcat.bayeux.BayeuxRequest;
-import org.apache.tomcat.bayeux.ClientImpl;
-import org.apache.tomcat.bayeux.TomcatBayeux;
-import org.json.JSONException;
-import org.json.JSONObject;
-import org.apache.cometd.bayeux.Bayeux;
-import org.apache.tomcat.bayeux.*;
-
-/******************************************************************************
- * Handshake request Bayeux message.
- *
- * @author Guy A. Molinari
- * @version 1.0
- *
- */
-public class MetaConnectRequest extends RequestBase implements BayeuxRequest {
- protected static HashMap<String,Object> responseTemplate = new HashMap<String,Object>();
-
- static {
- responseTemplate.put(Bayeux.CHANNEL_FIELD,Bayeux.META_CONNECT);
- responseTemplate.put(Bayeux.SUCCESSFUL_FIELD,Boolean.TRUE);
- responseTemplate.put(Bayeux.ADVICE_FIELD, new HashMap<String, Object>());
- }
-
- public MetaConnectRequest(TomcatBayeux tb, CometEvent event, JSONObject jsReq) throws JSONException {
- super(tb, event, jsReq);
- if (clientId!=null && getTomcatBayeux().hasClient(clientId)) {
- event.getHttpServletRequest().setAttribute("client",getTomcatBayeux().getClient(clientId));
- }
- }
-
-
- /**
- * Check client request for validity.
- *
- * Per section 4.2.1 of the Bayuex spec a connect request must contain:
- * 1) The "/meta/connect" channel identifier.
- * 2) The clientId returned by the server after handshake.
- * 3) The desired connectionType (must be one of the server's supported
- * types returned by handshake response.
- *
- * @return HttpError This method returns null if no errors were found
- */
- public HttpError validate() {
- if(clientId==null|| (!getTomcatBayeux().hasClient(clientId)))
- return new HttpError(400,"Client Id not valid.", null);
- if (! (Bayeux.TRANSPORT_LONG_POLL.equals(conType) || Bayeux.TRANSPORT_CALLBACK_POLL.equals(conType)))
- return new HttpError(400,"Unsupported connection type.",null);
- return null;//no error
- }
-
- /**
- * Transition to connected state, flushing pending messages if
- * available. If there are pending subscriptions and no messages to
- * flush then the connection is held until there is a pending publish
- * event to be delivered to this client (Section 4.2.2 of spec).
- */
- public int process(int prevops) throws BayeuxException {
- super.process(prevops);
- response = (HashMap<String, Object>)responseTemplate.clone();
- ClientImpl client = (ClientImpl)getTomcatBayeux().getClient(clientId);
- boolean success = false;
- HttpError error = validate();
- if (error == null) {
- client.setDesirectConnType(desiredConnTypeFlag);
- ((HashMap) response.get(Bayeux.ADVICE_FIELD)).put(Bayeux.RECONNECT_FIELD, Bayeux.RETRY_RESPONSE);
- ((HashMap) response.get(Bayeux.ADVICE_FIELD)).put(Bayeux.INTERVAL_FIELD, getReconnectInterval());
- success = true;
- }else {
- response.put(Bayeux.SUCCESSFUL_FIELD,Boolean.FALSE);
- response.put(Bayeux.ERROR_FIELD, error.toString());
- ((HashMap) response.get(Bayeux.ADVICE_FIELD)).put(Bayeux.RECONNECT_FIELD, Bayeux.HANDSHAKE_RESPONSE);
- if (client==null) client = TomcatBayeux.getErrorClient();
- }
- response.put(Bayeux.CLIENT_FIELD, client.getId());
- response.put(Bayeux.TIMESTAMP_FIELD,getTimeStamp());
- try {
- JSONObject obj = new JSONObject(response);
- addToDeliveryQueue(client, obj);
- } catch (ServletException x) {
- throw new BayeuxException(x);
- } catch (IOException x) {
- throw new BayeuxException(x);
- }
-
- //return immediately if there is no subscriptions
- //so that we can process the next message
- int result = client.isSubscribed()?1:0;
-
- if (success && client!=null && client.hasMessages()) {
- //send out messages
- flushMessages(client);
- result = 0; //flush out the messages
- }
-
- return result;
- }
-}
-
diff --git a/modules/bayeux/java/org/apache/tomcat/bayeux/request/MetaDisconnectRequest.java b/modules/bayeux/java/org/apache/tomcat/bayeux/request/MetaDisconnectRequest.java
deleted file mode 100644
index 3a7b76e..0000000
--- a/modules/bayeux/java/org/apache/tomcat/bayeux/request/MetaDisconnectRequest.java
+++ /dev/null
@@ -1,104 +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.bayeux.request;
-
-import java.io.IOException;
-import java.util.HashMap;
-import javax.servlet.ServletException;
-
-import org.apache.catalina.comet.CometEvent;
-import org.apache.tomcat.bayeux.HttpError;
-import org.apache.tomcat.bayeux.BayeuxException;
-import org.apache.tomcat.bayeux.BayeuxRequest;
-import org.apache.tomcat.bayeux.ClientImpl;
-import org.apache.tomcat.bayeux.TomcatBayeux;
-import org.json.JSONException;
-import org.json.JSONObject;
-import org.apache.cometd.bayeux.Bayeux;
-import org.apache.tomcat.bayeux.*;
-import org.apache.cometd.bayeux.Channel;
-
-/******************************************************************************
- * Handshake request Bayeux message.
- *
- * @author Guy A. Molinari
- * @version 1.0
- *
- */
-public class MetaDisconnectRequest extends RequestBase implements BayeuxRequest {
-
- protected static HashMap<String,Object> responseTemplate = new HashMap<String,Object>();
-
- static {
- responseTemplate.put(Bayeux.CHANNEL_FIELD,Bayeux.META_DISCONNECT);
- responseTemplate.put(Bayeux.SUCCESSFUL_FIELD,Boolean.TRUE);
- responseTemplate.put(Bayeux.ADVICE_FIELD, new HashMap<String, Object>());
- }
-
- public MetaDisconnectRequest(TomcatBayeux tb, CometEvent event, JSONObject jsReq) throws JSONException {
- super(tb, event, jsReq);
- }
-
-
- /**
- * Check client request for validity.
- *
- * Per section 4.4.1 of the Bayuex spec a connect request must contain:
- * 1) The "/meta/disconnect" channel identifier.
- * 2) The clientId.
- *
- * @return HttpError This method returns null if no errors were found
- */
- public HttpError validate() {
- if(clientId==null|| (!this.getTomcatBayeux().hasClient(clientId)))
- return new HttpError(400,"Client Id not valid.", null);
-// if (! (Bayeux.TRANSPORT_LONG_POLL.equals(conType) || Bayeux.TRANSPORT_CALLBACK_POLL.equals(conType)))
-// return new HttpError(400,"Unsupported connection type.",null);
- return null;//no error
- }
-
- /**
- * Disconnect a client session.
- */
- public int process(int prevops) throws BayeuxException {
- super.process(prevops);
- response = (HashMap<String, Object>)responseTemplate.clone();
- ClientImpl client = (ClientImpl)getTomcatBayeux().getClient(clientId);
- HttpError error = validate();
- if (error == null) {
- ((HashMap) response.get(Bayeux.ADVICE_FIELD)).put("reconnect", "retry");
- ((HashMap) response.get(Bayeux.ADVICE_FIELD)).put("interval", getReconnectInterval());
- }else {
- getTomcatBayeux().remove(client);
- response.put(Bayeux.SUCCESSFUL_FIELD,Boolean.FALSE);
- response.put(Bayeux.ERROR_FIELD, error.toString());
- ((HashMap) response.get(Bayeux.ADVICE_FIELD)).put("reconnect", "none");
- if (client==null) client = TomcatBayeux.getErrorClient();
- }
- response.put(Bayeux.CLIENT_FIELD, client.getId());
- try {
- JSONObject obj = new JSONObject(response);
- addToDeliveryQueue(client, obj);
- } catch (ServletException x) {
- throw new BayeuxException(x);
- } catch (IOException x) {
- throw new BayeuxException(x);
- }
- return 0;
- }
-}
-
diff --git a/modules/bayeux/java/org/apache/tomcat/bayeux/request/MetaHandshakeRequest.java b/modules/bayeux/java/org/apache/tomcat/bayeux/request/MetaHandshakeRequest.java
deleted file mode 100644
index 5ce2566..0000000
--- a/modules/bayeux/java/org/apache/tomcat/bayeux/request/MetaHandshakeRequest.java
+++ /dev/null
@@ -1,115 +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.bayeux.request;
-
-import java.io.IOException;
-import java.util.HashMap;
-import javax.servlet.ServletException;
-
-import org.apache.catalina.comet.CometEvent;
-import org.apache.tomcat.bayeux.HttpError;
-import org.apache.tomcat.bayeux.BayeuxException;
-import org.apache.tomcat.bayeux.BayeuxRequest;
-import org.apache.tomcat.bayeux.ClientImpl;
-import org.apache.tomcat.bayeux.TomcatBayeux;
-import org.json.JSONException;
-import org.json.JSONObject;
-import org.apache.cometd.bayeux.Bayeux;
-import org.apache.tomcat.bayeux.*;
-
-/******************************************************************************
- * Handshake request Bayeux message.
- *
- * @author Guy A. Molinari
- * @version 1.0
- *
- */
-public class MetaHandshakeRequest extends RequestBase implements BayeuxRequest {
-
- protected static HashMap<String,Object> responseTemplate = new HashMap<String,Object>();
-
- static {
- responseTemplate.put(Bayeux.CHANNEL_FIELD,Bayeux.META_HANDSHAKE);
- responseTemplate.put(Bayeux.VERSION_FIELD,"1.0");
- responseTemplate.put(Bayeux.SUPP_CONNECTION_TYPE_FIELD,new String[] { Bayeux.TRANSPORT_LONG_POLL, Bayeux.TRANSPORT_CALLBACK_POLL });
- responseTemplate.put(Bayeux.SUCCESSFUL_FIELD,Boolean.TRUE);
- responseTemplate.put(Bayeux.ADVICE_FIELD, new HashMap<String, Object>());
- }
-
- public MetaHandshakeRequest(TomcatBayeux tomcatBayeux, CometEvent event, JSONObject jsReq) throws JSONException {
- super(tomcatBayeux, event, jsReq);
- }
-
-
- public String getVersion() { return version; }
- public String getMinimumVersion() { return minVersion; }
-
-
- /**
- * Check client request for validity.
- *
- * Per section 4.1.1 of the Bayuex spec a handshake request must contain:
- * 1) The "/meta/handshake" channel identifier.
- * 2) The version of the protocol supported by the client
- * 3) The client's supported connection types.
- *
- * @return HttpError This method returns null if no errors were found
- */
- public HttpError validate() {
- boolean error = (version==null || version.length()==0);
- if (!error) error = suppConnTypesFlag==0;
- if (error) return new HttpError(400,"Invalid handshake request, supportedConnectionType field missing.",null);
- else return null;
- }
-
- /**
- * Generate and return a client identifier. Return a list of
- * supported connection types. Must be a subset of or identical to
- * the list of types supported by the client. See section 4.1.2 of
- * the Bayuex specification.
- */
- public int process(int prevops) throws BayeuxException {
- super.process(prevops);
- response = (HashMap<String, Object>)responseTemplate.clone();
- ClientImpl client = null;
- HttpError error = validate();
- if (error == null) {
- client = (ClientImpl) getTomcatBayeux().newClient("http-", null, false,getEvent());
- clientId = client.getId();
- client.setSupportedConnTypes(suppConnTypesFlag);
- client.setUseJsonFiltered(getExt().get(Bayeux.JSON_COMMENT_FILTERED_FIELD) != null);
- response.put(Bayeux.CLIENT_FIELD, client.getId());
- ((HashMap) response.get(Bayeux.ADVICE_FIELD)).put(Bayeux.RECONNECT_FIELD, Bayeux.RETRY_RESPONSE);
- ((HashMap) response.get(Bayeux.ADVICE_FIELD)).put(Bayeux.INTERVAL_FIELD, getReconnectInterval());
- }else {
- response.put(Bayeux.SUCCESSFUL_FIELD,Boolean.FALSE);
- response.put(Bayeux.ERROR_FIELD, error.toString());
- client = TomcatBayeux.getErrorClient();
- ((HashMap) response.get(Bayeux.ADVICE_FIELD)).put(Bayeux.RECONNECT_FIELD, Bayeux.NONE_RESPONSE);
- }
- try {
- JSONObject obj = new JSONObject(response);
- addToDeliveryQueue(client, obj);
- } catch (ServletException x) {
- throw new BayeuxException(x);
- } catch (IOException x) {
- throw new BayeuxException(x);
- }
- return 0;
- }
-}
-
diff --git a/modules/bayeux/java/org/apache/tomcat/bayeux/request/MetaSubscribeRequest.java b/modules/bayeux/java/org/apache/tomcat/bayeux/request/MetaSubscribeRequest.java
deleted file mode 100644
index b37a46f..0000000
--- a/modules/bayeux/java/org/apache/tomcat/bayeux/request/MetaSubscribeRequest.java
+++ /dev/null
@@ -1,129 +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.bayeux.request;
-
-import java.io.IOException;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import javax.servlet.ServletException;
-
-import org.apache.catalina.comet.CometEvent;
-import org.apache.tomcat.bayeux.HttpError;
-import org.apache.tomcat.bayeux.BayeuxException;
-import org.apache.tomcat.bayeux.BayeuxRequest;
-import org.apache.tomcat.bayeux.ChannelImpl;
-import org.apache.tomcat.bayeux.ClientImpl;
-import org.apache.tomcat.bayeux.TomcatBayeux;
-import org.json.JSONException;
-import org.json.JSONObject;
-import org.apache.cometd.bayeux.Channel;
-import org.apache.cometd.bayeux.Bayeux;
-import org.apache.tomcat.bayeux.*;
-
-/******************************************************************************
- * Handshake request Bayeux message.
- *
- * @author Guy A. Molinari
- * @version 1.0
- */
-public class MetaSubscribeRequest extends RequestBase implements BayeuxRequest {
-
- protected static HashMap<String,Object> responseTemplate = new HashMap<String,Object>();
-
- static {
- responseTemplate.put(Bayeux.CHANNEL_FIELD,Bayeux.META_SUBSCRIBE);
- responseTemplate.put(Bayeux.SUCCESSFUL_FIELD,Boolean.TRUE);
- responseTemplate.put(Bayeux.ADVICE_FIELD, new HashMap<String, Object>());
- }
-
- public MetaSubscribeRequest(TomcatBayeux tb, CometEvent event, JSONObject jsReq) throws JSONException {
- super(tb, event, jsReq);
- }
-
-
- /**
- * Check client request for validity.
- *
- * Per section 4.5.1 of the Bayuex spec a connect request must contain:
- * 1) The "/meta/subscribe" channel identifier.
- * 2) The clientId.
- * 3) The subscription. This is the name of the channel of interest,
- * or a pattern.
- *
- * @return HttpError This method returns null if no errors were found
- */
- public HttpError validate() {
- if(clientId==null|| (!this.getTomcatBayeux().hasClient(clientId)))
- return new HttpError(400,"Client Id not valid.", null);
- if (subscription==null||subscription.length()==0)
- return new HttpError(400,"Subscription missing.",null);
- return null;//no error
- }
-
- /**
- * Register interest for one or more channels. Per section 2.2.1 of the
- * Bayeux spec, a pattern may be specified. Assign client to matching
- * channels and inverse client to channel reference.
- */
- public int process(int prevops) throws BayeuxException {
- super.process(prevops);
- response = (HashMap<String, Object>)this.responseTemplate.clone();
- ClientImpl client = (ClientImpl)getTomcatBayeux().getClient(clientId);
- HttpError error = validate();
- if (error == null) {
- boolean wildcard = subscription.indexOf('*')!=-1;
- boolean subscribed = false;
- if (wildcard) {
- List<Channel> channels = getTomcatBayeux().getChannels();
- Iterator<Channel> it = channels.iterator();
- while (it.hasNext()) {
- ChannelImpl ch = (ChannelImpl)it.next();
- if (ch.matches(subscription)) {
- ch.subscribe(client);
- subscribed = true;
- }
- }
- }else {
- ChannelImpl ch = (ChannelImpl)getTomcatBayeux().getChannel(subscription,true);
- ch.subscribe(client);
- subscribed = true;
- }
- response.put(Bayeux.SUCCESSFUL_FIELD, Boolean.valueOf(subscribed));
- response.put(Bayeux.SUBSCRIPTION_FIELD,subscription);
- ((HashMap) response.get(Bayeux.ADVICE_FIELD)).put("reconnect", "retry");
- ((HashMap) response.get(Bayeux.ADVICE_FIELD)).put("interval", getReconnectInterval());
- }else {
- response.put(Bayeux.SUCCESSFUL_FIELD,Boolean.FALSE);
- response.put(Bayeux.ERROR_FIELD, error.toString());
- ((HashMap) response.get(Bayeux.ADVICE_FIELD)).put("reconnect", "handshake");
- if (client==null) client = TomcatBayeux.getErrorClient();
- }
- response.put(Bayeux.CLIENT_FIELD, client.getId());
- response.put(Bayeux.TIMESTAMP_FIELD,getTimeStamp());
- try {
- JSONObject obj = new JSONObject(response);
- addToDeliveryQueue(client, obj);
- } catch (ServletException x) {
- throw new BayeuxException(x);
- } catch (IOException x) {
- throw new BayeuxException(x);
- }
- return 0;
- }
-}
-
diff --git a/modules/bayeux/java/org/apache/tomcat/bayeux/request/MetaUnsubscribeRequest.java b/modules/bayeux/java/org/apache/tomcat/bayeux/request/MetaUnsubscribeRequest.java
deleted file mode 100644
index 38401ed..0000000
--- a/modules/bayeux/java/org/apache/tomcat/bayeux/request/MetaUnsubscribeRequest.java
+++ /dev/null
@@ -1,129 +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.bayeux.request;
-
-import java.io.IOException;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import javax.servlet.ServletException;
-
-import org.apache.catalina.comet.CometEvent;
-import org.apache.tomcat.bayeux.HttpError;
-import org.apache.tomcat.bayeux.BayeuxException;
-import org.apache.tomcat.bayeux.BayeuxRequest;
-import org.apache.tomcat.bayeux.ChannelImpl;
-import org.apache.tomcat.bayeux.ClientImpl;
-import org.apache.tomcat.bayeux.TomcatBayeux;
-import org.json.JSONException;
-import org.json.JSONObject;
-import org.apache.cometd.bayeux.Channel;
-import org.apache.cometd.bayeux.Bayeux;
-import org.apache.tomcat.bayeux.*;
-
-/******************************************************************************
- * Handshake request Bayeux message.
- *
- * @author Guy A. Molinari
- * @version 1.0
- *
- */
-public class MetaUnsubscribeRequest extends RequestBase implements BayeuxRequest {
-
- protected static HashMap<String,Object> responseTemplate = new HashMap<String,Object>();
-
- static {
- responseTemplate.put(Bayeux.CHANNEL_FIELD,Bayeux.META_UNSUBSCRIBE);
- responseTemplate.put(Bayeux.SUCCESSFUL_FIELD,Boolean.TRUE);
- responseTemplate.put(Bayeux.ADVICE_FIELD, new HashMap<String, Object>());
- }
-
- public MetaUnsubscribeRequest(TomcatBayeux tb, CometEvent event, JSONObject jsReq) throws JSONException {
- super(tb, event, jsReq);
- }
-
-
- /**
- * Check client request for validity.
- *
- * Per section 4.6.1 of the Bayuex spec a connect request must contain:
- * 1) The "/meta/unsubscribe" channel identifier.
- * 2) The clientId.
- * 3) The subscription. This is the name of the channel of interest,
- * or a pattern.
- *
- * @return HttpError This method returns null if no errors were found
- */
- public HttpError validate() {
- if(clientId==null|| (!this.getTomcatBayeux().hasClient(clientId)))
- return new HttpError(400,"Client Id not valid.", null);
- if (subscription==null||subscription.length()==0)
- return new HttpError(400,"Subscription missing.",null);
- return null;//no error
- }
-
- /**
- * De-register interest for one or more channels. Per section 2.2.1 of the
- * Bayeux spec, a pattern may be specified. Sever relationships.
- */
- public int process(int prevops) throws BayeuxException {
- super.process(prevops);
- response = (HashMap<String, Object>)responseTemplate.clone();
- ClientImpl client = (ClientImpl)getTomcatBayeux().getClient(clientId);
- HttpError error = validate();
- if (error == null) {
- boolean wildcard = subscription.indexOf('*')!=-1;
- boolean unsubscribed = false;
- if (wildcard) {
- List<Channel> channels = getTomcatBayeux().getChannels();
- Iterator<Channel> it = channels.iterator();
- while (it.hasNext()) {
- ChannelImpl ch = (ChannelImpl)it.next();
- if (ch.matches(subscription)) {
- ch.unsubscribe(client);
- unsubscribed = true;
- }
- }
- }else {
- ChannelImpl ch = (ChannelImpl)getTomcatBayeux().getChannel(subscription,true);
- ch.unsubscribe(client);
- unsubscribed = true;
- }
- response.put(Bayeux.SUCCESSFUL_FIELD, Boolean.valueOf(unsubscribed));
- response.put(Bayeux.SUBSCRIPTION_FIELD,subscription);
- ((HashMap) response.get(Bayeux.ADVICE_FIELD)).put("reconnect", "retry");
- ((HashMap) response.get(Bayeux.ADVICE_FIELD)).put("interval", getReconnectInterval());
- }else {
- response.put(Bayeux.SUCCESSFUL_FIELD,Boolean.FALSE);
- response.put(Bayeux.ERROR_FIELD, error.toString());
- ((HashMap) response.get(Bayeux.ADVICE_FIELD)).put("reconnect", "handshake");
- if (client==null) client = TomcatBayeux.getErrorClient();
- }
- response.put(Bayeux.CLIENT_FIELD, client.getId());
- response.put(Bayeux.TIMESTAMP_FIELD,getTimeStamp());
- try {
- JSONObject obj = new JSONObject(response);
- addToDeliveryQueue(client, obj);
- } catch (ServletException x) {
- throw new BayeuxException(x);
- } catch (IOException x) {
- throw new BayeuxException(x);
- }
- return 0;
- }
-}
-
diff --git a/modules/bayeux/java/org/apache/tomcat/bayeux/request/PublishRequest.java b/modules/bayeux/java/org/apache/tomcat/bayeux/request/PublishRequest.java
deleted file mode 100644
index afb5343..0000000
--- a/modules/bayeux/java/org/apache/tomcat/bayeux/request/PublishRequest.java
+++ /dev/null
@@ -1,141 +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.bayeux.request;
-
-import java.io.IOException;
-import java.util.HashMap;
-import javax.servlet.ServletException;
-
-import org.apache.catalina.comet.CometEvent;
-import org.apache.tomcat.bayeux.HttpError;
-import org.apache.tomcat.bayeux.BayeuxException;
-import org.apache.tomcat.bayeux.ChannelImpl;
-import org.apache.tomcat.bayeux.ClientImpl;
-import org.apache.tomcat.bayeux.MessageImpl;
-import org.apache.tomcat.bayeux.RequestBase;
-import org.apache.tomcat.bayeux.TomcatBayeux;
-import org.json.JSONException;
-import org.json.JSONObject;
-import org.apache.cometd.bayeux.Bayeux;
-import org.apache.juli.logging.Log;
-import org.apache.juli.logging.LogFactory;
-
-/******************************************************************************
- * Handshake request Bayeux message.
- *
- * @author Guy A. Molinari
- * @version 1.0
- *
- */
-public class PublishRequest extends RequestBase {
-
- private static final Log log = LogFactory.getLog(PublishRequest.class);
-
- protected static HashMap<String,Object> responseTemplate = new HashMap<String,Object>();
-
- static {
- responseTemplate.put(Bayeux.SUCCESSFUL_FIELD,Boolean.TRUE);
- responseTemplate.put(Bayeux.ADVICE_FIELD, new HashMap<String, Object>());
- }
-
- JSONObject msgData = null;
-
- public PublishRequest(TomcatBayeux tb, CometEvent event, JSONObject jsReq) throws JSONException {
- super(tb, event, jsReq);
- }
-
-
- /**
- * Check client request for validity.
- *
- * Per section 5.1.1 of the Bayuex spec a connect request must contain:
- * 1) The channel identifier of the channel for publication.
- * 2) The data to send.
- *
- * @return HttpError This method returns null if no errors were found
- */
- @Override
- public HttpError validate() {
- if(channel==null|| (!this.getTomcatBayeux().hasChannel(channel)))
- return new HttpError(400,"Channel Id not valid.", null);
- if(data==null || data.length()==0)
- return new HttpError(400,"Message data missing.", null);
- try {
- this.msgData = new JSONObject(data);
- }catch (JSONException x) {
- return new HttpError(400,"Invalid JSON object in data attribute.",x);
- }
- if(clientId==null|| (!this.getTomcatBayeux().hasClient(clientId)))
- return new HttpError(400,"Client Id not valid.", null);
- return null;//no error
- }
-
- /**
- * Send the event message to all registered subscribers.
- */
- @Override
- public int process(int prevops) throws BayeuxException {
- super.process(prevops);
- response = (HashMap<String, Object>)responseTemplate.clone();
- ClientImpl client = clientId!=null?(ClientImpl)getTomcatBayeux().getClient(clientId):
- (ClientImpl)event.getHttpServletRequest().getAttribute("client");
- boolean success = false;
- HttpError error = validate();
- if (error == null) {
- ChannelImpl chimpl = (ChannelImpl)getTomcatBayeux().getChannel(channel,false);
- MessageImpl mimpl = (MessageImpl)getTomcatBayeux().newMessage(client);
-
- try {
- String[] keys = JSONObject.getNames(msgData);
- for (int i = 0; i < keys.length; i++) {
- mimpl.put(keys[i], msgData.get(keys[i]));
- }
- success = true;
- ((HashMap) response.get(Bayeux.ADVICE_FIELD)).put(Bayeux.RECONNECT_FIELD, Bayeux.RETRY_RESPONSE);
- ((HashMap) response.get(Bayeux.ADVICE_FIELD)).put(Bayeux.INTERVAL_FIELD, getReconnectInterval());
- }catch (JSONException x) {
- if (log.isErrorEnabled()) log.error("Unable to parse:"+msgData,x);
- throw new BayeuxException(x);
- }
- chimpl.publish(mimpl);
- }
- if(!success) {
- response.put(Bayeux.SUCCESSFUL_FIELD,Boolean.FALSE);
- response.put(Bayeux.ERROR_FIELD, error.toString());
- ((HashMap) response.get(Bayeux.ADVICE_FIELD)).put(Bayeux.RECONNECT_FIELD, Bayeux.HANDSHAKE_RESPONSE);
- if (client==null) client = TomcatBayeux.getErrorClient();
- }
- response.put(Bayeux.CHANNEL_FIELD,channel);
- response.put(Bayeux.CLIENT_FIELD, client.getId());
- try {
- JSONObject obj = new JSONObject(response);
- addToDeliveryQueue(client, obj);
- } catch (ServletException x) {
- throw new BayeuxException(x);
- } catch (IOException x) {
- throw new BayeuxException(x);
- }
-
- if (success && client!=null && client.hasMessages()) {
- //send out messages
- flushMessages(client);
- }
-
- return 0;
- }
-}
-
diff --git a/modules/bayeux/test/org/apache/cometd/bayeux/samples/BayeuxStockTicker.java b/modules/bayeux/test/org/apache/cometd/bayeux/samples/BayeuxStockTicker.java
deleted file mode 100644
index cb8d30c..0000000
--- a/modules/bayeux/test/org/apache/cometd/bayeux/samples/BayeuxStockTicker.java
+++ /dev/null
@@ -1,232 +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.cometd.bayeux.samples;
-
-import javax.servlet.ServletContextEvent;
-import javax.servlet.ServletContextListener;
-import javax.servlet.ServletContextAttributeListener;
-import javax.servlet.ServletContextAttributeEvent;
-import org.apache.cometd.bayeux.Bayeux;
-
-import java.text.DecimalFormat;
-import java.util.List;
-import java.util.Random;
-import java.util.concurrent.atomic.AtomicInteger;
-import org.apache.cometd.bayeux.Client;
-import org.apache.cometd.bayeux.Listener;
-import org.apache.cometd.bayeux.Message;
-import org.apache.cometd.bayeux.Channel;
-
-public class BayeuxStockTicker implements ServletContextListener,
- ServletContextAttributeListener, Listener {
-
- static AtomicInteger counter = new AtomicInteger(0);
- protected int id;
- protected Bayeux b;
- protected Client c;
- protected boolean alive = true;
- protected boolean initialized = false;
- protected TickerThread tt = new TickerThread();
-
- public BayeuxStockTicker() {
- id = counter.incrementAndGet();
- System.out.println("new listener created with id:" + id);
- }
-
- public void contextDestroyed(ServletContextEvent servletContextEvent) {
- alive = false;
- tt.run = false;
- tt.interrupt();
- }
-
- public void contextInitialized(ServletContextEvent servletContextEvent) {
- }
-
- public void attributeAdded(ServletContextAttributeEvent scae) {
- if (scae.getName().equals(Bayeux.DOJOX_COMETD_BAYEUX)) {
- if (initialized) return;
- initialized = true;
- System.out.println("Starting stock ticker server client!");
- b = (Bayeux) scae.getValue();
- c = b.newClient("stock-ticker-", this);
- tt.start();
- }
- }
-
- public void attributeRemoved(ServletContextAttributeEvent scae) {
- if (scae.getName().equals(Bayeux.DOJOX_COMETD_BAYEUX)) {
- initialized = false;
- b = (Bayeux) scae.getValue();
- List<Channel> chs = b.getChannels();
- for (Channel ch : chs) {
- ch.unsubscribe(c);
- }
- }
- }
-
- public void attributeReplaced(
- ServletContextAttributeEvent servletContextAttributeEvent) {
- }
-
- public void removed(boolean timeout) {
- System.out.println("Client removed.");
- }
-
- public void deliver(Message[] msgs) {
- for (int i = 0; msgs != null && i < msgs.length; i++) {
- Message msg = msgs[i];
- System.out.println("[stock ticker server client ]received message:" + msg);
- }
- }
-
- public class TickerThread extends Thread {
- public boolean run = true;
-
- public TickerThread() {
- setName("Ticker Thread");
- }
-
- public void run() {
- try {
-
- Stock[] stocks = new Stock[] {
- new Stock("GOOG", 435.43),
- new Stock("YHOO", 27.88),
- new Stock("ASF", 1015.55), };
- for (Stock s : stocks) {
- Channel ch = b.getChannel("/stock/"+s.getSymbol(), true);
- ch.subscribe(c);
-
- }
- Random r = new Random(System.currentTimeMillis());
- while (run) {
- for (int j = 0; j < 1; j++) {
- int i = r.nextInt() % 3;
- if (i < 0)
- i = i * (-1);
- Stock stock = stocks[i];
- double change = r.nextDouble();
- boolean plus = r.nextBoolean();
- if (plus) {
- stock.setValue(stock.getValue() + change);
- } else {
- stock.setValue(stock.getValue() - change);
- }
- Channel ch = b.getChannel("/stock/"+stock.getSymbol(), true);
- Message m = b.newMessage(c);
- m.put("stock", stock.toString());
- m.put("symbol", stock.getSymbol());
- m.put("price", stock.getValueAsString());
- m.put("change", stock.getLastChangeAsString());
- ch.publish(m);
- System.out.println("Bayeux Stock: "+stock.getSymbol()+" Price: "+stock.getValueAsString()+" Change: "+stock.getLastChangeAsString());
- }
- Thread.sleep(850);
- }
- } catch (InterruptedException ix) {
-
- } catch (Exception x) {
- x.printStackTrace();
- }
- }
- }
-
- public static class Stock {
- protected static DecimalFormat df = new DecimalFormat("0.00");
- protected String symbol = "";
- protected double value = 0.0d;
- protected double lastchange = 0.0d;
- protected int cnt = 0;
-
- public Stock(String symbol, double initvalue) {
- this.symbol = symbol;
- this.value = initvalue;
- }
-
- public void setCnt(int c) {
- this.cnt = c;
- }
-
- public int getCnt() {
- return cnt;
- }
-
- public String getSymbol() {
- return symbol;
- }
-
- public double getValue() {
- return value;
- }
-
- public void setValue(double value) {
- double old = this.value;
- this.value = value;
- this.lastchange = value - old;
- }
-
- public String getValueAsString() {
- return df.format(value);
- }
-
- public double getLastChange() {
- return this.lastchange;
- }
-
- public void setLastChange(double lastchange) {
- this.lastchange = lastchange;
- }
-
- public String getLastChangeAsString() {
- return df.format(lastchange);
- }
-
- public int hashCode() {
- return symbol.hashCode();
- }
-
- public boolean equals(Object other) {
- if (other instanceof Stock) {
- return this.symbol.equals(((Stock) other).symbol);
- } else {
- return false;
- }
- }
-
- public String toString(){
- StringBuilder buf = new StringBuilder("STOCK#");
- buf.append(getSymbol());
- buf.append("#");
- buf.append(getValueAsString());
- buf.append("#");
- buf.append(getLastChangeAsString());
- buf.append("#");
- buf.append(String.valueOf(getCnt()));
- return buf.toString();
-
- }
-
- public Object clone() {
- Stock s = new Stock(this.getSymbol(), this.getValue());
- s.setLastChange(this.getLastChange());
- s.setCnt(this.cnt);
- return s;
- }
- }
-
-}
\ No newline at end of file
diff --git a/modules/bayeux/test/org/apache/cometd/bayeux/samples/EchoChatClient.java b/modules/bayeux/test/org/apache/cometd/bayeux/samples/EchoChatClient.java
deleted file mode 100644
index 5f1d458..0000000
--- a/modules/bayeux/test/org/apache/cometd/bayeux/samples/EchoChatClient.java
+++ /dev/null
@@ -1,118 +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.cometd.bayeux.samples;
-
-import javax.servlet.ServletContextEvent;
-import javax.servlet.ServletContextListener;
-import javax.servlet.ServletContextAttributeListener;
-import javax.servlet.ServletContextAttributeEvent;
-import org.apache.cometd.bayeux.Bayeux;
-import java.util.concurrent.atomic.AtomicInteger;
-import org.apache.cometd.bayeux.Client;
-import org.apache.cometd.bayeux.Listener;
-import org.apache.cometd.bayeux.Message;
-import org.apache.cometd.bayeux.Channel;
-
-public class EchoChatClient implements ServletContextListener, ServletContextAttributeListener, Listener {
-
- static AtomicInteger counter = new AtomicInteger(0);
- protected int id;
- protected Bayeux b;
- protected Client c;
- protected boolean alive = true;
- protected TimestampThread tt = new TimestampThread();
-
- public EchoChatClient() {
- id = counter.incrementAndGet();
- System.out.println("new listener created with id:"+id);
- }
-
- public void contextDestroyed(ServletContextEvent servletContextEvent) {
- alive = false;
- tt.interrupt();
- }
-
- public void contextInitialized(ServletContextEvent servletContextEvent) {
- }
-
- public void attributeAdded(ServletContextAttributeEvent scae) {
- if (scae.getName().equals(Bayeux.DOJOX_COMETD_BAYEUX)) {
- System.out.println("Starting echo chat client!");
- b = (Bayeux)scae.getValue();
- c = b.newClient("echochat-",this);
- Channel ch = b.getChannel("/chat/demo",true);
- ch.subscribe(c);
- tt.start();
- }
- }
-
- public void attributeRemoved(ServletContextAttributeEvent servletContextAttributeEvent) {
- }
-
- public void attributeReplaced(ServletContextAttributeEvent servletContextAttributeEvent) {
- }
-
- public void removed(boolean timeout) {
- System.out.println("Client removed.");
- }
-
- public void deliver(Message[] msgs) {
- for (int i=0; msgs!=null && i<msgs.length; i++) {
- Message msg = msgs[i];
- System.out.println("[echochatclient ]received message:" + msg);
- Message m = b.newMessage(c);
- m.putAll(msg);
- //echo the same message
- m.put("user", "echochatserver");
- if (m.containsKey("msg")) {
- //simple chat demo
- String chat = (String) m.get("msg");
- m.put("msg", "echochatserver|I received your message-" + chat.substring(chat.indexOf("|") + 1));
- }
- System.out.println("[echochatclient ]sending message:" + m);
- msg.getChannel().publish(m);
- }
- }
-
- public class TimestampThread extends Thread {
- public TimestampThread() {
- setDaemon(true);
- }
-
- public void run() {
- while (alive) {
- try {
- sleep(5000);
- Channel ch = b.getChannel("/chat/demo",false);
- if (ch.getSubscribers().size()<=1) {
- continue;
- }
- Message m = b.newMessage(c);
- m.put("user","echochatserver");
- m.put("chat","Time is:"+new java.sql.Date(System.currentTimeMillis()).toLocaleString());
- m.put("join",false);
- ch.publish(m);
- }catch (InterruptedException ignore) {
- Thread.currentThread().interrupted();
- }catch (Exception x) {
- x.printStackTrace();
- }
- }
- }
- }
-}
diff --git a/modules/bayeux/webapps/cometd/WEB-INF/web.xml b/modules/bayeux/webapps/cometd/WEB-INF/web.xml
deleted file mode 100644
index ea3d943..0000000
--- a/modules/bayeux/webapps/cometd/WEB-INF/web.xml
+++ /dev/null
@@ -1,53 +0,0 @@
-<?xml version="1.0" encoding="ISO-8859-1"?>
-<!--
- Licensed to the Apache Software Foundation (ASF) under one or more
- contributor license agreements. See the NOTICE file distributed with
- this work for additional information regarding copyright ownership.
- The ASF licenses this file to You under the Apache License, Version 2.0
- (the "License"); you may not use this file except in compliance with
- the License. You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<web-app
- xmlns="http://java.sun.com/xml/ns/javaee"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
- version="2.5">
- <display-name>Cometd Test WebApp</display-name>
-
- <servlet>
- <servlet-name>cometd</servlet-name>
- <servlet-class>org.apache.tomcat.bayeux.BayeuxServlet</servlet-class>
- <init-param>
- <param-name>timeout</param-name>
- <param-value>120000000</param-value>
- </init-param>
- <init-param>
- <param-name>reconnectInterval</param-name>
- <param-value>250</param-value>
- </init-param>
- <load-on-startup>1</load-on-startup>
- </servlet>
-
- <servlet-mapping>
- <servlet-name>cometd</servlet-name>
- <url-pattern>/cometd/*</url-pattern>
- </servlet-mapping>
-
- <listener>
- <listener-class>org.apache.cometd.bayeux.samples.EchoChatClient</listener-class>
- </listener>
- <listener>
- <listener-class>org.apache.cometd.bayeux.samples.BayeuxStockTicker</listener-class>
- </listener>
-
-</web-app>
-
-
diff --git a/modules/bayeux/webapps/cometd/examples/simplechat/cometdchat.htm b/modules/bayeux/webapps/cometd/examples/simplechat/cometdchat.htm
deleted file mode 100644
index 0f30cad..0000000
--- a/modules/bayeux/webapps/cometd/examples/simplechat/cometdchat.htm
+++ /dev/null
@@ -1,130 +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.
--->
-<html>
-<header><title>Comet Simple Chat Application</title>
-
-<script type="text/javascript" src="../../dojo/dojo.js.uncompressed.js"></script>
-<script type="text/javascript" src="../../dojox/cometd.js"></script>
-<script type="text/javascript" src="../../dojox/cometd/_base.js"></script>
-
-<script type="text/javascript">
-
-dojo.require("dojox.cometd");
-
-dojo.addOnLoad(function() {
- dojo.byId("message").style.visibility='hidden';
- dojox.cometd.init("/cometd/cometd");
-});
-
-
-function trim(str) {
- return str.replace(/(^\s+|\s+$)/g,'');
-}
-
-
-function clear() {
- dojo.byId("msgtext").value = "";
- dojo.byId("msgtext").focus();
-}
-
-
-function enterKeyHandler(e) {
-if (!e) e = window.event;
- if (e.keyCode == 13) {
- send(trim(dojo.byId("msgtext").value));
- clear();
- }
-
-}
-
-
-function connect() {
- dojox.cometd.subscribe("/chat/demo", onMsgEvent);
- dojo.byId("login").style.visibility='hidden';
- dojo.byId("message").style.visibility='visible';
- send("Has joined the chat room");
- clear();
- dojo.byId("msgtext").onkeydown = enterKeyHandler;
- dojo.byId("myname").appendChild(document.createTextNode("-> " + dojo.byId("scrname").value + " <-"));
-}
-
-
-function onMsgEvent(event) {
-
- // Break apart the text string into screen name and message parts.
- var str = trim(event.data.msg);
- var scrname = "";
- if (str.match(/^.*[|].*$/)) {
- var spl = str.split("|");
- scrname = spl[0];
- str = " - " + spl[1];
- }
-
- // Insert the screen name in red and the message black into the DOM
- var newP = document.createElement("p");
- var fnt1 = document.createElement("font");
- var attr1 = document.createAttribute("color");
- attr1.nodeValue = "red";
- fnt1.setAttributeNode(attr1);
-
- var newT = document.createTextNode(scrname);
- fnt1.appendChild(newT);
-
- newP.appendChild(fnt1);
-
- var fnt2 = document.createElement("font");
- var attr2 = document.createAttribute("color");
- attr2.nodeValue = "black";
- fnt2.setAttributeNode(attr2);
-
- var newT2 = document.createTextNode(str);
- fnt2.appendChild(newT2);
-
- newP.appendChild(fnt2);
-
- dojo.byId("dialog").appendChild(newP)
-}
-
-
-function send(msg) {
- var scrname = dojo.byId("scrname").value;
- var evt = {'data': { 'msg': trim(scrname) + '|' + msg }};
- onMsgEvent(evt); // Echo local
- dojox.cometd.publish("/chat/demo", evt.data);
-}
-
-
-</script>
-
-</head>
-</header>
-<body>
-<form>
-<div id="login">
-Screen name: <input type="text" id="scrname">
-<input type=Button Id=logbtn value=Connect onClick=connect()><br/>Type a screen name and click the 'Connect' button
-</div>
-
-<div id="dialog"></div>
-<hr/>
-<div id="message">
-<div id="myname"></div> Is my screen name<br/>
-<textarea rows="3" cols="50" id="msgtext"></textarea>[ENTER] sends message</div>
-</form>
-</body>
-</html>
-
diff --git a/modules/bayeux/webapps/cometd/examples/simplechat/ticker.html b/modules/bayeux/webapps/cometd/examples/simplechat/ticker.html
deleted file mode 100644
index 51e4e6b..0000000
--- a/modules/bayeux/webapps/cometd/examples/simplechat/ticker.html
+++ /dev/null
@@ -1,143 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
-<!--
- 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.
--->
-<html>
-<head>
-<meta http-equiv="Content-Type" content="text/html;charset=ISO-8859-1" >
-<title>Bayeux Stock Ticker</title>
-<script type="text/javascript" src="../../dojo/dojo.js.uncompressed.js"></script>
-<script type="text/javascript" src="../../dojox/cometd.js"></script>
-<script type="text/javascript" src="../../dojox/cometd/_base.js"></script>
-<script type="text/javascript">
-
-dojo.require("dojox.cometd");
-
-dojo.addOnLoad(function() {
- dojox.cometd.init("/cometd/cometd");
- dojox.cometd.startBatch();
- dojox.cometd.subscribe("/stock/GOOG", onMsgEvent);
- dojox.cometd.subscribe("/stock/YHOO", onMsgEvent);
- dojox.cometd.subscribe("/stock/SPRG", onMsgEvent);
- dojox.cometd.endBatch();
-});
-
-
-dojo.addOnUnload(function() {
- dojox.cometd.startBatch();
- dojox.cometd.unsubscribe("/stock/GOOG", this,"");
- dojox.cometd.unsubscribe("/stock/YHOO", this,"");
- dojox.cometd.unsubscribe("/stock/SPRG", this,"");
- dojox.cometd.endBatch();
- });
-
-
-function subscribe(box, symbol) {
- if (box.checked) {
- dojox.cometd.subscribe("/stock/"+symbol, onMsgEvent);
- var rowCurrent = dojo.byId("row."+symbol);
- rowCurrent.bgColor="white";
- } else {
- dojox.cometd.unsubscribe("/stock/"+symbol, onMsgEvent);
- var rowCurrent = dojo.byId("row."+symbol);
- rowCurrent.bgColor="gray";
- }
-}
-
-function removeChildrenFromNode(node)
-{
- if(node == undefined || node == null)
- {
- return;
- }
-
- var len = node.childNodes.length;
-
- while (node.hasChildNodes())
- {
- node.removeChild(node.firstChild);
- }
-}
-
-function onMsgEvent(event) {
- // Break apart the text string into screen name and message parts.
- var symbol = event.data.symbol;
- var price = event.data.price;
- var pricechange = event.data.change;
- //alert("symbol: "+symbol+" price: "+price+" change: "+pricechange);
-
- var pricenode = dojo.byId("price."+symbol);
- var changenode = dojo.byId("change."+symbol);
- removeChildrenFromNode(pricenode);
- removeChildrenFromNode(changenode);
- var pricelabel = document.createTextNode(price);
- pricelabel.value = price;
- var changelabel = document.createTextNode(pricechange);
- changelabel.value = pricechange;
- pricenode.appendChild(pricelabel);
- changenode.appendChild(changelabel);
-
- var table = dojo.byId("stocktable");
- var rows = table.getElementsByTagName("tr");
- for(i = 0; i < rows.length; i++){
- if (rows[i].bgColor != "gray") {
- rows[i].bgColor = "white";
- }
- }
- //manipulate rows
- var rowCurrent = dojo.byId("row."+symbol);
- if (pricechange<=0) {
- rowCurrent.bgColor = "red";
- } else {
- rowCurrent.bgColor = "cyan";
- }
-}
-
-
-</script>
-</head>
-<body bgcolor="#ffffff">
-<h1 align="center">Bayeux Stock Ticker</h1>
-<h2 align="left"> </h2>
-<p>
-<table id="stocktable" cellspacing="0" cellpadding="3" width="100%" align="center" border="0">
- <tr id="row.HEADER">
- <td>SYMBOL</td>
- <td>PRICE</td>
- <td>LAST CHANGE</td>
- <td>SUBSCRIBE</td></tr>
- <tr id="row.SPRG">
- <td>SPRG</td>
- <td id="price.SPRG"></td>
- <td id="change.SPRG"></td>
- <td id="check.SPRG"><input type="checkbox" id="check.SPRG" checked onClick="subscribe(this,'SPRG')"></td>
- </tr>
- <tr id="row.GOOG">
- <td>GOOG</td>
- <td id="price.GOOG"></td>
- <td id="change.GOOG"></td>
- <td id="check.GOOG"><input type="checkbox" id="check.GOOG" checked onClick="subscribe(this,'GOOG')"></td>
- </tr>
- <tr id="row.YHOO">
- <td>YHOO</td>
- <td id="price.YHOO"></td>
- <td id="change.YHOO"></td>
- <td id="check.YHOO"><input type="checkbox" id="check.GOOG" checked onClick="subscribe(this,'YHOO')"></td>
- </tr>
-</table>
-</p>
-</body>
-</html>
\ No newline at end of file
diff --git a/modules/bayeux/webapps/cometd/index.html b/modules/bayeux/webapps/cometd/index.html
deleted file mode 100644
index f7f722e..0000000
--- a/modules/bayeux/webapps/cometd/index.html
+++ /dev/null
@@ -1,22 +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.
--->
-<h1>Cometd demo</h1>
-
-<p>
-Try the <a href="examples/simplechat/cometdchat.htm">Simple Chat Demo</a>.</br>
-Try the <a href="examples/simplechat/ticker.html">Stock Ticker Demo</a>.</br>
-</p>
diff --git a/modules/jdbc-pool/build.xml b/modules/jdbc-pool/build.xml
index 1e7368d..5b714be 100644
--- a/modules/jdbc-pool/build.xml
+++ b/modules/jdbc-pool/build.xml
@@ -1,4 +1,4 @@
-<?xml version="1.0"?>
+<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
diff --git a/modules/jdbc-pool/doc/changelog.xml b/modules/jdbc-pool/doc/changelog.xml
index 07f401f..2d7ddf8 100644
--- a/modules/jdbc-pool/doc/changelog.xml
+++ b/modules/jdbc-pool/doc/changelog.xml
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="ISO-8859-1"?>
+<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
diff --git a/modules/jdbc-pool/doc/jdbc-pool.xml b/modules/jdbc-pool/doc/jdbc-pool.xml
index dfbf862..8d69624 100644
--- a/modules/jdbc-pool/doc/jdbc-pool.xml
+++ b/modules/jdbc-pool/doc/jdbc-pool.xml
@@ -1,4 +1,4 @@
-<?xml version="1.0"?>
+<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
@@ -606,7 +606,7 @@
</p>
</subsection>
<subsection name="org.apache.tomcat.jdbc.pool.JdbcInterceptor">
- <p>Abstract base class for all interceptors, can not be instantiated.</p>
+ <p>Abstract base class for all interceptors, cannot be instantiated.</p>
<attributes>
<attribute name="useEquals" required="false">
<p>(boolean) Set to true if you wish the <code>ProxyConnection</code> class to use <code>String.equals</code> and set to <code>false</code>
diff --git a/modules/jdbc-pool/doc/package.xsl b/modules/jdbc-pool/doc/package.xsl
index 50604c4..cdadd53 100644
--- a/modules/jdbc-pool/doc/package.xsl
+++ b/modules/jdbc-pool/doc/package.xsl
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="ISO-8859-1"?>
+<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
diff --git a/modules/jdbc-pool/doc/project.xml b/modules/jdbc-pool/doc/project.xml
index ecc9a70..912903d 100644
--- a/modules/jdbc-pool/doc/project.xml
+++ b/modules/jdbc-pool/doc/project.xml
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="ISO-8859-1"?>
+<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
diff --git a/modules/jdbc-pool/pom.xml b/modules/jdbc-pool/pom.xml
index 51bfc2c..4a76e98 100644
--- a/modules/jdbc-pool/pom.xml
+++ b/modules/jdbc-pool/pom.xml
@@ -1,4 +1,4 @@
-<?xml version="1.0"?>
+<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
@@ -90,8 +90,8 @@
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
- <source>1.7</source>
- <target>1.7</target>
+ <source>1.8</source>
+ <target>1.8</target>
<optimize>true</optimize>
<debug>true</debug>
<showDeprecation>true</showDeprecation>
diff --git a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/ConnectionPool.java b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/ConnectionPool.java
index b446d39..ceb2239 100644
--- a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/ConnectionPool.java
+++ b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/ConnectionPool.java
@@ -136,7 +136,7 @@ public class ConnectionPool {
* Instantiate a connection pool. This will create connections if initialSize is larger than 0.
* The {@link PoolProperties} should not be reused for another connection pool.
* @param prop PoolProperties - all the properties for this connection pool
- * @throws SQLException
+ * @throws SQLException Pool initialization error
*/
public ConnectionPool(PoolConfiguration prop) throws SQLException {
//setup quick access variables and pools
@@ -150,7 +150,7 @@ public class ConnectionPool {
* If a connection is not retrieved, the Future must be cancelled in order for the connection to be returned
* to the pool.
* @return a Future containing a reference to the connection or the future connection
- * @throws SQLException
+ * @throws SQLException Cannot use asyncronous connect
*/
public Future<Connection> getConnectionAsync() throws SQLException {
try {
@@ -195,7 +195,8 @@ public class ConnectionPool {
* maxActive} connections a connection is returned immediately. If no
* connection is available, the pool will attempt to fetch a connection for
* {@link PoolProperties#maxWait maxWait} milliseconds.
- *
+ * @param username The user name to use for the connection
+ * @param password The password for the connection
* @return Connection - a java.sql.Connection/javax.sql.PooledConnection
* reflection proxy, wrapping the underlying object.
* @throws SQLException
@@ -339,8 +340,9 @@ public class ConnectionPool {
/**
* Creates and caches a {@link java.lang.reflect.Constructor} used to instantiate the proxy object.
* We cache this, since the creation of a constructor is fairly slow.
+ * @param xa Use a XA connection
* @return constructor used to instantiate the wrapper object
- * @throws NoSuchMethodException
+ * @throws NoSuchMethodException Failed to get a constructor
*/
public Constructor<?> getProxyConstructor(boolean xa) throws NoSuchMethodException {
//cache the constructor
@@ -524,6 +526,7 @@ public class ConnectionPool {
}
}
+
//===============================================================================
// CONNECTION POOLING IMPL LOGIC
//===============================================================================
@@ -610,8 +613,10 @@ public class ConnectionPool {
* Thread safe way to retrieve a connection from the pool
* @param wait - time to wait, overrides the maxWait from the properties,
* set to -1 if you wish to use maxWait, 0 if you wish no wait time.
- * @return PooledConnection
- * @throws SQLException
+ * @param username The user name to use for the connection
+ * @param password The password for the connection
+ * @return a connection
+ * @throws SQLException Failed to get a connection
*/
private PooledConnection borrowConnection(int wait, String username, String password) throws SQLException {
@@ -696,8 +701,10 @@ public class ConnectionPool {
* Creates a JDBC connection and tries to connect to the database.
* @param now timestamp of when this was called
* @param notUsed Argument not used
+ * @param username The user name to use for the connection
+ * @param password The password for the connection
* @return a PooledConnection that has been connected
- * @throws SQLException
+ * @throws SQLException Failed to get a connection
*/
protected PooledConnection createConnection(long now, PooledConnection notUsed, String username, String password) throws SQLException {
//no connections where available we'll create one
@@ -748,7 +755,9 @@ public class ConnectionPool {
* Validates and configures a previously idle connection
* @param now - timestamp
* @param con - the connection to validate and configure
- * @return con
+ * @param username The user name to use for the connection
+ * @param password The password for the connection
+ * @return a connection
* @throws SQLException if a validation error happens
*/
protected PooledConnection borrowConnection(long now, PooledConnection con, String username, String password) throws SQLException {
@@ -831,7 +840,7 @@ public class ConnectionPool {
}
/**
* Terminate the current transaction for the given connection.
- * @param con
+ * @param con The connection
* @return <code>true</code> if the connection TX termination succeeded
* otherwise <code>false</code>
*/
@@ -858,7 +867,7 @@ public class ConnectionPool {
* Determines if a connection should be closed upon return to the pool.
* @param con - the connection
* @param action - the validation action that should be performed
- * @return true if the connection should be closed
+ * @return <code>true</code> if the connection should be closed
*/
protected boolean shouldClose(PooledConnection con, int action) {
if (con.getConnectionVersion() < getPoolVersion()) return true;
@@ -930,7 +939,7 @@ public class ConnectionPool {
/**
* Determines if a connection should be abandoned based on
* {@link PoolProperties#abandonWhenPercentageFull} setting.
- * @return true if the connection should be abandoned
+ * @return <code>true</code> if the connection should be abandoned
*/
protected boolean shouldAbandon() {
if (!poolProperties.isRemoveAbandoned()) return false;
@@ -1093,6 +1102,7 @@ public class ConnectionPool {
/**
* Create a new pooled connection object. Not connected nor validated.
+ * @param incrementCounter <code>true</code> to increment the connection count
* @return a pooled connection object
*/
protected PooledConnection create(boolean incrementCounter) {
@@ -1124,7 +1134,7 @@ public class ConnectionPool {
/**
* Hook to perform final actions on a pooled connection object once it has been disconnected and will be discarded
- * @param con
+ * @param con The connection
*/
protected void finalize(PooledConnection con) {
JdbcInterceptor handler = con.getHandler();
@@ -1136,7 +1146,8 @@ public class ConnectionPool {
/**
* Hook to perform final actions on a pooled connection object once it has been disconnected and will be discarded
- * @param con
+ * @param con The connection
+ * @param finalizing <code>true</code> if finalizing the connection
*/
protected void disconnectEvent(PooledConnection con, boolean finalizing) {
JdbcInterceptor handler = con.getHandler();
diff --git a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/DataSource.java b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/DataSource.java
index b002fbc..28beb7a 100644
--- a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/DataSource.java
+++ b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/DataSource.java
@@ -47,7 +47,7 @@ public class DataSource extends DataSourceProxy implements javax.sql.DataSource,
/**
* Constructs a DataSource object wrapping a connection
- * @param poolProperties
+ * @param poolProperties The pool properties
*/
public DataSource(PoolConfiguration poolProperties) {
super(poolProperties);
@@ -111,7 +111,7 @@ public class DataSource extends DataSourceProxy implements javax.sql.DataSource,
* Creates the ObjectName for the ConnectionPoolMBean object to be registered
* @param original the ObjectName for the DataSource
* @return the ObjectName for the ConnectionPoolMBean
- * @throws MalformedObjectNameException
+ * @throws MalformedObjectNameException Invalid object name
*/
public ObjectName createObjectName(ObjectName original) throws MalformedObjectNameException {
String domain = ConnectionPool.POOL_JMX_DOMAIN;
diff --git a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/DataSourceFactory.java b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/DataSourceFactory.java
index 3f96f80..480ad30 100644
--- a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/DataSourceFactory.java
+++ b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/DataSourceFactory.java
@@ -536,6 +536,7 @@ public class DataSourceFactory implements ObjectFactory {
* given properties.
*
* @param properties the datasource configuration properties
+ * @return the datasource
* @throws Exception if an error occurs creating the data source
*/
public DataSource createDataSource(Properties properties) throws Exception {
@@ -564,14 +565,14 @@ public class DataSourceFactory implements ObjectFactory {
log.warn("dataSourceJNDI property is configued, but local JNDI context is null.");
}
} catch (NamingException e) {
- log.debug("The name \""+poolProperties.getDataSourceJNDI()+"\" can not be found in the local context.");
+ log.debug("The name \""+poolProperties.getDataSourceJNDI()+"\" cannot be found in the local context.");
}
if (jndiDS==null) {
try {
context = new InitialContext();
jndiDS = context.lookup(poolProperties.getDataSourceJNDI());
} catch (NamingException e) {
- log.warn("The name \""+poolProperties.getDataSourceJNDI()+"\" can not be found in the InitialContext.");
+ log.warn("The name \""+poolProperties.getDataSourceJNDI()+"\" cannot be found in the InitialContext.");
}
}
if (jndiDS!=null) {
@@ -580,9 +581,9 @@ public class DataSourceFactory implements ObjectFactory {
}
/**
- * <p>Parse properties from the string. Format of the string must be [propertyName=property;]*<p>
- * @param propText
- * @return Properties
+ * Parse properties from the string. Format of the string must be [propertyName=property;]*.
+ * @param propText The properties string
+ * @return the properties
*/
protected static Properties getProperties(String propText) {
return PoolProperties.getProperties(propText,null);
diff --git a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/DataSourceProxy.java b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/DataSourceProxy.java
index 5997428..655ebc9 100644
--- a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/DataSourceProxy.java
+++ b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/DataSourceProxy.java
@@ -55,7 +55,7 @@ public class DataSourceProxy implements PoolConfiguration {
}
public DataSourceProxy(PoolConfiguration poolProperties) {
- if (poolProperties == null) throw new NullPointerException("PoolConfiguration can not be null.");
+ if (poolProperties == null) throw new NullPointerException("PoolConfiguration cannot be null.");
this.poolProperties = poolProperties;
}
@@ -74,7 +74,12 @@ public class DataSourceProxy implements PoolConfiguration {
}
/**
+ * Get a database connection.
* {@link javax.sql.DataSource#getConnection()}
+ * @param username The user name
+ * @param password The password
+ * @return the connection
+ * @throws SQLException Connection error
*/
public Connection getConnection(String username, String password) throws SQLException {
if (this.getPoolProperties().isAlternateUsernameAllowed()) {
@@ -92,8 +97,8 @@ public class DataSourceProxy implements PoolConfiguration {
/**
* Sets up the connection pool, by creating a pooling driver.
- * @return Driver
- * @throws SQLException
+ * @return the connection pool
+ * @throws SQLException Error creating pool
*/
public ConnectionPool createPool() throws SQLException {
if (pool != null) {
@@ -105,8 +110,6 @@ public class DataSourceProxy implements PoolConfiguration {
/**
* Sets up the connection pool, by creating a pooling driver.
- * @return Driver
- * @throws SQLException
*/
private synchronized ConnectionPool pCreatePool() throws SQLException {
if (pool != null) {
@@ -118,9 +121,11 @@ public class DataSourceProxy implements PoolConfiguration {
}
/**
+ * Get a database connection.
* {@link javax.sql.DataSource#getConnection()}
+ * @return the connection
+ * @throws SQLException Connection error
*/
-
public Connection getConnection() throws SQLException {
if (pool == null)
return createPool().getConnection();
@@ -130,7 +135,7 @@ public class DataSourceProxy implements PoolConfiguration {
/**
* Invokes an sync operation to retrieve the connection.
* @return a Future containing a reference to the connection when it becomes available
- * @throws SQLException
+ * @throws SQLException Connection error
*/
public Future<Connection> getConnectionAsync() throws SQLException {
if (pool == null)
@@ -139,7 +144,10 @@ public class DataSourceProxy implements PoolConfiguration {
}
/**
+ * Get a database connection.
* {@link javax.sql.XADataSource#getXAConnection()}
+ * @return the connection
+ * @throws SQLException Connection error
*/
public XAConnection getXAConnection() throws SQLException {
Connection con = getConnection();
@@ -156,7 +164,12 @@ public class DataSourceProxy implements PoolConfiguration {
}
/**
+ * Get a database connection.
* {@link javax.sql.XADataSource#getXAConnection(String, String)}
+ * @param username The user name
+ * @param password The password
+ * @return the connection
+ * @throws SQLException Connection error
*/
public XAConnection getXAConnection(String username, String password) throws SQLException {
Connection con = getConnection(username, password);
@@ -174,16 +187,22 @@ public class DataSourceProxy implements PoolConfiguration {
/**
+ * Get a database connection.
* {@link javax.sql.DataSource#getConnection()}
+ * @return the connection
+ * @throws SQLException Connection error
*/
public javax.sql.PooledConnection getPooledConnection() throws SQLException {
return (javax.sql.PooledConnection) getConnection();
}
/**
+ * Get a database connection.
* {@link javax.sql.DataSource#getConnection()}
* @param username unused
* @param password unused
+ * @return the connection
+ * @throws SQLException Connection error
*/
public javax.sql.PooledConnection getPooledConnection(String username,
String password) throws SQLException {
@@ -563,6 +582,8 @@ public class DataSourceProxy implements PoolConfiguration {
/**
* no-op
* {@link javax.sql.DataSource#getParentLogger}
+ * @return no return value
+ * @throws SQLFeatureNotSupportedException Unsupported
*/
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
throw new SQLFeatureNotSupportedException();
@@ -571,8 +592,9 @@ public class DataSourceProxy implements PoolConfiguration {
/**
* no-op
* {@link javax.sql.DataSource#getLogWriter}
+ * @return null
+ * @throws SQLException No exception
*/
- @SuppressWarnings("unused") // Has to match signature in DataSource
public PrintWriter getLogWriter() throws SQLException {
return null;
}
@@ -581,8 +603,9 @@ public class DataSourceProxy implements PoolConfiguration {
/**
* no-op
* {@link javax.sql.DataSource#setLogWriter(PrintWriter)}
+ * @param out Ignored
+ * @throws SQLException No exception
*/
- @SuppressWarnings("unused") // Has to match signature in DataSource
public void setLogWriter(PrintWriter out) throws SQLException {
// NOOP
}
@@ -590,6 +613,7 @@ public class DataSourceProxy implements PoolConfiguration {
/**
* no-op
* {@link javax.sql.DataSource#getLoginTimeout}
+ * @return the timeout
*/
public int getLoginTimeout() {
if (poolProperties == null) {
@@ -601,6 +625,7 @@ public class DataSourceProxy implements PoolConfiguration {
/**
* {@link javax.sql.DataSource#setLoginTimeout(int)}
+ * @param i The timeout value
*/
public void setLoginTimeout(int i) {
if (poolProperties == null) {
@@ -647,6 +672,7 @@ public class DataSourceProxy implements PoolConfiguration {
/**
* {@link #getIdle()}
+ * @return the number of established but idle connections
*/
public int getNumIdle() {
return getIdle();
diff --git a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/FairBlockingQueue.java b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/FairBlockingQueue.java
index c96249c..0418eb6 100644
--- a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/FairBlockingQueue.java
+++ b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/FairBlockingQueue.java
@@ -37,6 +37,7 @@ import java.util.concurrent.locks.ReentrantLock;
* <br>
* Not all of the methods of the {@link java.util.concurrent.BlockingQueue} are implemented.
*
+ * @param <E> Type of element in the queue
*/
public class FairBlockingQueue<E> implements BlockingQueue<E> {
diff --git a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/JdbcInterceptor.java b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/JdbcInterceptor.java
index e4a6935..c361735 100644
--- a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/JdbcInterceptor.java
+++ b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/JdbcInterceptor.java
@@ -119,7 +119,7 @@ public abstract class JdbcInterceptor implements InvocationHandler {
/**
* configures the next interceptor in the chain
- * @param next
+ * @param next The next chain item
*/
public void setNext(JdbcInterceptor next) {
this.next = next;
@@ -127,8 +127,8 @@ public abstract class JdbcInterceptor implements InvocationHandler {
/**
* Performs a string comparison, using references unless the useEquals property is set to true.
- * @param name1
- * @param name2
+ * @param name1 The first name
+ * @param name2 The second name
* @return true if name1 is equal to name2 based on {@link #useEquals}
*/
public boolean compare(String name1, String name2) {
@@ -143,9 +143,9 @@ public abstract class JdbcInterceptor implements InvocationHandler {
* Compares a method name (String) to a method (Method)
* {@link #compare(String,String)}
* Uses reference comparison unless the useEquals property is set to true
- * @param methodName
- * @param method
- * @return true if the name matches
+ * @param methodName The method name
+ * @param method The method
+ * @return <code>true</code> if the name matches
*/
public boolean compare(String methodName, Method method) {
return compare(methodName, method.getName());
@@ -186,7 +186,7 @@ public abstract class JdbcInterceptor implements InvocationHandler {
* Called during the creation of an interceptor
* The properties can be set during the configuration of an interceptor
* Override this method to perform type casts between string values and object properties
- * @param properties
+ * @param properties The properties
*/
public void setProperties(Map<String,InterceptorProperty> properties) {
this.properties = properties;
@@ -208,7 +208,7 @@ public abstract class JdbcInterceptor implements InvocationHandler {
/**
* Set to true if string comparisons (for the {@link #compare(String, Method)} and {@link #compare(String, String)} methods) should use the Object.equals(Object) method
* The default is false
- * @param useEquals
+ * @param useEquals <code>true</code> if equals will be used for comparisons
*/
public void setUseEquals(boolean useEquals) {
this.useEquals = useEquals;
diff --git a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/MultiLockFairBlockingQueue.java b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/MultiLockFairBlockingQueue.java
index e1572f1..f27a96b 100644
--- a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/MultiLockFairBlockingQueue.java
+++ b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/MultiLockFairBlockingQueue.java
@@ -41,6 +41,7 @@ import java.util.concurrent.locks.ReentrantLock;
* <br>
* Not all of the methods of the {@link java.util.concurrent.BlockingQueue} are implemented.
*
+ * @param <E> Type of element in the queue
*/
public class MultiLockFairBlockingQueue<E> implements BlockingQueue<E> {
diff --git a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/PoolConfiguration.java b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/PoolConfiguration.java
index 7c2d572..374f22c 100644
--- a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/PoolConfiguration.java
+++ b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/PoolConfiguration.java
@@ -55,8 +55,8 @@ public interface PoolConfiguration {
public int getAbandonWhenPercentageFull();
/**
- * Returns true if a fair queue is being used by the connection pool
- * @return true if a fair waiting queue is being used
+ * Returns <code>true</code> if a fair queue is being used by the connection pool
+ * @return <code>true</code> if a fair waiting queue is being used
*/
public boolean isFairQueue();
@@ -66,7 +66,7 @@ public interface PoolConfiguration {
* This uses the {@link FairBlockingQueue} implementation for the list of the idle connections.
* The default value is true.
* This flag is required when you want to use asynchronous connection retrieval.
- * @param fairQueue
+ * @param fairQueue <code>true</code> to use a fair queue
*/
public void setFairQueue(boolean fairQueue);
@@ -74,7 +74,7 @@ public interface PoolConfiguration {
* Property not used. Access is always allowed.
* Access can be achieved by calling unwrap on the pooled connection. see {@link javax.sql.DataSource} interface
* or call getConnection through reflection or cast the object as {@link javax.sql.PooledConnection}
- * @return true
+ * @return <code>true</code>
*/
public boolean isAccessToUnderlyingConnectionAllowed();
@@ -89,6 +89,7 @@ public interface PoolConfiguration {
* Format of the string is [propertyName=property;] <br>
* NOTE - The "user" and "password" properties will be passed explicitly, so they do not need to be included here.
* The default value is null.
+ * @return the connection properties
*/
public String getConnectionProperties();
@@ -108,7 +109,7 @@ public interface PoolConfiguration {
/**
* Overrides the database properties passed into the {@link java.sql.Driver#connect(String, Properties)} method.
- * @param dbProperties
+ * @param dbProperties The database properties
*/
public void setDbProperties(Properties dbProperties);
@@ -341,14 +342,14 @@ public interface PoolConfiguration {
/**
* Sets the password to establish the connection with.
* The password will be included as a database property with the name 'password'.
- * @param password
+ * @param password The password
* @see #getDbProperties()
*/
public void setPassword(String password);
/**
* @see #getName()
- * @return name
+ * @return the pool name
*/
public String getPoolName();
@@ -361,7 +362,7 @@ public interface PoolConfiguration {
/**
* Sets the username used to establish the connection with
* It will also be a property called 'user' in the database properties.
- * @param username
+ * @param username The user name
* @see #getDbProperties()
*/
public void setUsername(String username);
@@ -532,6 +533,7 @@ public interface PoolConfiguration {
/**
* The timeout in seconds before a connection validation queries fail.
* A value less than or equal to zero will disable this feature. Defaults to -1.
+ * @param validationQueryTimeout The timeout value
*/
public void setValidationQueryTimeout(int validationQueryTimeout);
@@ -559,6 +561,7 @@ public interface PoolConfiguration {
* Sets the validator object
* If this is a non null object, it will be used as a validator instead of the validationQuery
* If this is null, remove the usage of the validator.
+ * @param validator The validator object
*/
public void setValidator(Validator validator);
@@ -816,6 +819,7 @@ public interface PoolConfiguration {
/**
* @see PoolConfiguration#setCommitOnReturn(boolean)
+ * @return <code>true</code> if the pool should commit when a connection is returned to it
*/
public boolean getCommitOnReturn();
@@ -830,20 +834,21 @@ public interface PoolConfiguration {
/**
* @see PoolConfiguration#setRollbackOnReturn(boolean)
+ * @return <code>true</code> if the pool should rollback when a connection is returned to it
*/
public boolean getRollbackOnReturn();
/**
- * If set to true, the connection will be wrapped with facade that will disallow the connection to be used after
- * {@link java.sql.Connection#close()} is called. If set to true, after {@link java.sql.Connection#close()} all calls except
+ * If set to <code>true</code>, the connection will be wrapped with facade that will disallow the connection to be used after
+ * {@link java.sql.Connection#close()} is called. If set to <code>true</code>, after {@link java.sql.Connection#close()} all calls except
* {@link java.sql.Connection#close()} and {@link java.sql.Connection#isClosed()} will throw an exception.
- * @param useDisposableConnectionFacade
+ * @param useDisposableConnectionFacade <code>true</code> to use a facade
*/
public void setUseDisposableConnectionFacade(boolean useDisposableConnectionFacade);
/**
- * Returns true if this connection pool is configured to use a connection facade to prevent re-use of connection after
+ * Returns <code>true</code> if this connection pool is configured to use a connection facade to prevent re-use of connection after
* {@link java.sql.Connection#close()} has been invoked
- * @return true if {@link java.sql.Connection#close()} has been invoked.
+ * @return <code>true</code> if {@link java.sql.Connection#close()} has been invoked.
*/
public boolean getUseDisposableConnectionFacade();
@@ -884,6 +889,7 @@ public interface PoolConfiguration {
public void setIgnoreExceptionOnPreLoad(boolean ignoreExceptionOnPreLoad);
/**
+ * @return <code>true</code> to ignore exceptions
* @see PoolConfiguration#setIgnoreExceptionOnPreLoad(boolean)
*/
public boolean isIgnoreExceptionOnPreLoad();
diff --git a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/PoolProperties.java b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/PoolProperties.java
index 962a717..6a459ce 100644
--- a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/PoolProperties.java
+++ b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/PoolProperties.java
@@ -37,7 +37,7 @@ public class PoolProperties implements PoolConfiguration, Cloneable, Serializabl
public static final int DEFAULT_MAX_ACTIVE = 100;
- protected static AtomicInteger poolCounter = new AtomicInteger(0);
+ protected static final AtomicInteger poolCounter = new AtomicInteger(0);
private volatile Properties dbProperties = new Properties();
private volatile String url = null;
private volatile String driverClassName = null;
diff --git a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/PooledConnection.java b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/PooledConnection.java
index 9f3040c..75cb43a 100644
--- a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/PooledConnection.java
+++ b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/PooledConnection.java
@@ -136,7 +136,10 @@ public class PooledConnection {
/**
* @deprecated use {@link #shouldForceReconnect(String, String)}
- * method kept since it was public, to avoid changing interface. name was pooo
+ * method kept since it was public, to avoid changing interface.
+ * @param username The user name
+ * @param password The password
+ * @return <code>true</code>if the pool does not need to reconnect
*/
@Deprecated
public boolean checkUser(String username, String password) {
@@ -405,9 +408,9 @@ public class PooledConnection {
}
/**
- * Returns true if the connection pool is configured
+ * Returns <code>true</code> if the connection pool is configured
* to do validation for a certain action.
- * @param action
+ * @param action The validation action
*/
private boolean doValidate(int action) {
if (action == PooledConnection.VALIDATE_BORROW &&
@@ -429,9 +432,12 @@ public class PooledConnection {
return false;
}
- /**Returns true if the object is still valid. if not
+ /**
+ * Returns <code>true</code> if the object is still valid. if not
* the pool will call the getExpiredAction() and follow up with one
* of the four expired methods
+ * @param validateAction The value
+ * @return <code>true</code> if the connection is valid
*/
public boolean validate(int validateAction) {
return validate(validateAction,null);
@@ -604,7 +610,7 @@ public class PooledConnection {
/**
* Sets the pool configuration for this connection and connection pool.
* Object is shared with the {@link ConnectionPool}
- * @param poolProperties
+ * @param poolProperties The pool properties
*/
public void setPoolProperties(PoolConfiguration poolProperties) {
this.poolProperties = poolProperties;
diff --git a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/XADataSource.java b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/XADataSource.java
index ce829d3..2a2f374 100644
--- a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/XADataSource.java
+++ b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/XADataSource.java
@@ -28,7 +28,7 @@ public class XADataSource extends DataSource implements javax.sql.XADataSource {
/**
* Constructs a DataSource object wrapping a connection
- * @param poolProperties
+ * @param poolProperties The pool configuration
*/
public XADataSource(PoolConfiguration poolProperties) {
super(poolProperties);
diff --git a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/AbstractCreateStatementInterceptor.java b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/AbstractCreateStatementInterceptor.java
index 98b4dc4..11fc655 100644
--- a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/AbstractCreateStatementInterceptor.java
+++ b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/AbstractCreateStatementInterceptor.java
@@ -81,6 +81,7 @@ public abstract class AbstractCreateStatementInterceptor extends JdbcIntercepto
* @param method the method that was called. It will be one of the methods defined in {@link #STATEMENT_TYPES}
* @param args the arguments to the method
* @param statement the statement that the underlying connection created
+ * @param time Elapsed time
* @return a {@link java.sql.Statement} object
*/
public abstract Object createStatement(Object proxy, Method method, Object[] args, Object statement, long time);
diff --git a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/AbstractQueryReport.java b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/AbstractQueryReport.java
index 7510945..499839f 100644
--- a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/AbstractQueryReport.java
+++ b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/AbstractQueryReport.java
@@ -148,7 +148,7 @@ public abstract class AbstractQueryReport extends AbstractCreateStatementInterce
* @param idx - the index of the constructor
* @param clazz - the interface that the proxy will implement
* @return - returns a constructor used to create new instances
- * @throws NoSuchMethodException
+ * @throws NoSuchMethodException Constructor not found
*/
protected Constructor<?> getConstructor(int idx, Class<?> clazz) throws NoSuchMethodException {
if (constructors[idx]==null) {
diff --git a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/SlowQueryReport.java b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/SlowQueryReport.java
index a25bda6..1ea1ca6 100644
--- a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/SlowQueryReport.java
+++ b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/SlowQueryReport.java
@@ -48,7 +48,7 @@ public class SlowQueryReport extends AbstractQueryReport {
/**
* we will be keeping track of query stats on a per pool basis
*/
- protected static ConcurrentHashMap<String,ConcurrentHashMap<String,QueryStats>> perPoolStats =
+ protected static final ConcurrentHashMap<String,ConcurrentHashMap<String,QueryStats>> perPoolStats =
new ConcurrentHashMap<>();
/**
* the queries that are used for this interceptor.
@@ -215,7 +215,7 @@ public class SlowQueryReport extends AbstractQueryReport {
/**
* Sort QueryStats by last invocation time
- * @param queries
+ * @param queries The queries map
*/
protected void removeOldest(ConcurrentHashMap<String,QueryStats> queries) {
ArrayList<QueryStats> list = new ArrayList<>(queries.values());
diff --git a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/SlowQueryReportJmx.java b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/SlowQueryReportJmx.java
index ee0010a..e1b595c 100644
--- a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/SlowQueryReportJmx.java
+++ b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/SlowQueryReportJmx.java
@@ -64,7 +64,7 @@ public class SlowQueryReportJmx extends SlowQueryReport implements NotificationE
private static final Log log = LogFactory.getLog(SlowQueryReportJmx.class);
- protected static ConcurrentHashMap<String,SlowQueryReportJmxMBean> mbeans =
+ protected static final ConcurrentHashMap<String,SlowQueryReportJmxMBean> mbeans =
new ConcurrentHashMap<>();
@@ -99,7 +99,7 @@ public class SlowQueryReportJmx extends SlowQueryReport implements NotificationE
protected String poolName = null;
- protected static AtomicLong notifySequence = new AtomicLong(0);
+ protected static final AtomicLong notifySequence = new AtomicLong(0);
protected boolean notifyPool = true;
diff --git a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/StatementCache.java b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/StatementCache.java
index 568416d..646c631 100644
--- a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/StatementCache.java
+++ b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/StatementCache.java
@@ -186,6 +186,10 @@ public class StatementCache extends StatementDecoratorInterceptor {
}
/**
+ * @param sql The SQL to attempt to match to entires in the statement cache
+ *
+ * @return The CachedStatement for the given SQL
+ *
* @deprecated Unused. Will be removed in Tomcat 9
*/
@Deprecated
diff --git a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/StatementDecoratorInterceptor.java b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/StatementDecoratorInterceptor.java
index 25c96ec..c8179d6 100644
--- a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/StatementDecoratorInterceptor.java
+++ b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/StatementDecoratorInterceptor.java
@@ -69,7 +69,7 @@ public class StatementDecoratorInterceptor extends AbstractCreateStatementInterc
* @param clazz
* - the interface that the proxy will implement
* @return - returns a constructor used to create new instances
- * @throws NoSuchMethodException
+ * @throws NoSuchMethodException Constructor not found
*/
protected Constructor<?> getConstructor(int idx, Class<?> clazz) throws NoSuchMethodException {
if (constructors[idx] == null) {
@@ -143,6 +143,9 @@ public class StatementDecoratorInterceptor extends AbstractCreateStatementInterc
* @param sql The sql of of the statement
*
* @return A new proxy for the Statement
+ * @throws InstantiationException Couldn't instantiate object
+ * @throws IllegalAccessException Inaccessible constructor
+ * @throws InvocationTargetException Exception thrown from constructor
*/
protected Object createDecorator(Object proxy, Method method, Object[] args,
Object statement, Constructor<?> constructor, String sql)
diff --git a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/mbeans-descriptors.xml b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/mbeans-descriptors.xml
index 846b411..d086daf 100644
--- a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/mbeans-descriptors.xml
+++ b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/mbeans-descriptors.xml
@@ -1,4 +1,4 @@
-<?xml version="1.0"?>
+<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
diff --git a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/jmx/ConnectionPool.java b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/jmx/ConnectionPool.java
index dd3cdbf..a27b5fe 100644
--- a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/jmx/ConnectionPool.java
+++ b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/jmx/ConnectionPool.java
@@ -98,8 +98,8 @@ public class ConnectionPool extends NotificationBroadcasterSupport implements Co
/**
* Return true if the notification was sent successfully, false otherwise.
- * @param type
- * @param message
+ * @param type The notification type
+ * @param message The message
* @return true if the notification succeeded
*/
public boolean notify(final String type, String message) {
diff --git a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/mbeans-descriptors.xml b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/mbeans-descriptors.xml
index 7ea4278..191cb03 100644
--- a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/mbeans-descriptors.xml
+++ b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/mbeans-descriptors.xml
@@ -1,4 +1,4 @@
-<?xml version="1.0"?>
+<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
diff --git a/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/pool/interceptor/InduceSlowQuery.java b/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/pool/interceptor/InduceSlowQuery.java
index 19a9eff..5c2be9a 100644
--- a/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/pool/interceptor/InduceSlowQuery.java
+++ b/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/pool/interceptor/InduceSlowQuery.java
@@ -21,7 +21,7 @@ import java.lang.reflect.Method;
import java.security.SecureRandom;
public class InduceSlowQuery extends AbstractQueryReport {
- public static SecureRandom random = new SecureRandom();
+ public static final SecureRandom random = new SecureRandom();
public InduceSlowQuery() {
// TODO Auto-generated constructor stub
diff --git a/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/pool/interceptor/TestInterceptor.java b/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/pool/interceptor/TestInterceptor.java
index 2509381..16fb3c7 100644
--- a/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/pool/interceptor/TestInterceptor.java
+++ b/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/pool/interceptor/TestInterceptor.java
@@ -27,7 +27,7 @@ import org.apache.tomcat.jdbc.pool.PooledConnection;
public class TestInterceptor extends JdbcInterceptor {
public static boolean poolstarted = false;
public static boolean poolclosed = false;
- public static AtomicInteger instancecount = new AtomicInteger(0);
+ public static final AtomicInteger instancecount = new AtomicInteger(0);
@Override
public void poolClosed(ConnectionPool pool) {
diff --git a/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/test/CreateTestTable.java b/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/test/CreateTestTable.java
index adae5ef..e375bcd 100644
--- a/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/test/CreateTestTable.java
+++ b/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/test/CreateTestTable.java
@@ -118,7 +118,7 @@ public class CreateTestTable extends DefaultTestCase {
con.close();
}
- public static Random random = new Random(System.currentTimeMillis());
+ public static final Random random = new Random(System.currentTimeMillis());
public static String getRandom() {
StringBuilder s = new StringBuilder(256);
for (int i=0;i<254; i++) {
diff --git a/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/test/TestJdbcInterceptorConfigParsing.java b/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/test/TestJdbcInterceptorConfigParsing.java
index 0572a57..f09ad5c 100644
--- a/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/test/TestJdbcInterceptorConfigParsing.java
+++ b/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/test/TestJdbcInterceptorConfigParsing.java
@@ -105,13 +105,11 @@ public class TestJdbcInterceptorConfigParsing {
assertEquals(emptyParmValProps.get("parm1").getValue(), "");
}
- /**
+ /*
* Some of these should probably be handled more cleanly by the parser, but a few known
* exception scenarios are presented here just to document current behavior. In many cases
* failure in parsing will just be propagated to a definition that will fail later
* when instantiated. Should we be failing faster (and with more detail)?
- *
- * @throws Exception
*/
@Test
public void testExceptionOrNot() throws Exception {
diff --git a/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/test/TestStatementCache.java b/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/test/TestStatementCache.java
index b6bb4d3..3a8778a 100644
--- a/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/test/TestStatementCache.java
+++ b/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/test/TestStatementCache.java
@@ -166,7 +166,6 @@ public class TestStatementCache extends DefaultTestCase {
Connection con1 = datasource.getConnection();
Connection con2 = datasource.getConnection();
for (int i=0; i<120; i++) {
- @SuppressWarnings("resource") // Connections are closed below
Connection con = (i%2==0)?con1:con2;
PreparedStatement ps = con.prepareStatement("select "+i);
ps.close();
diff --git a/modules/tomcat-lite/.classpath b/modules/tomcat-lite/.classpath
deleted file mode 100644
index bfb9921..0000000
--- a/modules/tomcat-lite/.classpath
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<classpath>
- <classpathentry excluding="org/apache/tomcat/lite/servlet/ServletApi30.java" kind="src" path="java"/>
- <classpathentry excluding="org/apache/coyote/servlet/" kind="src" path="test"/>
- <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
- <classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/3"/>
- <classpathentry kind="lib" path="target/lib/annotations-api.jar"/>
- <classpathentry kind="lib" path="target/lib/ant-launcher.jar"/>
- <classpathentry kind="lib" path="target/lib/ant.jar"/>
- <classpathentry kind="lib" path="target/lib/asm-tree.jar"/>
- <classpathentry kind="lib" path="target/lib/asm.jar"/>
- <classpathentry kind="lib" path="target/lib/catalina.jar"/>
- <classpathentry kind="lib" path="target/lib/commons-codec.jar"/>
- <classpathentry kind="lib" path="target/lib/coyote.jar"/>
- <classpathentry kind="lib" path="target/lib/el-api.jar"/>
- <classpathentry kind="lib" path="target/lib/jasper-el.jar"/>
- <classpathentry kind="lib" path="target/lib/jasper-jdt.jar"/>
- <classpathentry kind="lib" path="target/lib/jasper.jar"/>
- <classpathentry kind="lib" path="target/lib/jsp-api.jar"/>
- <classpathentry kind="lib" path="target/lib/juli.jar"/>
- <classpathentry kind="lib" path="target/lib/junit.jar"/>
- <classpathentry kind="lib" path="target/lib/jzlib.jar"/>
- <classpathentry kind="lib" path="target/lib/servlet-api.jar"/>
- <classpathentry kind="output" path="output/classes"/>
-</classpath>
diff --git a/modules/tomcat-lite/.project b/modules/tomcat-lite/.project
deleted file mode 100644
index b4c826d..0000000
--- a/modules/tomcat-lite/.project
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<projectDescription>
- <name>tomcat-lite</name>
- <comment></comment>
- <projects>
- </projects>
- <buildSpec>
- <buildCommand>
- <name>org.eclipse.jdt.core.javabuilder</name>
- <arguments>
- </arguments>
- </buildCommand>
- </buildSpec>
- <natures>
- <nature>org.eclipse.jdt.core.javanature</nature>
- </natures>
-</projectDescription>
diff --git a/modules/tomcat-lite/build.xml b/modules/tomcat-lite/build.xml
deleted file mode 100644
index d2c79dd..0000000
--- a/modules/tomcat-lite/build.xml
+++ /dev/null
@@ -1,319 +0,0 @@
-<?xml version="1.0"?>
-<project name="Tomcat Lite" default="tomcat-lite.jar"
- xmlns:ivy="antlib:org.apache.ivy.ant"
- basedir=".">
-
- <property file="${user.home}/build.properties" />
- <property file="${basedir}/build.properties" />
- <property file="${basedir}/build.properties.default" />
-
- <property name="tomcat.base" value="${basedir}/../.." />
- <property name="tomcat.src" value="${tomcat.base}/java" />
- <property name="tomcat.build" value="${tomcat.base}/output/build" />
-
- <property name="tomcat.lite.src" value="${basedir}/" />
- <property name="classes" value="${basedir}/target/tomcat-lite/classes" />
- <property name="test-classes" value="${basedir}/target/tomcat-lite/test-classes" />
- <property name="http.test-classes" value="${basedir}/target/tomcat-lite/http-test-classes" />
- <property name="jar.dir" value="${basedir}/target/tomcat-lite/" />
- <property name="MAIN" value="org.apache.tomcat.integration.simple.Main"/>
- <property name="compile.source" value="1.6"/>
- <property name="compile.debug" value="true"/>
-
- <!-- All Ivy downloads -->
- <path id='lite-classpath'>
- <fileset dir='target/lib' includes="*.jar" />
- </path>
-
- <path id='head-classpath'>
- <pathelement location="${tomcat.build}/../classes" />
- <pathelement location="target/lib/asm.jar" />
- <pathelement location="target/lib/asm-tree.jar" />
- <pathelement location="target/lib/junit.jar"/>
- <pathelement location="target/lib/commons-codec.jar"/>
- </path>
-
- <target name="http" depends="http.compile,http.test,http.pack"/>
-
- <target name="http.compile"
- description="Compile only the HTTP connector classes, no servlets">
- <mkdir dir="${classes}"/>
- <javac destdir="${classes}"
- debug="${compile.debug}"
- deprecation="${compile.deprecation}"
- source="${compile.source}"
- encoding="ISO-8859-1">
- <classpath refid="lite-classpath" />
- <src path="${tomcat.lite.src}/java" />
- <classpath refid="head-classpath" />
- <exclude name="org/apache/tomcat/servlets/**"/>
- <exclude name="org/apache/tomcat/lite/servlet/**"/>
- </javac>
- <copy todir="${classes}">
- <fileset dir="${tomcat.lite.src}/java"
- includes="**/*.properties **/*.xml" />
- </copy>
- </target>
-
- <target name="http.test.compile"
- description="Test only the HTTP connector classes, no servlets">
- <mkdir dir="${http.test-classes}"/>
- <javac destdir="${http.test-classes}"
- debug="${compile.debug}"
- deprecation="${compile.deprecation}"
- source="${compile.source}"
- encoding="ISO-8859-1"
- >
- <classpath refid="lite-classpath" />
- <classpath refid="head-classpath" />
- <classpath path="${classes}" />
- <src path="${tomcat.lite.src}/test" />
- <exclude name="org/apache/tomcat/lite/servlet/**"/>
- <exclude name="org/apache/coyote/lite/**"/>
- <exclude name="org/apache/tomcat/test/watchdog/**"/>
- </javac>
- <copy todir="${http.test-classes}">
- <fileset dir="${tomcat.lite.src}/test"
- includes="**/*.properties **/*.xml **/*.bin **/*.keystore **/*.cert **/*.der" />
- </copy>
- </target>
-
- <target name="http.test" depends="http.test.compile" >
- <!-- also: perTest(default) -->
- <junit printsummary="on" fork="once"
- timeout="600000" maxmemory="1G" outputtoformatters="no"
- >
- <classpath refid="lite-classpath" />
- <classpath refid="head-classpath" />
- <classpath path="${http.test-classes}" />
- <classpath path="${classes}" />
-
- <formatter type="brief" usefile="false" />
-
- <batchtest>
- <fileset dir="test" >
- <!-- Include all by default -->
- <include name="**/*Test.java" />
- <include name="**/*Tests.java" />
- <!-- Exclude TestAll ortherwise there will be duplicated -->
- <exclude name="**/TestAll.java" />
- <!-- Exclude helper classes -->
- <exclude name="**/Tester*.java" />
-
- <exclude name="org/apache/tomcat/lite/servlet/**"/>
- <exclude name="org/apache/coyote/lite/**"/>
- <exclude name="org/apache/tomcat/test/watchdog/**"/>
- </fileset>
- </batchtest>
- </junit>
- </target>
-
- <target name="runtest" depends="http.test.compile">
- <junit printsummary="withOutAndErr" fork="no" dir="${tomcat.base}">
- <sysproperty key="tests" value="${tests}"/>
- <classpath refid="lite-classpath" />
- <classpath refid="head-classpath" />
- <classpath path="${http.test-classes}" />
- <classpath path="${classes}" />
-
- <formatter type="plain" usefile="false" />
-
- <batchtest>
- <fileset dir="test" >
- <include name="**/${test}.java" />
- </fileset>
- </batchtest>
- </junit>
-
- </target>
-
- <target name="http.pack"
- description="Pack the HTTP client and connector">
- </target>
-
- <target name="compile"
- description="Build all classes against tomcat head.">
- <mkdir dir="${classes}"/>
- <javac destdir="${classes}"
- debug="${compile.debug}"
- deprecation="${compile.deprecation}"
- source="${compile.source}"
- encoding="ISO-8859-1">
- <classpath refid="lite-classpath" />
- <src path="${tomcat.lite.src}/java" />
- <classpath refid="head-classpath" />
- <exclude name="**/ServletApi25.java"/>
- </javac>
- <copy todir="${classes}">
- <fileset dir="${tomcat.lite.src}/java"
- includes="**/*.properties **/*.xml" />
- </copy>
- </target>
-
- <target name="compile25"
- description="Build against 2.5 servlet API, similar with the maven">
- <mkdir dir="${classes}" />
- <javac destdir="${classes}"
- debug="${compile.debug}"
- deprecation="${compile.deprecation}"
- source="${compile.source}"
- encoding="ISO-8859-1">
- <classpath refid="lite-classpath" />
- <src path="${tomcat.lite.src}/java" />
- <exclude name="**/ServletApi30.java"/>
- <exclude name="org/apache/tomcat/coyote/servlet/*.java"/>
- </javac>
- <copy todir="${classes}">
- <fileset dir="${tomcat.lite.src}/java"
- includes="**/*.properties **/*.xml" />
- </copy>
- </target>
-
- <target name="test" depends="test30,test25"/>
-
- <target name="test30" depends="compile">
- <mkdir dir="${test-classes}"/>
- <javac destdir="${test-classes}"
- debug="${compile.debug}"
- deprecation="${compile.deprecation}"
- source="${compile.source}"
- encoding="ISO-8859-1"
- >
- <classpath refid="lite-classpath" />
- <classpath refid="head-classpath" />
- <classpath path="${classes}" />
- <src path="${tomcat.lite.src}/test" />
- </javac>
- <copy todir="${test-classes}">
- <fileset dir="${tomcat.lite.src}/test"
- includes="**/*.properties **/*.xml **/*.keystore" />
- </copy>
-
- <!-- Need to run it in tomcat to find output/build/webapps -->
- <junit printsummary="yes" fork="yes" dir="${tomcat.base}"
- >
- <classpath refid="lite-classpath" />
- <classpath refid="head-classpath" />
- <classpath path="${test-classes}" />
- <classpath path="${classes}" />
-
- <formatter type="plain" usefile="false" />
-
- <batchtest>
- <fileset dir="test" >
- <!-- Include all by default -->
- <include name="**/*Test.java" />
- <include name="**/*Tests.java" />
- <!-- Exclude TestAll ortherwise there will be duplicated -->
- <exclude name="**/TestAll.java" />
- <!-- Exclude helper classes -->
- <exclude name="**/Tester*.java" />
- </fileset>
- </batchtest>
- </junit>
- </target>
-
- <target name="test25" depends="compile25">
- <mkdir dir="${test-classes}"/>
- <javac destdir="${test-classes}"
- debug="${compile.debug}"
- deprecation="${compile.deprecation}"
- source="${compile.source}"
- encoding="ISO-8859-1"
- >
- <classpath refid="lite-classpath" />
- <classpath path="${classes}" />
- <classpath path="target/lib/junit.jar"/>
- <classpath path="target/lib/commons-codec.jar"/>
- <src path="${tomcat.lite.src}/test" />
- <exclude name="org/apache/coyote/**"/>
- </javac>
- <copy todir="${test-classes}">
- <fileset dir="${tomcat.lite.src}/test"
- includes="**/*.properties **/*.xml **/*.keystore" />
- </copy>
-
- <junit printsummary="yes" fork="yes">
- <classpath refid="lite-classpath" />
- <classpath path="${test-classes}" />
- <classpath path="${classes}" />
-
- <formatter type="plain" usefile="false" />
-
- <batchtest>
- <fileset dir="test" >
- <exclude name="org/apache/coyote/**" />
- <!-- Include all by default -->
- <include name="**/*Test.java" />
- <include name="**/*Tests.java" />
- <!-- Exclude TestAll ortherwise there will be duplicated -->
- <exclude name="**/TestAll.java" />
- <!-- Exclude helper classes -->
- <exclude name="**/Tester*.java" />
- </fileset>
- </batchtest>
- </junit>
- </target>
-
- <target name="clean">
- <delete dir="${classes}" includes="**" />
- <delete dir="${jar.dir}" includes="**" />
- <delete dir="${jar.dir}/jar" includes="**" />
- </target>
-
- <target name="tomcat-lite.jar" depends="compile,pack_tomcat-lite.jar" />
-
- <target name="pack_tomcat-lite.jar">
- <mkdir dir="${jar.dir}/jar" />
- <jar destfile="${jar.dir}/jar/tomcat-lite.jar">
- <manifest>
- <attribute name="Main-Class" value="${MAIN}"/>
- </manifest>
- <fileset dir="${classes}">
- <include name ="org/apache/tomcat/lite/**" />
- <include name ="org/apache/tomcat/servlets/**" />
- </fileset>
- </jar>
- </target>
-
- <target name="run">
- <java jar="${jar.dir}/tomcat-lite.jar"/>
- </target>
-
- <!-- Boilreplate for dependencies -->
-
- <property name="ivy.install.version" value="2.1.0" />
- <condition property="ivy.home" value="${env.IVY_HOME}">
- <isset property="env.IVY_HOME" />
- </condition>
- <property name="ivy.home" value="${basedir}/target/ivy" />
- <property name="ivy.jar.dir" value="${ivy.home}/lib" />
- <property name="ivy.jar.file" value="${ivy.jar.dir}/ivy.jar" />
- <available file="${ivy.jar.file}" property="ivy.exist"/>
-
- <target name="download-ivy" unless="ivy.exist">
- <mkdir dir="${ivy.jar.dir}"/>
- <get src="http://repo2.maven.org/maven2/org/apache/ivy/ivy/${ivy.install.version}/ivy-${ivy.install.version}.jar"
- dest="${ivy.jar.file}" usetimestamp="true"/>
- </target>
-
- <target name="init-ivy" depends="download-ivy">
- <!-- try to load ivy here from ivy home, in case the user has not already dropped
- it into ant's lib dir (note that the latter copy will always take precedence).
- We will not fail as long as local lib dir exists (it may be empty) and
- ivy is in at least one of ant's lib dir or the local lib dir. -->
- <path id="ivy.lib.path">
- <fileset dir="${ivy.jar.dir}" includes="*.jar"/>
-
- </path>
- <taskdef resource="org/apache/ivy/ant/antlib.xml"
- uri="antlib:org.apache.ivy.ant" classpathref="ivy.lib.path"/>
- </target>
-
- <target name="download" depends="init-ivy">
- <mkdir dir="target/lib"/>
- <ivy:resolve file="pom.xml" conf="compile" />
- <ivy:retrieve pattern="target/lib/[artifact].[ext]"/>
- </target>
-
-</project>
diff --git a/modules/tomcat-lite/ivy.xml b/modules/tomcat-lite/ivy.xml
deleted file mode 100644
index 2034732..0000000
--- a/modules/tomcat-lite/ivy.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<?xml version="1.0" encoding="ISO-8859-1"?>
-<!--
- Licensed to the Apache Software Foundation (ASF) under one
- or more contributor license agreements. See the NOTICE file
- distributed with this work for additional information
- regarding copyright ownership. The ASF licenses this file
- to you under the Apache License, Version 2.0 (the
- "License"); you may not use this file except in compliance
- with the License. You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing,
- software distributed under the License is distributed on an
- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- KIND, either express or implied. See the License for the
- specific language governing permissions and limitations
- under the License.
--->
-<ivy-module version="2.0"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:noNamespaceSchemaLocation="http://ant.apache.org/ivy/schemas/ivy.xsd">
- <info
- organisation="org.apache.tomcat.lite"
- module="tomcat-lite"
- status="integration">
- </info>
-
- <dependencies>
-
- </dependencies>
-</ivy-module>
diff --git a/modules/tomcat-lite/java/org/apache/coyote/lite/LiteProtocolHandler.java b/modules/tomcat-lite/java/org/apache/coyote/lite/LiteProtocolHandler.java
deleted file mode 100644
index c7817d4..0000000
--- a/modules/tomcat-lite/java/org/apache/coyote/lite/LiteProtocolHandler.java
+++ /dev/null
@@ -1,426 +0,0 @@
-package org.apache.coyote.lite;
-
-import java.io.IOException;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Map;
-
-import org.apache.coyote.ActionCode;
-import org.apache.coyote.ActionHook;
-import org.apache.coyote.Adapter;
-import org.apache.coyote.InputBuffer;
-import org.apache.coyote.OutputBuffer;
-import org.apache.coyote.ProtocolHandler;
-import org.apache.coyote.Request;
-import org.apache.coyote.Response;
-import org.apache.tomcat.lite.http.HttpClient;
-import org.apache.tomcat.lite.http.HttpChannel;
-import org.apache.tomcat.lite.http.HttpConnectionPool;
-import org.apache.tomcat.lite.http.HttpConnector;
-import org.apache.tomcat.lite.http.HttpRequest;
-import org.apache.tomcat.lite.http.HttpResponse;
-import org.apache.tomcat.lite.http.HttpServer;
-import org.apache.tomcat.lite.http.MultiMap;
-import org.apache.tomcat.lite.http.HttpChannel.HttpService;
-import org.apache.tomcat.lite.http.HttpConnectionPool.RemoteServer;
-import org.apache.tomcat.lite.http.HttpConnector.HttpChannelEvents;
-import org.apache.tomcat.lite.http.HttpConnector.HttpConnection;
-import org.apache.tomcat.lite.http.MultiMap.Entry;
-import org.apache.tomcat.lite.io.CBuffer;
-import org.apache.tomcat.lite.io.IOConnector;
-import org.apache.tomcat.lite.io.SocketConnector;
-import org.apache.tomcat.lite.io.SslProvider;
-import org.apache.tomcat.util.buf.ByteChunk;
-import org.apache.tomcat.util.buf.MessageBytes;
-import org.apache.tomcat.util.http.MimeHeaders;
-import org.apache.tomcat.util.modeler.Registry;
-
-/**
- * Work in progress - use the refactored http as a coyote connector.
- * Just basic requests work right now - need to implement all the
- * methods of coyote.
- *
- *
- * @author Costin Manolache
- */
-public class LiteProtocolHandler implements ProtocolHandler {
-
- Adapter adapter;
- Map<String, Object> attributes = new HashMap<String, Object>();
-
-
- HttpConnector httpConnServer;
- int port = 8999;
-
- // Tomcat JMX integration
- Registry registry;
-
- public LiteProtocolHandler() {
- }
-
- @Override
- public void destroy() throws Exception {
- }
-
- @Override
- public Adapter getAdapter() {
- return adapter;
- }
-
- @Override
- public Object getAttribute(String name) {
- // TODO: dynamic
- return attributes.get(name);
- }
-
- @Override
- public Iterator<String> getAttributeNames() {
- return attributes.keySet().iterator();
- }
-
- @Override
- public void init() throws Exception {
- registry = Registry.getRegistry(null, null);
- httpConnServer = HttpServer.newServer(port);
-
- httpConnServer.getDispatcher().setDefaultService(new HttpService() {
- @Override
- public void service(HttpRequest httpReq, HttpResponse httpRes)
- throws IOException {
- coyoteService(httpReq, httpRes);
- }
-
- });
- final String base = "" + port;
- bind("Httpconnector-" + port, httpConnServer);
- bind("HttpconnectorPool-" + port, httpConnServer.cpool);
- IOConnector io = httpConnServer.getIOConnector();
- int ioLevel = 0;
- while (io != null) {
- bind("IOConnector-" + (ioLevel++) + "-" + base, io);
- if (io instanceof SocketConnector) {
- bind("NioThread-" + base,
- ((SocketConnector) io).getSelector());
-
- }
- io = io.getNet();
- }
- httpConnServer.cpool.setEvents(new HttpConnectionPool.HttpConnectionPoolEvents() {
-
- @Override
- public void closedConnection(RemoteServer host, HttpConnection con) {
- unbind("HttpConnection-" + base + "-" + con.getId());
- }
-
- @Override
- public void newConnection(RemoteServer host, HttpConnection con) {
- bind("HttpConnection-" + base + "-" + con.getId(), con);
- }
-
- @Override
- public void newTarget(RemoteServer host) {
- bind("AsyncHttp-" + base + "-" + host.target, host);
- }
-
- @Override
- public void targetRemoved(RemoteServer host) {
- unbind("AsyncHttp-" + base + "-" + host.target);
- }
-
- });
-
- httpConnServer.setOnCreate(new HttpChannelEvents() {
- @Override
- public void onCreate(HttpChannel data, HttpConnector extraData)
- throws IOException {
- bind("AsyncHttp-" + base + "-" + data.getId(), data);
- }
- @Override
- public void onDestroy(HttpChannel data, HttpConnector extraData)
- throws IOException {
- unbind("AsyncHttp-" + base + "-" + data.getId());
- }
- });
-
- // TODO: process attributes via registry !!
-
- }
-
- private void bind(String name, Object o) {
- try {
- registry.registerComponent(o, "TomcatLite:name=" + name, null);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
-
- private void unbind(String name) {
- registry.unregisterComponent("name=" + name);
- }
-
- @Override
- public void pause() throws Exception {
- }
-
- @Override
- public void resume() throws Exception {
- }
-
- @Override
- public void setAdapter(Adapter adapter) {
- this.adapter = adapter;
-
- }
-
- @Override
- public void setAttribute(String name, Object value) {
- attributes.put(name, value);
- }
-
- @Override
- public void start() throws Exception {
- httpConnServer.start();
- }
-
- public void setPort(int port) {
- this.port = port;
- }
-
- /**
- * Wrap old tomcat buffer to lite buffer.
- */
- private void wrap(MessageBytes dest, CBuffer buffer) {
- dest.setChars(buffer.array(), buffer.position(),
- buffer.length());
- }
-
- /**
- * Main lite service method, will wrap to coyote request
- */
- private void coyoteService(final HttpRequest httpReq, final HttpResponse httpRes) {
- // TODO: reuse, per req
- RequestData rc = new RequestData();
- rc.init(httpReq, httpRes);
-
- try {
- adapter.service(rc.req, rc.res);
- } catch (Exception e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
-
- /**
- * ActionHook implementation, include coyote request/response objects.
- */
- public class RequestData implements ActionHook {
- private final class LiteOutputBuffer implements OutputBuffer {
- @Override
- public int doWrite(org.apache.tomcat.util.buf.ByteChunk chunk,
- Response response) throws IOException {
- httpRes.getBody().append(chunk.getBuffer(), chunk.getStart(),
- chunk.getLength());
- return chunk.getLength();
- }
- }
-
- OutputBuffer outputBuffer = new LiteOutputBuffer();
- // TODO: recycle, etc.
- Request req = new Request();
-
- Response res = new Response();
- HttpResponse httpRes;
- HttpRequest httpReq;
-
- InputBuffer inputBuffer = new InputBuffer() {
- @Override
- public int doRead(ByteChunk bchunk, Request request)
- throws IOException {
- httpReq.getBody().waitData(httpReq.getHttpChannel().getIOTimeout());
- int rd =
- httpReq.getBody().read(bchunk.getBytes(),
- bchunk.getStart(), bchunk.getBytes().length);
- if (rd > 0) {
- bchunk.setEnd(bchunk.getEnd() + rd);
- }
- return rd;
- }
- };
-
- public RequestData() {
- req.setInputBuffer(inputBuffer);
- res.setOutputBuffer(outputBuffer);
- req.setResponse(res);
- res.setRequest(req);
- res.setHook(this);
- }
-
- public void init(HttpRequest httpReq, HttpResponse httpRes) {
- this.httpRes = httpRes;
- this.httpReq = httpReq;
- // TODO: turn http request into a coyote request - copy all fields,
- // add hooks where needed.
-
- wrap(req.decodedURI(), httpReq.decodedURI());
- wrap(req.method(), httpReq.method());
- wrap(req.protocol(), httpReq.protocol());
- wrap(req.requestURI(), httpReq.requestURI());
- wrap(req.queryString(), httpReq.queryString());
-
- req.setServerPort(httpReq.getServerPort());
- req.serverName().setString(req.localName().toString());
-
- MultiMap mimeHeaders = httpReq.getMimeHeaders();
- MimeHeaders coyoteHeaders = req.getMimeHeaders();
- for (int i = 0; i < mimeHeaders.size(); i++ ) {
- Entry entry = mimeHeaders.getEntry(i);
- MessageBytes val =
- coyoteHeaders.addValue(entry.getName().toString());
- val.setString(entry.getValue().toString());
- }
- }
-
- /**
- * Send an action to the connector.
- *
- * @param actionCode Type of the action
- * @param param Action parameter
- */
- public void action(ActionCode actionCode, Object param) {
-
- if (actionCode == ActionCode.ACTION_POST_REQUEST) {
- commit(); // make sure it's sent - on errors
- } else if (actionCode == ActionCode.ACTION_COMMIT) {
- commit();
- } else if (actionCode == ActionCode.ACTION_ACK) {
- // Done automatically by http connector
- } else if (actionCode == ActionCode.ACTION_CLIENT_FLUSH) {
- try {
- httpReq.send();
- } catch (IOException e) {
- httpReq.getHttpChannel().abort(e);
- res.setErrorException(e);
- }
-
- } else if (actionCode == ActionCode.ACTION_CLOSE) {
- // Close
-
- // End the processing of the current request, and stop any further
- // transactions with the client
-
-// comet = false;
-// try {
-// outputBuffer.endRequest();
-// } catch (IOException e) {
-// // Set error flag
-// error = true;
-// }
-
- } else if (actionCode == ActionCode.ACTION_RESET) {
- // Reset response
- // Note: This must be called before the response is committed
- httpRes.getBody().clear();
-
- } else if (actionCode == ActionCode.ACTION_CUSTOM) {
-
- // Do nothing
-
- } else if (actionCode == ActionCode.ACTION_REQ_HOST_ADDR_ATTRIBUTE) {
- req.remoteAddr().setString(httpReq.remoteAddr().toString());
- } else if (actionCode == ActionCode.ACTION_REQ_LOCAL_NAME_ATTRIBUTE) {
- req.localName().setString(httpReq.localName().toString());
- } else if (actionCode == ActionCode.ACTION_REQ_HOST_ATTRIBUTE) {
- req.remoteHost().setString(httpReq.remoteHost().toString());
- } else if (actionCode == ActionCode.ACTION_REQ_LOCAL_ADDR_ATTRIBUTE) {
- req.localAddr().setString(httpReq.localAddr().toString());
- } else if (actionCode == ActionCode.ACTION_REQ_REMOTEPORT_ATTRIBUTE) {
- req.setRemotePort(httpReq.getRemotePort());
- } else if (actionCode == ActionCode.ACTION_REQ_LOCALPORT_ATTRIBUTE) {
- req.setLocalPort(httpReq.getLocalPort());
- } else if (actionCode == ActionCode.ACTION_REQ_SSL_ATTRIBUTE ) {
-
- Object sslAtt = httpReq.getHttpChannel().getNet().getAttribute(SslProvider.ATT_SSL_CIPHER);
- req.setAttribute("javax.servlet.request.cipher_suite", sslAtt);
-
- sslAtt = httpReq.getHttpChannel().getNet().getAttribute(SslProvider.ATT_SSL_KEY_SIZE);
- req.setAttribute("javax.servlet.request.key_size", sslAtt);
-
- sslAtt = httpReq.getHttpChannel().getNet().getAttribute(SslProvider.ATT_SSL_SESSION_ID);
- req.setAttribute("javax.servlet.request.ssl_session", sslAtt);
-
- } else if (actionCode == ActionCode.ACTION_REQ_SSL_CERTIFICATE) {
-
- Object cert = httpReq.getHttpChannel().getNet().getAttribute(SslProvider.ATT_SSL_CERT);
- req.setAttribute("javax.servlet.request.X509Certificate", cert);
-
- } else if (actionCode == ActionCode.ACTION_REQ_SET_BODY_REPLAY) {
- ByteChunk body = (ByteChunk) param;
- httpReq.getBody().clear();
- try {
- httpReq.getBody().append(body.getBuffer(), body.getStart(), body.getLength());
- } catch (IOException e) {
- e.printStackTrace();
- }
-
- } else if (actionCode == ActionCode.ACTION_AVAILABLE) {
- req.setAvailable(httpReq.getBody().available());
- } else if (actionCode == ActionCode.ACTION_COMET_BEGIN) {
-// comet = true;
- } else if (actionCode == ActionCode.ACTION_COMET_END) {
-// comet = false;
- } else if (actionCode == ActionCode.ACTION_COMET_CLOSE) {
- //no op
- } else if (actionCode == ActionCode.ACTION_COMET_SETTIMEOUT) {
- //no op
-// } else if (actionCode == ActionCode.ACTION_ASYNC_START) {
-// //TODO SERVLET3 - async
-// } else if (actionCode == ActionCode.ACTION_ASYNC_COMPLETE) {
-// //TODO SERVLET3 - async
-// } else if (actionCode == ActionCode.ACTION_ASYNC_SETTIMEOUT) {
-// //TODO SERVLET3 - async
- }
-
-
- }
-
- private void commit() {
- if (res.isCommitted())
- return;
-
- // TODO: copy headers, fields
- httpRes.setStatus(res.getStatus());
- httpRes.setMessage(res.getMessage());
- MultiMap mimeHeaders = httpRes.getMimeHeaders();
- MimeHeaders coyoteHeaders = res.getMimeHeaders();
- for (int i = 0; i < coyoteHeaders.size(); i++ ) {
- MessageBytes name = coyoteHeaders.getName(i);
- MessageBytes val = coyoteHeaders.getValue(i);
- Entry entry = mimeHeaders.addEntry(name.toString());
- entry.getValue().set(val.toString());
- }
- String contentType = res.getContentType();
- if (contentType != null) {
- mimeHeaders.addEntry("Content-Type").getValue().set(contentType);
- }
- String contentLang = res.getContentType();
- if (contentLang != null) {
- mimeHeaders.addEntry("Content-Language").getValue().set(contentLang);
- }
- long contentLength = res.getContentLengthLong();
- if (contentLength != -1) {
- httpRes.setContentLength(contentLength);
- }
- String lang = res.getContentLanguage();
- if (lang != null) {
- httpRes.setHeader("Content-Language", lang);
- }
-
- try {
- httpReq.send();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
-
- }
-}
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/lite/http/BaseMapper.java b/modules/tomcat-lite/java/org/apache/tomcat/lite/http/BaseMapper.java
deleted file mode 100644
index 6b2075b..0000000
--- a/modules/tomcat-lite/java/org/apache/tomcat/lite/http/BaseMapper.java
+++ /dev/null
@@ -1,1112 +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.lite.http;
-
-
-import java.io.IOException;
-import java.util.logging.Logger;
-
-import org.apache.tomcat.lite.http.HttpChannel.HttpService;
-import org.apache.tomcat.lite.io.CBuffer;
-import org.apache.tomcat.lite.io.FileConnector;
-import org.apache.tomcat.lite.io.BBucket;
-
-/**
- * Mapper, which implements the servlet API mapping rules (which are derived
- * from the HTTP rules).
- *
- * This class doesn't use JNDI.
- */
-public class BaseMapper {
-
- private static Logger logger =
- Logger.getLogger(BaseMapper.class.getName());
-
- // TODO:
- /**
- * Mapping should be done on bytes - as received from net, before
- * translation to chars. This would allow setting the default charset
- * for the context - or even executing the servlet and letting it specify
- * the charset to use for further decoding.
- *
- */
- public static interface Mapper {
- public void map(BBucket host, BBucket url, MappingData md);
- }
-
-
- /**
- * Like BaseMapper, for a Context.
- */
- public static class ServiceMapper extends BaseMapper {
- /**
- * Context associated with this wrapper, used for wrapper mapping.
- */
- public BaseMapper.Context contextMapElement = new BaseMapper.Context(this);
-
- /**
- * Set context, used for wrapper mapping (request dispatcher).
- *
- * @param welcomeResources Welcome files defined for this context
- */
- public void setContext(String path, String[] welcomeResources) {
- contextMapElement.name = path;
- contextMapElement.welcomeResources = welcomeResources;
- }
-
-
- /**
- * Add a wrapper to the context associated with this wrapper.
- *
- * @param path Wrapper mapping
- * @param wrapper The Wrapper object
- */
- public void addWrapper(String path, Object wrapper) {
- addWrapper(contextMapElement, path, wrapper);
- }
-
-
- public void addWrapper(String path, Object wrapper, boolean jspWildCard) {
- addWrapper(contextMapElement, path, wrapper, jspWildCard);
- }
-
-
- /**
- * Remove a wrapper from the context associated with this wrapper.
- *
- * @param path Wrapper mapping
- */
- public void removeWrapper(String path) {
- removeWrapper(contextMapElement, path);
- }
-
-
-// /**
-// * Map the specified URI relative to the context,
-// * mutating the given mapping data.
-// *
-// * @param uri URI
-// * @param mappingData This structure will contain the result of the mapping
-// * operation
-// */
-// public void map(CBuffer uri, MappingData mappingData)
-// throws Exception {
-//
-// CBuffer uricc = uri.getCharBuffer();
-// internalMapWrapper(contextMapElement, uricc, mappingData);
-//
-// }
- }
-
- /**
- * Array containing the virtual hosts definitions.
- */
- Host[] hosts = new Host[0];
-
- /**
- * If no other host is found.
- * For single-host servers ( most common ) this is the only one
- * used.
- */
- Host defaultHost = new Host();
-
- public BaseMapper() {
- defaultHost.contextList = new ContextList();
- }
-
- // --------------------------------------------------------- Public Methods
-
- public synchronized Host addHost(String name) {
- if (name == null) {
- name = "localhost";
- }
- Host[] newHosts = new Host[hosts.length + 1];
- Host newHost = new Host();
- newHost.name = name;
- newHost.contextList = new ContextList();
-
- if (insertMap(hosts, newHosts, newHost)) {
- hosts = newHosts;
- }
- return newHost;
- }
-
-
- /**
- * Remove a host from the mapper.
- *
- * @param name Virtual host name
- */
- public synchronized void removeHost(String name) {
- // Find and remove the old host
- int pos = find(hosts, name);
- if (pos < 0) {
- return;
- }
- Object host = hosts[pos].object;
- Host[] newHosts = new Host[hosts.length - 1];
- if (removeMap(hosts, newHosts, name)) {
- hosts = newHosts;
- }
- // Remove all aliases (they will map to the same host object)
- for (int i = 0; i < newHosts.length; i++) {
- if (newHosts[i].object == host) {
- Host[] newHosts2 = new Host[hosts.length - 1];
- if (removeMap(hosts, newHosts2, newHosts[i].name)) {
- hosts = newHosts2;
- }
- }
- }
- }
-
- /**
- * Add an alias to an existing host.
- * @param name The name of the host
- * @param alias The alias to add
- */
- public synchronized void addHostAlias(String name, String alias) {
- int pos = find(hosts, name);
- if (pos < 0) {
- // Should not be adding an alias for a host that doesn't exist but
- // just in case...
- return;
- }
- Host realHost = hosts[pos];
-
- Host[] newHosts = new Host[hosts.length + 1];
- Host newHost = new Host();
- newHost.name = alias;
- newHost.contextList = realHost.contextList;
- newHost.object = realHost;
- if (insertMap(hosts, newHosts, newHost)) {
- hosts = newHosts;
- }
- }
-
- private Host getHost(String host) {
- return getHost(CBuffer.newInstance().append(host));
- }
-
- private Host getHost(CBuffer host) {
- if (hosts == null || hosts.length <= 1 || host == null
- || host.length() == 0 || host.equals("")) {
- return defaultHost;
- } else {
- Host[] hosts = this.hosts;
- // TODO: if hosts.length == 1 or defaultHost ?
- int pos = findIgnoreCase(hosts, host);
- if ((pos != -1) && (host.equalsIgnoreCase(hosts[pos].name))) {
- return hosts[pos];
- } else {
- return defaultHost;
- }
- }
- }
-
- private Host getOrCreateHost(String hostName) {
- Host host = getHost(CBuffer.newInstance().append(hostName));
- if (host == null) {
- host = addHost(hostName);
- }
- return host;
- }
-
- // Contexts
-
- /**
- * Add a new Context to an existing Host.
- *
- * @param hostName Virtual host name this context belongs to
- * @param path Context path
- * @param context Context object
- * @param welcomeResources Welcome files defined for this context
- * @param resources Static resources of the context
- * @param ctxService
- */
- public BaseMapper.Context addContext(String hostName, String path, Object context,
- String[] welcomeResources, FileConnector resources,
- HttpChannel.HttpService ctxService) {
-
- if (path == null) {
- path = "/";
- }
-
- Host host = getOrCreateHost(hostName);
-
- int slashCount = slashCount(path);
- synchronized (host) {
- BaseMapper.Context[] contexts = host.contextList.contexts;
- // Update nesting
- if (slashCount > host.contextList.nesting) {
- host.contextList.nesting = slashCount;
- }
- for (int i = 0; i < contexts.length; i++) {
- if (path.equals(contexts[i].name)) {
- return contexts[i];
- }
- }
- BaseMapper.Context[] newContexts = new BaseMapper.Context[contexts.length + 1];
- BaseMapper.Context newContext = new BaseMapper.Context(this);
- newContext.name = path;
- newContext.object = context;
- if (welcomeResources != null) {
- newContext.welcomeResources = welcomeResources;
- }
- newContext.resources = resources;
- if (ctxService != null) {
- newContext.defaultWrapper = new BaseMapper.ServiceMapping();
- newContext.defaultWrapper.object = ctxService;
- }
-
- if (insertMap(contexts, newContexts, newContext)) {
- host.contextList.contexts = newContexts;
- }
- return newContext;
- }
-
- }
-
-
- /**
- * Remove a context from an existing host.
- *
- * @param hostName Virtual host name this context belongs to
- * @param path Context path
- */
- public void removeContext(String hostName, String path) {
- Host host = getHost(hostName);
- synchronized (host) {
- BaseMapper.Context[] contexts = host.contextList.contexts;
- if( contexts.length == 0 ){
- return;
- }
- BaseMapper.Context[] newContexts = new BaseMapper.Context[contexts.length - 1];
- if (removeMap(contexts, newContexts, path)) {
- host.contextList.contexts = newContexts;
- // Recalculate nesting
- host.contextList.nesting = 0;
- for (int i = 0; i < newContexts.length; i++) {
- int slashCount = slashCount(newContexts[i].name);
- if (slashCount > host.contextList.nesting) {
- host.contextList.nesting = slashCount;
- }
- }
- }
- }
- }
-
-
- /**
- * Add a new Wrapper to an existing Context.
- *
- * @param hostName Virtual host name this wrapper belongs to
- * @param contextPath Context path this wrapper belongs to
- * @param path Wrapper mapping
- * @param wrapper Wrapper object
- */
- public void addWrapper(String hostName, String contextPath, String path,
- Object wrapper) {
- addWrapper(hostName, contextPath, path, wrapper, false);
- }
-
-
- public void addWrapper(String hostName, String contextPath, String path,
- Object wrapper, boolean jspWildCard) {
- Host host = getHost(hostName);
- BaseMapper.Context[] contexts = host.contextList.contexts;
- int pos2 = find(contexts, contextPath);
- if( pos2<0 ) {
- logger.severe("No context found: " + contextPath );
- return;
- }
- BaseMapper.Context context = contexts[pos2];
- if (context.name.equals(contextPath)) {
- addWrapper(context, path, wrapper, jspWildCard);
- }
- }
-
-
- public void addWrapper(BaseMapper.Context context, String path, Object wrapper) {
- addWrapper(context, path, wrapper, false);
- }
-
-
- /**
- * Adds a wrapper to the given context.
- *
- * @param context The context to which to add the wrapper
- * @param path Wrapper mapping
- * @param wrapper The Wrapper object
- * @param jspWildCard true if the wrapper corresponds to the JspServlet
- * and the mapping path contains a wildcard; false otherwise
- */
- protected void addWrapper(BaseMapper.Context context, String path, Object wrapper,
- boolean jspWildCard) {
-
- synchronized (context) {
- BaseMapper.ServiceMapping newWrapper = new BaseMapper.ServiceMapping();
- newWrapper.object = wrapper;
- newWrapper.jspWildCard = jspWildCard;
- if (path.endsWith("/*")) {
- // Wildcard wrapper
- newWrapper.name = path.substring(0, path.length() - 2);
- BaseMapper.ServiceMapping[] oldWrappers = context.wildcardWrappers;
- BaseMapper.ServiceMapping[] newWrappers =
- new BaseMapper.ServiceMapping[oldWrappers.length + 1];
- if (insertMap(oldWrappers, newWrappers, newWrapper)) {
- context.wildcardWrappers = newWrappers;
- int slashCount = slashCount(newWrapper.name);
- if (slashCount > context.nesting) {
- context.nesting = slashCount;
- }
- }
- } else if (path.startsWith("*.")) {
- // Extension wrapper
- newWrapper.name = path.substring(2);
- BaseMapper.ServiceMapping[] oldWrappers = context.extensionWrappers;
- BaseMapper.ServiceMapping[] newWrappers =
- new BaseMapper.ServiceMapping[oldWrappers.length + 1];
- if (insertMap(oldWrappers, newWrappers, newWrapper)) {
- context.extensionWrappers = newWrappers;
- }
- } else if (path.equals("/")) {
- // Default wrapper
- newWrapper.name = "";
- context.defaultWrapper = newWrapper;
- } else {
- // Exact wrapper
- newWrapper.name = path;
- BaseMapper.ServiceMapping[] oldWrappers = context.exactWrappers;
- BaseMapper.ServiceMapping[] newWrappers =
- new BaseMapper.ServiceMapping[oldWrappers.length + 1];
- if (insertMap(oldWrappers, newWrappers, newWrapper)) {
- context.exactWrappers = newWrappers;
- }
- }
- }
- }
-
- /**
- * Remove a wrapper from an existing context.
- *
- * @param hostName Virtual host name this wrapper belongs to
- * @param contextPath Context path this wrapper belongs to
- * @param path Wrapper mapping
- */
- public void removeWrapper(String hostName, String contextPath,
- String path) {
- Host host = getHost(hostName);
- BaseMapper.Context[] contexts = host.contextList.contexts;
- int pos2 = find(contexts, contextPath);
- if (pos2 < 0) {
- return;
- }
- BaseMapper.Context context = contexts[pos2];
- if (context.name.equals(contextPath)) {
- removeWrapper(context, path);
- }
- }
-
- protected void removeWrapper(BaseMapper.Context context, String path) {
- synchronized (context) {
- if (path.endsWith("/*")) {
- // Wildcard wrapper
- String name = path.substring(0, path.length() - 2);
- BaseMapper.ServiceMapping[] oldWrappers = context.wildcardWrappers;
- BaseMapper.ServiceMapping[] newWrappers =
- new BaseMapper.ServiceMapping[oldWrappers.length - 1];
- if (removeMap(oldWrappers, newWrappers, name)) {
- // Recalculate nesting
- context.nesting = 0;
- for (int i = 0; i < newWrappers.length; i++) {
- int slashCount = slashCount(newWrappers[i].name);
- if (slashCount > context.nesting) {
- context.nesting = slashCount;
- }
- }
- context.wildcardWrappers = newWrappers;
- }
- } else if (path.startsWith("*.")) {
- // Extension wrapper
- String name = path.substring(2);
- BaseMapper.ServiceMapping[] oldWrappers = context.extensionWrappers;
- BaseMapper.ServiceMapping[] newWrappers =
- new BaseMapper.ServiceMapping[oldWrappers.length - 1];
- if (removeMap(oldWrappers, newWrappers, name)) {
- context.extensionWrappers = newWrappers;
- }
- } else if (path.equals("/")) {
- // Default wrapper
- context.defaultWrapper = null;
- } else {
- // Exact wrapper
- String name = path;
- BaseMapper.ServiceMapping[] oldWrappers = context.exactWrappers;
- BaseMapper.ServiceMapping[] newWrappers =
- new BaseMapper.ServiceMapping[oldWrappers.length - 1];
- if (removeMap(oldWrappers, newWrappers, name)) {
- context.exactWrappers = newWrappers;
- }
- }
- }
- }
-
- /**
- * Map the specified host name and URI, mutating the given mapping data.
- *
- * @param host Virtual host name
- * @param uri URI
- * @param mappingData This structure will contain the result of the mapping
- * operation
- */
- public void map(CBuffer host, CBuffer uri,
- MappingData mappingData)
- throws Exception {
-
- internalMap(host.length() == 0 ? null :
- host, uri, mappingData);
- }
-
-
- // -------------------------------------------------------- Private Methods
-
- // public Context mapContext(CBuffer host, CBuffer url);
-
- /**
- * Map the specified URI.
- */
- private final void internalMap(CBuffer host, CBuffer uri,
- MappingData mappingData)
- throws Exception {
- BaseMapper.Context[] contexts = null;
- BaseMapper.Context context = null;
- int nesting = 0;
-
- // Virtual host mapping
- Host mappedHost = getHost(host);
- contexts = mappedHost.contextList.contexts;
- nesting = mappedHost.contextList.nesting;
-
- // Context mapping
- if (contexts.length == 0) {
- return;
- }
-
- if (mappingData.context == null) {
- if (nesting < 1 || contexts.length == 1 && "".equals(contexts[0].name)) {
- // if 1 context (default) -> fast return
- context = contexts[0];
- } else if (nesting == 1) {
- // if all contexts are 1-component-only
- int nextSlash = uri.indexOf('/', 1);
- if (nextSlash == -1) {
- nextSlash = uri.length();
- }
- mappingData.contextPath.set(uri, 0, nextSlash);
- int pos = find(contexts, uri);
- if (pos == -1) {
- pos = find(contexts, "/");
- }
- if (pos >= 0) {
- context = contexts[pos];
- }
- } else {
- int pos = find(contexts, uri);
- if (pos >= 0) {
- int lastSlash = -1;
- int length = -1;
- boolean found = false;
- CBuffer tmp = mappingData.tmpPrefix;
- tmp.wrap(uri, 0, uri.length());
-
- while (pos >= 0) {
- if (tmp.startsWith(contexts[pos].name)) {
- length = contexts[pos].name.length();
- if (tmp.length() == length) {
- found = true;
- break;
- } else if (tmp.startsWithIgnoreCase("/", length)) {
- found = true;
- break;
- }
- }
- if (lastSlash == -1) {
- lastSlash = tmp.nthSlash(nesting + 1);
- } else {
- lastSlash = tmp.lastIndexOf('/');
- }
- tmp.delete(lastSlash);
- pos = find(contexts, tmp);
- }
-
- if (!found) {
- if (contexts[0].name.equals("")) {
- context = contexts[0];
- }
- } else {
- context = contexts[pos];
- }
- }
- }
-
- if (context != null) {
- mappingData.context = context.object;
- mappingData.contextPath.set(context.name);
- }
- }
-
- // Wrapper mapping
- if ((context != null) && (mappingData.getServiceObject() == null)) {
- internalMapWrapper(context, uri, mappingData);
- }
-
- }
-
-
- /**
- * Wrapper mapping, using servlet rules.
- */
- protected final void internalMapWrapper(
- BaseMapper.Context context,
- CBuffer url,
- MappingData mappingData)
- throws Exception {
-
- boolean noServletPath = false;
- if (url.length() < context.name.length()) {
- throw new IOException("Invalid mapping " + context.name + " " +
- url);
- }
-
- try {
- // Set the servlet path.
- mappingData.tmpServletPath.set(url,
- context.name.length(),
- url.length() - context.name.length());
-
- if (mappingData.tmpServletPath.length() == 0) {
- mappingData.tmpServletPath.append('/');
- // This is just the context /example or /
- if (!context.name.equals("/")) {
- noServletPath = true;
- }
- }
-
- mapAfterContext(context, url, mappingData.tmpServletPath, mappingData,
- noServletPath);
- } catch (ArrayIndexOutOfBoundsException ex) {
- System.err.println(1);
- }
- }
-
- void mapAfterContext(BaseMapper.Context context,
- CBuffer url, CBuffer urlNoContext,
- MappingData mappingData, boolean noServletPath)
- throws Exception {
-
-
- // Rule 1 -- Exact Match
- BaseMapper.ServiceMapping[] exactWrappers = context.exactWrappers;
- internalMapExactWrapper(exactWrappers, urlNoContext, mappingData);
-
- // Rule 2 -- Prefix Match
- boolean checkJspWelcomeFiles = false;
- BaseMapper.ServiceMapping[] wildcardWrappers = context.wildcardWrappers;
- if (mappingData.getServiceObject() == null) {
-
- internalMapWildcardWrapper(wildcardWrappers, context.nesting,
- urlNoContext, mappingData);
-
- if (mappingData.getServiceObject() != null
- && mappingData.service.jspWildCard) {
- if (urlNoContext.lastChar() == '/') {
- /*
- * 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 JSP files (since they match the url-pattern), to be
- * considered. See Bugzilla 27664.
- */
- mappingData.service = null;
- checkJspWelcomeFiles = true;
- } else {
- // See Bugzilla 27704
- mappingData.wrapperPath.set(urlNoContext);
- mappingData.pathInfo.recycle();
- }
- }
- }
-
- if(mappingData.getServiceObject() == null && noServletPath) {
- // The path is empty, redirect to "/"
- mappingData.redirectPath.set(context.name);
- mappingData.redirectPath.append("/");
- return;
- }
-
- // Rule 3 -- Extension Match
- BaseMapper.ServiceMapping[] extensionWrappers = context.extensionWrappers;
- if (mappingData.getServiceObject() == null && !checkJspWelcomeFiles) {
- internalMapExtensionWrapper(extensionWrappers, urlNoContext, mappingData);
- }
-
- // Rule 4 -- Welcome resources processing for servlets
- if (mappingData.getServiceObject() == null) {
- boolean checkWelcomeFiles = checkJspWelcomeFiles;
- if (!checkWelcomeFiles) {
- checkWelcomeFiles = (urlNoContext.lastChar() == '/');
- }
- if (checkWelcomeFiles) {
- for (int i = 0; (i < context.welcomeResources.length)
- && (mappingData.getServiceObject() == null); i++) {
-
- CBuffer wpath = mappingData.tmpWelcome;
- wpath.set(urlNoContext);
- wpath.append(context.welcomeResources[i]);
-
- // Rule 4a -- Welcome resources processing for exact macth
- internalMapExactWrapper(exactWrappers, urlNoContext, mappingData);
-
- // Rule 4b -- Welcome resources processing for prefix match
- if (mappingData.getServiceObject() == null) {
- internalMapWildcardWrapper
- (wildcardWrappers, context.nesting,
- urlNoContext, mappingData);
- }
-
- // Rule 4c -- Welcome resources processing
- // for physical folder
- if (mappingData.getServiceObject() == null
- && context.resources != null) {
- String pathStr = urlNoContext.toString();
-
- mapWelcomResource(context, urlNoContext, mappingData,
- extensionWrappers, pathStr);
-
- }
- }
- }
-
- }
-
-
- // Rule 7 -- Default servlet
- if (mappingData.getServiceObject() == null && !checkJspWelcomeFiles) {
- if (context.defaultWrapper != null) {
- mappingData.service = context.defaultWrapper;
- mappingData.requestPath.set(urlNoContext);
- mappingData.wrapperPath.set(urlNoContext);
- }
- // Redirection to a folder
- if (context.resources != null && urlNoContext.lastChar() != '/') {
- String pathStr = urlNoContext.toString();
- mapDefaultServlet(context, urlNoContext, mappingData,
- url,
- pathStr);
- }
- }
- }
-
- /**
- * Filesystem-dependent method:
- * if pathStr corresponds to a directory, we'll need to redirect with /
- * at end.
- */
- protected void mapDefaultServlet(BaseMapper.Context context,
- CBuffer path,
- MappingData mappingData,
- CBuffer url,
- String pathStr) throws IOException {
-
- if (context.resources != null
- && context.resources.isDirectory(pathStr)) {
- mappingData.redirectPath.set(url);
- mappingData.redirectPath.append("/");
- } else {
- mappingData.requestPath.set(pathStr);
- mappingData.wrapperPath.set(pathStr);
- }
- }
-
-
- /**
- * Filesystem dependent method:
- * check if a resource exists in filesystem.
- */
- protected void mapWelcomResource(BaseMapper.Context context, CBuffer path,
- MappingData mappingData,
- BaseMapper.ServiceMapping[] extensionWrappers, String pathStr) {
-
- if (context.resources != null &&
- context.resources.isFile(pathStr)) {
- internalMapExtensionWrapper(extensionWrappers,
- path, mappingData);
- if (mappingData.getServiceObject() == null
- && context.defaultWrapper != null) {
- mappingData.service = context.defaultWrapper;
- mappingData.requestPath.set(path);
- mappingData.wrapperPath.set(path);
- mappingData.requestPath.set(pathStr);
- mappingData.wrapperPath.set(pathStr);
- }
- }
- }
-
- /**
- * Exact mapping.
- */
- private final void internalMapExactWrapper
- (BaseMapper.ServiceMapping[] wrappers, CBuffer path, MappingData mappingData) {
- int pos = find(wrappers, path);
- if ((pos != -1) && (path.equals(wrappers[pos].name))) {
- mappingData.requestPath.set(wrappers[pos].name);
- mappingData.wrapperPath.set(wrappers[pos].name);
- mappingData.service = wrappers[pos];
- }
- }
-
-
- /**
- * Prefix mapping. ( /foo/* )
- */
- private final void internalMapWildcardWrapper
- (BaseMapper.ServiceMapping[] wrappers, int nesting, CBuffer path,
- MappingData mappingData) {
-
- int lastSlash = -1;
- int length = -1;
-
- CBuffer tmp = mappingData.tmpPrefix;
- tmp.wrap(path, 0, path.length());
-
- int pos = find(wrappers, tmp);
- if (pos != -1) {
- boolean found = false;
- while (pos >= 0) {
- if (tmp.startsWith(wrappers[pos].name)) {
- length = wrappers[pos].name.length();
- if (tmp.length() == length) {
- found = true;
- break;
- } else if (tmp.startsWithIgnoreCase("/", length)) {
- found = true;
- break;
- }
- }
- if (lastSlash == -1) {
- lastSlash = tmp.nthSlash(nesting + 1);
- } else {
- lastSlash = tmp.lastIndexOf('/');
- }
- tmp.delete(lastSlash);
- pos = find(wrappers, tmp);
- }
- if (found) {
- mappingData.wrapperPath.set(wrappers[pos].name);
-
- if (path.length() > length) {
- mappingData.pathInfo.set
- (path, length, path.length() - length);
- }
- mappingData.requestPath.set(path);
-
- mappingData.service = wrappers[pos];
- }
- }
- }
-
-
- /**
- * Extension mappings.
- */
- protected final void internalMapExtensionWrapper
- (BaseMapper.ServiceMapping[] wrappers, CBuffer path, MappingData mappingData) {
-
- int dot = path.getExtension(mappingData.ext, '/', '.');
- if (dot >= 0) {
- int pos = find(wrappers, mappingData.ext);
-
- if ((pos != -1)
- && (mappingData.ext.equals(wrappers[pos].name))) {
-
- mappingData.wrapperPath.set(path);
- mappingData.requestPath.set(path);
-
- mappingData.service = wrappers[pos];
- }
- }
- }
-
-
- /**
- * Find a map elemnt given its name in a sorted array of map elements.
- * This will return the index for the closest inferior or equal item in the
- * given array.
- */
- private static final int find(BaseMapper.Mapping[] map, CBuffer name) {
-
- int a = 0;
- int b = map.length - 1;
-
- // Special cases: -1 and 0
- if (b == -1) {
- return -1;
- }
-
- if (name.compare(map[0].name) < 0 ) {
- return -1;
- }
- if (b == 0) {
- return 0;
- }
-
- int i = 0;
- while (true) {
- i = (b + a) / 2;
- int result = name.compare(map[i].name);
- if (result == 1) {
- a = i;
- } else if (result == 0) {
- return i;
- } else {
- b = i;
- }
- if ((b - a) == 1) {
- int result2 = name.compare(map[b].name);
- if (result2 < 0) {
- return a;
- } else {
- return b;
- }
- }
- }
-
- }
-
- /**
- * Find a map elemnt given its name in a sorted array of map elements.
- * This will return the index for the closest inferior or equal item in the
- * given array.
- */
- private static final int findIgnoreCase(BaseMapper.Mapping[] map,
- CBuffer name) {
- int a = 0;
- int b = map.length - 1;
-
- // Special cases: -1 and 0
- if (b == -1) {
- return -1;
- }
- if (name.compareIgnoreCase(map[0].name) < 0 ) {
- return -1;
- }
- if (b == 0) {
- return 0;
- }
-
- int i = 0;
- while (true) {
- i = (b + a) / 2;
- int result = name.compareIgnoreCase(map[i].name);
- if (result == 1) {
- a = i;
- } else if (result == 0) {
- return i;
- } else {
- b = i;
- }
- if ((b - a) == 1) {
- int result2 = name.compareIgnoreCase(map[b].name);
- if (result2 < 0) {
- return a;
- } else {
- return b;
- }
- }
- }
-
- }
-
-
- /**
- * Find a map element given its name in a sorted array of map elements.
- * This will return the index for the closest inferior or equal item in the
- * given array.
- */
- private static final int find(BaseMapper.Mapping[] map, String name) {
-
- int a = 0;
- int b = map.length - 1;
-
- // Special cases: -1 and 0
- if (b == -1) {
- return -1;
- }
-
- if (name.compareTo(map[0].name) < 0) {
- return -1;
- }
- if (b == 0) {
- return 0;
- }
-
- int i = 0;
- while (true) {
- i = (b + a) / 2;
- int result = name.compareTo(map[i].name);
- if (result > 0) {
- a = i;
- } else if (result == 0) {
- return i;
- } else {
- b = i;
- }
- if ((b - a) == 1) {
- int result2 = name.compareTo(map[b].name);
- if (result2 < 0) {
- return a;
- } else {
- return b;
- }
- }
- }
-
- }
-
-
- /**
- * Return the slash count in a given string.
- */
- private static final int slashCount(String name) {
- int pos = -1;
- int count = 0;
- while ((pos = name.indexOf('/', pos + 1)) != -1) {
- count++;
- }
- return count;
- }
-
-
- /**
- * Insert into the right place in a sorted MapElement array, and prevent
- * duplicates.
- */
- private static final boolean insertMap
- (BaseMapper.Mapping[] oldMap, BaseMapper.Mapping[] newMap, BaseMapper.Mapping newElement) {
- int pos = find(oldMap, newElement.name);
- if ((pos != -1) && (newElement.name.equals(oldMap[pos].name))) {
- return false;
- }
- System.arraycopy(oldMap, 0, newMap, 0, pos + 1);
- newMap[pos + 1] = newElement;
- System.arraycopy
- (oldMap, pos + 1, newMap, pos + 2, oldMap.length - pos - 1);
- return true;
- }
-
-
- /**
- * Insert into the right place in a sorted MapElement array.
- */
- private static final boolean removeMap
- (BaseMapper.Mapping[] oldMap, BaseMapper.Mapping[] newMap, String name) {
- int pos = find(oldMap, name);
- if ((pos != -1) && (name.equals(oldMap[pos].name))) {
- System.arraycopy(oldMap, 0, newMap, 0, pos);
- System.arraycopy(oldMap, pos + 1, newMap, pos,
- oldMap.length - pos - 1);
- return true;
- }
- return false;
- }
-
-
- // ------------------------------------------------- MapElement Inner Class
-
-
- protected static final class Host
- extends BaseMapper.Mapping {
- //Map<String, Context> contexts = new HashMap();
- //Context rootContext;
-
- public ContextList contextList = null;
-
- }
-
-
- // ------------------------------------------------ ContextList Inner Class
-
- // Shared among host aliases.
- protected static final class ContextList {
-
- public BaseMapper.Context[] contexts = new BaseMapper.Context[0];
- public int nesting = 0;
-
- }
-
-
- public static final class Context extends BaseMapper.Mapping {
-
- Context(BaseMapper mapper) {
- this.mapper = mapper;
- }
- public BaseMapper mapper;
- public String[] welcomeResources = new String[0];
- public FileConnector resources = null;
-
- public BaseMapper.ServiceMapping defaultWrapper = null;
-
- public BaseMapper.ServiceMapping[] exactWrappers = new BaseMapper.ServiceMapping[0];
- public BaseMapper.ServiceMapping[] wildcardWrappers = new BaseMapper.ServiceMapping[0];
- public BaseMapper.ServiceMapping[] extensionWrappers = new BaseMapper.ServiceMapping[0];
- public int nesting = 0;
-
- public void addWrapper(String path, HttpService service) {
- mapper.addWrapper(this, path, service);
- }
-
- }
-
-
- public static class ServiceMapping extends BaseMapper.Mapping {
- public boolean jspWildCard = false;
- // If set, the service will run in the selector thread ( should
- // be non-blocking )
- public boolean selectorThread = false;
-
- }
-
-
- protected static abstract class Mapping {
- public String name = null;
- public Object object = null;
-
- public String toString() {
- if (name == null || "".equals(name)) {
- return "DEFAULT";
- }
- return name;
- }
- }
-
-
- // ---------------------------------------------------- Context Inner Class
-
-
-}
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/lite/http/CompressFilter.java b/modules/tomcat-lite/java/org/apache/tomcat/lite/http/CompressFilter.java
deleted file mode 100644
index ecf5851..0000000
--- a/modules/tomcat-lite/java/org/apache/tomcat/lite/http/CompressFilter.java
+++ /dev/null
@@ -1,225 +0,0 @@
-/*
- */
-package org.apache.tomcat.lite.http;
-
-import java.io.IOException;
-import java.nio.ByteBuffer;
-
-import org.apache.tomcat.lite.io.BBucket;
-import org.apache.tomcat.lite.io.IOBuffer;
-
-import com.jcraft.jzlib.JZlib;
-import com.jcraft.jzlib.ZStream;
-
-public class CompressFilter {
-
- // Stream format: RFC1950
- // 1CMF 1FLG [4DICTID] DATA 4ADLER
- // CMF: CINFO + CM (compression method). == x8
- // 78 == deflate with 32k window, i.e. max window
-
- // FLG: 2bit level, 1 bit FDICT, 5 bit FCHECK
- // Cx, Dx - no dict; Fx, Ex - dict ( for BEST_COMPRESSION )
-
- // Overhead: 6 bytes without dict, 10 with dict
- // data is encoded in blocks - there is a 'block end' marker and
- // 'last block'.
-
- // Flush: http://www.bolet.org/~pornin/deflate-flush.html
- // inflater needs about 9 bits
- // Z_SYNC_FLUSH: send empty block, 00 00 FF FF - seems recomended
- // PPP can skip this - there is a record format on top
- // Z_PARTIAL_FLUSH: standard for SSH
-
- ZStream cStream;
- ZStream dStream;
-
- byte[] dict;
- long dictId;
-
- public CompressFilter() {
- }
-
- public void recycle() {
- if (cStream == null) {
- return;
- }
- cStream.free();
- cStream = null;
- dStream.free();
- dStream = null;
- }
-
- public void init() {
- if (cStream != null) {
- return;
- }
- // can't call: cStream.free(); - will kill the adler, NPE
- cStream = new ZStream();
- // BEST_COMRESSION results in 256Kb per Deflate
- // 15 == default = 32k window
- cStream.deflateInit(JZlib.Z_BEST_SPEED, 10);
-
- dStream = new ZStream();
- dStream.inflateInit();
-
- }
-
- CompressFilter setDictionary(byte[] dict, long id) {
- init();
- this.dict = dict;
- this.dictId = id;
- cStream.deflateSetDictionary(dict, dict.length);
- return this;
- }
-
- void compress(IOBuffer in, IOBuffer out) throws IOException {
- init();
- BBucket bb = in.popFirst();
-
- while (bb != null) {
- // TODO: only the last one needs flush
-
- // TODO: size missmatches ?
- compress(bb, out, false);
- bb = in.popFirst();
- }
-
- if (in.isClosedAndEmpty()) {
- compressEnd(out);
- }
- }
-
- void compress(BBucket bb, IOBuffer out, boolean last) throws IOException {
- // TODO: only the last one needs flush
-
- // TODO: size missmatches ?
- init();
- int flush = JZlib.Z_PARTIAL_FLUSH;
-
- cStream.next_in = bb.array();
- cStream.next_in_index = bb.position();
- cStream.avail_in = bb.remaining();
-
- while (true) {
- ByteBuffer outB = out.getWriteBuffer();
- cStream.next_out = outB.array();
- cStream.next_out_index = outB.position();
- cStream.avail_out = outB.remaining();
-
- int err = cStream.deflate(flush);
- check(err, cStream);
- outB.position(cStream.next_out_index);
- out.releaseWriteBuffer(1);
- if (cStream.avail_out > 0 || cStream.avail_in == 0) {
- break;
- }
- }
-
- if (last) {
- compressEnd(out);
- }
- }
-
- private void compressEnd(IOBuffer out) throws IOException {
- while (true) {
- ByteBuffer outB = out.getWriteBuffer();
- cStream.next_out = outB.array();
-
- cStream.next_out_index = outB.position();
- cStream.avail_out = outB.remaining();
- cStream.deflate(JZlib.Z_FINISH);
- cStream.deflateEnd();
-
- outB.position(cStream.next_out_index);
- out.releaseWriteBuffer(1);
- if (cStream.avail_out > 0) {
- break;
- }
- }
- }
-
- void decompress(IOBuffer in, IOBuffer out) throws IOException {
- decompress(in, out, in.available());
- }
-
- void decompress(IOBuffer in, IOBuffer out, int len) throws IOException {
- init();
- BBucket bb = in.peekFirst();
-
- while (bb != null && len > 0) {
- dStream.next_in = bb.array();
- dStream.next_in_index = bb.position();
- int rd = Math.min(bb.remaining(), len);
- dStream.avail_in = rd;
-
- while (true) {
- ByteBuffer outB = out.getWriteBuffer();
-
- dStream.next_out = outB.array();
- dStream.next_out_index = outB.position();
- dStream.avail_out = outB.remaining();
-
- int err = dStream.inflate(JZlib.Z_SYNC_FLUSH);
- if (err == JZlib.Z_NEED_DICT && dict != null) {
- // dStream.adler has the dict id - not sure how to check
- if (dictId != 0 && dStream.adler != dictId) {
- throw new IOException("Invalid dictionary");
- }
- if (dictId == 0) {
- // initDict should pass a real dict id.
- System.err.println("Missing dict ID: " + dStream.adler);
- }
- dStream.inflateSetDictionary(dict, dict.length);
- err = dStream.inflate(JZlib.Z_SYNC_FLUSH);
- }
- outB.position(dStream.next_out_index);
- out.releaseWriteBuffer(1);
-
- if (err == JZlib.Z_STREAM_END) {
- err = dStream.inflateEnd();
- out.close();
- check(err, dStream);
- // move in back, not consummed
- bb.position(dStream.next_in_index);
- return;
- }
- check(err, dStream);
-
- if (dStream.avail_out > 0 || dStream.avail_in == 0) {
- break;
- }
- }
-
- in.advance(rd); // consummed
- len -= rd;
- bb = in.peekFirst();
- }
-
- if (in.isClosedAndEmpty()) {
- // Shouldn't happen - input was not properly closed..
- // This should throw an exception, inflateEnd will check the CRC
- int err = dStream.inflateEnd();
- out.close();
- check(err, dStream);
- out.close();
- }
- }
-
- private void check(int err, ZStream stream) throws IOException {
- if (err != JZlib.Z_OK) {
- throw new IOException(err + " " + stream.msg);
- }
- }
-
- boolean isCompressed(HttpMessage http) {
- return false;
- }
-
- boolean needsCompression(HttpMessage in, HttpMessage out) {
- return false;
- }
-
-
-}
-
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/lite/http/ContentType.java b/modules/tomcat-lite/java/org/apache/tomcat/lite/http/ContentType.java
deleted file mode 100644
index 31c6b9c..0000000
--- a/modules/tomcat-lite/java/org/apache/tomcat/lite/http/ContentType.java
+++ /dev/null
@@ -1,96 +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.lite.http;
-
-
-/**
- * Usefull methods for Content-Type processing
- *
- * @author James Duncan Davidson [duncan at eng.sun.com]
- * @author James Todd [gonzo at eng.sun.com]
- * @author Jason Hunter [jch at eng.sun.com]
- * @author Harish Prabandham
- * @author costin at eng.sun.com
- */
-public class ContentType {
-
- /**
- * Parse the character encoding from the specified content type header.
- * If the content type is null, or there is no explicit character encoding,
- * <code>null</code> is returned.
- *
- * @param contentType a content type header
- */
- public static String getCharsetFromContentType(String contentType) {
-
- if (contentType == null)
- return (null);
- int start = contentType.indexOf("charset=");
- if (start < 0)
- return (null);
- String encoding = contentType.substring(start + 8);
- int end = encoding.indexOf(';');
- if (end >= 0)
- encoding = encoding.substring(0, end);
- encoding = encoding.trim();
- if ((encoding.length() > 2) && (encoding.startsWith("\""))
- && (encoding.endsWith("\"")))
- encoding = encoding.substring(1, encoding.length() - 1);
- return (encoding.trim());
-
- }
-
-
- /**
- * Returns true if the given content type contains a charset component,
- * false otherwise.
- *
- * @param type Content type
- * @return true if the given content type contains a charset component,
- * false otherwise
- */
- public static boolean hasCharset(String type) {
-
- boolean hasCharset = false;
-
- int len = type.length();
- int index = type.indexOf(';');
- while (index != -1) {
- index++;
- while (index < len && Character.isWhitespace(type.charAt(index))) {
- index++;
- }
- if (index+8 < len
- && type.charAt(index) == 'c'
- && type.charAt(index+1) == 'h'
- && type.charAt(index+2) == 'a'
- && type.charAt(index+3) == 'r'
- && type.charAt(index+4) == 's'
- && type.charAt(index+5) == 'e'
- && type.charAt(index+6) == 't'
- && type.charAt(index+7) == '=') {
- hasCharset = true;
- break;
- }
- index = type.indexOf(';', index);
- }
-
- return hasCharset;
- }
-
-}
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/lite/http/DefaultHttpConnector.java b/modules/tomcat-lite/java/org/apache/tomcat/lite/http/DefaultHttpConnector.java
deleted file mode 100644
index e43b4fd..0000000
--- a/modules/tomcat-lite/java/org/apache/tomcat/lite/http/DefaultHttpConnector.java
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- */
-package org.apache.tomcat.lite.http;
-
-import org.apache.tomcat.lite.io.SocketConnector;
-
-public class DefaultHttpConnector {
-
- public synchronized static HttpConnector getNew() {
- return new HttpConnector(new SocketConnector());
- }
-
- public synchronized static HttpConnector get() {
- if (DefaultHttpConnector.socketConnector == null) {
- socketConnector =
- new SocketConnector();
- }
- return new HttpConnector(socketConnector);
- }
-
- private static SocketConnector socketConnector;
-
-}
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/lite/http/Dispatcher.java b/modules/tomcat-lite/java/org/apache/tomcat/lite/http/Dispatcher.java
deleted file mode 100644
index 51537e6..0000000
--- a/modules/tomcat-lite/java/org/apache/tomcat/lite/http/Dispatcher.java
+++ /dev/null
@@ -1,199 +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.lite.http;
-
-import java.io.IOException;
-import java.util.concurrent.Executor;
-import java.util.concurrent.Executors;
-import java.util.logging.Logger;
-
-import org.apache.tomcat.lite.http.HttpChannel.HttpService;
-import org.apache.tomcat.lite.http.HttpChannel.RequestCompleted;
-import org.apache.tomcat.lite.io.CBuffer;
-import org.apache.tomcat.lite.io.FileConnector;
-
-/**
- * This class has several functions:
- * - maps the request to another HttpService
- * - decide if the request should be run in the selector thread
- * or in a thread pool
- * - finalizes the request ( close / flush )
- * - detects if the request is complete or set callbacks
- * for receive/flush/done.
- *
- */
-public class Dispatcher implements HttpService {
-
- private BaseMapper mapper;
- static boolean debug = false;
- static Logger log = Logger.getLogger("Mapper");
- Executor tp = Executors.newCachedThreadPool();
-
- public Dispatcher() {
- init();
- }
-
- protected void init() {
- mapper = new BaseMapper();
- }
-
- public void runService(HttpChannel ch) {
- runService(ch, true);
- }
-
- public void runService(HttpChannel ch, boolean recycle) {
- MappingData mapRes = ch.getRequest().getMappingData();
- HttpService h = (HttpService) mapRes.getServiceObject();
- try {
- h.service(ch.getRequest(), ch.getResponse());
- if (!ch.getRequest().isAsyncStarted()) {
- ch.complete();
- if (recycle) {
- ch.release(); // recycle objects.
- }
- } else {
- // Nothing - complete must be called when done.
- }
- } catch (IOException e) {
- e.printStackTrace();
- } catch( Throwable t ) {
- t.printStackTrace();
- }
- }
-
- @Override
- public void service(HttpRequest httpReq, HttpResponse httpRes) throws IOException {
- service(httpReq, httpRes, false, true);
- }
-
- /**
- * Process the request/response in the current thread, without
- * release ( recycle ) at the end.
- *
- * For use by tests and/or in-memory running of servlets.
- *
- * If no connection is associated with the request - the
- * output will remain in the out buffer.
- */
- public void run(HttpRequest httpReq, HttpResponse httpRes) throws IOException {
- service(httpReq, httpRes, true, false);
- }
-
-
- public void service(HttpRequest httpReq, HttpResponse httpRes, boolean noThread, boolean recycle)
- throws IOException {
- long t0 = System.currentTimeMillis();
- HttpChannel http = httpReq.getHttpChannel();
-
- http.setCompletedCallback(doneCallback);
-
- try {
- // compute decodedURI - not done by connector
- MappingData mapRes = httpReq.getMappingData();
- mapRes.recycle();
-
- mapper.map(httpReq.serverName(),
- httpReq.decodedURI(), mapRes);
-
- HttpService h = (HttpService) mapRes.getServiceObject();
-
- if (h != null) {
- if (debug) {
- log.info(">>>>>>>> START: " + http.getRequest().method() + " " +
- http.getRequest().decodedURI() + " " +
- h.getClass().getSimpleName());
- }
-
- if (mapRes.service.selectorThread || noThread) {
- runService(http, recycle);
- } else {
- tp.execute(httpReq.getHttpChannel().dispatcherRunnable);
- }
-
- } else {
- httpRes.setStatus(404);
- http.complete();
- }
-
- } catch (IOException ex) {
- if ("Broken pipe".equals(ex.getMessage())) {
- log.warning("Connection interrupted while writting");
- }
- throw ex;
- } catch( Throwable t ) {
- t.printStackTrace();
- httpRes.setStatus(500);
- http.abort(t);
- }
- }
-
- private RequestCompleted doneCallback = new RequestCompleted() {
- @Override
- public void handle(HttpChannel client, Object extraData) throws IOException {
- if (debug) {
- log.info("<<<<<<<< DONE: " + client.getRequest().method() + " " +
- client.getRequest().decodedURI() + " " +
- client.getResponse().getStatus() + " "
- );
- }
- }
- };
-
- public BaseMapper.Context addContext(String hostname, String ctxPath,
- Object ctx, String[] welcomeResources, FileConnector resources,
- HttpService ctxService) {
- return mapper.addContext(hostname, ctxPath, ctx, welcomeResources, resources,
- ctxService);
- }
-
- public BaseMapper.Context addContext(String ctxPath) {
- return mapper.addContext(null, ctxPath, null, null, null,
- null);
- }
-
- public void map(CBuffer hostMB, CBuffer urlMB, MappingData md) {
- try {
- mapper.map(hostMB, urlMB, md);
- } catch (Exception e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
-
- public void map(BaseMapper.Context ctx,
- CBuffer uri, MappingData md) {
- try {
- mapper.internalMapWrapper(ctx, uri, md);
- } catch (Exception e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
-
- public void addWrapper(BaseMapper.Context ctx, String path,
- HttpService service) {
- mapper.addWrapper(ctx, path, service);
- }
-
-
- public void setDefaultService(HttpService service) {
- BaseMapper.Context mCtx =
- mapper.addContext(null, "/", null, null, null, null);
- mapper.addWrapper(mCtx, "/", service);
- }
-
-
-}
\ No newline at end of file
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/lite/http/Http11Connection.java b/modules/tomcat-lite/java/org/apache/tomcat/lite/http/Http11Connection.java
deleted file mode 100644
index 335333b..0000000
--- a/modules/tomcat-lite/java/org/apache/tomcat/lite/http/Http11Connection.java
+++ /dev/null
@@ -1,1459 +0,0 @@
-/*
- */
-package org.apache.tomcat.lite.http;
-
-import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import org.apache.tomcat.lite.http.HttpConnector.HttpConnection;
-import org.apache.tomcat.lite.http.HttpMessage.HttpMessageBytes;
-import org.apache.tomcat.lite.io.BBucket;
-import org.apache.tomcat.lite.io.BBuffer;
-import org.apache.tomcat.lite.io.CBuffer;
-import org.apache.tomcat.lite.io.DumpChannel;
-import org.apache.tomcat.lite.io.FastHttpDateFormat;
-import org.apache.tomcat.lite.io.Hex;
-import org.apache.tomcat.lite.io.IOBuffer;
-import org.apache.tomcat.lite.io.IOChannel;
-import org.apache.tomcat.lite.io.IOConnector;
-
-public class Http11Connection extends HttpConnection
- implements IOConnector.ConnectedCallback {
- public static final String CHUNKED = "chunked";
-
- public static final String CLOSE = "close";
-
- public static final String KEEPALIVE_S = "keep-alive";
-
- public static final String CONNECTION = "connection";
-
- public static final String TRANSFERENCODING = "transfer-encoding";
-
-
- protected static Logger log = Logger.getLogger("Http11Connection");
- static final byte COLON = (byte) ':';
-
- // super.net is the socket
-
- boolean debug;
- BBuffer line = BBuffer.wrapper();
- boolean endSent = false;
-
- BodyState receiveBodyState = new BodyState();
- BodyState sendBodyState = new BodyState();
-
- BBuffer headW = BBuffer.wrapper();
-
- boolean headersReceived = false;
- boolean bodyReceived = false;
-
- /**
- * Close connection when done writting, no content-length/chunked,
- * or no keep-alive ( http/1.0 ) or error.
- *
- * ServerMode: set if HTTP/0.9 &1.0 || !keep-alive
- * ClientMode: not currently used
- */
- boolean keepAlive = true;
-
- protected boolean http11 = true;
- protected boolean http10 = false;
- protected boolean http09 = false;
-
- HttpConnection switchedProtocol = null;
-
- private int requestCount = 0;
-
- // dataReceived and endSendReceive
- private Object readLock = new Object();
-
- public Http11Connection(HttpConnector httpConnector) {
- this.httpConnector = httpConnector;
- if (httpConnector != null) {
- debug = httpConnector.debugHttp;
- }
- }
-
- public void beforeRequest() {
- nextRequest();
- headRecvBuf.recycle();
- }
-
- public void nextRequest() {
- endSent = false;
- keepAlive = true;
- receiveBodyState.recycle();
- sendBodyState.recycle();
- http11 = true;
- http09 = false;
- http10 = false;
- headersReceived = false;
- bodyReceived = false;
- }
-
- public Http11Connection serverMode() {
- serverMode = true;
- return this;
- }
-
- private boolean readHead() throws IOException {
- while (true) {
- int read;
- if (requestCount == 0 && headRecvBuf.remaining() < 4) {
- // requests have at least 4 bytes - detect protocol
- read = net.getIn().read(headRecvBuf, 4);
- if (read < 0) {
- return closeInHead();
- }
- if (read < 4) {
- return false; // need more
- }
- // we have at least 4 bytes
- if (headRecvBuf.get(0) == 0x80 &&
- headRecvBuf.get(1) == 0x01) {
- // SPDY signature ( experimental )
- switchedProtocol = new SpdyConnection(httpConnector,
- remoteHost);
- if (serverMode) {
- switchedProtocol.serverMode = true;
- }
- switchedProtocol.withExtraBuffer(headRecvBuf);
- // Will also call handleReceived
- switchedProtocol.setSink(net);
- return false;
- }
-
- }
-
- // we know we have one
- read = net.getIn().readLine(headRecvBuf);
- // Remove starting empty lines.
- headRecvBuf.skipEmptyLines();
-
- // Do we have another full line in the input ?
- if (BBuffer.hasLFLF(headRecvBuf)) {
- break; // done
- }
- if (read == 0) { // no more data
- return false;
- }
- if (read < 0) {
- return closeInHead();
- }
- }
-
-
- return true;
- }
-
- private boolean closeInHead() throws IOException {
- if (debug) {
- trace("CLOSE while reading HEAD");
- }
- // too early - we don't have the head
- abort("Close in head");
- return false;
- }
-
- // Unit tests use this to access the HttpChannel
- protected HttpChannel checkHttpChannel() throws IOException {
- if (switchedProtocol != null) {
- return switchedProtocol.checkHttpChannel();
- }
- if (activeHttp == null) {
- if (serverMode) {
- activeHttp = httpConnector.getServer();
- activeHttp.setConnection(this);
- if (httpConnector.defaultService != null) {
- activeHttp.setHttpService(httpConnector.defaultService);
- }
- } else {
- }
- }
- return activeHttp;
- }
-
- @Override
- public void dataReceived(IOBuffer netx) throws IOException {
- if (switchedProtocol != null) {
- switchedProtocol.dataReceived(netx);
- return;
- }
- //trace("handleReceived " + headersReceived);
- if (!checkKeepAliveClient()) {
- return; // we were in client keep alive mode
- }
- // endSendReceived uses same lock - it will call this
- // to check outstanding bytes
- synchronized (readLock) {
- if (bodyReceived) {
- return; // leave data in net buffer, for next req
- }
-
- if (!headersReceived) {
- if (!readHead()) {
- return;
- }
- }
-
- // We have a header
- if (activeHttp == null) {
- if (checkHttpChannel() == null) {
- return;
- }
- }
-
- IOBuffer receiveBody = activeHttp.receiveBody;
-
- if (!headersReceived) {
- headRecvBuf.wrapTo(headW);
- parseMessage(activeHttp, headW);
- // Part of parseMessage we can switch the protocol
- if (switchedProtocol != null) {
- return;
- }
-
- if (serverMode && activeHttp.httpReq.decodedUri.remaining() == 0) {
- abort(activeHttp, "Invalid url");
- }
-
- headersReceived = true;
- // Send header callbacks - we process any incoming data
- // first, so callbacks have more info
- trace("Send headers received callback " + activeHttp.httpService);
- activeHttp.handleHeadersReceived(activeHttp.inMessage);
- }
-
- // any remaining data will be processed as part of the
- // body - or left in the channel until endSendReceive()
-
- if (!bodyReceived) {
- // Will close receiveBody when it consummed enough
- rawDataReceived(activeHttp, receiveBody, net.getIn());
- // Did we process anything ?
- if (receiveBody.getBufferCount() > 0) {
- activeHttp.sendHandleReceivedCallback(); // callback
- }
- }
- // Receive has marked the body as closed
- if (receiveBody.isAppendClosed()) {
- bodyReceived = true;
- activeHttp.handleEndReceive();
- }
-
-
- if (net.getIn().isClosedAndEmpty()) {
- // If not already closed.
- closeStreamOnEnd("closed after body");
- }
-
- }
- }
-
- /**
- * We got data while in client keep alive ( no activeHttp )
- *
- * @return false if there is an error
- */
- private boolean checkKeepAliveClient() throws IOException {
- // Client, no active connection ( keep alive )
- if (!serverMode && activeHttp == null) {
- if (net.getIn().isClosedAndEmpty() || !net.isOpen()) {
- // server disconnected, fine
- httpConnector.cpool.stopKeepAlive(this);
- return false;
- }
- if (net.getIn().available() == 0) {
- return true;
- }
- log.warning("Unexpected message from server in client keep alive "
- + net.getIn() + ": " + net.getIn().readAll(null));
- if (net.isOpen()) {
- net.close();
- }
- return false;
- }
- return true;
- }
-
- private void processProtocol(CBuffer protocolMB) throws IOException {
- http11 = false;
- http09 = false;
- http10 = false;
-
- if (protocolMB.equals(HttpChannel.HTTP_11)) {
- http11 = true;
- } else if (protocolMB.equals(HttpChannel.HTTP_10)) {
- http10 = true;
- } else if (protocolMB.equals("")) {
- http09 = true;
- } else {
- http11 = true; // hopefully will be backward compat
- }
- }
-
- void closeStreamOnEnd(String cause) {
- if (debug) {
- log.info("Not reusing connection because: " + cause);
- }
- keepAlive = false;
- }
-
- boolean keepAlive() {
- if (httpConnector != null) {
- if (serverMode && !httpConnector.serverKeepAlive) {
- keepAlive = false;
- }
- if (!serverMode && !httpConnector.clientKeepAlive) {
- keepAlive = false;
- }
- }
- if (http09) {
- keepAlive = false;
- }
- if (net != null && !net.isOpen()) {
- keepAlive = false;
- }
- return keepAlive;
- }
-
- @Override
- protected void endSendReceive(HttpChannel http) throws IOException {
- if (switchedProtocol != null) {
- switchedProtocol.endSendReceive(http);
- return;
- }
- chunk.recycle();
- rchunk.recycle();
- boolean keepAlive = keepAlive();
- if (!keepAlive) {
- if (debug) {
- log.info("--- Close socket, no keepalive " + net);
- }
- if (net != null) {
- net.close();
- net.startSending();
-
- }
- }
-
- requestCount++;
- beforeRequest();
- httpConnector.cpool.afterRequest(http, this, true);
-
- if (serverMode && keepAlive) {
- handleReceived(net); // will attempt to read next req
- }
- }
-
- private void trace(String s) {
- if(debug) {
- log.info(this.toString() + " " + activeHttp + " " + s);
- }
- }
-
- private boolean isDone(BodyState bodys, IOBuffer body) {
- if (bodys.noBody) {
- return true;
- }
- if (bodys.isContentDelimited()) {
- if (!bodys.chunked && bodys.remaining == 0) {
- return true;
- } else if (bodys.chunked && body.isAppendClosed()) {
- return true;
- }
- }
- return false;
- }
-
- void parseMessage(HttpChannel http, BBuffer headB) throws IOException {
- //Parse the response
- line.recycle();
- headB.readLine(line);
-
- HttpMessageBytes msgBytes;
-
- if (serverMode) {
- msgBytes = http.httpReq.getMsgBytes();
- parseRequestLine(line, msgBytes.method(),
- msgBytes.url(),
- msgBytes.query(),
- msgBytes.protocol());
- } else {
- msgBytes = http.httpRes.getMsgBytes();
- parseResponseLine(line, msgBytes.protocol(),
- msgBytes.status(), msgBytes.message());
- }
-
- parseHeaders(http, msgBytes, headB);
-
- http.inMessage.state = HttpMessage.State.BODY_DATA;
-
- http.inMessage.processReceivedHeaders();
-
- // TODO: hook to allow specific charsets ( can be done later )
- processProtocol(http.inMessage.protocol());
-
- if (serverMode) {
- // requested connection:close/keepAlive and proto
- updateKeepAlive(http.getRequest().getMimeHeaders(), true);
-
- processExpectation(http);
-
- processContentDelimitation(receiveBodyState, http.getRequest());
- // Spec:
- // The presence of a message-body in a request is signaled by the
- // inclusion of a Content-Length or Transfer-Encoding header field in
- // the request's message-headers
- // Server should read - but ignore ..
- receiveBodyState.noBody = !receiveBodyState.isContentDelimited();
-
- updateCloseOnEnd(receiveBodyState, http, http.receiveBody);
-
- /*
- * The presence of a message-body in a request is signaled by the
- * inclusion of a Content-Length or Transfer-Encoding header field in
- * the request's message-headers. A message-body MUST NOT be included
- * in a request if the specification of the request method
- * (section 5.1.1) does not allow sending an entity-body in requests.
- * A server SHOULD read and forward a message-body on any request; if the request method does not include defined semantics for an entity-body, then the message-body SHOULD be ignored when handling the request.
- */
- if (!receiveBodyState.isContentDelimited()) {
- // No body
- http.getIn().close();
- }
-
- } else {
- receiveBodyState.noBody = !http.getResponse().hasBody();
-
- updateKeepAlive(http.getResponse().getMimeHeaders(), false);
-
- if (statusDropsConnection(http.getResponse().getStatus())) {
- closeStreamOnEnd("response status drops connection");
- }
- IOBuffer body = http.receiveBody;
- processContentDelimitation(receiveBodyState, http.getResponse());
-
- if (isDone(receiveBodyState, body)) {
- body.close();
- }
-
- if (!receiveBodyState.isContentDelimited()) {
- closeStreamOnEnd("not content delimited");
- }
- }
-
- }
-
- private void processExpectation(HttpChannel http) throws IOException {
- http.expectation = false;
- MultiMap headers = http.getRequest().getMimeHeaders();
-
- CBuffer expect = headers.getHeader("expect");
- if ((expect != null)
- && (expect.indexOf("100-continue") != -1)) {
- http.expectation = true;
-
- // TODO: configure, use the callback or the servlet 'read'.
- net.getOut().append("HTTP/1.1 100 Continue\r\n\r\n");
- net.startSending();
- }
- }
-
-
- /**
- * Updates chunked, contentLength, remaining - based
- * on headers
- */
- private void processContentDelimitation(BodyState bodys,
- HttpMessage httpMsg) {
-
- bodys.contentLength = httpMsg.getContentLength();
- if (bodys.contentLength >= 0) {
- bodys.remaining = bodys.contentLength;
- }
-
- // TODO: multiple transfer encoding headers, only process the last
- String transferEncodingValue = httpMsg.getHeader(TRANSFERENCODING);
- if (transferEncodingValue != null) {
- int startPos = 0;
- int commaPos = transferEncodingValue.indexOf(',');
- String encodingName = null;
- while (commaPos != -1) {
- encodingName = transferEncodingValue.substring
- (startPos, commaPos).toLowerCase().trim();
- if ("chunked".equalsIgnoreCase(encodingName)) {
- bodys.chunked = true;
- }
- startPos = commaPos + 1;
- commaPos = transferEncodingValue.indexOf(',', startPos);
- }
- encodingName = transferEncodingValue.substring(startPos)
- .toLowerCase().trim();
- if ("chunked".equals(encodingName)) {
- bodys.chunked = true;
- httpMsg.chunked = true;
- } else {
- System.err.println("TODO: ABORT 501");
- //return 501; // Currently only chunked is supported for
- // transfer encoding.
- }
- }
-
- if (bodys.chunked) {
- bodys.remaining = 0;
- }
- }
-
- /**
- * Read the request line. This function is meant to be used during the
- * HTTP request header parsing. Do NOT attempt to read the request body
- * using it.
- *
- * @throws IOException If an exception occurs during the underlying socket
- * read operations, or if the given buffer is not big enough to accomodate
- * the whole line.
- */
- boolean parseRequestLine(BBuffer line,
- BBuffer methodMB, BBuffer requestURIMB,
- BBuffer queryMB,
- BBuffer protoMB)
- throws IOException {
-
- line.readToSpace(methodMB);
- line.skipSpace();
-
- line.readToDelimOrSpace(HttpChannel.QUESTION, requestURIMB);
- if (line.remaining() > 0 && line.get(0) == HttpChannel.QUESTION) {
- // Has query
- line.readToSpace(queryMB);
- // don't include '?'
- queryMB.position(queryMB.position() + 1);
- } else {
- queryMB.setBytes(line.array(), line.position(), 0);
- }
- line.skipSpace();
-
- line.readToSpace(protoMB);
-
- // proto is optional ( for 0.9 )
- return requestURIMB.remaining() > 0;
- }
-
- boolean parseResponseLine(BBuffer line,
- BBuffer protoMB, BBuffer statusCode, BBuffer status)
- throws IOException {
- line.skipEmptyLines();
-
- line.readToSpace(protoMB);
- line.skipSpace();
- line.readToSpace(statusCode);
- line.skipSpace();
- line.wrapTo(status);
-
- // message may be empty
- return statusCode.remaining() > 0;
- }
-
- List<String> connectionHeaders = new ArrayList<String>();
-
- private void parseHeaders(HttpChannel http, HttpMessageBytes msgBytes,
- BBuffer head)
- throws IOException {
-
- head.readLine(line);
-
- int idx = 0;
-
- BBuffer upgrade = null;
-
- while(line.remaining() > 0) {
- // not empty..
- idx = msgBytes.addHeader();
- BBuffer nameBuf = msgBytes.getHeaderName(idx);
- BBuffer valBuf = msgBytes.getHeaderValue(idx);
- parseHeader(http, head, line, nameBuf, valBuf);
-
- // TODO: process 'interesting' headers here.
- if (nameBuf.equalsIgnoreCase("connection")) {
- // TODO: save and remove if not recognized
- }
- if (nameBuf.equalsIgnoreCase("upgrade")) {
- upgrade = valBuf;
- }
- }
-
- if (upgrade != null) {
- if (upgrade.equalsIgnoreCase("WebSocket")) {
-
- } else if (upgrade.equalsIgnoreCase("SPDY/1.0")) {
-
- }
- }
-
- // TODO: process connection headers
- }
-
- /**
- * Parse one header.
- * Line must be populated. On return line will be populated
- * with the next header:
- *
- * @param line current header line, not empty.
- */
- int parseHeader(HttpChannel http, BBuffer head,
- BBuffer line, BBuffer name, BBuffer value)
- throws IOException {
-
- int newPos = line.readToDelimOrSpace(COLON, name);
- line.skipSpace();
- if (line.readByte() != COLON) {
- throw new IOException("Missing ':' in header name " + line);
- }
- line.skipSpace();
- line.read(value); // remaining of the line
-
- while (true) {
- head.readLine(line);
- if (line.remaining() == 0) {
- break;
- }
- int first = line.get(0);
- if (first != BBuffer.SP && first != BBuffer.HT) {
- break;
- }
- // continuation line - append it to value
- value.setEnd(line.getEnd());
- line.position(line.limit());
- }
-
- // We may want to keep the original and use separate buffer ?
- http.normalizeHeader(value);
- return 1;
- }
-
- private int receiveDone(HttpChannel http, IOBuffer body, boolean frameError) throws IOException {
- // Content-length case, we're done reading
- body.close();
-
- http.error = frameError;
- if (frameError) {
- closeStreamOnEnd("frame error");
- }
-
- return DONE;
- }
-
- /**
- * Called when raw body data is received.
- * Callback should not consume past the end of the body.
- * @param rawReceiveBuffers
- *
- */
- private void rawDataReceived(HttpChannel http, IOBuffer body,
- IOBuffer rawReceiveBuffers) throws IOException {
- // TODO: Make sure we don't process more than we need ( eat next req ).
- // If we read too much: leave it in readBuf, the finalzation code
- // should skip KeepAlive and start processing it.
- // we need to read at least something - to detect -1 ( we could
- // suspend right away, but seems safer
- BodyState bodys = receiveBodyState;
-
- while (http.inMessage.state == HttpMessage.State.BODY_DATA) {
- if (receiveBodyState.noBody) {
- receiveDone(http, body, false);
- return;
- }
- if (rawReceiveBuffers.isClosedAndEmpty()) {
- if (receiveBodyState.isContentDelimited()) {
- if (receiveBodyState.contentLength >= 0 && receiveBodyState.remaining == 0) {
- receiveDone(http, body, false);
- } else {
- // End of input - other side closed, no more data
- //log.info("CLOSE while reading " + this);
- // they're not supposed to close !
- receiveDone(http, body, true);
- }
- } else {
- receiveDone(http, body, false); // ok
- }
- // input connection closed ?
- closeStreamOnEnd("Closed input");
- return;
- }
- BBucket rawBuf = rawReceiveBuffers.peekFirst();
- if (rawBuf == null) {
- return; // need more data
- }
-
- if (!bodys.isContentDelimited()) {
- while (true) {
- BBucket first = rawReceiveBuffers.popFirst();
- if (first == null) {
- break; // will go back to check if done.
- } else {
- received(body, first);
- }
- }
- } else {
-
- if (bodys.contentLength >= 0 && bodys.remaining == 0) {
- receiveDone(http, body, false);
- return;
- }
-
- if (bodys.chunked && bodys.remaining == 0) {
- int rc = NEED_MORE;
- // TODO: simplify, use readLine()
- while (rc == NEED_MORE) {
- rc = rchunk.parseChunkHeader(rawReceiveBuffers);
- if (rc == ERROR) {
- http.abort("Chunk error");
- receiveDone(http, body, true);
- return;
- } else if (rc == NEED_MORE) {
- return;
- }
- }
- if (rc == 0) { // last chunk
- receiveDone(http, body, false);
- return;
- } else {
- bodys.remaining = rc;
- }
- }
-
- rawBuf = (BBucket) rawReceiveBuffers.peekFirst();
- if (rawBuf == null) {
- return; // need more data
- }
-
-
- if (bodys.remaining < rawBuf.remaining()) {
- // To buffer has more data than we need.
- int lenToConsume = (int) bodys.remaining;
- BBucket sb = rawReceiveBuffers.popLen(lenToConsume);
- received(body, sb);
- //log.info("Queue received buffer " + this + " " + lenToConsume);
- bodys.remaining = 0;
- } else {
- BBucket first = rawReceiveBuffers.popFirst();
- bodys.remaining -= first.remaining();
- received(body, first);
- //log.info("Queue full received buffer " + this + " RAW: " + rawReceiveBuffers);
- }
- if (bodys.contentLength >= 0 && bodys.remaining == 0) {
- // Content-Length, all done
- body.close();
- receiveDone(http, body, false);
- }
- }
- }
- }
-
- private void received(IOBuffer body, BBucket bb) throws IOException {
- body.queue(bb);
- }
-
-
- protected void sendRequest(HttpChannel http)
- throws IOException {
- if (switchedProtocol != null) {
- switchedProtocol.sendRequest(http);
- return;
- }
-
- // Update transfer fields based on headers.
- processProtocol(http.getRequest().protocol());
- updateKeepAlive(http.getRequest().getMimeHeaders(), true);
-
- // Update Host header
- if (http.getRequest().getMimeHeaders().getHeader("Host") == null) {
- String target = http.getTarget();
- if (target == null) {
- throw new IOException("Missing host header");
- }
- CBuffer hostH = http.getRequest().getMimeHeaders().addValue("Host");
- if (target.endsWith(":80")) {
- hostH.set(target.substring(0, target.length() - 3));
- } else {
- hostH.set(target);
- }
- }
-
- processContentDelimitation(sendBodyState,
- http.getRequest());
-
-
- CBuffer method = http.getRequest().method();
- if (method.equals("GET") || method.equals("HEAD")) {
- // TODO: add the others
- sendBodyState.noBody = true;
- }
-
- // 1.0: The presence of an entity body in a request is signaled by
- // the inclusion of a Content-Length header field in the request
- // message headers. HTTP/1.0 requests containing an entity body
- // must include a valid Content-Length header field.
- if (http10 && !sendBodyState.isContentDelimited()) {
- // Will not close connection - just flush and mark the body
- // as sent
- sendBodyState.noBody = true;
- }
-
- if (sendBodyState.noBody) {
- http.getRequest().getMimeHeaders().remove(HttpChannel.CONTENT_LENGTH);
- http.getRequest().getMimeHeaders().remove(TRANSFERENCODING);
- http.getOut().close();
- } else {
- long contentLength =
- http.getRequest().getContentLength();
- if (contentLength < 0) {
- http.getRequest().getMimeHeaders().addValue("Transfer-Encoding").
- set(CHUNKED);
- }
- }
-
- updateCloseOnEnd(sendBodyState, http, http.sendBody);
-
- try {
- serialize(http.getRequest(), net.getOut());
- if (http.debug) {
- http.trace("S: \n" + net.getOut());
- }
-
- if (http.outMessage.state == HttpMessage.State.HEAD) {
- http.outMessage.state = HttpMessage.State.BODY_DATA;
- }
-
-
- // TODO: add any body and flush. More body can be added later -
- // including 'end'.
-
- http.startSending();
- } catch (Throwable t) {
- log.log(Level.SEVERE, "Error sending request", t);
- abort(t.getMessage());
- }
-
- }
-
-
- /**
- * Determine if we must drop the connection because of the HTTP status
- * code. Use the same list of codes as Apache/httpd.
- */
- private boolean statusDropsConnection(int status) {
- return status == 400 /* SC_BAD_REQUEST */ ||
- status == 408 /* SC_REQUEST_TIMEOUT */ ||
- status == 411 /* SC_LENGTH_REQUIRED */ ||
- status == 413 /* SC_REQUEST_ENTITY_TOO_LARGE */ ||
- status == 414 /* SC_REQUEST_URI_TOO_LARGE */ ||
- status == 500 /* SC_INTERNAL_SERVER_ERROR */ ||
- status == 503 /* SC_SERVICE_UNAVAILABLE */ ||
- status == 501 /* SC_NOT_IMPLEMENTED */;
- }
-
- protected void sendResponseHeaders(HttpChannel http)
- throws IOException {
- if (switchedProtocol != null) {
- switchedProtocol.sendResponseHeaders(http);
- return;
- }
-
- if (!serverMode) {
- throw new IOException("Only in server mode");
- }
- endSent = false;
- IOBuffer sendBody = http.sendBody;
- HttpResponse res = http.getResponse();
- if (res.isCommitted()) {
- return;
- }
- res.setCommitted(true);
-
- sendBodyState.noBody = !res.hasBody();
-
- if (statusDropsConnection(res.getStatus())) {
- closeStreamOnEnd("status drops connection");
- }
- if (http.error) {
- closeStreamOnEnd("error");
- }
-
- MultiMap headers = res.getMimeHeaders();
-
- // Add date header
- if (headers.getHeader("Date") == null) {
- headers.setValue("Date").set(FastHttpDateFormat.getCurrentDate());
- }
-
- // Add server header
- if (http.serverHeader.length() > 0) {
- headers.setValue("Server").set(http.serverHeader);
- }
-
- // Decide on a transfer encoding for out.
- if (keepAlive()) { // request and user allows keep alive
- int cl = res.getContentLength();
-
- if (http10) {
- if (cl < 0 && !sendBodyState.noBody &&
- sendBody.isAppendClosed()) {
- // We can generate content-lenght
- cl = sendBody.available();
- res.setContentLength(cl);
- }
- if (cl < 0 && !sendBodyState.noBody) {
- closeStreamOnEnd("HTTP/1.0 without content length");
- } else {
- headers.setValue(CONNECTION).set(KEEPALIVE_S);
- }
- } else { // http11
- if (!sendBodyState.noBody) {
- if (cl < 0) {
- res.getMimeHeaders().setValue(TRANSFERENCODING).set(CHUNKED);
- }
- }
- }
- } else {
- headers.setValue(CONNECTION).set(CLOSE);
- // since we close the connection - don't bother with
- // transfer encoding
- headers.remove(TRANSFERENCODING);
- }
-
- // Update our internal state based on headers we just set.
- processContentDelimitation(sendBodyState, res);
- updateCloseOnEnd(sendBodyState, http, sendBody);
-
-
- if (http.debug) {
- http.trace("Send response headers " + net);
- }
- if (net != null) {
- serialize(res, net.getOut());
- }
-
- if (http.outMessage.state == HttpMessage.State.HEAD) {
- http.outMessage.state = HttpMessage.State.BODY_DATA;
- }
-
- if (isDone(sendBodyState, sendBody)) {
- http.getOut().close();
- }
-
- if (net != null) {
- net.startSending();
- }
- }
-
- private void abort(String t) throws IOException {
- abort(activeHttp, t);
- }
-
- private void updateCloseOnEnd(BodyState bodys, HttpChannel http, IOBuffer body) {
- if (!bodys.isContentDelimited() && !bodys.noBody) {
- closeStreamOnEnd("not content delimited");
- }
- }
-
- /**
- * Disconnect abruptly - client closed, frame errors, etc
- * @param t
- * @throws IOException
- */
- public void abort(HttpChannel http, String t) throws IOException {
- if (switchedProtocol != null) {
- switchedProtocol.abort(http, t);
- return;
- }
- keepAlive = false;
- if (net != null ) {
- if (net.isOpen()) {
- net.close();
- net.startSending();
- }
- }
- if (http != null) {
- http.abort(t);
- }
- }
-
- /**
- * Update keepAlive based on Connection header and protocol.
- */
- private void updateKeepAlive(MultiMap headers, boolean request) {
- if (http09) {
- closeStreamOnEnd("http 0.9");
- return;
- }
-
- // TODO: also need to remove headers matching connection
- // ( like 'upgrade')
-
- CBuffer value = headers.getHeader(CONNECTION);
- // TODO: split it by space
- if (value != null) {
- value.toLower();
- if (value.indexOf(CLOSE) >= 0) {
- // 1.1 ( but we accept it for 1.0 too )
- closeStreamOnEnd("connection close");
- }
- if (http10 && value.indexOf(KEEPALIVE_S) < 0) {
- // Keep-Alive required for http/1.0
- closeStreamOnEnd("connection != keep alive");
- }
- // we have connection: keepalive, good
- } else {
- // no connection header - for 1.1 default is keepAlive,
- // for 10 it's close
- if (http10) {
- closeStreamOnEnd("http1.0 no connection header");
- }
- }
- }
-
- @Override
- public void startSending() throws IOException {
- if (switchedProtocol != null) {
- switchedProtocol.startSending();
- return;
- }
-
- }
-
- @Override
- public void startSending(HttpChannel http) throws IOException {
- if (switchedProtocol != null) {
- switchedProtocol.startSending(http);
- return;
- }
- http.send(); // if needed
-
- if (net == null) {
- return; // not connected yet.
- }
-
- if (net.getOut().isAppendClosed()) {
- abort("Net closed");
- } else {
- flushToNext(http.sendBody, net.getOut());
- net.startSending();
- }
-
- }
-
- protected void outClosed(HttpChannel http) throws IOException {
- if (switchedProtocol != null) {
- switchedProtocol.outClosed(http);
- return;
- }
- // TODO: move it ?
- if (sendBodyState.isContentDelimited() && !http.error) {
- if (!sendBodyState.chunked &&
- sendBodyState.remaining - http.getOut().available() > 0) {
- http.abort("CLOSE CALLED WITHOUT FULL LEN");
- }
- }
-
- }
-
- @Override
- public void handleFlushed(IOChannel net) throws IOException {
- if (switchedProtocol != null) {
- switchedProtocol.handleFlushed(net);
- return;
- }
- if (activeHttp != null) {
- activeHttp.flushLock.signal(this);
- activeHttp.handleFlushed(this);
- if (activeHttp.sendBody.isClosedAndEmpty()) {
- activeHttp.handleEndSent();
- }
- }
- }
-
-
- private void flushToNext(IOBuffer body, IOBuffer out) throws IOException {
-
- synchronized (this) {
- // TODO: better head support
- if (sendBodyState.noBody) {
- for (int i = 0; i < body.getBufferCount(); i++) {
- Object bc = body.peekBucket(i);
- if (bc instanceof BBucket) {
- ((BBucket) bc).release();
- }
- }
- body.clear();
- return;
- }
-
- // TODO: only send < remainingWrite, if buffer
- // keeps changing after startWrite() is called (shouldn't)
-
- if (sendBodyState.chunked) {
- sendChunked(sendBodyState, body, out);
- } else if (sendBodyState.contentLength >= 0) {
- // content-length based
- sendContentLen(sendBodyState, body, out);
- } else {
- sendCloseDelimited(body, out);
- }
- }
- }
-
- private void sendCloseDelimited(IOBuffer body, IOBuffer out) throws IOException {
- // Close delimitation
- while (true) {
- Object bc = body.popFirst();
- if (bc == null) {
- break;
- }
- out.queue(bc);
- }
- if (body.isClosedAndEmpty()) {
- out.close(); // no content-delimitation
- }
- }
-
- /**
- * Convert the request to bytes, ready to send.
- */
- public static void serialize(HttpRequest req, IOBuffer rawSendBuffers2) throws IOException {
- rawSendBuffers2.append(req.method());
- rawSendBuffers2.append(BBuffer.SP);
-
- // TODO: encode or use decoded
- rawSendBuffers2.append(req.requestURI());
- if (req.queryString().length() > 0) {
- rawSendBuffers2.append("?");
- rawSendBuffers2.append(req.queryString());
- }
-
- rawSendBuffers2.append(BBuffer.SP);
- rawSendBuffers2.append(req.protocol());
- rawSendBuffers2.append(BBuffer.CRLF_BYTES);
-
- serializeHeaders(req.getMimeHeaders(), rawSendBuffers2);
- }
-
- /**
- * Convert the response to bytes, ready to send.
- */
- public static void serialize(HttpResponse res, IOBuffer rawSendBuffers2) throws IOException {
-
- rawSendBuffers2.append(res.protocol()).append(' ');
- String status = Integer.toString(res.getStatus());
- rawSendBuffers2.append(status).append(' ');
- if (res.getMessageBuffer().length() > 0) {
- rawSendBuffers2.append(res.getMessage());
- } else {
- rawSendBuffers2
- .append(res.getMessage(res.getStatus()));
- }
- rawSendBuffers2.append(BBuffer.CRLF_BYTES);
- // Headers
- serializeHeaders(res.getMimeHeaders(), rawSendBuffers2);
- }
-
- public static void serializeHeaders(MultiMap mimeHeaders, IOBuffer rawSendBuffers2) throws IOException {
- for (int i = 0; i < mimeHeaders.size(); i++) {
- CBuffer name = mimeHeaders.getName(i);
- CBuffer value = mimeHeaders.getValue(i);
- if (name.length() == 0 || value.length() == 0) {
- continue;
- }
- rawSendBuffers2.append(name);
- rawSendBuffers2.append(Http11Connection.COLON);
- rawSendBuffers2.append(value);
- rawSendBuffers2.append(BBuffer.CRLF_BYTES);
- }
- rawSendBuffers2.append(BBuffer.CRLF_BYTES);
- }
-
-
- private boolean sendContentLen(BodyState bodys, IOBuffer body, IOBuffer out) throws IOException {
- while (true) {
- BBucket bucket = body.peekFirst();
- if (bucket == null) {
- break;
- }
- int len = bucket.remaining();
- if (len <= bodys.remaining) {
- bodys.remaining -= len;
- bucket = body.popFirst();
- out.queue(bucket);
- } else {
- // Write over the end of the buffer !
- log.severe("write more than Content-Length");
- len = (int) bodys.remaining;
- // data between position and limit
- bucket = body.popLen((int) bodys.remaining);
- out.queue(bucket);
- while (bucket != null) {
- bucket = body.popFirst();
- if (bucket != null) {
- bucket.release();
- }
- }
-
- // forced close
- //close();
- bodys.remaining = 0;
- return true;
- }
- }
- if (body.isClosedAndEmpty()) {
- //http.rawSendBuffers.queue(IOBrigade.MARK);
- if (bodys.remaining > 0) {
- closeStreamOnEnd("sent more than content-length");
- log.severe("Content-Length > body");
- }
- return true;
- }
- return false;
- }
-
- private boolean sendChunked(BodyState bodys, IOBuffer body, IOBuffer out) throws IOException {
- int len = body.available();
-
- if (len > 0) {
- ByteBuffer sendChunkBuffer = chunk.prepareChunkHeader(len);
- bodys.remaining = len;
- out.queue(sendChunkBuffer);
- while (bodys.remaining > 0) {
- BBucket bc = body.popFirst();
- bodys.remaining -= bc.remaining();
- out.queue(bc);
- }
- }
-
- if (body.isClosedAndEmpty()) {
- synchronized(this) {
- if (!endSent) {
- out.append(chunk.endChunk());
- endSent = true;
- }
- }
- return true;
- } else {
- return false;
- }
- }
-
- // used for chunk parsing/end
- ChunkState chunk = new ChunkState();
- ChunkState rchunk = new ChunkState();
- static final int NEED_MORE = -1;
- static final int ERROR = -4;
- static final int DONE = -5;
-
-
- static class ChunkState {
- static byte[] END_CHUNK_BYTES = {
- (byte) '\r', (byte) '\n',
- (byte) '0',
- (byte) '\r', (byte) '\n',
- (byte) '\r', (byte) '\n'};
-
-
- int partialChunkLen;
- boolean readDigit = false;
- boolean trailer = false;
- protected boolean needChunkCrlf = false;
-
- // Buffer used for chunk length conversion.
- protected byte[] sendChunkLength = new byte[10];
-
- /** End chunk marker - will include chunked end or empty */
- protected BBuffer endSendBuffer = BBuffer.wrapper();
-
- public ChunkState() {
- sendChunkLength[8] = (byte) '\r';
- sendChunkLength[9] = (byte) '\n';
- }
-
- void recycle() {
- partialChunkLen = 0;
- readDigit = false;
- trailer = false;
- needChunkCrlf = false;
- endSendBuffer.recycle();
- }
-
- /**
- * Parse the header of a chunk.
- * A chunk header can look like
- * A10CRLF
- * F23;chunk-extension to be ignoredCRLF
- * The letters before CRLF but after the trailer mark, must be valid hex digits,
- * we should not parse F23IAMGONNAMESSTHISUP34CRLF as a valid header
- * according to spec
- */
- int parseChunkHeader(IOBuffer buffer) throws IOException {
- if (buffer.peekFirst() == null) {
- return NEED_MORE;
- }
- if (needChunkCrlf) {
- // TODO: Trailing headers
- int c = buffer.read();
- if (c == BBuffer.CR) {
- if (buffer.peekFirst() == null) {
- return NEED_MORE;
- }
- c = buffer.read();
- }
- if (c == BBuffer.LF) {
- needChunkCrlf = false;
- } else {
- System.err.println("Bad CRLF " + c);
- return ERROR;
- }
- }
-
- while (true) {
- if (buffer.peekFirst() == null) {
- return NEED_MORE;
- }
- int c = buffer.read();
-
- if (c == BBuffer.CR) {
- continue;
- } else if (c == BBuffer.LF) {
- break;
- } else if (c == HttpChannel.SEMI_COLON) {
- trailer = true;
- } else if (c == BBuffer.SP) {
- // ignore
- } else if (trailer) {
- // ignore
- } else {
- //don't read data after the trailer
- if (Hex.DEC[c] != -1) {
- readDigit = true;
- partialChunkLen *= 16;
- partialChunkLen += Hex.DEC[c];
- } else {
- //we shouldn't allow invalid, non hex characters
- //in the chunked header
- log.info("Chunk parsing error1 " + c + " " + buffer);
- //http.abort("Chunk error");
- return ERROR;
- }
- }
- }
-
- if (!readDigit) {
- log.info("Chunk parsing error2 " + buffer);
- return ERROR;
- }
-
- needChunkCrlf = true; // next time I need to parse CRLF
- int result = partialChunkLen;
- partialChunkLen = 0;
- trailer = false;
- readDigit = false;
- return result;
- }
-
-
- ByteBuffer prepareChunkHeader(int current) {
- int pos = 7; // 8, 9 are CRLF
- while (current > 0) {
- int digit = current % 16;
- current = current / 16;
- sendChunkLength[pos--] = Hex.HEX[digit];
- }
- if (needChunkCrlf) {
- sendChunkLength[pos--] = (byte) '\n';
- sendChunkLength[pos--] = (byte) '\r';
- } else {
- needChunkCrlf = true;
- }
- // TODO: pool - this may stay in the queue while we flush more
- ByteBuffer chunkBB = ByteBuffer.allocate(16);
- chunkBB.put(sendChunkLength, pos + 1, 9 - pos);
- chunkBB.flip();
- return chunkBB;
- }
-
- public BBuffer endChunk() {
- if (! needChunkCrlf) {
- endSendBuffer.setBytes(END_CHUNK_BYTES, 2,
- END_CHUNK_BYTES.length - 2); // CRLF
- } else { // 0
- endSendBuffer.setBytes(END_CHUNK_BYTES, 0,
- END_CHUNK_BYTES.length);
- }
- return endSendBuffer;
- }
- }
-
- static class BodyState {
- /** response: HEAD or 1xx, 204, 304 status
- * req: missing content-length or transfer-encoding
- */
- protected boolean noBody = false;
- protected boolean chunked = false;
- protected long contentLength = -1; // C-L header
- /** Bytes remaining in the current chunk or body ( if CL ) */
- protected long remaining = 0; // both chunked and C-L
-
- public void recycle() {
- chunked = false;
- remaining = 0;
- contentLength = -1;
- noBody = false;
- }
- public boolean isContentDelimited() {
- return chunked || contentLength >= 0;
- }
-
- }
-
- public String toString() {
- if (switchedProtocol != null) {
- return switchedProtocol.toString();
- }
-
- return (serverMode ? "SR " : "CL ") +
- (keepAlive() ? " KA " : "") +
- (headersReceived ? " HEAD " : "") +
- (bodyReceived ? " BODY " : "")
- ;
- }
-
- @Override
- public void handleConnected(IOChannel net) throws IOException {
- HttpChannel httpCh = activeHttp;
-
- if (!net.isOpen()) {
- httpCh.abort(net.lastException());
- return;
- }
-
- boolean ssl = httpCh.getRequest().isSecure();
- if (ssl) {
- String[] hostPort = httpCh.getTarget().split(":");
-
- IOChannel ch1 = httpConnector.sslProvider.channel(net,
- hostPort[0], Integer.parseInt(hostPort[1]));
- //net.setHead(ch1);
- net = ch1;
- }
- if (httpConnector.debugHttp) {
- net = DumpChannel.wrap("Http-Client-", net);
- }
-
- setSink(net);
-
- sendRequest(httpCh);
- }
-
-}
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/lite/http/HttpChannel.java b/modules/tomcat-lite/java/org/apache/tomcat/lite/http/HttpChannel.java
deleted file mode 100644
index cb4b2ad..0000000
--- a/modules/tomcat-lite/java/org/apache/tomcat/lite/http/HttpChannel.java
+++ /dev/null
@@ -1,830 +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.lite.http;
-
-import java.io.IOException;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.logging.Logger;
-
-import org.apache.tomcat.lite.http.HttpConnector.HttpConnection;
-import org.apache.tomcat.lite.io.BBucket;
-import org.apache.tomcat.lite.io.BBuffer;
-import org.apache.tomcat.lite.io.FutureCallbacks;
-import org.apache.tomcat.lite.io.IOBuffer;
-import org.apache.tomcat.lite.io.IOChannel;
-import org.apache.tomcat.lite.io.IOConnector;
-
-/**
- * HTTP async client and server, based on tomcat NIO/APR connectors
- *
- * 'Input', 'read', 'Recv' refers to information we get from the remote side -
- * the request body for server-mode or response body for client.
- *
- * 'Output', 'write', 'Send' is for info we send - the post in client mode
- * and the response body for server mode.
- *
- * @author Costin Manolache
- */
-public class HttpChannel extends IOChannel {
-
- static final int HEADER_SIZE = 8192;
-
- static AtomicInteger serCnt = new AtomicInteger();
-
- public static final String CONTENT_LENGTH= "Content-Length";
-
- public static final String HTTP_10 = "HTTP/1.0";
-
- public static final String HTTP_11 = "HTTP/1.1";
-
- /**
- * SEMI_COLON.
- */
- public static final byte SEMI_COLON = (byte) ';';
-
- public static final byte QUESTION = (byte) '?';
-
-
- protected static Logger log = Logger.getLogger("HttpChannel");
-
-
- boolean debug = false;
-
- // ---- Callbacks and locks
-
- FutureCallbacks<HttpChannel> doneLock = new FutureCallbacks<HttpChannel>();
- FutureCallbacks<HttpChannel> headersReceivedLock =
- new FutureCallbacks<HttpChannel>();
- /**
- * Called when the incoming headers have been received.
- * ( response for client mode, request for server mode )
- * @throws IOException
- */
- protected HttpService httpService;
- /**
- * Called when:
- * - body sent
- * - body received
- * - release() called - either service() done or client done with the
- * buffers.
- *
- * After this callback:
- * - socket closed if closeOnEndSend, or put in keep-alive
- * - AsyncHttp.recycle()
- * - returned to the pool.
- */
- private RequestCompleted doneAllCallback;
- protected boolean sendReceiveDone = false;
-
- // Will be signalled (open) when the buffer is empty.
- FutureCallbacks<IOChannel> flushLock = new FutureCallbacks<IOChannel>();
-
- FutureCallbacks<HttpChannel> doneFuture;
- boolean doneCallbackCalled = false;
-
-
- // ----------
-
- // Set if Exect: 100-continue was set on reqest.
- // If this is the case - body won't be sent until
- // server responds ( client ) and server will only
- // read body after ack() - or skip to next request
- // without swallowing the body.
- protected boolean expectation = false;
-
- /** Ready for recycle, if send/receive are done */
- protected boolean release = false;
-
- // -----------
-
- protected boolean headersDone = false;
- protected boolean error = false;
- protected boolean abortDone = false;
-
-
- protected int ser; // id - for jmx registration and logs
- protected int channelId;
-
- /**
- * Null after endSendReceive and before sending the request
- */
- HttpConnection conn;
-
- HttpConnector httpConnector;
-
- // Different ways to point to request response (server/client)
- HttpRequest httpReq;
- HttpResponse httpRes;
- HttpMessage inMessage;
- HttpMessage outMessage;
- // receive can be for request ( server mode ) or response ( client )
- IOBuffer receiveBody = new IOBuffer();
-
- // notify us that user called close()
- IOBuffer sendBody = new IOBuffer() {
- public void close() throws IOException {
- if (isAppendClosed()) {
- return;
- }
- super.close();
- outClosed();
- }
- };
-
-
- // Server side only
- protected String serverHeader = "TomcatLite";
-
- long ioTimeout = 30 * 60000; // 30 min seems high enough
-
-
- public HttpChannel() {
- ser = serCnt.incrementAndGet();
- httpReq = new HttpRequest(this);
- httpRes = new HttpResponse(this);
- init();
- serverMode(false);
- }
-
- /**
- * Close the connection, return to pool. Called if a
- * framing error happens, or if we want to force the connection
- * to close, without waiting for all data to be sent/received.
- * @param t
- *
- * @throws IOException
- */
- public void abort(Throwable t) {
- abort(t.toString());
- }
-
- public void abort(String t) {
- synchronized (this) {
- if (abortDone) {
- return;
- }
- abortDone = true;
- }
- try {
- checkRelease();
- trace("abort " + t);
- if (conn != null) {
- conn.abort(this, t);
- }
- inMessage.state = HttpMessage.State.DONE;
- outMessage.state = HttpMessage.State.DONE;
- sendReceiveDone = true;
- error = true;
- handleEndSendReceive();
- } catch (Throwable ex) {
- log.severe("Exception in abort " + ex);
- }
- }
-
- /**
- * If release was called - throw exception, you shouldn't use
- * the object again.
- * @throws IOException
- */
- private void checkRelease() throws IOException {
- if (release && sendReceiveDone) {
- throw new IOException("Object released");
- }
- }
-
- public IOChannel getSink() {
- if (conn == null) {
- return null;
- }
- return conn.getSink();
- }
-
-
- /**
- * Called when the request is done. Need to send remaining byte.
- *
- */
- public void complete() throws IOException {
- checkRelease();
- if (!getOut().isAppendClosed()) {
- getOut().close();
- }
- if (!getIn().isAppendClosed()) {
- getIn().close();
- }
-
- startSending();
- }
-
- public int doRead(BBuffer chunk)
- throws IOException {
- checkRelease();
- BBucket next = null;
- while (true) {
- getIn().waitData(0);
- next = (BBucket) getIn().popFirst();
- if (next != null) {
- break;
- } else if (getIn().isAppendClosed()) {
- return -1;
- } else {
- System.err.println("Spurious waitData signal, no data");
- }
- }
- chunk.append(next.array(), next.position(), next.remaining());
- int read = next.remaining();
- next.release();
- return read;
- }
-
- public HttpConnector getConnector() {
- return httpConnector;
- }
-
- public boolean getError() {
- return error;
- }
-
- // ---------------- Writting -------------------------------
-
- public String getId() {
- return Integer.toString(ser);
- }
-
- public IOBuffer getIn() {
- return receiveBody;
- }
-
-
- public long getIOTimeout() {
- return ioTimeout;
- }
-
- // TODO: replace with getSocketChannel - used for remote addr, etc
- public IOChannel getNet() {
- if (conn == null) {
- return null;
- }
- return conn.getSink();
- }
-
-
- public IOBuffer getOut() {
- return sendBody;
- }
-
- public HttpRequest getRequest() {
- return httpReq;
- }
-
-
- public HttpResponse getResponse() {
- return httpRes;
- }
-
-
- public String getState() {
- return
- conn +
- "RCV=[" + inMessage.state.toString() + " " +
- receiveBody.toString()
- + "] SND=[" + outMessage.state.toString()
- + " " + sendBody.toString() + "]";
- }
-
-
- public String getStatus() {
- return getResponse().getStatus() + " " + getResponse().getMessage();
- }
-
-
- public String getTarget() {
- if (target == null) {
- return ":0"; // server mode ?
- }
- return target.toString();
- }
-
-
- /**
- * Called from IO thread, after the request body
- * is completed ( or if there is no req body )
- * @throws IOException
- */
- protected void handleEndReceive() throws IOException {
- if (inMessage.state == HttpMessage.State.DONE) {
- return;
- }
- if (debug) {
- trace("END_RECV");
- }
- getIn().close();
-
- inMessage.state = HttpMessage.State.DONE;
- handleEndSendReceive();
- }
-
- /*
- * Called when sending, receiving and processing is done.
- * Can be called:
- * - from IO thread, if this is a result of a read/write event that
- * finished the send/recev pair.
- * - from an arbitrary thread, if read was complete and the last write
- * was a success and done in that thread ( write is not bound to IO thr)
- *
- */
- protected void handleEndSendReceive() throws IOException {
- // make sure the callback was called ( needed for abort )
- handleHeadersReceived(inMessage);
-
- this.doneLock.signal(this);
- synchronized (this) {
- if (doneCallbackCalled) {
- return;
- }
- if (outMessage.state != HttpMessage.State.DONE ||
- inMessage.state != HttpMessage.State.DONE) {
- return;
- }
- doneCallbackCalled = true;
- }
-
- getIn().close();
-
- if (doneAllCallback != null) {
- doneAllCallback.handle(this, error ? new Throwable() : null);
- }
-
- if (conn != null) {
- conn.endSendReceive(this);
- }
-
- conn = null;
-
- if (debug) {
- trace("END_SEND_RECEIVE"
- + (release ? " REL" : ""));
- }
-
- synchronized(this) {
- sendReceiveDone = true;
- maybeRelease();
- }
- }
-
- /**
- * called from IO thread OR servlet thread when last block has been sent.
- * If not using the socket ( net.getOut().flushCallback ) - this must
- * be called explicitely after flushing the body.
- */
- void handleEndSent() throws IOException {
- if (outMessage.state == HttpMessage.State.DONE) {
- // Only once.
- if (debug) {
- trace("Duplicate END SEND");
- }
- return;
- }
- outMessage.state = HttpMessage.State.DONE;
-
- getOut().close();
-
- // Make sure the send/receive callback is called once
- if (debug) {
- trace("END_SEND");
- }
- handleEndSendReceive();
- }
-
- // ----- End Selector thread callbacks ----
- public void handleError(String type) {
- System.err.println("Error " + type + " " + outMessage.state);
- }
-
- void handleHeadersReceived(HttpMessage in) throws IOException {
- if (!headersDone) {
- headersDone = true;
- headersReceivedLock.signal(this);
- if (httpService != null) {
- try {
- httpService.service(getRequest(), getResponse());
- } catch (Throwable t) {
- t.printStackTrace();
- abort(t);
- }
- }
- }
- }
-
-
- private void init() {
- headersDone = false;
- sendReceiveDone = false;
-
- receiveBody.recycle();
- sendBody.recycle();
- expectation = false;
-
- error = false;
- abortDone = false;
-
-
- getRequest().recycle();
- getResponse().recycle();
- target = null;
-
- doneLock.recycle();
- headersReceivedLock.recycle();
- flushLock.recycle();
-
- doneCallbackCalled = false;
- // Will be set again after pool
- setHttpService(null);
- doneAllCallback = null;
- release = false;
- }
-
- public boolean isDone() {
- return outMessage.state == HttpMessage.State.DONE && inMessage.state == HttpMessage.State.DONE;
- }
-
- /**
- * Called when all done:
- * - service finished ( endService was called )
- * - output written
- * - input read
- *
- * or by abort().
- *
- * @throws IOException
- */
- private void maybeRelease() throws IOException {
- synchronized (this) {
- if (release && sendReceiveDone) {
- if (debug) {
- trace("RELEASE");
- }
- if (getConnector() != null) {
- getConnector().returnToPool(this);
- } else {
- log.severe("Attempt to release with no pool");
- }
- }
- }
- }
-
-
- /*
- The field-content does not include any leading or trailing LWS:
- linear white space occurring before the first non-whitespace
- character of the field-value or after the last non-whitespace
- character of the field-value. Such leading or trailing LWS MAY
- be removed without changing the semantics of the field value.
- Any LWS that occurs between field-content MAY be replaced with
- a single Http11Parser.SP before interpreting the field value or forwarding
- the message downstream.
- */
- int normalizeHeader(BBuffer value) {
- byte[] buf = value.array();
- int cstart = value.position();
- int end = value.limit();
-
- int realPos = cstart;
- int lastChar = cstart;
- byte chr = 0;
- boolean gotSpace = true;
-
- for (int i = cstart; i < end; i++) {
- chr = buf[i];
- if (chr == BBuffer.CR) {
- // skip
- } else if(chr == BBuffer.LF) {
- // skip
- } else if (chr == BBuffer.SP || chr == BBuffer.HT) {
- if (gotSpace) {
- // skip
- } else {
- buf[realPos++] = BBuffer.SP;
- gotSpace = true;
- }
- } else {
- buf[realPos++] = chr;
- lastChar = realPos; // to skip trailing spaces
- gotSpace = false;
- }
- }
- realPos = lastChar;
-
- // so buffer is clean
- for (int i = realPos; i < end; i++) {
- buf[i] = BBuffer.SP;
- }
- value.setEnd(realPos);
- return realPos;
- }
-
-
- protected void recycle() {
- if (debug) {
- trace("RECYCLE");
- }
- init();
- }
-
- /**
- * Finalize sending and receiving.
- * Indicates client is no longer interested, some IO may still be in flight.
- * If in a POST and you're not interested in the body - it may be
- * better to call abort().
- *
- * MUST be called to allow connection reuse and pooling.
- *
- * @throws IOException
- */
- public void release() throws IOException {
- synchronized(this) {
- if (release) {
- return;
- }
- trace("RELEASE");
- release = true;
- // If send/receive is done - we can reuse this object
- maybeRelease();
- }
- }
-
- public void send() throws IOException {
- checkRelease();
- if (httpReq == inMessage) {
- conn.sendResponseHeaders(this);
- } else {
- if (getRequest().isCommitted()) {
- return;
- }
- getRequest().setCommitted(true);
-
- outMessage.state = HttpMessage.State.HEAD;
-
- getConnector().connectAndSend(this);
- }
- }
-
- /** Called when the outgoing stream is closed:
- * - by an explicit call to close()
- * - when all content has been sent.
- */
- protected void outClosed() throws IOException {
- if (conn != null) {
- conn.outClosed(this);
- }
- }
-
- public HttpChannel serverMode(boolean enabled) {
- if (enabled) {
- httpReq.setBody(receiveBody);
- httpRes.setBody(sendBody);
- inMessage = httpReq;
- outMessage = httpRes;
- } else {
- httpReq.setBody(sendBody);
- httpRes.setBody(receiveBody);
- inMessage = httpRes;
- outMessage = httpReq;
- }
- if (debug) {
- }
- return this;
- }
-
- public void setCompletedCallback(RequestCompleted doneAllCallback)
- throws IOException {
- this.doneAllCallback = doneAllCallback;
- synchronized (this) {
- if (doneCallbackCalled) {
- return;
- }
- if (outMessage.state != HttpMessage.State.DONE || inMessage.state != HttpMessage.State.DONE) {
- return;
- }
- }
- doneCallbackCalled = true;
- if (doneAllCallback != null) {
- doneAllCallback.handle(this, error ? new Throwable() : null);
- }
- }
-
- public void setConnector(HttpConnector pool) {
- this.httpConnector = pool;
- }
-
- public void setHttpService(HttpService headersReceivedCallback) {
- this.httpService = headersReceivedCallback;
- }
-
- public void setIOTimeout(long timeout) {
- ioTimeout = timeout;
- }
-
-
- public void setTarget(String host) {
- this.target = host;
- }
-
- public void startSending() throws IOException {
- checkRelease();
- if (conn != null) {
- conn.startSending(this);
- }
- }
-
- public String toString() {
- StringBuffer sb = new StringBuffer();
- sb.append("id=").append(ser)
- .append(",rs=").append(getState())
- .append(")");
- return sb.toString();
- }
-
-
- void trace(String msg) {
- if(debug) {
- log.info(this.toString() + " " + msg + " done=" + doneCallbackCalled);
- }
- }
-
- @Override
- public void waitFlush(long timeMs) throws IOException {
- if (getOut().getBufferCount() == 0) {
- return;
- }
- flushLock.waitSignal(timeMs);
- }
-
- public HttpChannel setConnection(HttpConnection conn) {
- this.conn = conn;
- return this;
- }
-
- /**
- * Normalize URI.
- * <p>
- * This method normalizes "\", "//", "/./" and "/../". This method will
- * return false when trying to go above the root, or if the URI contains
- * a null byte.
- *
- * @param uriMB URI to be normalized, will be modified
- */
- public static boolean normalize(BBuffer uriBC) {
-
- byte[] b = uriBC.array();
- int start = uriBC.getStart();
- int end = uriBC.getEnd();
-
- // URL * is acceptable
- if ((end - start == 1) && b[start] == (byte) '*')
- return true;
-
- if (b[start] != '/') {
- // TODO: http://.... URLs
- return true;
- }
-
- int pos = 0;
- int index = 0;
-
- // Replace '\' with '/'
- // Check for null byte
- for (pos = start; pos < end; pos++) {
- if (b[pos] == (byte) '\\')
- b[pos] = (byte) '/';
- if (b[pos] == (byte) 0)
- return false;
- }
-
- // The URL must start with '/'
- if (b[start] != (byte) '/') {
- return false;
- }
-
- // Replace "//" with "/"
- for (pos = start; pos < (end - 1); pos++) {
- if (b[pos] == (byte) '/') {
- while ((pos + 1 < end) && (b[pos + 1] == (byte) '/')) {
- copyBytes(b, pos, pos + 1, end - pos - 1);
- end--;
- }
- }
- }
-
- // If the URI ends with "/." or "/..", then we append an extra "/"
- // Note: It is possible to extend the URI by 1 without any side effect
- // as the next character is a non-significant WS.
- if (((end - start) >= 2) && (b[end - 1] == (byte) '.')) {
- if ((b[end - 2] == (byte) '/')
- || ((b[end - 2] == (byte) '.')
- && (b[end - 3] == (byte) '/'))) {
- b[end] = (byte) '/';
- end++;
- }
- }
-
- uriBC.setEnd(end);
-
- index = 0;
-
- // Resolve occurrences of "/./" in the normalized path
- while (true) {
- index = uriBC.indexOf("/./", 0, 3, index);
- if (index < 0)
- break;
- copyBytes(b, start + index, start + index + 2,
- end - start - index - 2);
- end = end - 2;
- uriBC.setEnd(end);
- }
-
- index = 0;
-
- // Resolve occurrences of "/../" in the normalized path
- while (true) {
- index = uriBC.indexOf("/../", 0, 4, index);
- if (index < 0)
- break;
- // Prevent from going outside our context
- if (index == 0)
- return false;
- int index2 = -1;
- for (pos = start + index - 1; (pos >= 0) && (index2 < 0); pos --) {
- if (b[pos] == (byte) '/') {
- index2 = pos;
- }
- }
- copyBytes(b, start + index2, start + index + 3,
- end - start - index - 3);
- end = end + index2 - index - 3;
- uriBC.setEnd(end);
- index = index2;
- }
-
- //uriBC.setBytes(b, start, end);
- uriBC.setEnd(end);
- return true;
-
- }
-
- /**
- * Copy an array of bytes to a different position. Used during
- * normalization.
- */
- private static void copyBytes(byte[] b, int dest, int src, int len) {
- for (int pos = 0; pos < len; pos++) {
- b[pos + dest] = b[pos + src];
- }
- }
-
-
- /**
- * This method will be called when the http headers have been received -
- * the body may or may not be available.
- *
- * In server mode this is equivalent with a servlet request.
- * This is also called for http client, when the response headers
- * are received.
- *
- * TODO: rename it to HttMessageReceived or something similar.
- */
- public static interface HttpService {
- void service(HttpRequest httpReq, HttpResponse httpRes) throws IOException;
- }
-
- /**
- * Called when both request and response bodies have been sent/
- * received. After this call the HttpChannel will be disconnected
- * from the http connection, which can be used for other requests.
- */
- public static interface RequestCompleted {
- void handle(HttpChannel data, Object extraData) throws IOException;
- }
-
- Runnable dispatcherRunnable = new Runnable() {
- @Override
- public void run() {
- getConnector().getDispatcher().runService(HttpChannel.this);
- }
- };
-
-
-}
\ No newline at end of file
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/lite/http/HttpClient.java b/modules/tomcat-lite/java/org/apache/tomcat/lite/http/HttpClient.java
deleted file mode 100644
index d537962..0000000
--- a/modules/tomcat-lite/java/org/apache/tomcat/lite/http/HttpClient.java
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- */
-package org.apache.tomcat.lite.http;
-
-import org.apache.tomcat.lite.io.SocketConnector;
-import org.apache.tomcat.lite.io.SslProvider;
-import org.apache.tomcat.lite.io.jsse.JsseSslProvider;
-
-/**
- * Entry point for http client code.
- *
- * ( initial version after removing 'integration', will add settings,
- * defaults, helpers )
- */
-public class HttpClient {
- static SslProvider sslConC = new JsseSslProvider();
-
- public synchronized static HttpConnector newClient() {
- return new HttpConnector(new SocketConnector()).withSsl(sslConC);
- }
-
-}
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/lite/http/HttpConnectionPool.java b/modules/tomcat-lite/java/org/apache/tomcat/lite/http/HttpConnectionPool.java
deleted file mode 100644
index ab56830..0000000
--- a/modules/tomcat-lite/java/org/apache/tomcat/lite/http/HttpConnectionPool.java
+++ /dev/null
@@ -1,399 +0,0 @@
-/*
- */
-package org.apache.tomcat.lite.http;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.Queue;
-import java.util.Set;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.logging.Logger;
-
-import org.apache.tomcat.lite.http.HttpConnector.HttpConnection;
-import org.apache.tomcat.lite.io.IOChannel;
-import org.apache.tomcat.lite.io.IOConnector;
-
-/**
- * - Holds references to all active and kept-alive connections.
- * - makes decisions on accepting more connections, closing old
- * connections, etc
- *
- */
-public class HttpConnectionPool {
- // TODO: add timeouts, limits per host/total, expire old entries
-
- public static interface HttpConnectionPoolEvents {
- public void newTarget(RemoteServer host);
-
- public void targetRemoved(RemoteServer host);
-
- public void newConnection(RemoteServer host, HttpConnection con);
- public void closedConnection(RemoteServer host, HttpConnection con);
- }
-
- /**
- * Connections for one remote host.
- * This should't be restricted by IP:port or even hostname,
- * for example if a server has multiple IPs or LB replicas - any would work.
- */
- public static class RemoteServer {
- // all access sync on RemoteServer
- private SpdyConnection spdy;
-
- // all access sync on RemoteServer
- private ArrayList<Http11Connection> connections
- = new ArrayList<Http11Connection>();
-
- Queue<HttpChannel> pending = new LinkedList<HttpChannel>();
-
-
- // TODO: setter, default from connector
- private int maxConnections = 20;
-
- AtomicInteger activeRequests = new AtomicInteger();
- AtomicInteger totalRequests = new AtomicInteger();
- private volatile long lastActivity;
-
- public String target;
-
- public synchronized List<HttpConnector.HttpConnection> getConnections()
- {
- return new ArrayList<HttpConnection>(connections);
- }
-
- public synchronized Collection<HttpChannel> getActives() {
- ArrayList<HttpChannel> actives = new ArrayList();
- for (Http11Connection con: connections) {
- if (con.activeHttp != null) {
- actives.add(con.activeHttp);
- }
- }
- if (spdy != null) {
- actives.addAll(spdy.getActives());
- }
-
- return actives;
- }
-
- public synchronized void touch() {
- lastActivity = System.currentTimeMillis();
- }
- }
-
- private HttpConnectionPoolEvents poolEvents;
-
- private static Logger log = Logger.getLogger("HttpConnector");
-
- // visible for debugging - will be made private, with accessor
- /**
- * Map from client names to socket pools.
- */
- public Map<CharSequence, HttpConnectionPool.RemoteServer> hosts = new HashMap<CharSequence,
- HttpConnectionPool.RemoteServer>();
-
- // Statistics
- public AtomicInteger waitingSockets = new AtomicInteger();
- public AtomicInteger closedSockets = new AtomicInteger();
-
- public AtomicInteger hits = new AtomicInteger();
- public AtomicInteger misses = new AtomicInteger();
- public AtomicInteger queued = new AtomicInteger();
-
- public AtomicInteger activeRequests = new AtomicInteger();
-
- private static boolean debug = false;
- HttpConnector httpConnector;
-
- public HttpConnectionPool(HttpConnector httpConnector) {
- this.httpConnector = httpConnector;
- }
-
- public int getTargetCount() {
- return hosts.size();
- }
-
- public int getSocketCount() {
- return waitingSockets.get();
- }
-
- public int getClosedSockets() {
- return closedSockets.get();
- }
-
- public Set<CharSequence> getKeepAliveTargets() {
- return hosts.keySet();
- }
-
- public List<RemoteServer> getServers() {
- return new ArrayList<RemoteServer>(hosts.values());
- }
-
- public void setEvents(HttpConnectionPoolEvents events) {
- this.poolEvents = events;
- }
- /**
- * Stop all cached connections.
- */
- public void clear() throws IOException {
- synchronized (hosts) {
- int active = 0;
- for (RemoteServer rs: hosts.values()) {
- synchronized (rs) {
- int hostActive = 0;
- if (rs.spdy != null) {
- if (rs.spdy.channels.size() == 0) {
- rs.spdy.close();
- rs.spdy = null;
- } else {
- hostActive += rs.spdy.channels.size();
- }
- }
- for (Http11Connection con: rs.connections) {
- if (con.activeHttp == null) {
- con.close();
- } else {
- hostActive++;
- }
- }
- if (hostActive != rs.activeRequests.get()) {
- log.warning("Active missmatch " + rs.target + " " +
- hostActive + " "
- + rs.activeRequests.get());
- rs.activeRequests.set(hostActive);
- }
- active += hostActive;
- }
- }
- if (active != this.activeRequests.get()) {
- log.warning("Active missmatch " + active + " "
- + activeRequests.get());
- activeRequests.set(active);
- }
- }
- }
-
- /**
- * Stop all active and cached connections
- * @throws IOException
- */
- public void abort() throws IOException {
- // TODO
- clear();
- hosts.clear();
- }
-
- /**
- * @param key host:port, or some other key if multiple hosts:ips
- * are connected to equivalent servers ( LB )
- * @param httpCh
- * @throws IOException
- */
- public void send(HttpChannel httpCh)
- throws IOException {
- String target = httpCh.getTarget();
- HttpConnection con = null;
- // TODO: check ssl on connection - now if a second request
- // is received on a ssl connection - we just send it
- boolean ssl = httpCh.getRequest().isSecure();
-
- HttpConnectionPool.RemoteServer remoteServer = null;
- synchronized (hosts) {
- remoteServer = hosts.get(target);
- if (remoteServer == null) {
- remoteServer = new HttpConnectionPool.RemoteServer();
- remoteServer.target = target;
- hosts.put(target, remoteServer);
- }
- }
-
- // TODO: remove old servers and connections
-
- // Temp magic - until a better negotiation is defined
- boolean forceSpdy = "SPDY/1.0".equals(httpCh.getRequest().getProtocol());
- if (forceSpdy) {
- // switch back the protocol
- httpCh.getRequest().setProtocol("HTTP/1.1");
- }
-
- activeRequests.incrementAndGet();
- remoteServer.activeRequests.incrementAndGet();
-
- // if we already have a spdy connection or explicitely
- // requested.
- if (forceSpdy || remoteServer.spdy != null) {
- synchronized (remoteServer) {
- if (remoteServer.spdy == null) {
- remoteServer.spdy = new SpdyConnection(httpConnector,
- remoteServer);
- }
- con = remoteServer.spdy;
- }
-
- // Will be queued - multiple threads may try to send
- // at the same time, and we need to queue anyways.
- con.sendRequest(httpCh);
- } else {
- synchronized (remoteServer) {
- Http11Connection hcon;
- for (int i = 0; i < remoteServer.connections.size(); i++) {
- hcon = (Http11Connection) remoteServer.connections.get(i);
- if (hcon != null && hcon.activeHttp == null) {
- hcon.beforeRequest(); // recycle
-
- hcon.activeHttp = httpCh;
- con = hcon;
- break;
- }
- }
- if (con == null) {
-// if (remoteServer.connections.size() > remoteServer.maxConnections) {
-// remoteServer.pending.add(httpCh);
-// queued.incrementAndGet();
-// if (debug) {
-// log.info("Queue: " + target + " " + remoteServer.connections.size());
-// }
-// return;
-// }
- hcon = new Http11Connection(httpConnector);
- hcon.setTarget(target);
- hcon.activeHttp = httpCh;
- hcon.remoteHost = remoteServer;
- remoteServer.connections.add(hcon);
- con = hcon;
- }
- }
-
-
- // we got a connection - make sure we're connected
- http11ConnectOrSend(httpCh, target, con, ssl);
- }
- }
-
- private void http11ConnectOrSend(HttpChannel httpCh, String target,
- HttpConnection con, boolean ssl) throws IOException {
- httpCh.setConnection(con);
-
- if (con.isOpen()) {
- hits.incrementAndGet();
-// if (debug) {
-// log.info("HTTP_CONNECT: Reuse connection " + target + " " + this);
-// }
- con.sendRequest(httpCh);
- } else {
- misses.incrementAndGet();
- if (debug) {
- log.info("HTTP_CONNECT: Start connection " + target + " " + this);
- }
- httpConnect(httpCh, target, ssl,
- (Http11Connection) con);
- }
- }
-
- void httpConnect(HttpChannel httpCh, String target,
- boolean ssl, IOConnector.ConnectedCallback cb)
- throws IOException {
- if (debug) {
- log.info("HTTP_CONNECT: New connection " + target);
- }
- String[] hostPort = target.split(":");
-
- int targetPort = ssl ? 443 : 80;
- if (hostPort.length > 1) {
- targetPort = Integer.parseInt(hostPort[1]);
- }
-
- httpConnector.getIOConnector().connect(hostPort[0], targetPort,
- cb);
- }
-
- public void afterRequest(HttpChannel http, HttpConnection con,
- boolean keepAlive)
- throws IOException {
- activeRequests.decrementAndGet();
- if (con.remoteHost != null) {
- con.remoteHost.touch();
- con.remoteHost.activeRequests.decrementAndGet();
- }
- if (con.serverMode) {
- afterServerRequest(con, keepAlive);
- } else {
- afterClientRequest(con);
- }
- }
-
- private void afterClientRequest(HttpConnection con)
- throws IOException {
- RemoteServer remoteServer = con.remoteHost;
- HttpChannel req = null;
-
- // If we have pending requests ( because too many active limit ), pick
- // one and send it.
- synchronized (remoteServer) {
- // If closed - we can remove the object - or
- // let a background thread do it, in case it's needed
- // again.
- if (remoteServer.pending.size() == 0) {
- con.activeHttp = null;
- return;
- }
- req = remoteServer.pending.remove();
- con.activeHttp = req;
- if (debug) {
- log.info("After request: send pending " + remoteServer.pending.size());
- }
- }
-
- http11ConnectOrSend(req, con.getTarget().toString(),
- con, req.getRequest().isSecure());
- }
-
- RemoteServer serverPool = new RemoteServer();
-
- public void afterServerRequest(HttpConnection con, boolean keepAlive)
- throws IOException {
- con.activeHttp = null;
- if (!keepAlive) {
- synchronized (serverPool) {
- // I could also reuse the object.
- serverPool.connections.remove(con);
- }
- }
- }
-
- public HttpConnection accepted(IOChannel accepted) {
- Http11Connection con = new Http11Connection(httpConnector);
- con.remoteHost = serverPool;
- synchronized (serverPool) {
- serverPool.connections.add(con);
- }
- return con;
- }
-
-
- // Called by handleClosed
- void stopKeepAlive(IOChannel schannel) {
- CharSequence target = schannel.getTarget();
- HttpConnectionPool.RemoteServer remoteServer = null;
- synchronized (hosts) {
- remoteServer = hosts.get(target);
- if (remoteServer == null) {
- return;
- }
- }
- synchronized (remoteServer) {
- if (remoteServer.connections.remove(schannel)) {
- waitingSockets.decrementAndGet();
- if (remoteServer.connections.size() == 0) {
- hosts.remove(target);
- }
- }
- }
- }
-
-
-}
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/lite/http/HttpConnector.java b/modules/tomcat-lite/java/org/apache/tomcat/lite/http/HttpConnector.java
deleted file mode 100644
index 0b4708d..0000000
--- a/modules/tomcat-lite/java/org/apache/tomcat/lite/http/HttpConnector.java
+++ /dev/null
@@ -1,514 +0,0 @@
-/*
- */
-package org.apache.tomcat.lite.http;
-
-import java.io.IOException;
-import java.net.URL;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Timer;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.logging.Logger;
-
-import org.apache.tomcat.lite.http.HttpChannel.HttpService;
-import org.apache.tomcat.lite.io.BBuffer;
-import org.apache.tomcat.lite.io.DumpChannel;
-import org.apache.tomcat.lite.io.IOBuffer;
-import org.apache.tomcat.lite.io.IOChannel;
-import org.apache.tomcat.lite.io.IOConnector;
-import org.apache.tomcat.lite.io.SslProvider;
-import org.apache.tomcat.lite.io.IOConnector.DataReceivedCallback;
-
-/**
- * Manages HttpChannels and associated socket pool.
- *
- *
- * @author Costin Manolache
- */
-public class HttpConnector {
-
- public static interface HttpChannelEvents {
- /** HttpChannel object created. It'll be used many times.
- * @throws IOException
- */
- public void onCreate(HttpChannel ch, HttpConnector con) throws IOException;
-
- /**
- * HttpChannel object no longer needed, out of pool.
- * @throws IOException
- */
- public void onDestroy(HttpChannel ch, HttpConnector con) throws IOException;
- }
-
- private static Logger log = Logger.getLogger("HttpConnector");
-
- /**
- * Cache HttpChannel/request/buffers
- */
- private int maxHttpPoolSize = 50;
-
- /**
- * Max number of connections to keep alive.
- * Each connection holds a header buffer and the socket.
- * ( we could skip the header buffer )
- */
- private int maxSocketPoolSize = 500; // 10000;
-
- private int keepAliveTimeMs = 300000;
-
- private List<HttpChannel> httpChannelPool = new ArrayList<HttpChannel>();
-
- protected IOConnector ioConnector;
-
- // for https connections
- protected SslProvider sslProvider;
-
- boolean debugHttp = false;
- boolean debug = false;
-
- boolean clientKeepAlive = true;
- boolean serverKeepAlive = true;
-
- HttpChannelEvents httpEvents;
-
- public AtomicInteger inUse = new AtomicInteger();
- public AtomicInteger newHttpChannel = new AtomicInteger();
- public AtomicInteger totalHttpChannel = new AtomicInteger();
- public AtomicInteger totalClientHttpChannel = new AtomicInteger();
- public AtomicInteger recycledChannels = new AtomicInteger();
- public AtomicInteger reusedChannels = new AtomicInteger();
-
- public HttpConnectionPool cpool = new HttpConnectionPool(this);
-
- // Host + context mapper.
- Dispatcher dispatcher;
- protected HttpService defaultService;
- int port = 8080;
-
- private Timer timer;
-
- boolean compression = true;
-
- boolean serverSSL = false;
-
- private static Timer defaultTimer = new Timer(true);
-
- public HttpConnector(IOConnector ioConnector) {
- this.ioConnector = ioConnector;
- dispatcher = new Dispatcher();
- defaultService = dispatcher;
- if (ioConnector != null) {
- timer = ioConnector.getTimer();
- } else {
- // tests
- timer = defaultTimer;
- }
- }
-
- protected HttpConnector() {
- this(null);
- }
-
- public Dispatcher getDispatcher() {
- return dispatcher;
- }
-
- public HttpConnectionPool getConnectionPool() {
- return cpool;
- }
-
- public HttpConnector withIOConnector(IOConnector selectors) {
- ioConnector = selectors;
- return this;
- }
-
- public void setDebug(boolean b) {
- this.debug = b;
- }
-
- public void setDebugHttp(boolean b) {
- this.debugHttp = b;
- }
-
- public HttpConnector withSsl(SslProvider ssl) {
- sslProvider = ssl;
- return this;
- }
-
- HttpConnector setServerSsl(boolean b) {
- serverSSL = b;
- return this;
- }
-
- public SslProvider getSslProvider() {
- return sslProvider;
- }
-
- /**
- * Allow or disable compression for this connector.
- * Compression is enabled by default.
- */
- public HttpConnector setCompression(boolean b) {
- this.compression = b;
- return this;
- }
-
- public void setClientKeepAlive(boolean b) {
- this.clientKeepAlive = b;
- }
-
- public void setServerKeepAlive(boolean b) {
- this.serverKeepAlive = b;
- }
-
- public boolean isDebug() {
- return debug;
- }
-
- public boolean isClientKeepAlive() {
- return clientKeepAlive;
- }
-
- public boolean isServerKeepAlive() {
- return serverKeepAlive;
- }
-
- public int getInUse() {
- return inUse.get();
- }
-
- public int getMaxHttpPoolSize() {
- return maxHttpPoolSize;
- }
-
- public void setMaxHttpPoolSize(int maxHttpPoolSize) {
- this.maxHttpPoolSize = maxHttpPoolSize;
- }
-
- public void setOnCreate(HttpChannelEvents callback) {
- httpEvents = callback;
- }
-
- /**
- * Override to create customized client/server connections.
- *
- * @return
- * @throws IOException
- */
- protected HttpChannel create() throws IOException {
- HttpChannel res = new HttpChannel();
- newHttpChannel.incrementAndGet();
- res.setConnector(this);
- if (httpEvents != null) {
- httpEvents.onCreate(res, this);
- }
- if (debugHttp) {
- res.debug = debugHttp;
- }
- return res;
- }
-
- public HttpChannel get(String host, int port) throws IOException {
- HttpChannel http = get(false);
- http.setTarget(host + ":" + port);
- return http;
- }
-
- public HttpChannel getServer() {
- try {
- return get(true);
- } catch (IOException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- return null;
- }
- }
-
- public HttpRequest request(String host, int port) throws IOException {
- HttpChannel http = get(false);
- http.setTarget(host + ":" + port);
- return http.getRequest();
-
- }
-
- public HttpRequest request(CharSequence urlString) throws IOException {
- return get(urlString).getRequest();
- }
-
- /**
- * Get an existing AsyncHttp object. Since it uses many buffers and
- * objects - it's more efficient to pool it.
- *
- * release will return the object to the pool.
- * @throws IOException
- */
- public HttpChannel get(CharSequence urlString) throws IOException {
- URL url = new URL(urlString.toString());
- String host = url.getHost();
- int port = url.getPort();
- boolean secure = "http".equals(url.getAuthority());
- if (port == -1) {
- port = secure ? 443: 80;
- }
- // TODO: insert SSL filter
- HttpChannel http = get(false);
- http.setTarget(host + ":" + port);
- String path = url.getFile(); // path + qry
- // TODO: query string
- http.getRequest().requestURI().set(path);
- return http;
- }
-
- protected HttpChannel get(boolean server) throws IOException {
- HttpChannel processor = null;
- synchronized (httpChannelPool) {
- int cnt = httpChannelPool.size();
- if (cnt > 0) {
- processor = httpChannelPool.remove(cnt - 1);
- }
- }
- boolean reuse = false;
- totalHttpChannel.incrementAndGet();
- if (!server) {
- totalClientHttpChannel.incrementAndGet();
- }
- if (processor == null) {
- processor = create();
- } else {
- reuse = true;
- reusedChannels.incrementAndGet();
- processor.release = false;
- }
- processor.serverMode(server);
- if (debug) {
- log.info((reuse ? "REUSE ": "Create ") +
- (server? " S" : "")
- + " id=" + processor.ser +
- " " + processor +
- " size=" + httpChannelPool.size());
- }
-
- processor.setConnector(this);
- inUse.incrementAndGet();
- return processor;
- }
-
- protected void returnToPool(HttpChannel http) throws IOException {
- inUse.decrementAndGet();
- recycledChannels.incrementAndGet();
- int size = 0;
- boolean pool = false;
-
- http.recycle();
- http.setConnection(null);
- http.setConnector(null);
-
- // No more data - release the object
- synchronized (httpChannelPool) {
- size = httpChannelPool.size();
- if (httpChannelPool.contains(http)) {
- log.severe("Duplicate element in pool !");
- } else if (size < maxHttpPoolSize) {
- httpChannelPool.add(http);
- pool = true;
- }
- }
-
- if (!pool && httpEvents != null) {
- httpEvents.onDestroy(http, this);
- }
- if (debug) {
- log.info((pool ? "Return " : "Destroy ")
- + http.getTarget() + " obj=" +
- http + " size=" + size);
- }
- }
-
-
- public IOConnector getIOConnector() {
- return ioConnector;
- }
-
-
- public void setHttpService(HttpService s) {
- defaultService = s;
- }
-
- public void start() throws IOException {
- if (ioConnector != null) {
- ioConnector.acceptor(new AcceptorCallback(),
- Integer.toString(port), null);
- }
- }
-
- /**
- *
- * TODO: only clean our state and sockets we listen on.
- *
- */
- public void stop() {
- if (ioConnector != null) {
- ioConnector.stop();
- }
- }
-
- protected void connectAndSend(HttpChannel httpCh) throws IOException {
- cpool.send(httpCh);
-
- }
-
- private class AcceptorCallback implements IOConnector.ConnectedCallback {
- @Override
- public void handleConnected(IOChannel accepted) throws IOException {
- handleAccepted(accepted);
- }
- }
-
- public HttpConnection handleAccepted(IOChannel accepted) throws IOException {
- // TODO: reuse
- HttpConnection shttp = cpool.accepted(accepted);
- shttp.serverMode = true;
-
- IOChannel head = accepted;
- IOChannel ch;
-
- String id = null;
- if (debugHttp) {
- id = port + "-" + accepted.getFirst().getAttribute(IOChannel.ATT_REMOTE_PORT);
- log.info("Accepted " + id);
- head = DumpChannel.wrap("SSL-" + id, head);
- }
-
- // TODO: seems cleaner this way...
- if (serverSSL) {
- ch = sslProvider.serverChannel(head);
- head.setHead(ch);
- head = ch;
-
- if (debugHttp) {
- head = DumpChannel.wrap("CLEAR-" + id, head);
- }
- }
-
- shttp.setSink(head);
-
- // Will read any data in the channel, notify data available up
- accepted.handleReceived(accepted);
- return shttp;
- }
-
- public HttpConnector setPort(int port2) {
- this.port = port2;
- return this;
- }
-
- /**
- * Actual HTTP/1.1 wire protocol.
- *
- */
- public static abstract class HttpConnection extends IOChannel
- implements DataReceivedCallback
- {
- protected HttpConnector httpConnector;
- protected boolean serverMode = false;
-
- protected BBuffer headRecvBuf = BBuffer.allocate(8192);
- protected CompressFilter compress = new CompressFilter();
-
- protected boolean secure = false;
-
- protected HttpConnectionPool.RemoteServer remoteHost;
- // If set, the connection is in use ( active )
- // null == keep alive. Changes synchronized on remoteHost
- // before/after request
- protected HttpChannel activeHttp;
-
- @Override
- public final void handleReceived(IOChannel ch) throws IOException {
- int before = ch.getIn().available();
- dataReceived(ch.getIn());
- }
-
- protected HttpChannel checkHttpChannel() throws IOException {
- return null;
- }
-
- /**
- * Called before a new request is sent, on a channel that is
- * reused.
- */
- public void beforeRequest() {
- }
-
- public void setSink(IOChannel ch) throws IOException {
- this.net = ch;
- ch.setDataReceivedCallback(this);
- ch.setDataFlushedCallback(this);
- // we may have data in the buffer;
- handleReceived(ch);
- }
-
-
- /**
- * Incoming data.
- */
- public abstract void dataReceived(IOBuffer iob) throws IOException;
-
- /**
- * Framing error, client interrupt, etc.
- */
- public void abort(HttpChannel http, String t) throws IOException {
- }
-
- protected void sendRequest(HttpChannel http)
- throws IOException {
- }
-
- protected void sendResponseHeaders(HttpChannel http)
- throws IOException {
- }
-
- public void startSending(HttpChannel http) throws IOException {
- }
-
- @Override
- public IOBuffer getIn() {
- return net == null ? null : net.getIn();
- }
-
- @Override
- public IOBuffer getOut() {
- return net == null ? null : net.getOut();
- }
-
- @Override
- public void startSending() throws IOException {
- }
-
- /** Called when the outgoing stream is closed:
- * - by an explicit call to close()
- * - when all content has been sent.
- */
- protected void outClosed(HttpChannel http) throws IOException {
- }
-
- /**
- * Called by HttpChannel when both input and output are fully
- * sent/received. When this happens the request is no longer associated
- * with the Connection, and the connection can be re-used.
- *
- * The channel can still be used to access the retrieved data that may
- * still be buffered until HttpChannel.release() is called.
- *
- * This method will be called only once, for both succesful and aborted
- * requests.
- */
- protected abstract void endSendReceive(HttpChannel httpChannel) throws IOException;
-
- public void withExtraBuffer(BBuffer received) {
- return;
- }
-
- }
-
-}
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/lite/http/HttpMessage.java b/modules/tomcat-lite/java/org/apache/tomcat/lite/http/HttpMessage.java
deleted file mode 100644
index 5d30e63..0000000
--- a/modules/tomcat-lite/java/org/apache/tomcat/lite/http/HttpMessage.java
+++ /dev/null
@@ -1,508 +0,0 @@
-/*
- */
-package org.apache.tomcat.lite.http;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.PrintWriter;
-import java.io.UnsupportedEncodingException;
-import java.io.Writer;
-import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-import java.util.Locale;
-import java.util.TimeZone;
-
-import org.apache.tomcat.lite.http.HttpChannel.RequestCompleted;
-import org.apache.tomcat.lite.http.HttpConnector.HttpConnection;
-import org.apache.tomcat.lite.io.BBuffer;
-import org.apache.tomcat.lite.io.BufferedIOReader;
-import org.apache.tomcat.lite.io.CBuffer;
-import org.apache.tomcat.lite.io.FastHttpDateFormat;
-import org.apache.tomcat.lite.io.IOBuffer;
-import org.apache.tomcat.lite.io.IOInputStream;
-import org.apache.tomcat.lite.io.IOOutputStream;
-import org.apache.tomcat.lite.io.IOReader;
-import org.apache.tomcat.lite.io.IOWriter;
-import org.apache.tomcat.lite.io.UrlEncoding;
-
-
-/**
- * Basic Http request or response message.
- *
- * Because the HttpChannel can be used for both client and
- * server, and to make proxy and other code simpler - the request
- * and response are represented by the same class.
- *
- * @author Costin Manolache
- */
-public abstract class HttpMessage {
-
- public static enum State {
- HEAD,
- BODY_DATA,
- DONE
- }
-
- /**
- * Raw, off-the-wire message.
- */
- public static class HttpMessageBytes {
- BBuffer head1 = BBuffer.wrapper();
- BBuffer head2 = BBuffer.wrapper();
- BBuffer proto = BBuffer.wrapper();
-
- BBuffer query = BBuffer.wrapper();
-
- List<BBuffer> headerNames = new ArrayList<BBuffer>();
- List<BBuffer> headerValues = new ArrayList<BBuffer>();
-
- int headerCount;
-
- public BBuffer status() {
- return head1;
- }
-
- public BBuffer method() {
- return head1;
- }
-
- public BBuffer url() {
- return head2;
- }
-
- public BBuffer query() {
- return query;
- }
-
- public BBuffer protocol() {
- return proto;
- }
-
- public BBuffer message() {
- return head2;
- }
-
- public int addHeader() {
- if (headerCount >= headerNames.size()) {
- // make space for the new header.
- headerNames.add(BBuffer.wrapper());
- headerValues.add(BBuffer.wrapper());
- }
- return headerCount++;
- }
-
- public BBuffer getHeaderName(int i) {
- if (i >= headerNames.size()) {
- return null;
- }
- return headerNames.get(i);
- }
-
- public BBuffer getHeaderValue(int i) {
- if (i >= headerValues.size()) {
- return null;
- }
- return headerValues.get(i);
- }
-
- public void recycle() {
- head1.recycle();
- head2.recycle();
- proto.recycle();
- query.recycle();
- headerCount = 0;
- for (int i = 0; i < headerCount; i++) {
- headerNames.get(i).recycle();
- headerValues.get(i).recycle();
- }
- }
- }
-
- protected static final TimeZone GMT_ZONE = TimeZone.getTimeZone("GMT");
-
- private HttpMessageBytes msgBytes = new HttpMessageBytes();
-
- protected HttpMessage.State state = HttpMessage.State.HEAD;
-
- protected HttpChannel httpCh;
-
- protected MultiMap headers = new MultiMap().insensitive();
-
- protected CBuffer protoMB;
-
- // Cookies
- protected boolean cookiesParsed = false;
-
- // TODO: cookies parsed when headers are added !
- protected ArrayList<ServerCookie> cookies;
- protected ArrayList<ServerCookie> cookiesCache;
-
- protected UrlEncoding urlDecoder = new UrlEncoding();
- protected String charEncoding;
-
- IOReader reader;
- BufferedIOReader bufferedReader;
- HttpWriter writer;
- IOWriter conv;
-
- IOOutputStream out;
- private IOInputStream in;
-
- boolean commited;
-
- protected IOBuffer body;
-
- long contentLength = -2;
- boolean chunked;
-
- /**
- * The set of SimpleDateFormat formats to use in getDateHeader().
- *
- * Notice that because SimpleDateFormat is not thread-safe, we can't
- * declare formats[] as a static variable.
- */
- protected SimpleDateFormat formats[] = null;
-
-
- BBuffer clBuffer = BBuffer.allocate(64);
-
- public HttpMessage(HttpChannel httpCh) {
- this.httpCh = httpCh;
-
- out = new IOOutputStream(httpCh.getOut(), httpCh);
- conv = new IOWriter(httpCh);
- writer = new HttpWriter(this, out, conv);
-
- in = new IOInputStream(httpCh, httpCh.getIOTimeout());
-
- reader = new IOReader(httpCh.getIn());
- bufferedReader = new BufferedIOReader(reader);
-
- cookies = new ArrayList<ServerCookie>();
- cookiesCache = new ArrayList<ServerCookie>();
- protoMB = CBuffer.newInstance();
- }
-
- public void addHeader(String name, String value) {
- getMimeHeaders().addValue(name).set(value);
- }
-
- public void setHeader(String name, String value) {
- getMimeHeaders().setValue(name).set(value);
- }
-
- public void setMimeHeaders(MultiMap resHeaders) {
- this.headers = resHeaders;
- }
-
- public String getHeader(String name) {
- CBuffer cb = headers.getHeader(name);
- return (cb == null) ? null : cb.toString();
- }
-
- public MultiMap getMimeHeaders() {
- return headers;
- }
-
- /**
- * Return the value of the specified date header, if any; otherwise
- * return -1.
- *
- * @param name Name of the requested date header
- *
- * @exception IllegalArgumentException if the specified header value
- * cannot be converted to a date
- */
- public long getDateHeader(String name) {
-
- String value = getHeader(name);
- if (value == null)
- return (-1L);
- if (formats == null) {
- formats = new SimpleDateFormat[] {
- new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US),
- new SimpleDateFormat("EEEEEE, dd-MMM-yy HH:mm:ss zzz", Locale.US),
- new SimpleDateFormat("EEE MMMM d HH:mm:ss yyyy", Locale.US)
- };
- formats[0].setTimeZone(GMT_ZONE);
- formats[1].setTimeZone(GMT_ZONE);
- formats[2].setTimeZone(GMT_ZONE);
- }
-
- // Attempt to convert the date header in a variety of formats
- long result = FastHttpDateFormat.parseDate(value, formats);
- if (result != (-1L)) {
- return result;
- }
- throw new IllegalArgumentException(value);
-
- }
-
-
- public Collection<String> getHeaderNames() {
-
- MultiMap headers = getMimeHeaders();
- int n = headers.size();
- ArrayList<String> result = new ArrayList<String>();
- for (int i = 0; i < n; i++) {
- result.add(headers.getName(i).toString());
- }
- return result;
- }
-
- public boolean containsHeader(String name) {
- return headers.getHeader(name) != null;
- }
-
- public void setContentLength(long len) {
- contentLength = len;
- clBuffer.setLong(len);
- setCLHeader();
- }
-
- public void setContentLength(int len) {
- contentLength = len;
- clBuffer.setLong(len);
- setCLHeader();
- }
-
- private void setCLHeader() {
- MultiMap.Entry clB = headers.setEntry("content-length");
- clB.valueB = clBuffer;
- }
-
- public long getContentLengthLong() {
- if (contentLength == -2) {
- CBuffer clB = headers.getHeader("content-length");
- contentLength = (clB == null) ?
- -1 : clB.getLong();
- }
- return contentLength;
- }
-
- public int getContentLength() {
- long length = getContentLengthLong();
-
- if (length < Integer.MAX_VALUE) {
- return (int) length;
- }
- return -1;
- }
-
- public String getContentType() {
- CBuffer contentTypeMB = headers.getHeader("content-type");
- if (contentTypeMB == null) {
- return null;
- }
- return contentTypeMB.toString();
- }
-
- public void setContentType(String contentType) {
- CBuffer clB = getMimeHeaders().getHeader("content-type");
- if (clB == null) {
- setHeader("Content-Type", contentType);
- } else {
- clB.set(contentType);
- }
- }
-
- /**
- * Get the character encoding used for this request.
- * Need a field because it can be overriden. Used to construct the
- * Reader.
- */
- public String getCharacterEncoding() {
- if (charEncoding != null)
- return charEncoding;
-
- charEncoding = ContentType.getCharsetFromContentType(getContentType());
- return charEncoding;
- }
-
- private static final String DEFAULT_ENCODING = "ISO-8859-1";
-
- public String getEncoding() {
- String charEncoding = getCharacterEncoding();
- if (charEncoding == null) {
- return DEFAULT_ENCODING;
- } else {
- return charEncoding;
- }
- }
-
- public void setCharacterEncoding(String enc)
- throws UnsupportedEncodingException {
- this.charEncoding = enc;
- }
-
-
- public void recycle() {
- commited = false;
- headers.recycle();
- protoMB.set("HTTP/1.1");
- for (int i = 0; i < cookies.size(); i++) {
- cookies.get(i).recycle();
- }
- cookies.clear();
- charEncoding = null;
- bufferedReader.recycle();
-
- writer.recycle();
- conv.recycle();
-
- contentLength = -2;
- chunked = false;
- clBuffer.recycle();
- state = State.HEAD;
- cookiesParsed = false;
- getMsgBytes().recycle();
-
- }
-
-
- public String getProtocol() {
- return protoMB.toString();
- }
-
- public void setProtocol(String proto) {
- protoMB.set(proto);
- }
-
- public CBuffer protocol() {
- return protoMB;
- }
-
- public ServerCookie getCookie(String name) {
- for (ServerCookie sc: getServerCookies()) {
- if (sc.getName().equalsIgnoreCase(name)) {
- return sc;
- }
- }
- return null;
- }
-
- public List<ServerCookie> getServerCookies() {
- if (!cookiesParsed) {
- cookiesParsed = true;
- ServerCookie.processCookies(cookies, cookiesCache, getMsgBytes());
- }
- return cookies;
- }
-
- public UrlEncoding getURLDecoder() {
- return urlDecoder;
- }
-
- public boolean isCommitted() {
- return commited;
- }
-
- public void setCommitted(boolean b) {
- commited = b;
- }
-
- public HttpChannel getHttpChannel() {
- return httpCh;
- }
-
- public IOBuffer getBody() {
- return body;
- }
-
- void setBody(IOBuffer body) {
- this.body = body;
- }
-
- public void flush() throws IOException {
- httpCh.startSending();
- }
-
- // not servlet input stream
- public IOInputStream getBodyInputStream() {
- return in;
- }
-
- public InputStream getInputStream() {
- return in;
- }
-
- public IOOutputStream getOutputStream() {
- return out;
- }
-
- public IOOutputStream getBodyOutputStream() {
- return out;
- }
-
- public IOReader getBodyReader() throws IOException {
- reader.setEncoding(getCharacterEncoding());
- return reader;
- }
-
- public BBuffer readAll(BBuffer chunk, long to) throws IOException {
- return httpCh.readAll(chunk, to);
- }
-
- public BBuffer readAll() throws IOException {
- return httpCh.readAll(null, httpCh.ioTimeout);
- }
-
- /**
- * We're done with this object, it can be recycled.
- * Any use after this should throw exception or affect an
- * unrelated request.
- */
- public void release() throws IOException {
- httpCh.release();
- }
-
- public void setCompletedCallback(RequestCompleted doneAllCallback) throws IOException {
- httpCh.setCompletedCallback(doneAllCallback);
- }
-
- public void setReadTimeout(long to) {
- reader.setTimeout(to);
- }
-
- /**
- * Returns a buffered reader.
- */
- public BufferedReader getReader() throws IOException {
- reader.setEncoding(getCharacterEncoding());
- return bufferedReader;
- }
-
- public PrintWriter getWriter() {
- return new PrintWriter(getBodyWriter());
- }
-
- public HttpWriter getBodyWriter() {
- conv.setEncoding(getCharacterEncoding());
- return writer;
- }
-
-
- protected void processMimeHeaders() {
- for (int idx = 0; idx < getMsgBytes().headerCount; idx++) {
- BBuffer nameBuf = getMsgBytes().getHeaderName(idx);
- BBuffer valBuf = getMsgBytes().getHeaderValue(idx);
-
- MultiMap.Entry header = headers.addEntry(nameBuf);
- header.valueB = valBuf;
- }
- }
-
-
- protected abstract void processReceivedHeaders() throws IOException;
-
- public abstract boolean hasBody();
-
- public HttpMessageBytes getMsgBytes() {
- // TODO: serialize if not set
- return msgBytes;
- }
-
-}
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/lite/http/HttpRequest.java b/modules/tomcat-lite/java/org/apache/tomcat/lite/http/HttpRequest.java
deleted file mode 100644
index be934dd..0000000
--- a/modules/tomcat-lite/java/org/apache/tomcat/lite/http/HttpRequest.java
+++ /dev/null
@@ -1,1019 +0,0 @@
-/*
- */
-package org.apache.tomcat.lite.http;
-
-import java.io.IOException;
-import java.util.Enumeration;
-import java.util.HashMap;
-import java.util.Map;
-
-import org.apache.tomcat.lite.http.HttpChannel.HttpService;
-import org.apache.tomcat.lite.http.MultiMap.Entry;
-import org.apache.tomcat.lite.io.BBuffer;
-import org.apache.tomcat.lite.io.CBuffer;
-import org.apache.tomcat.lite.io.Hex;
-import org.apache.tomcat.lite.io.IOBuffer;
-import org.apache.tomcat.lite.io.IOChannel;
-import org.apache.tomcat.lite.io.IOReader;
-import org.apache.tomcat.lite.io.IOWriter;
-import org.apache.tomcat.lite.io.UrlEncoding;
-
-public class HttpRequest extends HttpMessage {
- public static final String DEFAULT_CHARACTER_ENCODING="ISO-8859-1";
-
- protected CBuffer schemeMB;
- protected CBuffer methodMB;
- protected CBuffer remoteAddrMB;
- protected CBuffer remoteHostMB;
- protected int remotePort;
-
- protected CBuffer localNameMB;
- protected CBuffer localAddrMB;
- protected int localPort = -1;
-
- // Host: header, or default:80
- protected CBuffer serverNameMB;
- protected int serverPort = -1;
-
-
- // ==== Derived fields, computed after request is received ===
-
- protected CBuffer requestURI;
- protected CBuffer queryMB;
-
- protected BBuffer decodedUri = BBuffer.allocate();
- protected CBuffer decodedUriMB;
-
- // Decoded query
- protected MultiMap parameters;
-
- boolean parametersParsed = false;
-
- protected IOWriter charEncoder = new IOWriter(null);
- protected IOReader charDecoder = new IOReader(null);
- protected UrlEncoding urlEncoding = new UrlEncoding();
-
- // Reference to 'real' request object
- // will not be recycled
- public Object nativeRequest;
- public Object wrapperRequest;
-
- boolean ssl = false;
-
- boolean async = false;
-
- CBuffer requestURL = CBuffer.newInstance();
-
- private Map<String, Object> attributes = new HashMap<String, Object>();
-
- /**
- * Mapping data.
- */
- protected MappingData mappingData = new MappingData();
-
-
- HttpRequest(HttpChannel httpCh) {
- super(httpCh);
- decodedUriMB = CBuffer.newInstance();
- requestURI = CBuffer.newInstance();
- queryMB = CBuffer.newInstance();
- serverNameMB = CBuffer.newInstance();
-
- parameters = new MultiMap();
-
- schemeMB =
- CBuffer.newInstance();
- methodMB = CBuffer.newInstance();
- initRemote();
- }
-
- protected void initRemote() {
- remoteAddrMB = CBuffer.newInstance();
- localNameMB = CBuffer.newInstance();
- remoteHostMB = CBuffer.newInstance();
- localAddrMB = CBuffer.newInstance();
- }
-
- public void recycle() {
- super.recycle();
- schemeMB.recycle();
- methodMB.set("GET");
- requestURI.recycle();
- requestURL.recycle();
- queryMB.recycle();
- decodedUriMB.recycle();
-
- parameters.recycle();
- remoteAddrMB.recycle();
- remoteHostMB.recycle();
- parametersParsed = false;
- ssl = false;
- async = false;
- asyncTimeout = -1;
- charEncoder.recycle();
-
- localPort = -1;
- remotePort = -1;
- localAddrMB.recycle();
- localNameMB.recycle();
-
- serverPort = -1;
- serverNameMB.recycle();
- decodedUri.recycle();
- decodedQuery.recycle();
- }
-
- public Object getAttribute(String name) {
- return attributes.get(name);
- }
-
- public void setAttribute(String name, Object o) {
- if (o == null) {
- attributes.remove(name);
- } else {
- attributes.put(name, o);
- }
- }
- // getAttributeNames not supported
-
- public Map<String, Object> attributes() {
- return attributes;
- }
-
-
- public CBuffer method() {
- return methodMB;
- }
-
- public String getMethod() {
- return methodMB.toString();
- }
-
- public void setMethod(String method) {
- methodMB.set(method);
- }
-
- public CBuffer scheme() {
- return schemeMB;
- }
-
- public String getScheme() {
- String scheme = schemeMB.toString();
- if (scheme == null) {
- return "http";
- }
- return scheme;
- }
-
- public void setScheme(String s) {
- schemeMB.set(s);
- }
-
- public MappingData getMappingData() {
- return (mappingData);
- }
-
- /**
- * Return the portion of the request URI used to select the Context
- * of the Request.
- */
- public String getContextPath() {
- return (getMappingData().contextPath.toString());
- }
-
- public String getPathInfo() {
- CBuffer pathInfo = getMappingData().pathInfo;
- if (pathInfo.length() == 0) {
- return null;
- }
- return (getMappingData().pathInfo.toString());
- }
-
- /**
- * Return the portion of the request URI used to select the servlet
- * that will process this request.
- */
- public String getServletPath() {
- return (getMappingData().wrapperPath.toString());
- }
-
- /**
- * Parse query parameters - but not POST body.
- *
- * If you don't call this method, getParameters() will
- * also read the body for POST with x-www-url-encoded
- * mime type.
- */
- public void parseQueryParameters() {
- parseQuery();
- }
-
- /**
- * Explicitely parse the body, adding the parameters to
- * those from the query ( if already parsed ).
- *
- * By default servlet mode ( both query and body ) is used.
- */
- public void parsePostParameters() {
- parseBody();
- }
-
- MultiMap getParameters() {
- if (!parametersParsed) {
- parseQuery();
- parseBody();
- }
- return parameters;
- }
-
- public Enumeration<String> getParameterNames() {
- return getParameters().names();
- }
-
- /**
- * Expensive, creates a copy on each call.
- * @param name
- * @return
- */
- public String[] getParameterValues(String name) {
- Entry entry = getParameters().getEntry(name);
- if (entry == null) {
- return null;
- }
- String[] values = new String[entry.values.size()];
- for (int j = 0; j < values.length; j++) {
- values[j] = entry.values.get(j).toString();
- }
- return values;
- }
-
- // Inefficient - we convert from a different representation.
- public Map<String, String[]> getParameterMap() {
- // we could allow 'locking' - I don't think this is
- // a very useful optimization
- Map<String, String[]> map = new HashMap();
- for (int i = 0; i < getParameters().size(); i++) {
- Entry entry = getParameters().getEntry(i);
- if (entry == null) {
- continue;
- }
- if (entry.key == null) {
- continue;
- }
- String name = entry.key.toString();
- String[] values = new String[entry.values.size()];
- for (int j = 0; j < values.length; j++) {
- values[j] = entry.values.get(j).toString();
- }
- map.put(name, values);
- }
- return map;
- }
-
- public String getParameter(String name) {
- CharSequence value = getParameters().get(name);
- if (value == null) {
- return null;
- }
- return value.toString();
- }
-
- public void setParameter(String name, String value) {
- getParameters().set(name, value);
- }
-
- public void addParameter(String name, String values) {
- getParameters().add(name, values);
- }
-
- public CBuffer queryString() {
- return queryMB;
- }
-
- // TODO
- void serializeParameters(Appendable cc) throws IOException {
- int keys = parameters.size();
- boolean notFirst = false;
- for (int i = 0; i < parameters.size(); i++) {
- Entry entry = parameters.getEntry(i);
- for (int j = 0; j < entry.values.size(); j++) {
- // TODO: Uencode
- if (notFirst) {
- cc.append('&');
- } else {
- notFirst = true;
- }
- cc.append(entry.key);
- cc.append("=");
- cc.append(entry.values.get(j).getValue());
- }
- }
- }
-
- public void setURI(CharSequence encoded) {
- decodedUriMB.recycle();
- decodedUriMB.append(encoded);
- // TODO: generate % encoding ( reverse of decodeRequest )
- }
-
- public CBuffer decodedURI() {
- return decodedUriMB;
- }
-
- public CBuffer requestURI() {
- return requestURI;
- }
-
- public CBuffer requestURL() {
- CBuffer url = requestURL;
- url.recycle();
-
- String scheme = getScheme();
- int port = getServerPort();
- if (port < 0)
- port = 80; // Work around java.net.URL bug
-
- url.append(scheme);
- url.append("://");
- url.append(getServerName());
- if ((scheme.equals("http") && (port != 80))
- || (scheme.equals("https") && (port != 443))) {
- url.append(':');
- url.append(port);
- }
- // Decoded !!
- url.append(getRequestURI());
-
- return (url);
-
- }
-
- /**
- * Not decoded - %xx as in original.
- * @return
- */
- public String getRequestURI() {
- return requestURI.toString();
- }
-
- public void setRequestURI(String encodedUri) {
- requestURI.set(encodedUri);
- }
-
- CBuffer getOrAdd(String name) {
- CBuffer header = getMimeHeaders().getHeader(name);
- if (header == null) {
- header = getMimeHeaders().addValue(name);
- }
- return header;
- }
-
- /**
- * Set the Host header of the request.
- * @param target
- */
- public void setHost(String target) {
- serverNameMB.recycle();
- getOrAdd("Host").set(target);
- }
-
- // XXX
- public CBuffer serverName() {
- if (serverNameMB.length() == 0) {
- parseHost();
- }
- return serverNameMB;
- }
-
- public String getServerName() {
- return serverName().toString();
- }
-
- public void setServerName(String name) {
- serverName().set(name);
- }
-
- public int getServerPort() {
- serverName();
- return serverPort;
- }
-
- public void setServerPort(int serverPort ) {
- this.serverPort=serverPort;
- }
-
- public CBuffer remoteAddr() {
- if (remoteAddrMB.length() == 0) {
- HttpChannel asyncHttp = getHttpChannel();
- IOChannel iochannel = asyncHttp.getNet().getFirst();
- remoteAddrMB.set((String)
- iochannel.getAttribute(IOChannel.ATT_REMOTE_ADDRESS));
- }
- return remoteAddrMB;
- }
-
- public CBuffer remoteHost() {
- if (remoteHostMB.length() == 0) {
- HttpChannel asyncHttp = getHttpChannel();
- IOChannel iochannel = asyncHttp.getNet().getFirst();
- remoteHostMB.set((String)
- iochannel.getAttribute(IOChannel.ATT_REMOTE_HOSTNAME));
- }
- return remoteHostMB;
- }
-
- public CBuffer localName() {
- return localNameMB;
- }
-
- public CBuffer localAddr() {
- return localAddrMB;
- }
-
- public int getRemotePort(){
- if (remotePort == -1) {
- HttpChannel asyncHttp = getHttpChannel();
- IOChannel iochannel = asyncHttp.getNet().getFirst();
- remotePort = (Integer) iochannel.getAttribute(IOChannel.ATT_REMOTE_PORT);
- }
- return remotePort;
- }
-
- public void setRemotePort(int port){
- this.remotePort = port;
- }
-
- public int getLocalPort(){
- if (localPort == -1) {
- HttpChannel asyncHttp = getHttpChannel();
- IOChannel iochannel = asyncHttp.getNet().getFirst();
- localPort = (Integer) iochannel.getAttribute(IOChannel.ATT_LOCAL_PORT);
- }
- return localPort;
- }
-
- public void setLocalPort(int port){
- this.localPort = port;
- }
-
- public HttpResponse waitResponse() throws IOException {
- return waitResponse(httpCh.ioTimeout);
- }
-
- public void send(HttpService headersCallback, long timeout) throws IOException {
- if (headersCallback != null) {
- httpCh.setHttpService(headersCallback);
- }
-
- httpCh.send();
- }
-
- public void send(HttpService headersCallback) throws IOException {
- send(headersCallback, httpCh.ioTimeout);
- }
-
- public void send() throws IOException {
- send(null, httpCh.ioTimeout);
- }
-
- public HttpResponse waitResponse(long timeout) throws IOException {
- // TODO: close out if post
- httpCh.send();
-
- httpCh.headersReceivedLock.waitSignal(timeout);
-
- return httpCh.getResponse();
- }
-
- /**
- * Parse host.
- * @param serverNameMB2
- * @throws IOException
- */
- boolean parseHost() {
- MultiMap.Entry hostHF = getMimeHeaders().getEntry("Host");
- if (hostHF == null) {
- // HTTP/1.0
- // Default is what the socket tells us. Overriden if a host is
- // found/parsed
- return true;
- }
-
- BBuffer valueBC = hostHF.valueB;
- if (valueBC == null) {
- valueBC = BBuffer.allocate();
- hostHF.getValue().toAscii(valueBC);
- }
- byte[] valueB = valueBC.array();
- int valueL = valueBC.getLength();
- int valueS = valueBC.getStart();
-
- int colonPos = valueBC.indexOf(':', 0);
-
- serverNameMB.recycle();
-
- boolean ipv6 = (valueB[valueS] == '[');
- boolean bracketClosed = false;
- for (int i = 0; i < valueL; i++) {
- char b = (char) valueB[i + valueS];
- if (b == ':') {
- if (!ipv6 || bracketClosed) {
- colonPos = i;
- break;
- }
- }
- serverNameMB.append(b);
- if (b == ']') {
- bracketClosed = true;
- }
- }
-
- if (colonPos < 0) {
- if (!ssl) {
- setServerPort(80);
- } else {
- setServerPort(443);
- }
- } else {
- int port = 0;
- int mult = 1;
- for (int i = valueL - 1; i > colonPos; i--) {
- int charValue = Hex.DEC[(int) valueB[i + valueS]];
- if (charValue == -1) {
- // we don't return 400 - could do it
- return false;
- }
- port = port + (charValue * mult);
- mult = 10 * mult;
- }
- setServerPort(port);
-
- }
- return true;
- }
-
- // TODO: this is from coyote - MUST be rewritten !!!
- // - cleaner
- // - chunked encoding for body
- // - buffer should be in a pool, etc.
- /**
- * Post data buffer.
- */
- public final static int CACHED_POST_LEN = 8192;
-
- public byte[] postData = null;
-
- private long asyncTimeout = -1;
-
- /**
- * Parse request parameters.
- */
- protected void parseQuery() {
-
- parametersParsed = true;
-
- // getCharacterEncoding() may have been overridden to search for
- // hidden form field containing request encoding
- String enc = getEncoding();
-
-// boolean useBodyEncodingForURI = connector.getUseBodyEncodingForURI();
-// if (enc != null) {
-// parameters.setEncoding(enc);
-//// if (useBodyEncodingForURI) {
-//// parameters.setQueryStringEncoding(enc);
-//// }
-// } else {
-// parameters.setEncoding(DEFAULT_CHARACTER_ENCODING);
-//// if (useBodyEncodingForURI) {
-//// parameters.setQueryStringEncoding
-//// (DEFAULT_CHARACTER_ENCODING);
-//// }
-// }
-
- handleQueryParameters();
- }
-
- // Copy - will be modified by decoding
- BBuffer decodedQuery = BBuffer.allocate(1024);
-
- CBuffer tmpNameC = CBuffer.newInstance();
- BBuffer tmpName = BBuffer.wrapper();
- BBuffer tmpValue = BBuffer.wrapper();
-
- CBuffer tmpNameCB = CBuffer.newInstance();
- CBuffer tmpValueCB = CBuffer.newInstance();
-
- /**
- * Process the query string into parameters
- */
- public void handleQueryParameters() {
- if( queryMB.length() == 0) {
- return;
- }
-
- decodedQuery.recycle();
- decodedQuery.append(getMsgBytes().query());
- // TODO: option 'useBodyEncodingForUri' - versus UTF or ASCII
- String queryStringEncoding = getEncoding();
- processParameters( decodedQuery, queryStringEncoding );
- }
-
- public void processParameters( BBuffer bc, String encoding ) {
- if( bc.isNull())
- return;
- if (bc.remaining() ==0) {
- return;
- }
- processParameters( bc.array(), bc.getOffset(),
- bc.getLength(), encoding);
- }
-
- public void processParameters( byte bytes[], int start, int len,
- String enc ) {
- int end=start+len;
- int pos=start;
-
- do {
- boolean noEq=false;
- int valStart=-1;
- int valEnd=-1;
-
- int nameStart=pos;
- int nameEnd=BBuffer.indexOf(bytes, nameStart, end, '=' );
- // Workaround for a&b&c encoding
- int nameEnd2=BBuffer.indexOf(bytes, nameStart, end, '&' );
- if( (nameEnd2!=-1 ) &&
- ( nameEnd==-1 || nameEnd > nameEnd2) ) {
- nameEnd=nameEnd2;
- noEq=true;
- valStart=nameEnd;
- valEnd=nameEnd;
- }
- if( nameEnd== -1 )
- nameEnd=end;
-
- if( ! noEq ) {
- valStart= (nameEnd < end) ? nameEnd+1 : end;
- valEnd=BBuffer.indexOf(bytes, valStart, end, '&');
- if( valEnd== -1 ) valEnd = (valStart < end) ? end : valStart;
- }
-
- pos=valEnd+1;
-
- if( nameEnd<=nameStart ) {
- // No name eg ...&=xx&... will trigger this
- continue;
- }
-
- // TODO: use CBuffer, recycle
- tmpName.setBytes( bytes, nameStart, nameEnd-nameStart );
- tmpValue.setBytes( bytes, valStart, valEnd-valStart );
-
- try {
- parameters.add(urlDecode(tmpName, enc),
- urlDecode(tmpValue, enc));
- } catch (IOException e) {
- // ignored
- }
- } while( pos<end );
- }
-
-// public void processParameters(char bytes[], int start, int len,
-// String enc ) {
-// int end=start+len;
-// int pos=start;
-//
-// do {
-// boolean noEq=false;
-// int valStart=-1;
-// int valEnd=-1;
-//
-// int nameStart=pos;
-// int nameEnd=CBuffer.indexOf(bytes, nameStart, end, '=' );
-// // Workaround for a&b&c encoding
-// int nameEnd2=CBuffer.indexOf(bytes, nameStart, end, '&' );
-// if( (nameEnd2!=-1 ) &&
-// ( nameEnd==-1 || nameEnd > nameEnd2) ) {
-// nameEnd=nameEnd2;
-// noEq=true;
-// valStart=nameEnd;
-// valEnd=nameEnd;
-// }
-// if( nameEnd== -1 )
-// nameEnd=end;
-//
-// if( ! noEq ) {
-// valStart= (nameEnd < end) ? nameEnd+1 : end;
-// valEnd=CBuffer.indexOf(bytes, valStart, end, '&');
-// if( valEnd== -1 ) valEnd = (valStart < end) ? end : valStart;
-// }
-//
-// pos=valEnd+1;
-//
-// if( nameEnd<=nameStart ) {
-// // No name eg ...&=xx&... will trigger this
-// continue;
-// }
-//
-// // TODO: use CBuffer, recycle
-// tmpNameCB.recycle();
-// tmpValueCB.recycle();
-//
-// tmpNameCB.wrap( bytes, nameStart, nameEnd );
-// tmpValueCB.wrap( bytes, valStart, valEnd );
-//
-// //CharChunk name = new CharChunk();
-// //CharChunk value = new CharChunk();
-// // TODO:
-// try {
-// parameters.add(urlDecode(tmpName, enc),
-// urlDecode(tmpValue, enc));
-// } catch (IOException e) {
-// // ignored
-// }
-// } while( pos<end );
-// }
-
- private String urlDecode(BBuffer bc, String enc)
- throws IOException {
- // Replace %xx
- urlDecoder.urlDecode(bc, true);
-
- String result = null;
- if (enc != null) {
- result = bc.toString(enc);
- } else {
- // Ascii
-
- CBuffer cc = tmpNameC;
- cc.recycle();
- int length = bc.getLength();
- byte[] bbuf = bc.array();
- int start = bc.getStart();
- cc.appendAscii(bbuf, start, length);
- result = cc.toString();
- cc.recycle();
- }
- return result;
- }
-
- private void processParameters( byte bytes[], int start, int len ) {
- processParameters(bytes, start, len, getEncoding());
- }
-
- protected void parseBody() {
-
- parametersParsed = true;
- String enc = getCharacterEncoding();
-
-// if (usingInputStream || usingReader)
-// return;
- if (!getMethod().equalsIgnoreCase("POST"))
- return;
-
- String contentType = getContentType();
- if (contentType == null)
- contentType = "";
- int semicolon = contentType.indexOf(';');
- if (semicolon >= 0) {
- contentType = contentType.substring(0, semicolon).trim();
- } else {
- contentType = contentType.trim();
- }
- if (!("application/x-www-form-urlencoded".equals(contentType)))
- return;
-
- int len = getContentLength();
-
- if (len > 0) {
- try {
- byte[] formData = null;
- if (len < CACHED_POST_LEN) {
- if (postData == null)
- postData = new byte[CACHED_POST_LEN];
- formData = postData;
- } else {
- formData = new byte[len];
- }
- int actualLen = readPostBody(formData, len);
- if (actualLen == len) {
- processParameters(formData, 0, len);
- }
- } catch (Throwable t) {
- ; // Ignore
- }
- }
-
- }
-
- /**
- * Read post body in an array.
- */
- protected int readPostBody(byte body[], int len)
- throws IOException {
-
- int offset = 0;
- do {
- int inputLen = getBodyInputStream().read(body, offset, len - offset);
- if (inputLen <= 0) {
- return offset;
- }
- offset += inputLen;
- } while ((len - offset) > 0);
- return len;
-
- }
-
- // Async support - a subset of servlet spec, the fancy stuff is in the
- // facade.
-
- public boolean isAsyncStarted() {
- return async;
- }
-
- public void async() {
- this.async = true;
- }
-
- public void setAsyncTimeout(long timeout) {
- this.asyncTimeout = timeout;
- }
-
- /**
- * Server mode, request just received.
- */
- protected void processReceivedHeaders() throws IOException {
- BBuffer url = getMsgBytes().url();
- if (url.remaining() == 0) {
- System.err.println("No input");
- }
- if (url.get(0) == 'h') {
- int firstSlash = url.indexOf('/', 0);
- schemeMB.appendAscii(url.array(),
- url.getStart(), firstSlash + 2);
- if (!schemeMB.equals("http://") &&
- !schemeMB.equals("https://")) {
- httpCh.getResponse().setStatus(400);
- httpCh.abort("Error normalizing url " +
- getMsgBytes().url());
- return;
- }
-
- int urlStart = url.indexOf('/', firstSlash + 2);
- serverNameMB.recycle();
- serverNameMB.appendAscii(url.array(),
- url.getStart() + firstSlash + 2, urlStart - firstSlash - 2);
-
- url.position(url.getStart() + urlStart);
- }
- if (!httpCh.normalize(getMsgBytes().url())) {
- httpCh.getResponse().setStatus(400);
- httpCh.abort("Error normalizing url " +
- getMsgBytes().url());
- return;
- }
-
- method().set(getMsgBytes().method());
- requestURI().set(getMsgBytes().url());
- queryString().set(getMsgBytes().query());
- protocol().set(getMsgBytes().protocol());
-
- processMimeHeaders();
-
- // URL decode and normalize
- decodedUri.append(getMsgBytes().url());
-
- getURLDecoder().urlDecode(decodedUri, false);
-
- // Need to normalize again - %decoding may decode /
- if (!httpCh.normalize(decodedUri)) {
- httpCh.getResponse().setStatus(400);
- httpCh.abort("Invalid decoded uri " + decodedUri);
- return;
- }
- decodedURI().set(decodedUri);
-
- // default response protocol
- httpCh.getResponse().protocol().set(getMsgBytes().protocol());
- }
-
-
- public boolean hasBody() {
- return chunked || contentLength >= 0;
- }
-
- /**
- * Convert (if necessary) and return the absolute URL that represents the
- * resource referenced by this possibly relative URL. If this URL is
- * already absolute, return it unchanged.
- *
- * @param location URL to be (possibly) converted and then returned
- *
- * @exception IllegalArgumentException if a MalformedURLException is
- * thrown when converting the relative URL to an absolute one
- */
- public void toAbsolute(String location, CBuffer cb) {
-
- cb.recycle();
- if (location == null)
- return;
-
- boolean leadingSlash = location.startsWith("/");
- if (leadingSlash || !hasScheme(location)) {
-
- String scheme = getScheme();
- String name = serverName().toString();
- int port = getServerPort();
-
- cb.append(scheme);
- cb.append("://", 0, 3);
- cb.append(name);
- if ((scheme.equals("http") && port != 80)
- || (scheme.equals("https") && port != 443)) {
- cb.append(':');
- String portS = port + "";
- cb.append(portS);
- }
- if (!leadingSlash) {
- String relativePath = decodedURI().toString();
- int pos = relativePath.lastIndexOf('/');
- relativePath = relativePath.substring(0, pos);
-
- //String encodedURI = null;
- urlEncoding.urlEncode(relativePath, cb, charEncoder);
- //encodedURI = urlEncoder.encodeURL(relativePath);
- //redirectURLCC.append(encodedURI, 0, encodedURI.length());
- cb.append('/');
- }
-
- cb.append(location);
- } else {
- cb.append(location);
- }
-
- }
-
- /**
- * Determine if a URI string has a <code>scheme</code> component.
- */
- public static boolean hasScheme(String uri) {
- int len = uri.length();
- for(int i=0; i < len ; i++) {
- char c = uri.charAt(i);
- if(c == ':') {
- return i > 0;
- } else if(!isSchemeChar(c)) {
- return false;
- }
- }
- return false;
- }
-
- /**
- * Determine if the character is allowed in the scheme of a URI.
- * See RFC 2396, Section 3.1
- */
- private static boolean isSchemeChar(char c) {
- return Character.isLetterOrDigit(c) ||
- c == '+' || c == '-' || c == '.';
- }
-
- public IOWriter getCharEncoder() {
- return charEncoder;
- }
-
- public IOReader getCharDecoder() {
- return charDecoder;
- }
-
- public UrlEncoding getUrlEncoding() {
- return urlEncoding;
- }
-
- public BBuffer toBytes(CBuffer cb, BBuffer bb) {
- if (bb == null) {
- bb = BBuffer.allocate(cb.length());
- }
- getCharEncoder().encodeAll(cb, bb, "UTF-8");
- return bb;
- }
-
- public String toString() {
- IOBuffer out = new IOBuffer();
- try {
- Http11Connection.serialize(this, out);
- return out.readAll(null).toString();
- } catch (IOException e) {
- return "Invalid request";
- }
- }
-
- public boolean isSecure() {
- return ssl;
- }
-
- public HttpRequest setSecure(boolean ssl) {
- this.ssl = ssl;
- return this;
- }
-}
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/lite/http/HttpResponse.java b/modules/tomcat-lite/java/org/apache/tomcat/lite/http/HttpResponse.java
deleted file mode 100644
index d616c1c..0000000
--- a/modules/tomcat-lite/java/org/apache/tomcat/lite/http/HttpResponse.java
+++ /dev/null
@@ -1,581 +0,0 @@
-/*
- */
-package org.apache.tomcat.lite.http;
-
-import java.io.IOException;
-import java.util.HashMap;
-
-import org.apache.tomcat.lite.io.BBucket;
-import org.apache.tomcat.lite.io.BBuffer;
-import org.apache.tomcat.lite.io.CBuffer;
-
-public class HttpResponse extends HttpMessage {
-
- /*
- * Server status codes; see RFC 2068.
- */
-
- /**
- * Status code (100) indicating the client can continue.
- */
-
- public static final int SC_CONTINUE = 100;
-
-
- /**
- * Status code (101) indicating the server is switching protocols
- * according to Upgrade header.
- */
-
- public static final int SC_SWITCHING_PROTOCOLS = 101;
-
- /**
- * Status code (200) indicating the request succeeded normally.
- */
-
- public static final int SC_OK = 200;
-
- /**
- * Status code (201) indicating the request succeeded and created
- * a new resource on the server.
- */
-
- public static final int SC_CREATED = 201;
-
- /**
- * Status code (202) indicating that a request was accepted for
- * processing, but was not completed.
- */
-
- public static final int SC_ACCEPTED = 202;
-
- /**
- * Status code (203) indicating that the meta information presented
- * by the client did not originate from the server.
- */
-
- public static final int SC_NON_AUTHORITATIVE_INFORMATION = 203;
-
- /**
- * Status code (204) indicating that the request succeeded but that
- * there was no new information to return.
- */
-
- public static final int SC_NO_CONTENT = 204;
-
- /**
- * Status code (205) indicating that the agent <em>SHOULD</em> reset
- * the document view which caused the request to be sent.
- */
-
- public static final int SC_RESET_CONTENT = 205;
-
- /**
- * Status code (206) indicating that the server has fulfilled
- * the partial GET request for the resource.
- */
-
- public static final int SC_PARTIAL_CONTENT = 206;
-
- /**
- * Used by Webdav.
- */
- public static final int SC_MULTI_STATUS = 207;
- // This one collides with HTTP 1.1
- // "207 Partial Update OK"
-
- /**
- * Status code (300) indicating that the requested resource
- * corresponds to any one of a set of representations, each with
- * its own specific location.
- */
-
- public static final int SC_MULTIPLE_CHOICES = 300;
-
- /**
- * Status code (301) indicating that the resource has permanently
- * moved to a new location, and that future references should use a
- * new URI with their requests.
- */
-
- public static final int SC_MOVED_PERMANENTLY = 301;
-
- /**
- * Status code (302) indicating that the resource has temporarily
- * moved to another location, but that future references should
- * still use the original URI to access the resource.
- *
- * This definition is being retained for backwards compatibility.
- * SC_FOUND is now the preferred definition.
- */
-
- public static final int SC_MOVED_TEMPORARILY = 302;
-
- /**
- * Status code (302) indicating that the resource reside
- * temporarily under a different URI. Since the redirection might
- * be altered on occasion, the client should continue to use the
- * Request-URI for future requests.(HTTP/1.1) To represent the
- * status code (302), it is recommended to use this variable.
- */
-
- public static final int SC_FOUND = 302;
-
- /**
- * Status code (303) indicating that the response to the request
- * can be found under a different URI.
- */
-
- public static final int SC_SEE_OTHER = 303;
-
- /**
- * Status code (304) indicating that a conditional GET operation
- * found that the resource was available and not modified.
- */
-
- public static final int SC_NOT_MODIFIED = 304;
-
- /**
- * Status code (305) indicating that the requested resource
- * <em>MUST</em> be accessed through the proxy given by the
- * <code><em>Location</em></code> field.
- */
-
- public static final int SC_USE_PROXY = 305;
-
- /**
- * Status code (307) indicating that the requested resource
- * resides temporarily under a different URI. The temporary URI
- * <em>SHOULD</em> be given by the <code><em>Location</em></code>
- * field in the response.
- */
-
- public static final int SC_TEMPORARY_REDIRECT = 307;
-
- /**
- * Status code (400) indicating the request sent by the client was
- * syntactically incorrect.
- */
-
- public static final int SC_BAD_REQUEST = 400;
-
- /**
- * Status code (401) indicating that the request requires HTTP
- * authentication.
- */
-
- public static final int SC_UNAUTHORIZED = 401;
-
- /**
- * Status code (402) reserved for future use.
- */
-
- public static final int SC_PAYMENT_REQUIRED = 402;
-
- /**
- * Status code (403) indicating the server understood the request
- * but refused to fulfill it.
- */
-
- public static final int SC_FORBIDDEN = 403;
-
- /**
- * Status code (404) indicating that the requested resource is not
- * available.
- */
-
- public static final int SC_NOT_FOUND = 404;
-
- /**
- * Status code (405) indicating that the method specified in the
- * <code><em>Request-Line</em></code> is not allowed for the resource
- * identified by the <code><em>Request-URI</em></code>.
- */
-
- public static final int SC_METHOD_NOT_ALLOWED = 405;
-
- /**
- * Status code (406) indicating that the resource identified by the
- * request is only capable of generating response entities which have
- * content characteristics not acceptable according to the accept
- * headers sent in the request.
- */
-
- public static final int SC_NOT_ACCEPTABLE = 406;
-
- /**
- * Status code (407) indicating that the client <em>MUST</em> first
- * authenticate itself with the proxy.
- */
-
- public static final int SC_PROXY_AUTHENTICATION_REQUIRED = 407;
-
- /**
- * Status code (408) indicating that the client did not produce a
- * request within the time that the server was prepared to wait.
- */
-
- public static final int SC_REQUEST_TIMEOUT = 408;
-
- /**
- * Status code (409) indicating that the request could not be
- * completed due to a conflict with the current state of the
- * resource.
- */
-
- public static final int SC_CONFLICT = 409;
-
- /**
- * Status code (410) indicating that the resource is no longer
- * available at the server and no forwarding address is known.
- * This condition <em>SHOULD</em> be considered permanent.
- */
-
- public static final int SC_GONE = 410;
-
- /**
- * Status code (411) indicating that the request cannot be handled
- * without a defined <code><em>Content-Length</em></code>.
- */
-
- public static final int SC_LENGTH_REQUIRED = 411;
-
- /**
- * Status code (412) indicating that the precondition given in one
- * or more of the request-header fields evaluated to false when it
- * was tested on the server.
- */
-
- public static final int SC_PRECONDITION_FAILED = 412;
-
- /**
- * Status code (413) indicating that the server is refusing to process
- * the request because the request entity is larger than the server is
- * willing or able to process.
- */
-
- public static final int SC_REQUEST_ENTITY_TOO_LARGE = 413;
-
- /**
- * Status code (414) indicating that the server is refusing to service
- * the request because the <code><em>Request-URI</em></code> is longer
- * than the server is willing to interpret.
- */
-
- public static final int SC_REQUEST_URI_TOO_LONG = 414;
-
- /**
- * Status code (415) indicating that the server is refusing to service
- * the request because the entity of the request is in a format not
- * supported by the requested resource for the requested method.
- */
-
- public static final int SC_UNSUPPORTED_MEDIA_TYPE = 415;
-
- /**
- * Status code (416) indicating that the server cannot serve the
- * requested byte range.
- */
-
- public static final int SC_REQUESTED_RANGE_NOT_SATISFIABLE = 416;
-
- /**
- * Status code (417) indicating that the server could not meet the
- * expectation given in the Expect request header.
- */
-
- public static final int SC_EXPECTATION_FAILED = 417;
-
- /**
- * Status code (423) indicating the destination resource of a
- * method is locked, and either the request did not contain a
- * valid Lock-Info header, or the Lock-Info header identifies
- * a lock held by another principal.
- */
- public static final int SC_LOCKED = 423;
-
- /**
- * Status code (500) indicating an error inside the HTTP server
- * which prevented it from fulfilling the request.
- */
-
- public static final int SC_INTERNAL_SERVER_ERROR = 500;
-
- /**
- * Status code (501) indicating the HTTP server does not support
- * the functionality needed to fulfill the request.
- */
-
- public static final int SC_NOT_IMPLEMENTED = 501;
-
- /**
- * Status code (502) indicating that the HTTP server received an
- * invalid response from a server it consulted when acting as a
- * proxy or gateway.
- */
-
- public static final int SC_BAD_GATEWAY = 502;
-
- /**
- * Status code (503) indicating that the HTTP server is
- * temporarily overloaded, and unable to handle the request.
- */
-
- public static final int SC_SERVICE_UNAVAILABLE = 503;
-
- /**
- * Status code (504) indicating that the server did not receive
- * a timely response from the upstream server while acting as
- * a gateway or proxy.
- */
-
- public static final int SC_GATEWAY_TIMEOUT = 504;
-
- /**
- * Status code (505) indicating that the server does not support
- * or refuses to support the HTTP protocol version that was used
- * in the request message.
- */
-
- public static final int SC_HTTP_VERSION_NOT_SUPPORTED = 505;
-
- // will not be recycled
- public Object nativeResponse;
-
- protected CBuffer message = CBuffer.newInstance();
-
- int status = -1;
-
- HttpResponse(HttpChannel httpCh) {
- super(httpCh);
- }
-
- public void recycle() {
- super.recycle();
- message.recycle();
- status = -1;
- }
-
- public void setMessage(String s) {
- message.set(filter(s));
- }
-
- public String getMessage() {
- return message.toString();
- }
-
- public CBuffer getMessageBuffer() {
- return message;
- }
-
- byte[] S_200 = new byte[] { '2', '0', '0' };
-
- public void setStatus(int i) {
- status = i;
- }
-
- public void sendError(int status) {
- this.status = status;
- }
-
- public void sendError(int status, String msg) {
- message.set(msg);
- }
-
- public int getStatus() {
- if (status >= 0) {
- return status;
- }
- if (getMsgBytes().status().isNull()) {
- status = 200;
- } else {
- try {
- status = getMsgBytes().status().getInt();
- } catch(NumberFormatException ex) {
- status = 500;
- httpCh.log.severe("Invalid status " + getMsgBytes().status());
- }
- }
- return status;
- }
-
- public HttpRequest getRequest() {
- return getHttpChannel().getRequest();
- }
-
- // Http client mode.
- protected void processReceivedHeaders() throws IOException {
- protocol().set(getMsgBytes().protocol());
- message.set(getMsgBytes().message());
- processMimeHeaders();
- // TODO: if protocol == 1.0 and we requested 1.1, downgrade getHttpChannel().pro
- try {
- status = getStatus();
- } catch (Throwable t) {
- getHttpChannel().log.warning("Invalid status " + getMsgBytes().status() + " " + getMessage());
- }
- }
-
- /**
- * All responses to the HEAD request method MUST NOT include a
- * message-body, even though the presence of entity- header fields might
- * lead one to believe they do. All 1xx (informational), 204 (no content)
- * , and 304 (not modified) responses MUST NOT include a message-body. All
- * other responses do include a message-body, although it MAY be of zero
- * length.
- */
- public boolean hasBody() {
- if (httpCh.getRequest().method().equals("HEAD")) {
- return false;
- }
- if (status >= 100 && status < 200) {
- return false;
- }
- // what about (status == 205) ?
- if ((status == 204)
- || (status == 304)) {
- return false;
- }
- return true;
- }
-
- /** Get the status string associated with a status code.
- * No I18N - return the messages defined in the HTTP spec.
- * ( the user isn't supposed to see them, this is the last
- * thing to translate)
- *
- * Common messages are cached.
- *
- */
- static BBucket getMessage( int status ) {
- // method from Response.
-
- // Does HTTP requires/allow international messages or
- // are pre-defined? The user doesn't see them most of the time
- switch( status ) {
- case 200:
- return st_200;
- case 302:
- return st_302;
- case 400:
- return st_400;
- case 404:
- return st_404;
- }
- BBucket bb = stats.get(status);
- if (bb == null) {
- return st_unknown;
- }
- return bb;
- }
-
- public static String getStatusText(int code) {
- return getMessage(code).toString();
- }
-
- static BBucket st_unknown = BBuffer.wrapper("No Message");
- static BBucket st_200 = BBuffer.wrapper("OK");
- static BBucket st_302= BBuffer.wrapper("Moved Temporarily");
- static BBucket st_400= BBuffer.wrapper("Bad Request");
- static BBucket st_404= BBuffer.wrapper("Not Found");
-
- static HashMap<Integer,BBucket> stats = new HashMap<Integer, BBucket>();
- private static void addStatus(int stat, String msg) {
- stats.put(stat, BBuffer.wrapper(msg));
- }
-
- static {
- addStatus(100, "Continue");
- addStatus(101, "Switching Protocols");
- addStatus(200, "OK");
- addStatus(201, "Created");
- addStatus(202, "Accepted");
- addStatus(203, "Non-Authoritative Information");
- addStatus(204, "No Content");
- addStatus(205, "Reset Content");
- addStatus(206, "Partial Content");
- addStatus(207, "Multi-Status");
- addStatus(300, "Multiple Choices");
- addStatus(301, "Moved Permanently");
- addStatus(302, "Moved Temporarily");
- addStatus(303, "See Other");
- addStatus(304, "Not Modified");
- addStatus(305, "Use Proxy");
- addStatus(307, "Temporary Redirect");
- addStatus(400, "Bad Request");
- addStatus(401, "Unauthorized");
- addStatus(402, "Payment Required");
- addStatus(403, "Forbidden");
- addStatus(404, "Not Found");
- addStatus(405, "Method Not Allowed");
- addStatus(406, "Not Acceptable");
- addStatus(407, "Proxy Authentication Required");
- addStatus(408, "Request Timeout");
- addStatus(409, "Conflict");
- addStatus(410, "Gone");
- addStatus(411, "Length Required");
- addStatus(412, "Precondition Failed");
- addStatus(413, "Request Entity Too Large");
- addStatus(414, "Request-URI Too Long");
- addStatus(415, "Unsupported Media Type");
- addStatus(416, "Requested Range Not Satisfiable");
- addStatus(417, "Expectation Failed");
- addStatus(422, "Unprocessable Entity");
- addStatus(423, "Locked");
- addStatus(424, "Failed Dependency");
- addStatus(500, "Internal Server Error");
- addStatus(501, "Not Implemented");
- addStatus(502, "Bad Gateway");
- addStatus(503, "Service Unavailable");
- addStatus(504, "Gateway Timeout");
- addStatus(505, "HTTP Version Not Supported");
- addStatus(507, "Insufficient Storage");
- addStatus(SC_LOCKED, "Locked");
-
-
- }
-
- /**
- * Filter the specified message string for characters that are sensitive
- * in HTML. This avoids potential attacks caused by including JavaScript
- * codes in the request URL that is often reported in error messages.
- *
- * @param message The message string to be filtered
- */
- private static String filter(String message) {
-
- if (message == null)
- return (null);
- if (message.indexOf('<') < 0 &&
- message.indexOf('>') < 0 &&
- message.indexOf('&') < 0 &&
- message.indexOf('"') < 0) {
- return message;
- }
-
- char content[] = new char[message.length()];
- message.getChars(0, message.length(), content, 0);
-
- StringBuffer result = new StringBuffer(content.length + 50);
- for (int i = 0; i < content.length; i++) {
- switch (content[i]) {
- case '<':
- result.append("<");
- break;
- case '>':
- result.append(">");
- break;
- case '&':
- result.append("&");
- break;
- case '"':
- result.append(""");
- break;
- default:
- result.append(content[i]);
- }
- }
- return (result.toString());
- }
-
-}
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/lite/http/HttpServer.java b/modules/tomcat-lite/java/org/apache/tomcat/lite/http/HttpServer.java
deleted file mode 100644
index 799e4c4..0000000
--- a/modules/tomcat-lite/java/org/apache/tomcat/lite/http/HttpServer.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- */
-package org.apache.tomcat.lite.http;
-
-import org.apache.tomcat.lite.io.SocketConnector;
-import org.apache.tomcat.lite.io.SslProvider;
-import org.apache.tomcat.lite.io.jsse.JsseSslProvider;
-
-/**
- * Main entry point for HTTP server code.
- *
- * ( initial draft - will replace statics, add helpers, etc )
- */
-public class HttpServer {
- static SslProvider sslConC = new JsseSslProvider();
-
- public synchronized static HttpConnector newServer(int port) {
- return new HttpConnector(new SocketConnector()).
- withSsl(sslConC).setPort(port);
- }
-
- public synchronized static HttpConnector newSslServer(int port) {
- // DHE broken in harmony - will replace with a flag
- // SslConnector.setEnabledCiphers(new String[] {
- // "TLS_RSA_WITH_3DES_EDE_CBC_SHA"
- // });
- // -cipher DES-CBC3-SHA
-
- SslProvider sslCon = new JsseSslProvider();
-
- return new HttpConnector(new SocketConnector()).
- withSsl(sslCon).setPort(port).setServerSsl(true);
- }
-
-}
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/lite/http/HttpWriter.java b/modules/tomcat-lite/java/org/apache/tomcat/lite/http/HttpWriter.java
deleted file mode 100644
index b18e0ac..0000000
--- a/modules/tomcat-lite/java/org/apache/tomcat/lite/http/HttpWriter.java
+++ /dev/null
@@ -1,313 +0,0 @@
-/*
- */
-package org.apache.tomcat.lite.http;
-
-import java.io.IOException;
-import java.io.Writer;
-
-import org.apache.tomcat.lite.io.IOOutputStream;
-import org.apache.tomcat.lite.io.IOWriter;
-
-/**
- * Implement character translation and buffering.
- *
- * The actual buffering happens in the IOBuffer - we translate the
- * chars as soon as we get them.
- *
- * For servlet compat you can set a buffer size and a flush() will happen
- * when the number of chars have been written. Note that writes at a lower
- * layer can be done and are not counted.
- *
- * @author Costin Manolache
- */
-public class HttpWriter extends Writer {
-
- public static final String DEFAULT_ENCODING = "ISO-8859-1";
- public static final int DEFAULT_BUFFER_SIZE = 8*1024;
-
- // ----------------------------------------------------- Instance Variables
- HttpMessage message;
-
- /**
- * The byte buffer.
- */
- protected IOOutputStream bb;
-
- int bufferSize = DEFAULT_BUFFER_SIZE;
-
- /**
- * Number of chars written.
- */
- protected int wSinceFlush = 0;
-
-
- /**
- * Flag which indicates if the output buffer is closed.
- */
- protected boolean closed = false;
-
- /**
- * Encoding to use.
- * TODO: isn't it redundant ? enc, gotEnc, conv plus the enc in the bb
- */
- protected String enc;
-
-
- /**
- * Encoder is set.
- */
- protected boolean gotEnc = false;
-
-
- /**
- * List of encoders. The writer is reused - the encoder mapping
- * avoids creating expensive objects. In future it'll contain nio.Charsets
- */
- //protected Map<String, C2BConverter> encoders = new HashMap();
-
-
- /**
- * Current char to byte converter. TODO: replace with Charset
- */
- private IOWriter conv;
-
- /**
- * Suspended flag. All output bytes will be swallowed if this is true.
- */
- protected boolean suspended = false;
-
-
- // ----------------------------------------------------------- Constructors
-
-
- /**
- * Default constructor. Allocate the buffer with the default buffer size.
- * @param out
- */
- public HttpWriter(HttpMessage message, IOOutputStream out,
- IOWriter conv) {
- this.message = message;
- bb = out;
- this.conv = conv;
- }
-
- // ------------------------------------------------------------- Properties
-
-
- /**
- * Is the response output suspended ?
- *
- * @return suspended flag value
- */
- public boolean isSuspended() {
- return this.suspended;
- }
-
-
- /**
- * Set the suspended flag.
- *
- * @param suspended New suspended flag value
- */
- public void setSuspended(boolean suspended) {
- this.suspended = suspended;
- }
-
-
- // --------------------------------------------------------- Public Methods
-
-
- /**
- * Recycle the output buffer.
- */
- public void recycle() {
- wSinceFlush = 0;
- bb.recycle();
- closed = false;
- suspended = false;
-
-// if (conv != null) {
-// conv.recycle();
-// }
-
- gotEnc = false;
- enc = null;
- }
-
- public void close()
- throws IOException {
-
- if (closed)
- return;
- if (suspended)
- return;
-
- push();
- closed = true;
-
- bb.close();
- }
-
-
- /**
- * Flush bytes or chars contained in the buffer.
- *
- * @throws IOException An underlying IOException occurred
- */
- public void flush()
- throws IOException {
- push();
- bb.flush(); // will send the data
- wSinceFlush = 0;
- }
-
- /**
- * Flush chars to the byte buffer.
- */
- public void push()
- throws IOException {
-
- if (suspended)
- return;
- getConv().push();
-
- }
-
-
- private void updateSize(int cnt) throws IOException {
- wSinceFlush += cnt;
- if (wSinceFlush > bufferSize) {
- flush();
- }
- }
-
- public void write(int c)
- throws IOException {
- if (suspended)
- return;
- getConv().write(c);
- updateSize(1);
- }
-
-
- public void write(char c[])
- throws IOException {
- write(c, 0, c.length);
- }
-
-
- public void write(char c[], int off, int len)
- throws IOException {
- if (suspended)
- return;
- getConv().write(c, off, len);
- updateSize(len);
- }
-
-
- public void write(StringBuffer sb)
- throws IOException {
- if (suspended)
- return;
- int len = sb.length();
- getConv().write(sb.toString());
- updateSize(len);
- }
-
-
- /**
- * Append a string to the buffer
- */
- public void write(String s, int off, int len)
- throws IOException {
- if (suspended)
- return;
- if (s==null)
- s="null";
- getConv().write( s, off, len );
- updateSize(len);
- }
-
-
- public void write(String s)
- throws IOException {
- if (s==null)
- s="null";
- write(s, 0, s.length());
- }
-
- public void println() throws IOException {
- write("\n");
- }
-
- public void println(String s) throws IOException {
- write(s);
- write("\n");
- }
-
- public void print(String s) throws IOException {
- write(s);
- }
-
- public void checkConverter()
- throws IOException {
-// if (gotEnc) {
-// return;
-// }
-// if (enc == null) {
-// enc = message.getCharacterEncoding();
-// }
-//
-// gotEnc = true;
-// if (enc == null)
-// enc = DEFAULT_ENCODING;
-// conv = (C2BConverter) encoders.get(enc);
-//
-// if (conv == null) {
-// conv = C2BConverter.newConverter(message.getBodyOutputStream(),
-// enc);
-// encoders.put(enc, conv);
-//
-// }
- }
-
- public int getWrittenSinceFlush() {
- return wSinceFlush;
- }
-
-
- public void setBufferSize(int size) {
- if (size > bufferSize) {
- bufferSize = size;
- }
- }
-
- /**
- * Clear any data that was buffered.
- */
- public void reset() {
- if (conv != null) {
- conv.recycle();
- }
- wSinceFlush = 0;
- gotEnc = false;
- enc = null;
- bb.reset();
- }
-
-
- public int getBufferSize() {
- return bufferSize;
- }
-
- protected IOWriter getConv() throws IOException {
- checkConverter();
- return conv;
- }
-
- public void println(CharSequence key) throws IOException {
- // TODO: direct
- println(key.toString());
- }
-
-}
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/lite/http/MappingData.java b/modules/tomcat-lite/java/org/apache/tomcat/lite/http/MappingData.java
deleted file mode 100644
index 1b8d939..0000000
--- a/modules/tomcat-lite/java/org/apache/tomcat/lite/http/MappingData.java
+++ /dev/null
@@ -1,69 +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.lite.http;
-
-import org.apache.tomcat.lite.io.CBuffer;
-
-
-/**
- * Mapping data.
- *
- * @author Remy Maucherat
- */
-public class MappingData {
-
- public Object context = null; // ServletContextImpl
-
- public BaseMapper.Context contextMap;
-
- public BaseMapper.ServiceMapping service = null;
-
- public CBuffer contextPath = CBuffer.newInstance();
- public CBuffer requestPath = CBuffer.newInstance();
- public CBuffer wrapperPath = CBuffer.newInstance();
- public CBuffer pathInfo = CBuffer.newInstance();
-
- public CBuffer redirectPath = CBuffer.newInstance();
-
- // Extension
- CBuffer ext = CBuffer.newInstance();
- CBuffer tmpPrefix = CBuffer.newInstance();
-
- // Excluding context path, with a '/' added if needed
- CBuffer tmpServletPath = CBuffer.newInstance();
-
- // Excluding context path, with a '/' added if needed
- CBuffer tmpWelcome = CBuffer.newInstance();
-
- public void recycle() {
- service = null;
- context = null;
- pathInfo.recycle();
- requestPath.recycle();
- wrapperPath.recycle();
- contextPath.recycle();
- redirectPath.recycle();
- contextMap = null;
- }
-
-
- public Object getServiceObject() {
- return service == null ? null : service.object;
- }
-
-}
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/lite/http/MultiMap.java b/modules/tomcat-lite/java/org/apache/tomcat/lite/http/MultiMap.java
deleted file mode 100644
index 7b8fb14..0000000
--- a/modules/tomcat-lite/java/org/apache/tomcat/lite/http/MultiMap.java
+++ /dev/null
@@ -1,357 +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.lite.http;
-
-import java.util.ArrayList;
-import java.util.Enumeration;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-
-import org.apache.tomcat.lite.io.BBuffer;
-import org.apache.tomcat.lite.io.CBucket;
-import org.apache.tomcat.lite.io.CBuffer;
-
-/**
- * Map used to represent headers and parameters ( could be used
- * for cookies too )
- *
- * It'll avoid garbage collection, like original tomcat classes,
- * by converting to chars and strings late.
- *
- * Not thread safe.
- */
-public class MultiMap {
-
- public static class Entry {
- // Wrappers from the head message bytes.
- BBuffer nameB;
- BBuffer valueB;
-
- CBuffer key = CBuffer.newInstance();
- private CBuffer value = CBuffer.newInstance();
-
- /**
- * For the first entry with a given name: list of all
- * other entries, including this one, with same name.
- *
- * For second or more: empty list
- */
- public List<Entry> values = new ArrayList<Entry>();
-
- public void recycle() {
- key.recycle();
- value.recycle();
- //next=null;
- nameB = null;
- valueB = null;
- values.clear();
- }
-
- public CBuffer getName() {
- if (key.length() == 0 && nameB != null) {
- key.set(nameB);
- }
- return key;
- }
-
- public CBuffer getValue() {
- if (value.length() == 0 && valueB != null) {
- value.set(valueB);
- }
- return value;
- }
-
- /** Important - used by values iterator, returns strings
- * from each entry
- */
- public String toString() {
- return getValue().toString();
- }
-
- }
-
- // active entries
- protected int count;
-
- // The key will be converted to lower case
- boolean toLower = false;
-
- // Some may be inactive - up to count.
- protected List<Entry> entries = new ArrayList<Entry>();
-
- // 2 options: convert all header/param names to String
- // or use a temp CBuffer to map
- Map<CBuffer, Entry> map =
- new HashMap<CBuffer, Entry>();
-
- public void recycle() {
- for (int i = 0; i < count; i++) {
- Entry entry = entries.get(i);
- entry.recycle();
- }
- count = 0;
- map.clear();
- }
-
- // ----------- Mutations ------------------------
-
- protected Entry newEntry() {
- return new Entry();
- }
-
- /**
- * Adds a partially constructed field entry.
- * Updates count - but will not affect the map.
- */
- private Entry getEntryForAdd() {
- Entry entry;
- if (count >= entries.size()) {
- entry = newEntry();
- entries.add(entry);
- } else {
- entry = entries.get(count);
- }
- count++;
- return entry;
- }
-
-
- /** Create a new named header , return the CBuffer
- * container for the new value
- */
- public Entry addEntry(CharSequence name ) {
- Entry mh = getEntryForAdd();
- mh.getName().append(name);
- if (toLower) {
- mh.getName().toLower();
- }
- updateMap(mh);
- return mh;
- }
-
- /** Create a new named header , return the CBuffer
- * container for the new value
- */
- public Entry addEntry(BBuffer name ) {
- Entry mh = getEntryForAdd();
- mh.nameB = name;
- if (toLower) {
- mh.getName().toLower();
- }
- updateMap(mh);
-
- return mh;
- }
-
- private void updateMap(Entry mh) {
- Entry topEntry = map.get(mh.getName());
-
- if (topEntry == null) {
- map.put(mh.getName(), mh);
- mh.values.add(mh);
- } else {
- topEntry.values.add(mh);
- }
- }
-
-
-
- public void remove(CharSequence key) {
- CBucket ckey = key(key);
- Entry entry = getEntry(ckey);
- if (entry != null) {
- map.remove(ckey);
-
- for (int i = count - 1; i >= 0; i--) {
- entry = entries.get(i);
- if (entry.getName().equals(key)) {
- entry.recycle();
- entries.remove(i);
- count--;
- }
- }
- }
- }
-
- // --------------- Key-based access --------------
- CBuffer tmpKey = CBuffer.newInstance();
-
- /**
- * Finds and returns a header field with the given name. If no such
- * field exists, null is returned. If more than one such field is
- * in the header, an arbitrary one is returned.
- */
- public CBuffer getHeader(String name) {
- for (int i = 0; i < count; i++) {
- if (entries.get(i).getName().equalsIgnoreCase(name)) {
- return entries.get(i).getValue();
- }
- }
- return null;
- }
-
- private CBucket key(CharSequence key) {
- if (key instanceof CBucket) {
- CBucket res = (CBucket) key;
- if (!toLower || !res.hasUpper()) {
- return res;
- }
- }
- tmpKey.recycle();
- tmpKey.append(key);
- if (toLower) {
- tmpKey.toLower();
- }
- return tmpKey;
- }
-
- public Entry getEntry(CharSequence key) {
- Entry entry = map.get(key(key));
- return entry;
- }
-
- public Entry getEntry(CBucket buf) {
- // lowercase ?
- Entry entry = map.get(buf);
- return entry;
- }
-
- public Enumeration<String> names() {
- return new IteratorEnumerator(map.keySet().iterator());
- }
-
- // ----------- Index access --------------
-
- /**
- * Number of entries ( including those with same key
- *
- * @return
- */
- public int size() {
- return count;
- }
-
-
- public CharSequence getKey(int idx) {
- return entries.get(idx).key;
- }
-
- public Entry getEntry(int idx) {
- return entries.get(idx);
- }
-
- /**
- * Returns the Nth header name, or null if there is no such header.
- * This may be used to iterate through all header fields.
- */
- public CBuffer getName(int n) {
- return n < count ? entries.get(n).getName() : null;
- }
-
- /**
- * Returns the Nth header value, or null if there is no such header.
- * This may be used to iterate through all header fields.
- */
- public CBuffer getValue(int n) {
- return n >= 0 && n < count ? entries.get(n).getValue() : null;
- }
-
- // ----------- Helpers --------------
- public void add(CharSequence key, CharSequence value) {
- Entry mh = addEntry(key);
- mh.value.append(value);
- }
-
- /** Create a new named header , return the CBuffer
- * container for the new value
- */
- public CBuffer addValue( String name ) {
- return addEntry(name).getValue();
- }
-
- public Entry setEntry( String name ) {
- remove(name);
- return addEntry(name);
- }
-
- public void set(CharSequence key, CharSequence value) {
- remove(key);
- add(key, value);
- }
-
- public CBuffer setValue( String name ) {
- remove(name);
- return addValue(name);
- }
-
- public CBuffer get(CharSequence key) {
- Entry entry = getEntry(key);
- return (entry == null) ? null : entry.value;
- }
-
- public String getString(CharSequence key) {
- Entry entry = getEntry(key);
- return (entry == null) ? null : entry.value.toString();
- }
-
-
- // -------------- support classes ----------------
-
- public static class IteratorEnumerator implements Enumeration<String> {
- private final Iterator keyI;
-
- public IteratorEnumerator(Iterator iterator) {
- this.keyI = iterator;
- }
-
-
- public boolean hasMoreElements() {
- return keyI.hasNext();
- }
-
-
- public String nextElement() {
- return keyI.next().toString();
- }
-
- }
-
- public static final Enumeration<String> EMPTY =
- new Enumeration<String>() {
-
- @Override
- public boolean hasMoreElements() {
- return false;
- }
-
- @Override
- public String nextElement() {
- return null;
- }
-
- };
-
- public MultiMap insensitive() {
- toLower = true;
- return this;
- }
-
-
-}
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/lite/http/ServerCookie.java b/modules/tomcat-lite/java/org/apache/tomcat/lite/http/ServerCookie.java
deleted file mode 100644
index 4cf66f2..0000000
--- a/modules/tomcat-lite/java/org/apache/tomcat/lite/http/ServerCookie.java
+++ /dev/null
@@ -1,819 +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.lite.http;
-
-import java.io.Serializable;
-import java.text.DateFormat;
-import java.text.FieldPosition;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-import java.util.List;
-import java.util.Locale;
-import java.util.TimeZone;
-
-import org.apache.tomcat.lite.io.BBuffer;
-import org.apache.tomcat.lite.io.CBuffer;
-
-
-/**
- * Server-side cookie representation.
- * Allows recycling and uses MessageBytes as low-level
- * representation ( and thus the byte-> char conversion can be delayed
- * until we know the charset ).
- *
- * Tomcat.core uses this recyclable object to represent cookies,
- * and the facade will convert it to the external representation.
- */
-public class ServerCookie implements Serializable {
-
- // Version 0 (Netscape) attributes
- private BBuffer name = BBuffer.allocate();
- private BBuffer value = BBuffer.allocate();
-
- private CBuffer nameC = CBuffer.newInstance();
-
- // Expires - Not stored explicitly. Generated from Max-Age (see V1)
- private BBuffer path = BBuffer.allocate();
- private BBuffer domain = BBuffer.allocate();
- private boolean secure;
-
- // Version 1 (RFC2109) attributes
- private BBuffer comment = BBuffer.allocate();
- private int maxAge = -1;
- private int version = 0;
-
- // Other fields
- private static final String OLD_COOKIE_PATTERN =
- "EEE, dd-MMM-yyyy HH:mm:ss z";
- private static final ThreadLocal<DateFormat> OLD_COOKIE_FORMAT =
- new ThreadLocal<DateFormat>() {
- protected DateFormat initialValue() {
- DateFormat df =
- new SimpleDateFormat(OLD_COOKIE_PATTERN, Locale.US);
- df.setTimeZone(TimeZone.getTimeZone("GMT"));
- return df;
- }
- };
-
- private static final String ancientDate;
-
-
- static {
- ancientDate = OLD_COOKIE_FORMAT.get().format(new Date(10000));
- }
-
- /**
- * If set to true, we parse cookies according to the servlet spec,
- */
- public static final boolean STRICT_SERVLET_COMPLIANCE =
- Boolean.valueOf(System.getProperty("org.apache.catalina.STRICT_SERVLET_COMPLIANCE", "false")).booleanValue();
-
- /**
- * If set to false, we don't use the IE6/7 Max-Age/Expires work around
- */
- public static final boolean ALWAYS_ADD_EXPIRES =
- Boolean.valueOf(System.getProperty("org.apache.tomcat.util.http.ServerCookie.ALWAYS_ADD_EXPIRES", "true")).booleanValue();
-
- // Note: Servlet Spec =< 2.5 only refers to Netscape and RFC2109,
- // not RFC2965
-
- // Version 1 (RFC2965) attributes
- // TODO Add support for CommentURL
- // Discard - implied by maxAge <0
- // TODO Add support for Port
-
- public ServerCookie() {
- }
-
- public void recycle() {
- path.recycle();
- name.recycle();
- value.recycle();
- comment.recycle();
- maxAge=-1;
- path.recycle();
- domain.recycle();
- version=0;
- secure=false;
- }
-
- public BBuffer getComment() {
- return comment;
- }
-
- public BBuffer getDomain() {
- return domain;
- }
-
- public void setMaxAge(int expiry) {
- maxAge = expiry;
- }
-
- public int getMaxAge() {
- return maxAge;
- }
-
- public BBuffer getPath() {
- return path;
- }
-
- public void setSecure(boolean flag) {
- secure = flag;
- }
-
- public boolean getSecure() {
- return secure;
- }
-
- public BBuffer getName() {
- return name;
- }
-
- public BBuffer getValue() {
- return value;
- }
-
- public int getVersion() {
- return version;
- }
-
- public void setVersion(int v) {
- version = v;
- }
-
-
- // -------------------- utils --------------------
-
- public String toString() {
- return "Cookie " + getName() + "=" + getValue() + " ; "
- + getVersion() + " " + getPath() + " " + getDomain();
- }
-
- private static final String tspecials = ",; ";
- private static final String tspecials2 = "()<>@,;:\\\"/[]?={} \t";
- private static final String tspecials2NoSlash = "()<>@,;:\\\"[]?={} \t";
-
- /*
- * Tests a string and returns true if the string counts as a
- * reserved token in the Java language.
- *
- * @param value the <code>String</code> to be tested
- *
- * @return <code>true</code> if the <code>String</code> is a reserved
- * token; <code>false</code> if it is not
- */
- public static boolean isToken(String value) {
- return isToken(value,null);
- }
-
- public static boolean isToken(String value, String literals) {
- String tspecials = (literals==null?ServerCookie.tspecials:literals);
- if( value==null) return true;
- int len = value.length();
-
- for (int i = 0; i < len; i++) {
- char c = value.charAt(i);
-
- if (tspecials.indexOf(c) != -1)
- return false;
- }
- return true;
- }
-
- public static boolean containsCTL(String value, int version) {
- if( value==null) return false;
- int len = value.length();
- for (int i = 0; i < len; i++) {
- char c = value.charAt(i);
- if (c < 0x20 || c >= 0x7f) {
- if (c == 0x09)
- continue; //allow horizontal tabs
- return true;
- }
- }
- return false;
- }
-
- public static boolean isToken2(String value) {
- return isToken2(value,null);
- }
-
- public static boolean isToken2(String value, String literals) {
- String tspecials2 = (literals==null?ServerCookie.tspecials2:literals);
- if( value==null) return true;
- int len = value.length();
-
- for (int i = 0; i < len; i++) {
- char c = value.charAt(i);
- if (tspecials2.indexOf(c) != -1)
- return false;
- }
- return true;
- }
-
- // -------------------- Cookie parsing tools
-
-
- /**
- * Return the header name to set the cookie, based on cookie version.
- */
- public String getCookieHeaderName() {
- return getCookieHeaderName(version);
- }
-
- /**
- * Return the header name to set the cookie, based on cookie version.
- */
- public static String getCookieHeaderName(int version) {
- // TODO Re-enable logging when RFC2965 is implemented
- // log( (version==1) ? "Set-Cookie2" : "Set-Cookie");
- if (version == 1) {
- // XXX RFC2965 not referenced in Servlet Spec
- // Set-Cookie2 is not supported by Netscape 4, 6, IE 3, 5
- // Set-Cookie2 is supported by Lynx and Opera
- // Need to check on later IE and FF releases but for now...
- // RFC2109
- return "Set-Cookie";
- // return "Set-Cookie2";
- } else {
- // Old Netscape
- return "Set-Cookie";
- }
- }
-
- // TODO RFC2965 fields also need to be passed
- public static void appendCookieValue( StringBuffer headerBuf,
- int version,
- String name,
- String value,
- String path,
- String domain,
- String comment,
- int maxAge,
- boolean isSecure,
- boolean isHttpOnly)
- {
- StringBuffer buf = new StringBuffer();
- // Servlet implementation checks name
- buf.append( name );
- buf.append("=");
- // Servlet implementation does not check anything else
-
- version = maybeQuote2(version, buf, value,true);
-
- // Add version 1 specific information
- if (version == 1) {
- // Version=1 ... required
- buf.append ("; Version=1");
-
- // Comment=comment
- if ( comment!=null ) {
- buf.append ("; Comment=");
- maybeQuote2(version, buf, comment);
- }
- }
-
- // Add domain information, if present
- if (domain!=null) {
- buf.append("; Domain=");
- maybeQuote2(version, buf, domain);
- }
-
- // Max-Age=secs ... or use old "Expires" format
- // TODO RFC2965 Discard
- if (maxAge >= 0) {
- if (version > 0) {
- buf.append ("; Max-Age=");
- buf.append (maxAge);
- }
- // IE6, IE7 and possibly other browsers don't understand Max-Age.
- // They do understand Expires, even with V1 cookies!
- if (version == 0 || ALWAYS_ADD_EXPIRES) {
- // Wdy, DD-Mon-YY HH:MM:SS GMT ( Expires Netscape format )
- buf.append ("; Expires=");
- // To expire immediately we need to set the time in past
- if (maxAge == 0)
- buf.append( ancientDate );
- else
- OLD_COOKIE_FORMAT.get().format(
- new Date(System.currentTimeMillis() +
- maxAge*1000L),
- buf, new FieldPosition(0));
- }
- }
-
- // Path=path
- if (path!=null) {
- buf.append ("; Path=");
- if (version==0) {
- maybeQuote2(version, buf, path);
- } else {
- maybeQuote2(version, buf, path, ServerCookie.tspecials2NoSlash, false);
- }
- }
-
- // Secure
- if (isSecure) {
- buf.append ("; Secure");
- }
-
- // HttpOnly
- if (isHttpOnly) {
- buf.append("; HttpOnly");
- }
- headerBuf.append(buf);
- }
-
- public static boolean alreadyQuoted (String value) {
- if (value==null || value.length()==0) return false;
- return (value.charAt(0)=='\"' && value.charAt(value.length()-1)=='\"');
- }
-
- /**
- * Quotes values using rules that vary depending on Cookie version.
- * @param version
- * @param buf
- * @param value
- */
- public static int maybeQuote2 (int version, StringBuffer buf, String value) {
- return maybeQuote2(version,buf,value,false);
- }
-
- public static int maybeQuote2 (int version, StringBuffer buf, String value, boolean allowVersionSwitch) {
- return maybeQuote2(version,buf,value,null,allowVersionSwitch);
- }
-
- public static int maybeQuote2 (int version, StringBuffer buf, String value, String literals, boolean allowVersionSwitch) {
- if (value==null || value.length()==0) {
- buf.append("\"\"");
- }else if (containsCTL(value,version))
- throw new IllegalArgumentException("Control character in cookie value, consider BASE64 encoding your value");
- else if (alreadyQuoted(value)) {
- buf.append('"');
- buf.append(escapeDoubleQuotes(value,1,value.length()-1));
- buf.append('"');
- } else if (allowVersionSwitch && (!STRICT_SERVLET_COMPLIANCE) && version==0 && !isToken2(value, literals)) {
- buf.append('"');
- buf.append(escapeDoubleQuotes(value,0,value.length()));
- buf.append('"');
- version = 1;
- } else if (version==0 && !isToken(value,literals)) {
- buf.append('"');
- buf.append(escapeDoubleQuotes(value,0,value.length()));
- buf.append('"');
- } else if (version==1 && !isToken2(value,literals)) {
- buf.append('"');
- buf.append(escapeDoubleQuotes(value,0,value.length()));
- buf.append('"');
- }else {
- buf.append(value);
- }
- return version;
- }
-
-
- /**
- * Escapes any double quotes in the given string.
- *
- * @param s the input string
- * @param beginIndex start index inclusive
- * @param endIndex exclusive
- * @return The (possibly) escaped string
- */
- private static String escapeDoubleQuotes(String s, int beginIndex, int endIndex) {
-
- if (s == null || s.length() == 0 || s.indexOf('"') == -1) {
- return s;
- }
-
- StringBuffer b = new StringBuffer();
- for (int i = beginIndex; i < endIndex; i++) {
- char c = s.charAt(i);
- if (c == '\\' ) {
- b.append(c);
- //ignore the character after an escape, just append it
- if (++i>=endIndex) throw new IllegalArgumentException("Invalid escape character in cookie value.");
- b.append(s.charAt(i));
- } else if (c == '"')
- b.append('\\').append('"');
- else
- b.append(c);
- }
-
- return b.toString();
- }
-
- /**
- * Unescapes any double quotes in the given cookie value.
- *
- * @param bc The cookie value to modify
- */
- public static void unescapeDoubleQuotes(BBuffer bc) {
-
- if (bc == null || bc.getLength() == 0 || bc.indexOf('"', 0) == -1) {
- return;
- }
-
- int src = bc.getStart();
- int end = bc.getEnd();
- int dest = src;
- byte[] buffer = bc.array();
-
- while (src < end) {
- if (buffer[src] == '\\' && src < end && buffer[src+1] == '"') {
- src++;
- }
- buffer[dest] = buffer[src];
- dest ++;
- src ++;
- }
- bc.setEnd(dest);
- }
-
- /*
- List of Separator Characters (see isSeparator())
- Excluding the '/' char violates the RFC, but
- it looks like a lot of people put '/'
- in unquoted values: '/': ; //47
- '\t':9 ' ':32 '\"':34 '\'':39 '(':40 ')':41 ',':44 ':':58 ';':59 '<':60
- '=':61 '>':62 '?':63 '@':64 '[':91 '\\':92 ']':93 '{':123 '}':125
- */
- public static final char SEPARATORS[] = { '\t', ' ', '\"', '\'', '(', ')', ',',
- ':', ';', '<', '=', '>', '?', '@', '[', '\\', ']', '{', '}' };
-
- protected static final boolean separators[] = new boolean[128];
- static {
- for (int i = 0; i < 128; i++) {
- separators[i] = false;
- }
- for (int i = 0; i < SEPARATORS.length; i++) {
- separators[SEPARATORS[i]] = true;
- }
- }
-
- /** Add all Cookie found in the headers of a request.
- */
- public static void processCookies(List<ServerCookie> cookies,
- List<ServerCookie> cookiesCache,
- HttpMessage.HttpMessageBytes msgBytes ) {
-
- // process each "cookie" header
- for (int i = 0; i < msgBytes.headerCount; i++) {
- if (msgBytes.getHeaderName(i).equalsIgnoreCase("Cookie")) {
- BBuffer bc = msgBytes.getHeaderValue(i);
- if (bc.remaining() == 0) {
- continue;
- }
- processCookieHeader(cookies, cookiesCache,
- bc.array(),
- bc.getOffset(),
- bc.getLength());
-
- }
-
- }
- }
-
- /**
- * Returns true if the byte is a separator character as
- * defined in RFC2619. Since this is called often, this
- * function should be organized with the most probable
- * outcomes first.
- * JVK
- */
- private static final boolean isSeparator(final byte c) {
- if (c > 0 && c < 126)
- return separators[c];
- else
- return false;
- }
-
- /**
- * Returns true if the byte is a whitespace character as
- * defined in RFC2619
- * JVK
- */
- private static final boolean isWhiteSpace(final byte c) {
- // This switch statement is slightly slower
- // for my vm than the if statement.
- // Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_07-164)
- /*
- switch (c) {
- case ' ':;
- case '\t':;
- case '\n':;
- case '\r':;
- case '\f':;
- return true;
- default:;
- return false;
- }
- */
- if (c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f')
- return true;
- else
- return false;
- }
-
- /**
- * Parses a cookie header after the initial "Cookie:"
- * [WS][$]token[WS]=[WS](token|QV)[;|,]
- * RFC 2965
- * JVK
- */
- public static final void processCookieHeader(
- List<ServerCookie> cookies,
- List<ServerCookie> cookiesCache,
- byte bytes[], int off, int len){
- if( len<=0 || bytes==null ) return;
- int end=off+len;
- int pos=off;
- int nameStart=0;
- int nameEnd=0;
- int valueStart=0;
- int valueEnd=0;
- int version = 0;
- ServerCookie sc=null;
- boolean isSpecial;
- boolean isQuoted;
-
- while (pos < end) {
- isSpecial = false;
- isQuoted = false;
-
- // Skip whitespace and non-token characters (separators)
- while (pos < end &&
- (isSeparator(bytes[pos]) || isWhiteSpace(bytes[pos])))
- {pos++; }
-
- if (pos >= end)
- return;
-
- // Detect Special cookies
- if (bytes[pos] == '$') {
- isSpecial = true;
- pos++;
- }
-
- // Get the cookie name. This must be a token
- valueEnd = valueStart = nameStart = pos;
- pos = nameEnd = getTokenEndPosition(bytes,pos,end);
-
- // Skip whitespace
- while (pos < end && isWhiteSpace(bytes[pos])) {pos++; }
-
-
- // Check for an '=' -- This could also be a name-only
- // cookie at the end of the cookie header, so if we
- // are past the end of the header, but we have a name
- // skip to the name-only part.
- if (pos < end && bytes[pos] == '=') {
-
- // Skip whitespace
- do {
- pos++;
- } while (pos < end && isWhiteSpace(bytes[pos]));
-
- if (pos >= end)
- return;
-
- // Determine what type of value this is, quoted value,
- // token, name-only with an '=', or other (bad)
- switch (bytes[pos]) {
- case '"': // Quoted Value
- isQuoted = true;
- valueStart=pos + 1; // strip "
- // getQuotedValue returns the position before
- // at the last qoute. This must be dealt with
- // when the bytes are copied into the cookie
- valueEnd=getQuotedValueEndPosition(bytes,
- valueStart, end);
- // We need pos to advance
- pos = valueEnd;
- // Handles cases where the quoted value is
- // unterminated and at the end of the header,
- // e.g. [myname="value]
- if (pos >= end)
- return;
- break;
- case ';':
- case ',':
- // Name-only cookie with an '=' after the name token
- // This may not be RFC compliant
- valueStart = valueEnd = -1;
- // The position is OK (On a delimiter)
- break;
- default:
- if (!isSeparator(bytes[pos])) {
- // Token
- valueStart=pos;
- // getToken returns the position at the delimeter
- // or other non-token character
- valueEnd=getTokenEndPosition(bytes, valueStart, end);
- // We need pos to advance
- pos = valueEnd;
- } else {
- // INVALID COOKIE, advance to next delimiter
- // The starting character of the cookie value was
- // not valid.
- //log("Invalid cookie. Value not a token or quoted value");
- while (pos < end && bytes[pos] != ';' &&
- bytes[pos] != ',')
- {pos++; }
- pos++;
- // Make sure no special avpairs can be attributed to
- // the previous cookie by setting the current cookie
- // to null
- sc = null;
- continue;
- }
- }
- } else {
- // Name only cookie
- valueStart = valueEnd = -1;
- pos = nameEnd;
-
- }
-
- // We should have an avpair or name-only cookie at this
- // point. Perform some basic checks to make sure we are
- // in a good state.
-
- // Skip whitespace
- while (pos < end && isWhiteSpace(bytes[pos])) {pos++; }
-
-
- // Make sure that after the cookie we have a separator. This
- // is only important if this is not the last cookie pair
- while (pos < end && bytes[pos] != ';' && bytes[pos] != ',') {
- pos++;
- }
-
- pos++;
-
- /*
- if (nameEnd <= nameStart || valueEnd < valueStart ) {
- // Something is wrong, but this may be a case
- // of having two ';' characters in a row.
- // log("Cookie name/value does not conform to RFC 2965");
- // Advance to next delimiter (ignoring everything else)
- while (pos < end && bytes[pos] != ';' && bytes[pos] != ',')
- { pos++; };
- pos++;
- // Make sure no special cookies can be attributed to
- // the previous cookie by setting the current cookie
- // to null
- sc = null;
- continue;
- }
- */
-
- // All checks passed. Add the cookie, start with the
- // special avpairs first
- if (isSpecial) {
- isSpecial = false;
- // $Version must be the first avpair in the cookie header
- // (sc must be null)
- if (equals( "Version", bytes, nameStart, nameEnd) &&
- sc == null) {
- // Set version
- if( bytes[valueStart] =='1' && valueEnd == (valueStart+1)) {
- version=1;
- } else {
- // unknown version (Versioning is not very strict)
- }
- continue;
- }
-
- // We need an active cookie for Path/Port/etc.
- if (sc == null) {
- continue;
- }
-
- // Domain is more common, so it goes first
- if (equals( "Domain", bytes, nameStart, nameEnd)) {
- sc.getDomain().setBytes( bytes,
- valueStart,
- valueEnd-valueStart);
- continue;
- }
-
- if (equals( "Path", bytes, nameStart, nameEnd)) {
- sc.getPath().setBytes( bytes,
- valueStart,
- valueEnd-valueStart);
- continue;
- }
-
-
- if (equals( "Port", bytes, nameStart, nameEnd)) {
- // sc.getPort is not currently implemented.
- // sc.getPort().setBytes( bytes,
- // valueStart,
- // valueEnd-valueStart );
- continue;
- }
-
- // Unknown cookie, complain
- //log("Unknown Special Cookie");
-
- } else { // Normal Cookie
- // use a previous value from cache, if any (to avoid GC - tomcat
- // legacy )
- if (cookiesCache.size() > cookies.size()) {
- sc = cookiesCache.get(cookies.size());
- cookies.add(sc);
- } else {
- sc = new ServerCookie();
- cookiesCache.add(sc);
- cookies.add(sc);
- }
- sc.setVersion( version );
- sc.getName().append( bytes, nameStart,
- nameEnd-nameStart);
-
- if (valueStart != -1) { // Normal AVPair
- sc.getValue().append( bytes, valueStart,
- valueEnd-valueStart);
- if (isQuoted) {
- // We know this is a byte value so this is safe
- ServerCookie.unescapeDoubleQuotes(
- sc.getValue());
- }
- } else {
- // Name Only
- sc.getValue().recycle();
- }
- sc.nameC.recycle();
- sc.nameC.append(sc.getName());
- continue;
- }
- }
- }
-
- /**
- * Given the starting position of a token, this gets the end of the
- * token, with no separator characters in between.
- * JVK
- */
- private static final int getTokenEndPosition(byte bytes[], int off, int end){
- int pos = off;
- while (pos < end && !isSeparator(bytes[pos])) {pos++; }
-
- if (pos > end)
- return end;
- return pos;
- }
-
- /**
- * Given a starting position after an initial quote chracter, this gets
- * the position of the end quote. This escapes anything after a '\' char
- * JVK RFC 2616
- */
- private static final int getQuotedValueEndPosition(byte bytes[], int off, int end){
- int pos = off;
- while (pos < end) {
- if (bytes[pos] == '"') {
- return pos;
- } else if (bytes[pos] == '\\' && pos < (end - 1)) {
- pos+=2;
- } else {
- pos++;
- }
- }
- // Error, we have reached the end of the header w/o a end quote
- return end;
- }
-
-
- public static boolean equals( String s, byte b[], int start, int end) {
- int blen = end-start;
- if (b == null || blen != s.length()) {
- return false;
- }
- int boff = start;
- for (int i = 0; i < blen; i++) {
- if (b[boff++] != s.charAt(i)) {
- return false;
- }
- }
- return true;
- }
-
-}
-
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/lite/http/SpdyConnection.java b/modules/tomcat-lite/java/org/apache/tomcat/lite/http/SpdyConnection.java
deleted file mode 100644
index af4e200..0000000
--- a/modules/tomcat-lite/java/org/apache/tomcat/lite/http/SpdyConnection.java
+++ /dev/null
@@ -1,820 +0,0 @@
-/*
- */
-package org.apache.tomcat.lite.http;
-
-import java.io.IOException;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import org.apache.tomcat.lite.http.HttpConnectionPool.RemoteServer;
-import org.apache.tomcat.lite.http.HttpMessage.HttpMessageBytes;
-import org.apache.tomcat.lite.io.BBucket;
-import org.apache.tomcat.lite.io.BBuffer;
-import org.apache.tomcat.lite.io.CBuffer;
-import org.apache.tomcat.lite.io.DumpChannel;
-import org.apache.tomcat.lite.io.IOBuffer;
-import org.apache.tomcat.lite.io.IOChannel;
-import org.apache.tomcat.lite.io.IOConnector;
-
-/*
- * TODO: expectations ?
- * Fix docs - order matters
- * Crashes in chrome
- *
- * Test with unit tests or:
- * google-chrome --use-flip=no-ssl
- * --user-data-dir=/home/$USER/.config/google-chrome/Test
- * http://localhost:8802/hello
- */
-
-public class SpdyConnection extends HttpConnector.HttpConnection
- implements IOConnector.ConnectedCallback {
-
-
- /** Use compression for headers. Will magically turn to false
- * if the first request doesn't have x8xx ( i.e. compress header )
- */
- boolean headerCompression = true;
- boolean firstFrame = true;
-
- public static long DICT_ID = 3751956914L;
- private static String SPDY_DICT_S =
- "optionsgetheadpostputdeletetraceacceptaccept-charsetaccept-encodingaccept-" +
- "languageauthorizationexpectfromhostif-modified-sinceif-matchif-none-matchi" +
- "f-rangeif-unmodifiedsincemax-forwardsproxy-authorizationrangerefererteuser" +
- "-agent10010120020120220320420520630030130230330430530630740040140240340440" +
- "5406407408409410411412413414415416417500501502503504505accept-rangesageeta" +
- "glocationproxy-authenticatepublicretry-afterservervarywarningwww-authentic" +
- "ateallowcontent-basecontent-encodingcache-controlconnectiondatetrailertran" +
- "sfer-encodingupgradeviawarningcontent-languagecontent-lengthcontent-locati" +
- "oncontent-md5content-rangecontent-typeetagexpireslast-modifiedset-cookieMo" +
- "ndayTuesdayWednesdayThursdayFridaySaturdaySundayJanFebMarAprMayJunJulAugSe" +
- "pOctNovDecchunkedtext/htmlimage/pngimage/jpgimage/gifapplication/xmlapplic" +
- "ation/xhtmltext/plainpublicmax-agecharset=iso-8859-1utf-8gzipdeflateHTTP/1" +
- ".1statusversionurl ";
- public static byte[] SPDY_DICT = SPDY_DICT_S.getBytes();
- // C code uses this - not in the spec
- static {
- SPDY_DICT[SPDY_DICT.length - 1] = (byte) 0;
- }
-
-
- protected static Logger log = Logger.getLogger("SpdyConnection");
-
- /**
- * @param spdyConnector
- * @param remoteServer
- */
- SpdyConnection(HttpConnector spdyConnector, RemoteServer remoteServer) {
- this.httpConnector = spdyConnector;
- this.remoteHost = remoteServer;
- this.target = remoteServer.target;
- }
-
- AtomicInteger streamErrors = new AtomicInteger();
-
- AtomicInteger lastInStream = new AtomicInteger();
- AtomicInteger lastOutStream = new AtomicInteger();
-
- // TODO: use int map
- Map<Integer, HttpChannel> channels = new HashMap();
-
- SpdyConnection.Frame currentInFrame = null;
-
- SpdyConnection.Frame lastFrame = null; // for debug
-
- BBuffer outFrameBuffer = BBuffer.allocate();
- BBuffer inFrameBuffer = BBuffer.allocate();
-
- BBuffer headW = BBuffer.wrapper();
-
- CompressFilter headCompressIn = new CompressFilter()
- .setDictionary(SPDY_DICT, DICT_ID);
-
- CompressFilter headCompressOut = new CompressFilter()
- .setDictionary(SPDY_DICT, DICT_ID);
-
- IOBuffer headerCompressBuffer = new IOBuffer();
- IOBuffer headerDeCompressBuffer = new IOBuffer();
-
- AtomicInteger inFrames = new AtomicInteger();
- AtomicInteger inDataFrames = new AtomicInteger();
- AtomicInteger inSyncStreamFrames = new AtomicInteger();
- AtomicInteger inBytes = new AtomicInteger();
-
- AtomicInteger outFrames = new AtomicInteger();
- AtomicInteger outDataFrames = new AtomicInteger();
- AtomicInteger outBytes = new AtomicInteger();
-
-
- volatile boolean connecting = false;
- volatile boolean connected = false;
-
- // TODO: detect if it's spdy or http based on bit 8
-
- @Override
- public void withExtraBuffer(BBuffer received) {
- inFrameBuffer = received;
- }
-
- @Override
- public void dataReceived(IOBuffer iob) throws IOException {
- // Only one thread doing receive at a time.
- synchronized (inFrameBuffer) {
- while (true) {
- int avail = iob.available();
- if (avail == 0) {
- return;
- }
- if (currentInFrame == null) {
- if (inFrameBuffer.remaining() + avail < 8) {
- return;
- }
- if (inFrameBuffer.remaining() < 8) {
- int headRest = 8 - inFrameBuffer.remaining();
- int rd = iob.read(inFrameBuffer, headRest);
- }
- currentInFrame = new SpdyConnection.Frame(); // TODO: reuse
- currentInFrame.parse(this, inFrameBuffer);
- }
- if (iob.available() < currentInFrame.length) {
- return;
- }
- // We have a full frame. Process it.
- onFrame(iob);
-
- // TODO: extra checks, make sure the frame is correct and
- // it consumed all data.
- currentInFrame = null;
- }
- }
- }
-
-
- /**
- * Frame received. Must consume all data for the frame.
- *
- * @param iob
- * @throws IOException
- */
- protected void onFrame(IOBuffer iob) throws IOException {
- // TODO: make sure we have enough data.
- lastFrame = currentInFrame;
- inFrames.incrementAndGet();
- inBytes.addAndGet(currentInFrame.length + 8);
-
- if (currentInFrame.c) {
- if (currentInFrame.type == SpdyConnection.Frame.TYPE_HELO) {
- // receivedHello = currentInFrame;
- } else if (currentInFrame.type == SpdyConnection.Frame.TYPE_SYN_STREAM) {
- inSyncStreamFrames.incrementAndGet();
- HttpChannel ch = new HttpChannel(); // TODO: reuse
- ch.channelId = SpdyConnection.readInt(iob);
- ch.setConnection(this);
- ch.httpConnector = this.httpConnector;
- if (serverMode) {
- ch.serverMode(true);
- }
- if (this.httpConnector.defaultService != null) {
- ch.setHttpService(this.httpConnector.defaultService);
- }
-
- synchronized (channels) {
- channels.put(ch.channelId, ch);
- }
-
- try {
- // pri and unused
- SpdyConnection.readShort(iob);
-
- HttpMessageBytes reqBytes = ch.getRequest().getMsgBytes();
-
- processHeaders(iob, ch, reqBytes);
- } catch (Throwable t) {
- log.log(Level.SEVERE, "Error parsing head", t);
- abort("Error reading headers " + t);
- return;
- }
- ch.getRequest().processReceivedHeaders();
-
- ch.handleHeadersReceived(ch.getRequest());
-
- if ((currentInFrame.flags & SpdyConnection.Frame.FLAG_HALF_CLOSE) != 0) {
- ch.getIn().close();
- ch.handleEndReceive();
- }
- } else if (currentInFrame.type == SpdyConnection.Frame.TYPE_SYN_REPLY) {
- int chId = SpdyConnection.readInt(iob);
- HttpChannel ch;
- synchronized (channels) {
- ch = channels.get(chId);
- if (ch == null) {
- abort("Channel not found");
- }
- }
- try {
- SpdyConnection.readShort(iob);
-
- HttpMessageBytes resBytes = ch.getResponse().getMsgBytes();
-
- BBuffer head = processHeaders(iob, ch, resBytes);
- } catch (Throwable t) {
- log.log(Level.SEVERE, "Error parsing head", t);
- abort("Error reading headers " + t);
- return;
- }
- ch.getResponse().processReceivedHeaders();
-
- ch.handleHeadersReceived(ch.getResponse());
-
- if ((currentInFrame.flags & SpdyConnection.Frame.FLAG_HALF_CLOSE) != 0) {
- ch.getIn().close();
- ch.handleEndReceive();
- }
- } else {
- log.warning("Unknown frame type " + currentInFrame.type);
- iob.advance(currentInFrame.length);
- }
- } else {
- inDataFrames.incrementAndGet();
- // data frame - part of an existing stream
- HttpChannel ch;
- synchronized (channels) {
- ch = channels.get(currentInFrame.streamId);
- }
- if (ch == null) {
- log.warning("Unknown stream ");
- net.close();
- net.startSending();
- return;
- }
- int len = currentInFrame.length;
- while (len > 0) {
- BBucket bb = iob.peekFirst();
- if (bb == null) {
- // we should have all data
- abort("Unexpected short read");
- return;
- }
- if (len > bb.remaining()) {
- ch.getIn().append(bb);
- len -= bb.remaining();
- bb.position(bb.limit());
- } else {
- ch.getIn().append(bb, len);
- bb.position(bb.position() + len);
- len = 0;
- }
- }
- ch.sendHandleReceivedCallback();
-
- if ((currentInFrame.flags & SpdyConnection.Frame.FLAG_HALF_CLOSE) != 0) {
- ch.handleEndReceive();
- }
- }
- firstFrame = false;
- }
-
- /**
- * On frame error.
- */
- private void abort(String msg) throws IOException {
- streamErrors.incrementAndGet();
- synchronized(channels) {
- for (HttpChannel ch : channels.values()) {
- ch.abort(msg);
- }
- }
- close();
- }
-
- private BBuffer processHeaders(IOBuffer iob, HttpChannel ch,
- HttpMessageBytes reqBytes) throws IOException {
- int nvCount = 0;
- if (firstFrame) {
- int res = iob.peek() & 0xFF;
- if ((res & 0x0F) != 8) {
- headerCompression = false;
- }
- }
- headRecvBuf.recycle();
- if (headerCompression) {
- // 0x800 headers seems a bit too much - assume compressed.
- // I wish this was a flag...
- headerDeCompressBuffer.recycle();
- // stream id ( 4 ) + unused ( 2 )
- // nvCount is compressed in impl - spec is different
- headCompressIn.decompress(iob, headerDeCompressBuffer,
- currentInFrame.length - 6);
- headerDeCompressBuffer.copyAll(headRecvBuf);
- headerDeCompressBuffer.recycle();
- nvCount = readShort(headRecvBuf);
- } else {
- nvCount = readShort(iob);
- // 8 = stream Id (4) + pri/unused (2) + nvCount (2)
- // we know we have enough data
- int rd = iob.read(headRecvBuf, currentInFrame.length - 8);
- if (rd != currentInFrame.length - 8) {
- abort("Unexpected incomplete read");
- }
- }
- // Wrapper - so we don't change position in head
- headRecvBuf.wrapTo(headW);
-
- BBuffer nameBuf = BBuffer.wrapper();
- BBuffer valBuf = BBuffer.wrapper();
-
- for (int i = 0; i < nvCount; i++) {
-
- int nameLen = SpdyConnection.readShort(headW);
- if (nameLen > headW.remaining()) {
- abort("Name too long");
- }
-
- nameBuf.setBytes(headW.array(), headW.position(),
- nameLen);
- headW.advance(nameLen);
-
- int valueLen = SpdyConnection.readShort(headW);
- valBuf.setBytes(headW.array(), headW.position(),
- valueLen);
- headW.advance(valueLen);
-
- // TODO: no need to send version, method if default
-
- if (nameBuf.equals("method")) {
- valBuf.wrapTo(reqBytes.method());
- } else if (nameBuf.equals("version")) {
- valBuf.wrapTo(reqBytes.protocol());
- } else if (nameBuf.equals("url")) {
- valBuf.wrapTo(reqBytes.url());
- // TODO: spdy uses full URL, we may want to trim
- // also no host header
- } else {
- int idx = reqBytes.addHeader();
- nameBuf.wrapTo(reqBytes.getHeaderName(idx));
- valBuf.wrapTo(reqBytes.getHeaderValue(idx));
- }
-
- // TODO: repeated values are separated by a 0
- }
- return headW;
- }
-
- @Override
- protected synchronized void sendRequest(HttpChannel http) throws IOException {
- if (serverMode) {
- throw new IOException("Only in client mode");
- }
- if (!checkConnection(http)) {
- return;
- }
- MultiMap mimeHeaders = http.getRequest().getMimeHeaders();
-
- BBuffer headBuf = BBuffer.allocate();
- SpdyConnection.appendShort(headBuf, mimeHeaders.size() + 3);
- serializeMime(mimeHeaders, headBuf);
-
- // TODO: url - with host prefix , method
- // optimize...
- SpdyConnection.appendAsciiHead(headBuf, "version");
- SpdyConnection.appendAsciiHead(headBuf, "HTTP/1.1");
-
- SpdyConnection.appendAsciiHead(headBuf, "method");
- SpdyConnection.appendAsciiHead(headBuf, http.getRequest().getMethod());
-
- SpdyConnection.appendAsciiHead(headBuf, "url");
- // TODO: url
- SpdyConnection.appendAsciiHead(headBuf, http.getRequest().requestURL());
-
- if (headerCompression && httpConnector.compression) {
- headerCompressBuffer.recycle();
- headCompressOut.compress(headBuf, headerCompressBuffer, false);
- headBuf.recycle();
- headerCompressBuffer.copyAll(headBuf);
- }
-
- // Frame head - 8
- BBuffer out = BBuffer.allocate();
- // Syn-reply
- out.putByte(0x80);
- out.putByte(0x01);
- out.putByte(0x00);
- out.putByte(0x01);
-
- CBuffer method = http.getRequest().method();
- if (method.equals("GET") || method.equals("HEAD")) {
- http.getOut().close();
- }
-
- if (http.getOut().isAppendClosed()) {
- out.putByte(0x01); // closed
- } else {
- out.putByte(0x00);
- }
-
- // Length, channel id (4) + unused (2) - headBuf has header count
- // and headers
- SpdyConnection.append24(out, headBuf.remaining() + 6);
-
- if (serverMode) {
- http.channelId = 2 * lastOutStream.incrementAndGet();
- } else {
- http.channelId = 2 * lastOutStream.incrementAndGet() + 1;
- }
- SpdyConnection.appendInt(out, http.channelId);
-
- http.setConnection(this);
-
- synchronized (channels) {
- channels.put(http.channelId, http);
- }
-
- out.putByte(0x00); // no priority
- out.putByte(0x00);
-
- sendFrame(out, headBuf);
-
- if (http.outMessage.state == HttpMessage.State.HEAD) {
- http.outMessage.state = HttpMessage.State.BODY_DATA;
- }
- if (http.getOut().isAppendClosed()) {
- http.handleEndSent();
- }
-
- // Any existing data
- //sendData(http);
- }
-
-
- public synchronized Collection<HttpChannel> getActives() {
- synchronized(channels) {
- return channels.values();
- }
- }
-
- @Override
- protected synchronized void sendResponseHeaders(HttpChannel http) throws IOException {
- if (!serverMode) {
- throw new IOException("Only in server mode");
- }
-
- if (http.getResponse().isCommitted()) {
- return;
- }
- http.getResponse().setCommitted(true);
-
- MultiMap mimeHeaders = http.getResponse().getMimeHeaders();
-
- BBuffer headBuf = BBuffer.allocate();
-
-
- //mimeHeaders.remove("content-length");
- BBuffer headers = headBuf;
- if (headerCompression) {
- headers = BBuffer.allocate();
- }
-
- //SpdyConnection.appendInt(headers, http.channelId);
- //headers.putByte(0);
- //headers.putByte(0);
- SpdyConnection.appendShort(headers, mimeHeaders.size() + 2);
-
- // chrome will crash if we don't send the header
- serializeMime(mimeHeaders, headers);
-
- // Must be at the end
- SpdyConnection.appendAsciiHead(headers, "status");
- SpdyConnection.appendAsciiHead(headers,
- Integer.toString(http.getResponse().getStatus()));
-
- SpdyConnection.appendAsciiHead(headers, "version");
- SpdyConnection.appendAsciiHead(headers, "HTTP/1.1");
-
- if (headerCompression) {
- headerCompressBuffer.recycle();
- headCompressOut.compress(headers, headerCompressBuffer, false);
- headerCompressBuffer.copyAll(headBuf);
- headerCompressBuffer.recycle();
- }
-
- BBuffer frameHead = BBuffer.allocate();
- // Syn-reply
- frameHead.putByte(0x80); // Control
- frameHead.putByte(0x01); // version
- frameHead.putByte(0x00); // 00 02 - SYN_REPLY
- frameHead.putByte(0x02);
-
- // It seems piggibacking data is not allowed
- frameHead.putByte(0x00);
-
- int len = headBuf.remaining() + 6;
- SpdyConnection.append24(frameHead, len);
-
-// // Stream-Id, unused
- SpdyConnection.appendInt(frameHead, http.channelId);
- frameHead.putByte(0);
- frameHead.putByte(0);
-
- sendFrame(frameHead, headBuf);
- }
-
-
- public void startSending(HttpChannel http) throws IOException {
- http.send(); // if needed
-
- if (net != null) {
- sendData(http);
- net.startSending();
- }
- }
-
- private void sendData(HttpChannel http) throws IOException {
- int avail = http.getOut().available();
- boolean closed = http.getOut().isAppendClosed();
- if (avail > 0 || closed) {
- sendDataFrame(http.getOut(), avail,
- http.channelId, closed);
- if (avail > 0) {
- getOut().advance(avail);
- }
- }
- if (closed) {
- http.handleEndSent();
- }
- }
-
- private BBuffer serializeMime(MultiMap mimeHeaders, BBuffer headBuf)
- throws IOException {
-
- // TODO: duplicated headers not allowed
- for (int i = 0; i < mimeHeaders.size(); i++) {
- CBuffer name = mimeHeaders.getName(i);
- CBuffer value = mimeHeaders.getValue(i);
- if (name.length() == 0 || value.length() == 0) {
- continue;
- }
- SpdyConnection.appendShort(headBuf, name.length());
- name.toAscii(headBuf);
- SpdyConnection.appendShort(headBuf, value.length());
- value.toAscii(headBuf);
- }
- return headBuf;
- }
-
-
- private synchronized void sendFrame(BBuffer out, BBuffer headBuf)
- throws IOException {
- if (net == null) {
- return; // unit test
- }
- outBytes.addAndGet(out.remaining());
- net.getOut().append(out);
- if (headBuf != null) {
- net.getOut().append(headBuf);
- outBytes.addAndGet(headBuf.remaining());
- }
- net.startSending();
- outFrames.incrementAndGet();
- }
-
- public synchronized void sendDataFrame(IOBuffer out2, int avail,
- int channelId, boolean last) throws IOException {
- if (net == null) {
- return; // unit test
- }
- outFrameBuffer.recycle();
- SpdyConnection.appendInt(outFrameBuffer, channelId); // first bit 0 ?
- if (last) {
- outFrameBuffer.putByte(0x01); // closed
- } else {
- outFrameBuffer.putByte(0x00);
- }
-
- // TODO: chunk if too much data ( at least at 24 bits)
- SpdyConnection.append24(outFrameBuffer, avail);
-
- outBytes.addAndGet(outFrameBuffer.remaining() + avail);
- net.getOut().append(outFrameBuffer);
-
- if (avail > 0) {
- net.getOut().append(out2, avail);
- }
- net.startSending();
- outDataFrames.incrementAndGet();
- }
-
- static void appendInt(BBuffer headBuf, int length) throws IOException {
- headBuf.putByte((length & 0xFF000000) >> 24);
- headBuf.putByte((length & 0xFF0000) >> 16);
- headBuf.putByte((length & 0xFF00) >> 8);
- headBuf.putByte((length & 0xFF));
- }
-
- static void append24(BBuffer headBuf, int length) throws IOException {
- headBuf.putByte((length & 0xFF0000) >> 16);
- headBuf.putByte((length & 0xFF00) >> 8);
- headBuf.putByte((length & 0xFF));
- }
-
- static void appendAsciiHead(BBuffer headBuf, CBuffer s) throws IOException {
- appendShort(headBuf, s.length());
- for (int i = 0; i < s.length(); i++) {
- headBuf.append(s.charAt(i));
- }
- }
-
- static void appendShort(BBuffer headBuf, int length) throws IOException {
- if (length > 0xFFFF) {
- throw new IOException("Too long");
- }
- headBuf.putByte((length & 0xFF00) >> 8);
- headBuf.putByte((length & 0xFF));
- }
-
- static void appendAsciiHead(BBuffer headBuf, String s) throws IOException {
- SpdyConnection.appendShort(headBuf, s.length());
- for (int i = 0; i < s.length(); i++) {
- headBuf.append(s.charAt(i));
- }
- }
-
- static int readShort(BBuffer iob) throws IOException {
- int res = iob.readByte();
- return res << 8 | iob.readByte();
- }
-
- static int readShort(IOBuffer iob) throws IOException {
- int res = iob.read();
- return res << 8 | iob.read();
- }
-
- static int readInt(IOBuffer iob) throws IOException {
- int res = 0;
- for (int i = 0; i < 4; i++) {
- int b0 = iob.read();
- res = res << 8 | b0;
- }
- return res;
- }
-
- public static class Frame {
- int flags;
-
- int length;
-
- boolean c; // for control
-
- int version;
-
- int type;
-
- int streamId; // for data
-
- static int TYPE_HELO = 4;
-
- static int TYPE_SYN_STREAM = 1;
-
- static int TYPE_SYN_REPLY = 2;
-
- static int FLAG_HALF_CLOSE = 1;
-
- public void parse(SpdyConnection spdyConnection,
- BBuffer iob) throws IOException {
- int b0 = iob.read();
- if (b0 < 128) {
- // data frame
- c = false;
- streamId = b0;
- for (int i = 0; i < 3; i++) {
- b0 = iob.read();
- streamId = streamId << 8 | b0;
- }
- } else {
- c = true;
- b0 -= 128;
- version = ((b0 << 8) | iob.read());
- if (version != 1) {
- spdyConnection.abort("Wrong version");
- return;
- }
- b0 = iob.read();
- type = ((b0 << 8) | iob.read());
- }
-
- flags = iob.read();
- for (int i = 0; i < 3; i++) {
- b0 = iob.read();
- length = length << 8 | b0;
- }
-
- iob.recycle();
- }
-
- }
-
- @Override
- protected void endSendReceive(HttpChannel http) throws IOException {
- synchronized (channels) {
- HttpChannel doneHttp = channels.remove(http.channelId);
- if (doneHttp != http) {
- log.severe("Error removing " + doneHttp + " " + http);
- }
- }
- httpConnector.cpool.afterRequest(http, this, true);
- }
-
- /**
- * Framing error, client interrupt, etc.
- */
- public void abort(HttpChannel http, String t) throws IOException {
- // TODO: send interrupt signal
-
- }
-
-
- private boolean checkConnection(HttpChannel http) throws IOException {
- synchronized(this) {
- if (net == null || !isOpen()) {
- connected = false;
- }
-
- if (!connected) {
- if (!connecting) {
- // TODO: secure set at start ?
- connecting = true;
- httpConnector.cpool.httpConnect(http,
- target.toString(),
- http.getRequest().isSecure(), this);
- }
-
- synchronized (remoteHost) {
- remoteHost.pending.add(http);
- httpConnector.cpool.queued.incrementAndGet();
- }
- return false;
- }
- }
-
- return true;
- }
-
- @Override
- public void handleConnected(IOChannel net) throws IOException {
- HttpChannel httpCh = null;
- if (!net.isOpen()) {
- while (true) {
- synchronized (remoteHost) {
- if (remoteHost.pending.size() == 0) {
- return;
- }
- httpCh = remoteHost.pending.remove();
- }
- httpCh.abort("Can't connect");
- }
- }
-
- synchronized (remoteHost) {
- httpCh = remoteHost.pending.peek();
- }
- if (httpCh != null) {
- secure = httpCh.getRequest().isSecure();
- if (secure) {
- if (httpConnector.debugHttp) {
- net = DumpChannel.wrap("SPDY-SSL", net);
- }
- String[] hostPort = httpCh.getTarget().split(":");
-
- IOChannel ch1 = httpConnector.sslProvider.channel(net,
- hostPort[0], Integer.parseInt(hostPort[1]));
- //net.setHead(ch1);
- net = ch1;
- }
- }
- if (httpConnector.debugHttp) {
- net = DumpChannel.wrap("SPDY", net);
- }
-
- setSink(net);
-
- synchronized(this) {
- connecting = false;
- connected = true;
- }
-
- while (true) {
- synchronized (remoteHost) {
- if (remoteHost.pending.size() == 0) {
- return;
- }
- httpCh = remoteHost.pending.remove();
- }
- sendRequest(httpCh);
- }
-
- }
-}
\ No newline at end of file
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/lite/http/package.html b/modules/tomcat-lite/java/org/apache/tomcat/lite/http/package.html
deleted file mode 100644
index e69de29..0000000
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/lite/io/BBucket.java b/modules/tomcat-lite/java/org/apache/tomcat/lite/io/BBucket.java
deleted file mode 100644
index 560629b..0000000
--- a/modules/tomcat-lite/java/org/apache/tomcat/lite/io/BBucket.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- */
-package org.apache.tomcat.lite.io;
-
-import java.nio.ByteBuffer;
-
-
-
-/**
- * Holds raw data. Similar interface with a ByteBuffer in 'channel write'
- * or 'read mode'. Data is between position and limit - there is no
- * switching.
- *
- * TODO: FileBucket, DirectBufferBucket, CharBucket, ...
- *
- * @author Costin Manolache
- */
-public interface BBucket {
-
- public void release();
-
- public byte[] array();
- public int position();
- public int remaining();
- public int limit();
-
- public boolean hasRemaining();
-
- public void position(int newStart);
-
- /**
- * Return a byte buffer, with data between position and limit.
- * Changes in the ByteBuffer position will not be reflected
- * in the IOBucket.
- *
- * @return
- */
- public ByteBuffer getByteBuffer();
-
-
-}
\ No newline at end of file
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/lite/io/BBuffer.java b/modules/tomcat-lite/java/org/apache/tomcat/lite/io/BBuffer.java
deleted file mode 100644
index e3c20fa..0000000
--- a/modules/tomcat-lite/java/org/apache/tomcat/lite/io/BBuffer.java
+++ /dev/null
@@ -1,1204 +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.lite.io;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.Serializable;
-import java.nio.ByteBuffer;
-import java.nio.charset.Charset;
-
-/*
- * 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.
- */
-
-// Renamed from ByteChunk to make it easier to write code using both
-
-/**
- * This class is used to represent a chunk of bytes, and utilities to manipulate
- * byte[].
- *
- * The buffer can be modified and used for both input and output.
- *
- * There are 2 modes: The chunk can be associated with a sink - ByteInputChannel
- * or ByteOutputChannel, which will be used when the buffer is empty ( on input
- * ) or filled ( on output ). For output, it can also grow. This operating mode
- * is selected by calling setLimit() or allocate(initial, limit) with limit !=
- * -1.
- *
- * Various search and append method are defined - similar with String and
- * StringBuffer, but operating on bytes.
- *
- * This is important because it allows processing the http headers directly on
- * the received bytes, without converting to chars and Strings until the strings
- * are needed. In addition, the charset is determined later, from headers or
- * user code.
- *
- *
- * @author dac at sun.com
- * @author James Todd [gonzo at sun.com]
- * @author Costin Manolache
- * @author Remy Maucherat
- */
-public class BBuffer implements Cloneable, Serializable,
- BBucket {
-
- /**
- * Default encoding used to convert to strings. It should be UTF8, but:
- * - the servlet API requires 8859_1 as default
- * -
- */
- public static final String DEFAULT_CHARACTER_ENCODING = "ISO-8859-1";
-
- // byte[]
- private byte[] buff;
-
- private int start = 0;
-
- private int end;
-
- private ByteBuffer byteBuffer;
-
- public static final String CRLF = "\r\n";
-
- /* Various constant "strings" */
- public static final byte[] CRLF_BYTES = convertToBytes(BBuffer.CRLF);
-
- /**
- * HT.
- */
- public static final byte HT = (byte) '\t';
-
- /**
- * SP.
- */
- public static final byte SP = (byte) ' ';
-
- /**
- * LF.
- */
- public static final byte LF = (byte) '\n';
-
- /**
- * CR.
- */
- public static final byte CR = (byte) '\r';
-
- //private int useCount;
-
-
- private static final boolean[] isDigit = new boolean[256];
-
- static Charset UTF8;
-
- public static final byte A = (byte) 'A';
-
- public static final byte Z = (byte) 'Z';
-
- public static final byte a = (byte) 'a';
-
- public static final byte LC_OFFSET = A - a;
- private static final byte[] toLower = new byte[256];
- private static final boolean[] isUpper = new boolean[256];
-
- static {
- for (int i = 0; i < 256; i++) {
- toLower[i] = (byte)i;
- }
-
- for (int lc = 'a'; lc <= 'z'; lc++) {
- int uc = lc + 'A' - 'a';
- toLower[uc] = (byte)lc;
- isUpper[uc] = true;
- }
- }
-
- static {
- for (int d = '0'; d <= '9'; d++) {
- isDigit[d] = true;
- }
- UTF8 = Charset.forName("UTF-8");
- }
-
- public static BBuffer allocate() {
- return new BBuffer();
- }
-
- public static BBuffer allocate(int initial) {
- return new BBuffer().makeSpace(initial);
- }
-
-
- public static BBuffer allocate(String msg) {
- BBuffer bc = allocate();
- byte[] data = msg.getBytes();
- bc.append(data, 0, data.length);
- return bc;
- }
-
- public static BBuffer wrapper(String msg) {
- BBuffer bc = new IOBucketWrap();
- byte[] data = msg.getBytes();
- bc.setBytes(data, 0, data.length);
- return bc;
- }
-
- public static BBuffer wrapper() {
- return new IOBucketWrap();
- }
-
- public static BBuffer wrapper(BBuffer bb) {
- BBuffer res = new IOBucketWrap();
- res.setBytes(bb.array(), bb.position(), bb.remaining());
- return res;
- }
-
- public static BBuffer wrapper(byte b[], int off, int len) {
- BBuffer res = new IOBucketWrap();
- res.setBytes(b, off, len);
- return res;
- }
-
- public static BBuffer wrapper(BBucket bb, int start, int len) {
- BBuffer res = new IOBucketWrap();
- res.setBytes(bb.array(), bb.position() + start, len);
- return res;
- }
-
- /**
- * Creates a new, uninitialized ByteChunk object.
- */
- private BBuffer() {
- }
-
- public void append(BBuffer src) {
- append(src.array(), src.getStart(), src.getLength());
- }
-
- /**
- * Add data to the buffer
- */
- public void append(byte src[], int off, int len) {
- // will grow, up to limit
- makeSpace(len);
-
- // assert: makeSpace made enough space
- System.arraycopy(src, off, buff, end, len);
- end += len;
- return;
- }
-
- // -------------------- Adding data to the buffer --------------------
- /**
- * Append a char, by casting it to byte. This IS NOT intended for unicode.
- *
- * @param c
- */
- public void append(char c) {
- put((byte) c);
- }
-
- // -------------------- Removing data from the buffer --------------------
-
- /**
- * Returns the message bytes.
- */
- @Override
- public byte[] array() {
- return buff;
- }
-
- public int capacity() {
- return buff.length;
- }
-
- public boolean equals(BBuffer bb) {
- return equals(bb.array(), bb.getStart(), bb.getLength());
- }
-
- public boolean equals(byte b2[], int off2, int len2) {
- byte b1[] = buff;
- if (b1 == null && b2 == null)
- return true;
-
- int len = end - start;
- if (len2 != len || b1 == null || b2 == null)
- return false;
-
- int off1 = start;
-
- while (len-- > 0) {
- if (b1[off1++] != b2[off2++]) {
- return false;
- }
- }
- return true;
- }
-
-
- public boolean equals(char c2[], int off2, int len2) {
- // XXX works only for enc compatible with ASCII/UTF !!!
- byte b1[] = buff;
- if (c2 == null && b1 == null)
- return true;
-
- if (b1 == null || c2 == null || end - start != len2) {
- return false;
- }
- int off1 = start;
- int len = end - start;
-
- while (len-- > 0) {
- if ((char) b1[off1++] != c2[off2++]) {
- return false;
- }
- }
- return true;
- }
-
- // -------------------- Conversion and getters --------------------
-
- /**
- * Compares the message bytes to the specified String object.
- *
- * @param s
- * the String to compare
- * @return true if the comparison succeeded, false otherwise
- */
- public boolean equals(String s) {
- // XXX ENCODING - this only works if encoding is UTF8-compat
- // ( ok for tomcat, where we compare ascii - header names, etc )!!!
-
- byte[] b = buff;
- int blen = end - start;
- if (b == null || blen != s.length()) {
- return false;
- }
- int boff = start;
- for (int i = 0; i < blen; i++) {
- if (b[boff++] != s.charAt(i)) {
- return false;
- }
- }
- return true;
- }
-
- /**
- * Compares the message bytes to the specified String object.
- *
- * @param s
- * the String to compare
- * @return true if the comparison succeeded, false otherwise
- */
- public boolean equalsIgnoreCase(String s) {
- byte[] b = buff;
- int blen = end - start;
- if (b == null || blen != s.length()) {
- return false;
- }
- int boff = start;
- for (int i = 0; i < blen; i++) {
- if (toLower(b[boff++]) != toLower(s.charAt(i))) {
- return false;
- }
- }
- return true;
- }
-
- public int get(int off) {
- if (start + off >= end) {
- throw new ArrayIndexOutOfBoundsException();
- }
- return buff[start + off] & 0xFF;
- }
-
- /**
- * Return a byte buffer. Changes in the ByteBuffer position will
- * not be reflected in the IOBucket
- * @return
- */
- public ByteBuffer getByteBuffer() {
- if (byteBuffer == null || byteBuffer.array() != buff) {
- byteBuffer = ByteBuffer.wrap(buff, start, end - start);
- } else {
- byteBuffer.position(start);
- byteBuffer.limit(end);
- }
- return byteBuffer;
- }
-
- // --------------------
- public BBuffer getClone() {
- try {
- return (BBuffer) this.clone();
- } catch (Exception ex) {
- return null;
- }
- }
-
- public int getEnd() {
- return end;
- }
-
- public int getInt() {
- return parseInt(buff, start, end - start);
- }
- /**
- * Returns the length of the bytes. XXX need to clean this up
- */
- public int getLength() {
- return end - start;
- }
-
- public long getLong() {
- return parseLong(buff, start, end - start);
- }
-
- public int getOffset() {
- return start;
- }
-
- // -------------------- equals --------------------
-
- /**
- * Returns the start offset of the bytes. For output this is the end of the
- * buffer.
- */
- public int getStart() {
- return start;
- }
-
- public ByteBuffer getWriteByteBuffer(int space) {
- if (space == 0) {
- space = 16;
- }
- makeSpace(space);
- if (byteBuffer == null || byteBuffer.array() != buff) {
- byteBuffer = ByteBuffer.wrap(buff, end, buff.length);
- } else {
- byteBuffer.position(end);
- byteBuffer.limit(buff.length);
- }
- return byteBuffer;
- }
-
- // -------------------- Hash code --------------------
- public int hashCode() {
- return hashBytes(buff, start, end - start);
- }
-
- public boolean hasLFLF() {
- return hasLFLF(this);
- }
-
- public boolean hasRemaining() {
- return start < end;
- }
-
- /**
- * Returns true if the message bytes starts with the specified string.
- *
- * @param s
- * the string
- */
-// public boolean startsWith(String s) {
-// // Works only if enc==UTF
-// byte[] b = buff;
-// int blen = s.length();
-// if (b == null || blen > end - start) {
-// return false;
-// }
-// int boff = start;
-// for (int i = 0; i < blen; i++) {
-// if (b[boff++] != s.charAt(i)) {
-// return false;
-// }
-// }
-// return true;
-// }
-
- /* Returns true if the message bytes start with the specified byte array */
-// public boolean startsWith(byte[] b2) {
-// byte[] b1 = buff;
-// if (b1 == null && b2 == null) {
-// return true;
-// }
-//
-// int len = end - start;
-// if (b1 == null || b2 == null || b2.length > len) {
-// return false;
-// }
-// for (int i = start, j = 0; i < end && j < b2.length;) {
-// if (b1[i++] != b2[j++])
-// return false;
-// }
-// return true;
-// }
-
- /**
- * Returns true if the message bytes starts with the specified string.
- *
- * @param c
- * the character
- * @param starting
- * The start position
- */
- public int indexOf(char c, int starting) {
- int ret = indexOf(buff, start + starting, end, c);
- return (ret >= start) ? ret - start : -1;
- }
-
- /**
- * Returns true if the message bytes starts with the specified string.
- *
- * @param s
- * the string
- * @param pos
- * The position
- */
-// public boolean startsWithIgnoreCase(String s, int pos) {
-// byte[] b = buff;
-// int len = s.length();
-// if (b == null || len + pos > end - start) {
-// return false;
-// }
-// int off = start + pos;
-// for (int i = 0; i < len; i++) {
-// if (Ascii.toLower(b[off++]) != Ascii.toLower(s.charAt(i))) {
-// return false;
-// }
-// }
-// return true;
-// }
- public int indexOf(String src) {
- return indexOf(src, 0, src.length(), 0);
- }
-
- public int indexOf(String src, int srcOff, int srcLen, int myOff) {
- if ("".equals(src)) {
- return myOff;
- }
- char first = src.charAt(srcOff);
-
- // Look for first char
- int srcEnd = srcOff + srcLen;
-
- for (int i = myOff + start; i <= (end - srcLen); i++) {
- if (buff[i] != first)
- continue;
- // found first char, now look for a match
- int myPos = i + 1;
- for (int srcPos = srcOff + 1; srcPos < srcEnd;) {
- if (buff[myPos++] != src.charAt(srcPos++))
- break;
- if (srcPos == srcEnd)
- return i - start; // found it
- }
- }
- return -1;
- }
-
- // hash ignoring case
-// public int hashIgnoreCase() {
-// return hashBytesIC(buff, start, end - start);
-// }
-
- public boolean isNull() {
- return start == end;
- }
-
-// private static int hashBytesIC(byte bytes[], int start, int bytesLen) {
-// int max = start + bytesLen;
-// byte bb[] = bytes;
-// int code = 0;
-// for (int i = start; i < max; i++) {
-// code = code * 37 + Ascii.toLower(bb[i]);
-// }
-// return code;
-// }
-
- @Override
- public int limit() {
- return end;
- }
-
- public void limit(int newEnd) {
- end = newEnd;
- }
-
- /**
- * Make space for len chars.
- * If len is small, allocate a reserve space too.
- */
- public BBuffer makeSpace(int count) {
- byte[] tmp = null;
-
- int newSize;
- int desiredSize = end + count;
-
- if (buff == null) {
- if (desiredSize < 16)
- desiredSize = 16; // take a minimum
- buff = new byte[desiredSize];
- start = 0;
- end = 0;
- return this;
- }
-
- // limit < buf.length ( the buffer is already big )
- // or we already have space XXX
- if (desiredSize <= buff.length) {
- return this;
- }
- // grow in larger chunks
- if (desiredSize < 2 * buff.length) {
- newSize = buff.length * 2;
- tmp = new byte[newSize];
- } else {
- newSize = buff.length * 2 + count;
- tmp = new byte[newSize];
- }
-
- System.arraycopy(buff, start, tmp, 0, end - start);
- buff = tmp;
- tmp = null;
- end = end - start;
- start = 0;
- return this;
- }
-
-// /**
-// * Find a character, no side effects.
-// *
-// * @return index of char if found, -1 if not
-// */
-// public static int findChars(byte buf[], int start, int end, byte c[]) {
-// int clen = c.length;
-// int offset = start;
-// while (offset < end) {
-// for (int i = 0; i < clen; i++)
-// if (buf[offset] == c[i]) {
-// return offset;
-// }
-// offset++;
-// }
-// return -1;
-// }
-
-// /**
-// * Find the first character != c
-// *
-// * @return index of char if found, -1 if not
-// */
-// public static int findNotChars(byte buf[], int start, int end, byte c[]) {
-// int clen = c.length;
-// int offset = start;
-// boolean found;
-//
-// while (offset < end) {
-// found = true;
-// for (int i = 0; i < clen; i++) {
-// if (buf[offset] == c[i]) {
-// found = false;
-// break;
-// }
-// }
-// if (found) { // buf[offset] != c[0..len]
-// return offset;
-// }
-// offset++;
-// }
-// return -1;
-// }
-
- @Override
- public int position() {
- return start;
- }
-
- public void advance(int len) {
- start += len;
- }
-
- @Override
- public void position(int newStart) {
- start = newStart;
- }
-
- public void put(byte b) {
- makeSpace(1);
- buff[end++] = b;
- }
-
- public void putByte(int b) {
- makeSpace(1);
- buff[end++] = (byte) b;
- }
-
- public int read(BBuffer res) {
- res.setBytes(buff, start, remaining());
- end = start;
- return res.remaining();
- }
-
- /**
- * Read a chunk from is.
- *
- * You don't need to use buffered input stream, we do the
- * buffering.
- */
- public int read(InputStream is) throws IOException {
- makeSpace(1024);
- int res = is.read(buff, end, buff.length - end);
- if (res > 0) {
- end += res;
- }
- return res;
- }
-
- public int readAll(InputStream is) throws IOException {
- int size = 0;
- while (true) {
- int res = read(is);
- if (res < 0) {
- return size;
- }
- size += res;
- }
- }
-
- public int readByte() {
- if (start == end) {
- return -1;
- }
- return buff[start++];
- }
-
-
- /**
- * Read a line - excluding the line terminator, which is consummed as
- * well but not included in the response.
- *
- * Line can end with CR, LF or CR/LF
- *
- * @param res
- * @return number of bytes read, or -1 if line ending not found in buffer.
- */
- public int readLine(BBuffer res) {
- int cstart = start;
- while(start < end) {
- byte chr = buff[start++];
- if (chr == CR || chr == LF) {
- res.setBytes(buff, cstart, start - cstart -1);
- if (chr == CR) {
- if (start < end) {
- byte chr2 = buff[start];
- if (chr2 == LF) {
- start++;
- }
- }
- }
- return res.remaining();
- }
- }
- start = cstart;
- return -1;
- }
- /**
- * Consume up to but not including delim.
- *
- */
- public final int readToDelimOrSpace(byte delim,
- BBuffer res) {
- int resStart = start;
- while (true) {
- if (start >= end) {
- break;
- }
- byte chr = buff[start];
- if (chr == delim || chr == SP || chr == HT) {
- break;
- }
- start++;
- }
- res.setBytes(buff, resStart, start - resStart);
- return res.remaining();
- }
-
-
- /**
- * Consume all up to the first space or \t, which will be the
- * first character in the buffer.
- *
- * Consumed data is wrapped in res.
- */
- public int readToSpace(BBuffer res) {
- int resStart = start;
- while (true) {
- if (start >= end) {
- break;
- }
- if (buff[start] == SP
- || buff[start] == HT) {
- break;
- }
- start++;
- }
- res.setBytes(buff, resStart, start - resStart);
- return res.remaining();
- }
- /**
- * Resets the message buff to an uninitialized state.
- */
- public void recycle() {
- start = 0;
- end = 0;
- }
- @Override
- public void release() {
-// synchronized (this) {
-// useCount--;
-// if (useCount == -1) {
-// // all slices have been released -
-// // TODO: callback, return to pool
-// }
-// }
- }
- public int remaining() {
- return end - start;
- }
-
- public void reset() {
- buff = null;
- }
-
- // -------------------- Setup --------------------
- /**
- * Sets the message bytes to the specified subarray of bytes.
- *
- * @param b
- * the ascii bytes
- * @param off
- * the start offset of the bytes
- * @param len
- * the length of the bytes
- */
- public void setBytes(byte[] b, int off, int len) {
- throw new RuntimeException("Can't setBytes on allocated buffer");
- }
-
- public void wrap(BBucket b) {
- setBytes(b.array(), b.position(), b.remaining());
- }
-
- public void wrap(ByteBuffer b) {
- setBytes(b.array(), b.position(), b.remaining());
- }
-
- protected void setBytesInternal(byte[] b, int off, int len) {
- buff = b;
- start = off;
- end = start + len;
- }
-
-// public final void lowerCase() {
-// while (start < end) {
-// byte chr = buff[start];
-// if ((chr >= A) && (chr <= Z)) {
-// buff[start] = (byte) (chr - LC_OFFSET);
-// }
-// start++;
-// }
-// }
-
- public void setEnd(int i) {
- end = i;
- }
-
- /**
- * The old code from MessageBytes, used for setContentLength
- * and setStatus.
- * TODO: just use StringBuilder, the method is faster.
- */
- public void setLong(long l) {
- if (array() == null) {
- makeSpace(20);
- }
- long current = l;
- byte[] buf = array();
- int start = 0;
- int end = 0;
- if (l == 0) {
- buf[end++] = (byte) '0';
- } else if (l < 0) {
- current = -l;
- buf[end++] = (byte) '-';
- }
- while (current > 0) {
- int digit = (int) (current % 10);
- current = current / 10;
- buf[end++] = Hex.HEX[digit];
- }
- setOffset(0);
- setEnd(end);
- // Inverting buffer
- end--;
- if (l < 0) {
- start++;
- }
- while (end > start) {
- byte temp = buf[start];
- buf[start] = buf[end];
- buf[end] = temp;
- start++;
- end--;
- }
- }
-
- public void setOffset(int off) {
- if (end < off)
- end = off;
- start = off;
- }
-
-
- public int skipEmptyLines() {
- int resStart = start;
- while (buff[start] == CR || buff[start] == LF) {
- start++;
- if (start == end) {
- break;
- }
- }
- return start - resStart;
- }
-
- public int skipSpace() {
- int cstart = start;
- while (true) {
- if (start >= end) {
- return start - cstart;
- }
- if ((buff[start] == SP) || (buff[start] == HT)) {
- start++;
- } else {
- return start - cstart;
- }
- }
- }
-
- public int read() {
- if (end == start) {
- return -1;
- }
- return (buff[start++] & 0xFF);
-
- }
-
- public int substract(BBuffer src) {
-
- if (end == start) {
- return -1;
- }
-
- int len = getLength();
- src.append(buff, start, len);
- start = end;
- return len;
-
- }
-
- public int substract(byte src[], int off, int len) {
-
- if ((end - start) == 0) {
- return -1;
- }
-
- int n = len;
- if (len > getLength()) {
- n = getLength();
- }
- System.arraycopy(buff, start, src, off, n);
- start += n;
- return n;
-
- }
-
- public String toString() {
- return toString(DEFAULT_CHARACTER_ENCODING);
- }
-
- public String toString(String enc) {
- if (null == buff) {
- return null;
- } else if (end == start) {
- return "";
- }
-
- String strValue = null;
- try {
- if (enc == null) {
- enc = DEFAULT_CHARACTER_ENCODING;
- }
-
- strValue = new String(buff, start, end - start, enc);
- /*
- * Does not improve the speed too much on most systems, it's safer
- * to use the "clasical" new String().
- *
- * Most overhead is in creating char[] and copying, the internal
- * implementation of new String() is very close to what we do. The
- * decoder is nice for large buffers and if we don't go to String (
- * so we can take advantage of reduced GC)
- *
- * // Method is commented out, in: return B2CConverter.decodeString(
- * enc );
- */
- } catch (java.io.UnsupportedEncodingException e) {
- // Use the platform encoding in that case; the usage of a bad
- // encoding will have been logged elsewhere already
- strValue = new String(buff, start, end - start);
- }
- return strValue;
- }
-
- public void wrapTo(BBuffer res) {
- res.setBytes(buff, start, remaining());
- }
-
- /**
- * Convert specified String to a byte array. This ONLY WORKS for ascii, UTF
- * chars will be truncated.
- *
- * @param value
- * to convert to byte array
- * @return the byte array value
- */
- public static final byte[] convertToBytes(String value) {
- byte[] result = new byte[value.length()];
- for (int i = 0; i < value.length(); i++) {
- result[i] = (byte) value.charAt(i);
- }
- return result;
- }
-
- /**
- * Find a character, no side effects.
- *
- * @return index of char if found, -1 if not
- */
- public static int findChar(byte buf[], int start, int end, char c) {
- byte b = (byte) c;
- int offset = start;
- while (offset < end) {
- if (buf[offset] == b) {
- return offset;
- }
- offset++;
- }
- return -1;
- }
- private static int hashBytes(byte buff[], int start, int bytesLen) {
- int max = start + bytesLen;
- byte bb[] = buff;
- int code = 0;
- for (int i = start; i < max; i++) {
- code = code * 31 + bb[i];
- // TODO: if > 0x7F, convert to chars / switch to UTF8
- }
- return code;
- }
-
- public static boolean hasLFLF(BBucket bucket) {
- int pos = bucket.position();
- int lastValid = bucket.limit();
- byte[] buf = bucket.array();
-
- for (int i = pos; i < lastValid; i++) {
- byte chr = buf[i];
- if (chr == LF) {
- if (i + 1 < lastValid && buf[i + 1] == CR) {
- // \n\r\n
- i++;
- }
- if (i + 1 < lastValid && buf[i + 1] == LF) {
- return true; // \n\n
- }
- } else if (chr == CR) {
- if (i + 1 < lastValid && buf[i + 1] == CR) {
- return true; // \r\r
- }
- if (i + 1 < lastValid && buf[i + 1] == LF) {
- // \r\n
- i++; // skip LF
- if (i + 1 < lastValid && buf[i + 1] == CR &&
- i + 2 < lastValid && buf[i + 2] == LF) {
- i++;
- return true;
- }
- }
-
- }
- }
- return false;
- }
-
- public static int indexOf(byte bytes[], int off, int end, char qq) {
- // Works only for UTF
- while (off < end) {
- byte b = bytes[off];
- if (b == qq)
- return off;
- off++;
- }
- return -1;
- }
-
- /**
- * Returns true if the specified ASCII character is a digit.
- */
-
- public static boolean isDigit(int c) {
- return isDigit[c & 0xff];
- }
-
- /**
- * Parses an unsigned integer from the specified subarray of bytes.
- * @param b the bytes to parse
- * @param off the start offset of the bytes
- * @param len the length of the bytes
- * @exception NumberFormatException if the integer format was invalid
- */
- public static int parseInt(byte[] b, int off, int len)
- throws NumberFormatException
- {
- int c;
-
- if (b == null || len <= 0 || !isDigit(c = b[off++])) {
- throw new NumberFormatException();
- }
-
- int n = c - '0';
-
- while (--len > 0) {
- if (!isDigit(c = b[off++])) {
- throw new NumberFormatException();
- }
- n = n * 10 + c - '0';
- }
-
- return n;
- }
-
- /**
- * Parses an unsigned long from the specified subarray of bytes.
- * @param b the bytes to parse
- * @param off the start offset of the bytes
- * @param len the length of the bytes
- * @exception NumberFormatException if the long format was invalid
- */
- public static long parseLong(byte[] b, int off, int len)
- throws NumberFormatException
- {
- int c;
-
- if (b == null || len <= 0 || !isDigit(c = b[off++])) {
- throw new NumberFormatException();
- }
-
- long n = c - '0';
- long m;
-
- while (--len > 0) {
- if (!isDigit(c = b[off++])) {
- throw new NumberFormatException();
- }
- m = n * 10 + c - '0';
-
- if (m < n) {
- // Overflow
- throw new NumberFormatException();
- } else {
- n = m;
- }
- }
-
- return n;
- }
-
-
-
- /**
- * Returns the lower case equivalent of the specified ASCII character.
- */
- public static int toLower(int c) {
- if (c > 0x7f) return c;
- return toLower[c & 0xff] & 0xff;
- }
-
- /**
- * Returns true if the specified ASCII character is upper case.
- */
-
- public static boolean isUpper(int c) {
- return c < 0x7f && isUpper[c];
- }
-
- /**
- * A slice of a bucket, holding reference to a parent bucket.
- *
- * This is used when a filter splits a bucket - the original
- * will be replaced with 1 or more slices. When all slices are
- * released, the parent will also be released.
- *
- * It is not possible to add data.
- *
- * @author Costin Manolache
- */
- static class IOBucketWrap extends BBuffer {
- //IOBucket parent;
-
-
- public BBuffer makeSpace(int count) {
- throw new RuntimeException("Attempting to change buffer " +
- "on a wrapped BBuffer");
- }
-
- public void release() {
-// if (parent != null) {
-// parent.release();
-// }
- }
-
- public void setBytes(byte[] b, int off, int len) {
- super.setBytesInternal(b, off, len);
- }
- }
-
-
-}
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/lite/io/BufferedIOReader.java b/modules/tomcat-lite/java/org/apache/tomcat/lite/io/BufferedIOReader.java
deleted file mode 100644
index 35d339a..0000000
--- a/modules/tomcat-lite/java/org/apache/tomcat/lite/io/BufferedIOReader.java
+++ /dev/null
@@ -1,380 +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.lite.io;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.Reader;
-import java.nio.CharBuffer;
-
-
-/**
- * Cut&pasted from Harmony buffered reader ( apache license ).
- * Changes:
- * - additional method to recycle to avoid re-allocating on
- * each request.
- */
-public class BufferedIOReader extends BufferedReader {
-
- // Not recycled - the buffer is tied to the message/IOReader
- IOReader in;
-
- private String enc;
- boolean closed;
- private char[] buf;
- private int marklimit = -1;
-
- private int count;
-
- private int markpos = -1;
-
- private int pos;
-
- public BufferedIOReader(IOReader realReader) {
- // we're not using super - we override all methods, but need the
- // signature
- super(DUMMY_READER, 1);
- this.in = realReader;
- buf = new char[8192];
- }
-
- public void recycle() {
- enc = null;
- closed = false;
-
- if (in != null) {
- in.recycle();
- }
- marklimit = -1;
- count = 0;
- markpos = -1;
- pos = 0;
- }
-
- private void checkClosed() throws IOException {
- if (closed) throw new IOException("closed");
- }
-
- public int read(CharBuffer target) throws IOException {
- checkClosed();
- int len = target.remaining();
- int n = read(target.array(), target.position(), target.remaining());
- if (n > 0)
- target.position(target.position() + n);
- return n;
- }
-
-
- public int read(char[] cbuf) throws IOException {
- return read(cbuf, 0, cbuf.length);
- }
-
-
- /**
- * Closes this reader. This implementation closes the buffered source reader
- * and releases the buffer. Nothing is done if this reader has already been
- * closed.
- *
- * @throws IOException
- * if an error occurs while closing this reader.
- */
- @Override
- public void close() throws IOException {
- synchronized (lock) {
- if (!isClosed()) {
- in.close();
- closed = true;
- // buf remains
- }
- }
- }
-
- private int fillbuf() throws IOException {
- if (markpos == -1 || (pos - markpos >= marklimit)) {
- /* Mark position not set or exceeded readlimit */
- int result = in.read(buf, 0, buf.length);
- if (result > 0) {
- markpos = -1;
- pos = 0;
- count = result == -1 ? 0 : result;
- }
- return result;
- }
- if (markpos == 0 && marklimit > buf.length) {
- /* Increase buffer size to accommodate the readlimit */
- int newLength = buf.length * 2;
- if (newLength > marklimit) {
- newLength = marklimit;
- }
- char[] newbuf = new char[newLength];
- System.arraycopy(buf, 0, newbuf, 0, buf.length);
- buf = newbuf;
- } else if (markpos > 0) {
- System.arraycopy(buf, markpos, buf, 0, buf.length - markpos);
- }
-
- /* Set the new position and mark position */
- pos -= markpos;
- count = markpos = 0;
- int charsread = in.read(buf, pos, buf.length - pos);
- count = charsread == -1 ? pos : pos + charsread;
- return charsread;
- }
-
- private boolean isClosed() {
- return closed;
- }
-
- @Override
- public void mark(int readlimit) throws IOException {
- if (readlimit < 0) {
- throw new IllegalArgumentException();
- }
- synchronized (lock) {
- checkClosed();
- marklimit = readlimit;
- markpos = pos;
- }
- }
-
- @Override
- public boolean markSupported() {
- return true;
- }
-
- @Override
- public int read() throws IOException {
- synchronized (lock) {
- checkClosed();
- /* Are there buffered characters available? */
- if (pos < count || fillbuf() != -1) {
- return buf[pos++];
- }
- markpos = -1;
- return -1;
- }
- }
-
- @Override
- public int read(char[] buffer, int offset, int length) throws IOException {
- synchronized (lock) {
- checkClosed();
- if (offset < 0 || offset > buffer.length - length || length < 0) {
- throw new IndexOutOfBoundsException();
- }
- if (length == 0) {
- return 0;
- }
- int required;
- if (pos < count) {
- /* There are bytes available in the buffer. */
- int copylength = count - pos >= length ? length : count - pos;
- System.arraycopy(buf, pos, buffer, offset, copylength);
- pos += copylength;
- if (copylength == length || !in.ready()) {
- return copylength;
- }
- offset += copylength;
- required = length - copylength;
- } else {
- required = length;
- }
-
- while (true) {
- int read;
- /*
- * If we're not marked and the required size is greater than the
- * buffer, simply read the bytes directly bypassing the buffer.
- */
- if (markpos == -1 && required >= buf.length) {
- read = in.read(buffer, offset, required);
- if (read == -1) {
- return required == length ? -1 : length - required;
- }
- } else {
- if (fillbuf() == -1) {
- return required == length ? -1 : length - required;
- }
- read = count - pos >= required ? required : count - pos;
- System.arraycopy(buf, pos, buffer, offset, read);
- pos += read;
- }
- required -= read;
- if (required == 0) {
- return length;
- }
- if (!in.ready()) {
- return length - required;
- }
- offset += read;
- }
- }
- }
-
- /**
- * Returns the next line of text available from this reader. A line is
- * represented by zero or more characters followed by {@code '\n'},
- * {@code '\r'}, {@code "\r\n"} or the end of the reader. The string does
- * not include the newline sequence.
- *
- * @return the contents of the line or {@code null} if no characters were
- * read before the end of the reader has been reached.
- * @throws IOException
- * if this reader is closed or some other I/O error occurs.
- */
- public String readLine() throws IOException {
- synchronized (lock) {
- checkClosed();
- /* Are there buffered characters available? */
- if ((pos >= count) && (fillbuf() == -1)) {
- return null;
- }
- for (int charPos = pos; charPos < count; charPos++) {
- char ch = buf[charPos];
- if (ch > '\r') {
- continue;
- }
- if (ch == '\n') {
- String res = new String(buf, pos, charPos - pos);
- pos = charPos + 1;
- return res;
- } else if (ch == '\r') {
- String res = new String(buf, pos, charPos - pos);
- pos = charPos + 1;
- if (((pos < count) || (fillbuf() != -1))
- && (buf[pos] == '\n')) {
- pos++;
- }
- return res;
- }
- }
-
- char eol = '\0';
- StringBuilder result = new StringBuilder(80);
- /* Typical Line Length */
-
- result.append(buf, pos, count - pos);
- pos = count;
- while (true) {
- /* Are there buffered characters available? */
- if (pos >= count) {
- if (eol == '\n') {
- return result.toString();
- }
- // attempt to fill buffer
- if (fillbuf() == -1) {
- // characters or null.
- return result.length() > 0 || eol != '\0' ? result
- .toString() : null;
- }
- }
- for (int charPos = pos; charPos < count; charPos++) {
- if (eol == '\0') {
- if ((buf[charPos] == '\n' || buf[charPos] == '\r')) {
- eol = buf[charPos];
- }
- } else if (eol == '\r' && (buf[charPos] == '\n')) {
- if (charPos > pos) {
- result.append(buf, pos, charPos - pos - 1);
- }
- pos = charPos + 1;
- return result.toString();
- } else {
- if (charPos > pos) {
- result.append(buf, pos, charPos - pos - 1);
- }
- pos = charPos;
- return result.toString();
- }
- }
- if (eol == '\0') {
- result.append(buf, pos, count - pos);
- } else {
- result.append(buf, pos, count - pos - 1);
- }
- pos = count;
- }
- }
-
- }
-
-
- @Override
- public boolean ready() throws IOException {
- synchronized (lock) {
- checkClosed();
- return ((count - pos) > 0) || in.ready();
- }
- }
-
- @Override
- public void reset() throws IOException {
- synchronized (lock) {
- checkClosed();
- if (markpos == -1) {
- throw new IOException("No mark");
- }
- pos = markpos;
- }
- }
-
- @Override
- public long skip(long amount) throws IOException {
- if (amount < 0) {
- throw new IllegalArgumentException();
- }
- synchronized (lock) {
- checkClosed();
- if (amount < 1) {
- return 0;
- }
- if (count - pos >= amount) {
- pos += amount;
- return amount;
- }
-
- long read = count - pos;
- pos = count;
- while (read < amount) {
- if (fillbuf() == -1) {
- return read;
- }
- if (count - pos >= amount - read) {
- pos += amount - read;
- return amount;
- }
- // Couldn't get all the characters, skip what we read
- read += (count - pos);
- pos = count;
- }
- return amount;
- }
- }
-
- private static Reader DUMMY_READER = new Reader() {
- @Override
- public void close() throws IOException {
- }
-
- @Override
- public int read(char[] cbuf, int off, int len) throws IOException {
- return 0;
- }
- };
-
-
-}
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/lite/io/CBucket.java b/modules/tomcat-lite/java/org/apache/tomcat/lite/io/CBucket.java
deleted file mode 100644
index 688785e..0000000
--- a/modules/tomcat-lite/java/org/apache/tomcat/lite/io/CBucket.java
+++ /dev/null
@@ -1,508 +0,0 @@
-/*
- */
-package org.apache.tomcat.lite.io;
-
-import java.io.Serializable;
-import java.nio.CharBuffer;
-
-/**
- * Wraps a char[].
- *
- * Doesn't provide any mutation methods. Classes in this package
- * have access to the buffer, for conversions.
- *
- *
- * @author Costin Manolache
- */
-public class CBucket implements CharSequence, Comparable, Serializable {
- protected char value[];
-
- protected int start;
-
- protected int end;
-
- // Reused.
- protected CharBuffer cb;
-
- // cache
- protected String strValue;
- protected int hash;
-
- public CBucket() {
- }
-
- /**
- * Used by IOWriter for conversion. Will not modify the content.
- */
- CharBuffer getNioBuffer() {
- if (cb == null || cb.array() != value) {
- cb = CharBuffer.wrap(value, start, end - start);
- } else {
- cb.position(start);
- cb.limit(end);
- }
- return cb;
- }
-
- public void recycle() {
- start = 0;
- end = 0;
- value = null;
- strValue = null;
- hash = 0;
- }
-
- public String toString() {
- if (null == value) {
- return null;
- } else if (end - start == 0) {
- return "";
- }
- if (strValue == null) {
- strValue = new String(value, start, end - start);
- }
- return strValue;
- }
-
- /**
- * Same as String
- */
- public int hashCode() {
- int h = hash;
- if (h == 0) {
- int off = start;
- char val[] = value;
-
- for (int i = start; i < end; i++) {
- h = 31*h + val[off++];
- }
- hash = h;
- }
- return h;
- }
-
- public long getLong() {
- return parseLong(value, start, end - start);
- }
-
- public int getInt() {
- return parseInt(value, start, end - start);
- }
-
- public static int parseInt(char[] b, int off, int len)
- throws NumberFormatException
- {
- int c;
-
- if (b == null || len <= 0 || !BBuffer.isDigit(c = b[off++])) {
- throw new NumberFormatException();
- }
-
- int n = c - '0';
-
- while (--len > 0) {
- if (!BBuffer.isDigit(c = b[off++])) {
- throw new NumberFormatException();
- }
- n = n * 10 + c - '0';
- }
-
- return n;
- }
-
-
- public static long parseLong(char[] b, int off, int len)
- throws NumberFormatException
- {
- int c;
-
- if (b == null || len <= 0 || !BBuffer.isDigit(c = b[off++])) {
- throw new NumberFormatException();
- }
-
- long n = c - '0';
- long m;
-
- while (--len > 0) {
- if (!BBuffer.isDigit(c = b[off++])) {
- throw new NumberFormatException();
- }
- m = n * 10 + c - '0';
-
- if (m < n) {
- // Overflow
- throw new NumberFormatException();
- } else {
- n = m;
- }
- }
-
- return n;
- }
-
-
- /**
- * Compares the message bytes to the specified String object.
- *
- * @param s
- * the String to compare
- * @return true if the comparison succeeded, false otherwise
- */
- public boolean equals(String s) {
- char[] c = value;
- int len = end - start;
- if (c == null || len != s.length()) {
- return false;
- }
- int off = start;
- for (int i = 0; i < len; i++) {
- if (c[off++] != s.charAt(i)) {
- return false;
- }
- }
- return true;
- }
-
- /**
- * Compares the message bytes to the specified String object.
- *
- * @param s
- * the String to compare
- * @return true if the comparison succeeded, false otherwise
- */
- public boolean equalsIgnoreCase(String s) {
- char[] c = value;
- int len = end - start;
- if (c == null || len != s.length()) {
- return false;
- }
- int off = start;
- for (int i = 0; i < len; i++) {
- if (BBuffer.toLower(c[off++]) != BBuffer.toLower(s.charAt(i))) {
- return false;
- }
- }
- return true;
- }
-
- public boolean equals(Object obj) {
- if (obj instanceof CBuffer) {
- CBuffer cc = (CBuffer) obj;
- return equals(cc.value, cc.start, cc.length());
- } else if (obj instanceof String) {
- return equals((String)obj);
- }
- return false;
- }
-
- public boolean equals(char b2[], int off2, int len2) {
- char b1[] = value;
- if (b1 == null && b2 == null)
- return true;
-
- if (b1 == null || b2 == null || end - start != len2) {
- return false;
- }
- int off1 = start;
- int len = end - start;
- while (len-- > 0) {
- if (b1[off1++] != b2[off2++]) {
- return false;
- }
- }
- return true;
- }
-
- public boolean equals(byte b2[], int off2, int len2) {
- char b1[] = value;
- if (b2 == null && b1 == null)
- return true;
-
- if (b1 == null || b2 == null || end - start != len2) {
- return false;
- }
- int off1 = start;
- int len = end - start;
-
- while (len-- > 0) {
- if (b1[off1++] != (char) b2[off2++]) {
- return false;
- }
- }
- return true;
- }
-
-
- /**
- * Returns true if the message bytes starts with the specified string.
- *
- * @param s
- * the string
- */
- public boolean startsWith(String s) {
- char[] c = value;
- int len = s.length();
- if (c == null || len > end - start) {
- return false;
- }
- int off = start;
- for (int i = 0; i < len; i++) {
- if (c[off++] != s.charAt(i)) {
- return false;
- }
- }
- return true;
- }
-
- /**
- * Returns true if the message bytes starts with the specified string.
- *
- * @param s
- * the string
- */
- public boolean startsWithIgnoreCase(String s, int pos) {
- char[] c = value;
- int len = s.length();
- if (c == null || len + pos > end - start) {
- return false;
- }
- int off = start + pos;
- for (int i = 0; i < len; i++) {
- if (BBuffer.toLower(c[off++]) != BBuffer.toLower(s.charAt(i))) {
- return false;
- }
- }
- return true;
- }
-
- public int indexOf(char c) {
- return indexOf(c, start);
- }
-
- public int lastIndexOf(char c) {
- return lastIndexOf(c, 0, end - start);
- }
-
- /**
- */
- public int lastIndexOf(char c, int off, int len) {
- char[] buf = value;
- int slash = -1;
- for (int i = start + len - 1; i >= start + off; i--) {
- if (buf[i] == c) {
- slash = i - start;
- break;
- }
- }
- return slash;
- }
-
- /**
- * Returns true if the message bytes starts with the specified string.
- *
- * @param c
- * the character
- */
- public int indexOf(char c, int starting) {
- int ret = indexOf(value, start + starting, end, c);
- return (ret >= start) ? ret - start : -1;
- }
-
- public static int indexOf(char chars[], int off, int cend, char qq) {
- while (off < cend) {
- char b = chars[off];
- if (b == qq)
- return off;
- off++;
- }
- return -1;
- }
-
- public int indexOf(String src) {
- return indexOf(src, 0, src.length(), 0);
- }
-
- public int indexOf(String src, int srcOff, int srcLen, int myOff) {
- char first = src.charAt(srcOff);
-
- // Look for first char
- int srcEnd = srcOff + srcLen;
-
- for (int i = myOff + start; i <= (end - srcLen); i++) {
- if (value[i] != first)
- continue;
- // found first char, now look for a match
- int myPos = i + 1;
- for (int srcPos = srcOff + 1; srcPos < srcEnd;) {
- if (value[myPos++] != src.charAt(srcPos++))
- break;
- if (srcPos == srcEnd)
- return i - start; // found it
- }
- }
- return -1;
- }
-
- public char lastChar() {
- return value[end - 1];
- }
-
- public char charAt(int index) {
- return value[index + start];
- }
-
- public void wrap(char[] buff, int start, int end) {
- if (value != null) {
- throw new RuntimeException("Can wrap only once");
- }
- this.value = buff;
- this.start = start;
- this.end = end;
- }
-
- public CharSequence subSequence(int sstart, int send) {
- CBucket seq = new CBucket();
- seq.wrap(this.value, start + sstart, start + send);
- return seq;
- }
-
- public int length() {
- return end - start;
- }
-
- @Override
- public int compareTo(Object o) {
- // Code based on Harmony
- if (o instanceof CBuffer) {
- CBuffer dest = (CBuffer) o;
- int o1 = start, o2 = dest.start, result;
- int len = end - start;
- int destLen = dest.end - dest.start;
- int fin = (len < destLen ?
- end : start + destLen);
- char[] target = dest.value;
- while (o1 < fin) {
- if ((result = value[o1++] - target[o2++]) != 0) {
- return result;
- }
- }
- return len - destLen;
-
- } else if (o instanceof CharSequence) {
- CharSequence dest = (CharSequence) o;
- int o1 = start, o2 = 0, result;
- int len = end - start;
- int destLen = dest.length();
- int fin = (len < destLen ?
- end : start + destLen);
- while (o1 < fin) {
- if ((result = value[o1++] - dest.charAt(o2++)) != 0) {
- return result;
- }
- }
- return len - destLen;
-
- } else {
- throw new RuntimeException("CompareTo not supported " + o);
- }
- }
-
- /**
- * Compare given char chunk with String ignoring case.
- * Return -1, 0 or +1 if inferior, equal, or superior to the String.
- */
- public final int compareIgnoreCase(String compareTo) {
- int result = 0;
- char[] c = value;
- int len = compareTo.length();
- if ((end - start) < len) {
- len = end - start;
- }
- for (int i = 0; (i < len) && (result == 0); i++) {
- if (BBuffer.toLower(c[i + start]) > BBuffer.toLower(compareTo.charAt(i))) {
- result = 1;
- } else if (BBuffer.toLower(c[i + start]) < BBuffer.toLower(compareTo.charAt(i))) {
- result = -1;
- }
- }
- if (result == 0) {
- if (compareTo.length() > (end - start)) {
- result = -1;
- } else if (compareTo.length() < (end - start)) {
- result = 1;
- }
- }
- return result;
- }
-
- /**
- * Compare given char chunk with String.
- * Return -1, 0 or +1 if inferior, equal, or superior to the String.
- */
- public final int compare(String compareTo) {
- int result = 0;
- char[] c = value;
- int len = compareTo.length();
- if ((end - start) < len) {
- len = end - start;
- }
- for (int i = 0; (i < len) && (result == 0); i++) {
- if (c[i + start] > compareTo.charAt(i)) {
- result = 1;
- } else if (c[i + start] < compareTo.charAt(i)) {
- result = -1;
- }
- }
- if (result == 0) {
- if (compareTo.length() > (end - start)) {
- result = -1;
- } else if (compareTo.length() < (end - start)) {
- result = 1;
- }
- }
- return result;
- }
-
- public int getExtension(CBuffer ext, char slashC, char dotC) {
- int slash = lastIndexOf(slashC);
- if (slash < 0) {
- slash = 0;
- }
- int dot = lastIndexOf(dotC, slash, length());
- if (dot < 0) {
- return -1;
- }
- ext.wrap(this, dot + 1, length());
- return dot;
- }
-
- /**
- * Find the position of the nth slash, in the given char chunk.
- */
- public final int nthSlash(int n) {
- char[] c = value;
- int pos = start;
- int count = 0;
-
- while (pos < end) {
- if ((c[pos++] == '/') && ((++count) == n)) {
- pos--;
- break;
- }
- }
-
- return pos - start;
- }
-
-
- public boolean hasUpper() {
- for (int i = start; i < end; i++) {
- char c = value[i];
- if (c < 0x7F && BBuffer.isUpper(c)) {
- return true;
- }
- }
- return false;
- }
-
-}
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/lite/io/CBuffer.java b/modules/tomcat-lite/java/org/apache/tomcat/lite/io/CBuffer.java
deleted file mode 100644
index 86b4234..0000000
--- a/modules/tomcat-lite/java/org/apache/tomcat/lite/io/CBuffer.java
+++ /dev/null
@@ -1,387 +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.lite.io;
-
-import java.io.IOException;
-import java.nio.CharBuffer;
-
-
-/**
- * Similar with StringBuilder or StringBuffer, but with access to the
- * raw buffer - this avoids copying the data.
- *
- * Utilities to manipluate 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 imutable and secure objects.
- *
- * @author dac at sun.com
- * @author James Todd [gonzo at sun.com]
- * @author Costin Manolache
- * @author Remy Maucherat
- */
-public class CBuffer extends CBucket implements Cloneable,
- Appendable {
-
-
- /**
- * Creates a new, uninitialized CharChunk object.
- */
- public static CBuffer newInstance() {
- return new CBuffer();
- }
-
- private CBuffer() {
- }
-
- /**
- * Resets the message bytes to an uninitialized state.
- */
- public void recycle() {
- dirty();
- start = 0;
- end = 0;
- }
-
- /**
- * Same as String
- */
- public int hashCode() {
- int h = 0;
- int off = start;
- char val[] = value;
-
- for (int i = start; i < end; i++) {
- h = 31*h + val[off++];
- }
- return h;
- }
-
- public String toString() {
- if (null == value) {
- return null;
- } else if (end - start == 0) {
- return "";
- }
- return new String(value, start, end - start);
- }
-
- public void wrap(char[] buff, int start, int end) {
- dirty();
- this.value = buff;
- this.start = start;
- this.end = end;
- }
-
- public void wrap(CBucket buff, int off, int srcEnd) {
- dirty();
- this.value = buff.value;
- this.start = buff.start + off;
- this.end = this.start + srcEnd - off;
- }
-
-
- // ----------- Used for IOWriter / conversion ---------
-
- public char[] array() {
- return value;
- }
-
- public int position() {
- return start;
- }
-
- CharBuffer getAppendCharBuffer() {
- makeSpace(16);
- if (cb == null || cb.array() != value) {
- cb = CharBuffer.wrap(value, end, value.length - end);
- } else {
- cb.position(end);
- cb.limit(value.length);
- }
- return cb;
- }
-
- void returnNioBuffer(CharBuffer c) {
- dirty();
- start = c.position();
- }
-
- void returnAppendCharBuffer(CharBuffer c) {
- dirty();
- end = c.position();
- }
-
- // -------- Delete / replace ---------------
-
- /**
- * 'Delete' all chars after offset.
- *
- * @param offset
- */
- public void delete(int offset) {
- dirty();
- end = start + offset;
- }
-
- // -------------------- Adding data --------------------
-
- /**
- * Append methods take start and end - similar with this one.
- * The source is not modified.
- */
- @Override
- public CBuffer append(CharSequence csq, int astart, int aend)
- throws IOException {
- makeSpace(aend - astart);
-
- for (int i = astart; i < aend; i++) {
- value[end++] = csq.charAt(i);
- }
- return this;
- }
-
- public CBuffer append(char b) {
- makeSpace(1);
- value[end++] = b;
- return this;
- }
-
- public CBuffer append(int i) {
- // TODO: can be optimizeed...
- append(Integer.toString(i));
- return this;
- }
-
- /**
- * Add data to the buffer
- */
- public CBuffer append(char src[], int srcStart, int srcEnd) {
- int len = srcEnd - srcStart;
- if (len == 0) {
- return this;
- }
- // will grow, up to limit
- makeSpace(len);
-
- // assert: makeSpace made enough space
- System.arraycopy(src, srcStart, value, end, len);
- end += len;
- return this;
- }
-
- /**
- * Add data to the buffer
- */
- public CBuffer append(StringBuffer sb) {
- int len = sb.length();
- if (len == 0) {
- return this;
- }
- makeSpace(len);
- sb.getChars(0, len, value, end);
- end += len;
- return this;
- }
-
- /**
- * Append a string to the buffer
- */
- public CBuffer append(String s) {
- if (s == null || s.length() == 0) {
- return this;
- }
- append(s, 0, s.length());
- return this;
- }
-
-
- /**
- * Append a string to the buffer
- */
- public CBuffer append(String s, int off, int srcEnd) {
- if (s == null)
- return this;
-
- // will grow, up to limit
- makeSpace(srcEnd - off);
-
- // assert: makeSpace made enough space
- s.getChars(off, srcEnd, value, end);
- end += srcEnd - off;
- return this;
- }
-
- // TODO: long, int conversions -> get from harmony Long
- public CBuffer appendInt(int i) {
- // TODO: copy from harmony StringBuffer
- append(Integer.toString(i));
- return this;
- }
-
-
- public Appendable append(CharSequence cs) {
- if (cs instanceof CBuffer) {
- CBuffer src = (CBuffer) cs;
- append(src.value, src.start, src.end);
- } else if (cs instanceof String) {
- append((String) cs);
- } else {
- for (int i = 0; i < cs.length(); i++) {
- append(cs.charAt(i));
- }
- }
- return this;
- }
-
- public CBuffer append(CBuffer src) {
- append(src.value, src.start, src.end);
- return this;
- }
-
-
- public CBuffer append(BBucket bb) {
- byte[] bbuf = bb.array();
- int start = bb.position();
- appendAscii(bbuf, start, bb.remaining());
- return this;
- }
-
- public CBuffer appendAscii(byte[] bbuf, int start, int len) {
- makeSpace(len);
- char[] cbuf = value;
- for (int i = 0; i < len; i++) {
- cbuf[end + i] = (char) (bbuf[i + start] & 0xff);
- }
- end += len;
- return this;
- }
-
-
- public void toAscii(BBuffer bb) {
- for (int i = start; i < end; i++) {
- bb.append(value[i]);
- }
- }
-
- /**
- * Append and advance CharBuffer.
- *
- * @param c
- */
- public CBuffer put(CharBuffer c) {
- append(c.array(), c.position(), c.limit());
- c.position(c.limit());
- return this;
- }
-
- // ------------- 'set' methods ---------------
- // equivalent with clean + append
-
- public CBuffer set(CBuffer csq, int off, int len) {
- recycle();
- append(csq.value, csq.start + off, csq.start + off + len);
- return this;
- }
-
- public CBuffer setChars(char[] c, int off, int len) {
- recycle();
- append(c, off, off + len);
- return this;
- }
-
- public CBuffer set(BBucket bb) {
- recycle();
- byte[] bbuf = bb.array();
- int start = bb.position();
- appendAscii(bbuf, start, bb.remaining());
- return this;
- }
-
- public CBuffer set(CharSequence csq) {
- recycle();
- append(csq);
- return this;
- }
-
- public CBuffer set(CBuffer csq) {
- recycle();
- append(csq);
- return this;
- }
-
- public CBuffer set(String csq) {
- recycle();
- append(csq);
- return this;
- }
-
- private void dirty() {
- hash = 0;
- strValue = null;
- }
-
- /**
- * Make space for len chars. If len is small, allocate a reserve space too.
- * Never grow bigger than limit.
- */
- private void makeSpace(int count) {
- dirty();
- char[] tmp = null;
-
- int newSize;
- int desiredSize = end + count;
-
- if (value == null) {
- if (desiredSize < 256)
- desiredSize = 256; // take a minimum
- value = new char[desiredSize];
- }
-
- // limit < buf.length ( the buffer is already big )
- // or we already have space XXX
- if (desiredSize <= value.length) {
- return;
- }
- // grow in larger chunks
- if (desiredSize < 2 * value.length) {
- newSize = value.length * 2;
- tmp = new char[newSize];
- } else {
- newSize = value.length * 2 + count;
- tmp = new char[newSize];
- }
-
- System.arraycopy(value, 0, tmp, 0, end);
- value = tmp;
- tmp = null;
- }
-
- public void toLower() {
- for (int i = start; i < end; i++) {
- char c = value[i];
- if (c < 0x7F) {
- if (BBuffer.isUpper(c)) {
- value[i] = (char) BBuffer.toLower(c);
- }
-
- }
- }
- }
-
-
-}
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/lite/io/DumpChannel.java b/modules/tomcat-lite/java/org/apache/tomcat/lite/io/DumpChannel.java
deleted file mode 100644
index 31529fe..0000000
--- a/modules/tomcat-lite/java/org/apache/tomcat/lite/io/DumpChannel.java
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- */
-package org.apache.tomcat.lite.io;
-
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-
-// TODO: dump to a file, hex, etc.
-
-/**
- * For debug - will print all bytes that go trough the channel
- */
-public class DumpChannel extends IOChannel {
-
- IOBuffer in = new IOBuffer(this);
- IOBuffer out = new IOBuffer(this);
- static final boolean dumpToFile = false;
- static int idCnt = 0;
-
- DumpChannel(String id) {
- this.id = id + idCnt++;
- }
-
- public static IOChannel wrap(String id, IOChannel net) throws IOException {
- if (id == null) {
- id = "";
- }
- DumpChannel dmp = new DumpChannel(id + idCnt++);
- net.setHead(dmp);
- return dmp;
- }
-
- public String toString() {
- return "Dump-" + id + "-" + net.toString();
- }
-
- @Override
- public void handleReceived(IOChannel ch) throws IOException {
- processInput(ch.getIn());
- }
-
- private void processInput(IOBuffer netIn) throws IOException {
- boolean any = false;
- while (true) {
- BBucket first = netIn.popFirst();
- if (first == null) {
- if (netIn.isClosedAndEmpty()) {
- out("IN", first, true);
- in.close();
- any = true;
- }
- if (any) {
- sendHandleReceivedCallback();
- }
- return;
- }
- any = true;
- out("IN", first, false);
- if (!in.isAppendClosed()) {
- in.queue(first);
- }
- }
- }
-
- public void startSending() throws IOException {
- while (true) {
- BBucket first = out.popFirst();
- if (first == null) {
- if (out.isClosedAndEmpty()) {
- out("OUT", first, true);
- net.getOut().close();
- }
-
- net.startSending();
- return;
- }
- // Dump
- out("OUT", first, net.getOut().isAppendClosed());
- net.getOut().queue(first);
- }
- }
-
- static int did = 0;
-
- protected void out(String dir, BBucket first, boolean closed) {
- // Dump
- if (first != null) {
- String hd = Hex.getHexDump(first.array(), first.position(),
- first.remaining(), true);
- System.err.println("\n" + dir + ": " + id + " " +
- (closed ? "CLS" : "") +
- + first.remaining() + "\n" +
- hd);
- } else {
- System.err.println("\n" + dir + ": " + id + " " +
- (closed ? "CLS " : "") +
- "END\n");
- }
- if (dumpToFile && first != null) {
- try {
- OutputStream os = new FileOutputStream("dmp" + did++);
- os.write(first.array(), first.position(), first.remaining());
- os.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
-
- @Override
- public IOBuffer getIn() {
- return in;
- }
-
- @Override
- public IOBuffer getOut() {
- return out;
- }
-}
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/lite/io/FastHttpDateFormat.java b/modules/tomcat-lite/java/org/apache/tomcat/lite/io/FastHttpDateFormat.java
deleted file mode 100644
index f74b9b5..0000000
--- a/modules/tomcat-lite/java/org/apache/tomcat/lite/io/FastHttpDateFormat.java
+++ /dev/null
@@ -1,231 +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.lite.io;
-
-import java.text.DateFormat;
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-import java.util.Locale;
-import java.util.TimeZone;
-import java.util.concurrent.ConcurrentHashMap;
-
-/**
- * Utility class to generate HTTP dates.
- *
- * @author Remy Maucherat
- */
-public final class FastHttpDateFormat {
-
-
- // -------------------------------------------------------------- Variables
-
-
- protected static final int CACHE_SIZE =
- Integer.parseInt(System.getProperty("org.apache.tomcat.util.http.FastHttpDateFormat.CACHE_SIZE", "1000"));
-
-
- /**
- * HTTP date format.
- */
- protected static final SimpleDateFormat format =
- new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US);
-
-
- /**
- * The set of SimpleDateFormat formats to use in getDateHeader().
- */
- protected static final SimpleDateFormat formats[] = {
- new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US),
- new SimpleDateFormat("EEEEEE, dd-MMM-yy HH:mm:ss zzz", Locale.US),
- new SimpleDateFormat("EEE MMMM d HH:mm:ss yyyy", Locale.US)
- };
-
-
- protected final static TimeZone gmtZone = TimeZone.getTimeZone("GMT");
-
-
- /**
- * GMT timezone - all HTTP dates are on GMT
- */
- static {
-
- format.setTimeZone(gmtZone);
-
- formats[0].setTimeZone(gmtZone);
- formats[1].setTimeZone(gmtZone);
- formats[2].setTimeZone(gmtZone);
-
- }
-
-
- /**
- * Instant on which the currentDate object was generated.
- */
- protected static long currentDateGenerated = 0L;
-
-
- /**
- * Current formatted date.
- */
- protected static String currentDate = null;
-
-
- /**
- * Formatter cache.
- */
- protected static final ConcurrentHashMap<Long, String> formatCache =
- new ConcurrentHashMap<Long, String>(CACHE_SIZE);
-
-
- /**
- * Parser cache.
- */
- protected static final ConcurrentHashMap<String, Long> parseCache =
- new ConcurrentHashMap<String, Long>(CACHE_SIZE);
-
-
- // --------------------------------------------------------- Public Methods
-
-
- /**
- * Get the current date in HTTP format.
- */
- public static final String getCurrentDate() {
-
- long now = System.currentTimeMillis();
- if ((now - currentDateGenerated) > 1000) {
- synchronized (format) {
- if ((now - currentDateGenerated) > 1000) {
- currentDateGenerated = now;
- currentDate = format.format(new Date(now));
- }
- }
- }
- return currentDate;
-
- }
-
-
- /**
- * Get the HTTP format of the specified date.
- */
- public static final String formatDate
- (long value, DateFormat threadLocalformat) {
-
- Long longValue = new Long(value);
- String cachedDate = formatCache.get(longValue);
- if (cachedDate != null)
- return cachedDate;
-
- String newDate = null;
- Date dateValue = new Date(value);
- if (threadLocalformat != null) {
- newDate = threadLocalformat.format(dateValue);
- updateFormatCache(longValue, newDate);
- } else {
- synchronized (formatCache) {
- synchronized (format) {
- newDate = format.format(dateValue);
- }
- updateFormatCache(longValue, newDate);
- }
- }
- return newDate;
-
- }
-
-
- /**
- * Try to parse the given date as a HTTP date.
- */
- public static final long parseDate(String value,
- DateFormat[] threadLocalformats) {
-
- Long cachedDate = parseCache.get(value);
- if (cachedDate != null)
- return cachedDate.longValue();
-
- Long date = null;
- if (threadLocalformats != null) {
- date = internalParseDate(value, threadLocalformats);
- updateParseCache(value, date);
- } else {
- synchronized (parseCache) {
- date = internalParseDate(value, formats);
- updateParseCache(value, date);
- }
- }
- if (date == null) {
- return (-1L);
- } else {
- return date.longValue();
- }
-
- }
-
-
- /**
- * Parse date with given formatters.
- */
- private static final Long internalParseDate
- (String value, DateFormat[] formats) {
- Date date = null;
- for (int i = 0; (date == null) && (i < formats.length); i++) {
- try {
- date = formats[i].parse(value);
- } catch (ParseException e) {
- ;
- }
- }
- if (date == null) {
- return null;
- }
- return new Long(date.getTime());
- }
-
-
- /**
- * Update cache.
- */
- private static void updateFormatCache(Long key, String value) {
- if (value == null) {
- return;
- }
- if (formatCache.size() > CACHE_SIZE) {
- formatCache.clear();
- }
- formatCache.put(key, value);
- }
-
-
- /**
- * Update cache.
- */
- private static void updateParseCache(String key, Long value) {
- if (value == null) {
- return;
- }
- if (parseCache.size() > CACHE_SIZE) {
- parseCache.clear();
- }
- parseCache.put(key, value);
- }
-
-
-}
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/lite/io/FileConnector.java b/modules/tomcat-lite/java/org/apache/tomcat/lite/io/FileConnector.java
deleted file mode 100644
index f4dd93b..0000000
--- a/modules/tomcat-lite/java/org/apache/tomcat/lite/io/FileConnector.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- */
-package org.apache.tomcat.lite.io;
-
-
-/**
- * Initial abstraction for non-blocking File access and to
- * support other abstraction.
- *
- * Tomcat uses JNDI - but that's blocking, does lots of data copy,
- * is complex.
- *
- * Work in progress..
- */
-public abstract class FileConnector extends IOConnector {
-
- public static class FileInfo {
- String type;
- int mode;
- long size;
-
- }
-
- public abstract boolean isDirectory(String path);
-
- public abstract boolean isFile(String path);
-}
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/lite/io/FileConnectorJavaIo.java b/modules/tomcat-lite/java/org/apache/tomcat/lite/io/FileConnectorJavaIo.java
deleted file mode 100644
index 082c025..0000000
--- a/modules/tomcat-lite/java/org/apache/tomcat/lite/io/FileConnectorJavaIo.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- */
-package org.apache.tomcat.lite.io;
-
-import java.io.File;
-import java.io.IOException;
-
-
-/**
- * Catalina uses JNDI to abstract filesystem - this is both heavy and
- * a bit complex.
- *
- * This is also a bit complex - but hopefully we can implement it as
- * non-blocking and without much copy.
- *
- */
-public class FileConnectorJavaIo extends FileConnector {
- File base;
-
- public FileConnectorJavaIo(File file) {
- this.base = file;
- }
-
- @Override
- public boolean isDirectory(String path) {
- File file = new File(base, path);
- return file.isDirectory();
- }
-
- @Override
- public boolean isFile(String path) {
- File file = new File(base, path);
- return file.exists() && !file.isDirectory();
- }
-
- @Override
- public void acceptor(ConnectedCallback sc,
- CharSequence port,
- Object extra) throws IOException {
- // TODO: unix domain socket impl.
- // Maybe: detect new files in the filesystem ?
- }
-
- @Override
- public void connect(String host, int port, ConnectedCallback sc)
- throws IOException {
- }
-
-}
\ No newline at end of file
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/lite/io/FutureCallbacks.java b/modules/tomcat-lite/java/org/apache/tomcat/lite/io/FutureCallbacks.java
deleted file mode 100644
index 782228c..0000000
--- a/modules/tomcat-lite/java/org/apache/tomcat/lite/io/FutureCallbacks.java
+++ /dev/null
@@ -1,171 +0,0 @@
-/*
- */
-package org.apache.tomcat.lite.io;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.Future;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-import java.util.concurrent.locks.AbstractQueuedSynchronizer;
-
-
-
-/**
- * Support for blocking calls and callbacks.
- *
- * Unlike FutureTask, it is possible to reuse this and hopefully
- * easier to extends. Also has callbacks.
- *
- * @author Costin Manolache
- */
-public class FutureCallbacks<V> implements Future<V> {
-
- // Other options: ReentrantLock uses AbstractQueueSynchronizer,
- // more complex. Same for CountDownLatch
- // FutureTask - uses Sync as well, ugly interface with
- // Callable, can't be recycled.
- // Mina: simple object lock, doesn't extend java.util.concurent.Future
-
- private Sync sync = new Sync();
-
- private V value;
-
- public static interface Callback<V> {
- public void run(V param);
- }
-
- private List<Callback<V>> callbacks = new ArrayList();
-
- public FutureCallbacks() {
- }
-
- /**
- * Unlocks the object if it was locked. Should be called
- * when the object is reused.
- *
- * Callbacks will not be invoked.
- */
- public void reset() {
- sync.releaseShared(0);
- sync.reset();
- }
-
- public void recycle() {
- callbacks.clear();
- sync.releaseShared(0);
- sync.reset();
- }
-
- /**
- * Unlocks object and calls the callbacks.
- * @param v
- *
- * @throws IOException
- */
- public void signal(V v) throws IOException {
- sync.releaseShared(0);
- onSignal(v);
- }
-
- protected boolean isSignaled() {
- return true;
- }
-
- /**
- * Override to call specific callbacks
- */
- protected void onSignal(V v) {
- for (Callback<V> cb: callbacks) {
- if (cb != null) {
- cb.run(v);
- }
- }
- }
-
- /**
- * Set the response. Will cause the callback to be called and lock to be
- * released.
- *
- * @param value
- * @throws IOException
- */
- public void setValue(V value) throws IOException {
- synchronized (this) {
- this.value = value;
- signal(value);
- }
- }
-
- public void waitSignal(long to) throws IOException {
- try {
- get(to, TimeUnit.MILLISECONDS);
- } catch (InterruptedException e1) {
- throw new WrappedException(e1);
- } catch (TimeoutException e1) {
- throw new WrappedException(e1);
- } catch (ExecutionException e) {
- throw new WrappedException(e);
- }
- }
-
- @Override
- public V get() throws InterruptedException, ExecutionException {
- sync.acquireSharedInterruptibly(0);
- return value;
- }
-
- @Override
- public V get(long timeout, TimeUnit unit) throws InterruptedException,
- ExecutionException, TimeoutException {
- if (!sync.tryAcquireSharedNanos(0, unit.toNanos(timeout))) {
- throw new TimeoutException("Waiting " + timeout);
- }
- return value;
- }
-
-
- @Override
- public boolean cancel(boolean mayInterruptIfRunning) {
- return false;
- }
-
- @Override
- public boolean isCancelled() {
- return false;
- }
-
- @Override
- public boolean isDone() {
- return sync.isSignaled();
- }
-
- private class Sync extends AbstractQueuedSynchronizer {
-
- static final int DONE = 1;
- static final int BLOCKED = 0;
- Object result;
- Throwable t;
-
- @Override
- protected int tryAcquireShared(int ignore) {
- return getState() == DONE ? 1 : -1;
- }
-
- @Override
- protected boolean tryReleaseShared(int ignore) {
- setState(DONE);
- return true;
- }
-
- public void reset() {
- setState(BLOCKED);
- }
-
- boolean isSignaled() {
- return getState() == DONE;
- }
- }
-}
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/lite/io/Hex.java b/modules/tomcat-lite/java/org/apache/tomcat/lite/io/Hex.java
deleted file mode 100644
index 78a5fe2..0000000
--- a/modules/tomcat-lite/java/org/apache/tomcat/lite/io/Hex.java
+++ /dev/null
@@ -1,249 +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.lite.io;
-
-import java.io.ByteArrayOutputStream;
-
-/**
- * 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 Hex {
-
-
- // -------------------------------------------------------------- Constants
-
- /**
- * Table for HEX to DEC byte translation.
- */
- public static final int[] DEC = {
- -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,
- 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, -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, -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, -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,
- };
-
-
- /**
- * Table for DEC to HEX byte translation.
- */
- public static final byte[] HEX =
- { (byte) '0', (byte) '1', (byte) '2', (byte) '3', (byte) '4', (byte) '5',
- (byte) '6', (byte) '7', (byte) '8', (byte) '9', (byte) 'a', (byte) 'b',
- (byte) 'c', (byte) 'd', (byte) 'e', (byte) 'f' };
-
-
- // --------------------------------------------------------- Static Methods
-
-
- /**
- * Convert a String of hexadecimal digits into the corresponding
- * byte array by encoding each two hexadecimal digits as a byte.
- *
- * @param digits Hexadecimal digits representation
- *
- * @exception IllegalArgumentException if an invalid hexadecimal digit
- * is found, or the input string contains an odd number of hexadecimal
- * digits
- */
- public static byte[] convert(String digits) {
-
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- for (int i = 0; i < digits.length(); i += 2) {
- char c1 = digits.charAt(i);
- if ((i+1) >= digits.length())
- throw new IllegalArgumentException
- ("hexUtil.odd");
- char c2 = digits.charAt(i + 1);
- byte b = 0;
- if ((c1 >= '0') && (c1 <= '9'))
- b += ((c1 - '0') * 16);
- else if ((c1 >= 'a') && (c1 <= 'f'))
- b += ((c1 - 'a' + 10) * 16);
- else if ((c1 >= 'A') && (c1 <= 'F'))
- b += ((c1 - 'A' + 10) * 16);
- else
- throw new IllegalArgumentException
- ("hexUtil.bad");
- if ((c2 >= '0') && (c2 <= '9'))
- b += (c2 - '0');
- else if ((c2 >= 'a') && (c2 <= 'f'))
- b += (c2 - 'a' + 10);
- else if ((c2 >= 'A') && (c2 <= 'F'))
- b += (c2 - 'A' + 10);
- else
- throw new IllegalArgumentException
- ("hexUtil.bad");
- baos.write(b);
- }
- return (baos.toByteArray());
-
- }
-
-
- /**
- * Convert a byte array into a printable format containing a
- * String of hexadecimal digit characters (two per byte).
- *
- * @param bytes Byte array representation
- */
- public static String convert(byte bytes[]) {
-
- StringBuffer sb = new StringBuffer(bytes.length * 2);
- for (int i = 0; i < bytes.length; i++) {
- sb.append(convertDigit((bytes[i] >> 4)));
- sb.append(convertDigit((bytes[i] & 0x0f)));
- }
- return (sb.toString());
-
- }
-
-
- /**
- * Convert 4 hex digits to an int, and return the number of converted
- * bytes.
- *
- * @param hex Byte array containing exactly four hexadecimal digits
- *
- * @exception IllegalArgumentException if an invalid hexadecimal digit
- * is included
- */
- public static int convert2Int( byte[] hex ) {
- // Code from Ajp11, from Apache's JServ
-
- // assert b.length==4
- // assert valid data
- int len;
- if(hex.length < 4 ) return 0;
- if( DEC[hex[0]]<0 )
- throw new IllegalArgumentException("hexUtil.bad");
- len = DEC[hex[0]];
- len = len << 4;
- if( DEC[hex[1]]<0 )
- throw new IllegalArgumentException("hexUtil.bad");
- len += DEC[hex[1]];
- len = len << 4;
- if( DEC[hex[2]]<0 )
- throw new IllegalArgumentException("hexUtil.bad");
- len += DEC[hex[2]];
- len = len << 4;
- if( DEC[hex[3]]<0 )
- throw new IllegalArgumentException("hexUtil.bad");
- len += DEC[hex[3]];
- return len;
- }
-
-
-
- /**
- * Provide a mechanism for ensuring this class is loaded.
- */
- public static void load() {
- // Nothing to do
- }
-
- /**
- * [Private] Convert the specified value (0 .. 15) to the corresponding
- * hexadecimal digit.
- *
- * @param value Value to be converted
- */
- private static char convertDigit(int value) {
-
- value &= 0x0f;
- if (value >= 10)
- return ((char) (value - 10 + 'a'));
- else
- return ((char) (value + '0'));
-
- }
-
- /**
- * <code>getHexValue</code> displays a formatted hex
- * representation of the passed byte array. It also
- * allows for only a specified offset and length of
- * a particular array to be returned.
- *
- * @param bytes <code>byte[]</code> array to process.
- * @param pos offset to begin processing.
- * @param len number of bytes to process.
- * @return <code>String</code> formatted hex representation of processed
- * array.
- */
- public static String getHexDump(byte[] bytes, int pos, int len,
- boolean displayOffset) {
- StringBuffer out = new StringBuffer( len * 2 );
-
- for (int j = 0; j < len; j += 16) {
- hexLine(out, bytes, pos + j, pos + len, displayOffset);
- }
-
- return out.toString();
- }
-
- private static void hexLine(StringBuffer out,
- byte[] bytes, int start, int end,
- boolean displayOffset) {
-
- if ( displayOffset ) {
- out.append(convertDigit((int) (start >> 12)));
- out.append(convertDigit((int) (start >> 8)));
- out.append(convertDigit((int) (start >> 4)));
- out.append(convertDigit(start & 0x0F));
- out.append(": ");
- }
- for (int i = start; i < start + 16; i++) {
-
- if (i < end) {
- out.append(convertDigit((int) (bytes[i] >> 4)));
- out.append(convertDigit(bytes[i] & 0x0F));
- out.append(" ");
- } else {
- out.append(" ");
- }
- }
-
- out.append(" | ");
-
- for (int i = start; i < start + 16 && i < end; i++) {
- if( ! Character.isISOControl( (char)bytes[i] )) {
- out.append( new Character((char)bytes[i]) );
- } else {
- out.append( "." );
- }
- }
-
- out.append("\n");
- }
-}
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/lite/io/IOBuffer.java b/modules/tomcat-lite/java/org/apache/tomcat/lite/io/IOBuffer.java
deleted file mode 100644
index 04ac268..0000000
--- a/modules/tomcat-lite/java/org/apache/tomcat/lite/io/IOBuffer.java
+++ /dev/null
@@ -1,698 +0,0 @@
-/*
- */
-package org.apache.tomcat.lite.io;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.nio.ByteBuffer;
-import java.util.LinkedList;
-import java.util.logging.Logger;
-
-
-// TODO: append() will trigger callbacks - do it explicitely !!!
-// TODO: queue() shouldn't modify the buffer
-
-
-/**
- * A list of data buckets.
- *
- * @author Costin Manolache
- */
-public class IOBuffer {
- static Logger log = Logger.getLogger("IOBrigade");
-
- static int ALLOC_SIZE = 8192;
- long defaultTimeout = Long.MAX_VALUE;
-
- private LinkedList<BBucket> buffers = new LinkedList<BBucket>();
-
- // close() has been called for out,
- // or EOF/FIN received for in. It may still have data.
- boolean closeQueued;
-
- // Will be signalled (open) when there is data in the buffer.
- // also used to sync on.
- FutureCallbacks<IOBuffer> hasDataLock = new FutureCallbacks<IOBuffer>() {
- protected boolean isSignaled() {
- return hasData();
- }
- };
-
- // may be null
- protected IOChannel ch;
-
- // Support for appending - needs improvements.
- // appendable buffer is part of the buffer list if it has
- // data, and kept here if empty.
- BBuffer appendable;
- boolean appending = false;
- ByteBuffer writeBuffer;
-
-
- public IOBuffer() {
- }
-
- public IOBuffer(IOChannel ch) {
- this.ch = ch;
- }
-
- public IOChannel getChannel() {
- return ch;
- }
-
- // ===== Buffer access =====
-
-
- /**
- * Return first non-empty buffer.
- *
- * The append buffer is part of the buffer list, and is left alone and
- * empty.
- *
- * @return
- */
- public BBucket peekFirst() {
- synchronized (buffers) {
- BBucket o = (buffers.size() == 0) ? null : buffers.getFirst();
-
- while (true) {
- boolean empty = o == null || isEmpty(o);
- if (o == null) {
- //hasDataLock.reset();
- return null; // no data in buffers
- }
- // o != null
- if (empty) {
- buffers.removeFirst();
- o = (buffers.size() == 0) ? null : buffers.getFirst();
- } else {
- return o;
- }
- }
- }
- }
-
- public BBucket peekBucket(int idx) {
- synchronized (buffers) {
- return buffers.get(idx);
- }
- }
-
-
- public void advance(int len) {
- while (len > 0) {
- BBucket first = peekFirst();
- if (first == null) {
- return;
- }
- if (len > first.remaining()) {
- len -= first.remaining();
- first.position(first.limit());
- } else {
- first.position(first.position() + len);
- len = 0;
- }
- }
- }
-
- public void queue(String s) throws IOException {
- // TODO: decode with prober charset
- byte[] bytes = s.getBytes("UTF8");
- queueInternal(BBuffer.wrapper(bytes, 0, bytes.length));
- }
-
- public void queue(BBuffer bc) throws IOException {
- queueInternal(bc);
- }
-
- public void queue(Object bb) throws IOException {
- queueInternal(bb);
- }
-
- private void queueInternal(Object bb) throws IOException {
- if (closeQueued) {
- throw new IOException("Closed");
- }
- synchronized (buffers) {
- if (appending) {
- throw new RuntimeException("Unexpected queue while " +
- "appending");
- }
- BBucket add = wrap(bb);
- buffers.add(add);
- //log.info("QUEUED: " + add.remaining() + " " + this);
- notifyDataAvailable(add);
- }
-
- }
-
- public int getBufferCount() {
- peekFirst();
- synchronized (buffers) {
- return buffers.size();
- }
- }
-
- public void clear() {
- synchronized (buffers) {
- buffers.clear();
- }
- }
-
- public void recycle() {
- closeQueued = false;
- clear();
- // Normally unlocked
- hasDataLock.recycle();
-
- appending = false;
- appendable = null;
- }
-
- // ===================
- /**
- * Closed for append. It may still have data.
- * @return
- */
- public boolean isClosedAndEmpty() {
- return closeQueued && 0 == getBufferCount();
- }
-
-
- /**
- * Mark as closed - but will not send data.
- */
- public void close() throws IOException {
- if (closeQueued) {
- return;
- }
- closeQueued = true;
- notifyDataAvailable(null);
- }
-
-
- private boolean isEmpty(BBucket o) {
- if (o instanceof BBucket &&
- ((BBucket) o).remaining() == 0) {
- return true;
- }
- return false;
- }
-
- private BBucket wrap(Object src) {
- if (src instanceof byte[]) {
- return BBuffer.wrapper((byte[]) src, 0, ((byte[]) src).length);
- }
- if (src instanceof ByteBuffer) {
- //return src;
- ByteBuffer bb = (ByteBuffer) src;
- return BBuffer.wrapper(bb.array(), bb.position(),
- bb.remaining());
- }
- if (src instanceof byte[]) {
- byte[] bb = (byte[]) src;
- return BBuffer.wrapper(bb, 0, bb.length);
- }
- return (BBucket) src;
- }
-
- protected void notifyDataAvailable(Object bb) throws IOException {
- synchronized (hasDataLock) {
- hasDataLock.signal(this); // or bb ?
- }
- }
-
- public boolean hasData() {
- return closeQueued || peekFirst() != null;
- }
-
- public void waitData(long timeMs) throws IOException {
- if (timeMs == 0) {
- timeMs = defaultTimeout;
- }
- synchronized (hasDataLock) {
- if (hasData()) {
- return;
- }
- hasDataLock.reset();
- }
- hasDataLock.waitSignal(timeMs);
- }
-
-
- public boolean isAppendClosed() {
- return closeQueued;
- }
-
- // =================== Helper methods ==================
-
- /**
- * Non-blocking read.
- *
- * @return -1 if EOF, -2 if no data available, or 0..255 for normal read.
- */
- public int read() throws IOException {
- if (isClosedAndEmpty()) {
- return -1;
- }
- BBucket bucket = peekFirst();
- if (bucket == null) {
- return -2;
- }
- int res = bucket.array()[bucket.position()];
- bucket.position(bucket.position() + 1);
- return res & 0xFF;
- }
-
- public int peek() throws IOException {
- BBucket bucket = peekFirst();
- if (bucket == null) {
- return -1;
- }
- int res = bucket.array()[bucket.position()];
- return res;
- }
-
- public int find(char c) {
- int pos = 0;
- for (int i = 0; i < buffers.size(); i++) {
- BBucket bucket = buffers.get(i);
- if (bucket == null || bucket.remaining() == 0) {
- continue;
- }
- int found= BBuffer.findChar(bucket.array(), bucket.position(),
- bucket.limit(), c);
- if (found >= 0) {
- return pos + found;
- }
- pos += bucket.remaining();
- }
- return -1;
- }
-
- public int readLine(BBuffer bc) throws IOException {
- return readToDelim(bc, '\n');
- }
-
- /**
- * Copy up to and including "delim".
- *
- * @return number of bytes read, or -1 for end of stream.
- */
- int readToDelim(BBuffer bc, int delim) throws IOException {
- int len = 0;
- for (int idx = 0; idx < buffers.size(); idx++) {
- BBucket bucket = buffers.get(idx);
- if (bucket == null || bucket.remaining() == 0) {
- continue;
- }
- byte[] data = bucket.array();
- int end = bucket.limit();
- int start = bucket.position();
- for (int i = start; i < end; i++) {
- byte chr = data[i];
- bc.put(chr);
- if (chr == delim) {
- bucket.position(i + 1);
- len += (i - start + 1);
- return len;
- }
- }
- bucket.position(end); // empty - should be removed
- }
- if (len == 0 && isClosedAndEmpty()) {
- return -1;
- }
- return len;
- }
-
-
- public int write(ByteBuffer bb) throws IOException {
- int len = bb.remaining();
- int pos = bb.position();
- if (len == 0) {
- return 0;
- }
- append(bb);
- bb.position(pos + len);
- return len;
- }
-
- public int read(byte[] buf, int off, int len) throws IOException {
- if (isClosedAndEmpty()) {
- return -1;
- }
- int rd = 0;
- while (true) {
- BBucket bucket = peekFirst();
- if (bucket == null) {
- return rd;
- }
- int toCopy = Math.min(len, bucket.remaining());
- System.arraycopy(bucket.array(), bucket.position(),
- buf, off + rd, toCopy);
- bucket.position(bucket.position() + toCopy);
- rd += toCopy;
- len -= toCopy;
- if (len == 0) {
- return rd;
- }
- }
-
- }
-
- public int read(BBuffer bb, int len) throws IOException {
- bb.makeSpace(len);
- int rd = read(bb.array(), bb.limit(), len);
- if (rd < 0) {
- return rd;
- }
- bb.limit(bb.limit() + rd);
- return rd;
- }
-
- /**
- * Non-blocking read.
- */
- public int read(ByteBuffer bb) {
- if (isClosedAndEmpty()) {
- return -1;
- }
- int len = 0;
- while (true) {
- int space = bb.remaining(); // to append
- if (space == 0) {
- return len;
- }
- BBucket first = peekFirst();
- if (first == null) {
- return len;
- }
- BBucket iob = ((BBucket) first);
- if (space > iob.remaining()) {
- space = iob.remaining();
- }
- bb.put(iob.array(), iob.position(), space);
-
- iob.position(iob.position() + space);
- iob.release();
- len += space;
- }
- }
-
-
- public BBuffer readAll(BBuffer chunk) throws IOException {
- if (chunk == null) {
- chunk = allocate();
- }
- while (true) {
- if (isClosedAndEmpty()) {
- return chunk;
- }
- BBucket first = peekFirst();
- if (first == null) {
- return chunk;
- }
- BBucket iob = ((BBucket) first);
- chunk.append(iob.array(), iob.position(), iob.remaining());
- iob.position(iob.position() + iob.remaining());
- iob.release();
-
- }
- }
-
- private BBuffer allocate() {
- int size = 0;
- for (int i = 0; i < getBufferCount(); i++) {
- BBucket first = peekBucket(i);
- if (first != null) {
- size += first.remaining();
- }
- }
- return BBuffer.allocate(size);
- }
-
- public BBuffer copyAll(BBuffer chunk) throws IOException {
- if (chunk == null) {
- chunk = allocate();
- }
- for (int i = 0; i < getBufferCount(); i++) {
- BBucket iob = peekBucket(i);
- chunk.append(iob.array(), iob.position(), iob.remaining());
- }
- return chunk;
- }
-
- public IOBuffer append(InputStream is) throws IOException {
- while (true) {
- ByteBuffer bb = getWriteBuffer();
- int rd = is.read(bb.array(), bb.position(), bb.remaining());
- if (rd <= 0) {
- return this;
- }
- bb.position(bb.position() + rd);
- releaseWriteBuffer(rd);
- }
- }
-
- public IOBuffer append(BBuffer bc) throws IOException {
- return append(bc.array(), bc.getStart(), bc.getLength());
- }
-
- public IOBuffer append(byte[] data) throws IOException {
- return append(data, 0, data.length);
- }
-
- public IOBuffer append(byte[] data, int start, int len) throws IOException {
- if (closeQueued) {
- throw new IOException("Closed");
- }
- ByteBuffer bb = getWriteBuffer();
-
- int i = start;
- int end = start + len;
- while (i < end) {
- int rem = Math.min(end - i, bb.remaining());
- // to write
- bb.put(data, i, rem);
- i += rem;
- if (bb.remaining() < 8) {
- releaseWriteBuffer(1);
- bb = getWriteBuffer();
- }
- }
-
- releaseWriteBuffer(1);
- return this;
- }
-
- public IOBuffer append(int data) throws IOException {
- if (closeQueued) {
- throw new IOException("Closed");
- }
- ByteBuffer bb = getWriteBuffer();
- bb.put((byte) data);
- releaseWriteBuffer(1);
- return this;
- }
-
- public IOBuffer append(ByteBuffer cs) throws IOException {
- return append(cs.array(), cs.position() + cs.arrayOffset(),
- cs.remaining());
- }
-
- /**
- * Append a buffer. The buffer will not be modified.
- */
- public IOBuffer append(BBucket cs) throws IOException {
- append(cs.array(), cs.position(), cs.remaining());
- return this;
- }
-
- /**
- * Append a buffer. The buffer will not be modified.
- */
- public IOBuffer append(BBucket cs, int len) throws IOException {
- append(cs.array(), cs.position(), len);
- return this;
- }
-
- public IOBuffer append(IOBuffer cs) throws IOException {
- for (int i = 0; i < cs.getBufferCount(); i++) {
- BBucket o = cs.peekBucket(i);
- append(o);
- }
-
- return this;
- }
-
- public IOBuffer append(IOBuffer cs, int len) throws IOException {
- for (int i = 0; i < cs.getBufferCount(); i++) {
- BBucket o = cs.peekBucket(i);
- append(o);
- }
-
- return this;
- }
-
- public IOBuffer append(CharSequence cs) throws IOException {
- byte[] data = cs.toString().getBytes();
- append(data, 0, data.length);
- return this;
- }
-
- public IOBuffer append(char c) throws IOException {
- ByteBuffer bb = getWriteBuffer();
- bb.put((byte) c);
- releaseWriteBuffer(1);
- return this;
- }
-
- /**
- * All operations that iterate over buffers must be
- * sync
- * @return
- */
- public synchronized int available() {
- int a = 0;
- int cnt = buffers.size();
- for (int i = 0; i < cnt; i++) {
- a += buffers.get(i).remaining();
- }
- return a;
- }
-
- public String toString() {
- return "IOB:{c:" + getBufferCount() +
- ", b:" + available() +
- (isAppendClosed() ? ", C}" : " }");
- }
-
- public BBucket popLen(int lenToConsume) {
- BBucket o = peekFirst(); // skip empty
- if (o == null) {
- return null;
- }
- BBucket sb = BBuffer.wrapper(o.array(),
- o.position(), lenToConsume);
- o.position(o.position() + lenToConsume);
- return sb;
- }
-
- public BBucket popFirst() {
- BBucket o = peekFirst(); // skip empty
- if (o == null) {
- return null;
- }
- if (o == appendable) {
- synchronized (buffers) {
- // TODO: concurrency ???
- BBucket sb =
- BBuffer.wrapper(appendable.array(),
- appendable.position(),
- appendable.limit() - appendable.position());
- appendable.position(appendable.limit());
- return sb;
- }
- } else {
- buffers.removeFirst();
- }
- return o;
- }
-
-
- public ByteBuffer getWriteBuffer() throws IOException {
- synchronized (buffers) {
- if (closeQueued) {
- throw new IOException("Closed");
- }
- BBucket last = (buffers.size() == 0) ?
- null : buffers.getLast();
- if (last == null || last != appendable ||
- last.array().length - last.limit() < 16) {
- last = BBuffer.allocate(ALLOC_SIZE);
- }
- appending = true;
- appendable = (BBuffer) last;
-
- if (writeBuffer == null || writeBuffer.array() != appendable.array()) {
- writeBuffer = ByteBuffer.wrap(appendable.array());
- }
- writeBuffer.position(appendable.limit());
- writeBuffer.limit(appendable.array().length);
- return writeBuffer;
- }
- }
-
- public void releaseWriteBuffer(int read) throws IOException {
- synchronized (buffers) {
- if (!appending) {
- throw new IOException("Not appending");
- }
- if (writeBuffer != null) {
- if (appendable.limit() != writeBuffer.position()) {
- appendable.limit(writeBuffer.position());
- // We have some more data.
- if (buffers.size() == 0 ||
- buffers.getLast() != appendable) {
- buffers.add(appendable);
- }
- notifyDataAvailable(appendable);
- }
- }
- appending = false;
- }
- }
-
-
- // ------ More utilities - for parsing request ( later )-------
-// public final int skipBlank(ByteBuffer bb, int start) {
-// // Skipping blank lines
-// byte chr = 0;
-// do {
-// if (!bb.hasRemaining()) {
-// return -1;
-// }
-// chr = bb.get();
-// } while ((chr == HttpParser.CR) || (chr == HttpParser.LF));
-// return bb.position();
-//}
-
-//public final int readToDelimAndLowerCase(ByteBuffer bb,
-// byte delim,
-// boolean lower) {
-// boolean space = false;
-// byte chr = 0;
-// while (!space) {
-// if (!bb.hasRemaining()) {
-// return -1;
-// }
-// chr = bb.get();
-// if (chr == delim) {
-// space = true;
-// }
-// if (lower && (chr >= HttpParser.A) && (chr <= HttpParser.Z)) {
-// bb.put(bb.position() - 1,
-// (byte) (chr - HttpParser.LC_OFFSET));
-// }
-// }
-// return bb.position();
-//}
-
-//public boolean skipSpace(ByteBuffer bb) {
-// boolean space = true;
-// while (space) {
-// if (!bb.hasRemaining()) {
-// return false;
-// }
-// byte chr = bb.get();
-// if ((chr == HttpParser.SP) || (chr == HttpParser.HT)) {
-// //
-// } else {
-// space = false;
-// bb.position(bb.position() -1); // move back
-// }
-// }
-// return true;
-//}
-}
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/lite/io/IOChannel.java b/modules/tomcat-lite/java/org/apache/tomcat/lite/io/IOChannel.java
deleted file mode 100644
index a7db4df..0000000
--- a/modules/tomcat-lite/java/org/apache/tomcat/lite/io/IOChannel.java
+++ /dev/null
@@ -1,371 +0,0 @@
-/*
- */
-package org.apache.tomcat.lite.io;
-
-import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.nio.channels.ByteChannel;
-
-
-
-/**
- * Buffered, non-blocking ByteChannel.
- *
- * write() data will be added to the buffer. Call startSending() to
- * flush.
- *
- *
- *
- * - you can use it as a normal non-blocking ByteChannel.
- * - you can call getRead
- *
- * Very different from MINA IoFilters, also much lower level.
- *
- *
- * @author Costin Manolache
- */
-public abstract class IOChannel implements ByteChannel, IOConnector.DataReceivedCallback,
- IOConnector.DataFlushedCallback {
-
- /**
- * If this channel wraps another channel - for example a socket.
- * Will be null if this is the 'root' channel - a socket, memory.
- */
- protected IOChannel net;
-
- /**
- * Set with another channel layered on top of the current channel.
- */
- protected IOChannel head;
-
- protected String id;
-
- /**
- * A string that can be parsed to extract the target.
- * host:port for normal sockets
- */
- protected CharSequence target;
-
- /**
- * Connector that created the channel.
- */
- protected IOConnector connector;
-
- /**
- * Callbacks. Will be moved if a new head is inserted.
- */
- protected IOConnector.ConnectedCallback connectedCallback;
-
- /**
- * Will be called if any data is received.
- * Will also be called on close. Close with lastException set indicates
- * an error condition.
- */
- protected IOConnector.DataReceivedCallback dataReceivedCallback;
-
- /**
- * Out data is buffered, then sent with startSending.
- * This callback indicates the data has been sent. Can be used
- * to implement blocking flush.
- */
- protected IOConnector.DataFlushedCallback dataFlushedCallback;
-
- // Last activity timestamp.
- // TODO: update and use it ( placeholder )
- public long ts;
-
- /**
- * If an async exception happens.
- */
- protected Throwable lastException;
-
- protected IOChannel() {
- }
-
- public void setConnectedCallback(IOConnector.ConnectedCallback connectedCallback) {
- this.connectedCallback = connectedCallback;
- }
-
- public void setDataReceivedCallback(IOConnector.DataReceivedCallback dataReceivedCallback) {
- this.dataReceivedCallback = dataReceivedCallback;
- }
-
- /**
- * Callback called when the bottom ( OS ) channel has finished flushing.
- *
- * @param dataFlushedCallback
- */
- public void setDataFlushedCallback(IOConnector.DataFlushedCallback dataFlushedCallback) {
- this.dataFlushedCallback = dataFlushedCallback;
- }
-
- // Input
- public abstract IOBuffer getIn();
-
- // Output
- public abstract IOBuffer getOut();
-
-
- /**
- * From downstream ( NET ). Pass it to the next channel.
- */
- public void handleReceived(IOChannel net) throws IOException {
- sendHandleReceivedCallback();
- }
-
- /**
- * Called from lower layer (NET) when the last flush is
- * done and all buffers have been sent to OS ( or
- * intended recipient ).
- *
- * Will call the callback or next filter, may do additional
- * processing.
- *
- * @throws IOException
- */
- public void handleFlushed(IOChannel net) throws IOException {
- sendHandleFlushedCallback();
- }
-
- private void sendHandleFlushedCallback() throws IOException {
- try {
- if (dataFlushedCallback != null) {
- dataFlushedCallback.handleFlushed(this);
- }
- if (head != null) {
- head.handleFlushed(this);
- }
- } catch (Throwable t) {
- close();
- if (t instanceof IOException) {
- throw (IOException) t;
- } else {
- throw new WrappedException("Error in handleFlushed", t);
- }
- }
- }
-
-
- /**
- * Notify next channel or callback that data has been received.
- * Called after a lower channel gets more data ( in the IOThread
- * for example ).
- *
- * Also called when closed stream is detected. Can be called
- * to just force upper layers to check for data.
- */
- public void sendHandleReceivedCallback() throws IOException {
- try {
- if (dataReceivedCallback != null) {
- dataReceivedCallback.handleReceived(this);
- }
- if (head != null) {
- head.handleReceived(this);
- }
- } catch (Throwable t) {
- t.printStackTrace();
- try {
- close();
- } catch(Throwable t2) {
- t2.printStackTrace();
- }
- if (t instanceof IOException) {
- throw (IOException) t;
- } else {
- throw new WrappedException(t);
- }
- }
- }
-
- /**
- * Return last IO exception.
- *
- * The channel is async, exceptions can happen at any time.
- * The normal callback will be called ( connected, received ), it
- * should check if the channel is closed and the exception.
- */
- public Throwable lastException() {
- return lastException;
- }
-
- public void close() throws IOException {
- shutdownOutput();
- // Should it read the buffers ?
-
- if (getIn() == null || getIn().isAppendClosed()) {
- return;
- } else {
- getIn().close();
- sendHandleReceivedCallback();
- }
- getIn().hasDataLock.signal(getIn());
- }
-
- public boolean isOpen() {
- return getIn() != null &&
- getOut() != null &&
- !getIn().isAppendClosed() && !getOut().isAppendClosed();
- }
-
- public void shutdownOutput() throws IOException {
- if (getOut() == null || getOut().isAppendClosed()) {
- return;
- } else {
- getOut().close();
- startSending();
- }
- }
-
- public void setSink(IOChannel previous) throws IOException {
- this.net = previous;
- }
-
- public IOChannel getSink() {
- return net;
- }
-
- // Chaining/filtering
-
- /**
- * Called to add an filter after the current channel, for
- * example set SSL on top of a socket channel.
- *
- * The 'next' channel will have the received/flushed callbacks
- * of the current channel. The current channel's callbacks will
- * be reset.
- *
- * "Head" is from STREAMS.
- *
- * @throws IOException
- */
- public IOChannel setHead(IOChannel head) throws IOException {
- this.head = head;
- head.setSink(this);
-
- // TODO: do we want to migrate them automatically ?
- head.setDataReceivedCallback(dataReceivedCallback);
- head.setDataFlushedCallback(dataFlushedCallback);
- // app.setClosedCallback(closedCallback);
-
- dataReceivedCallback = null;
- dataFlushedCallback = null;
- return this;
- }
-
- public IOChannel getFirst() {
- IOChannel first = this;
- while (true) {
- if (!(first instanceof IOChannel)) {
- return first;
- }
- IOChannel before = ((IOChannel) first).getSink();
- if (before == null) {
- return first;
- } else {
- first = before;
- }
- }
- }
-
- // Socket support
-
- public void readInterest(boolean b) throws IOException {
- if (net != null) {
- net.readInterest(b);
- }
- }
-
- // Helpers
-
- public int read(ByteBuffer bb) throws IOException {
- return getIn().read(bb);
- }
-
- public int readNonBlocking(ByteBuffer bb) throws IOException {
- return getIn().read(bb);
- }
-
- public void waitFlush(long timeMs) throws IOException {
- return;
- }
-
- public int readBlocking(ByteBuffer bb, long timeMs) throws IOException {
- getIn().waitData(timeMs);
- return getIn().read(bb);
- }
-
- /**
- * Capture all output in a buffer.
- */
- public BBuffer readAll(BBuffer chunk, long to)
- throws IOException {
- if (chunk == null) {
- chunk = BBuffer.allocate();
- }
- while (true) {
- getIn().waitData(to);
- BBucket next = getIn().peekFirst();
- if (getIn().isClosedAndEmpty() && next == null) {
- return chunk;
- }
- if (next == null) {
- continue; // false positive
- }
- chunk.append(next.array(), next.position(), next.remaining());
- getIn().advance(next.remaining());
- }
- }
-
- public int write(ByteBuffer bb) throws IOException {
- return getOut().write(bb);
- }
-
- public void write(byte[] data) throws IOException {
- getOut().append(data, 0, data.length);
- }
-
- public void write(String string) throws IOException {
- write(string.getBytes());
- }
-
- /**
- * Send data in out to the intended recipient.
- * This is not blocking.
- */
- public abstract void startSending() throws IOException;
-
-
- public void setId(String id) {
- this.id = id;
- }
-
- public String getId() {
- return id;
- }
-
- public CharSequence getTarget() {
- if (net != null) {
- return net.getTarget();
- }
- return target;
- }
-
- public void setTarget(CharSequence target) {
- this.target = target;
- }
-
- public static final String ATT_REMOTE_HOSTNAME = "RemoteHostname";
- public static final String ATT_LOCAL_HOSTNAME = "LocalHostname";
- public static final String ATT_REMOTE_PORT = "RemotePort";
- public static final String ATT_LOCAL_PORT = "LocalPort";
- public static final String ATT_LOCAL_ADDRESS = "LocalAddress";
- public static final String ATT_REMOTE_ADDRESS = "RemoteAddress";
-
- public Object getAttribute(String name) {
- if (net != null) {
- return net.getAttribute(name);
- }
- return null;
- }
-
-}
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/lite/io/IOConnector.java b/modules/tomcat-lite/java/org/apache/tomcat/lite/io/IOConnector.java
deleted file mode 100644
index 4344146..0000000
--- a/modules/tomcat-lite/java/org/apache/tomcat/lite/io/IOConnector.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- */
-package org.apache.tomcat.lite.io;
-
-import java.io.IOException;
-import java.util.Timer;
-
-
-/**
- * Factory for IOChannels, with support for caching.
- *
- *
- * @author Costin Manolache
- */
-public abstract class IOConnector {
-
- public static interface DataReceivedCallback {
- /**
- * Called when data or EOF has been received.
- */
- public void handleReceived(IOChannel ch) throws IOException;
- }
-
- /**
- * Callback for accept and connect.
- *
- * Will also be called if an error happens while connecting, in
- * which case the connection will be closed.
- */
- public static interface ConnectedCallback {
- public void handleConnected(IOChannel ch) throws IOException;
- }
-
- public static interface DataFlushedCallback {
- public void handleFlushed(IOChannel ch) throws IOException;
- }
-
- protected Timer timer;
-
- public Timer getTimer() {
- return timer;
- }
-
- /**
- * If the connector is layered on top of a different connector,
- * return the lower layer ( for example the socket connector)
- */
- public IOConnector getNet() {
- return null;
- }
-
- public abstract void acceptor(IOConnector.ConnectedCallback sc,
- CharSequence port, Object extra)
- throws IOException;
-
- // TODO: failures ?
- // TODO: use String target or url
- public abstract void connect(String host, int port,
- IOConnector.ConnectedCallback sc) throws IOException;
-
- public void stop() {
- if (timer != null) {
- timer.cancel();
- }
- }
-}
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/lite/io/IOInputStream.java b/modules/tomcat-lite/java/org/apache/tomcat/lite/io/IOInputStream.java
deleted file mode 100644
index aebf5c5..0000000
--- a/modules/tomcat-lite/java/org/apache/tomcat/lite/io/IOInputStream.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- */
-package org.apache.tomcat.lite.io;
-
-import java.io.IOException;
-import java.io.InputStream;
-
-
-/**
- * Similar with ServletInputStream - adds readLine(byte[]..), using
- * a IOBuffer.
- *
- *
- *
- * @author Costin Manolache
- */
-public class IOInputStream extends InputStream {
-
- IOBuffer bb;
- long timeout;
-
- public IOInputStream(IOChannel httpCh, long to) {
- bb = httpCh.getIn();
- this.timeout = to;
- }
-
- @Override
- public int read() throws IOException {
- // getReadableBucket/peekFirst returns a buffer with at least
- // 1 byte in it.
- if (bb.isClosedAndEmpty()) {
- return -1;
- }
- bb.waitData(timeout);
- if (bb.isClosedAndEmpty()) {
- return -1;
- }
-
- return bb.read();
- }
-
- public int read(byte[] buf, int off, int len) throws IOException {
- if (bb.isClosedAndEmpty()) {
- return -1;
- }
- bb.waitData(timeout);
- if (bb.isClosedAndEmpty()) {
- return -1;
- }
- return bb.read(buf, off, len);
- }
-
- /**
- * Servlet-style read line: terminator is \n or \r\n, left in buffer.
- */
- public int readLine(byte[] b, int off, int len) throws IOException {
- if (len <= 0) {
- return 0;
- }
- int count = 0, c;
-
- while ((c = read()) != -1) {
- b[off++] = (byte)c;
- count++;
- if (c == '\n' || count == len) {
- break;
- }
- }
- return count > 0 ? count : -1;
- }
-}
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/lite/io/IOOutputStream.java b/modules/tomcat-lite/java/org/apache/tomcat/lite/io/IOOutputStream.java
deleted file mode 100644
index 90a6e02..0000000
--- a/modules/tomcat-lite/java/org/apache/tomcat/lite/io/IOOutputStream.java
+++ /dev/null
@@ -1,204 +0,0 @@
-/*
- */
-package org.apache.tomcat.lite.io;
-
-import java.io.CharConversionException;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.nio.ByteBuffer;
-import java.text.MessageFormat;
-
-/**
- * Same methods with ServletOutputStream.
- *
- * There is no restriction in using the Writer and InputStream at the
- * same time - the servlet layer will impose it for compat. You can also use
- * IOBuffer directly.
- *
- * If you mix stream and writer:
- * - call BufferWriter.push() to make sure all chars are sent down
- * - the BufferOutputStream doesn't cache any data, all goes to the
- * IOBuffer.
- * - flush() on BufferOutputStream and BufferWriter will send the data
- * to the network and block until it gets to the socket ( so it can
- * throw exception ).
- * - You can also use non-blocking flush methods in IOBuffer, and a
- * callback if you want to know when the write was completed.
- *
- * @author Costin Manolache
- */
-public class IOOutputStream extends OutputStream {
-
- IOBuffer bb;
- IOChannel ch;
- int bufferSize = 8 * 1024;
-
- int wSinceFlush = 0;
-
- public IOOutputStream(IOBuffer out, IOChannel httpMessage) {
- this.bb = out;
- ch = httpMessage;
- }
-
- public void recycle() {
- wSinceFlush = 0;
- bufferSize = 8 * 1024;
- }
-
- public void reset() {
- wSinceFlush = 0;
- bb.clear();
- }
-
- public int getWrittenSinceFlush() {
- return wSinceFlush;
- }
-
-
- public int getBufferSize() {
- return bufferSize;
- }
-
- public void setBufferSize(int size) {
- if (size > bufferSize) {
- bufferSize = size;
- }
- }
-
- private void updateSize(int cnt) throws IOException {
- wSinceFlush += cnt;
- if (wSinceFlush > bufferSize) {
- flush();
- }
- }
-
- @Override
- public void write(int b) throws IOException {
- bb.append((char) b);
- updateSize(1);
- }
-
- @Override
- public void write(byte data[]) throws IOException {
- write(data, 0, data.length);
- }
-
- @Override
- public void write(byte data[], int start, int len) throws IOException {
- bb.append(data, start, len);
- updateSize(len);
- }
-
- public void flush() throws IOException {
- if (ch != null) {
- ch.startSending();
-
- ch.waitFlush(Long.MAX_VALUE);
- }
- wSinceFlush = 0;
- }
-
- public void close() throws IOException {
- flush();
- bb.close();
- }
-
-
- public void write(ByteBuffer source) throws IOException {
- write(source.array(), source.position(), source.remaining());
- source.position(source.limit());
- }
-
- public void print(String s) throws IOException {
- if (s==null) s="null";
- int len = s.length();
- for (int i = 0; i < len; i++) {
- char c = s.charAt (i);
-
- //
- // XXX NOTE: This is clearly incorrect for many strings,
- // but is the only consistent approach within the current
- // servlet framework. It must suffice until servlet output
- // streams properly encode their output.
- //
- if ((c & 0xff00) != 0) { // high order byte must be zero
- String errMsg = "Not ISO-8859-1";
- Object[] errArgs = new Object[1];
- errArgs[0] = new Character(c);
- errMsg = MessageFormat.format(errMsg, errArgs);
- throw new CharConversionException(errMsg);
- }
- write (c);
- }
- }
-
-
- public void print(boolean b) throws IOException {
- String msg;
- if (b) {
- msg = "true";
- } else {
- msg = "false";
- }
- print(msg);
- }
-
- public void print(char c) throws IOException {
- print(String.valueOf(c));
- }
-
- public void print(int i) throws IOException {
- print(String.valueOf(i));
- }
-
- public void print(long l) throws IOException {
- print(String.valueOf(l));
- }
-
- public void print(float f) throws IOException {
- print(String.valueOf(f));
- }
-
- public void print(double d) throws IOException {
- print(String.valueOf(d));
- }
-
- public void println() throws IOException {
- print("\r\n");
- }
-
- public void println(String s) throws IOException {
- print(s);
- println();
- }
-
- public void println(boolean b) throws IOException {
- print(b);
- println();
- }
-
- public void println(char c) throws IOException {
- print(c);
- println();
- }
-
- public void println(int i) throws IOException {
- print(i);
- println();
- }
-
- public void println(long l) throws IOException {
- print(l);
- println();
- }
-
- public void println(float f) throws IOException {
- print(f);
- println();
- }
-
- public void println(double d) throws IOException {
- print(d);
- println();
- }
-}
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/lite/io/IOReader.java b/modules/tomcat-lite/java/org/apache/tomcat/lite/io/IOReader.java
deleted file mode 100644
index bbeacdc..0000000
--- a/modules/tomcat-lite/java/org/apache/tomcat/lite/io/IOReader.java
+++ /dev/null
@@ -1,236 +0,0 @@
-/*
- */
-package org.apache.tomcat.lite.io;
-
-import java.io.IOException;
-import java.io.Reader;
-import java.nio.ByteBuffer;
-import java.nio.CharBuffer;
-import java.nio.charset.Charset;
-import java.nio.charset.CharsetDecoder;
-import java.nio.charset.CoderResult;
-import java.nio.charset.CodingErrorAction;
-import java.nio.charset.MalformedInputException;
-import java.nio.charset.UnmappableCharacterException;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.concurrent.atomic.AtomicInteger;
-
-
-/**
- * Conversion from Bytes to Chars and support for decoding.
- *
- * Replaces tomcat B2CConverter with NIO equivalent. B2CConverter was a hack
- * (re)using an dummy InputStream backed by a ByteChunk.
- *
- * @author Costin Manolache
- */
-public class IOReader extends Reader {
-
- IOBuffer iob;
- Map<String, CharsetDecoder> decoders = new HashMap<String, CharsetDecoder>();
- CharsetDecoder decoder;
-
- private static boolean REUSE = true;
- String enc;
- private boolean closed;
- public static final String DEFAULT_ENCODING = "ISO-8859-1";
- long timeout = 0;
-
- public IOReader(IOBuffer iob) {
- this.iob = iob;
- }
-
- public void setTimeout(long to) {
- timeout = to;
- }
-
- public void setEncoding(String charset) {
- enc = charset;
- if (enc == null) {
- enc = DEFAULT_ENCODING;
- }
- decoder = REUSE ? decoders.get(enc) : null;
- if (decoder == null) {
- decoder = Charset.forName(enc).newDecoder()
- .onMalformedInput(CodingErrorAction.REPLACE)
- .onUnmappableCharacter(CodingErrorAction.REPLACE);
- if (REUSE) {
- decoders.put(enc, decoder);
- }
- }
- }
-
- public String getEncoding() {
- return enc;
- }
-
- public void recycle() {
- if (decoder != null) {
- decoder.reset();
- }
- closed = false;
- enc = null;
- }
-
- private void checkClosed() throws IOException {
- if (closed) throw new IOException("closed");
- }
-
- public boolean ready() {
- return iob.peekFirst() != null;
- }
-
- public int read(java.nio.CharBuffer target) throws IOException {
- int len = target.remaining();
- char[] cbuf = new char[len];
- int n = read(cbuf, 0, len);
- if (n > 0)
- target.put(cbuf, 0, n);
- return n;
- }
-
- public int read() throws IOException {
- char cb[] = new char[1];
- if (read(cb, 0, 1) == -1)
- return -1;
- else
- return cb[0];
- }
-
- @Override
- public void close() throws IOException {
- closed = true;
- iob.close();
- }
-
- /**
- * Used if a bucket ends on a char boundary
- */
- BBuffer underFlowBuffer = BBuffer.allocate(10);
- public static AtomicInteger underFlows = new AtomicInteger();
-
- /**
- * Decode all bytes - for example a URL or header.
- */
- public void decodeAll(BBucket bb, CBuffer c) {
-
- while (bb.hasRemaining()) {
- CharBuffer charBuffer = c.getAppendCharBuffer();
- CoderResult res = decode1(bb, charBuffer, true);
- c.returnAppendCharBuffer(charBuffer);
- if (res != CoderResult.OVERFLOW) {
- if (res == CoderResult.UNDERFLOW || bb.hasRemaining()) {
- System.err.println("Ignored trailing bytes " + bb.remaining());
- }
- return;
- }
- }
-
- }
-
- /**
- * Do one decode pass.
- */
- public CoderResult decode1(BBucket bb, CharBuffer c, boolean eof) {
- ByteBuffer b = bb.getByteBuffer();
-
- if (underFlowBuffer.hasRemaining()) {
- // Need to get rid of the underFlow first
- for (int i = 0; i < 10; i++) {
- underFlowBuffer.put(b.get());
- bb.position(b.position());
- ByteBuffer ub = underFlowBuffer.getByteBuffer();
- CoderResult res = decoder.decode(ub, c, eof);
- if (! ub.hasRemaining()) {
- // underflow resolved
- break;
- }
- if (res == CoderResult.OVERFLOW) {
- return res;
- }
- }
- if (underFlowBuffer.hasRemaining()) {
- throw new RuntimeException("Can't resolve underflow after " +
- "10 bytes");
- }
- }
-
- CoderResult res = decoder.decode(b, c, eof);
- bb.position(b.position());
-
- if (res == CoderResult.UNDERFLOW && bb.hasRemaining()) {
- // b ends on a boundary
- underFlowBuffer.append(bb.array(), bb.position(), bb.remaining());
- bb.position(bb.limit());
- }
- return res;
- }
-
- @Override
- public int read(char[] cbuf, int offset, int length) throws IOException {
- checkClosed();
- if (length == 0) {
- return 0;
- }
- // we can either allocate a new CharBuffer or use a
- // static one and copy. Seems simpler this way - needs some
- // load test, but InputStreamReader seems to do the same.
- CharBuffer out = CharBuffer.wrap(cbuf, offset, length);
-
- CoderResult result = CoderResult.UNDERFLOW;
-
- BBucket bucket = iob.peekFirst();
-
- // Consume as much as possible without blocking
- while (result == CoderResult.UNDERFLOW) {
- // fill the buffer if needed
- if (bucket == null || ! bucket.hasRemaining()) {
- if (out.position() > offset) {
- // we could return the result without blocking read
- break;
- }
- bucket = null;
- while (bucket == null) {
- iob.waitData(timeout);
- bucket = iob.peekFirst();
- if (bucket == null && iob.isClosedAndEmpty()) {
- // EOF, we couldn't decode anything
- break;
- }
- }
-
- if (bucket == null) {
- // eof
- break;
- }
- }
-
- result = decode1(bucket, out, false);
- }
-
- if (result == CoderResult.UNDERFLOW && iob.isClosedAndEmpty()) {
- // Flush out any remaining data
- ByteBuffer bytes = bucket == null ?
- underFlowBuffer.getByteBuffer() : bucket.getByteBuffer();
- result = decoder.decode(bytes, out, true);
- if (bucket == null) {
- underFlowBuffer.position(bytes.position());
- } else {
- bucket.position(bytes.position());
- }
-
- decoder.flush(out);
- decoder.reset();
- }
-
- if (result.isMalformed()) {
- throw new MalformedInputException(result.length());
- } else if (result.isUnmappable()) {
- throw new UnmappableCharacterException(result.length());
- }
-
- int rd = out.position() - offset;
- return rd == 0 ? -1 : rd;
- }
-}
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/lite/io/IOWriter.java b/modules/tomcat-lite/java/org/apache/tomcat/lite/io/IOWriter.java
deleted file mode 100644
index 7d06cf1..0000000
--- a/modules/tomcat-lite/java/org/apache/tomcat/lite/io/IOWriter.java
+++ /dev/null
@@ -1,212 +0,0 @@
-/*
- */
-package org.apache.tomcat.lite.io;
-
-import java.io.IOException;
-import java.io.Writer;
-import java.nio.ByteBuffer;
-import java.nio.CharBuffer;
-import java.nio.charset.Charset;
-import java.nio.charset.CharsetEncoder;
-import java.nio.charset.CoderResult;
-import java.nio.charset.CodingErrorAction;
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * Converts chars to bytes, and associated encoding.
- *
- * Replaces C2B from old tomcat.
- *
- * @author Costin Manolache
- */
-public class IOWriter extends Writer {
-
- IOBuffer iob;
- Map<String, CharsetEncoder> encoders = new HashMap<String, CharsetEncoder>();
- CharsetEncoder encoder;
-
- private static boolean REUSE = true;
- String enc;
- private boolean closed;
- IOChannel ioCh;
-
- public IOWriter(IOChannel iob) {
- this.ioCh = iob;
- if (iob != null) {
- this.iob = iob.getOut();
- }
- }
-
- public void setEncoding(String charset) {
- if (charset == null) {
- charset = "UTF-8";
- }
- enc = charset;
- encoder = getEncoder(charset);
- if (encoder == null) {
- encoder = Charset.forName(charset).newEncoder()
- .onMalformedInput(CodingErrorAction.REPLACE)
- .onUnmappableCharacter(CodingErrorAction.REPLACE);
- if (REUSE) {
- encoders.put(charset, encoder);
- }
- }
- }
-
- CharsetEncoder getEncoder(String charset) {
- if (charset == null) {
- charset = "UTF-8";
- }
- encoder = REUSE ? encoders.get(charset) : null;
- if (encoder == null) {
- encoder = Charset.forName(charset).newEncoder()
- .onMalformedInput(CodingErrorAction.REPLACE)
- .onUnmappableCharacter(CodingErrorAction.REPLACE);
- if (REUSE) {
- encoders.put(charset, encoder);
- }
- }
- return encoder;
- }
-
- public String getEncoding() {
- return enc;
- }
-
- public void recycle() {
- if (encoder != null) {
- encoder.reset();
- }
- closed = false;
- enc = null;
- }
-
-
- private void checkClosed() throws IOException {
- if (closed) throw new IOException("closed");
- }
-
- @Override
- public void close() throws IOException {
- closed = true;
- // flush the buffer ?
- ByteBuffer out = iob.getWriteBuffer();
- encoder.flush(out);
- iob.releaseWriteBuffer(1);
-
- iob.close();
- }
-
- /**
- * Used if a bucket ends on a char boundary
- */
- CBuffer underFlowBuffer = CBuffer.newInstance();
-
- public void encode1(CBuffer cc,
- BBuffer bb, CharsetEncoder encoder, boolean eof) {
- CharBuffer c = cc.getNioBuffer();
- ByteBuffer b = bb.getWriteByteBuffer(c.remaining() * 2);
- encode1(c, b, encoder, eof);
- cc.returnNioBuffer(c);
- bb.limit(b.position());
- }
-
- /**
- *
- * @param cc
- * @return
- */
- public void encode1(CharBuffer c,
- ByteBuffer b, CharsetEncoder encoder, boolean eof) {
-
- // TODO: buffer growth in caller
-
- CoderResult res = encoder.encode(c, b, eof);
- if (res == CoderResult.OVERFLOW) {
- // bb is full - next call will get a larger buffer ( it
- // grows ) or maybe will be flushed.
- }
- if (res == CoderResult.UNDERFLOW && c.remaining() > 0 && !eof) {
- // TODO: if eof -> exception ?
- // cc has remaining chars - for example a surrogate start.
- underFlowBuffer.put(c);
- }
-
- }
-
- public void encodeAll(CBuffer cc,
- BBuffer bb, CharsetEncoder encoder, boolean eof) {
- while (cc.length() > 0) {
- encode1(cc, bb, encoder, eof);
- }
- }
-
- public void encodeAll(CBuffer cc,
- BBuffer bb, String cs) {
- encodeAll(cc, bb, getEncoder(cs), true);
- }
-
- @Override
- public void flush() throws IOException {
- if (ioCh != null) {
- ioCh.startSending();
- }
- }
-
- // TODO: use it for utf-8
- public static int char2utf8(byte[] ba, int off, char c, char c1) {
- int i = 0;
- if (c < 0x80) {
- ba[off++] = (byte) (c & 0xFF);
- return 1;
- } else if (c < 0x800)
- {
- ba[off++] = (byte) (0xC0 | c >> 6);
- ba[off++] = (byte) (0x80 | c & 0x3F);
- return 2;
- }
- else if (c < 0x10000)
- {
- ba[off++] = (byte) ((0xE0 | c >> 12));
- ba[off++] = (byte) ((0x80 | c >> 6 & 0x3F));
- ba[off++] = (byte) ((0x80 | c & 0x3F));
- return 3;
- }
- else if (c < 0x200000)
- {
- ba[off++] = (byte) ((0xF0 | c >> 18));
- ba[off++] = (byte) ((0x80 | c >> 12 & 0x3F));
- ba[off++] = (byte) ((0x80 | c >> 6 & 0x3F));
- ba[off++] = (byte) ((0x80 | c & 0x3F));
- return 4;
- }
-
-
- return i;
- }
-
-
- /**
- * Just send the chars to the byte[], without flushing down.
- *
- * @throws IOException
- */
- public void push() throws IOException {
- // we don't cache here.
- }
-
- @Override
- public void write(char[] cbuf, int off, int len) throws IOException {
- checkClosed();
- CharBuffer cb = CharBuffer.wrap(cbuf, off, len);
-
- while (cb.remaining() > 0) {
- ByteBuffer wb = iob.getWriteBuffer();
- encode1(cb, wb, encoder, false);
- iob.releaseWriteBuffer(1);
- }
- }
-
-
-}
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/lite/io/MemoryIOConnector.java b/modules/tomcat-lite/java/org/apache/tomcat/lite/io/MemoryIOConnector.java
deleted file mode 100644
index 75fcc3b..0000000
--- a/modules/tomcat-lite/java/org/apache/tomcat/lite/io/MemoryIOConnector.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- */
-package org.apache.tomcat.lite.io;
-
-import java.io.IOException;
-import java.util.Timer;
-
-public class MemoryIOConnector extends IOConnector {
-
- public static class MemoryIOChannel extends IOChannel {
- IOBuffer netIn = new IOBuffer(this) {
- protected void notifyDataAvailable(Object bb) throws IOException {
- sendHandleReceivedCallback();
- super.notifyDataAvailable(bb);
- }
- };
- IOBuffer netOut = new IOBuffer(this);
-
- /**
- * All flushed output will be saved to 'out'.
- */
- public BBuffer out = BBuffer.allocate(4096);
-
- public MemoryIOChannel() {
- }
-
- public void startSending() throws IOException {
- //
- IOBuffer bb = netOut;
- while (true) {
- if (bb.isClosedAndEmpty()) {
- break;
- }
- BBucket first = bb.peekFirst();
- if (first == null) {
- break;
- }
- BBucket iob = ((BBucket) first);
- out.append(iob.array(), iob.position(), iob.remaining());
- bb.advance(iob.remaining());
- iob.release();
- }
-
- handleFlushed(this);
- }
-
- @Override
- public IOBuffer getIn() {
- return netIn;
- }
- @Override
- public IOBuffer getOut() {
- return netOut;
- }
- }
-
- // TODO: in-process communication without sockets for testing
- ConnectedCallback acceptor;
- MemoryIOConnector server;
-
- public MemoryIOConnector() {
- timer = new Timer(true);
- }
-
- public MemoryIOConnector withServer(MemoryIOConnector server) {
- this.server = server;
- return server;
- }
-
- @Override
- public void acceptor(ConnectedCallback sc, CharSequence port, Object extra)
- throws IOException {
- this.acceptor = sc;
- }
-
- @Override
- public void connect(String host, int port, ConnectedCallback sc)
- throws IOException {
- IOChannel ch = new MemoryIOChannel();
- IOChannel sch = new MemoryIOChannel();
- // TODO: mix
- if (server != null && server.acceptor != null) {
- server.acceptor.handleConnected(sch);
- }
- sc.handleConnected(ch);
- }
-
-}
\ No newline at end of file
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/lite/io/NioChannel.java b/modules/tomcat-lite/java/org/apache/tomcat/lite/io/NioChannel.java
deleted file mode 100644
index 7cc1408..0000000
--- a/modules/tomcat-lite/java/org/apache/tomcat/lite/io/NioChannel.java
+++ /dev/null
@@ -1,198 +0,0 @@
-package org.apache.tomcat.lite.io;
-
-import java.io.IOException;
-import java.net.InetAddress;
-import java.nio.ByteBuffer;
-import java.nio.channels.ByteChannel;
-import java.nio.channels.Channel;
-
-
-/**
- * Wrapper around the real channel, with selector-specific info.
- *
- * It is stored as an attachment in the selector.
- */
-public class NioChannel implements ByteChannel {
-
- public static interface NioChannelCallback {
- public void handleConnected(NioChannel ch) throws IOException;
- public void handleClosed(NioChannel ch) throws IOException;
- public void handleReadable(NioChannel ch) throws IOException;
- public void handleWriteable(NioChannel ch) throws IOException;
-
- }
-
- NioChannel(NioThread sel) {
- this.sel = sel;
- }
-
- // APR long is wrapped in a ByteChannel as well - with few other longs.
- Channel channel;
-
- // sync access.
- Object selKey;
-
- NioThread sel;
-
- /**
- * If != 0 - the callback will be notified closely after this time.
- * Used for timeouts.
- */
- long nextTimeEvent = 0;
-
- // Callbacks
- Runnable timeEvent;
-
- NioChannelCallback callback;
-
-
- Throwable lastException;
-
- // True if the callback wants to be notified of read/write
- boolean writeInterest;
- boolean readInterest;
-
- // shutdownOutput has been called ?
- private boolean outClosed = false;
-
- // read() returned -1 OR input buffer closed ( no longer interested )
- boolean inClosed = false;
-
- // Saved to allow debug messages for bad interest/looping
- int lastReadResult;
- int zeroReads = 0;
- int lastWriteResult;
-
- protected NioChannel() {
-
- }
-
- public NioThread getSelectorThread() {
- return sel;
- }
-
- public String toString() {
- StringBuffer sb = new StringBuffer();
- sb.append("SelData/")
- .append(writeInterest ? "W/" : "")
- .append(readInterest ? "R/" : "")
- .append(outClosed ? "Out-CLOSE/" : "")
- .append(inClosed ? "In-CLOSE/" : "")
- .append("/")
- .append(channel.toString());
-
- return sb.toString();
- }
-
- public Channel getChannel() {
- return channel;
- }
-
- public boolean isOpen() {
- // in and out open
- return channel.isOpen() && !outClosed && !inClosed;
- }
-
- public int read(ByteBuffer bb) throws IOException {
- return sel.readNonBlocking(this, bb);
- }
-
- public int write(ByteBuffer bb) throws IOException {
- return sel.writeNonBlocking(this, bb);
- }
-
- public void readInterest(boolean b) throws IOException {
- sel.readInterest(this, b);
- }
-
- public void writeInterest() throws IOException {
- sel.writeInterest(this);
- }
-
- public InetAddress getAddress(boolean remote) {
- return sel.getAddress(this, remote);
- }
-
- public int getPort(boolean remote) {
- return sel.getPort(this, remote);
- }
-
- /**
- * Run in selector thread.
- */
- public void runInSelectorThread(Runnable t) throws IOException {
- sel.runInSelectorThread(t);
- }
-
- /**
- * Request a timer event. The thread will generate the events at
- * a configurable interval - for example no more often than 0.5 sec.
- */
- public void setTimer(long timeMs, Runnable cb) {
- this.nextTimeEvent = timeMs;
- this.timeEvent = cb;
- }
-
- /**
- * shutdown out + in
- * If there is still data in the input buffer - RST will be sent
- * instead of FIN.
- *
- *
- * The proper way to close a connection is to shutdownOutput() first,
- * wait until read() return -1, then call close().
- *
- * If read() returns -1, you need to finish sending, call shutdownOutput()
- * than close.
- * If read() returns -1 and there is an error - call close()
- * directly.
- *
- */
- @Override
- public void close() throws IOException {
- shutdownOutput();
- inputClosed();
- }
-
- /**
- * Send TCP close(FIN). HTTP uses this to transmit end of body. The other end
- * detects this with a '-1' in read().
- *
- * All other forms of close() are reported as exceptions in read().
- *
- * @throws IOException
- */
- public void shutdownOutput() throws IOException {
- synchronized (channel) {
- if (!outClosed) {
- outClosed = true;
- try {
- sel.shutdownOutput(this);
- } catch (IOException ex) {
- // ignore
- }
- }
- if (inClosed) {
- sel.close(this, null);
- }
- }
- }
-
- void inputClosed() throws IOException {
- synchronized (channel) {
- if (inClosed) {
- // already closed
- return;
- }
- inClosed = true; // detected end
- if (outClosed) {
- sel.close(this, null);
- } else {
- // Don't close the channel - write may still work ?
- readInterest(false);
- }
- }
- }
-
- boolean closeCalled = false;
-}
\ No newline at end of file
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/lite/io/NioThread.java b/modules/tomcat-lite/java/org/apache/tomcat/lite/io/NioThread.java
deleted file mode 100644
index 9ec80be..0000000
--- a/modules/tomcat-lite/java/org/apache/tomcat/lite/io/NioThread.java
+++ /dev/null
@@ -1,1154 +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.lite.io;
-
-import java.io.IOException;
-import java.net.InetAddress;
-import java.net.InetSocketAddress;
-import java.net.ServerSocket;
-import java.net.Socket;
-import java.net.SocketAddress;
-import java.net.SocketException;
-import java.nio.ByteBuffer;
-import java.nio.channels.ByteChannel;
-import java.nio.channels.Channel;
-import java.nio.channels.ClosedChannelException;
-import java.nio.channels.SelectableChannel;
-import java.nio.channels.SelectionKey;
-import java.nio.channels.Selector;
-import java.nio.channels.ServerSocketChannel;
-import java.nio.channels.SocketChannel;
-import java.nio.channels.spi.SelectorProvider;
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Set;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.atomic.AtomicLong;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import org.apache.tomcat.lite.io.NioChannel.NioChannelCallback;
-
-/**
- * Abstract NIO/APR to avoid some of the complexity and allow more code
- * sharing and experiments.
- *
- * SelectorThread provides non-blocking methods for read/write and generates
- * callbacks using SelectorCallback. It has no buffers of its own.
- *
- * This is non-blocking, non-buffering and uses callbacks.
- *
- * @author Costin Manolache
- */
-public class NioThread implements Runnable {
-
- // ----------- IO handling -----------
- protected long inactivityTimeout = 5000;
- protected Thread selectorThread;
-
-
- static Logger log = Logger.getLogger("NIO");
-
- Selector selector;
-
- // will be processed in the selector thread
- List<NioChannel> readInterest = new ArrayList<NioChannel>();
- List<NioChannel> writeInterest = new ArrayList<NioChannel>();
- List<NioChannel> connectAcceptInterest = new ArrayList<NioChannel>();
- List<NioChannel> updateCallback = new ArrayList<NioChannel>();
- List<NioChannel> closeInterest = new LinkedList<NioChannel>();
- List<Runnable> runnableInterest = new ArrayList<Runnable>();
-
- // Statistics
- AtomicInteger opened = new AtomicInteger();
- AtomicInteger closed = new AtomicInteger();
- AtomicInteger loops = new AtomicInteger();
-
- AtomicInteger callbackCount = new AtomicInteger();
- AtomicLong callbackTotalTime = new AtomicLong();
- long maxCallbackTime = 0;
-
- // actives are also stored in the Selector. This is only updated in the main
- // thread
- public ArrayList<NioChannel> active = new ArrayList<NioChannel>();
-
- public static boolean debug = false;
- boolean debugWakeup = false;
- boolean running = true;
-
- long lastWakeup = System.currentTimeMillis(); // last time we woke
- long nextWakeup; // next scheduled wakeup
-
- // Normally select will wait for the next time event - if it's
- // too far in future, maxSleep will override it.
- private long maxSleep = 600000;
- long sleepTime = maxSleep;
-
- // Never sleep less than minSleep. This defines the resulution for
- // time events.
- private long minSleep = 100;
-
- boolean daemon = false;
-
- // TODO: trace log - record all events with timestamps, replay
-
- public NioThread(String name, boolean daemon) {
- try {
- selectorThread = (name == null) ? new Thread(this) :
- new Thread(this, name);
-
- selector = Selector.open();
- // TODO: start it on-demand, close it when not in use
- selectorThread.setDaemon(daemon);
- this.daemon = daemon;
-
- selectorThread.start();
-
- } catch(IOException e) {
- throw new RuntimeException(e);
- }
- }
-
- /**
- * Opened sockets, waiting for something ( close at least )
- */
- public int getOpen() {
- return opened.get();
- }
-
- /**
- * Closed - we're done with them.
- */
- public int getClosed() {
- return closed.get();
- }
-
- public int getActive() {
- return active.size();
- }
-
- public int getCallbacks() {
- return callbackCount.get();
- }
-
- public long getMaxCallbackTime() {
- return maxCallbackTime;
- }
-
- public long getAvgCallbackTime() {
- int cnt = callbackCount.get();
- if (cnt == 0) {
- return 0;
- }
- return callbackTotalTime.get() / cnt;
- }
-
- /**
- * How many times we looped
- */
- public int getLoops() {
- return loops.get();
- }
-
- public long getLastWakeup() {
- return lastWakeup;
- }
-
- public long getTimeSinceLastWakeup() {
- return System.currentTimeMillis() - lastWakeup;
- }
-
- /**
- * Close all resources, stop accepting, stop the thread.
- * The actual stop will happen in background.
- */
- public void stop() {
- running = false;
- if (debug) {
- log.info("Selector thread stop " + this);
- }
- selector.wakeup();
- }
-
- public void run() {
- int sloops = 0;
- if (debug) {
- log.info("Start NIO thread, daemon=" + daemon);
- }
- while (running) {
- // if we want timeouts - set here.
- try {
- loops.incrementAndGet();
-
- // Check if new requests were added
- processPending();
-
- // Timers
- long now = System.currentTimeMillis();
- if (nextWakeup < now) {
- // We don't want to iterate on every I/O
- updateSleepTimeAndProcessTimeouts(now);
- }
-
- int selected = selector.select(sleepTime);
-
- lastWakeup = System.currentTimeMillis();
- long slept = lastWakeup - now;
-
- if (debugWakeup && selected == 0) {
- if (sleepTime < maxSleep - 1000) { // short wakeup
- log.info("Wakeup " + selected + " " + slept
- + " " + sleepTime);
- }
- }
- if (slept < 10 && selected == 0) {
- if (sloops > 50) {
- sloops = 0;
- log.severe("Looping !");
- resetSelector();
- }
- sloops++;
- }
-
- // handle events for existing req first.
- if (selected != 0) {
- sloops = 0;
- int callbackCnt = 0;
- Set<SelectionKey> sel = selector.selectedKeys();
- Iterator<SelectionKey> i = sel.iterator();
-
- while (i.hasNext()) {
- callbackCnt++;
- long beforeCallback = System.currentTimeMillis();
- SelectionKey sk = i.next();
- i.remove();
-
- boolean valid = sk.isValid();
- int readyOps = (valid) ? sk.readyOps() : 0;
-
- NioChannel ch = (NioChannel) sk.attachment();
- if (debugWakeup) {
- log.info("Wakeup selCnt=" + selected + " slept=" + (lastWakeup - now) +
- " ready: " + readyOps + " v=" +
- sk.isValid() + " ch=" + ch);
- }
- if (ch == null) {
- log.severe("Missing channel");
- sk.cancel();
- continue;
- }
- if (ch.selKey != sk) {
- // if (ch.selKey != null) { // null if closed
- log.severe("Invalid state, selKey doesn't match ");
- ch.selKey = sk;
- }
- if (ch.channel != sk.channel()) {
- ch.channel = sk.channel();
- log.severe("Invalid state, channel doesn't match ");
- }
-
- if (!sk.isValid()) {
- if (debug) {
- log.info("!isValid, closed socket " + ch);
- }
- ch.close();
- continue;
- }
-
- try {
- int ready = sk.readyOps();
- // callbacks
- if (sk.isValid() && sk.isAcceptable()) {
- handleAccept(ch, sk);
- }
-
- if (sk.isValid() && sk.isConnectable()) {
- sk.interestOps(sk.interestOps() & ~SelectionKey.OP_CONNECT);
- SocketChannel sc = (SocketChannel) sk.channel();
- handleConnect(ch, sc);
- }
-
- if (sk.isValid() && sk.isWritable()) {
- // Needs to be explicitely re-enabled by callback
- // if more data.
- sk.interestOps(sk.interestOps() & ~SelectionKey.OP_WRITE);
- ch.writeInterest = false;
- handleDataWriteable(ch);
- }
-
- if (sk.isValid() && sk.isReadable()) {
- // Leave readable interest !
- handleReadable(ch);
- }
-
- long callbackTime =
- System.currentTimeMillis() - beforeCallback;
-
- if (callbackTime > 250) {
- log.warning("Callback too long ! ops=" + ready +
- " time=" + callbackTime + " ch=" + ch +
- " " + callbackCnt);
- }
- if (callbackTime > maxCallbackTime) {
- maxCallbackTime = callbackTime;
- }
- callbackCount.incrementAndGet();
- this.callbackTotalTime.addAndGet(callbackTime);
-
- } catch (Throwable t) {
- log.log(Level.SEVERE, "SelectorThread: Channel error, closing", t);
- ch.lastException = t;
- ch.close();
- }
-
- }
- // All at once
- sel.clear();
- }
-
- } catch (Throwable e) {
- log.log(Level.SEVERE, "SelectorThread: Error in select", e);
- }
- } // while(running)
- log.info("SelectorThread done");
- }
-
- private void log(String msg, int selected, long slept, SelectionKey sk, int readyOps) {
- log.info(msg + " " + selected
- + " " + slept
- + " ready: " + readyOps + " "
- + sk.readyOps() + " " + sk);
- }
-
- private void resetSelector() throws IOException, ClosedChannelException {
- // Let's close all sockets - one is bad, but we can't do much.
- Set<SelectionKey> keys = selector.keys();
- //Set<SelectionKey> keys = selector.keys();
- ArrayList<NioChannel> oldCh = new ArrayList<NioChannel>();
- ArrayList<Integer> interests = new ArrayList<Integer>();
- for (SelectionKey k : keys) {
- NioChannel cd = (NioChannel) k.attachment();
- interests.add(k.interestOps());
- oldCh.add(cd);
- k.cancel();
- }
-
- selector.close();
- selector = Selector.open();
- for (int i = 0; i < oldCh.size(); i++) {
- NioChannel selectorData = oldCh.get(i);
- if (selectorData == null) {
- continue;
- }
- int interest = interests.get(i);
- if (selectorData.channel instanceof ServerSocketChannel) {
- ServerSocketChannel socketChannel =
- (ServerSocketChannel) selectorData.channel;
- selectorData.selKey = socketChannel.register(selector, SelectionKey.OP_ACCEPT);
- } else {
- SocketChannel socketChannel =
- (SocketChannel) selectorData.channel;
- if (interest != 0) {
- selectorData.selKey = socketChannel.register(selector,
- interest);
- }
-
- }
- }
- }
-
- private void handleReadable(NioChannel ch) throws IOException {
- ch.lastReadResult = 0;
- if (ch.callback != null) {
- ch.callback.handleReadable(ch);
- }
- if (ch.lastReadResult != 0 && ch.readInterest && !ch.inClosed) {
- log.warning("LOOP: read interest" +
- " after incomplete read");
- ch.close();
- }
- }
-
- private void handleDataWriteable(NioChannel ch) throws IOException {
- ch.lastWriteResult = 0;
- if (ch.callback != null) {
- ch.callback.handleWriteable(ch);
- }
- if (ch.lastWriteResult > 0 && ch.writeInterest) {
- log.warning("SelectorThread: write interest" +
- " after incomplete write, LOOP");
- }
- }
-
- private void handleConnect(NioChannel ch, SocketChannel sc)
- throws IOException, SocketException {
- try {
- if (!sc.finishConnect()) {
- log.warning("handleConnected - finishConnect returns false");
- }
- ch.sel = this;
- //sc.socket().setSoLinger(true, 0);
- if (debug) {
- log.info("connected() " + ch + " isConnected()=" + sc.isConnected() + " " +
- sc.isConnectionPending());
- }
-
- readInterest(ch, true);
- } catch (Throwable t) {
- close(ch, t);
- }
- try {
- if (ch.callback != null) {
- ch.callback.handleConnected(ch);
- }
- } catch(Throwable t1) {
- log.log(Level.WARNING, "Error in connect callback", t1);
- }
-
- }
-
- private void handleAccept(NioChannel ch, SelectionKey sk)
- throws IOException, ClosedChannelException {
- SelectableChannel selc = sk.channel();
- ServerSocketChannel ssc=(ServerSocketChannel)selc;
- SocketChannel sockC = ssc.accept();
- sockC.configureBlocking(false);
-
- NioChannel acceptedChannel = new NioChannel(this);
- acceptedChannel.selKey = sockC.register(selector,
- SelectionKey.OP_READ,
- acceptedChannel);
- acceptedChannel.channel = sockC;
-
- synchronized (active) {
- active.add(acceptedChannel);
- }
-
- // Find the callback for the new socket
- if (ch.callback != null) {
- // TODO: use future !
- try {
- ch.callback.handleConnected(acceptedChannel);
- } catch (Throwable t) {
- log.log(Level.SEVERE, "SelectorThread: Channel error, closing ", t);
- acceptedChannel.lastException = t;
- acceptedChannel.close();
- }
- }
-
- //sk.interestOps(sk.interestOps() | SelectionKey.OP_ACCEPT);
- if (debug) {
- log.info("handleAccept " + ch);
- }
- }
-
-
- public void shutdownOutput(NioChannel ch) throws IOException {
- Channel channel = ch.channel;
- if (channel instanceof SocketChannel) {
- SocketChannel sc = (SocketChannel) channel;
- if (sc.isOpen() && sc.isConnected()) {
- if (debug) {
- log.info("Half shutdown " + ch);
- }
- sc.socket().shutdownOutput(); // TCP end to the other side
- }
- }
- }
-
- /**
- * Called from the IO thread
- */
- private void closeIOThread(NioChannel ch, boolean remove) {
- SelectionKey sk = (SelectionKey) ch.selKey;
- Channel channel = ch.channel;
- try {
- synchronized(closeInterest) {
- if (ch.closeCalled) {
- if (debug) {
- log.severe("Close called 2x ");
- }
- return;
- }
- ch.closeCalled = true;
- int o = opened.decrementAndGet();
- if (debug) {
- log.info("-------------> close: " + ch + " t=" + ch.lastException);
- }
- if (sk != null) {
- if (sk.isValid()) {
- sk.interestOps(0);
- }
- sk.cancel();
- ch.selKey = null;
- }
-
- if (channel instanceof SocketChannel) {
- SocketChannel sc = (SocketChannel) channel;
-
- if (sc.isConnected()) {
- if (debug) {
- log.info("Close socket, opened=" + o);
- }
- try {
- sc.socket().shutdownInput();
- } catch(IOException io1) {
- }
- try {
- sc.socket().shutdownOutput(); // TCP end to the other side
- } catch(IOException io1) {
- }
- sc.socket().close();
- }
- }
- channel.close();
-
- closed.incrementAndGet();
-
- if (ch.callback != null) {
- ch.callback.handleClosed(ch);
- }
- // remove from active - false only if already removed
- if (remove) {
- synchronized (active) {
- boolean removed = active.remove(ch);
- }
- }
- }
- } catch (IOException ex2) {
- log.log(Level.SEVERE, "SelectorThread: Error closing socket ", ex2);
- }
- }
-
- // --------------- Socket op abstractions ------------
-
- public int readNonBlocking(NioChannel selectorData, ByteBuffer bb)
- throws IOException {
- try {
- int off = bb.position();
-
- int done = 0;
-
- done = ((SocketChannel) selectorData.channel).read(bb);
-
- if (debug) {
- log.info("-------------readNB rd=" + done + " bb.limit=" +
- bb.limit() + " pos=" + bb.position() + " " + selectorData);
- }
- if (done > 0) {
- if (debug) {
- if (!bb.isDirect()) {
- String s = new String(bb.array(), off,
- bb.position() - off);
- log.info("Data:\n" + s);
- } else {
- log.info("Data: " + bb.toString());
- }
- }
- selectorData.zeroReads = 0;
- } else if (done < 0) {
- if (debug) {
- log.info("SelectorThread: EOF while reading " + selectorData);
- }
- } else {
- // need more...
- if (selectorData.lastReadResult == 0) {
- selectorData.zeroReads++;
- if (selectorData.zeroReads > 6) {
- log.severe("LOOP 0 reading ");
- selectorData.lastException = new IOException("Polling read");
- selectorData.close();
- return -1;
- }
- }
- }
- selectorData.lastReadResult = done;
- return done;
- } catch(IOException ex) {
- if (debug) {
- log.info("readNB error rd=" + -1 + " bblen=" +
- (bb.limit() - bb.position()) + " " + selectorData + " " + ex);
- }
- // common case: other side closed the connection. No need for trace
- if (ex.toString().indexOf("Connection reset by peer") < 0) {
- ex.printStackTrace();
- }
- selectorData.lastException = ex;
- selectorData.close();
- return -1;
- }
- }
-
- /**
- * May be called from any thread
- */
- public int writeNonBlocking(NioChannel selectorData, ByteBuffer bb)
- throws IOException {
- try {
- if (debug) {
- log.info("writeNB pos=" + bb.position() + " len=" +
- (bb.limit() - bb.position()) + " " + selectorData);
- if (!bb.isDirect()) {
- String s = new String(bb.array(), bb.position(),
-
- bb.limit() - bb.position());
- log.info("Data:\n" + s);
- }
- }
- if (selectorData.writeInterest) {
- // writeInterest will be false after a callback, if it is
- // set it means we want to wait for the callback.
- if (debug) {
- log.info("Prevent writeNB when writeInterest is set");
- }
- return 0;
- }
-
- int done = 0;
- done = ((SocketChannel) selectorData.channel).write(bb);
- selectorData.lastWriteResult = done;
- return done;
- } catch(IOException ex) {
- if (debug) {
- log.info("writeNB error pos=" + bb.position() + " len=" +
- (bb.limit() - bb.position()) + " " + selectorData + " " +
- ex);
- }
- //ex.printStackTrace();
- selectorData.lastException = ex;
- selectorData.close();
- throw ex;
- // return -1;
- }
- }
-
- public int getPort(NioChannel sd, boolean remote) {
- SocketChannel socketChannel = (SocketChannel) sd.channel;
-
- if (remote) {
- return socketChannel.socket().getPort();
- } else {
- return socketChannel.socket().getLocalPort();
- }
- }
-
- public InetAddress getAddress(NioChannel sd, boolean remote) {
- SocketChannel socketChannel = (SocketChannel) sd.channel;
-
- if (remote) {
- return socketChannel.socket().getInetAddress();
- } else {
- return socketChannel.socket().getLocalAddress();
- }
- }
-
- /**
- */
- public void connect(String host, int port, NioChannelCallback cstate)
- throws IOException {
- connect(new InetSocketAddress(host, port), cstate);
- }
-
-
- public void connect(SocketAddress sa, NioChannelCallback cstate)
- throws IOException {
- connect(sa, cstate, null);
- }
-
- public void connect(SocketAddress sa, NioChannelCallback cstate,
- NioChannel filter)
- throws IOException {
-
- SocketChannel socketChannel = SocketChannel.open();
- socketChannel.configureBlocking(false);
- NioChannel selectorData = new NioChannel(this);
- selectorData.sel = this;
- selectorData.callback = cstate;
- selectorData.channel = socketChannel;
- selectorData.channel = socketChannel; // no key
-
- socketChannel.connect(sa);
- opened.incrementAndGet();
-
- synchronized (connectAcceptInterest) {
- connectAcceptInterest.add(selectorData);
- }
- selector.wakeup();
- }
-
- // TODO
- public void configureSocket(ByteChannel ch,
- boolean noDelay) throws IOException {
- SocketChannel sockC = (SocketChannel) ch;
- sockC.socket().setTcpNoDelay(noDelay);
- }
-
- // TODO
- public void setSocketOptions(NioChannel selectorData,
- int linger,
- boolean tcpNoDelay,
- int socketTimeout)
- throws IOException {
-
- SocketChannel socketChannel =
- (SocketChannel) selectorData.channel;
- Socket socket = socketChannel.socket();
-
- if(linger >= 0 )
- socket.setSoLinger( true, linger);
- if( tcpNoDelay )
- socket.setTcpNoDelay(tcpNoDelay);
- if( socketTimeout > 0 )
- socket.setSoTimeout( socketTimeout );
- }
-
- /**
- * Can be called from multiple threads or multiple times.
- */
- public int close(NioChannel selectorData, Throwable exception) throws IOException {
- synchronized (closeInterest) {
- if (exception != null) {
- selectorData.lastException = exception;
- }
- selectorData.readInterest = false;
- if (isSelectorThread()) {
- closeIOThread(selectorData, true);
- return 0;
- }
- if (!selectorData.inClosed) {
- closeInterest.add(selectorData);
- }
- }
- selector.wakeup();
- return 0;
- }
-
-
- public void acceptor(NioChannelCallback cstate,
- int port,
- InetAddress inet,
- int backlog,
- int serverTimeout)
- throws IOException
- {
- ServerSocketChannel ssc=ServerSocketChannel.open();
- ServerSocket serverSocket = ssc.socket();
-
- SocketAddress sa = null;
-
- if (inet == null) {
- sa = new InetSocketAddress( port );
- } else {
- sa = new InetSocketAddress(inet, port);
- }
- if (backlog > 0) {
- serverSocket.bind( sa , backlog);
- } else {
- serverSocket.bind(sa);
- }
- if( serverTimeout >= 0 ) {
- serverSocket.setSoTimeout( serverTimeout );
- }
-
-
- ssc.configureBlocking(false);
-
- NioChannel selectorData = new NioChannel(this);
- selectorData.channel = ssc; // no key yet
- selectorData.callback = cstate;
- // key will be set in pending
-
- // TODO: add SSL here
-
- synchronized (connectAcceptInterest) {
- connectAcceptInterest.add(selectorData);
- }
- selector.wakeup();
- }
-
- public void runInSelectorThread(Runnable cb) throws IOException {
- if (isSelectorThread()) {
- cb.run();
- } else {
- synchronized (runnableInterest) {
- runnableInterest.add(cb);
- }
- selector.wakeup();
- }
- }
-
- /**
- * Example config:
- *
- * www stream tcp wait USER PATH_TO_tomcatInetd.sh
- *
- * For a different port, you need to add it to /etc/services.
- *
- * 'wait' is critical - the common use of inetd is 'nowait' for
- * tcp services, which doesn't make sense for java ( too slow startup
- * time ). It may make sense in future with something like android VM.
- *
- * In 'wait' mode, inetd will pass the acceptor socket to java - so
- * you can listen on port 80 and run as regular user with no special
- * code and magic.
- * If tomcat dies, inetd will get back the acceptor and on next connection
- * restart tomcat.
- *
- * This also works with xinetd. It might work with Apple launchd.
- *
- * TODO: detect inactivity for N minutes, exist - to free resources.
- */
- public void inetdAcceptor(NioChannelCallback cstate) throws IOException {
- SelectorProvider sp=SelectorProvider.provider();
-
- Channel ch=sp.inheritedChannel();
- if(ch!=null ) {
- log.info("Inherited: " + ch.getClass().getName());
- // blocking mode
- ServerSocketChannel ssc=(ServerSocketChannel)ch;
- ssc.configureBlocking(false);
-
- NioChannel selectorData = new NioChannel(this);
- selectorData.channel = ssc;
- selectorData.callback = cstate;
-
- synchronized (connectAcceptInterest) {
- connectAcceptInterest.add(selectorData);
- }
- selector.wakeup();
- } else {
- log.severe("No inet socket ");
- throw new IOException("Invalid inheritedChannel");
- }
- }
-
- // -------------- Housekeeping -------------
- /**
- * Same as APR connector - iterate over tasks, get
- * smallest timeout
- * @throws IOException
- */
- void updateSleepTimeAndProcessTimeouts(long now)
- throws IOException {
- long min = Long.MAX_VALUE;
- // TODO: test with large sets, maybe sort
- synchronized (active) {
- Iterator<NioChannel> activeIt = active.iterator();
-
- while(activeIt.hasNext()) {
- NioChannel selectorData = activeIt.next();
- if (! selectorData.channel.isOpen()) {
- if (debug) {
- log.info("Found closed socket, removing " +
- selectorData.channel);
- }
-// activeIt.remove();
-// selectorData.close();
- }
-
- long t = selectorData.nextTimeEvent;
- if (t == 0) {
- continue;
- }
- if (t < now) {
- // Timeout
- if (debug) {
- log.info("Time event " + selectorData);
- }
- if (selectorData.timeEvent != null) {
- selectorData.timeEvent.run();
- }
- // TODO: make sure this is updated if it was selected
- continue;
- }
- if (t < min) {
- min = t;
- }
- }
- }
- long nextSleep = min - now;
- if (nextSleep > maxSleep) {
- sleepTime = maxSleep;
- } else if (nextSleep < minSleep) {
- sleepTime = minSleep;
- } else {
- sleepTime = nextSleep;
- }
- nextWakeup = now + sleepTime;
- }
-
- /**
- * Request a callback whenever data can be written.
- * When the callback is invoked, the write interest is removed ( to avoid
- * looping ). If the write() operation doesn't complete, you must call
- * writeInterest - AND stop writing, some implementations will throw
- * exception. write() will actually attempt to detect this and avoid the
- * error.
- *
- * @param sc
- */
- public void writeInterest(NioChannel selectorData) {
- // TODO: suspended ?
-
- SelectionKey sk = (SelectionKey) selectorData.selKey;
- if (!sk.isValid()) {
- return;
- }
- selectorData.writeInterest = true;
- int interest = sk.interestOps();
- if ((interest & SelectionKey.OP_WRITE) != 0) {
- return;
- }
- if (Thread.currentThread() == selectorThread) {
- interest =
- interest | SelectionKey.OP_WRITE;
- sk.interestOps(interest);
- if (debug) {
- log.info("Write interest " + selectorData + " i=" + interest);
- }
- return;
- }
- if (debug) {
- log.info("Pending write interest " + selectorData);
- }
- synchronized (writeInterest) {
- writeInterest.add(selectorData);
- }
- selector.wakeup();
- }
-
-
- public void readInterest(NioChannel selectorData, boolean b) throws IOException {
- if (Thread.currentThread() == selectorThread) {
- selectorData.readInterest = b;
- selThreadReadInterest(selectorData);
- return;
- }
- SelectionKey sk = (SelectionKey) selectorData.selKey;
- if (sk == null) {
- close(selectorData, null);
- return;
- }
- int interest = sk.interestOps();
- selectorData.readInterest = b;
- if (b && (interest & SelectionKey.OP_READ) != 0) {
- return;
- }
- if (!b && (interest & SelectionKey.OP_READ) == 0) {
- return;
- }
- // Schedule the interest update.
- synchronized (readInterest) {
- readInterest.add(selectorData);
- }
- if (debug) {
- log.info("Registering pending read interest");
- }
- selector.wakeup();
- }
-
-
- private void selThreadReadInterest(NioChannel selectorData) throws IOException {
- SelectionKey sk = (SelectionKey) selectorData.selKey;
- if (sk == null) {
- if (selectorData.readInterest) {
- if (debug) {
- log.info("Register again for read interest");
- }
- SocketChannel socketChannel =
- (SocketChannel) selectorData.channel;
- if (socketChannel.isOpen()) {
- selectorData.sel = this;
- selectorData.selKey =
- socketChannel.register(selector,
- SelectionKey.OP_READ, selectorData);
- selectorData.channel = socketChannel;
- }
- }
- return;
- }
- if (!sk.isValid()) {
- return;
- }
- int interest = sk.interestOps();
- if (sk != null && sk.isValid()) {
- if (selectorData.readInterest) {
-// if ((interest | SelectionKey.OP_READ) != 0) {
-// return;
-// }
- interest =
- interest | SelectionKey.OP_READ;
- } else {
-// if ((interest | SelectionKey.OP_READ) == 0) {
-// return;
-// }
- interest =
- interest & ~SelectionKey.OP_READ;
- }
- if (interest == 0) {
- if (!selectorData.inClosed) {
- new Throwable().printStackTrace();
- log.warning("No interest(rd removed) " + selectorData);
- }
- // TODO: should we remove it ? It needs to be re-activated
- // later.
- sk.cancel(); //??
- selectorData.selKey = null;
- } else {
- sk.interestOps(interest);
- }
- if (debug) {
- log.info(((selectorData.readInterest)
- ? "RESUME read " : "SUSPEND read ")
- + selectorData);
- }
- }
- }
-
-
- private void processPendingConnectAccept() throws IOException {
- synchronized (connectAcceptInterest) {
- Iterator<NioChannel> ci = connectAcceptInterest.iterator();
-
- while (ci.hasNext()) {
- NioChannel selectorData = ci.next();
-
- // Find host, port - initiate connection
- try {
- // Accept interest ?
- if (selectorData.channel instanceof ServerSocketChannel) {
- ServerSocketChannel socketChannel =
- (ServerSocketChannel) selectorData.channel;
- selectorData.sel = this;
- selectorData.selKey =
- socketChannel.register(selector,
- SelectionKey.OP_ACCEPT, selectorData);
-
- selectorData.channel = socketChannel;
- synchronized (active) {
- active.add(selectorData);
- }
- if (debug) {
- log.info("Pending acceptor added: " + selectorData);
- }
- } else {
- SocketChannel socketChannel =
- (SocketChannel) selectorData.channel;
- selectorData.sel = this;
- selectorData.selKey =
- socketChannel.register(selector,
- SelectionKey.OP_CONNECT, selectorData);
- synchronized (active) {
- active.add(selectorData);
- }
- if (debug) {
- log.info("Pending connect added: " + selectorData);
- }
- }
- } catch (Throwable e) {
- log.log(Level.SEVERE, "error registering connect/accept",
- e);
- }
- }
- connectAcceptInterest.clear();
- }
- }
-
- private void processPending() throws IOException {
- if (closeInterest.size() > 0) {
- synchronized (closeInterest) {
- List<NioChannel> closeList = new ArrayList(closeInterest);
- closeInterest.clear();
-
- Iterator<NioChannel> ci = closeList.iterator();
-
- while (ci.hasNext()) {
- try {
- NioChannel selectorData = ci.next();
- closeIOThread(selectorData, true);
- } catch (Throwable t) {
- t.printStackTrace();
- }
- }
- }
- }
- processPendingConnectAccept();
- processPendingReadWrite();
-
- if (runnableInterest.size() > 0) {
- synchronized (runnableInterest) {
- Iterator<Runnable> ci = runnableInterest.iterator();
- while (ci.hasNext()) {
- Runnable cstate = ci.next();
- try {
- cstate.run();
- } catch (Throwable t) {
- t.printStackTrace();
- }
- if (debug) {
- log.info("Run in selthread: " + cstate);
- }
- }
- runnableInterest.clear();
- }
- }
- //processPendingUpdateCallback();
- }
-
- private void processPendingReadWrite() throws IOException {
- // Update interest
- if (readInterest.size() > 0) {
- synchronized (readInterest) {
- Iterator<NioChannel> ci = readInterest.iterator();
- while (ci.hasNext()) {
- NioChannel cstate = ci.next();
- selThreadReadInterest(cstate);
- if (debug) {
- log.info("Read interest added: " + cstate);
- }
- }
- readInterest.clear();
- }
- }
- if (writeInterest.size() > 0) {
- synchronized (writeInterest) {
- Iterator<NioChannel> ci = writeInterest.iterator();
- while (ci.hasNext()) {
- NioChannel cstate = ci.next();
- // Fake callback - will update as side effect
- handleDataWriteable(cstate);
- if (debug) {
- log.info("Write interest, calling dataWritable: " + cstate);
- }
- }
- writeInterest.clear();
- }
- }
- }
-
-
- protected boolean isSelectorThread() {
- return Thread.currentThread() == selectorThread;
- }
-
- public static boolean isSelectorThread(IOChannel ch) {
- SocketIOChannel sc = (SocketIOChannel) ch.getFirst();
- return Thread.currentThread() == sc.ch.sel.selectorThread;
- }
-
-}
\ No newline at end of file
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/lite/io/SocketConnector.java b/modules/tomcat-lite/java/org/apache/tomcat/lite/io/SocketConnector.java
deleted file mode 100644
index 283b947..0000000
--- a/modules/tomcat-lite/java/org/apache/tomcat/lite/io/SocketConnector.java
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- */
-package org.apache.tomcat.lite.io;
-
-import java.io.IOException;
-import java.net.InetSocketAddress;
-import java.util.Timer;
-import java.util.concurrent.Executor;
-import java.util.concurrent.Executors;
-import java.util.logging.Logger;
-
-import org.apache.tomcat.lite.io.NioChannel.NioChannelCallback;
-
-/**
- * Class for handling sockets. It manages a pool of SelectorThreads, fully
- * non-blocking. There is no caching or buffer management. SelectorChannel
- * represents on connection.
- *
- * In the old types, the connector was socket-centric, and quite ugly. After
- * many refactoring the buffers ( buckets and brigade ) and callbacks are
- * used everywhere, and the sockets play a supporting role.
- *
- * TODO: discover if APR is available and use it, or fall back to NIO.
- *
- * @author Costin Manolache
- */
-public class SocketConnector extends IOConnector {
- static Logger log = Logger.getLogger(SocketConnector.class.getName());
- static boolean debug = false;
-
- // TODO: pool, balanced usage
- // TODO: bind into OM or callback when created
-
- private NioThread selector;
-
- // For resolving DNS ( i.e. connect )
- Executor threadPool = Executors.newCachedThreadPool();
-
- public SocketConnector() {
- timer = new Timer(true);
- }
-
- public SocketConnector(int port) {
- timer = new Timer(true);
- }
-
- /**
- * This may be blocking - involves host resolution, connect.
- * If the IP address is provided - it shouldn't block.
- */
- @Override
- public void connect(final String host, final int port,
- final IOConnector.ConnectedCallback sc) throws IOException {
- final SocketIOChannel ioch = new SocketIOChannel(this, null, host + ":" + port);
- ioch.setConnectedCallback(sc);
- threadPool.execute(new Runnable() {
- @Override
- public void run() {
- try {
- getSelector().connect(new InetSocketAddress(host, port), ioch, null);
- } catch (Throwable e) {
- e.printStackTrace();
- try {
- sc.handleConnected(ioch);
- ioch.close();
- } catch (Throwable e1) {
- e1.printStackTrace();
- }
- }
- }
- });
- }
-
- /**
- * Create a new server socket, register the callback.
- * If port == 0 it'll use the inherited channel, i.e. inetd mode.
- * TODO: if port == -1, detect a free port. May block.
- */
- public void acceptor(final IOConnector.ConnectedCallback sc,
- final CharSequence address, Object extra)
- throws IOException
- {
- final int port = Integer.parseInt(address.toString());
- NioChannelCallback acceptCb = new NioChannelCallback() {
- @Override
- public void handleClosed(NioChannel ch) throws IOException {
- }
-
- @Override
- public void handleConnected(NioChannel ch) throws IOException {
- SocketIOChannel ioch = new SocketIOChannel(SocketConnector.this,
- ch, ":" + port);
- sc.handleConnected(ioch);
- }
-
- @Override
- public void handleReadable(NioChannel ch) throws IOException {
- }
-
- @Override
- public void handleWriteable(NioChannel ch) throws IOException {
- }
- };
-
- if (port == -1) {
- // TODO: find an unused port
- } else if (port == 0) {
- getSelector().inetdAcceptor(acceptCb);
- } else {
- getSelector().acceptor(acceptCb, port, null, 200, 20000);
- }
- }
-
- static int id = 0;
-
- public synchronized NioThread getSelector() {
- if (selector == null) {
- String name = "SelectorThread-" + id++;
- selector = new NioThread(name, true);
- }
-
- return selector;
- }
-
- public void stop() {
- getSelector().stop();
- }
-
-
- // TODO: suspendAccept(boolean)
-
-}
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/lite/io/SocketIOChannel.java b/modules/tomcat-lite/java/org/apache/tomcat/lite/io/SocketIOChannel.java
deleted file mode 100644
index c74ccc5..0000000
--- a/modules/tomcat-lite/java/org/apache/tomcat/lite/io/SocketIOChannel.java
+++ /dev/null
@@ -1,271 +0,0 @@
-/*
- */
-package org.apache.tomcat.lite.io;
-
-import java.io.IOException;
-import java.net.InetAddress;
-import java.nio.ByteBuffer;
-
-import org.apache.tomcat.lite.io.NioChannel.NioChannelCallback;
-
-/**
- * Buffered socket channel
- */
-public class SocketIOChannel extends IOChannel implements NioChannelCallback {
- IOBuffer out;
- IOBuffer in;
-
- NioChannel ch;
-
- SocketIOChannel(IOConnector connector, NioChannel data,
- String target)
- throws IOException {
- this.connector = connector;
- in = new IOBuffer(this);
- out = new IOBuffer(this);
- this.ch = data;
- setOutBuffer(out);
- setChannel(data);
- this.target = target;
- }
-
- void setChannel(NioChannel data) {
- this.ch = data;
- if (ch != null) {
- ch.callback = this;
- }
- }
-
-
- @Override
- public IOBuffer getIn() {
- return in;
- }
-
- @Override
- public IOBuffer getOut() {
- return out;
- }
-
- /**
- * Both in and out open
- */
- public boolean isOpen() {
- if (ch == null) {
- return false;
- }
- return ch.isOpen() && ch.channel != null &&
- ch.channel.isOpen() && !getIn().isAppendClosed() &&
- !getOut().isAppendClosed();
- }
-
- NioChannel getSelectorChannel() {
- return ch;
- }
-
- public String toString() {
- return ch.toString();
- }
-
- public void setOutBuffer(IOBuffer out) {
- this.out = out;
- }
-
- ByteBuffer flushBuffer;
-
- /**
- * Send as much as possible.
- *
- * Adjust write interest so we can send more when possible.
- */
- private void flush(NioChannel ch) throws IOException {
- synchronized (this) {
- if (ch == null) {
- if (out.isClosedAndEmpty()) {
- return;
- }
- throw new IOException("flush() with closed socket");
- }
- while (true) {
- if (out.isClosedAndEmpty()) {
- ch.shutdownOutput();
- break;
- }
- BBucket bb = out.peekFirst();
- if (bb == null) {
- break;
- }
- flushBuffer = getReadableBuffer(flushBuffer, bb);
- int before = flushBuffer.position();
-
- int done = 0;
- while (flushBuffer.remaining() > 0) {
- try {
- done = ch.write(flushBuffer);
- } catch (IOException ex) {
- // can't write - was closed !
- done = -1;
- }
-
- if (done < 0) {
- ch.close();
- out.close();
- handleFlushed(this);
- //throw new IOException("Closed while writting ");
- return;
- }
- if (done == 0) {
- bb.position(flushBuffer.position());
- ch.writeInterest(); // it is cleared on next dataWriteable
- return;
- }
- }
- releaseReadableBuffer(flushBuffer, bb);
- }
- handleFlushed(this);
-
- }
- }
-
- /**
- * Data available for read, called from IO thread.
- * You MUST read all data ( i.e. until read() returns 0).
- *
- * OP_READ remain active - call readInterest(false) to disable -
- * for example to suspend reading if buffer is full.
- */
- public void handleReceived(IOChannel net) throws IOException {
- // All data will go to currentReceiveBuffer, until it's full.
- // Then a new buffer will be allocated/pooled.
-
- // When we fill the buffers or finish this round of reading -
- // we place the Buckets in the queue, as 'readable' buffers.
- boolean newData = false;
- try {
- int read = 0;
- synchronized(in) {
- // data between 0 and position
- int total = 0;
- while (true) {
- if (in.isAppendClosed()) { // someone closed me ?
- ch.inputClosed(); // remove read interest.
- // if outClosed - close completely
- newData = true;
- break;
- }
-
- ByteBuffer bb = in.getWriteBuffer();
- read = ch.read(bb);
- in.releaseWriteBuffer(read);
-
- if (in == null) { // Detached.
- break;
- }
-
- if (read < 0) {
- // mark the in buffer as closed
- in.close();
- ch.inputClosed();
- newData = true;
- break;
- }
- if (read == 0) {
- break;
- }
- total += read;
- newData = true;
- }
- } // sync
- if (newData) {
- super.sendHandleReceivedCallback();
- }
-
- } catch (Throwable t) {
- close();
- if (t instanceof IOException) {
- throw (IOException) t;
- } else {
- throw new IOException(t.toString());
- }
- }
- }
-
- public static final ByteBuffer getReadableBuffer(ByteBuffer orig, BBucket bucket) {
- if (orig == null || orig.array() != bucket.array()) {
- orig = ByteBuffer.wrap(bucket.array());
- }
- orig.position(bucket.position());
- orig.limit(bucket.limit());
- return orig;
- }
-
- public static final void releaseReadableBuffer(ByteBuffer bb, BBucket bucket) {
- bucket.position(bb.position());
- }
-
-
- public void readInterest(boolean b) throws IOException {
- ch.readInterest(b);
- }
-
- public InetAddress getAddress(boolean remote) {
- return ch.getAddress(remote);
- }
-
- @Override
- public Object getAttribute(String name) {
- if (ATT_REMOTE_HOSTNAME.equals(name)) {
- return getAddress(true).getHostName();
- } else if (ATT_LOCAL_HOSTNAME.equals(name)) {
- return getAddress(false).getHostName();
- } else if (ATT_REMOTE_ADDRESS.equals(name)) {
- return getAddress(true).getHostAddress();
- } else if (ATT_LOCAL_ADDRESS.equals(name)) {
- return getAddress(false).getHostAddress();
- } else if (ATT_REMOTE_PORT.equals(name)) {
- return ch.getPort(true);
- } else if (ATT_LOCAL_PORT.equals(name)) {
- return ch.getPort(false);
- }
- return null;
- }
-
- public void startSending() throws IOException {
- flush(ch);
- }
-
- public void shutdownOutput() throws IOException {
- getOut().close();
- if (ch != null) {
- startSending();
- }
- }
-
- @Override
- public void handleClosed(NioChannel ch) throws IOException {
- lastException = ch.lastException;
- closed(); // our callback.
- }
-
- public void closed() throws IOException {
- getIn().close();
- sendHandleReceivedCallback();
- //super.closed();
- }
-
- @Override
- public void handleConnected(NioChannel ch) throws IOException {
- setChannel(ch);
- connectedCallback.handleConnected(this);
- }
-
- @Override
- public void handleReadable(NioChannel ch) throws IOException {
- handleReceived(this);
- }
-
- @Override
- public void handleWriteable(NioChannel ch) throws IOException {
- flush(ch);
- }
-}
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/lite/io/SslProvider.java b/modules/tomcat-lite/java/org/apache/tomcat/lite/io/SslProvider.java
deleted file mode 100644
index 8c584b8..0000000
--- a/modules/tomcat-lite/java/org/apache/tomcat/lite/io/SslProvider.java
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- */
-package org.apache.tomcat.lite.io;
-
-import java.io.IOException;
-
-public interface SslProvider {
-
- public static final String ATT_SSL_CERT = "SslCert";
- public static final String ATT_SSL_CIPHER = "SslCipher";
- public static final String ATT_SSL_KEY_SIZE = "SslKeySize";
- public static final String ATT_SSL_SESSION_ID = "SslSessionId";
-
- /**
- * Wrap channel with SSL.
- *
- * The result will start a handshake
- */
- public IOChannel channel(IOChannel net, String host, int port)
- throws IOException;
-
- public IOChannel serverChannel(IOChannel net) throws IOException;
-
-}
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/lite/io/UrlEncoding.java b/modules/tomcat-lite/java/org/apache/tomcat/lite/io/UrlEncoding.java
deleted file mode 100644
index 09e4b53..0000000
--- a/modules/tomcat-lite/java/org/apache/tomcat/lite/io/UrlEncoding.java
+++ /dev/null
@@ -1,215 +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.lite.io;
-
-import java.io.CharConversionException;
-import java.io.IOException;
-import java.nio.charset.CharsetEncoder;
-import java.util.BitSet;
-
-
-/**
- * Support for %xx URL encoding.
- *
- * @author Costin Manolache
- */
-public final class UrlEncoding {
-
- protected static final boolean ALLOW_ENCODED_SLASH =
- Boolean.valueOf(
- System.getProperty(
- "org.apache.tomcat.util.buf.UDecoder.ALLOW_ENCODED_SLASH",
- "false")).booleanValue();
-
- public UrlEncoding() {
- }
-
- // Utilities for URL encoding.
- static BitSet SAFE_CHARS_URL = new BitSet(128);
- static BitSet SAFE_CHARS = new BitSet(128);
- BBuffer tmpBuffer = BBuffer.allocate(1024);
- CBuffer tmpCharBuffer = CBuffer.newInstance();
-
- public void urlEncode(CBuffer url, CBuffer encoded, IOWriter enc) {
- tmpBuffer.recycle();
- urlEncode(url, tmpBuffer, encoded, enc.getEncoder("UTF-8"),
- SAFE_CHARS_URL, true, enc);
- }
-
- public void urlEncode(String url, CBuffer encoded, IOWriter enc) {
- tmpCharBuffer.recycle();
- tmpCharBuffer.append(url);
- urlEncode(tmpCharBuffer, encoded, enc);
- }
-
- /** Only works for UTF-8 or charsets preserving ascii.
- *
- * @param url
- * @param tmpBuffer
- * @param encoded
- * @param utf8Enc
- * @param safeChars
- */
- public void urlEncode(CBuffer url,
- BBuffer tmpBuffer,
- CBuffer encoded,
- CharsetEncoder utf8Enc,
- BitSet safeChars, boolean last, IOWriter enc) {
- // tomcat charset-encoded each character first. I don't think
- // this is needed.
-
- // TODO: space to +
- enc.encodeAll(url, tmpBuffer, utf8Enc, last);
- byte[] array = tmpBuffer.array();
- for (int i = tmpBuffer.position(); i < tmpBuffer.limit(); i++) {
- int c = array[i];
- if (safeChars.get(c)) {
- encoded.append((char) c);
- } else {
- encoded.append('%');
- char ch = Character.forDigit((c >> 4) & 0xF, 16);
- encoded.append(ch);
- ch = Character.forDigit(c & 0xF, 16);
- encoded.append(ch);
- }
- }
- }
-
- static {
- initSafeChars(SAFE_CHARS);
- initSafeChars(SAFE_CHARS_URL);
- SAFE_CHARS_URL.set('/');
- }
-
- private static void initSafeChars(BitSet safeChars) {
- int i;
- for (i = 'a'; i <= 'z'; i++) {
- safeChars.set(i);
- }
- for (i = 'A'; i <= 'Z'; i++) {
- safeChars.set(i);
- }
- for (i = '0'; i <= '9'; i++) {
- safeChars.set(i);
- }
- // safe
- safeChars.set('-');
- safeChars.set('_');
- safeChars.set('.');
-
- // Dangerous: someone may treat this as " "
- // RFC1738 does allow it, it's not reserved
- // safeChars.set('+');
- // extra
- safeChars.set('*');
- // tomcat has them - not sure if this is correct
- safeChars.set('$'); // ?
- safeChars.set('!'); // ?
- safeChars.set('\''); // ?
- safeChars.set('('); // ?
- safeChars.set(')'); // ?
- safeChars.set(','); // ?
- }
-
- public void urlDecode(BBuffer bb, CBuffer dest, boolean q,
- IOReader charDec) throws IOException {
- // Replace %xx
- tmpBuffer.append(bb);
- urlDecode(tmpBuffer, q);
- charDec.decodeAll(bb, dest);
- }
-
-
- public void urlDecode(BBuffer bb, CBuffer dest,
- IOReader charDec) throws IOException {
- // Replace %xx
- tmpBuffer.append(bb);
- urlDecode(tmpBuffer, true);
- charDec.decodeAll(bb, dest);
- }
-
-
- /**
- * URLDecode, will modify the source. This is only at byte level -
- * it needs conversion to chars using the right charset.
- *
- * @param query Converts '+' to ' ' and allow '/'
- */
- public void urlDecode(BBuffer mb, boolean query) throws IOException {
- int start = mb.getOffset();
- byte buff[] = mb.array();
- int end = mb.getEnd();
-
- int idx = BBuffer.indexOf(buff, start, end, '%');
- int idx2 = -1;
- if (query)
- idx2 = BBuffer.indexOf(buff, start, end, '+');
- if (idx < 0 && idx2 < 0) {
- return;
- }
-
- // idx will be the smallest positive inxes ( first % or + )
- if (idx2 >= 0 && idx2 < idx)
- idx = idx2;
- if (idx < 0)
- idx = idx2;
-
- //boolean noSlash = !query;
-
- for (int j = idx; j < end; j++, idx++) {
- if (buff[j] == '+' && query) {
- buff[idx] = (byte) ' ';
- } else if (buff[j] != '%') {
- buff[idx] = buff[j];
- } else {
- // read next 2 digits
- if (j + 2 >= end) {
- throw new CharConversionException("EOF");
- }
- byte b1 = buff[j + 1];
- byte b2 = buff[j + 2];
- if (!isHexDigit(b1) || !isHexDigit(b2))
- throw new CharConversionException("isHexDigit");
-
- j += 2;
- int res = x2c(b1, b2);
-// if (noSlash && (res == '/')) {
-// throw new CharConversionException("noSlash " + mb);
-// }
- buff[idx] = (byte) res;
- }
- }
-
- mb.setEnd(idx);
-
- return;
- }
-
-
- private static boolean isHexDigit(int c) {
- return ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'));
- }
-
- private static int x2c(byte b1, byte b2) {
- int digit = (b1 >= 'A') ? ((b1 & 0xDF) - 'A') + 10 : (b1 - '0');
- digit *= 16;
- digit += (b2 >= 'A') ? ((b2 & 0xDF) - 'A') + 10 : (b2 - '0');
- return digit;
- }
-
-}
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/lite/io/WrappedException.java b/modules/tomcat-lite/java/org/apache/tomcat/lite/io/WrappedException.java
deleted file mode 100644
index 5d0897c..0000000
--- a/modules/tomcat-lite/java/org/apache/tomcat/lite/io/WrappedException.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- */
-package org.apache.tomcat.lite.io;
-
-import java.io.IOException;
-
-/**
- * For specific exceptions - also has cause ( good if compiling against
- * JDK1.5 )
- *
- * @author Costin Manolache
- */
-public class WrappedException extends IOException {
-
- public WrappedException() {
- super();
- }
-
- public WrappedException(String message) {
- super(message);
- }
-
- public WrappedException(String message, Throwable cause) {
- super(message);
- initCause(cause);
- }
-
- public WrappedException(Throwable cause) {
- super("");
- initCause(cause);
- }
-
-
- public static class ClientAbortException extends WrappedException {
- public ClientAbortException(Throwable throwable) {
- super(null, throwable);
- }
- }
-
-}
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/lite/io/jsse/JsseSslProvider.java b/modules/tomcat-lite/java/org/apache/tomcat/lite/io/jsse/JsseSslProvider.java
deleted file mode 100644
index aad716a..0000000
--- a/modules/tomcat-lite/java/org/apache/tomcat/lite/io/jsse/JsseSslProvider.java
+++ /dev/null
@@ -1,466 +0,0 @@
-/*
- */
-package org.apache.tomcat.lite.io.jsse;
-
-import java.io.ByteArrayInputStream;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.Socket;
-import java.security.Key;
-import java.security.KeyFactory;
-import java.security.KeyManagementException;
-import java.security.KeyPair;
-import java.security.KeyPairGenerator;
-import java.security.KeyStore;
-import java.security.KeyStoreException;
-import java.security.NoSuchAlgorithmException;
-import java.security.Principal;
-import java.security.PrivateKey;
-import java.security.UnrecoverableKeyException;
-import java.security.cert.Certificate;
-import java.security.cert.CertificateException;
-import java.security.cert.CertificateFactory;
-import java.security.cert.X509Certificate;
-import java.security.spec.PKCS8EncodedKeySpec;
-import java.security.spec.RSAKeyGenParameterSpec;
-import java.util.concurrent.Executor;
-import java.util.concurrent.Executors;
-import java.util.concurrent.atomic.AtomicInteger;
-
-import javax.net.ssl.HostnameVerifier;
-import javax.net.ssl.KeyManager;
-import javax.net.ssl.KeyManagerFactory;
-import javax.net.ssl.SSLContext;
-import javax.net.ssl.SSLPeerUnverifiedException;
-import javax.net.ssl.SSLSession;
-import javax.net.ssl.TrustManager;
-import javax.net.ssl.TrustManagerFactory;
-import javax.net.ssl.X509ExtendedKeyManager;
-import javax.net.ssl.X509TrustManager;
-
-import org.apache.tomcat.lite.io.BBuffer;
-import org.apache.tomcat.lite.io.DumpChannel;
-import org.apache.tomcat.lite.io.IOChannel;
-import org.apache.tomcat.lite.io.IOConnector;
-import org.apache.tomcat.lite.io.SocketConnector;
-import org.apache.tomcat.lite.io.SslProvider;
-import org.apache.tomcat.lite.io.WrappedException;
-import org.apache.tomcat.lite.io.IOConnector.ConnectedCallback;
-
-
-public class JsseSslProvider implements SslProvider {
-
- /**
- * TODO: option to require validation.
- * TODO: remember cert signature. This is needed to support self-signed
- * certs, like those used by the test.
- *
- */
- public static class BasicTrustManager implements X509TrustManager {
-
- private X509Certificate[] chain;
-
- public void checkClientTrusted(X509Certificate[] chain, String authType)
- throws CertificateException {
- this.chain = chain;
- }
-
- public void checkServerTrusted(X509Certificate[] chain, String authType)
- throws CertificateException {
- this.chain = chain;
- }
-
- public X509Certificate[] getAcceptedIssuers() {
- return new X509Certificate[0];
- }
- }
-
- public static TrustManager[] trustAllCerts = new TrustManager[] {
- new BasicTrustManager() };
-
- static String[] enabledCiphers;
-
- static final boolean debug = false;
-
- IOConnector net;
- private KeyManager[] keyManager;
- SSLContext sslCtx;
- boolean server;
- private TrustManager[] trustManagers;
-
- public AtomicInteger handshakeCount = new AtomicInteger();
- public AtomicInteger handshakeOk = new AtomicInteger();
- public AtomicInteger handshakeErr = new AtomicInteger();
- public AtomicInteger handshakeTime = new AtomicInteger();
-
- Executor handshakeExecutor = Executors.newCachedThreadPool();
- static int id = 0;
-
- public JsseSslProvider() {
- }
-
- public static void setEnabledCiphers(String[] enabled) {
- enabledCiphers = enabled;
- }
-
- public void start() {
-
- }
-
- SSLContext getSSLContext() {
- if (sslCtx == null) {
- try {
- sslCtx = SSLContext.getInstance("TLS");
- if (trustManagers == null) {
- trustManagers =
- new TrustManager[] {new BasicTrustManager()};
-
- }
- sslCtx.init(keyManager, trustManagers, null);
- } catch (NoSuchAlgorithmException e) {
- throw new RuntimeException(e);
- } catch (KeyManagementException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- return sslCtx;
- }
-
- public IOConnector getNet() {
- if (net == null) {
- getSSLContext();
- net = new SocketConnector();
- }
- return net;
- }
-
- @Override
- public IOChannel channel(IOChannel net, String host, int port) throws IOException {
- if (debug) {
- net = DumpChannel.wrap("S-ENC-" + id, net);
- }
- SslChannel ch = new SslChannel()
- .setTarget(host, port)
- .setSslContext(getSSLContext())
- .setSslProvider(this);
- net.setHead(ch);
- return ch;
- }
-
- @Override
- public SslChannel serverChannel(IOChannel net) throws IOException {
- SslChannel ch = new SslChannel()
- .setSslContext(getSSLContext())
- .setSslProvider(this).withServer();
- ch.setSink(net);
- return ch;
- }
-
- public void acceptor(final ConnectedCallback sc, CharSequence port, Object extra)
- throws IOException {
- getNet().acceptor(new ConnectedCallback() {
- @Override
- public void handleConnected(IOChannel ch) throws IOException {
- IOChannel first = ch;
- if (debug) {
- first = DumpChannel.wrap("S-ENC-" + id, ch);
- }
-
- IOChannel sslch = serverChannel(first);
- sslch.setSink(first);
- first.setHead(sslch);
-
- if (debug) {
- sslch = DumpChannel.wrap("S-CLR-" + id, sslch);
- id++;
- }
-
- sc.handleConnected(sslch);
- }
- }, port, extra);
- }
-
- public void connect(final String host, final int port, final ConnectedCallback sc)
- throws IOException {
- getNet().connect(host, port, new ConnectedCallback() {
-
- @Override
- public void handleConnected(IOChannel ch) throws IOException {
- IOChannel first = ch;
- if (debug) {
- first = DumpChannel.wrap("ENC-" + id, first);
- }
-
- IOChannel sslch = channel(first, host, port);
-// first.setHead(sslch);
-
- if (debug) {
- sslch = DumpChannel.wrap("CLR-" + id, sslch);
- id++;
- }
-
- sc.handleConnected(sslch);
- }
-
- });
- }
-
- public JsseSslProvider withKeyManager(KeyManager[] kms) {
- this.keyManager = kms;
- return this;
- }
-
- public JsseSslProvider setKeystoreFile(String file, String pass) throws IOException {
- return setKeystore(new FileInputStream(file), pass);
- }
-
- public JsseSslProvider setKeystoreResource(String res, String pass) throws IOException {
- return setKeystore(this.getClass().getClassLoader().getResourceAsStream(res),
- pass);
- }
-
- public JsseSslProvider setKeystore(InputStream file, String pass) {
- char[] passphrase = pass.toCharArray();
- KeyStore ks;
- try {
- String type = KeyStore.getDefaultType();
- System.err.println("Keystore: " + type);
- // Java: JKS
- // Android: BKS
- ks = KeyStore.getInstance(type);
- ks.load(file, passphrase);
- KeyManagerFactory kmf =
- KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
- kmf.init(ks, passphrase);
-
- TrustManagerFactory tmf =
- TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
- tmf.init(ks);
-
- keyManager = kmf.getKeyManagers();
- trustManagers = tmf.getTrustManagers();
- } catch (KeyStoreException e) {
- // No JKS keystore ?
- // TODO Auto-generated catch block
- }catch (NoSuchAlgorithmException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } catch (CertificateException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } catch (FileNotFoundException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } catch (IOException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } catch (UnrecoverableKeyException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
-
- return this;
- }
-
- public JsseSslProvider setKeys(X509Certificate cert, PrivateKey privKey) {
- keyManager = new KeyManager[] {
- new TestKeyManager(cert, privKey)
- };
- return this;
- }
-
- public JsseSslProvider setKeyFiles(String certPem, String keyFile)
- throws IOException {
-
-
- return this;
- }
-
- public JsseSslProvider setKeyRes(String certPem, String keyFile)
- throws IOException {
- setKeys(this.getClass().getClassLoader().getResourceAsStream(certPem),
- this.getClass().getClassLoader().getResourceAsStream(keyFile));
- return this;
- }
-
- private void setKeys(InputStream certPem,
- InputStream keyDer) throws IOException {
- BBuffer keyB = BBuffer.allocate(2048);
- keyB.readAll(keyDer);
- byte[] key = new byte[keyB.remaining()];
- keyB.getByteBuffer().get(key);
-
- setKeys(certPem, key);
- }
-
- public JsseSslProvider setKeys(String certPem, byte[] keyBytes) throws IOException{
- InputStream is = new ByteArrayInputStream(certPem.getBytes());
- return setKeys(is, keyBytes);
- }
-
- /**
- * Initialize using a PEM certificate and key bytes.
- * ( TODO: base64 dep to set the key as PEM )
- *
- * openssl genrsa 1024 > host.key
- * openssl pkcs8 -topk8 -nocrypt -in host.key -inform PEM
- * -out host.der -outform DER
- * openssl req -new -x509 -nodes -sha1 -days 365 -key host.key > host.cert
- *
- */
- public JsseSslProvider setKeys(InputStream certPem, byte[] keyBytes) throws IOException{
- // convert key
- try {
- KeyFactory kf = KeyFactory.getInstance("RSA");
- PKCS8EncodedKeySpec keysp = new PKCS8EncodedKeySpec(keyBytes);
- PrivateKey priv = kf.generatePrivate (keysp);
-
- // Convert cert pem to certificate
- CertificateFactory cf = CertificateFactory.getInstance("X.509");
- final X509Certificate cert = (X509Certificate) cf.generateCertificate(certPem);
-
- setKeys(cert, priv);
- } catch (Throwable t) {
- throw new WrappedException(t);
- }
- return this;
- }
-
- public class TestKeyManager extends X509ExtendedKeyManager {
- X509Certificate cert;
- PrivateKey privKey;
-
- public TestKeyManager(X509Certificate cert2, PrivateKey privKey2) {
- cert = cert2;
- privKey = privKey2;
- }
-
- public String chooseEngineClientAlias(String[] keyType,
- java.security.Principal[] issuers, javax.net.ssl.SSLEngine engine) {
- return "client";
- }
-
- public String chooseEngineServerAlias(String keyType,
- java.security.Principal[] issuers, javax.net.ssl.SSLEngine engine) {
- return "server";
- }
-
- public String chooseClientAlias(String[] keyType,
- Principal[] issuers, Socket socket) {
- return "client";
- }
-
- public String chooseServerAlias(String keyType,
- Principal[] issuers, Socket socket) {
- return "server";
- }
-
- public X509Certificate[] getCertificateChain(String alias) {
- return new X509Certificate[] {cert};
- }
-
- public String[] getClientAliases(String keyType, Principal[] issuers) {
- return null;
- }
-
- public PrivateKey getPrivateKey(String alias) {
-
- return privKey;
- }
-
- public String[] getServerAliases(String keyType, Principal[] issuers) {
- return null;
- }
- }
-
- // TODO: add a mode that trust a defined list of certs, like SSH
-
- /**
- * Make URLConnection accept all certificates.
- * Use only for testing !
- */
- public static void testModeURLConnection() {
- try {
- SSLContext sc = SSLContext.getInstance("TLS");
- sc.init(null, JsseSslProvider.trustAllCerts, null);
-
- javax.net.ssl.HttpsURLConnection.setDefaultSSLSocketFactory(
- sc.getSocketFactory());
- javax.net.ssl.HttpsURLConnection.setDefaultHostnameVerifier(
- new HostnameVerifier() {
-
- @Override
- public boolean verify(String hostname,
- SSLSession session) {
- try {
- Certificate[] certs = session.getPeerCertificates();
- // TODO...
- // see org/apache/http/conn/ssl/AbstractVerifier
- } catch (SSLPeerUnverifiedException e) {
- e.printStackTrace();
- }
- return true;
- }
-
- });
-
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
-
- // Utilities
- public static byte[] getPrivateKeyFromStore(String file, String pass)
- throws Exception {
- KeyStore store = KeyStore.getInstance("JKS");
- store.load(new FileInputStream(file), pass.toCharArray());
- Key key = store.getKey("tomcat", "changeit".toCharArray());
- PrivateKey pk = (PrivateKey) key;
- byte[] encoded = pk.getEncoded();
- return encoded;
- }
-
- public static byte[] getCertificateFromStore(String file, String pass)
- throws Exception {
- KeyStore store = KeyStore.getInstance("JKS");
- store.load(new FileInputStream(file), pass.toCharArray());
- Certificate certificate = store.getCertificate("tomcat");
-
- return certificate.getEncoded();
- }
-
- public static KeyPair generateRsaOrDsa(boolean rsa) throws Exception {
- if (rsa) {
- KeyPairGenerator keyPairGen =
- KeyPairGenerator.getInstance("RSA");
- keyPairGen.initialize(1024);
-
- RSAKeyGenParameterSpec keySpec = new RSAKeyGenParameterSpec(1024,
- RSAKeyGenParameterSpec.F0);
- keyPairGen.initialize(keySpec);
-
- KeyPair rsaKeyPair = keyPairGen.generateKeyPair();
-
- return rsaKeyPair;
- } else {
- KeyPairGenerator keyPairGen =
- KeyPairGenerator.getInstance("DSA");
- keyPairGen.initialize(1024);
-
- KeyPair pair = keyPairGen.generateKeyPair();
-
- return pair;
- }
- }
-
- /**
- * I know 2 ways to generate certs:
- * - keytool
- * - openssl req -x509 -nodes -days 365 \
- * -newkey rsa:1024 -keyout mycert.pem -out mycert.pem
- * openssl s_server -accept 9443 -cert mycert.pem -debug -msg -state -www
- */
-}
\ No newline at end of file
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/lite/io/jsse/SslChannel.java b/modules/tomcat-lite/java/org/apache/tomcat/lite/io/jsse/SslChannel.java
deleted file mode 100644
index 87705df..0000000
--- a/modules/tomcat-lite/java/org/apache/tomcat/lite/io/jsse/SslChannel.java
+++ /dev/null
@@ -1,636 +0,0 @@
-/*
- */
-package org.apache.tomcat.lite.io.jsse;
-
-import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.security.GeneralSecurityException;
-import java.util.concurrent.TimeoutException;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import javax.net.ssl.SSLContext;
-import javax.net.ssl.SSLEngine;
-import javax.net.ssl.SSLEngineResult;
-import javax.net.ssl.SSLException;
-import javax.net.ssl.SSLPeerUnverifiedException;
-import javax.net.ssl.SSLSession;
-import javax.net.ssl.SSLEngineResult.HandshakeStatus;
-import javax.net.ssl.SSLEngineResult.Status;
-
-import org.apache.tomcat.lite.io.IOBuffer;
-import org.apache.tomcat.lite.io.IOChannel;
-import org.apache.tomcat.lite.io.SslProvider;
-
-class SslChannel extends IOChannel implements Runnable {
-
- static Logger log = Logger.getLogger("SSL");
-
- static ByteBuffer EMPTY = ByteBuffer.allocate(0);
-
-
- SSLEngine sslEngine;
- // Last result
- SSLEngineResult unwrapR;
-
- boolean handshakeDone = false;
- boolean handshakeInProgress = false;
- Object handshakeSync = new Object();
- boolean flushing = false;
-
- IOBuffer in = new IOBuffer(this);
- IOBuffer out = new IOBuffer(this);
-
- long handshakeTimeout = 20000;
- // Used for session reuse
- String host;
- int port;
-
- ByteBuffer myAppOutData;
- ByteBuffer myNetOutData;
- private static boolean debugWrap = false;
-
- /*
- * Special: SSL works in packet mode, and we may receive an incomplete
- * packet. This should be in compacted write mode (i.e. data from 0 to pos,
- * limit at end )
- */
- ByteBuffer myNetInData;
- ByteBuffer myAppInData;
- boolean client = true;
-
- private SSLContext sslCtx;
-
- private boolean closeHandshake = false;
-
- public SslChannel() {
- }
-
- /**
- * Setting the host/port enables clients to reuse SSL session -
- * less traffic and encryption overhead at startup, assuming the
- * server caches the session ( i.e. single server or distributed cache ).
- *
- * SSL ticket extension is another possibility.
- */
- public SslChannel setTarget(String host, int port) {
- this.host = host;
- this.port = port;
- return this;
- }
-
- private synchronized void initSsl() throws GeneralSecurityException {
- if (sslEngine != null) {
- log.severe("Double initSsl");
- return;
- }
-
- if (client) {
- if (port > 0) {
- sslEngine = sslCtx.createSSLEngine(host, port);
- } else {
- sslEngine = sslCtx.createSSLEngine();
- }
- sslEngine.setUseClientMode(client);
- } else {
- sslEngine = sslCtx.createSSLEngine();
- sslEngine.setUseClientMode(false);
-
- }
-
- // Some VMs have broken ciphers.
- if (JsseSslProvider.enabledCiphers != null) {
- sslEngine.setEnabledCipherSuites(JsseSslProvider.enabledCiphers);
- }
-
- SSLSession session = sslEngine.getSession();
-
- int packetBuffer = session.getPacketBufferSize();
- myAppOutData = ByteBuffer.allocate(session.getApplicationBufferSize());
- myNetOutData = ByteBuffer.allocate(packetBuffer);
- myAppInData = ByteBuffer.allocate(session.getApplicationBufferSize());
- myNetInData = ByteBuffer.allocate(packetBuffer);
- myNetInData.flip();
- myNetOutData.flip();
- myAppInData.flip();
- myAppOutData.flip();
- }
-
- public SslChannel withServer() {
- client = false;
- return this;
- }
-
-
- @Override
- public synchronized void setSink(IOChannel net) throws IOException {
- try {
- if (sslEngine == null) {
- initSsl();
- }
- super.setSink(net);
- } catch (GeneralSecurityException e) {
- log.log(Level.SEVERE, "Error initializing ", e);
- }
- }
-
- @Override
- public IOBuffer getIn() {
- return in;
- }
-
- @Override
- public IOBuffer getOut() {
- return out;
- }
-
- /**
- * Typically called when a dataReceived callback is passed up.
- * It's up to the higher layer to decide if it can handle more data
- * and disable read interest and manage its buffers.
- *
- * We have to use one buffer.
- * @throws IOException
- */
- public int processInput(IOBuffer netIn, IOBuffer appIn) throws IOException {
- if (log.isLoggable(Level.FINEST)) {
- log.info("JSSE: processInput " + handshakeInProgress + " " + netIn.getBufferCount());
- }
- synchronized(handshakeSync) {
- if (!handshakeDone && !handshakeInProgress) {
- handshakeInProgress = true;
- handleHandshking();
- return 0;
- }
- if (handshakeInProgress) {
- return 0; // leave it there
- }
- }
- return processRealInput(netIn, appIn);
- }
-
- private synchronized int processRealInput(IOBuffer netIn, IOBuffer appIn) throws IOException {
- int rd = 0;
- boolean needsMore = true;
- boolean notEnough = false;
-
- while (needsMore) {
- if (netIn.isClosedAndEmpty()) {
- appIn.close();
- sendHandleReceivedCallback();
- return -1;
- }
- myNetInData.compact();
- int rdNow;
- try {
- rdNow = netIn.read(myNetInData);
- } finally {
- myNetInData.flip();
- }
- if (rdNow == 0 && (myNetInData.remaining() == 0 ||
- notEnough)) {
- return rd;
- }
- if (rdNow == -1) {
- appIn.close();
- sendHandleReceivedCallback();
- return rd;
- }
-
- notEnough = true; // next read of 0
- while (myNetInData.remaining() > 0) {
- myAppInData.compact();
- try {
- unwrapR = sslEngine.unwrap(myNetInData, myAppInData);
- } catch (SSLException ex) {
- log.warning("Read error: " + ex);
- close();
- return -1;
- } finally {
- myAppInData.flip();
- }
- if (myAppInData.remaining() > 0) {
- in.write(myAppInData); // all will be written
- }
- if (unwrapR.getStatus() == Status.CLOSED) {
- in.close();
- if (unwrapR.getHandshakeStatus() == HandshakeStatus.NEED_WRAP) {
- // TODO: send/receive one more packet ( handshake mode ? )
- synchronized(handshakeSync) {
- handshakeInProgress = true;
- closeHandshake = true;
- }
- handleHandshking();
-
- startSending();
- }
- break;
- }
-
- if (unwrapR.getHandshakeStatus() == HandshakeStatus.NEED_TASK) {
- tasks();
- }
- if (unwrapR.getStatus() == Status.BUFFER_OVERFLOW) {
- log.severe("Unhandled overflow " + unwrapR);
- break;
- }
- if (unwrapR.getStatus() == Status.BUFFER_UNDERFLOW) {
- // harmless
- //log.severe("Unhandled underflow " + unwrapR);
- break;
- }
- }
- sendHandleReceivedCallback();
-
-
- }
- return rd;
- }
-
- protected SSLEngineResult.HandshakeStatus tasks() {
- Runnable r = null;
- while ( (r = sslEngine.getDelegatedTask()) != null) {
- r.run();
- }
- return sslEngine.getHandshakeStatus();
- }
-
- public void startSending() throws IOException {
-
- flushing = true;
- boolean needHandshake = false;
- synchronized(handshakeSync) {
- if (handshakeInProgress) {
- return; // don't bother me.
- }
- if (!handshakeDone) {
- handshakeInProgress = true;
- needHandshake = true;
- }
- }
- if (needHandshake) {
- handleHandshking();
- return; // can't write yet.
- }
-
- startRealSending();
- }
-
- public void close() throws IOException {
- if (net.getOut().isAppendClosed()) {
- return;
- }
- sslEngine.closeOutbound(); // mark as closed
- synchronized(myNetOutData) {
- myNetOutData.compact();
-
- SSLEngineResult wrap;
- try {
- wrap = sslEngine.wrap(EMPTY, myNetOutData);
- if (wrap.getStatus() != Status.CLOSED) {
- log.warning("Unexpected close status " + wrap);
- }
- } catch (Throwable t ) {
- log.info("Error wrapping " + myNetOutData);
- } finally {
- myNetOutData.flip();
- }
- if (myNetOutData.remaining() > 0) {
- net.getOut().write(myNetOutData);
- }
- }
- // TODO: timer to close socket if we don't get
- // clean close handshake
- super.close();
- }
-
- private Object sendLock = new Object();
-
- private JsseSslProvider sslProvider;
-
- private void startRealSending() throws IOException {
- // Only one thread at a time
- synchronized (sendLock) {
- while (true) {
-
- myAppOutData.compact();
- int rd;
- try {
- rd = out.read(myAppOutData);
- } finally {
- myAppOutData.flip();
- }
- if (rd == 0) {
- break;
- }
- if (rd < 0) {
- close();
- break;
- }
-
- SSLEngineResult wrap;
- synchronized(myNetOutData) {
- myNetOutData.compact();
- try {
- wrap = sslEngine.wrap(myAppOutData,
- myNetOutData);
- } finally {
- myNetOutData.flip();
- }
- net.getOut().write(myNetOutData);
- }
- if (wrap != null) {
- switch (wrap.getStatus()) {
- case BUFFER_UNDERFLOW: {
- break;
- }
- case OK: {
- break;
- }
- case BUFFER_OVERFLOW: {
- throw new IOException("Overflow");
- }
- }
- }
- }
- }
-
- net.startSending();
- }
-
-
- // SSL handshake require slow tasks - that will need to be executed in a
- // thread anyways. Better to keep it simple ( the code is very complex ) -
- // and do the initial handshake in a thread, not in the IO thread.
- // We'll need to unregister and register again from the selector.
- private void handleHandshking() {
- if (log.isLoggable(Level.FINEST)) {
- log.info("Starting handshake");
- }
- synchronized(handshakeSync) {
- handshakeInProgress = true;
- }
-
- sslProvider.handshakeExecutor.execute(this);
- }
-
- private void endHandshake() throws IOException {
- if (log.isLoggable(Level.FINEST)) {
- log.info("Handshake done " + net.getIn().available());
- }
- synchronized(handshakeSync) {
- handshakeDone = true;
- handshakeInProgress = false;
- }
- if (flushing) {
- flushing = false;
- startSending();
- }
- if (myNetInData.remaining() > 0 || net.getIn().available() > 0) {
- // Last SSL packet also includes data.
- handleReceived(net);
- }
- }
-
- /**
- * Actual handshake magic, in background thread.
- */
- public void run() {
- try {
- boolean initial = true;
- SSLEngineResult wrap = null;
-
- HandshakeStatus hstatus = sslEngine.getHandshakeStatus();
- if (!closeHandshake &&
- (hstatus == HandshakeStatus.NOT_HANDSHAKING || initial)) {
- sslEngine.beginHandshake();
- hstatus = sslEngine.getHandshakeStatus();
- }
-
- long t0 = System.currentTimeMillis();
-
- while (hstatus != HandshakeStatus.NOT_HANDSHAKING
- && hstatus != HandshakeStatus.FINISHED
- && !net.getIn().isAppendClosed()) {
- if (System.currentTimeMillis() - t0 > handshakeTimeout) {
- throw new TimeoutException();
- }
- if (wrap != null && wrap.getStatus() == Status.CLOSED) {
- break;
- }
- if (log.isLoggable(Level.FINEST)) {
- log.info("-->doHandshake() loop: status = " + hstatus + " " +
- sslEngine.getHandshakeStatus());
- }
-
- if (hstatus == HandshakeStatus.NEED_WRAP) {
- // || initial - for client
- initial = false;
- synchronized(myNetOutData) {
- while (hstatus == HandshakeStatus.NEED_WRAP) {
- myNetOutData.compact();
- try {
- wrap = sslEngine.wrap(myAppOutData, myNetOutData);
- } catch (Throwable t) {
- log.log(Level.SEVERE, "Wrap error", t);
- close();
- return;
- } finally {
- myNetOutData.flip();
- }
- if (myNetOutData.remaining() > 0) {
- net.getOut().write(myNetOutData);
- }
- hstatus = wrap.getHandshakeStatus();
- }
- }
- net.startSending();
- } else if (hstatus == HandshakeStatus.NEED_UNWRAP) {
- while (hstatus == HandshakeStatus.NEED_UNWRAP) {
- // If we have few remaining bytes - process them
- if (myNetInData.remaining() > 0) {
- myAppInData.clear();
- if (debugWrap) {
- log.info("UNWRAP: rem=" + myNetInData.remaining());
- }
- wrap = sslEngine.unwrap(myNetInData, myAppInData);
- hstatus = wrap.getHandshakeStatus();
- myAppInData.flip();
- if (myAppInData.remaining() > 0) {
- log.severe("Unexpected data after unwrap");
- }
- if (wrap.getStatus() == Status.CLOSED) {
- break;
- }
- }
- // Still need unwrap
- if (wrap == null
- || wrap.getStatus() == Status.BUFFER_UNDERFLOW
- || (hstatus == HandshakeStatus.NEED_UNWRAP && myNetInData.remaining() == 0)) {
- myNetInData.compact();
- // non-blocking
- int rd;
- try {
- rd = net.getIn().read(myNetInData);
- if (debugWrap) {
- log.info("Read: " + rd);
- }
- } finally {
- myNetInData.flip();
- }
- if (rd == 0) {
- if (debugWrap) {
- log.info("Wait: " + handshakeTimeout);
- }
- net.getIn().waitData(handshakeTimeout);
- rd = net.getIn().read(myNetInData);
- if (debugWrap) {
- log.info("Read after wait: " + rd);
- }
- }
- if (rd < 0) {
- // in closed
- break;
- }
- }
- if (log.isLoggable(Level.FINEST)) {
- log.info("Unwrap chunk done " + hstatus + " " + wrap
- + " " + sslEngine.getHandshakeStatus());
- }
-
- }
-
- // rd may have some input bytes.
- } else if (hstatus == HandshakeStatus.NEED_TASK) {
- long t0task = System.currentTimeMillis();
- Runnable r;
- while ((r = sslEngine.getDelegatedTask()) != null) {
- r.run();
- }
- long t1task = System.currentTimeMillis();
- hstatus = sslEngine.getHandshakeStatus();
- if (log.isLoggable(Level.FINEST)) {
- log.info("Tasks done in " + (t1task - t0task) + " new status " +
- hstatus);
- }
-
- }
- if (hstatus == HandshakeStatus.NOT_HANDSHAKING) {
- //log.warning("NOT HANDSHAKING " + this);
- break;
- }
- }
- endHandshake();
- processRealInput(net.getIn(), in);
- } catch (Throwable t) {
- log.log(Level.SEVERE, "Error handshaking", t);
- try {
- close();
- net.close();
- sendHandleReceivedCallback();
- } catch (IOException ex) {
- log.log(Level.SEVERE, "Error closing", ex);
- }
- }
- }
-
-
- @Override
- public void handleReceived(IOChannel ch) throws IOException {
- processInput(net.getIn(), in);
- // Maybe we don't have data - that's fine.
- sendHandleReceivedCallback();
- }
-
- SslChannel setSslContext(SSLContext sslCtx) {
- this.sslCtx = sslCtx;
- return this;
- }
-
- SslChannel setSslProvider(JsseSslProvider con) {
- this.sslProvider = con;
- return this;
- }
-
- public Object getAttribute(String name) {
- if (SslProvider.ATT_SSL_CERT.equals(name)) {
- try {
- return sslEngine.getSession().getPeerCertificateChain();
- } catch (SSLPeerUnverifiedException e) {
- return null; // no re-negotiation
- }
- } else if (SslProvider.ATT_SSL_CIPHER.equals(name)) {
- return sslEngine.getSession().getCipherSuite();
- } else if (SslProvider.ATT_SSL_KEY_SIZE.equals(name)) {
- // looks like we need to get it from the string cipher
- CipherData c_aux[] = ciphers;
-
- int size = 0;
- String cipherSuite = sslEngine.getSession().getCipherSuite();
- for (int i = 0; i < c_aux.length; i++) {
- if (cipherSuite.indexOf(c_aux[i].phrase) >= 0) {
- size = c_aux[i].keySize;
- break;
- }
- }
- return size;
- } else if (SslProvider.ATT_SSL_SESSION_ID.equals(name)) {
- byte [] ssl_session = sslEngine.getSession().getId();
- if ( ssl_session == null)
- return null;
- StringBuilder buf=new StringBuilder();
- for(int x=0; x<ssl_session.length; x++) {
- String digit=Integer.toHexString(ssl_session[x]);
- if (digit.length()<2) buf.append('0');
- if (digit.length()>2) digit=digit.substring(digit.length()-2);
- buf.append(digit);
- }
- return buf.toString();
- }
-
- if (net != null) {
- return net.getAttribute(name);
- }
- return null;
- }
-
-
- /**
- * Simple data class that represents the cipher being used, along with the
- * corresponding effective key size. The specified phrase must appear in the
- * name of the cipher suite to be recognized.
- */
-
- static final class CipherData {
-
- public String phrase = null;
-
- public int keySize = 0;
-
- public CipherData(String phrase, int keySize) {
- this.phrase = phrase;
- this.keySize = keySize;
- }
-
- }
-
-
- /**
- * A mapping table to determine the number of effective bits in the key
- * when using a cipher suite containing the specified cipher name. The
- * underlying data came from the TLS Specification (RFC 2246), Appendix C.
- */
- static final CipherData ciphers[] = {
- new CipherData("_WITH_NULL_", 0),
- new CipherData("_WITH_IDEA_CBC_", 128),
- new CipherData("_WITH_RC2_CBC_40_", 40),
- new CipherData("_WITH_RC4_40_", 40),
- new CipherData("_WITH_RC4_128_", 128),
- new CipherData("_WITH_DES40_CBC_", 40),
- new CipherData("_WITH_DES_CBC_", 56),
- new CipherData("_WITH_3DES_EDE_CBC_", 168),
- new CipherData("_WITH_AES_128_CBC_", 128),
- new CipherData("_WITH_AES_256_CBC_", 256)
- };
-
-}
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/lite/io/package.html b/modules/tomcat-lite/java/org/apache/tomcat/lite/io/package.html
deleted file mode 100644
index 07a0f30..0000000
--- a/modules/tomcat-lite/java/org/apache/tomcat/lite/io/package.html
+++ /dev/null
@@ -1,7 +0,0 @@
-IO layer based on tomcat coyote connector and utils.
-
-There are many big changes:
-<ul>
- <li>
- <li>
-</ul>
\ No newline at end of file
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/lite/proxy/CopyCallback.java b/modules/tomcat-lite/java/org/apache/tomcat/lite/proxy/CopyCallback.java
deleted file mode 100644
index cfb499d..0000000
--- a/modules/tomcat-lite/java/org/apache/tomcat/lite/proxy/CopyCallback.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- */
-package org.apache.tomcat.lite.proxy;
-
-import java.io.IOException;
-import java.nio.ByteBuffer;
-
-import org.apache.tomcat.lite.http.HttpChannel;
-import org.apache.tomcat.lite.io.IOBuffer;
-import org.apache.tomcat.lite.io.IOChannel;
-import org.apache.tomcat.lite.io.IOConnector;
-
-/**
- * Used by socks and http proxy. Will copy received data to a different
- * channel.
- */
-public class CopyCallback implements IOConnector.DataReceivedCallback {
- IOChannel mOutBuffer;
-
- public CopyCallback(IOChannel sc) {
- mOutBuffer = sc;
- }
-
- @Override
- public void handleReceived(IOChannel ch) throws IOException {
- IOBuffer inBuffer = ch.getIn();
- IOChannel outBuffer = mOutBuffer;
- if (outBuffer == null &&
- ch instanceof HttpChannel) {
- outBuffer =
- (IOChannel) ((HttpChannel)ch).getRequest().getAttribute("P");
- }
- // body.
- while (true) {
- if (outBuffer == null || outBuffer.getOut() == null) {
- return;
- }
- if (outBuffer.getOut().isAppendClosed()) {
- return;
- }
-
- ByteBuffer bb = outBuffer.getOut().getWriteBuffer();
- int rd = inBuffer.read(bb);
- outBuffer.getOut().releaseWriteBuffer(rd);
-
- if (rd == 0) {
- outBuffer.startSending();
- return;
- }
- if (rd < 0) {
- outBuffer.getOut().close();
- outBuffer.startSending();
- return;
- }
- }
- }
- }
\ No newline at end of file
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/lite/proxy/HttpProxyService.java b/modules/tomcat-lite/java/org/apache/tomcat/lite/proxy/HttpProxyService.java
deleted file mode 100644
index 6c426b7..0000000
--- a/modules/tomcat-lite/java/org/apache/tomcat/lite/proxy/HttpProxyService.java
+++ /dev/null
@@ -1,368 +0,0 @@
-/*
- */
-package org.apache.tomcat.lite.proxy;
-
-import java.io.IOException;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import org.apache.tomcat.lite.http.HttpChannel;
-import org.apache.tomcat.lite.http.HttpConnector;
-import org.apache.tomcat.lite.http.HttpRequest;
-import org.apache.tomcat.lite.http.HttpResponse;
-import org.apache.tomcat.lite.http.MultiMap;
-import org.apache.tomcat.lite.http.HttpChannel.HttpService;
-import org.apache.tomcat.lite.http.HttpChannel.RequestCompleted;
-import org.apache.tomcat.lite.io.CBuffer;
-import org.apache.tomcat.lite.io.IOChannel;
-import org.apache.tomcat.lite.io.IOConnector;
-import org.apache.tomcat.lite.io.CBuffer;
-import org.apache.tomcat.lite.io.SocketConnector;
-
-/**
- * Http callback for the server-side. Will forward all requests to
- * a remote http server - either using proxy mode ( GET http://... ) or
- * forward requests ( GET /foo -> will be served by the remote server ).
- *
- * This is not blocking (except the connect, which currenly blocks on dns).
- */
-public class HttpProxyService implements HttpService {
-
- // target - when used in forwarding mode.
- String target = "localhost";
- int port = 8802;
-
- static Logger log = Logger.getLogger("HttpProxy");
- public static boolean debug = false;
- boolean keepOpen = true;
-
- // client side - this connect to the real server that generates the resp.
- ProxyClientCallback clientHeadersReceived = new ProxyClientCallback();
-
- HttpConnector httpConnector;
- IOConnector ioConnector;
-
- public HttpProxyService withSelector(IOConnector pool) {
- this.ioConnector = pool;
- return this;
- }
-
- public HttpProxyService withHttpClient(HttpConnector pool) {
- this.httpConnector = pool;
- return this;
- }
-
- public HttpProxyService withTarget(String host, int port) {
- this.target = host;
- this.port = port;
- return this;
- }
-
- private IOConnector getSelector() {
- if (ioConnector == null) {
- ioConnector = new SocketConnector();
- }
- return ioConnector;
- }
-
- private HttpConnector getHttpConnector() {
- if (httpConnector == null) {
- httpConnector = new HttpConnector(getSelector());
- }
- return httpConnector;
- }
-
- // Connects to the target CONNECT server, as client, forwards
- static class ProxyConnectClientConnection implements IOConnector.ConnectedCallback {
-
- IOChannel serverNet;
- private HttpChannel serverHttp;
-
- public ProxyConnectClientConnection(HttpChannel sproc) throws IOException {
- this.serverNet = sproc.getSink();
- this.serverHttp = sproc;
- }
-
- @Override
- public void handleConnected(IOChannel ioch) throws IOException {
- if (!ioch.isOpen()) {
- serverNet.close();
- log.severe("Connection failed");
- return;
- }
- afterClientConnect(ioch);
-
- ioch.setDataReceivedCallback(new CopyCallback(serverNet));
- //ioch.setDataFlushedCallback(new ProxyFlushedCallback(serverNet, ioch));
- serverNet.setDataReceivedCallback(new CopyCallback(ioch));
- //serverNet.setDataFlushedCallback(new ProxyFlushedCallback(ioch, serverNet));
-
- ioch.sendHandleReceivedCallback();
- }
-
- static byte[] OK = "HTTP/1.1 200 OK\r\n\r\n".getBytes();
-
- protected void afterClientConnect(IOChannel clientCh) throws IOException {
- serverNet.getOut().queue(OK);
- serverNet.startSending();
-
- serverHttp.release(); // no longer used
- }
- }
-
- /**
- * Parse the req, dispatch the connection.
- */
- @Override
- public void service(HttpRequest serverHttpReq, HttpResponse serverHttpRes)
- throws IOException {
-
- String dstHost = target; // default target ( for normal req ).
- int dstPort = port;
-
- // TODO: more flexibility/callbacks on selecting the target, acl, etc
- if (serverHttpReq.method().equals("CONNECT")) {
- // SSL proxy - just connect and forward all packets
- // TODO: optimize, add error checking
- String[] hostPort = serverHttpReq.requestURI().toString().split(":");
- String host = hostPort[0];
- int port = 443;
- if (hostPort.length > 1) {
- port = Integer.parseInt(hostPort[1]);
- }
- if (log.isLoggable(Level.FINE)) {
- HttpChannel server = serverHttpReq.getHttpChannel();
- log.info("NEW: " + server.getId() + " " + dstHost + " " +
- server.getRequest().getMethod() +
- " " + server.getRequest().getRequestURI() + " " +
- server.getIn());
- }
-
- try {
- getSelector().connect(host, port,
- new ProxyConnectClientConnection(serverHttpReq.getHttpChannel()));
- } catch (IOException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- return;
- }
-
-
- CBuffer origURIx = serverHttpReq.requestURI();
-// String origURI = origURIx.toString();
-// if (origURI.startsWith("http://")) {
-// // Real proxy - extract client address, modify the uri.
-// // TODO: optimize the strings.
-// int start = origURI.indexOf('/', 7);
-// String hostPortS = (start == -1) ?
-// origURI.subSequence(7, origURI.length()).toString() :
-// origURI.subSequence(7, start).toString();
-// String[] hostPort = hostPortS.split(":");
-//
-// dstHost = hostPort[0];
-// dstPort = (hostPort.length > 1) ? Integer.parseInt(hostPort[1]) :
-// 80;
-//
-// if (start >= 0) {
-// serverHttpReq.requestURI().set(origURI.substring(start));
-// } else {
-// serverHttpReq.requestURI().set("/");
-// }
-// } else {
- // Adjust the host header.
- CBuffer hostHdr =
- serverHttpReq.getMimeHeaders().getHeader("host");
- if (hostHdr != null) {
- hostHdr.recycle();
- CBuffer cb = hostHdr;
- cb.append(dstHost);
- if (dstPort != 80) {
- cb.append(':');
- cb.appendInt(dstPort);
- }
- }
-// }
- if (debug) {
- HttpChannel server = serverHttpReq.getHttpChannel();
- log.info("START: " + server.getId() + " " + dstHost + " " +
- server.getRequest().getMethod() +
- " " + server.getRequest().getRequestURI() + " " +
- server.getIn());
- }
-
- // Send the request with a non-blocking write
- HttpChannel serverHttp = serverHttpReq.getHttpChannel();
-
- // Client connection
- HttpChannel httpClient = getHttpConnector().get(dstHost, dstPort);
-
- serverHttp.getRequest().setAttribute("CLIENT", httpClient);
- httpClient.getRequest().setAttribute("SERVER", serverHttp);
- serverHttp.getRequest().setAttribute("P", httpClient);
- httpClient.getRequest().setAttribute("P", serverHttp);
-
- httpClient.setHttpService(clientHeadersReceived);
-
- // Will send the original request (TODO: small changes)
- // Response is not affected ( we use the callback )
- httpClient.getRequest().method().set(serverHttp.getRequest().method());
- httpClient.getRequest().requestURI().set(serverHttp.getRequest().requestURI());
- if (serverHttp.getRequest().queryString().length() != 0) {
- httpClient.getRequest().queryString().set(serverHttp.getRequest().queryString());
- }
-
- httpClient.getRequest().protocol().set(serverHttp.getRequest().protocol());
-
- //cstate.reqHeaders.addValue(name)
- copyHeaders(serverHttp.getRequest().getMimeHeaders(),
- httpClient.getRequest().getMimeHeaders() /*dest*/);
-
- // For debug
- httpClient.getRequest().getMimeHeaders().remove("Accept-Encoding");
-
- if (!keepOpen) {
- httpClient.getRequest().getMimeHeaders().setValue("Connection").set("Close");
- }
-
- // Any data
- serverHttp.setDataReceivedCallback(copy);
- copy.handleReceived(serverHttp);
-
- httpClient.send();
-
-
- //serverHttp.handleReceived(serverHttp.getSink());
- //httpClient.flush(); // send any data still there
-
- httpClient.setCompletedCallback(done);
- // Will call release()
- serverHttp.setCompletedCallback(done);
-
- serverHttpReq.async();
- }
-
- static HttpDoneCallback done = new HttpDoneCallback();
- static CopyCallback copy = new CopyCallback(null);
- // POST: after sendRequest(ch) we need to forward the body !!
-
-
- static void copyHeaders(MultiMap mimeHeaders, MultiMap dest)
- throws IOException {
- for (int i = 0; i < mimeHeaders.size(); i++) {
- CBuffer name = mimeHeaders.getName(i);
- CBuffer val = dest.addValue(name.toString());
- val.set(mimeHeaders.getValue(i));
- }
- }
-
- /**
- * HTTP _CLIENT_ callback - from tomcat to final target.
- */
- public class ProxyClientCallback implements HttpService {
- /**
- * Headers received from the client (content http server).
- * TODO: deal with version missmatches.
- */
- @Override
- public void service(HttpRequest clientHttpReq, HttpResponse clientHttpRes) throws IOException {
- HttpChannel serverHttp = (HttpChannel) clientHttpReq.getAttribute("SERVER");
-
- try {
- serverHttp.getResponse().setStatus(clientHttpRes.getStatus());
- serverHttp.getResponse().getMessageBuffer().set(clientHttpRes.getMessageBuffer());
- copyHeaders(clientHttpRes.getMimeHeaders(),
- serverHttp.getResponse().getMimeHeaders());
-
- serverHttp.getResponse().getMimeHeaders().addValue("TomcatProxy").set("True");
-
- clientHttpReq.getHttpChannel().setDataReceivedCallback(copy);
- copy.handleReceived(clientHttpReq.getHttpChannel());
-
- serverHttp.startSending();
-
-
- //clientHttpReq.flush(); // send any data still there
-
- // if (clientHttpReq.getHttpChannel().getIn().isClosedAndEmpty()) {
- // serverHttp.getOut().close(); // all data from client already in buffers
- // }
-
- } catch (IOException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- }
-
- static final class HttpDoneCallback implements RequestCompleted {
-
- public HttpDoneCallback() {
- }
-
- @Override
- public void handle(HttpChannel doneCh, Object extraData) throws IOException {
- HttpChannel serverCh =
- (HttpChannel) doneCh.getRequest().getAttribute("SERVER");
- HttpChannel clientCh = doneCh;
- String tgt = "C";
- if (serverCh == null) {
- serverCh = doneCh;
- clientCh =
- (HttpChannel) doneCh.getRequest().getAttribute("CLIENT");
- tgt = "S";
- }
- if (serverCh == null || clientCh == null) {
- return;
- }
- if (doneCh.getError()) {
- serverCh.abort("Proxy error");
- clientCh.abort("Proxy error");
- return;
- }
-
- if (log.isLoggable(Level.FINE)) {
- HttpChannel peerCh =
- (HttpChannel) doneCh.getRequest().getAttribute("SERVER");
- if (peerCh == null) {
- peerCh =
- (HttpChannel) doneCh.getRequest().getAttribute("CLIENT");
- } else {
-
- }
- log.info(tgt + " " + peerCh.getId() + " " +
- doneCh.getTarget() + " " +
- doneCh.getRequest().getMethod() +
- " " + doneCh.getRequest().getRequestURI() + " " +
- doneCh.getResponse().getStatus() + " IN:" + doneCh.getIn()
- + " OUT:" + doneCh.getOut() +
- " SIN:" + peerCh.getIn() +
- " SOUT:" + peerCh.getOut() );
- }
- // stop forwarding. After this call the client object will be
- // recycled
- //clientCB.outBuffer = null;
-
- // We must releaes both at same time
- synchronized (this) {
-
- serverCh.complete();
-
- if (clientCh.getRequest().getAttribute("SERVER") == null) {
- return;
- }
- if (clientCh.isDone() && serverCh.isDone()) {
- clientCh.getRequest().setAttribute("SERVER", null);
- serverCh.getRequest().setAttribute("CLIENT", null);
- clientCh.getRequest().setAttribute("P", null);
- serverCh.getRequest().setAttribute("P", null);
- // Reuse the objects.
- serverCh.release();
- clientCh.release();
- }
- }
- }
- }
-
-
-}
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/lite/proxy/ProxyFlushedCallback.java b/modules/tomcat-lite/java/org/apache/tomcat/lite/proxy/ProxyFlushedCallback.java
deleted file mode 100644
index 63fb18b..0000000
--- a/modules/tomcat-lite/java/org/apache/tomcat/lite/proxy/ProxyFlushedCallback.java
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- */
-package org.apache.tomcat.lite.proxy;
-
-import java.io.IOException;
-
-import org.apache.tomcat.lite.io.IOChannel;
-import org.apache.tomcat.lite.io.IOConnector;
-
-public final class ProxyFlushedCallback implements IOConnector.DataFlushedCallback {
- IOChannel peerCh;
-
- public ProxyFlushedCallback(IOChannel ch2, IOChannel clientChannel2) {
- peerCh = ch2;
- }
-
- @Override
- public void handleFlushed(IOChannel ch) throws IOException {
- if (ch.getOut().isClosedAndEmpty()) {
- if (!peerCh.getOut().isAppendClosed()) {
- peerCh.close();
- }
- }
- }
-}
\ No newline at end of file
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/lite/proxy/SocksServer.java b/modules/tomcat-lite/java/org/apache/tomcat/lite/proxy/SocksServer.java
deleted file mode 100644
index ad68345..0000000
--- a/modules/tomcat-lite/java/org/apache/tomcat/lite/proxy/SocksServer.java
+++ /dev/null
@@ -1,448 +0,0 @@
-/*
- */
-package org.apache.tomcat.lite.proxy;
-
-import java.io.IOException;
-import java.net.InetAddress;
-import java.net.InetSocketAddress;
-import java.net.SocketAddress;
-import java.nio.ByteBuffer;
-import java.nio.CharBuffer;
-import java.util.Timer;
-import java.util.TimerTask;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import org.apache.tomcat.lite.io.IOBuffer;
-import org.apache.tomcat.lite.io.IOChannel;
-import org.apache.tomcat.lite.io.IOConnector;
-import org.apache.tomcat.lite.io.SocketConnector;
-
-/**
- * A test for the selector package, and helper for the proxy -
- * a SOCKS4a server.
- *
- * Besides the connection initialization, it's almost the
- * same as the CONNECT method in http proxy.
- *
- * http://ftp.icm.edu.pl/packages/socks/socks4/SOCKS4.protocol
- * http://www.smartftp.com/Products/SmartFTP/RFC/socks4a.protocol
- * http://www.faqs.org/rfcs/rfc1928.html
- * https://svn.torproject.org/svn/tor/trunk/doc/spec/socks-extensions.txt
- *
- * In firefox, set network.proxy.socks_remote_dns = true to do DNS via proxy.
- *
- * Also interesting:
- * http://transocks.sourceforge.net/
- *
- * @author Costin Manolache
- */
-public class SocksServer implements Runnable, IOConnector.ConnectedCallback {
- protected int port = 2080;
-
- protected IOConnector ioConnector;
- protected static Logger log = Logger.getLogger("SocksServer");
-
- protected long idleTimeout = 10 * 60000; // 10 min
-
- protected long lastConnection = 0;
- protected long totalConTime = 0;
- protected AtomicInteger totalConnections = new AtomicInteger();
-
- protected AtomicInteger active = new AtomicInteger();
-
- protected long inBytes;
- protected long outBytes;
- protected static int sockets;
-
- public int getPort() {
- return port;
- }
-
- public int getActive() {
- return active.get();
- }
-
- public int getTotal() {
- return totalConnections.get();
- }
-
- public void setPort(int port) {
- this.port = port;
- }
-
- public void handleAccepted(IOChannel accepted) throws IOException {
- lastConnection = System.currentTimeMillis();
- active.incrementAndGet();
- totalConnections.incrementAndGet();
- sockets++;
-
- final SocksServerConnection socksCon = new SocksServerConnection(accepted);
- socksCon.pool = ioConnector;
- socksCon.server = this;
-
- accepted.setDataReceivedCallback(socksCon);
- socksCon.handleReceived(accepted);
- }
-
- /**
- * Exit if no activity happens.
- */
- public void setIdleTimeout(long to) {
- idleTimeout = to;
- }
-
- public long getIdleTimeout() {
- return idleTimeout;
- }
-
- public void stop() {
- ioConnector.stop();
- }
-
- public void initServer() throws IOException {
- if (ioConnector == null) {
- ioConnector = new SocketConnector();
- }
- ioConnector.acceptor(this, Integer.toString(port), null);
-
- final Timer timer = new Timer(true /* daemon */);
- timer.scheduleAtFixedRate(new TimerTask() {
- @Override
- public void run() {
- try {
- // if lastConnection == 0 - it'll terminate on first timer
- float avg = (totalConnections.get() > 0) ?
- totalConTime / totalConnections.get() : 0;
- System.err.println("Socks:"
- + "\ttotal=" + totalConnections
- + "\tin=" + inBytes
- + "\tout=" + outBytes
- + "\tavg=" + (int) avg);
- if (active.get() <= 0
- && idleTimeout > 0
- && System.currentTimeMillis() - lastConnection > idleTimeout) {
- System.err.println("Idle timeout");
- stop();
- this.cancel();
- timer.cancel();
- }
- } catch (Throwable t) {
- log.log(Level.SEVERE, "Error in timer", t);
- }
- }
- }, 5 * 60 * 1000, 5 * 60 * 1000); // 5
-
-
- }
-
-
- public static class SocksServerConnection implements IOConnector.DataReceivedCallback, IOConnector.ConnectedCallback {
-
- protected SocksServer server;
-
- boolean headReceived;
- boolean head5Received = false;
-
- ByteBuffer headBuffer = ByteBuffer.allocate(256);
- ByteBuffer headReadBuffer = headBuffer.duplicate();
-
- ByteBuffer headResBuffer = ByteBuffer.allocate(256);
- IOConnector pool;
- byte ver;
- byte cmd;
- long startTime = System.currentTimeMillis();
-
- static final int CMD_CONNECT = 0;
- static final byte CMD_RESOLVE = (byte) 0xF0;
-
- int port;
- byte[] hostB = new byte[4];
- CharBuffer userId = CharBuffer.allocate(256);
- CharBuffer hostName = CharBuffer.allocate(256);
-
- SocketAddress sa = null;
-
- private byte atyp;
-
- IOChannel serverCh;
-
- public SocksServerConnection(IOChannel accepted) {
- this.serverCh = accepted;
- }
-
- protected void afterClientConnect(IOChannel clientCh) throws IOException {
- headResBuffer.clear();
- if (ver == 4) {
- headResBuffer.put((byte) 0);
- headResBuffer.put((byte) 90);
- for (int i = 0; i < 6; i++ ) {
- headResBuffer.put((byte) 0);
- }
- } else {
- headResBuffer.put((byte) 5);
- headResBuffer.put((byte) 0);
- headResBuffer.put((byte) 0);
- headResBuffer.put((byte) 1); // ip
-
- headResBuffer.put(hostB);
- int port2 = (Integer) clientCh.getAttribute(IOChannel.ATT_REMOTE_PORT);
- headResBuffer.putShort((short) port2);
- }
-
- headResBuffer.flip();
-
- serverCh.getOut().queue(headResBuffer);
- log.fine("Connected " + sa.toString());
-
- if (headReadBuffer.remaining() > 0) {
- serverCh.getOut().queue(headReadBuffer);
- }
- serverCh.startSending();
- }
-
- public void afterClose() {
- long conTime = System.currentTimeMillis() - startTime;
- int a = server.active.decrementAndGet();
- if (a < 0) {
- System.err.println("negative !!");
- server.active.set(0);
- }
-// System.err.println(sa + "\tsR:" +
-// received
-// + "\tcR:" + clientReceived
-// + "\tactive:" + a
-// + "\ttotC:" + server.totalConnections
-// + "\ttime:" + conTime);
-// server.inBytes += received;
-// server.totalConTime += conTime;
-// server.outBytes += clientReceived;
- }
-
-
- protected int parseHead() throws IOException {
- // data is between 0 and pos.
- int pos = headBuffer.position();
- headReadBuffer.clear();
- headReadBuffer.limit(pos);
- if (headReadBuffer.remaining() < 2) {
- return -1;
- }
-
- ByteBuffer bb = headReadBuffer;
- ver = bb.get();
- if (ver == 5) {
- return parseHead5();
- }
- if (headReadBuffer.remaining() < 8) {
- return -1;
- }
- cmd = bb.get();
- port = bb.getShort();
- bb.get(hostB);
- userId.clear();
- int rc = readStringZ(bb, userId);
- // Mozilla userid: MOZ ...
- if (rc == -1) {
- return rc;
- }
- if (hostB[0] == 0 && hostB[1] == 0 && hostB[2] == 0) {
- // 0.0.0.x
- atyp = 3;
- hostName.clear();
- rc = readStringZ(bb, hostName);
- if (rc == -1) {
- return rc;
- }
- } else {
- atyp = 1;
- }
-
- headReceived = true;
-
- return 4;
- }
-
- protected int parseHead5_2() throws IOException {
- // data is between 0 and pos.
- int pos = headBuffer.position();
-
- headReadBuffer.clear();
- headReadBuffer.limit(pos);
-
- if (headReadBuffer.remaining() < 7) {
- return -1;
- }
-
- ByteBuffer bb = headReadBuffer;
- ver = bb.get();
- cmd = bb.get();
- bb.get(); // reserved
- atyp = bb.get();
- if (atyp == 1) {
- bb.get(hostB);
- } else if (atyp == 3) {
- hostName.clear();
- int rc = readStringN(bb, hostName);
- if (rc == -1) {
- return rc;
- }
- } // ip6 not supported right now, easy to add
-
- port = bb.getShort();
-
- head5Received = true;
-
- return 5;
- }
-
- private int parseHead5() {
- ByteBuffer bb = headReadBuffer;
- int nrMethods = ((int)bb.get()) & 0xFF;
- if (bb.remaining() < nrMethods) {
- return -1;
- }
- for (int i = 0; i < nrMethods; i++) {
- // ignore
- bb.get();
- }
- return 5;
- }
-
- private int readStringZ(ByteBuffer bb, CharBuffer bc) throws IOException {
- bc.clear();
- while (true) {
- if (!bb.hasRemaining()) {
- return -1; // not complete
- }
- byte b = bb.get();
- if (b == 0) {
- bc.flip();
- return 0;
- } else {
- bc.put((char) b);
- }
- }
- }
-
- private int readStringN(ByteBuffer bb, CharBuffer bc) throws IOException {
- bc.clear();
- int len = ((int) bb.get()) & 0xff;
- for (int i = 0; i < len; i++) {
- if (!bb.hasRemaining()) {
- return -1; // not complete
- }
- byte b = bb.get();
- bc.put((char) b);
- }
- bc.flip();
- return len;
- }
-
- static ExecutorService connectTP = Executors.newCachedThreadPool();
-
- protected void startClientConnection() throws IOException {
- // TODO: use different thread ?
- if (atyp == 3) {
- connectTP.execute(new Runnable() {
-
- public void run() {
- try {
- sa = new InetSocketAddress(hostName.toString(), port);
- pool.connect(hostName.toString(), port,
- SocksServerConnection.this);
- } catch (Exception ex) {
- log.severe("Error connecting");
- }
- }
- });
- } else {
- InetAddress addr = InetAddress.getByAddress(hostB);
- pool.connect(addr.toString(), port, this);
- } // TODO: ip6
- }
-
- public void handleConnected(IOChannel ioch) throws IOException {
- ioch.setDataReceivedCallback(new CopyCallback(serverCh));
- //ioch.setDataFlushedCallback(new ProxyFlushedCallback(serverCh, ioch));
-
- serverCh.setDataReceivedCallback(new CopyCallback(ioch));
- //serverCh.setDataFlushedCallback(new ProxyFlushedCallback(ioch, serverCh));
-
- afterClientConnect(ioch);
-
- ioch.sendHandleReceivedCallback();
- }
-
-
- @Override
- public void handleReceived(IOChannel net) throws IOException {
- IOBuffer ch = net.getIn();
- //SelectorChannel ch = (SelectorChannel) ioch;
- if (!headReceived) {
- int rd = ch.read(headBuffer);
- if (rd == 0) {
- return;
- }
- if (rd == -1) {
- ch.close();
- }
-
- rd = parseHead();
- if (rd < 0) {
- return; // need more
- }
- if (rd == 5) {
- headResBuffer.clear();
- headResBuffer.put((byte) 5);
- headResBuffer.put((byte) 0);
- headResBuffer.flip();
- net.getOut().queue(headResBuffer);
- net.startSending();
- headReceived = true;
- headBuffer.clear();
- return;
- } else {
- headReceived = true;
- head5Received = true;
- startClientConnection();
- }
- }
-
- if (!head5Received) {
- int rd = ch.read(headBuffer);
- if (rd == 0) {
- return;
- }
- if (rd == -1) {
- ch.close();
- }
-
- rd = parseHead5_2();
- if (rd < 0) {
- return; // need more
- }
-
- startClientConnection();
- }
- }
- }
-
- @Override
- public void run() {
- try {
- initServer();
- } catch (IOException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
-
- @Override
- public void handleConnected(IOChannel ch) throws IOException {
- handleAccepted(ch);
- }
-}
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/lite/proxy/StaticContentService.java b/modules/tomcat-lite/java/org/apache/tomcat/lite/proxy/StaticContentService.java
deleted file mode 100644
index e50e73e..0000000
--- a/modules/tomcat-lite/java/org/apache/tomcat/lite/proxy/StaticContentService.java
+++ /dev/null
@@ -1,129 +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.lite.proxy;
-
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.util.logging.Logger;
-
-import org.apache.tomcat.lite.http.HttpRequest;
-import org.apache.tomcat.lite.http.HttpResponse;
-import org.apache.tomcat.lite.http.HttpChannel.HttpService;
-import org.apache.tomcat.lite.io.BBuffer;
-import org.apache.tomcat.lite.io.BBucket;
-import org.apache.tomcat.lite.io.IOBuffer;
-
-/*
- *
- * Serve static content, from memory.
- */
-public class StaticContentService implements HttpService {
- protected Logger log = Logger.getLogger("coyote.static");
- protected BBucket mb;
-
- protected boolean chunked = false;
- int code = 200;
-
- protected String contentType = "text/plain";
-
-
- public StaticContentService() {
- }
-
- /**
- * Used for testing chunked encoding.
- * @return
- */
- public StaticContentService chunked() {
- chunked = true;
- return this;
- }
-
- public StaticContentService setData(byte[] data) {
- mb = BBuffer.wrapper(data, 0, data.length);
- return this;
- }
-
- public StaticContentService setStatus(int status) {
- this.code = status;
- return this;
- }
-
- public StaticContentService withLen(int len) {
- byte[] data = new byte[len];
- for (int i = 0; i < len; i++) {
- data[i] = 'A';
- }
- mb = BBuffer.wrapper(data, 0, data.length);
- return this;
- }
-
-
- public StaticContentService setData(CharSequence data) {
- try {
- IOBuffer tmp = new IOBuffer(null);
- tmp.append(data);
- mb = tmp.readAll(null);
- } catch (IOException e) {
- }
- return this;
- }
-
- public StaticContentService setContentType(String ct) {
- this.contentType = ct;
- return this;
- }
-
- public void setFile(String path) {
- try {
- FileInputStream fis = new FileInputStream(path);
- BBuffer bc = BBuffer.allocate(4096);
-
- byte b[] = new byte[4096];
- int rd = 0;
- while ((rd = fis.read(b)) > 0) {
- bc.append(b, 0, rd);
- }
- mb = bc;
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- }
-
- @Override
- public void service(HttpRequest httpReq, HttpResponse res) throws IOException {
-
- res.setStatus(code);
-
- if (!chunked) {
- res.setContentLength(mb.remaining());
- }
- res.setContentType(contentType);
-
- int len = mb.remaining();
- int first = 0;
-
- if (chunked) {
- first = len / 2;
- res.getBody()
- .queue(BBuffer.wrapper(mb, 0, first));
- res.flush();
- }
-
- res.getBody().queue(BBuffer.wrapper(mb, 0, len - first));
- }
-}
\ No newline at end of file
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/lite/service/IOStatus.java b/modules/tomcat-lite/java/org/apache/tomcat/lite/service/IOStatus.java
deleted file mode 100644
index 0623e8d..0000000
--- a/modules/tomcat-lite/java/org/apache/tomcat/lite/service/IOStatus.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- */
-package org.apache.tomcat.lite.service;
-
-import java.io.IOException;
-import java.util.List;
-
-import org.apache.tomcat.lite.http.HttpConnectionPool;
-import org.apache.tomcat.lite.http.HttpRequest;
-import org.apache.tomcat.lite.http.HttpResponse;
-import org.apache.tomcat.lite.http.HttpWriter;
-import org.apache.tomcat.lite.http.HttpChannel.HttpService;
-import org.apache.tomcat.lite.http.HttpConnectionPool.RemoteServer;
-import org.apache.tomcat.lite.http.HttpConnector.HttpConnection;
-import org.apache.tomcat.lite.io.IOChannel;
-
-/**
- * Dump status of a connection pool.
- */
-public class IOStatus implements HttpService {
-
- private HttpConnectionPool pool;
-
- public IOStatus(HttpConnectionPool pool) {
- this.pool = pool;
- }
-
- @Override
- public void service(HttpRequest httpReq, HttpResponse httpRes)
- throws IOException {
- HttpConnectionPool sc = pool;
- HttpWriter out = httpRes.getBodyWriter();
-
- httpRes.setContentType("text/plain");
- // TODO: use JMX/DynamicObject to get all public info
- out.println("hosts=" + sc.getTargetCount());
- out.println("waiting=" + sc.getSocketCount());
- out.println("closed=" + sc.getClosedSockets());
- out.println();
-
- for (RemoteServer remote: sc.getServers()) {
- out.append(remote.target);
- out.append("=");
- List<HttpConnection> connections = remote.getConnections();
- out.println(Integer.toString(connections.size()));
-
- for (IOChannel ch: connections) {
- out.println(ch.getId() +
- " " + ch.toString());
- }
- out.println();
- }
-
- }
-
-}
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/lite/service/LogConfig.java b/modules/tomcat-lite/java/org/apache/tomcat/lite/service/LogConfig.java
deleted file mode 100644
index b3b99a8..0000000
--- a/modules/tomcat-lite/java/org/apache/tomcat/lite/service/LogConfig.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright 2004 Costin Manolache
- * 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.lite.service;
-
-import java.io.IOException;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import org.apache.tomcat.lite.http.HttpRequest;
-import org.apache.tomcat.lite.http.HttpResponse;
-import org.apache.tomcat.lite.http.HttpChannel.HttpService;
-
-/**
- * Log configuration
- *
- */
-public class LogConfig implements HttpService {
-
- /**
- * Framework can set this attribute with comma separated
- * list of loggers to set to debug level.
- * This is used at startup.
- */
- public void setDebug(String debug) {
- for (String log : debug.split(",")) {
- Logger logger = Logger.getLogger(log);
- logger.setLevel(Level.INFO);
- }
- }
-
- /**
- *
- */
- public void setWarn(String nodebug) {
- for (String log : nodebug.split(",")) {
- Logger logger = Logger.getLogger(log);
- logger.setLevel(Level.WARNING);
- }
- }
-
- @Override
- public void service(HttpRequest httpReq, HttpResponse httpRes)
- throws IOException {
- String debug = httpReq.getParameter("debug");
- setDebug(debug);
- String warn = httpReq.getParameter("warn");
- setWarn(warn);
- }
-}
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/lite/util/FastHttpDateFormat.java b/modules/tomcat-lite/java/org/apache/tomcat/lite/util/FastHttpDateFormat.java
deleted file mode 100644
index 5f76152..0000000
--- a/modules/tomcat-lite/java/org/apache/tomcat/lite/util/FastHttpDateFormat.java
+++ /dev/null
@@ -1,231 +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.lite.util;
-
-import java.text.DateFormat;
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-import java.util.Locale;
-import java.util.TimeZone;
-import java.util.concurrent.ConcurrentHashMap;
-
-/**
- * Utility class to generate HTTP dates.
- *
- * @author Remy Maucherat
- */
-public final class FastHttpDateFormat {
-
-
- // -------------------------------------------------------------- Variables
-
-
- protected static final int CACHE_SIZE =
- Integer.parseInt(System.getProperty("org.apache.tomcat.util.http.FastHttpDateFormat.CACHE_SIZE", "1000"));
-
-
- /**
- * HTTP date format.
- */
- protected static final SimpleDateFormat format =
- new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US);
-
-
- /**
- * The set of SimpleDateFormat formats to use in getDateHeader().
- */
- protected static final SimpleDateFormat formats[] = {
- new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US),
- new SimpleDateFormat("EEEEEE, dd-MMM-yy HH:mm:ss zzz", Locale.US),
- new SimpleDateFormat("EEE MMMM d HH:mm:ss yyyy", Locale.US)
- };
-
-
- protected final static TimeZone gmtZone = TimeZone.getTimeZone("GMT");
-
-
- /**
- * GMT timezone - all HTTP dates are on GMT
- */
- static {
-
- format.setTimeZone(gmtZone);
-
- formats[0].setTimeZone(gmtZone);
- formats[1].setTimeZone(gmtZone);
- formats[2].setTimeZone(gmtZone);
-
- }
-
-
- /**
- * Instant on which the currentDate object was generated.
- */
- protected static long currentDateGenerated = 0L;
-
-
- /**
- * Current formatted date.
- */
- protected static String currentDate = null;
-
-
- /**
- * Formatter cache.
- */
- protected static final ConcurrentHashMap<Long, String> formatCache =
- new ConcurrentHashMap<Long, String>(CACHE_SIZE);
-
-
- /**
- * Parser cache.
- */
- protected static final ConcurrentHashMap<String, Long> parseCache =
- new ConcurrentHashMap<String, Long>(CACHE_SIZE);
-
-
- // --------------------------------------------------------- Public Methods
-
-
- /**
- * Get the current date in HTTP format.
- */
- public static final String getCurrentDate() {
-
- long now = System.currentTimeMillis();
- if ((now - currentDateGenerated) > 1000) {
- synchronized (format) {
- if ((now - currentDateGenerated) > 1000) {
- currentDateGenerated = now;
- currentDate = format.format(new Date(now));
- }
- }
- }
- return currentDate;
-
- }
-
-
- /**
- * Get the HTTP format of the specified date.
- */
- public static final String formatDate
- (long value, DateFormat threadLocalformat) {
-
- Long longValue = new Long(value);
- String cachedDate = formatCache.get(longValue);
- if (cachedDate != null)
- return cachedDate;
-
- String newDate = null;
- Date dateValue = new Date(value);
- if (threadLocalformat != null) {
- newDate = threadLocalformat.format(dateValue);
- updateFormatCache(longValue, newDate);
- } else {
- synchronized (formatCache) {
- synchronized (format) {
- newDate = format.format(dateValue);
- }
- updateFormatCache(longValue, newDate);
- }
- }
- return newDate;
-
- }
-
-
- /**
- * Try to parse the given date as a HTTP date.
- */
- public static final long parseDate(String value,
- DateFormat[] threadLocalformats) {
-
- Long cachedDate = parseCache.get(value);
- if (cachedDate != null)
- return cachedDate.longValue();
-
- Long date = null;
- if (threadLocalformats != null) {
- date = internalParseDate(value, threadLocalformats);
- updateParseCache(value, date);
- } else {
- synchronized (parseCache) {
- date = internalParseDate(value, formats);
- updateParseCache(value, date);
- }
- }
- if (date == null) {
- return (-1L);
- } else {
- return date.longValue();
- }
-
- }
-
-
- /**
- * Parse date with given formatters.
- */
- private static final Long internalParseDate
- (String value, DateFormat[] formats) {
- Date date = null;
- for (int i = 0; (date == null) && (i < formats.length); i++) {
- try {
- date = formats[i].parse(value);
- } catch (ParseException e) {
- ;
- }
- }
- if (date == null) {
- return null;
- }
- return new Long(date.getTime());
- }
-
-
- /**
- * Update cache.
- */
- private static void updateFormatCache(Long key, String value) {
- if (value == null) {
- return;
- }
- if (formatCache.size() > CACHE_SIZE) {
- formatCache.clear();
- }
- formatCache.put(key, value);
- }
-
-
- /**
- * Update cache.
- */
- private static void updateParseCache(String key, Long value) {
- if (value == null) {
- return;
- }
- if (parseCache.size() > CACHE_SIZE) {
- parseCache.clear();
- }
- parseCache.put(key, value);
- }
-
-
-}
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/lite/util/LocaleParser.java b/modules/tomcat-lite/java/org/apache/tomcat/lite/util/LocaleParser.java
deleted file mode 100644
index cdd3718..0000000
--- a/modules/tomcat-lite/java/org/apache/tomcat/lite/util/LocaleParser.java
+++ /dev/null
@@ -1,394 +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.lite.util;
-
-import java.util.ArrayList;
-import java.util.Locale;
-import java.util.TreeMap;
-
-
-/**
- * Utility class for string parsing that is higher performance than
- * StringParser for simple delimited text cases. Parsing is performed
- * by setting the string, and then using the <code>findXxxx()</code> and
- * <code>skipXxxx()</code> families of methods to remember significant
- * offsets. To retrieve the parsed substrings, call the <code>extract()</code>
- * method with the appropriate saved offset values.
- *
- * @author Craig R. McClanahan
- */
-public final class LocaleParser {
-
- public LocaleParser() {
- this(null);
- }
-
- public LocaleParser(String string) {
- setString(string);
- }
-
- public TreeMap parseLocale(String value) {
- // Store the accumulated languages that have been requested in
- // a local collection, sorted by the quality value (so we can
- // add Locales in descending order). The values will be ArrayLists
- // containing the corresponding Locales to be added
- TreeMap locales = new TreeMap();
-
- // Preprocess the value to remove all whitespace
- int white = value.indexOf(' ');
- if (white < 0)
- white = value.indexOf('\t');
- if (white >= 0) {
- StringBuilder sb = new StringBuilder();
- int len = value.length();
- for (int i = 0; i < len; i++) {
- char ch = value.charAt(i);
- if ((ch != ' ') && (ch != '\t'))
- sb.append(ch);
- }
- value = sb.toString();
- }
-
- LocaleParser parser = this;
- // Process each comma-delimited language specification
- parser.setString(value); // ASSERT: parser is available to us
- int length = parser.getLength();
- while (true) {
-
- // Extract the next comma-delimited entry
- int start = parser.getIndex();
- if (start >= length)
- break;
- int end = parser.findChar(',');
- String entry = parser.extract(start, end).trim();
- parser.advance(); // For the following entry
-
- // Extract the quality factor for this entry
- double quality = 1.0;
- int semi = entry.indexOf(";q=");
- if (semi >= 0) {
- try {
- quality = Double.parseDouble(entry.substring(semi + 3));
- } catch (NumberFormatException e) {
- quality = 0.0;
- }
- entry = entry.substring(0, semi);
- }
-
- // Skip entries we are not going to keep track of
- if (quality < 0.00005)
- continue; // Zero (or effectively zero) quality factors
- if ("*".equals(entry))
- continue; // FIXME - "*" entries are not handled
-
- // Extract the language and country for this entry
- String language = null;
- String country = null;
- String variant = null;
- int dash = entry.indexOf('-');
- if (dash < 0) {
- language = entry;
- country = "";
- variant = "";
- } else {
- language = entry.substring(0, dash);
- country = entry.substring(dash + 1);
- int vDash = country.indexOf('-');
- if (vDash > 0) {
- String cTemp = country.substring(0, vDash);
- variant = country.substring(vDash + 1);
- country = cTemp;
- } else {
- variant = "";
- }
- }
-
- // Add a new Locale to the list of Locales for this quality level
- Locale locale = new Locale(language, country, variant);
- Double key = new Double(-quality); // Reverse the order
- ArrayList values = (ArrayList) locales.get(key);
- if (values == null) {
- values = new ArrayList();
- locales.put(key, values);
- }
- values.add(locale);
-
- }
-
- return locales;
- }
-
- /**
- * The characters of the current string, as a character array. Stored
- * when the string is first specified to speed up access to characters
- * being compared during parsing.
- */
- private char chars[] = null;
-
-
- /**
- * The zero-relative index of the current point at which we are
- * positioned within the string being parsed. <strong>NOTE</strong>:
- * the value of this index can be one larger than the index of the last
- * character of the string (i.e. equal to the string length) if you
- * parse off the end of the string. This value is useful for extracting
- * substrings that include the end of the string.
- */
- private int index = 0;
-
-
- /**
- * The length of the String we are currently parsing. Stored when the
- * string is first specified to avoid repeated recalculations.
- */
- private int length = 0;
-
-
- /**
- * The String we are currently parsing.
- */
- private String string = null;
-
-
- // ------------------------------------------------------------- Properties
-
-
- /**
- * Return the zero-relative index of our current parsing position
- * within the string being parsed.
- */
- public int getIndex() {
-
- return (this.index);
-
- }
-
-
- /**
- * Return the length of the string we are parsing.
- */
- public int getLength() {
-
- return (this.length);
-
- }
-
-
- /**
- * Return the String we are currently parsing.
- */
- public String getString() {
-
- return (this.string);
-
- }
-
-
- /**
- * Set the String we are currently parsing. The parser state is also reset
- * to begin at the start of this string.
- *
- * @param string The string to be parsed.
- */
- public void setString(String string) {
-
- this.string = string;
- if (string != null) {
- this.length = string.length();
- chars = this.string.toCharArray();
- } else {
- this.length = 0;
- chars = new char[0];
- }
- reset();
-
- }
-
-
- // --------------------------------------------------------- Public Methods
-
-
- /**
- * Advance the current parsing position by one, if we are not already
- * past the end of the string.
- */
- public void advance() {
-
- if (index < length)
- index++;
-
- }
-
-
- /**
- * Extract and return a substring that starts at the specified position,
- * and extends to the end of the string being parsed. If this is not
- * possible, a zero-length string is returned.
- *
- * @param start Starting index, zero relative, inclusive
- */
- public String extract(int start) {
-
- if ((start < 0) || (start >= length))
- return ("");
- else
- return (string.substring(start));
-
- }
-
-
- /**
- * Extract and return a substring that starts at the specified position,
- * and ends at the character before the specified position. If this is
- * not possible, a zero-length string is returned.
- *
- * @param start Starting index, zero relative, inclusive
- * @param end Ending index, zero relative, exclusive
- */
- public String extract(int start, int end) {
-
- if ((start < 0) || (start >= end) || (end > length))
- return ("");
- else
- return (string.substring(start, end));
-
- }
-
-
- /**
- * Return the index of the next occurrence of the specified character,
- * or the index of the character after the last position of the string
- * if no more occurrences of this character are found. The current
- * parsing position is updated to the returned value.
- *
- * @param ch Character to be found
- */
- public int findChar(char ch) {
-
- while ((index < length) && (ch != chars[index]))
- index++;
- return (index);
-
- }
-
-
- /**
- * Return the index of the next occurrence of a non-whitespace character,
- * or the index of the character after the last position of the string
- * if no more non-whitespace characters are found. The current
- * parsing position is updated to the returned value.
- */
- public int findText() {
-
- while ((index < length) && isWhite(chars[index]))
- index++;
- return (index);
-
- }
-
-
- /**
- * Return the index of the next occurrence of a whitespace character,
- * or the index of the character after the last position of the string
- * if no more whitespace characters are found. The current parsing
- * position is updated to the returned value.
- */
- public int findWhite() {
-
- while ((index < length) && !isWhite(chars[index]))
- index++;
- return (index);
-
- }
-
-
- /**
- * Reset the current state of the parser to the beginning of the
- * current string being parsed.
- */
- public void reset() {
-
- index = 0;
-
- }
-
-
- /**
- * Advance the current parsing position while it is pointing at the
- * specified character, or until it moves past the end of the string.
- * Return the final value.
- *
- * @param ch Character to be skipped
- */
- public int skipChar(char ch) {
-
- while ((index < length) && (ch == chars[index]))
- index++;
- return (index);
-
- }
-
-
- /**
- * Advance the current parsing position while it is pointing at a
- * non-whitespace character, or until it moves past the end of the string.
- * Return the final value.
- */
- public int skipText() {
-
- while ((index < length) && !isWhite(chars[index]))
- index++;
- return (index);
-
- }
-
-
- /**
- * Advance the current parsing position while it is pointing at a
- * whitespace character, or until it moves past the end of the string.
- * Return the final value.
- */
- public int skipWhite() {
-
- while ((index < length) && isWhite(chars[index]))
- index++;
- return (index);
-
- }
-
-
- // ------------------------------------------------------ Protected Methods
-
-
- /**
- * Is the specified character considered to be whitespace?
- *
- * @param ch Character to be checked
- */
- protected boolean isWhite(char ch) {
-
- if ((ch == ' ') || (ch == '\t') || (ch == '\r') || (ch == '\n'))
- return (true);
- else
- return (false);
-
- }
-
-
-}
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/lite/util/MimeMap.java b/modules/tomcat-lite/java/org/apache/tomcat/lite/util/MimeMap.java
deleted file mode 100644
index 11828ba..0000000
--- a/modules/tomcat-lite/java/org/apache/tomcat/lite/util/MimeMap.java
+++ /dev/null
@@ -1,195 +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.lite.util;
-
-import java.net.FileNameMap;
-import java.util.Enumeration;
-import java.util.Hashtable;
-
-
-/**
- * A mime type map that implements the java.net.FileNameMap interface.
- *
- * @author James Duncan Davidson [duncan at eng.sun.com]
- * @author Jason Hunter [jch at eng.sun.com]
- */
-public class MimeMap implements FileNameMap {
-
- // Defaults - all of them are "well-known" types,
- // you can add using normal web.xml.
-
- public static Hashtable<String,String> defaultMap =
- new Hashtable<String,String>(101);
- static {
- defaultMap.put("txt", "text/plain");
- defaultMap.put("css", "text/css");
- defaultMap.put("html","text/html");
- defaultMap.put("htm", "text/html");
- defaultMap.put("gif", "image/gif");
- defaultMap.put("jpg", "image/jpeg");
- defaultMap.put("jpe", "image/jpeg");
- defaultMap.put("jpeg", "image/jpeg");
- defaultMap.put("png", "image/png");
- defaultMap.put("java", "text/plain");
- defaultMap.put("body", "text/html");
- defaultMap.put("rtx", "text/richtext");
- defaultMap.put("tsv", "text/tab-separated-values");
- defaultMap.put("etx", "text/x-setext");
- defaultMap.put("ps", "application/x-postscript");
- defaultMap.put("class", "application/java");
- defaultMap.put("csh", "application/x-csh");
- defaultMap.put("sh", "application/x-sh");
- defaultMap.put("tcl", "application/x-tcl");
- defaultMap.put("tex", "application/x-tex");
- defaultMap.put("texinfo", "application/x-texinfo");
- defaultMap.put("texi", "application/x-texinfo");
- defaultMap.put("t", "application/x-troff");
- defaultMap.put("tr", "application/x-troff");
- defaultMap.put("roff", "application/x-troff");
- defaultMap.put("man", "application/x-troff-man");
- defaultMap.put("me", "application/x-troff-me");
- defaultMap.put("ms", "application/x-wais-source");
- defaultMap.put("src", "application/x-wais-source");
- defaultMap.put("zip", "application/zip");
- defaultMap.put("bcpio", "application/x-bcpio");
- defaultMap.put("cpio", "application/x-cpio");
- defaultMap.put("gtar", "application/x-gtar");
- defaultMap.put("shar", "application/x-shar");
- defaultMap.put("sv4cpio", "application/x-sv4cpio");
- defaultMap.put("sv4crc", "application/x-sv4crc");
- defaultMap.put("tar", "application/x-tar");
- defaultMap.put("ustar", "application/x-ustar");
- defaultMap.put("dvi", "application/x-dvi");
- defaultMap.put("hdf", "application/x-hdf");
- defaultMap.put("latex", "application/x-latex");
- defaultMap.put("bin", "application/octet-stream");
- defaultMap.put("oda", "application/oda");
- defaultMap.put("pdf", "application/pdf");
- defaultMap.put("ps", "application/postscript");
- defaultMap.put("eps", "application/postscript");
- defaultMap.put("ai", "application/postscript");
- defaultMap.put("rtf", "application/rtf");
- defaultMap.put("nc", "application/x-netcdf");
- defaultMap.put("cdf", "application/x-netcdf");
- defaultMap.put("cer", "application/x-x509-ca-cert");
- defaultMap.put("exe", "application/octet-stream");
- defaultMap.put("gz", "application/x-gzip");
- defaultMap.put("Z", "application/x-compress");
- defaultMap.put("z", "application/x-compress");
- defaultMap.put("hqx", "application/mac-binhex40");
- defaultMap.put("mif", "application/x-mif");
- defaultMap.put("ief", "image/ief");
- defaultMap.put("tiff", "image/tiff");
- defaultMap.put("tif", "image/tiff");
- defaultMap.put("ras", "image/x-cmu-raster");
- defaultMap.put("pnm", "image/x-portable-anymap");
- defaultMap.put("pbm", "image/x-portable-bitmap");
- defaultMap.put("pgm", "image/x-portable-graymap");
- defaultMap.put("ppm", "image/x-portable-pixmap");
- defaultMap.put("rgb", "image/x-rgb");
- defaultMap.put("xbm", "image/x-xbitmap");
- defaultMap.put("xpm", "image/x-xpixmap");
- defaultMap.put("xwd", "image/x-xwindowdump");
- defaultMap.put("au", "audio/basic");
- defaultMap.put("snd", "audio/basic");
- defaultMap.put("aif", "audio/x-aiff");
- defaultMap.put("aiff", "audio/x-aiff");
- defaultMap.put("aifc", "audio/x-aiff");
- defaultMap.put("wav", "audio/x-wav");
- defaultMap.put("mpeg", "video/mpeg");
- defaultMap.put("mpg", "video/mpeg");
- defaultMap.put("mpe", "video/mpeg");
- defaultMap.put("qt", "video/quicktime");
- defaultMap.put("mov", "video/quicktime");
- defaultMap.put("avi", "video/x-msvideo");
- defaultMap.put("movie", "video/x-sgi-movie");
- defaultMap.put("avx", "video/x-rad-screenplay");
- defaultMap.put("wrl", "x-world/x-vrml");
- defaultMap.put("mpv2", "video/mpeg2");
-
- /* Add XML related MIMEs */
-
- defaultMap.put("xml", "text/xml");
- defaultMap.put("xsl", "text/xml");
- defaultMap.put("svg", "image/svg+xml");
- defaultMap.put("svgz", "image/svg+xml");
- defaultMap.put("wbmp", "image/vnd.wap.wbmp");
- defaultMap.put("wml", "text/vnd.wap.wml");
- defaultMap.put("wmlc", "application/vnd.wap.wmlc");
- defaultMap.put("wmls", "text/vnd.wap.wmlscript");
- defaultMap.put("wmlscriptc", "application/vnd.wap.wmlscriptc");
- }
-
-
- private Hashtable<String,String> map = new Hashtable<String,String>();
-
- public void addContentType(String extn, String type) {
- map.put(extn, type.toLowerCase());
- }
-
- public Enumeration getExtensions() {
- return map.keys();
- }
-
- public String getMimeType(String ext) {
- return getContentTypeFor(ext);
- }
-
- public String getContentType(String extn) {
- String type = (String)map.get(extn.toLowerCase());
- if( type == null ) type=(String)defaultMap.get( extn );
- return type;
- }
-
- public void removeContentType(String extn) {
- map.remove(extn.toLowerCase());
- }
-
- /** Get extension of file, without fragment id
- */
- public static String getExtension( String fileName ) {
- // play it safe and get rid of any fragment id
- // that might be there
- int length=fileName.length();
-
- int newEnd = fileName.lastIndexOf('#');
- if( newEnd== -1 ) newEnd=length;
- // Instead of creating a new string.
- // if (i != -1) {
- // fileName = fileName.substring(0, i);
- // }
- int i = fileName.lastIndexOf('.', newEnd );
- if (i != -1) {
- return fileName.substring(i + 1, newEnd );
- } else {
- // no extension, no content type
- return null;
- }
- }
-
- public String getContentTypeFor(String fileName) {
- String extn=getExtension( fileName );
- if (extn!=null) {
- return getContentType(extn);
- } else {
- // no extension, no content type
- return null;
- }
- }
-
-}
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/lite/util/Range.java b/modules/tomcat-lite/java/org/apache/tomcat/lite/util/Range.java
deleted file mode 100644
index 210a827..0000000
--- a/modules/tomcat-lite/java/org/apache/tomcat/lite/util/Range.java
+++ /dev/null
@@ -1,160 +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.lite.util;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.StringTokenizer;
-
-/**
- * Utils to process HTTP/1.1 ranges. Used by default servlet, could
- * be used by any servlet that needs to deal with ranges.
- *
- * It is very good to support ranges if you have large content. In most
- * cases supporting one range is enough - getting multiple ranges doesn't
- * seem very common, and it's complex (multipart response).
- *
- * @author Costin Manolache
- * @author Remy Maucherat
- * @author - see DefaultServlet in Catalin for other contributors
- */
-public class Range {
-
- public long start;
- public long end;
- public long length;
-
- /**
- * Validate range.
- */
- public boolean validate() {
- if (end >= length)
- end = length - 1;
- return ( (start >= 0) && (end >= 0) && (start <= end)
- && (length > 0) );
- }
-
- public void recycle() {
- start = 0;
- end = 0;
- length = 0;
- }
-
- /** Parse ranges.
- *
- * @return null if the range is invalid or can't be parsed
- */
- public static ArrayList parseRanges(long fileLength,
- String rangeHeader) throws IOException {
- ArrayList result = new ArrayList();
- StringTokenizer commaTokenizer = new StringTokenizer(rangeHeader, ",");
-
- // Parsing the range list
- while (commaTokenizer.hasMoreTokens()) {
- String rangeDefinition = commaTokenizer.nextToken().trim();
-
- Range currentRange = new Range();
- currentRange.length = fileLength;
-
- int dashPos = rangeDefinition.indexOf('-');
-
- if (dashPos == -1) {
- return null;
- }
-
- if (dashPos == 0) {
- try {
- long offset = Long.parseLong(rangeDefinition);
- currentRange.start = fileLength + offset;
- currentRange.end = fileLength - 1;
- } catch (NumberFormatException e) {
- return null;
- }
- } else {
-
- try {
- currentRange.start = Long.parseLong
- (rangeDefinition.substring(0, dashPos));
- if (dashPos < rangeDefinition.length() - 1)
- currentRange.end = Long.parseLong
- (rangeDefinition.substring
- (dashPos + 1, rangeDefinition.length()));
- else
- currentRange.end = fileLength - 1;
- } catch (NumberFormatException e) {
- return null;
- }
-
- }
- if (!currentRange.validate()) {
- return null;
- }
- result.add(currentRange);
- }
- return result;
- }
-
-
- /**
- * Parse the Content-Range header. Used with PUT or in response.
- *
- * @return Range
- */
- public static Range parseContentRange(String rangeHeader)
- throws IOException {
- if (rangeHeader == null)
- return null;
-
- // bytes is the only range unit supported
- if (!rangeHeader.startsWith("bytes")) {
- return null;
- }
-
- rangeHeader = rangeHeader.substring(6).trim();
-
- int dashPos = rangeHeader.indexOf('-');
- int slashPos = rangeHeader.indexOf('/');
-
- if (dashPos == -1) {
- return null;
- }
-
- if (slashPos == -1) {
- return null;
- }
-
- Range range = new Range();
-
- try {
- range.start = Long.parseLong(rangeHeader.substring(0, dashPos));
- range.end =
- Long.parseLong(rangeHeader.substring(dashPos + 1, slashPos));
- range.length = Long.parseLong
- (rangeHeader.substring(slashPos + 1, rangeHeader.length()));
- } catch (NumberFormatException e) {
- return null;
- }
-
- if (!range.validate()) {
- return null;
- }
-
- return range;
- }
-
-}
\ No newline at end of file
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/lite/util/URLEncoder.java b/modules/tomcat-lite/java/org/apache/tomcat/lite/util/URLEncoder.java
deleted file mode 100644
index f946e56..0000000
--- a/modules/tomcat-lite/java/org/apache/tomcat/lite/util/URLEncoder.java
+++ /dev/null
@@ -1,227 +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.lite.util;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.OutputStreamWriter;
-import java.io.UnsupportedEncodingException;
-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 class URLEncoder {
- protected static final char[] hexadecimal =
- {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
- 'A', 'B', 'C', 'D', 'E', 'F'};
-
- //Array containing the safe characters set.
- protected BitSet safeChars = new BitSet(128);
-
- public URLEncoder() {
- for (char i = 'a'; i <= 'z'; i++) {
- addSafeCharacter(i);
- }
- for (char i = 'A'; i <= 'Z'; i++) {
- addSafeCharacter(i);
- }
- for (char i = '0'; i <= '9'; i++) {
- addSafeCharacter(i);
- }
- //safe
- safeChars.set('$');
- safeChars.set('-');
- safeChars.set('_');
- safeChars.set('.');
-
- // Dangerous: someone may treat this as " "
- // RFC1738 does allow it, it's not reserved
- // safeChars.set('+');
- //extra
- safeChars.set('!');
- safeChars.set('*');
- safeChars.set('\'');
- safeChars.set('(');
- safeChars.set(')');
- safeChars.set(',');
- }
-
- public void addSafeCharacter( char c ) {
- safeChars.set( c );
- }
-
- public String encodeURL(String path) {
- return encodeURL(path, "UTF-8", true);
- }
-
- public String encodeURL(String path, String enc, boolean allowSlash) {
- int maxBytesPerChar = 10;
-
- StringBuffer rewrittenPath = new StringBuffer(path.length());
- ByteArrayOutputStream buf = new ByteArrayOutputStream(maxBytesPerChar);
- OutputStreamWriter writer = null;
- try {
- writer = new OutputStreamWriter(buf, enc);
- } catch (UnsupportedEncodingException e1) {
- // shouldn't happen.
- }
-
- for (int i = 0; i < path.length(); i++) {
- int c = (int) path.charAt(i);
- if (c < 128 && safeChars.get(c) || allowSlash && c == '/') {
- rewrittenPath.append((char)c);
- } else {
- // convert to external encoding before hex conversion
- try {
- writer.write((char)c);
- if (c >= 0xD800 && c <= 0xDBFF) {
- if ( (i+1) < path.length()) {
- int d = path.charAt(i+1);
- if (d >= 0xDC00 && d <= 0xDFFF) {
- writer.write((char) d);
- i++;
- }
- }
- }
- writer.flush();
- } catch(IOException e) {
- buf.reset();
- continue;
- }
- byte[] ba = buf.toByteArray();
- for (int j = 0; j < ba.length; j++) {
- // Converting each byte in the buffer
- byte toEncode = ba[j];
- rewrittenPath.append('%');
- int low = (int) (toEncode & 0x0f);
- int high = (int) ((toEncode & 0xf0) >> 4);
- rewrittenPath.append(hexadecimal[high]);
- rewrittenPath.append(hexadecimal[low]);
- }
- buf.reset();
- }
- }
- return rewrittenPath.toString();
- }
-
- /**
- * Decode and return the specified URL-encoded String.
- *
- * @param str The url-encoded string
- * @param enc The encoding to use; if null, the default encoding is used
- * @exception IllegalArgumentException if a '%' character is not followed
- * by a valid 2-digit hexadecimal number
- */
- public static String URLDecode(String str, String enc) {
-
- if (str == null)
- return (null);
-
- // use the specified encoding to extract bytes out of the
- // given string so that the encoding is not lost. If an
- // encoding is not specified, let it use platform default
- byte[] bytes = null;
- try {
- if (enc == null) {
- bytes = str.getBytes();
- } else {
- bytes = str.getBytes(enc);
- }
- } catch (UnsupportedEncodingException uee) {}
-
- return URLDecode(bytes, enc);
-
- }
-
-
- /**
- * Decode and return the specified URL-encoded String.
- * When the byte array is converted to a string, the system default
- * character encoding is used... This may be different than some other
- * servers.
- *
- * @param str The url-encoded string
- *
- * @exception IllegalArgumentException if a '%' character is not followed
- * by a valid 2-digit hexadecimal number
- */
- public static String URLDecode(String str) {
-
- return URLDecode(str, null);
-
- }
-
- /**
- * Decode and return the specified URL-encoded byte array.
- *
- * @param bytes The url-encoded byte array
- * @param enc The encoding to use; if null, the default encoding is used
- * @exception IllegalArgumentException if a '%' character is not followed
- * by a valid 2-digit hexadecimal number
- */
- private static String URLDecode(byte[] bytes, String enc) {
-
- if (bytes == null)
- return (null);
-
- int len = bytes.length;
- int ix = 0;
- int ox = 0;
- while (ix < len) {
- byte b = bytes[ix++]; // Get byte to test
- if (b == '+') {
- b = (byte)' ';
- } else if (b == '%') {
- b = (byte) ((convertHexDigit(bytes[ix++]) << 4)
- + convertHexDigit(bytes[ix++]));
- }
- bytes[ox++] = b;
- }
- if (enc != null) {
- try {
- return new String(bytes, 0, ox, enc);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- return new String(bytes, 0, ox);
-
- }
-
- /**
- * Convert a byte character value to hexidecimal digit value.
- *
- * @param b the character value byte
- */
- private static byte convertHexDigit( byte b ) {
- if ((b >= '0') && (b <= '9')) return (byte)(b - '0');
- if ((b >= 'a') && (b <= 'f')) return (byte)(b - 'a' + 10);
- if ((b >= 'A') && (b <= 'F')) return (byte)(b - 'A' + 10);
- return 0;
- }
-
-}
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/lite/util/UrlUtils.java b/modules/tomcat-lite/java/org/apache/tomcat/lite/util/UrlUtils.java
deleted file mode 100644
index 05e6cc6..0000000
--- a/modules/tomcat-lite/java/org/apache/tomcat/lite/util/UrlUtils.java
+++ /dev/null
@@ -1,84 +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.lite.util;
-
-public class UrlUtils {
-
- /** Used by webdav.
- *
- * Return a context-relative path, beginning with a "/", that represents
- * the canonical version of the specified path after ".." and "." elements
- * are resolved out. If the specified path attempts to go outside the
- * boundaries of the current context (i.e. too many ".." path elements
- * are present), return <code>null</code> instead.
- *
- * @param path Path to be normalized
- */
- public static String normalize(String path) {
-
- if (path == null)
- return null;
-
- // Create a place for the normalized path
- String normalized = path;
-
- if (normalized.equals("/."))
- return "/";
-
- // Normalize the slashes and add leading slash if necessary
- if (normalized.indexOf('\\') >= 0)
- normalized = normalized.replace('\\', '/');
-
- if (!normalized.startsWith("/"))
- normalized = "/" + normalized;
-
- // Resolve occurrences of "//" in the normalized path
- while (true) {
- int index = normalized.indexOf("//");
- if (index < 0)
- break;
- normalized = normalized.substring(0, index) +
- normalized.substring(index + 1);
- }
-
- // Resolve occurrences of "/./" in the normalized path
- while (true) {
- int index = normalized.indexOf("/./");
- if (index < 0)
- break;
- normalized = normalized.substring(0, index) +
- normalized.substring(index + 2);
- }
-
- // Resolve occurrences of "/../" in the normalized path
- while (true) {
- int index = normalized.indexOf("/../");
- if (index < 0)
- break;
- if (index == 0)
- return (null); // Trying to go outside our context
- int index2 = normalized.lastIndexOf('/', index - 1);
- normalized = normalized.substring(0, index2) +
- normalized.substring(index + 3);
- }
-
- // Return the normalized path that we have completed
- return (normalized);
- }
-
-}
diff --git a/modules/tomcat-lite/pom.xml b/modules/tomcat-lite/pom.xml
deleted file mode 100644
index 742ae8d..0000000
--- a/modules/tomcat-lite/pom.xml
+++ /dev/null
@@ -1,118 +0,0 @@
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
- <modelVersion>4.0.0</modelVersion>
-
- <groupId>org.apache.tomcat.lite</groupId>
- <artifactId>lite</artifactId>
-
- <version>0.0.1-SNAPSHOT</version>
-
- <dependencies>
- <dependency>
- <groupId>com.jcraft</groupId>
- <artifactId>jzlib</artifactId>
- <version>1.0.7</version>
- <type>jar</type>
- <scope>compile</scope>
- </dependency>
- <dependency>
- <groupId>javax.servlet</groupId>
- <artifactId>servlet-api</artifactId>
- <version>2.5</version>
- </dependency>
- <dependency>
- <groupId>junit</groupId>
- <artifactId>junit</artifactId>
- <version>3.8.2</version>
- <type>jar</type>
- <scope>compile</scope>
- </dependency>
- <dependency>
- <groupId>commons-codec</groupId>
- <artifactId>commons-codec</artifactId>
- <version>1.4</version>
- <type>jar</type>
- <scope>compile</scope>
- </dependency>
- <dependency>
- <groupId>org.apache.tomcat</groupId>
- <artifactId>jasper</artifactId>
- <version>6.0.20</version>
- <type>jar</type>
- <scope>compile</scope>
- </dependency>
- <dependency>
- <groupId>org.apache.tomcat</groupId>
- <artifactId>jasper-jdt</artifactId>
- <version>6.0.20</version>
- <type>jar</type>
- <scope>compile</scope>
- </dependency>
- <dependency>
- <groupId>org.apache.tomcat</groupId>
- <artifactId>jasper-el</artifactId>
- <version>6.0.20</version>
- <type>jar</type>
- <scope>compile</scope>
- </dependency>
- <dependency>
- <groupId>asm</groupId>
- <artifactId>asm</artifactId>
- <version>3.1</version>
- <type>jar</type>
- <scope>compile</scope>
- </dependency>
- <dependency>
- <groupId>asm</groupId>
- <artifactId>asm-tree</artifactId>
- <version>3.1</version>
- <type>jar</type>
- <scope>compile</scope>
- </dependency>
- <dependency>
- <groupId>org.apache.tomcat</groupId>
- <artifactId>coyote</artifactId>
- <version>6.0.20</version>
- </dependency>
- <dependency>
- <groupId>org.apache.ant</groupId>
- <artifactId>ant</artifactId>
- <version>1.7.1</version>
- </dependency>
- </dependencies>
-
- <build>
-
- <sourceDirectory>java</sourceDirectory>
-
- <testSourceDirectory>test</testSourceDirectory>
-
- <plugins>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-compiler-plugin</artifactId>
- <configuration>
- <source>1.6</source>
- <target>1.6</target>
- <excludes>
- <exclude>org/apache/coyote/servlet/**</exclude>
- <exclude>**/ServletApi30.java</exclude>
- </excludes>
- <testExcludes>
- <exclude>org/apache/coyote/servlet/**</exclude>
- <exclude>**/ServletApi30.java</exclude>
- </testExcludes>
- </configuration>
- </plugin>
- </plugins>
-
- <testResources>
- <testResource>
- <directory>test</directory>
- <excludes>
- <exclude>**/*.java</exclude>
- </excludes>
- </testResource>
- </testResources>
-
- </build>
-</project>
diff --git a/modules/tomcat-lite/test/org/apache/coyote/lite/ServletTests.java b/modules/tomcat-lite/test/org/apache/coyote/lite/ServletTests.java
deleted file mode 100644
index 71b6bce..0000000
--- a/modules/tomcat-lite/test/org/apache/coyote/lite/ServletTests.java
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- */
-package org.apache.coyote.lite;
-
-import java.io.File;
-import java.io.IOException;
-
-import javax.servlet.ServletException;
-
-import org.apache.catalina.LifecycleException;
-import org.apache.tomcat.test.watchdog.WatchdogClient;
-
-import junit.framework.Test;
-
-/**
- * Wrapper to run watchdog.
- *
- */
-public class ServletTests extends WatchdogClient {
-
-
- public ServletTests() {
- super();
- goldenDir = getWatchdogdir() + "/src/clients/org/apache/jcheck/servlet/client/";
- testMatch =
- //"HttpServletResponseWrapperSetStatusMsgTest";
- //"ServletContextAttributeAddedEventTest";
- null;
- // ex: "ServletToJSP";
- file = getWatchdogdir() + "/src/conf/servlet-gtest.xml";
- targetMatch = "gtestservlet-test";
-
- port = 8883;
- exclude = new String[] {
- "DoInit1Test", // tomcat returns 404 if perm. unavailable
- "HttpServletDoInit1Test",
- "GetMajorVersionTest", // tomcat7
- "GetMinorVersionTest",
- "ServletToJSPErrorPageTest",
- "ServletToJSPError502PageTest",
- };
- }
-
- public ServletTests(String name) {
- this();
- super.single = name;
- port = 8883;
- }
-
- protected void beforeSuite() {
- // required for the tests
- System.setProperty("org.apache.coyote.USE_CUSTOM_STATUS_MSG_IN_HEADER",
- "true");
-
- try {
- initServerWithWatchdog(getWatchdogdir());
- } catch (ServletException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } catch (IOException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
-
- public void initServerWithWatchdog(String wdDir) throws ServletException,
- IOException {
- Tomcat tomcat = new Tomcat();
- tomcat.setPort(port);
-
- File f = new File(wdDir + "/build/webapps");
- tomcat.setBaseDir(f.getAbsolutePath());
-
- for (String s : new String[] {
- "servlet-compat",
- "servlet-tests",
- "jsp-tests"} ) {
- tomcat.addWebapp("/" + s, f.getCanonicalPath() + "/" + s);
- }
-
- TomcatStandaloneMain.setUp(tomcat, port);
-
- try {
- tomcat.start();
- } catch (LifecycleException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- System.err.println("Init done");
- }
-
- /**
- * Magic JUnit method
- */
- public static Test suite() {
- return new ServletTests().getSuite();
- }
-}
diff --git a/modules/tomcat-lite/test/org/apache/coyote/lite/Tomcat.java b/modules/tomcat-lite/test/org/apache/coyote/lite/Tomcat.java
deleted file mode 100644
index ae72345..0000000
--- a/modules/tomcat-lite/test/org/apache/coyote/lite/Tomcat.java
+++ /dev/null
@@ -1,911 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.coyote.lite;
-
-import java.io.File;
-import java.io.IOException;
-import java.security.Principal;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import javax.servlet.Servlet;
-import javax.servlet.ServletException;
-
-import org.apache.catalina.Container;
-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.Realm;
-import org.apache.catalina.connector.Connector;
-import org.apache.catalina.core.StandardContext;
-import org.apache.catalina.core.StandardEngine;
-import org.apache.catalina.core.StandardHost;
-import org.apache.catalina.core.StandardServer;
-import org.apache.catalina.core.StandardService;
-import org.apache.catalina.core.StandardWrapper;
-import org.apache.catalina.realm.RealmBase;
-import org.apache.catalina.session.StandardManager;
-import org.apache.catalina.startup.ContextConfig;
-
-// This class is here for compat with Tomcat6.
-
-// TODO: lazy init for the temp dir - only when a JSP is compiled or
-// get temp dir is called we need to create it. This will avoid the
-// need for the baseDir
-
-// TODO: allow contexts without a base dir - i.e.
-// only programmatic. This would disable the default servlet.
-
-/**
- * Minimal tomcat starter for embedding/unit tests.
- *
- * Tomcat supports multiple styles of configuration and
- * startup - the most common and stable is server.xml-based,
- * implemented in org.apache.catalina.startup.Bootstrap.
- *
- * This class is for use in apps that embed tomcat.
- * Requirements:
- *
- * - all tomcat classes and possibly servlets are in the classpath.
- * ( for example all is in one big jar, or in eclipse CP, or in any other
- * combination )
- *
- * - we need one temporary directory for work files
- *
- * - no config file is required. This class provides methods to
- * use if you have a webapp with a web.xml file, but it is
- * optional - you can use your own servlets.
- *
- * This class provides a main() and few simple CLI arguments,
- * see setters for doc. It can be used for simple tests and
- * demo.
- *
- * @see TestTomcat for examples on how to use this
- * @author Costin Manolache
- */
-public class Tomcat {
- // Single engine, service, server, connector - few cases need more,
- // they can use server.xml
- protected StandardServer server;
- protected StandardService service;
- protected StandardEngine engine;
- protected Connector connector; // for more - customize the classes
-
- boolean started = false;
- // To make it a bit easier to config for the common case
- // ( one host, one context ).
- protected StandardHost host;
-
- // TODO: it's easy to add support for more hosts - but is it
- // really needed ?
-
- // TODO: allow use of in-memory connector
-
- protected int port = 8080;
- protected String hostname = "localhost";
- protected String basedir;
-
- // Default in-memory realm, will be set by default on
- // created contexts. Can be replaced with setRealm() on
- // the context.
- protected Realm defaultRealm;
- private Map<String, String> userPass = new HashMap<String, String>();
- private Map<String, List<String>> userRoles =
- new HashMap<String, List<String>>();
- private Map<String, Principal> userPrincipals = new HashMap<String, Principal>();
-
- public Tomcat() {
- // NOOP
- }
-
- /**
- * Tomcat needs a directory for temp files. This should be the
- * first method called.
- *
- * By default, if this method is not called, we use:
- * - system properties - catalina.base, catalina.home
- * - $HOME/tomcat.$PORT
- * ( /tmp doesn't seem a good choice for security ).
- *
- *
- * TODO: better default ? Maybe current dir ?
- * TODO: disable work dir if not needed ( no jsp, etc ).
- */
- public void setBaseDir(String basedir) {
- this.basedir = basedir;
- }
-
- /**
- * Set the port for the default connector. Must
- * be called before start().
- */
- public void setPort(int port) {
- this.port = port;
- }
-
- /**
- * The the hostname of the default host, default is
- * 'localhost'.
- */
- public void setHostname(String s) {
- hostname = s;
- }
-
- /**
- * Add a webapp using normal WEB-INF/web.xml if found.
- *
- * @param contextPath
- * @param baseDir
- * @return new StandardContext
- * @throws ServletException
- */
- public StandardContext addWebapp(String contextPath,
- String baseDir) throws ServletException {
-
- return addWebapp(getHost(), contextPath, baseDir);
- }
-
-
- /**
- * Add a context - programmatic mode, no web.xml used.
- *
- * API calls equivalent with web.xml:
- *
- * context-param
- * ctx.addParameter("name", "value");
- *
- *
- * error-page
- * ErrorPage ep = new ErrorPage();
- * ep.setErrorCode(500);
- * ep.setLocation("/error.html");
- * ctx.addErrorPage(ep);
- *
- * ctx.addMimeMapping("ext", "type");
- *
- * Note: If you reload the Context, all your configuration will be lost. If
- * you need reload support, consider using a LifecycleListener to provide
- * your configuration.
- *
- * TODO: add the rest
- *
- * @param contextPath "/" for root context.
- * @param baseDir base dir for the context, for static files. Must exist,
- * relative to the server home
- */
- public StandardContext addContext(String contextPath,
- String baseDir) {
- return addContext(getHost(), contextPath, baseDir);
- }
-
- /**
- * Equivalent with
- * <servlet><servlet-name><servlet-class>.
- *
- * In general it is better/faster to use the method that takes a
- * Servlet as param - this one can be used if the servlet is not
- * commonly used, and want to avoid loading all deps.
- * ( for example: jsp servlet )
- *
- * You can customize the returned servlet, ex:
- *
- * wrapper.addInitParameter("name", "value");
- *
- * @param contextPath Context to add Servlet to
- * @param servletName Servlet name (used in mappings)
- * @param servletClass The class to be used for the Servlet
- * @return The wrapper for the servlet
- */
- public StandardWrapper addServlet(String contextPath,
- String servletName,
- String servletClass) {
- Container ctx = getHost().findChild(contextPath);
- return addServlet((StandardContext) ctx,
- servletName, servletClass);
- }
-
- /**
- * Static version of {@link #addServlet(String, String, String)}
- * @param ctx Context to add Servlet to
- * @param servletName Servlet name (used in mappings)
- * @param servletClass The class to be used for the Servlet
- * @return The wrapper for the servlet
- */
- public static StandardWrapper addServlet(StandardContext ctx,
- String servletName,
- String servletClass) {
- // will do class for name and set init params
- StandardWrapper sw = (StandardWrapper)ctx.createWrapper();
- sw.setServletClass(servletClass);
- sw.setName(servletName);
- ctx.addChild(sw);
-
- return sw;
- }
-
- /**
- * Add an existing Servlet to the context with no class.forName or
- * initialisation.
- * @param contextPath Context to add Servlet to
- * @param servletName Servlet name (used in mappings)
- * @param servlet The Servlet to add
- * @return The wrapper for the servlet
- */
- public StandardWrapper addServlet(String contextPath,
- String servletName,
- Servlet servlet) {
- Container ctx = getHost().findChild(contextPath);
- return addServlet((StandardContext) ctx,
- servletName, servlet);
- }
-
- /**
- * Static version of {@link #addServlet(String, String, Servlet)}.
- * @param ctx Context to add Servlet to
- * @param servletName Servlet name (used in mappings)
- * @param servlet The Servlet to add
- * @return The wrapper for the servlet
- */
- public static StandardWrapper addServlet(StandardContext ctx,
- String servletName,
- Servlet servlet) {
- // will do class for name and set init params
- StandardWrapper sw = new ExistingStandardWrapper(servlet);
- sw.setName(servletName);
- ctx.addChild(sw);
-
- return sw;
- }
-
-
- /**
- * Initialize and start the server.
- * @throws LifecycleException
- */
- public void start() throws LifecycleException {
- if (started) {
- return;
- }
- started = true;
- getServer();
- getConnector();
- server.start();
- }
-
- /**
- * Stop the server.
- * @throws LifecycleException
- */
- public void stop() throws LifecycleException {
- getServer().stop();
- }
-
-
- /**
- * Add a user for the in-memory realm. All created apps use this
- * by default, can be replaced using setRealm().
- *
- */
- public void addUser(String user, String pass) {
- userPass.put(user, pass);
- }
-
- /**
- * @see #addUser(String, String)
- */
- public void addRole(String user, String role) {
- List<String> roles = userRoles.get(user);
- if (roles == null) {
- roles = new ArrayList<String>();
- userRoles.put(user, roles);
- }
- roles.add(role);
- }
-
- // ------- Extra customization -------
- // You can tune individual tomcat objects, using internal APIs
-
- /**
- * Get the default http connector. You can set more
- * parameters - the port is already initialized.
- *
- * Alternatively, you can construct a Connector and set any params,
- * then call addConnector(Connector)
- *
- * @return A connector object that can be customized
- */
- public Connector getConnector() {
- getServer();
- if (connector != null) {
- return connector;
- }
- // This will load Apr connector if available,
- // default to nio. I'm having strange problems with apr
- // and for the use case the speed benefit wouldn't matter.
-
- //connector = new Connector("HTTP/1.1");
- try {
- connector = new Connector("org.apache.coyote.http11.Http11Protocol");
- connector.setPort(port);
- service.addConnector( connector );
- } catch (Exception e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- return connector;
- }
-
- public void setConnector(Connector connector) {
- this.connector = connector;
- }
-
- /**
- * Get the service object. Can be used to add more
- * connectors and few other global settings.
- */
- public StandardService getService() {
- getServer();
- return service;
- }
-
- /**
- * Sets the current host - all future webapps will
- * be added to this host. When tomcat starts, the
- * host will be the default host.
- *
- * @param host
- */
- public void setHost(StandardHost host) {
- this.host = host;
- }
-
- public StandardHost getHost() {
- if (host == null) {
- host = new StandardHost();
- host.setName(hostname);
-
- getEngine().addChild( host );
- }
- return host;
- }
-
- /**
- * Set a custom realm for auth. If not called, a simple
- * default will be used, using an internal map.
- *
- * Must be called before adding a context.
- */
- public void setDefaultRealm(Realm realm) {
- defaultRealm = realm;
- }
-
-
- /**
- * Access to the engine, for further customization.
- */
- public StandardEngine getEngine() {
- if(engine == null ) {
- getServer();
- engine = new StandardEngine();
- engine.setName( "Tomcat" );
- engine.setDefaultHost(hostname);
- service.setContainer(engine);
- }
- return engine;
- }
-
- /**
- * Get the server object. You can add listeners and few more
- * customizations. JNDI is disabled by default.
- */
- public StandardServer getServer() {
-
- if (server != null) {
- return server;
- }
-
- initBaseDir();
-
- System.setProperty("catalina.useNaming", "false");
-
- server = new StandardServer();
- server.setPort( -1 );
-
- service = new StandardService();
- service.setName("Tomcat");
- server.addService( service );
- return server;
- }
-
- public StandardContext addContext(StandardHost host,
- String contextPath,
- String dir) {
- silence(contextPath);
- StandardContext ctx = new StandardContext();
- ctx.setPath( contextPath );
- ctx.setDocBase(dir);
- ctx.addLifecycleListener(new FixContextListener());
-
- if (host == null) {
- getHost().addChild(ctx);
- } else {
- host.addChild(ctx);
- }
- return ctx;
- }
-
- public StandardContext addWebapp(StandardHost host,
- String url, String path) {
- silence(url);
-
- StandardContext ctx = new StandardContext();
- ctx.setPath( url );
- ctx.setDocBase(path);
- if (defaultRealm == null) {
- initSimpleAuth();
- }
- ctx.setRealm(defaultRealm);
- ctx.addLifecycleListener(new DefaultWebXmlListener());
-
- ContextConfig ctxCfg = new ContextConfig();
- ctx.addLifecycleListener( ctxCfg );
- // prevent it from looking ( if it finds one - it'll have dup error )
- ctxCfg.setDefaultWebXml("org/apache/catalin/startup/NO_DEFAULT_XML");
-
- if (host == null) {
- getHost().addChild(ctx);
- } else {
- host.addChild(ctx);
- }
-
- return ctx;
- }
-
-
- // ---------- Helper methods and classes -------------------
-
- /**
- * Initialize an in-memory realm. You can replace it
- * for contexts with a real one.
- */
- protected void initSimpleAuth() {
- defaultRealm = new RealmBase() {
- @Override
- protected String getName() {
- return "Simple";
- }
-
- @Override
- protected String getPassword(String username) {
- return userPass.get(username);
- }
-
- @Override
- protected Principal getPrincipal(final String username) {
- Principal p = userPrincipals.get(username);
- if (p == null) {
- String pass = userPass.get(username);
- if (pass != null) {
- p = new Principal() {
-
- @Override
- public String getName() {
- return username;
- }
-
- };
- }
- }
- return p;
- }
-
- };
- }
-
- protected void initBaseDir() {
- if (basedir == null) {
- basedir = System.getProperty("catalina.base");
- }
- if (basedir == null) {
- basedir = System.getProperty("catalina.home");
- }
- if (basedir == null) {
- // Create a temp dir.
- basedir = System.getProperty("user.dir") +
- "/tomcat." + port;
- File home = new File(basedir);
- home.mkdir();
- if (!home.isAbsolute()) {
- try {
- basedir = home.getCanonicalPath();
- } catch (IOException e) {
- basedir = home.getAbsolutePath();
- }
- }
- }
- System.setProperty("catalina.home", basedir);
- System.setProperty("catalina.base", basedir);
- }
-
- static String[] silences = new String[] {
- "org.apache.coyote.http11.Http11Protocol",
- "org.apache.catalina.core.StandardService",
- "org.apache.catalina.core.StandardEngine",
- "org.apache.catalina.startup.ContextConfig",
- "org.apache.catalina.core.ApplicationContext",
- "org.apache.catalina.core.AprLifecycleListener"
- };
-
- /**
- * Controls if the loggers will be silenced or not.
- * @param silent <code>true</code> sets the log level to WARN for the
- * loggers that log information on Tomcat start up. This
- * prevents the usual startup information being logged.
- * <code>false</code> sets the log level to the default
- * level of INFO.
- */
- public void setSilent(boolean silent) {
- for (String s : silences) {
- if (silent) {
- Logger.getLogger(s).setLevel(Level.WARNING);
- } else {
- Logger.getLogger(s).setLevel(Level.INFO);
- }
- }
- }
-
- private void silence(String ctx) {
- String base = "org.apache.catalina.core.ContainerBase.[default].[";
- base += getHost().getName();
- base += "].[";
- base += ctx;
- base += "]";
- Logger.getLogger(base).setLevel(Level.WARNING);
- }
-
- /**
- * Enables JNDI naming which is disabled by default.
- */
- public void enableNaming() {
- // Make sure getServer() has been called as that is where naming is
- // disabled
- getServer();
-
- System.setProperty("catalina.useNaming", "true");
- String value = "org.apache.naming";
- String oldValue =
- System.getProperty(javax.naming.Context.URL_PKG_PREFIXES);
- if (oldValue != null) {
- value = value + ":" + oldValue;
- }
- System.setProperty(javax.naming.Context.URL_PKG_PREFIXES, value);
- value = System.getProperty
- (javax.naming.Context.INITIAL_CONTEXT_FACTORY);
- if (value == null) {
- System.setProperty
- (javax.naming.Context.INITIAL_CONTEXT_FACTORY,
- "org.apache.naming.java.javaURLContextFactory");
- }
- }
-
- /**
- * Provide default configuration for a context. This is the programmatic
- * equivalent of the default web.xml.
- *
- * TODO: in normal tomcat, if default-web.xml is not found, use this
- * method
- *
- * @param contextPath The context to set the defaults for
- */
- public void initWebappDefaults(String contextPath) {
- Container ctx = getHost().findChild(contextPath);
- initWebappDefaults((StandardContext) ctx);
- }
-
- /**
- * Static version of {@link #initWebappDefaults(String)}
- * @param ctx The context to set the defaults for
- */
- public static void initWebappDefaults(StandardContext ctx) {
- // Default servlet
- StandardWrapper servlet = addServlet(
- ctx, "default", "org.apache.catalina.servlets.DefaultServlet");
- servlet.setLoadOnStartup(1);
-
- // JSP servlet (by class name - to avoid loading all deps)
- servlet = addServlet(
- ctx, "jsp", "org.apache.jasper.servlet.JspServlet");
- servlet.addInitParameter("fork", "false");
- servlet.setLoadOnStartup(3);
-
- // Servlet mappings
- ctx.addServletMapping("/", "default");
- ctx.addServletMapping("*.jsp", "jsp");
- ctx.addServletMapping("*.jspx", "jsp");
-
- // Sessions
- ctx.setManager( new StandardManager());
- ctx.setSessionTimeout(30);
-
- // MIME mappings
- for (int i = 0; i < DEFAULT_MIME_MAPPINGS.length; ) {
- ctx.addMimeMapping(DEFAULT_MIME_MAPPINGS[i++],
- DEFAULT_MIME_MAPPINGS[i++]);
- }
-
- // Welcome files
- ctx.addWelcomeFile("index.html");
- ctx.addWelcomeFile("index.htm");
- ctx.addWelcomeFile("index.jsp");
- }
-
-
- /**
- * Fix startup sequence - required if you don't use web.xml.
- *
- * The start() method in context will set 'configured' to false - and
- * expects a listener to set it back to true.
- */
- public static class FixContextListener implements LifecycleListener {
-
- public void lifecycleEvent(LifecycleEvent event) {
- try {
- Context context = (Context) event.getLifecycle();
- if (event.getType().equals(Lifecycle.START_EVENT)) {
- context.setConfigured(true);
- }
- } catch (ClassCastException e) {
- return;
- }
- }
-
- }
-
-
- /**
- * Fix reload - required if reloading and using programmatic configuration.
- * When a context is reloaded, any programmatic configuration is lost. This
- * listener sets the equivalent of conf/web.xml when the context starts. The
- * context needs to be an instance of StandardContext for this listener to
- * have any effect.
- */
- public static class DefaultWebXmlListener implements LifecycleListener {
- public void lifecycleEvent(LifecycleEvent event) {
- if (Lifecycle.BEFORE_START_EVENT.equals(event.getType()) &&
- event.getLifecycle() instanceof StandardContext) {
- initWebappDefaults((StandardContext) event.getLifecycle());
- }
- }
- }
-
-
- /**
- * Helper class for wrapping existing servlets. This disables servlet
- * lifecycle and normal reloading, but also reduces overhead and provide
- * more direct control over the servlet.
- */
- public static class ExistingStandardWrapper extends StandardWrapper {
- private Servlet existing;
- boolean init = false;
-
- public ExistingStandardWrapper( Servlet existing ) {
- this.existing = existing;
- }
- @Override
- public synchronized Servlet loadServlet() throws ServletException {
- if (!init) {
- existing.init(facade);
- init = true;
- }
- return existing;
-
- }
- @Override
- public long getAvailable() {
- return 0;
- }
- @Override
- public boolean isUnavailable() {
- return false;
- }
- }
-
- /**
- * TODO: would a properties resource be better ? Or just parsing
- * /etc/mime.types ?
- * This is needed because we don't use the default web.xml, where this
- * is encoded.
- */
- public static final String[] DEFAULT_MIME_MAPPINGS = {
- "abs", "audio/x-mpeg",
- "ai", "application/postscript",
- "aif", "audio/x-aiff",
- "aifc", "audio/x-aiff",
- "aiff", "audio/x-aiff",
- "aim", "application/x-aim",
- "art", "image/x-jg",
- "asf", "video/x-ms-asf",
- "asx", "video/x-ms-asf",
- "au", "audio/basic",
- "avi", "video/x-msvideo",
- "avx", "video/x-rad-screenplay",
- "bcpio", "application/x-bcpio",
- "bin", "application/octet-stream",
- "bmp", "image/bmp",
- "body", "text/html",
- "cdf", "application/x-cdf",
- "cer", "application/pkix-cert",
- "class", "application/java",
- "cpio", "application/x-cpio",
- "csh", "application/x-csh",
- "css", "text/css",
- "dib", "image/bmp",
- "doc", "application/msword",
- "dtd", "application/xml-dtd",
- "dv", "video/x-dv",
- "dvi", "application/x-dvi",
- "eps", "application/postscript",
- "etx", "text/x-setext",
- "exe", "application/octet-stream",
- "gif", "image/gif",
- "gtar", "application/x-gtar",
- "gz", "application/x-gzip",
- "hdf", "application/x-hdf",
- "hqx", "application/mac-binhex40",
- "htc", "text/x-component",
- "htm", "text/html",
- "html", "text/html",
- "ief", "image/ief",
- "jad", "text/vnd.sun.j2me.app-descriptor",
- "jar", "application/java-archive",
- "java", "text/x-java-source",
- "jnlp", "application/x-java-jnlp-file",
- "jpe", "image/jpeg",
- "jpeg", "image/jpeg",
- "jpg", "image/jpeg",
- "js", "application/javascript",
- "jsf", "text/plain",
- "jspf", "text/plain",
- "kar", "audio/midi",
- "latex", "application/x-latex",
- "m3u", "audio/x-mpegurl",
- "mac", "image/x-macpaint",
- "man", "text/troff",
- "mathml", "application/mathml+xml",
- "me", "text/troff",
- "mid", "audio/midi",
- "midi", "audio/midi",
- "mif", "application/x-mif",
- "mov", "video/quicktime",
- "movie", "video/x-sgi-movie",
- "mp1", "audio/mpeg",
- "mp2", "audio/mpeg",
- "mp3", "audio/mpeg",
- "mp4", "video/mp4",
- "mpa", "audio/mpeg",
- "mpe", "video/mpeg",
- "mpeg", "video/mpeg",
- "mpega", "audio/x-mpeg",
- "mpg", "video/mpeg",
- "mpv2", "video/mpeg2",
- "nc", "application/x-netcdf",
- "oda", "application/oda",
- "odb", "application/vnd.oasis.opendocument.database",
- "odc", "application/vnd.oasis.opendocument.chart",
- "odf", "application/vnd.oasis.opendocument.formula",
- "odg", "application/vnd.oasis.opendocument.graphics",
- "odi", "application/vnd.oasis.opendocument.image",
- "odm", "application/vnd.oasis.opendocument.text-master",
- "odp", "application/vnd.oasis.opendocument.presentation",
- "ods", "application/vnd.oasis.opendocument.spreadsheet",
- "odt", "application/vnd.oasis.opendocument.text",
- "otg", "application/vnd.oasis.opendocument.graphics-template",
- "oth", "application/vnd.oasis.opendocument.text-web",
- "otp", "application/vnd.oasis.opendocument.presentation-template",
- "ots", "application/vnd.oasis.opendocument.spreadsheet-template ",
- "ott", "application/vnd.oasis.opendocument.text-template",
- "ogx", "application/ogg",
- "ogv", "video/ogg",
- "oga", "audio/ogg",
- "ogg", "audio/ogg",
- "spx", "audio/ogg",
- "flac", "audio/flac",
- "anx", "application/annodex",
- "axa", "audio/annodex",
- "axv", "video/annodex",
- "xspf", "application/xspf+xml",
- "pbm", "image/x-portable-bitmap",
- "pct", "image/pict",
- "pdf", "application/pdf",
- "pgm", "image/x-portable-graymap",
- "pic", "image/pict",
- "pict", "image/pict",
- "pls", "audio/x-scpls",
- "png", "image/png",
- "pnm", "image/x-portable-anymap",
- "pnt", "image/x-macpaint",
- "ppm", "image/x-portable-pixmap",
- "ppt", "application/vnd.ms-powerpoint",
- "pps", "application/vnd.ms-powerpoint",
- "ps", "application/postscript",
- "psd", "image/vnd.adobe.photoshop",
- "qt", "video/quicktime",
- "qti", "image/x-quicktime",
- "qtif", "image/x-quicktime",
- "ras", "image/x-cmu-raster",
- "rdf", "application/rdf+xml",
- "rgb", "image/x-rgb",
- "rm", "application/vnd.rn-realmedia",
- "roff", "text/troff",
- "rtf", "application/rtf",
- "rtx", "text/richtext",
- "sh", "application/x-sh",
- "shar", "application/x-shar",
- /*"shtml", "text/x-server-parsed-html",*/
- "sit", "application/x-stuffit",
- "snd", "audio/basic",
- "src", "application/x-wais-source",
- "sv4cpio", "application/x-sv4cpio",
- "sv4crc", "application/x-sv4crc",
- "svg", "image/svg+xml",
- "svgz", "image/svg+xml",
- "swf", "application/x-shockwave-flash",
- "t", "text/troff",
- "tar", "application/x-tar",
- "tcl", "application/x-tcl",
- "tex", "application/x-tex",
- "texi", "application/x-texinfo",
- "texinfo", "application/x-texinfo",
- "tif", "image/tiff",
- "tiff", "image/tiff",
- "tr", "text/troff",
- "tsv", "text/tab-separated-values",
- "txt", "text/plain",
- "ulw", "audio/basic",
- "ustar", "application/x-ustar",
- "vxml", "application/voicexml+xml",
- "xbm", "image/x-xbitmap",
- "xht", "application/xhtml+xml",
- "xhtml", "application/xhtml+xml",
- "xls", "application/vnd.ms-excel",
- "xml", "application/xml",
- "xpm", "image/x-xpixmap",
- "xsl", "application/xml",
- "xslt", "application/xslt+xml",
- "xul", "application/vnd.mozilla.xul+xml",
- "xwd", "image/x-xwindowdump",
- "vsd", "application/vnd.visio",
- "wav", "audio/x-wav",
- "wbmp", "image/vnd.wap.wbmp",
- "wml", "text/vnd.wap.wml",
- "wmlc", "application/vnd.wap.wmlc",
- "wmls", "text/vnd.wap.wmlsc",
- "wmlscriptc", "application/vnd.wap.wmlscriptc",
- "wmv", "video/x-ms-wmv",
- "wrl", "model/vrml",
- "wspolicy", "application/wspolicy+xml",
- "Z", "application/x-compress",
- "z", "application/x-compress",
- "zip", "application/zip"
- };
-}
diff --git a/modules/tomcat-lite/test/org/apache/coyote/lite/TomcatLiteCoyoteTest.java b/modules/tomcat-lite/test/org/apache/coyote/lite/TomcatLiteCoyoteTest.java
deleted file mode 100644
index a995076..0000000
--- a/modules/tomcat-lite/test/org/apache/coyote/lite/TomcatLiteCoyoteTest.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- */
-package org.apache.coyote.lite;
-
-import java.io.IOException;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import junit.framework.TestCase;
-
-import org.apache.tomcat.lite.http.HttpClient;
-import org.apache.tomcat.lite.http.HttpChannel;
-import org.apache.tomcat.lite.http.HttpConnector;
-import org.apache.tomcat.lite.io.BBuffer;
-
-/**
- * Very simple test.
- */
-public class TomcatLiteCoyoteTest extends TestCase {
- static int port = 8884;
- static {
- Logger.getLogger("org.apache.catalina.core.StandardService").setLevel(Level.WARNING);
- Logger.getLogger("org.apache.catalina.core.StandardEngine").setLevel(Level.WARNING);
- Logger.getLogger("org.apache.catalina.startup.ContextConfig").setLevel(Level.WARNING);
- }
- static Tomcat main = TomcatStandaloneMain.setUp(port);
-
- public void testSimple() throws IOException {
- HttpConnector clientCon = HttpClient.newClient();
-
- HttpChannel ch = clientCon.get("localhost", port);
- ch.getRequest().setRequestURI("/index.html");
- ch.getRequest().send();
-
- BBuffer res = ch.readAll(null, 0);
-
- assertTrue(res.toString(),
- res.toString().indexOf("<title>Apache Tomcat</title>") >= 0);
- }
-
-
-}
diff --git a/modules/tomcat-lite/test/org/apache/coyote/lite/TomcatStandaloneMain.java b/modules/tomcat-lite/test/org/apache/coyote/lite/TomcatStandaloneMain.java
deleted file mode 100644
index 98cf16e..0000000
--- a/modules/tomcat-lite/test/org/apache/coyote/lite/TomcatStandaloneMain.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- */
-package org.apache.coyote.lite;
-
-import javax.servlet.ServletException;
-
-import org.apache.catalina.LifecycleException;
-import org.apache.catalina.connector.Connector;
-import org.apache.tomcat.lite.TestMain;
-
-/**
- * Startup tomcat + coyote lite connector.
- * No config files used.
- *
- * @author Costin Manolache
- */
-public class TomcatStandaloneMain {
-
- public static Tomcat setUp(int port) {
- try {
- Tomcat tomcat = new Tomcat();
-
- tomcat.setPort(port);
- String base = TestMain.findDir("/output/build");
- tomcat.setBaseDir(base);
- // Absolute path - tomcat6 and 7 are different,
- // 7 adds webapps.
- tomcat.addWebapp("/", base + "/webapps/ROOT");
-
- LiteProtocolHandler litePH = setUp(tomcat, port);
-
- tomcat.start();
- return tomcat;
- } catch (LifecycleException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } catch (ServletException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- return null;
- }
-
- public static LiteProtocolHandler setUp(Tomcat tomcat, int port) {
- Connector connector;
- try {
- connector = new Connector(LiteProtocolHandler.class.getName());
- tomcat.getService().addConnector(connector);
- connector.setPort(port);
- tomcat.setConnector(connector);
- LiteProtocolHandler ph =
- (LiteProtocolHandler) connector.getProtocolHandler();
- return ph;
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
-
-}
diff --git a/modules/tomcat-lite/test/org/apache/tomcat/lite/TestMain.java b/modules/tomcat-lite/test/org/apache/tomcat/lite/TestMain.java
deleted file mode 100644
index d81aa7c..0000000
--- a/modules/tomcat-lite/test/org/apache/tomcat/lite/TestMain.java
+++ /dev/null
@@ -1,313 +0,0 @@
-/*
- */
-package org.apache.tomcat.lite;
-
-import java.io.BufferedInputStream;
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.lang.reflect.Constructor;
-import java.net.HttpURLConnection;
-import java.net.URL;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import org.apache.tomcat.lite.http.BaseMapper;
-import org.apache.tomcat.lite.http.HttpClient;
-import org.apache.tomcat.lite.http.Dispatcher;
-import org.apache.tomcat.lite.http.HttpConnector;
-import org.apache.tomcat.lite.http.HttpRequest;
-import org.apache.tomcat.lite.http.HttpResponse;
-import org.apache.tomcat.lite.http.HttpServer;
-import org.apache.tomcat.lite.http.HttpChannel.HttpService;
-import org.apache.tomcat.lite.http.services.EchoCallback;
-import org.apache.tomcat.lite.http.services.SleepCallback;
-import org.apache.tomcat.lite.io.BBuffer;
-import org.apache.tomcat.lite.io.jsse.JsseSslProvider;
-import org.apache.tomcat.lite.proxy.HttpProxyService;
-import org.apache.tomcat.lite.proxy.StaticContentService;
-import org.apache.tomcat.lite.service.IOStatus;
-
-/**
- * Laucher for tomcat-lite standalone, configured with test handlers.
- *
- * Used in tests - one is running for the entire suite.
- *
- * @author Costin Manolache
- */
-public class TestMain {
-
- static {
- JsseSslProvider.testModeURLConnection();
- }
-
- static TestMain defaultServer;
-
- private boolean init = false;
-
- HttpConnector testClient;
- HttpConnector testServer;
- HttpConnector testProxy;
- HttpConnector sslServer;
- HttpProxyService proxy;
-
- public TestMain() {
- init();
- }
-
- protected void init() {
- testClient = HttpClient.newClient();
- }
-
- /**
- * A single instance used for all tests.
- */
- public static TestMain shared() {
- if (defaultServer == null) {
- defaultServer = new TestMain();
- defaultServer.run();
- }
- return defaultServer;
- }
-
- public static HttpConnector getTestServer() {
- return shared().testServer;
- }
-
- public HttpConnector getClient() {
- return shared().testClient;
- }
-
- public static BaseMapper.Context initTestContext(Dispatcher d) throws IOException {
- BaseMapper.Context mCtx = d.addContext(null, "", null, null, null, null);
-
- mCtx.addWrapper("/", new StaticContentService()
- .setContentType("text/html")
- .setData("<a href='/proc/cpool/client'>Client pool</a><br>" +
- "<a href='/proc/cpool/server'>Server pool</a><br>" +
- "<a href='/proc/cpool/proxy'>Proxy pool</a><br>" +
- ""));
-
- mCtx.addWrapper("/favicon.ico",
- new StaticContentService().setStatus(404).setData("Not found"));
-
- mCtx.addWrapper("/hello", new StaticContentService().setData("Hello world"));
- mCtx.addWrapper("/2nd", new StaticContentService().setData("Hello world2"));
- mCtx.addWrapper("/echo/*", new EchoCallback());
-
- mCtx.addWrapper("/sleep/1", new SleepCallback().setData("sleep 1"));
- mCtx.addWrapper("/sleep/10", new SleepCallback().sleep(10000).setData(
- "sleep 1"));
-
- mCtx.addWrapper("/chunked/*", new StaticContentService().setData("AAAA")
- .chunked());
- mCtx.addWrapper("/helloClose", new HttpService() {
- @Override
- public void service(HttpRequest httpReq, HttpResponse httpRes)
- throws IOException {
- httpRes.setHeader("Connection", "close");
- httpRes.getBodyWriter().write("Hello");
- }
- });
- return mCtx;
- }
-
- public void initTestCallback(Dispatcher d) throws IOException {
- BaseMapper.Context mCtx = initTestContext(d);
- mCtx.addWrapper("/proc/cpool/client", new IOStatus(testClient.cpool));
- mCtx.addWrapper("/proc/cpool/proxy", new IOStatus(testProxy.cpool));
- mCtx.addWrapper("/proc/cpool/server", new IOStatus(testServer.cpool));
- }
-
- public void run() {
- try {
- startAll();
- // TODO(costin): clean up
- // Hook in JMX and debug properties
- try {
- Class c = Class.forName("org.apache.tomcat.lite.TomcatLiteJmx");
- Constructor constructor = c.getConstructor(TestMain.class);
- constructor.newInstance(this);
- } catch (Throwable t) {
- // ignore
- }
- } catch (Throwable t) {
- t.printStackTrace();
- }
- }
-
- public static String findDir(String dir) {
- String path = ".";
- for (int i = 0; i < 5; i++) {
- File f = new File(path + dir);
- if (f.exists()) {
- try {
- return f.getCanonicalPath();
- } catch (IOException e) {
- return f.getAbsolutePath();
- }
- }
- path = path + "/..";
- }
- return null;
- }
-
- public int getServerPort() {
- return 8802;
- }
-
- public int getProxyPort() {
- return 8903;
- }
-
- public int getSslServerPort() {
- return 8443;
- }
-
- static String PRIVATE_KEY =
- "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBALsz2milZGHliWte61TfMTSwpAdq" +
-"9uJkMTqgpSVtwxxOe8kT84QtIzhdAsQYjRz9ZtQn9DYWhJQs/cs/R3wWsjWwgiFHLzGalvsmMYJ3" +
-"vBO8VMj762fAWu7GjUApIXcxMJoK4sQUpZKbqTuXpwzVUeeqBcspsIDgOLCo233G7/fBAgMBAAEC" +
-"gYAWEaDX4VeaKuMuYzw+/yjf20sbDMMaIVGkZbfSV8Q+nAn/xHhaxq92P5DJ6VMJbd4neKZTkggD" +
-"J+KriUQ2Hr7XXd/nM+sllaDWGmUnMYFI4txaNkikMA3ZyE/Xa79eDpTnSst8Nm11vrX9oF/hDNo4" +
-"dhbU1krjAwVl/WijzSk4gQJBANvSmsmdjPlzvGNE11Aq3Ffb9/SqAOdE8NevMFeVKtBEKHIe1WlO" +
-"ThRyWv3I8bUKTQMNULruSFVghTh6Hkt/CBkCQQDaAuxaXjv2voYozkOviXMpt0X5LZJMQu2gFc2x" +
-"6UgBqYP2pNGDdRVWpbxF65PpXcLNKllCss2WB8i8kdeixYHpAkEAnIrzfia7sR2RiCQLLWUIe20D" +
-"vHGgqRG4bfCtfYGV9rDDGNoKYq7H/dmeIOML9kA6rbS6zBRK4LoWxSx6DIuPaQJAL2c3USbwTuR6" +
-"c2D2IrL2UXnCQz3/c4mR9Z8IDMk2mPXs9bI8xCKvMxnyaBmjHbj/ZHDy26fZP+gNY8MqagAcEQJA" +
-"SidPwFV6cO8LCIA43wSVHlKZt4yU5wa9EWfzqVZxj7VSav7431kuxktW/YlwwxO4Pn8hgpPqD+W1" +
-"E+Ssocxi8A==";
-
- static String CERTIFICATE = "-----BEGIN CERTIFICATE-----\n" +
-"MIIC5DCCAk2gAwIBAgIJAMa8ioWQMpEZMA0GCSqGSIb3DQEBBQUAMFYxCzAJBgNV" +
-"BAYTAlVTMQswCQYDVQQIEwJDQTESMBAGA1UEChMJbG9jYWxob3N0MRIwEAYDVQQL" +
-"Ewlsb2NhbGhvc3QxEjAQBgNVBAMTCWxvY2FsaG9zdDAeFw0xMDAyMjYyMzIxNDBa" +
-"Fw0xMTAyMjYyMzIxNDBaMFYxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTESMBAG" +
-"A1UEChMJbG9jYWxob3N0MRIwEAYDVQQLEwlsb2NhbGhvc3QxEjAQBgNVBAMTCWxv" +
-"Y2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAuzPaaKVkYeWJa17r" +
-"VN8xNLCkB2r24mQxOqClJW3DHE57yRPzhC0jOF0CxBiNHP1m1Cf0NhaElCz9yz9H" +
-"fBayNbCCIUcvMZqW+yYxgne8E7xUyPvrZ8Ba7saNQCkhdzEwmgrixBSlkpupO5en" +
-"DNVR56oFyymwgOA4sKjbfcbv98ECAwEAAaOBuTCBtjAdBgNVHQ4EFgQUj3OnBK8R" +
-"UN2CcmPvfQ1/IBeFwn8wgYYGA1UdIwR/MH2AFI9zpwSvEVDdgnJj730NfyAXhcJ/" +
-"oVqkWDBWMQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExEjAQBgNVBAoTCWxvY2Fs" +
-"aG9zdDESMBAGA1UECxMJbG9jYWxob3N0MRIwEAYDVQQDEwlsb2NhbGhvc3SCCQDG" +
-"vIqFkDKRGTAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4GBAKcJWWZbHRuG" +
-"77ir1ETltxNIsAFvuhDD6E68eBwpviWfKhFxiOdD1vmAGqWWDYpmgORBGxFMZxTq" +
-"c82iSbM0LseFeHwxAfeNXosSShMFtQzKt2wKZLLQB/Oqrea32m4hU//NP8rNbTux" +
-"dcAHeNQEDB5EUUSewAlh+fUE6HB6c8j0\n" +
-"-----END CERTIFICATE-----\n\n";
-
- protected synchronized void startAll() throws IOException {
- if (init) {
- System.err.println("2x init ???");
- } else {
- init = true;
- boolean debug = false;
- if (debug) {
- System.setProperty("javax.net.debug", "ssl");
- System.setProperty("jsse", "conn_state,alert,engine,record,ssocket,socket,prf");
- Logger.getLogger("SSL").setLevel(Level.FINEST);
- testClient.setDebug(true);
- testClient.setDebugHttp(true);
- }
-
- proxy = new HttpProxyService()
- .withHttpClient(testClient);
- testProxy = HttpServer.newServer(getProxyPort());
-
- if (debug) {
- testProxy.setDebugHttp(true);
- testProxy.setDebug(true);
- }
-
- // dispatcher rejects 'http://'
- testProxy.setHttpService(proxy);
- try {
- testProxy.start();
- } catch (IOException e) {
- e.printStackTrace();
- }
-
- testServer = HttpServer.newServer(getServerPort());
- if (debug) {
- testServer.setDebugHttp(true);
- testServer.setDebug(true);
- }
- initTestCallback(testServer.getDispatcher());
- try {
- testServer.start();
- } catch (IOException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
-
-// Base64 b64 = new Base64();
-// byte[] keyBytes = b64.decode(PRIVATE_KEY);
-
- sslServer = HttpServer.newSslServer(getSslServerPort());
-
- if (debug) {
- sslServer.setDebug(true);
- sslServer.setDebugHttp(true);
- }
- JsseSslProvider sslCon = (JsseSslProvider) sslServer.getSslProvider();
-
- sslCon = sslCon
- .setKeyRes("org/apache/tomcat/lite/http/genrsa_512.cert",
- "org/apache/tomcat/lite/http/genrsa_512.der");
- initTestCallback(sslServer.getDispatcher());
- sslServer.start();
- }
-
- Runtime.getRuntime().addShutdownHook(new Thread() {
- public void run() {
- System.err.println("Done");
- }
- public void start() {
- System.err.println("Done1");
- }
- });
- }
-
- /**
- * Blocking get, returns when the body has been read.
- */
- public static BBuffer get(String url) throws IOException {
-
- BBuffer out = BBuffer.allocate();
-
- HttpRequest aclient = HttpClient.newClient().request(url);
- aclient.send();
- aclient.readAll(out,
- //Long.MAX_VALUE);//
- 2000000);
- aclient.release(); // return connection to pool
- return out;
- }
-
- public static BBuffer getUrl(String path) throws IOException {
- BBuffer out = BBuffer.allocate();
- getUrl(path, out);
- return out;
- }
-
- public static HttpURLConnection getUrl(String path,
- BBuffer out) throws IOException {
- URL url = new URL(path);
- HttpURLConnection connection =
- (HttpURLConnection) url.openConnection();
- connection.setReadTimeout(10000);
- connection.connect();
- int rc = connection.getResponseCode();
- InputStream is = connection.getInputStream();
- BufferedInputStream bis = new BufferedInputStream(is);
- byte[] buf = new byte[2048];
- int rd = 0;
- while((rd = bis.read(buf)) > 0) {
- out.append(buf, 0, rd);
- }
- return connection;
- }
-
-
-}
diff --git a/modules/tomcat-lite/test/org/apache/tomcat/lite/http/ClientTest.java b/modules/tomcat-lite/test/org/apache/tomcat/lite/http/ClientTest.java
deleted file mode 100644
index 226cad3..0000000
--- a/modules/tomcat-lite/test/org/apache/tomcat/lite/http/ClientTest.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- */
-package org.apache.tomcat.lite.http;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-
-import org.apache.tomcat.lite.TestMain;
-
-import junit.framework.TestCase;
-
-/**
- * Examples and tests for Tomcat-lite in client mode.
- *
- */
-public class ClientTest extends TestCase {
-
- /**
- * All connectors created this way will share a single
- * IO thread. Each connector will have its keep-alive
- * pool - so it's better to share them.
- *
- * Since I want to test keep-alive works, I use a static one
- */
- static HttpConnector httpCon = HttpClient.newClient();
-
- /**
- * Start a http server, runs on 8802 - shared by all tests.
- * Will use /echo handler.
- */
- static HttpConnector testServer = TestMain.getTestServer();
-
-
- public void testSimpleBlocking() throws IOException {
- HttpRequest req = httpCon.request("http://localhost:8802/echo/test1");
- HttpResponse res = req.waitResponse();
-
- assertEquals(200, res.getStatus());
- //assertEquals("", res.getHeader(""));
-
- BufferedReader reader = res.getReader();
- String line1 = reader.readLine();
- assertEquals("REQ HEAD:", line1);
- }
-
- public void testSimpleCallback() throws IOException {
-
- }
-
- public void testGetParams() throws IOException {
- }
-
- public void testPostParams() throws IOException {
- }
-
- public void testPostBody() throws IOException {
- }
-
-
-}
diff --git a/modules/tomcat-lite/test/org/apache/tomcat/lite/http/CompressFilterTest.java b/modules/tomcat-lite/test/org/apache/tomcat/lite/http/CompressFilterTest.java
deleted file mode 100644
index 0ec3b82..0000000
--- a/modules/tomcat-lite/test/org/apache/tomcat/lite/http/CompressFilterTest.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- */
-package org.apache.tomcat.lite.http;
-
-import java.util.Random;
-
-import junit.framework.TestCase;
-
-import org.apache.tomcat.lite.io.IOBuffer;
-
-public class CompressFilterTest extends TestCase {
-
- CompressFilter cf = new CompressFilter();
-
- private void check(String clear, String xtra) throws Exception {
- IOBuffer in = new IOBuffer();
- IOBuffer out = new IOBuffer();
-
- in.append(clear);
- in.close();
-
- cf.compress(in, out);
-
-// BBuffer bb = out.copyAll(null);
-// String hd = Hex.getHexDump(bb.array(), bb.position(),
-// bb.remaining(), true);
-// System.err.println(hd);
-
- if (xtra != null) {
- out.append(xtra);
- }
- in.recycle();
- out.close();
- cf.decompress(out, in);
-
- assertEquals(in.copyAll(null).toString(), clear);
- assertTrue(in.isAppendClosed());
-
- if (xtra != null) {
- assertEquals(out.copyAll(null).toString(), xtra);
- }
- }
-
- public void test1() throws Exception {
- check("X1Y2Z3", null);
- }
-
- public void testXtra() throws Exception {
- check("X1Y2Z3", "GET /");
- }
-
- public void testLarge() throws Exception {
- StringBuffer sb = new StringBuffer();
- for (int i = 0; i < 2 * 1024; i++) {
- sb.append("0123456789012345");
- }
- check(sb.toString(), null);
- }
-
- public void testLarge10() throws Exception {
- for (int i = 0; i < 10; i++) {
- testLargeIn();
- cf.recycle();
- }
- }
-
- public void testLargeIn() throws Exception {
- StringBuffer sb = new StringBuffer();
- Random r = new Random();
- for (int i = 0; i < 16 * 2 * 1024; i++) {
- sb.append(' ' + r.nextInt(32));
- }
- check(sb.toString(), null);
- }
-
-
- public void testSpdy() throws Exception {
- cf.setDictionary(SpdyConnection.SPDY_DICT, SpdyConnection.DICT_ID);
- check("connection: close\n", null);
- }
-
-}
diff --git a/modules/tomcat-lite/test/org/apache/tomcat/lite/http/DispatcherTest.java b/modules/tomcat-lite/test/org/apache/tomcat/lite/http/DispatcherTest.java
deleted file mode 100644
index f474007..0000000
--- a/modules/tomcat-lite/test/org/apache/tomcat/lite/http/DispatcherTest.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- */
-package org.apache.tomcat.lite.http;
-
-import org.apache.tomcat.lite.io.CBuffer;
-
-import junit.framework.TestCase;
-
-public class DispatcherTest extends TestCase {
-
- public void testMapper() throws Exception {
- BaseMapper mapper = new BaseMapper();
-
- String[] welcomes = new String[2];
- welcomes[0] = "index.html";
- welcomes[1] = "foo.html";
-
- mapper.addContext("test1.com", "", "context0", new String[0], null, null);
- mapper.addContext("test1.com", "/foo", "context1", new String[0], null, null);
- mapper.addContext("test1.com", "/foo/bar", "context2", welcomes, null, null);
- mapper.addContext("test1.com", "/foo/bar/bla", "context3", new String[0], null, null);
-
- mapper.addWrapper("test1.com", "/foo/bar", "/fo/*", "wrapper0");
- mapper.addWrapper("test1.com", "/foo/bar", "/", "wrapper1");
- mapper.addWrapper("test1.com", "/foo/bar", "/blh", "wrapper2");
- mapper.addWrapper("test1.com", "/foo/bar", "*.jsp", "wrapper3");
- mapper.addWrapper("test1.com", "/foo/bar", "/blah/bou/*", "wrapper4");
- mapper.addWrapper("test1.com", "/foo/bar", "/blah/bobou/*", "wrapper5");
- mapper.addWrapper("test1.com", "/foo/bar", "*.htm", "wrapper6");
-
- mapper.addContext("asdf.com", "", "context0", new String[0], null, null);
-
- MappingData mappingData = new MappingData();
-
- CBuffer host = CBuffer.newInstance();
- host.set("test1.com");
-
- CBuffer uri = CBuffer.newInstance();
- uri.set("/foo/bar/blah/bobou/foo");
-
- mapper.map(host, uri, mappingData);
-
- assertEquals("context2", mappingData.context.toString());
- assertEquals("/foo/bar", mappingData.contextPath.toString());
- }
-}
diff --git a/modules/tomcat-lite/test/org/apache/tomcat/lite/http/HttpChannelInMemoryTest.java b/modules/tomcat-lite/test/org/apache/tomcat/lite/http/HttpChannelInMemoryTest.java
deleted file mode 100644
index f0b2cf2..0000000
--- a/modules/tomcat-lite/test/org/apache/tomcat/lite/http/HttpChannelInMemoryTest.java
+++ /dev/null
@@ -1,384 +0,0 @@
-/*
- */
-package org.apache.tomcat.lite.http;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-
-import junit.framework.TestCase;
-
-import org.apache.tomcat.lite.http.HttpChannel.HttpService;
-import org.apache.tomcat.lite.http.HttpChannel.RequestCompleted;
-import org.apache.tomcat.lite.io.BBuffer;
-import org.apache.tomcat.lite.io.CBuffer;
-import org.apache.tomcat.lite.io.IOBuffer;
-import org.apache.tomcat.lite.io.IOChannel;
-import org.apache.tomcat.lite.io.IOConnector;
-import org.apache.tomcat.lite.io.MemoryIOConnector;
-import org.apache.tomcat.lite.io.MemoryIOConnector.MemoryIOChannel;
-
-// TODO: rename to Http11ConnectionTest
-public class HttpChannelInMemoryTest extends TestCase {
- /**
- * Connection under test
- */
- Http11Connection conn;
-
- /**
- * Last http channel created by the connection
- */
- volatile HttpChannel http;
-
- // Input/output for the connection
- MemoryIOConnector.MemoryIOChannel net = new MemoryIOChannel();
-
- HttpConnector serverConnector = new HttpConnector(null);
-
- // Callback results for callback tests
- boolean hasBody = false;
- boolean bodyDone = false;
- boolean bodySentDone = false;
- boolean headersDone = false;
- boolean allDone = false;
-
- public void setUp() throws IOException {
- // Requests will not be serviced - you must manually generate
- // the response.
- serverConnector.setHttpService(null);
-
- conn = new Http11Connection(serverConnector) {
- protected HttpChannel checkHttpChannel() throws IOException {
- return http = super.checkHttpChannel();
- }
- }.serverMode();
- conn.setSink(net);
- }
-
-
- public void test2Req() throws IOException {
- String req = "GET /index.html?q=b&c=d HTTP/1.1\r\n" +
- "Host: Foo.com \n" +
- "H2:Bar\r\n" +
- "H3: Foo \r\n" +
- " Bar\r\n" +
- "H4: Foo\n" +
- " Bar\n" +
- "\r\n" +
- "HEAD /r2? HTTP/1.1\n" +
- "Host: Foo.com\r\n" +
- "H3: Foo \r\n" +
- " Bar\r\n" +
- "H4: Foo\n" +
- " Bar\n" +
- "\r\n";
- net.getIn().append(req);
-
- //http = lastServer.get(0);
- assertTrue(http.getRequest().method().equals("GET"));
- assertTrue(http.getRequest().protocol().equals("HTTP/1.1"));
- assertEquals(http.getRequest().getMimeHeaders().size(), 4);
- assertEquals(http.getRequest().getMimeHeaders().getHeader("Host").toString(),
- "Foo.com");
- assertEquals(http.getRequest().getMimeHeaders().getHeader("H2").toString(),
- "Bar");
-
- http.getOut().append("Response1");
- http.getOut().close();
- http.startSending();
- http.release();
-
- // now second response must be in.
- // the connector will create a new http channel
-
- //http = lastServer.get(1);
-
- assertTrue(http.getRequest().method().equals("HEAD"));
- assertTrue(http.getRequest().protocol().equals("HTTP/1.1"));
- assertTrue(http.getRequest().getMimeHeaders().size() == 3);
- assertTrue(http.getRequest().getMimeHeaders().getHeader("Host")
- .equals("Foo.com"));
- }
-
- public void testHttp11Close() throws IOException {
- String req = "GET /index.html?q=b&c=d HTTP/1.1\r\n" +
- "Host: Foo.com\n" +
- "Connection: close\n" +
- "\n";
- net.getIn().append(req);
-
- assertTrue(http.getRequest().method().equals("GET"));
- assertTrue(http.getRequest().protocol().equals("HTTP/1.1"));
-
- http.getOut().append("Response1");
- http.getOut().close();
- http.startSending();
- http.release();
-
- assertTrue(net.out.indexOf("connection:close") > 0);
- assertFalse(net.isOpen());
- }
-
- public void testHttp10Close() throws IOException {
- String req = "GET /index.html?q=b&c=d HTTP/1.0\r\n" +
- "Host: Foo.com \n" +
- "\r\n";
- net.getIn().append(req);
-
- assertTrue(http.getRequest().method().equals("GET"));
- assertTrue(http.getRequest().protocol().equals("HTTP/1.0"));
-
- http.getOut().append("Response1");
- http.getOut().close();
- http.startSending();
-
- assertTrue(net.out.indexOf("connection:close") > 0);
- assertFalse(net.isOpen());
- }
-
- public void testHttp10KA() throws IOException {
- String req = "GET /index.html?q=b&c=d HTTP/1.0\r\n" +
- "Connection: Keep-Alive\n" +
- "Host: Foo.com \n" +
- "\r\n";
- net.getIn().append(req);
-
- assertTrue(http.getRequest().method().equals("GET"));
- assertTrue(http.getRequest().protocol().equals("HTTP/1.0"));
-
- http.getOut().append("Hi");
- http.getOut().close();
- http.startSending();
-
- // after request
- assertEquals(conn.activeHttp, null);
-
- assertTrue(net.out.indexOf("connection:keep-alive") > 0);
- assertTrue(net.isOpen());
- // inserted since we can calculate the response
- assertEquals(http.getResponse().getHeader("Content-Length"),
- "2");
- }
-
- public void testHttp10KANoCL() throws IOException {
- String req = "GET /index.html?q=b&c=d HTTP/1.0\r\n" +
- "Connection: Keep-Alive\n" +
- "Host: Foo.com \n" +
- "\r\n";
- net.getIn().append(req);
-
- assertTrue(http.getRequest().method().equals("GET"));
- assertTrue(http.getRequest().protocol().equals("HTTP/1.0"));
-
- http.getOut().append("Hi");
- http.startSending();
-
- http.getOut().append("After");
- http.getOut().close();
- http.startSending();
-
- // after request
- assertEquals(conn.activeHttp, null);
-
- assertFalse(net.out.indexOf("connection:keep-alive") > 0);
- assertFalse(net.isOpen());
- // inserted since we can calculate the response
- assertEquals(http.getResponse().getHeader("Content-Length"),
- null);
- assertEquals(http.getResponse().getHeader("Transfer-Encoding"),
- null);
- }
-
- public void testMultiLineHead() throws IOException {
- net.getIn().append("GET / HTTP/1.0\n" +
- "Cookie: 1234\n" +
- " 456 \n" +
- "Connection: Close\n\n");
- net.getIn().close();
-
- MultiMap headers = http.getRequest().getMimeHeaders();
- CBuffer cookie = headers.getHeader("Cookie");
- CBuffer conn = headers.getHeader("Connection");
- assertEquals(conn.toString(), "close");
- assertEquals(cookie.toString(), "1234 456");
-
- assertEquals(http.conn.headRecvBuf.toString(),
- "GET / HTTP/1.0\n" +
- "Cookie: 1234 456 \n" + // \n -> trailing space
- "Connection: Close\n\n");
- }
-
- public void testCloseSocket() throws IOException {
- net.getIn().append("GET / HTTP/1.1\n"
- + "Host: localhost\n"
- + "\n");
- assertTrue(((Http11Connection)http.conn).keepAlive());
-
- net.getIn().close();
- assertFalse(((Http11Connection)http.conn).keepAlive());
- }
-
- public void test2ReqByte2Byte() throws IOException {
- String req = "GET /index.html?q=b&c=d HTTP/1.1\r\n" +
- "Host: Foo.com \n" +
- "H2:Bar\r\n" +
- "H3: Foo \r\n" +
- " Bar\r\n" +
- "H4: Foo\n" +
- " Bar\n" +
- "\r\n" +
- "HEAD /r2? HTTP/1.1\n" +
- "Host: Foo1.com\n" +
- "H3: Foo \r\n" +
- " Bar\r\n" +
- "\r\n";
- for (int i = 0; i < req.length(); i++) {
- net.getIn().append(req.charAt(i));
- }
-
- assertTrue(http.getRequest().method().equals("GET"));
- assertTrue(http.getRequest().protocol().equals("HTTP/1.1"));
- assertTrue(http.getRequest().getMimeHeaders().size() == 4);
- assertTrue(http.getRequest().getMimeHeaders().getHeader("Host")
- .equals("Foo.com"));
-
- // send a response
- http.sendBody.append("Response1");
- http.getOut().close();
-
- http.startSending(); // This will trigger a pipelined request
-
- http.release(); // now second response must be in
-
- assertTrue(http.getRequest().method().equals("HEAD"));
- assertTrue(http.getRequest().protocol().equals("HTTP/1.1"));
- assertTrue(http.getRequest().getMimeHeaders().size() == 2);
- assertTrue(http.getRequest().getMimeHeaders().getHeader("Host")
- .equals("Foo1.com"));
-
- // send a response - service method will be called
- http.sendBody.append("Response2");
- http.getOut().close();
- http.release(); // now second response must be in
-
-
- }
-
- public void testEndWithoutFlushCallbacks() throws IOException {
-
- net.getIn().append(POST);
-
- net.getIn().close();
- http.setCompletedCallback(new RequestCompleted() {
- public void handle(HttpChannel data, Object extra)
- throws IOException {
- allDone = true;
- }
- });
-
- http.sendBody.queue("Hi");
- http.getOut().close();
- http.startSending(); // will call handleEndSend
-
- assertTrue(allDone);
-
- }
-
- public void testCallbacks() throws IOException {
- // already accepted - will change
- serverConnector.setHttpService(new HttpService() {
- public void service(HttpRequest httpReq, HttpResponse httpRes)
- throws IOException {
-
- headersDone = true;
- HttpChannel http = httpReq.getHttpChannel();
-
- http.setCompletedCallback(new RequestCompleted() {
- public void handle(HttpChannel data, Object extra)
- throws IOException {
- allDone = true;
- }
- });
- http.setDataReceivedCallback(new IOConnector.DataReceivedCallback() {
- @Override
- public void handleReceived(IOChannel ch) throws IOException {
- if (ch.getIn().isAppendClosed()) {
- bodyDone = true;
- }
- }
- });
- http.setDataFlushedCallback(new IOConnector.DataFlushedCallback() {
- @Override
- public void handleFlushed(IOChannel ch) throws IOException {
- if (ch.getOut().isAppendClosed()) {
- bodySentDone = true;
- }
- }
- });
- }
- });
-
- // Inject the request
- net.getIn().append("POST / HTTP/1.0\n" +
- "Connection: Close\n" +
- "Content-Length: 4\n\n" +
- "1");
- assertTrue(headersDone);
- net.getIn().append("234");
-
- net.getIn().close();
- assertTrue(bodyDone);
-
-
- http.sendBody.queue("Hi");
- http.getOut().close();
- http.startSending();
- assertTrue(bodySentDone);
-
- assertTrue(allDone);
-
- }
-
- public static String POST = "POST / HTTP/1.0\n" +
- "Connection: Close\n" +
- "Content-Length: 4\n\n" +
- "1234";
-
- public void testClose() throws IOException {
- net.getIn().append(POST);
- net.getIn().close();
-
- IOBuffer receiveBody = http.receiveBody;
- IOBuffer appData = receiveBody;
- BBuffer res = BBuffer.allocate(1000);
- appData.readAll(res);
-
- assertEquals(res.toString(), "1234");
- assertFalse(((Http11Connection)http.conn).keepAlive());
-
- http.sendBody.queue(res);
- http.getOut().close();
- http.startSending();
-
- assertTrue(net.getOut().isAppendClosed());
- assertTrue(net.out.toString().indexOf("\n1234") > 0);
-
- }
-
- public void testReadLine() throws Exception {
- net.getIn().append("POST / HTTP/1.0\n" +
- "Content-Length: 28\n\n" +
- "Line 1\n" +
- "Line 2\r\n" +
- "Line 3\r" +
- "Line 4");
- net.getIn().close();
-
- BufferedReader r = http.getRequest().getReader();
- assertEquals("Line 1", r.readLine());
- assertEquals("Line 2", r.readLine());
- assertEquals("Line 3", r.readLine());
- assertEquals("Line 4", r.readLine());
- assertEquals(null, r.readLine());
-
- }
-}
diff --git a/modules/tomcat-lite/test/org/apache/tomcat/lite/http/HttpChannelTest.java b/modules/tomcat-lite/test/org/apache/tomcat/lite/http/HttpChannelTest.java
deleted file mode 100644
index ad13187..0000000
--- a/modules/tomcat-lite/test/org/apache/tomcat/lite/http/HttpChannelTest.java
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- */
-package org.apache.tomcat.lite.http;
-
-import java.io.IOException;
-
-import junit.framework.TestCase;
-
-import org.apache.tomcat.lite.io.BBuffer;
-
-public class HttpChannelTest extends TestCase {
-
- HttpChannel ch = new HttpChannel().serverMode(true);
- Http11Connection con = new Http11Connection(null).serverMode();
- HttpRequest req = ch.getRequest();
-
-
- BBuffer head = BBuffer.allocate();
- BBuffer line = BBuffer.wrapper();
- BBuffer name = BBuffer.wrapper();
- BBuffer value = BBuffer.wrapper();
-
- BBuffer statusB = BBuffer.wrapper();
- BBuffer msgB = BBuffer.wrapper();
- BBuffer methodB = BBuffer.wrapper();
- BBuffer queryB = BBuffer.wrapper("");
- BBuffer requestB = BBuffer.wrapper();
- BBuffer protoB = BBuffer.wrapper();
-
- BBuffer l7 = BBuffer.wrapper("GET \n");
- BBuffer l8 = BBuffer.wrapper("GET /\n");
- BBuffer l9 = BBuffer.wrapper("GET /a?b\n");
- BBuffer l10 = BBuffer.wrapper("GET /a?b HTTP/1.0\n");
- BBuffer l11 = BBuffer.wrapper("GET /a?b HTTP/1.0\na:b");
- BBuffer l12 = BBuffer.wrapper("GET /a?b HTTP/1.0\na:b\n");
-
- BBuffer f1 = BBuffer.wrapper("GET /a?b HTTP/1.0\na:b\n\n");
- BBuffer f2 = BBuffer.wrapper("GET /a?b HTTP/1.0\na:b\r\n\r\n");
- BBuffer f3 = BBuffer.wrapper("GET /a?b HTTP/1.0\na:b\r\r");
- BBuffer f4 = BBuffer.wrapper("GET /a?b HTTP/1.0\na:b\r\n\r");
-
- public void reqTest(String lineS, String method, String req,
- String qry, String proto) throws IOException {
- BBuffer line = BBuffer.wrapper(lineS);
- queryB.recycle();
- protoB.recycle();
- requestB.recycle();
- methodB.recycle();
- con.parseRequestLine(line, methodB, requestB, queryB, protoB);
- assertEquals(proto, protoB.toString());
- assertEquals(req, requestB.toString());
- assertEquals(qry, queryB.toString());
- assertEquals(method, methodB.toString());
- }
-
- public void testParams() throws IOException {
- MultiMap params = processQry("a=b&c=d");
- assertEquals("b", params.getString("a"));
- }
-
- private MultiMap processQry(String qry) throws IOException {
- BBuffer head = BBuffer.wrapper("GET /a?" + qry + " HTTP/1.0\n" +
- "Host: a\n\n");
- con.parseMessage(ch, head);
- MultiMap params = req.getParameters();
- return params;
- }
-
- public void testParseReq() throws IOException {
- reqTest("GET / HTTP/1.0", "GET", "/", "", "HTTP/1.0");
- reqTest("GET", "GET", "", "", "");
- reqTest("GET / HTTP/1.0", "GET", "/", "", "HTTP/1.0");
- reqTest("GET / HTTP/1.0", "GET", "/", "", "HTTP/1.0");
- reqTest("GET /?b HTTP/1.0", "GET", "/", "b", "HTTP/1.0");
- reqTest("GET ?a HTTP/1.0", "GET", "", "a", "HTTP/1.0");
- reqTest("GET a HTTP/1.0", "GET", "a", "", "HTTP/1.0");
- reqTest("GET a? HTTP/1.0", "GET", "a", "", "HTTP/1.0");
- }
-
- public void headTest(String headS, String expName, String expValue,
- String expLine, String expRest) throws IOException {
- head = BBuffer.wrapper(headS);
- head.readLine(line);
- con.parseHeader(ch, head, line, name, value);
-
- assertEquals(expName, name.toString());
- assertEquals(expValue, value.toString());
-
- assertEquals(expLine, line.toString());
- assertEquals(expRest, head.toString());
- }
-
- public void testParseHeader() throws IOException {
- headTest("a:b\n", "a", "b", "", "");
- headTest("a :b\n", "a", "b", "", "");
- headTest("a : b\n", "a", "b", "", "");
- headTest("a : b\n", "a", "b", "", "");
- headTest("a : b c \n", "a", "b c", "", "");
- headTest("a : b c\n", "a", "b c", "", "");
- headTest("a : b c\n", "a", "b c", "", "");
- headTest("a : b \n c\n", "a", "b c", "", "");
- headTest("a : b \n c\n", "a", "b c", "", "");
- headTest("a : b \n c\nd:", "a", "b c", "", "d:");
-
- }
-
- public void responseTest(String lineS, String proto, String status,
- String msg) throws IOException {
- protoB.recycle();
- statusB.recycle();
- msgB.recycle();
- BBuffer line = BBuffer.wrapper(lineS);
- con.parseResponseLine(line,
- protoB, statusB, msgB);
- assertEquals(proto, protoB.toString());
- assertEquals(status, statusB.toString());
- assertEquals(msg, msgB.toString());
- }
-
- public void testResponse() throws Exception {
- responseTest("HTTP/1.1 200 OK", "HTTP/1.1", "200", "OK");
- responseTest("HTTP/1.1 200 OK", "HTTP/1.1", "200", "OK");
- responseTest("HTTP/1.1 200", "HTTP/1.1", "200", "");
- responseTest("HTTP/1.1", "HTTP/1.1", "", "");
- }
-
-
-}
diff --git a/modules/tomcat-lite/test/org/apache/tomcat/lite/http/HttpsTest.java b/modules/tomcat-lite/test/org/apache/tomcat/lite/http/HttpsTest.java
deleted file mode 100644
index bc42b3b..0000000
--- a/modules/tomcat-lite/test/org/apache/tomcat/lite/http/HttpsTest.java
+++ /dev/null
@@ -1,74 +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.lite.http;
-
-import junit.framework.TestCase;
-
-import org.apache.tomcat.lite.TestMain;
-import org.apache.tomcat.lite.io.BBuffer;
-
-public class HttpsTest extends TestCase {
-
- static int port = 8443;
-
- public void testSimpleClient() throws Exception {
- final HttpConnector httpClient = TestMain.shared().getClient();
- checkResponse(httpClient);
- }
-
- public void testSimpleServer() throws Exception {
- final HttpConnector httpClient = TestMain.shared().getClient();
- BBuffer res = TestMain.getUrl("https://localhost:8443/hello");
- assertTrue(res.toString().indexOf("Hello") >= 0);
- }
-
-
- private void checkResponse(HttpConnector httpCon) throws Exception {
- HttpRequest ch = httpCon.request("localhost", port).setSecure(true);
-
- ch.setRequestURI("/hello");
- ch.setProtocol("HTTP/1.0"); // to force close
- ch.send();
- BBuffer res = ch.readAll();
-
- assertTrue(res.toString().indexOf("Hello") >= 0);
- }
-
- public void testSimpleClient20() throws Exception {
- final HttpConnector httpClient = TestMain.shared().getClient();
- for (int i = 0; i < 10; i++) {
- checkResponse(httpClient);
- }
- }
-
- public void testSimpleRequestGoogle() throws Exception {
- for (int i = 0; i < 40; i++) {
- final HttpConnector httpClient = TestMain.shared().getClient();
- HttpRequest client = httpClient.request("www.google.com", 443).
- setSecure(true);
- client.getHttpChannel().setIOTimeout(2000000);
- client.setRequestURI("/accounts/ServiceLogin");
- client.send();
-
- BBuffer res = client.readAll();
- assertTrue(res.toString().indexOf("<title>Google Accounts</title>") > 0);
- }
- }
-
-
-}
diff --git a/modules/tomcat-lite/test/org/apache/tomcat/lite/http/LiveHttp1Test.java b/modules/tomcat-lite/test/org/apache/tomcat/lite/http/LiveHttp1Test.java
deleted file mode 100644
index 00e4eac..0000000
--- a/modules/tomcat-lite/test/org/apache/tomcat/lite/http/LiveHttp1Test.java
+++ /dev/null
@@ -1,152 +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.lite.http;
-
-import java.io.IOException;
-
-import junit.framework.TestCase;
-
-import org.apache.tomcat.lite.TestMain;
-import org.apache.tomcat.lite.io.BBuffer;
-
-public class LiveHttp1Test extends TestCase {
- // Proxy tests extend this class, run same tests via proxy on 8903
- protected int clientPort = 8802;
-
- HttpRequest httpReq;
-
- BBuffer bodyRecvBuffer = BBuffer.allocate(1024);
-
- int to = 1000000;
-
- public void setUp() throws IOException {
- // DefaultHttpConnector.get().setDebug(true);
- // DefaultHttpConnector.get().setDebugHttp(true);
- TestMain.getTestServer();
-
- httpReq = HttpClient.newClient().request("localhost",
- clientPort);
-
- bodyRecvBuffer.recycle();
- }
-
- public void tearDown() throws Exception {
- if (httpReq != null) {
- httpReq.release(); // async
- httpReq = null;
- }
- }
-
- public void testSimpleRequest() throws Exception {
- httpReq.requestURI().set("/hello");
-
- httpReq.send();
- httpReq.readAll(bodyRecvBuffer, to);
- assertEquals("Hello world", bodyRecvBuffer.toString());
- }
-
- public void testSimpleRequestClose() throws Exception {
- httpReq.requestURI().set("/hello");
- httpReq.setHeader("Connection", "close");
-
- httpReq.send();
- httpReq.readAll(bodyRecvBuffer, to);
- assertEquals("Hello world", bodyRecvBuffer.toString());
- }
-
- public void testPoolGetRelease() throws Exception {
- HttpConnector con = HttpClient.newClient();
- con.setMaxHttpPoolSize(10);
- HttpChannel httpCh = con.get("localhost", clientPort);
- httpCh.release();
-
- httpCh = con.get("localhost", clientPort);
- httpCh.release();
-
- httpCh = con.get("localhost", clientPort);
- httpCh.release();
-
- }
-
- public void testSimpleChunkedRequest() throws Exception {
- httpReq.requestURI().set("/chunked/foo");
- httpReq.send();
- httpReq.readAll(bodyRecvBuffer, to);
- assertTrue(bodyRecvBuffer.toString(), bodyRecvBuffer.toString().indexOf("AAA") >= 0);
- }
-
- // Check waitResponseHead()
- public void testRequestHead() throws Exception {
- httpReq.requestURI().set("/echo/foo");
-
- // Send the request, wait response
- httpReq.send();
-
- httpReq.readAll(bodyRecvBuffer, to);
- assertTrue(bodyRecvBuffer.toString().indexOf("GET /echo/foo") > 0);
- }
-
- public void test10() throws Exception {
- for (int i = 0; i < 10; i++) {
- testSimpleRequest();
- tearDown();
- setUp();
-
- notFound();
- tearDown();
- setUp();
-
- testSimpleRequest();
- tearDown();
- setUp();
- }
- }
-
- public void notFound() throws Exception {
- httpReq.requestURI().set("/foo");
- httpReq.send();
- httpReq.readAll(bodyRecvBuffer, to);
- }
-
- // compression not implemented
- public void testGzipRequest() throws Exception {
- httpReq.requestURI().set("/hello");
- httpReq.setHeader("accept-encoding",
- "gzip");
-
- // Send the request, wait response
- httpReq.send();
- // cstate.waitResponseHead(10000); // headers are received
- // ByteChunk data = new ByteChunk(1024);
- // acstate.serializeResponse(acstate.res, data);
-
- // System.err.println(bodyRecvBuffer.toString());
-
- httpReq.readAll(bodyRecvBuffer, to);
- // Done
- }
-
- public void testWrongPort() throws Exception {
- httpReq = HttpClient.newClient().request("localhost", 18904);
- httpReq.requestURI().set("/hello");
-
- httpReq.send();
-
- httpReq.readAll(bodyRecvBuffer, to);
- assertEquals(0, bodyRecvBuffer.remaining());
- }
-}
diff --git a/modules/tomcat-lite/test/org/apache/tomcat/lite/http/MultiMapTest.java b/modules/tomcat-lite/test/org/apache/tomcat/lite/http/MultiMapTest.java
deleted file mode 100644
index e222f36..0000000
--- a/modules/tomcat-lite/test/org/apache/tomcat/lite/http/MultiMapTest.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- */
-package org.apache.tomcat.lite.http;
-
-import org.apache.tomcat.lite.http.MultiMap.Entry;
-
-import junit.framework.TestCase;
-
-public class MultiMapTest extends TestCase {
-
- MultiMap map = new MultiMap();
- MultiMap lmap = new MultiMap().insensitive();
-
- public void testAdd() {
- map.add("foo", "bar");
- assertEquals("bar", map.get("foo").toString());
- }
-
- public void testRemove() {
- map.add("foo", "bar");
- map.add("foo", "bar");
- map.add("foo1", "bar");
- assertEquals(3, map.count);
- map.remove("foo");
- assertEquals(1, map.count);
- }
-
- public void testRemove1() {
- map.add("foo", "bar");
- map.add("foo1", "bar");
- map.add("foo", "bar");
- assertEquals(3, map.count);
- map.remove("foo");
- assertEquals(1, map.count);
- map.remove("foo1");
- assertEquals(0, map.count);
- }
-
- public void testCase() {
- lmap.add("foo", "bar1");
- lmap.add("Foo", "bar2");
- lmap.add("a", "bar3");
- lmap.add("B", "bar4");
- assertEquals(4, lmap.count);
- assertEquals(3, lmap.map.size());
-
- assertEquals("bar3", lmap.getString("a"));
- assertEquals("bar3", lmap.getString("A"));
- assertEquals("bar1", lmap.getString("Foo"));
- Entry entry = lmap.getEntry("FOO");
- assertEquals(2, entry.values.size());
- }
-
-}
diff --git a/modules/tomcat-lite/test/org/apache/tomcat/lite/http/SpdyTest.java b/modules/tomcat-lite/test/org/apache/tomcat/lite/http/SpdyTest.java
deleted file mode 100644
index 3b51e10..0000000
--- a/modules/tomcat-lite/test/org/apache/tomcat/lite/http/SpdyTest.java
+++ /dev/null
@@ -1,165 +0,0 @@
-/*
- */
-package org.apache.tomcat.lite.http;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStream;
-
-import junit.framework.TestCase;
-
-import org.apache.tomcat.lite.TestMain;
-import org.apache.tomcat.lite.http.HttpConnectionPool.RemoteServer;
-import org.apache.tomcat.lite.io.IOBuffer;
-import org.apache.tomcat.lite.io.SocketConnector;
-
-public class SpdyTest extends TestCase {
- HttpConnector http11Con = TestMain.shared().getClient();
-
- static HttpConnector spdyCon = HttpClient.newClient();
-
- static HttpConnector spdyConSsl = HttpClient.newClient();
-
- HttpConnector memSpdyCon = new HttpConnector(null);
-
- public void testClient() throws IOException {
- HttpRequest req =
- spdyCon.request("http://localhost:8802/echo/test1");
-
- // Force SPDY - no negotiation
- req.setProtocol("SPDY/1.0");
-
- HttpResponse res = req.waitResponse();
-
- assertEquals(200, res.getStatus());
- //assertEquals("", res.getHeader(""));
-
- BufferedReader reader = res.getReader();
- String line1 = reader.readLine();
- //assertEquals("", line1);
- }
-
- public void testSslClient() throws IOException {
-
- HttpRequest req =
- spdyConSsl.request("http://localhost:8443/echo/test1");
- // Enable SSL for the connection.
- // TODO: this must be done on the first request, all will be
- // encrypted.
- req.setSecure(true);
- // Force SPDY - no negotiation
- req.setProtocol("SPDY/1.0");
-
- HttpResponse res = req.waitResponse();
-
- assertEquals(200, res.getStatus());
- //assertEquals("", res.getHeader(""));
-
- res.setReadTimeout(2000);
- BufferedReader reader = res.getReader();
- String line1 = reader.readLine();
- //assertEquals("", line1);
- }
-
-
- // Initial frame generated by Chrome
- public void testParse() throws IOException {
- InputStream is =
- getClass().getClassLoader().getResourceAsStream("org/apache/tomcat/lite/http/spdyreq0.bin");
-
- IOBuffer iob = new IOBuffer();
- iob.append(is);
-
- SpdyConnection con = new SpdyConnection(memSpdyCon, new RemoteServer());
-
- // By default it has a dispatcher buit-in
- con.serverMode = true;
-
- con.dataReceived(iob);
-
- HttpChannel spdyChannel = con.channels.get(1);
-
- assertEquals(1, con.lastFrame.version);
- assertEquals(1, con.lastFrame.type);
- assertEquals(1, con.lastFrame.flags);
-
- assertEquals(417, con.lastFrame.length);
-
- // TODO: test req, headers
- HttpRequest req = spdyChannel.getRequest();
- assertTrue(req.getHeader("accept").indexOf("application/xml") >= 0);
-
- }
-
- // Initial frame generated by Chrome
- public void testParseCompressed() throws IOException {
- InputStream is =
- getClass().getClassLoader().getResourceAsStream("org/apache/tomcat/lite/http/spdyreqCompressed.bin");
-
- IOBuffer iob = new IOBuffer();
- iob.append(is);
-
- SpdyConnection con = new SpdyConnection(memSpdyCon, new RemoteServer());
-
- // By default it has a dispatcher buit-in
- con.serverMode = true;
-
- con.dataReceived(iob);
-
- HttpChannel spdyChannel = con.channels.get(1);
-
- assertEquals(1, con.lastFrame.version);
- assertEquals(1, con.lastFrame.type);
- assertEquals(1, con.lastFrame.flags);
-
- // TODO: test req, headers
- HttpRequest req = spdyChannel.getRequest();
- assertTrue(req.getHeader("accept").indexOf("application/xml") >= 0);
-
- }
-
- // Does int parsing works ?
- public void testLargeInt() throws Exception {
-
- IOBuffer iob = new IOBuffer();
- iob.append(0x80);
- iob.append(0x01);
- iob.append(0xFF);
- iob.append(0xFF);
-
- iob.append(0xFF);
- iob.append(0xFF);
- iob.append(0xFF);
- iob.append(0xFF);
-
- SpdyConnection con = new SpdyConnection(memSpdyCon, new RemoteServer());
- con.dataReceived(iob);
- assertEquals(1, con.currentInFrame.version);
- assertEquals(0xFFFF, con.currentInFrame.type);
- assertEquals(0xFF, con.currentInFrame.flags);
- assertEquals(0xFFFFFF, con.currentInFrame.length);
-
- }
-
- // Does int parsing works ?
- public void testBad() throws Exception {
-
- IOBuffer iob = new IOBuffer();
- iob.append(0xFF);
- iob.append(0xFF);
- iob.append(0xFF);
- iob.append(0xFF);
-
- iob.append(0xFF);
- iob.append(0xFF);
- iob.append(0xFF);
- iob.append(0xFF);
-
- SpdyConnection con = new SpdyConnection(memSpdyCon, new RemoteServer());
- con.dataReceived(iob);
-
- assertEquals(1, con.streamErrors.get());
-
- }
-
-}
diff --git a/modules/tomcat-lite/test/org/apache/tomcat/lite/http/genrsa_512.cert b/modules/tomcat-lite/test/org/apache/tomcat/lite/http/genrsa_512.cert
deleted file mode 100644
index 8b22354..0000000
--- a/modules/tomcat-lite/test/org/apache/tomcat/lite/http/genrsa_512.cert
+++ /dev/null
@@ -1,15 +0,0 @@
------BEGIN CERTIFICATE-----
-MIICXzCCAgmgAwIBAgIJAKl2huIxu+mhMA0GCSqGSIb3DQEBBQUAMFYxCzAJBgNV
-BAYTAlVTMQswCQYDVQQIEwJDQTESMBAGA1UEChMJbG9jYWxob3N0MRIwEAYDVQQL
-Ewlsb2NhbGhvc3QxEjAQBgNVBAMTCWxvY2FsaG9zdDAeFw0xMDAyMjgwNTM1NDVa
-Fw0xMTAyMjgwNTM1NDVaMFYxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTESMBAG
-A1UEChMJbG9jYWxob3N0MRIwEAYDVQQLEwlsb2NhbGhvc3QxEjAQBgNVBAMTCWxv
-Y2FsaG9zdDBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQCkMTUYO8aijGGXIUTMRp3h
-NTBudLHDF66MDQxomS+drL1sDGzpsLG1ltZcSPrp2Ak2tDVLd9eDb6VmddgyvE0N
-AgMBAAGjgbkwgbYwHQYDVR0OBBYEFPfuRg3nOxppwBPdpOQTbjDbcPbEMIGGBgNV
-HSMEfzB9gBT37kYN5zsaacAT3aTkE24w23D2xKFapFgwVjELMAkGA1UEBhMCVVMx
-CzAJBgNVBAgTAkNBMRIwEAYDVQQKEwlsb2NhbGhvc3QxEjAQBgNVBAsTCWxvY2Fs
-aG9zdDESMBAGA1UEAxMJbG9jYWxob3N0ggkAqXaG4jG76aEwDAYDVR0TBAUwAwEB
-/zANBgkqhkiG9w0BAQUFAANBAFqvhtdEd1OKNZ2TbLeFJbvavSZUDd0d/vz+qZgh
-L2Oe+CU/82D4lVpf52g4HFMrnEnAiJFuq1SwoNJZzlxk8kU=
------END CERTIFICATE-----
diff --git a/modules/tomcat-lite/test/org/apache/tomcat/lite/http/genrsa_512.der b/modules/tomcat-lite/test/org/apache/tomcat/lite/http/genrsa_512.der
deleted file mode 100644
index 75ef449..0000000
Binary files a/modules/tomcat-lite/test/org/apache/tomcat/lite/http/genrsa_512.der and /dev/null differ
diff --git a/modules/tomcat-lite/test/org/apache/tomcat/lite/http/services/EchoCallback.java b/modules/tomcat-lite/test/org/apache/tomcat/lite/http/services/EchoCallback.java
deleted file mode 100644
index bd614ec..0000000
--- a/modules/tomcat-lite/test/org/apache/tomcat/lite/http/services/EchoCallback.java
+++ /dev/null
@@ -1,61 +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.lite.http.services;
-
-import java.io.IOException;
-import java.util.logging.Logger;
-
-import org.apache.tomcat.lite.http.Http11Connection;
-import org.apache.tomcat.lite.http.HttpChannel;
-import org.apache.tomcat.lite.http.HttpRequest;
-import org.apache.tomcat.lite.http.HttpResponse;
-import org.apache.tomcat.lite.http.HttpChannel.HttpService;
-import org.apache.tomcat.lite.io.IOBuffer;
-
-/**
- * Response is plain/text, copy of the received request
- */
-public class EchoCallback implements HttpService {
- Logger log = Logger.getLogger("coyote.static");
-
- String contentType = "text/plain";
-
-
- public EchoCallback() {
- }
-
- @Override
- public void service(HttpRequest req, HttpResponse res) throws IOException {
- HttpChannel sproc = req.getHttpChannel();
- res.setStatus(200);
- res.setContentType(contentType);
-
- IOBuffer tmp = new IOBuffer(null);
- Http11Connection.serialize(req, tmp);
-
- sproc.getOut().append("REQ HEAD:\n");
- sproc.getOut().append(tmp.readAll(null));
- IOBuffer reqBuf = sproc.getOut();
-
- reqBuf.append("\nCONTENT_LENGTH:")
- .append(Long.toString(req.getContentLength()))
- .append("\n");
-//
-// sproc.release();
- }
-
-
-}
\ No newline at end of file
diff --git a/modules/tomcat-lite/test/org/apache/tomcat/lite/http/services/SleepCallback.java b/modules/tomcat-lite/test/org/apache/tomcat/lite/http/services/SleepCallback.java
deleted file mode 100644
index 5bb174b..0000000
--- a/modules/tomcat-lite/test/org/apache/tomcat/lite/http/services/SleepCallback.java
+++ /dev/null
@@ -1,85 +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.lite.http.services;
-
-import java.io.IOException;
-
-import org.apache.tomcat.lite.http.HttpRequest;
-import org.apache.tomcat.lite.http.HttpResponse;
-import org.apache.tomcat.lite.io.BBuffer;
-import org.apache.tomcat.lite.proxy.StaticContentService;
-
-/**
- * Test adapters that sleeps, for load test and for encoding.
- * REQUIRES THREAD POOL
- */
-public class SleepCallback extends StaticContentService {
- long t1;
- long t2;
- long t3;
- long t4;
-
- public SleepCallback() {
- }
-
- public SleepCallback sleep(long t1, long t2, long t3,
- long t4) {
- this.t1 = t1;
- this.t2 = t2;
- this.t3 = t3;
- this.t4 = t4;
- return this;
- }
-
- public SleepCallback sleep(long t1) {
- return sleep(t1, t1, t1, t1);
- }
-
- @Override
- public void service(HttpRequest httpReq, HttpResponse res) throws IOException {
- // TODO: blocking ! needs thread pool !
- try {
-
- Thread.currentThread().sleep(t1);
- res.setStatus(200);
- if (!chunked) {
- res.setContentLength(mb.remaining() * 2);
- }
- res.setContentType(contentType);
-
- res.flush();
-
- Thread.currentThread().sleep(t2);
-
- res.getBody().queue(BBuffer.wrapper(mb, 0, mb.remaining()));
- res.flush();
-
- //res.action(ActionCode.ACTION_CLIENT_FLUSH, res);
-
- Thread.currentThread().sleep(t3);
-
- res.getBody().queue(BBuffer.wrapper(mb, 0, mb.remaining()));
- res.flush();
-
- Thread.currentThread().sleep(t4);
- } catch (InterruptedException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
-
-}
\ No newline at end of file
diff --git a/modules/tomcat-lite/test/org/apache/tomcat/lite/http/spdyreq0 b/modules/tomcat-lite/test/org/apache/tomcat/lite/http/spdyreq0
deleted file mode 100644
index 4df3aa9..0000000
Binary files a/modules/tomcat-lite/test/org/apache/tomcat/lite/http/spdyreq0 and /dev/null differ
diff --git a/modules/tomcat-lite/test/org/apache/tomcat/lite/http/spdyreq0.bin b/modules/tomcat-lite/test/org/apache/tomcat/lite/http/spdyreq0.bin
deleted file mode 100644
index 4df3aa9..0000000
Binary files a/modules/tomcat-lite/test/org/apache/tomcat/lite/http/spdyreq0.bin and /dev/null differ
diff --git a/modules/tomcat-lite/test/org/apache/tomcat/lite/http/spdyreqCompressed b/modules/tomcat-lite/test/org/apache/tomcat/lite/http/spdyreqCompressed
deleted file mode 100644
index 3507ff9..0000000
Binary files a/modules/tomcat-lite/test/org/apache/tomcat/lite/http/spdyreqCompressed and /dev/null differ
diff --git a/modules/tomcat-lite/test/org/apache/tomcat/lite/http/spdyreqCompressed.bin b/modules/tomcat-lite/test/org/apache/tomcat/lite/http/spdyreqCompressed.bin
deleted file mode 100644
index 3507ff9..0000000
Binary files a/modules/tomcat-lite/test/org/apache/tomcat/lite/http/spdyreqCompressed.bin and /dev/null differ
diff --git a/modules/tomcat-lite/test/org/apache/tomcat/lite/io/BBufferTest.java b/modules/tomcat-lite/test/org/apache/tomcat/lite/io/BBufferTest.java
deleted file mode 100644
index ec7c9e9..0000000
--- a/modules/tomcat-lite/test/org/apache/tomcat/lite/io/BBufferTest.java
+++ /dev/null
@@ -1,159 +0,0 @@
-/*
- */
-package org.apache.tomcat.lite.io;
-
-import org.apache.tomcat.lite.io.BBuffer;
-
-import junit.framework.TestCase;
-
-public class BBufferTest extends TestCase {
- BBuffer res = BBuffer.wrapper("");
-
- BBuffer l1 = BBuffer.wrapper("");
- BBuffer l1a = BBuffer.wrapper("a");
-
- BBuffer l2 = BBuffer.wrapper("\r");
- BBuffer l3 = BBuffer.wrapper("\n");
- BBuffer l4 = BBuffer.wrapper("\r\n");
- BBuffer l5 = BBuffer.wrapper("\r\na");
- BBuffer l5_a = BBuffer.wrapper("\ra");
- BBuffer l5_b = BBuffer.wrapper("\na");
- BBuffer l6 = BBuffer.wrapper("a\n");
- BBuffer l7 = BBuffer.wrapper("GET \n");
- BBuffer l8 = BBuffer.wrapper("GET /\n");
- BBuffer l9 = BBuffer.wrapper("GET /a?b\n");
- BBuffer l10 = BBuffer.wrapper("GET /a?b HTTP/1.0\n");
- BBuffer l11 = BBuffer.wrapper("GET /a?b HTTP/1.0\na:b");
- BBuffer l12 = BBuffer.wrapper("GET /a?b HTTP/1.0\na:b\n");
-
- BBuffer f1 = BBuffer.wrapper("GET /a?b HTTP/1.0\na:b\n\n");
- BBuffer f2 = BBuffer.wrapper("GET /a?b HTTP/1.0\na:b\r\n\r\n");
- BBuffer f3 = BBuffer.wrapper("GET /a?b HTTP/1.0\na:b\r\r");
- BBuffer f4 = BBuffer.wrapper("GET /a?b HTTP/1.0\na:b\r\n\r");
-
- BBuffer s1 = BBuffer.wrapper(" \n");
- BBuffer s2 = BBuffer.wrapper(" a");
- BBuffer s3 = BBuffer.wrapper(" ");
- BBuffer s4 = BBuffer.wrapper(" a");
-
- BBuffer h1 = BBuffer.wrapper("a");
- BBuffer h2 = BBuffer.wrapper("a?b");
- BBuffer h3 = BBuffer.wrapper("a b");
-
- public void hashTest(String s) {
- assertEquals(s.hashCode(), BBuffer.wrapper(s).hashCode());
- }
-
- public void testHash() {
- hashTest("");
- hashTest("a");
- hashTest("123abc");
- hashTest("123abc\0");
- // Fails for UTF chars - only ascii hashTest("123abc\u12345;");
- }
-
- public void testReadToSpace() {
- assertEquals(3, l8.readToSpace(res));
- assertEquals("GET", res.toString());
- assertEquals(" /\n", l8.toString());
-
- assertEquals(0, l1.readToSpace(res));
- assertEquals("", res.toString());
- assertEquals("", l1.toString());
- }
-
- public void testReadToDelim() {
- assertEquals(1, h1.readToDelimOrSpace((byte)'?', res));
- assertEquals("a", res.toString());
- assertEquals("", h1.toString());
-
- assertEquals(1, h2.readToDelimOrSpace((byte)'?', res));
- assertEquals("a", res.toString());
- assertEquals("?b", h2.toString());
-
- assertEquals(1, h3.readToDelimOrSpace((byte)'?', res));
- assertEquals("a", res.toString());
- assertEquals(" b", h3.toString());
- }
-
- public void testGet() {
- assertEquals(0x20, s1.get(0));
- assertEquals(0x0a, s1.get(1));
- try {
- s1.get(2);
- } catch(ArrayIndexOutOfBoundsException ex) {
- return;
- }
- fail("Exception");
- }
-
- public void testSkipSpace() {
- assertEquals(1, s1.skipSpace());
- assertEquals("\n", s1.toString());
-
- assertEquals(1, s2.skipSpace());
- assertEquals("a", s2.toString());
-
- assertEquals(2, s3.skipSpace());
- assertEquals("", s3.toString());
-
- assertEquals(3, s4.skipSpace());
- assertEquals("a", s4.toString());
-
- assertEquals(0, l1.skipSpace());
- assertEquals("", l1.toString());
- }
-
- public void testLFLF() {
- assertTrue(f1.hasLFLF());
- assertTrue(f2.hasLFLF());
- assertTrue(f3.hasLFLF());
-
- assertFalse(f4.hasLFLF());
- assertFalse(l1.hasLFLF());
- assertFalse(l2.hasLFLF());
- assertFalse(l3.hasLFLF());
-
- assertFalse(l10.hasLFLF());
- assertFalse(l11.hasLFLF());
- assertFalse(l12.hasLFLF());
- }
-
- public void testReadLine() {
- assertEquals(-1, l1.readLine(res));
- assertEquals("", res.toString());
- assertEquals("", l1.toString());
-
- assertEquals(-1, l1a.readLine(res));
- assertEquals("", res.toString());
- assertEquals("a", l1a.toString());
-
- assertEquals(0, l2.readLine(res));
- assertEquals("", l2.toString());
- assertEquals("", res.toString());
- assertEquals(0, l3.readLine(res));
- assertEquals("", l3.toString());
- assertEquals("", res.toString());
- assertEquals(0, l4.readLine(res));
- assertEquals("", res.toString());
-
- assertEquals(0, l5.readLine(res));
- assertEquals("", res.toString());
- assertEquals("a", l5.toString());
- assertEquals(0, l5_b.readLine(res));
- assertEquals("", res.toString());
- assertEquals("a", l5_b.toString());
- assertEquals(0, l5_a.readLine(res));
- assertEquals("", res.toString());
- assertEquals("a", l5_a.toString());
-
- assertEquals(1, l6.readLine(res));
- assertEquals("a", res.toString());
-
- assertEquals(4, l7.readLine(res));
- assertEquals("GET ", res.toString());
- assertEquals(5, l8.readLine(res));
- assertEquals("GET /", res.toString());
-
- }
-}
diff --git a/modules/tomcat-lite/test/org/apache/tomcat/lite/io/CBufferTest.java b/modules/tomcat-lite/test/org/apache/tomcat/lite/io/CBufferTest.java
deleted file mode 100644
index ab064d9..0000000
--- a/modules/tomcat-lite/test/org/apache/tomcat/lite/io/CBufferTest.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- */
-package org.apache.tomcat.lite.io;
-
-import junit.framework.TestCase;
-
-public class CBufferTest extends TestCase {
-
- CBuffer ext = CBuffer.newInstance();
-
- public void extTest(String path, String exp) {
- CBuffer.newInstance().append(path).getExtension(ext, '/', '.');
- assertEquals(exp, ext.toString());
- }
-
- public void testExt() {
- extTest("foo.jsp", "jsp");
- extTest("foo.j", "j");
- extTest("/foo.j", "j");
- extTest("//foo.j", "j");
- extTest(".j", "j");
- extTest(".", "");
- extTest("/abc", "");
- extTest("/abc.", "");
- extTest("/abc/", "");
- extTest("/abc/d", "");
- }
-
- public void testLastIndexOf() {
-
- }
-}
diff --git a/modules/tomcat-lite/test/org/apache/tomcat/lite/io/OneTest.java b/modules/tomcat-lite/test/org/apache/tomcat/lite/io/OneTest.java
deleted file mode 100644
index eb5202a..0000000
--- a/modules/tomcat-lite/test/org/apache/tomcat/lite/io/OneTest.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- */
-package org.apache.tomcat.lite.io;
-
-import java.io.IOException;
-
-import org.apache.tomcat.lite.TestMain;
-import org.apache.tomcat.lite.http.HttpChannel;
-import org.apache.tomcat.lite.io.MemoryIOConnector;
-import org.apache.tomcat.lite.io.MemoryIOConnector.MemoryIOChannel;
-
-import junit.framework.TestCase;
-
-public class OneTest extends TestCase {
-
- public void setUp() throws Exception {
- TestMain.getTestServer();
- }
-
- public void tearDown() throws IOException {
- }
-
-
- public void testOne() throws Exception {
-
- }
-}
diff --git a/modules/tomcat-lite/test/org/apache/tomcat/lite/io/SocksTest.java b/modules/tomcat-lite/test/org/apache/tomcat/lite/io/SocksTest.java
deleted file mode 100644
index 3a1443d..0000000
--- a/modules/tomcat-lite/test/org/apache/tomcat/lite/io/SocksTest.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- */
-package org.apache.tomcat.lite.io;
-
-import java.io.IOException;
-import java.net.InetAddress;
-import java.net.InetSocketAddress;
-import java.net.Proxy;
-import java.net.ProxySelector;
-import java.net.SocketAddress;
-import java.net.URI;
-import java.net.UnknownHostException;
-import java.util.ArrayList;
-import java.util.List;
-
-import org.apache.tomcat.lite.proxy.SocksServer;
-
-
-import junit.framework.TestCase;
-
-public class SocksTest extends TestCase {
-
- public void setUp() {
-// SocksServer socks = new SocksServer();
-// try {
-// socks.initServer();
-// } catch (IOException e1) {
-// // TODO Auto-generated catch block
-// e1.printStackTrace();
-// }
-//
-// ProxySelector.setDefault(new ProxySelector() {
-//
-// @Override
-// public void connectFailed(URI uri, SocketAddress sa, IOException ioe) {
-// }
-//
-// @Override
-// public List<Proxy> select(URI uri) {
-//
-// List<Proxy> res = new ArrayList<Proxy>();
-// try {
-// res.add(new Proxy(Proxy.Type.SOCKS,
-// new InetSocketAddress(InetAddress.getLocalHost(), 1080)));
-// } catch (UnknownHostException e) {
-// // TODO Auto-generated catch block
-// e.printStackTrace();
-// }
-// return res;
-// }
-//
-// });
- }
-
- public void testSocks() {
-
- }
-}
diff --git a/modules/tomcat-lite/test/org/apache/tomcat/lite/io/UEncoderTest.java b/modules/tomcat-lite/test/org/apache/tomcat/lite/io/UEncoderTest.java
deleted file mode 100644
index 9f4ffcc..0000000
--- a/modules/tomcat-lite/test/org/apache/tomcat/lite/io/UEncoderTest.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- */
-package org.apache.tomcat.lite.io;
-
-import junit.framework.TestCase;
-
-
-public class UEncoderTest extends TestCase {
- IOWriter enc=new IOWriter(null);
- UrlEncoding dec = new UrlEncoding();
- CBuffer cc = CBuffer.newInstance();
-
- /*
- *
- * Test method for 'org.apache.tomcat.util.buf.UEncoder.encodeURL(String)'
- * TODO: find the relevant rfc and apache tests and add more
- */
- public void testEncodeURL() {
-
- String eurl1=encodeURL("test");
- assertEquals("test", eurl1);
-
- eurl1=encodeURL("/test");
- assertEquals("/test", eurl1);
-
- // safe ranges
- eurl1=encodeURL("test$-_.");
- assertEquals("test$-_.", eurl1);
-
- eurl1=encodeURL("test$-_.!*'(),");
- assertEquals("test$-_.!*'(),", eurl1);
-
- eurl1=encodeURL("//test");
- assertEquals("//test", eurl1);
- }
-
- public String encodeURL(String uri) {
- cc.recycle();
- dec.urlEncode(uri, cc, enc);
- return cc.toString();
- }
-
-
-}
diff --git a/modules/tomcat-lite/test/org/apache/tomcat/lite/io/test.properties b/modules/tomcat-lite/test/org/apache/tomcat/lite/io/test.properties
deleted file mode 100644
index 19ee5e4..0000000
--- a/modules/tomcat-lite/test/org/apache/tomcat/lite/io/test.properties
+++ /dev/null
@@ -1,33 +0,0 @@
-RUN=Log,JMXProxy,Socks
-
-Log.(class)=org.apache.tomcat.integration.simple.LogConfig
-Log.debug=org.apache.tomcat.async.AsyncHttpConnector
-Log.debug=Proxy
-
-JMXProxy.(class)=org.apache.tomcat.integration.simple.JMXProxy
-JMXProxy.port=8003
-
-Socks.(class)=org.apache.tomcat.async.callbacks.SocksServer
-Socks.port=2080
-Socks.idleTimeout=0
-
-HttpConnector-TestServer.debug=true
-#HttpConnector-TestServer.debugHttp=true
-HttpConnector-TestServer.clientKeepAlive=true
-HttpConnector-TestServer.serverKeepAlive=true
-HttpConnector-TestServer.maxHttpPoolSize=10
-
-HttpConnector.debug=true
-#HttpConnector.debugHttp=true
-HttpConnector.clientKeepAlive=true
-HttpConnector.serverKeepAlive=true
-HttpConnector.maxHttpPoolSize=10
-
-HttpConnector-Proxy.debug=true
-#HttpConnector-Proxy.debugHttp=true
-HttpConnector-Proxy.clientKeepAlive=true
-HttpConnector-Proxy.serverKeepAlive=true
-HttpConnector-Proxy.maxHttpPoolSize=10
-
-
-#IOConnector.debug=true
diff --git a/modules/tomcat-lite/test/org/apache/tomcat/lite/load/LiveHttp5Test.java b/modules/tomcat-lite/test/org/apache/tomcat/lite/load/LiveHttp5Test.java
deleted file mode 100644
index d167a5d..0000000
--- a/modules/tomcat-lite/test/org/apache/tomcat/lite/load/LiveHttp5Test.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- */
-package org.apache.tomcat.lite.load;
-
-import org.apache.tomcat.lite.http.LiveHttp1Test;
-
-import junit.framework.TestSuite;
-
-public class LiveHttp5Test extends LiveHttp1Test {
-
- /**
- * Want to run the same tests few times.
- */
- public static TestSuite suite() {
- TestSuite s = new TestSuite();
- for (int i = 0; i < 5; i++) {
- s.addTestSuite(LiveHttp1Test.class);
- }
- return s;
- }
-
- public void test100() throws Exception {
- for (int i = 0; i < 100; i++) {
- testSimpleRequest();
- tearDown();
- setUp();
-
- notFound();
- tearDown();
- setUp();
-
- testSimpleRequest();
- tearDown();
- setUp();
- }
- }
-
-}
diff --git a/modules/tomcat-lite/test/org/apache/tomcat/lite/load/LiveHttpThreadedTest.java b/modules/tomcat-lite/test/org/apache/tomcat/lite/load/LiveHttpThreadedTest.java
deleted file mode 100644
index 7849f49..0000000
--- a/modules/tomcat-lite/test/org/apache/tomcat/lite/load/LiveHttpThreadedTest.java
+++ /dev/null
@@ -1,292 +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.lite.load;
-
-
-import java.io.IOException;
-import java.net.HttpURLConnection;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.concurrent.atomic.AtomicInteger;
-
-import junit.framework.TestCase;
-
-import org.apache.tomcat.lite.TestMain;
-import org.apache.tomcat.lite.http.HttpClient;
-import org.apache.tomcat.lite.http.HttpChannel;
-import org.apache.tomcat.lite.http.HttpConnector;
-import org.apache.tomcat.lite.http.HttpRequest;
-import org.apache.tomcat.lite.http.HttpChannel.RequestCompleted;
-import org.apache.tomcat.lite.io.BBuffer;
-import org.apache.tomcat.lite.io.SocketConnector;
-
-/*
- Notes on memory use ( from heap dumps ):
- - buffers are not yet recycled ( the BBuffers used in channels )
-
- - each active connection consumes at least 26k - 2 buffers + head buffer
- ( 8k each )
- TODO: could 'peak' in the In buffer and move headRecv to HttpChannel
-
-
- - HttpChannel keeps about 64K ( for the hello world ).
- -- res is 25k
- -- req is 32k, BufferedIOReader 16k,
-
- TODO:
- - leak in NioThread.active - closed sockets not removed
- - need to rate-limit and queue requests - OOM
- - timeouts
- - seems few responses missing on large async requests (URL works)
- */
-
-/**
- * Long running test - async tests are failing since rate control
- * is not implemented ( too many outstanding requests - OOM ),
- * it seems there is a bug as well.
- */
-public class LiveHttpThreadedTest extends TestCase {
- HttpConnector clientCon = TestMain.shared().getClient();
- HttpConnector serverCon = TestMain.shared().getTestServer();
-
- HttpConnector spdyClient =
- HttpClient.newClient().setCompression(false);
-
- HttpConnector spdyClientCompress =
- HttpClient.newClient();
-
- HttpConnector spdyClientCompressSsl =
- HttpClient.newClient();
-
- ThreadRunner tr;
- static boolean dumpHeap = true;
-
- AtomicInteger ok = new AtomicInteger();
- int reqCnt;
-
- Map<HttpRequest, HttpRequest> active = new HashMap();
-
- public void tearDown() throws IOException {
- clientCon.cpool.clear();
- }
-
- public void test1000Async() throws Exception {
-// try {
- asyncRequest(10, 100, false, false, clientCon, "AsyncHttp");
-// } finally {
-// dumpHeap("heapAsync.bin");
-// }
-
- }
-
- public void test10000Async() throws Exception {
- asyncRequest(20, 500, false, false, clientCon, "AsyncHttp");
- }
-
-
- public void test1000AsyncSsl() throws Exception {
- asyncRequest(20, 50, false, true, clientCon, "AsyncHttpSsl");
- }
-
- public void test10000AsyncSsl() throws Exception {
- asyncRequest(20, 500, false, true, clientCon, "AsyncHttpSsl");
- }
-
- public void test1000AsyncSpdy() throws Exception {
- asyncRequest(10, 100, true, false, spdyClient, "AsyncSpdy");
- }
-
- public void test10000AsyncSpdy() throws Exception {
- asyncRequest(20, 500, true, false, spdyClient, "AsyncSpdy");
- }
-
- public void test1000AsyncSpdyComp() throws Exception {
- asyncRequest(10, 100, true, false, spdyClientCompress, "AsyncSpdyComp");
- }
-
- public void test10000AsyncSpdyComp() throws Exception {
- asyncRequest(20, 500, true, false, spdyClientCompress, "AsyncSpdyComp");
- }
-
- public void xtest1000AsyncSpdySsl() throws Exception {
- asyncRequest(10, 100, true, true, spdyClient, "AsyncSpdySsl");
- }
-
- public void xtest1000AsyncSpdyCompSsl() throws Exception {
- asyncRequest(10, 100, true, true, spdyClientCompress, "AsyncSpdyCompSsl");
- }
-
- public void xtest10000AsyncSpdyCompSsl() throws Exception {
- asyncRequest(20, 500, true, true, spdyClientCompress, "AsyncSpdyCompSsl");
- }
-
- Object thrlock = new Object();
- Object lock = new Object();
-
- public void asyncRequest(final int thr, int perthr,
- final boolean spdy, final boolean ssl,
- final HttpConnector clientCon, String test) throws Exception {
- clientCon.getConnectionPool().clear();
- reqCnt = thr * perthr;
- long t0 = System.currentTimeMillis();
-
- tr = new ThreadRunner(thr, perthr) {
- public void makeRequest(int i) throws Exception {
- HttpRequest cstate = clientCon.request("localhost",
- ssl ? 8443 : 8802);
- synchronized (active) {
- active.put(cstate, cstate);
- }
- if (spdy) {
- // Magic way to force spdy - will be replaced with
- // a negotiation.
- cstate.setProtocol("SPDY/1.0");
- }
- if (ssl) {
- cstate.setSecure(true);
- }
- cstate.requestURI().set("/hello");
- cstate.setCompletedCallback(reqCallback);
- // no body
- cstate.getBody().close();
-
- cstate.send();
-
- while (active.size() >= thr) {
- synchronized(thrlock) {
- thrlock.wait();
- }
- }
- }
- };
- tr.run();
- synchronized (lock) {
- if (ok.get() < reqCnt) {
- lock.wait(reqCnt * 100);
- }
- }
- long time = (System.currentTimeMillis() - t0);
-
- System.err.println("====== " + test +
- " threads: " + thr + ", req: " +
- reqCnt + ", sendTime" + tr.time +
- ", time: " + time +
- ", connections: " + clientCon.getConnectionPool().getSocketCount() +
- ", avg: " + (time / reqCnt));
-
- assertEquals(reqCnt, ok.get());
- assertEquals(0, tr.errors.get());
- }
-
- RequestCompleted reqCallback = new RequestCompleted() {
- @Override
- public void handle(HttpChannel data, Object extraData)
- throws IOException {
- String out = data.getIn().copyAll(null).toString();
- if (200 != data.getResponse().getStatus()) {
- System.err.println("Wrong status");
- tr.errors.incrementAndGet();
- } else if (!"Hello world".equals(out)) {
- tr.errors.incrementAndGet();
- System.err.println("bad result " + out);
- }
- synchronized (active) {
- active.remove(data.getRequest());
- }
- synchronized (thrlock) {
- thrlock.notify();
- }
- data.release();
- int okres = ok.incrementAndGet();
- if (okres >= reqCnt) {
- synchronized (lock) {
- lock.notify();
- }
- }
- }
- };
-
-
-
- public void testURLRequest1000() throws Exception {
- urlRequest(10, 100, false, "HttpURLConnection");
- }
-
- public void xtestURLRequest10000() throws Exception {
- urlRequest(20, 500, false, "HttpURLConnection");
-
- }
-
- // I can't seem to get 1000 requests to all complete...
- public void xtestURLRequestSsl100() throws Exception {
- urlRequest(10, 10, true, "HttpURLConnectionSSL");
- }
-
- public void xtestURLRequestSsl10000() throws Exception {
- urlRequest(20, 500, true, "HttpURLConnectionSSL");
-
- }
-
- /**
- * HttpURLConnection client against lite.http server.
- */
- public void urlRequest(int thr, int cnt, final boolean ssl, String test)
- throws Exception {
- long t0 = System.currentTimeMillis();
-
-
- try {
- HttpConnector testServer = TestMain.getTestServer();
-
- tr = new ThreadRunner(thr, cnt) {
-
- public void makeRequest(int i) throws Exception {
- try {
- BBuffer out = BBuffer.allocate();
- String url = ssl ? "https://localhost:8443/hello" :
- "http://localhost:8802/hello";
- HttpURLConnection con =
- TestMain.getUrl(url, out);
- if (con.getResponseCode() != 200) {
- errors.incrementAndGet();
- }
- if (!"Hello world".equals(out.toString())) {
- errors.incrementAndGet();
- System.err.println("bad result " + out);
- }
- } catch(Throwable t) {
- t.printStackTrace();
- errors.incrementAndGet();
- }
- }
- };
- tr.run();
- assertEquals(0, tr.errors.get());
- long time = (System.currentTimeMillis() - t0);
-
- System.err.println("====== " + test + " threads: " + thr + ", req: " +
- (thr * cnt) + ", time: " + time + ", avg: " +
- (time / (thr * cnt)));
- } finally {
- //dumpHeap("heapURLReq.bin");
- }
- }
-
- // TODO: move to a servlet
-
-
-}
diff --git a/modules/tomcat-lite/test/org/apache/tomcat/lite/load/MicroTest.java b/modules/tomcat-lite/test/org/apache/tomcat/lite/load/MicroTest.java
deleted file mode 100644
index 732205e..0000000
--- a/modules/tomcat-lite/test/org/apache/tomcat/lite/load/MicroTest.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- */
-package org.apache.tomcat.lite.load;
-
-import org.apache.tomcat.lite.http.BaseMapper;
-import org.apache.tomcat.lite.http.MappingData;
-import org.apache.tomcat.lite.io.CBuffer;
-
-import junit.framework.TestCase;
-
-public class MicroTest extends TestCase {
-
- public void testMapper() throws Exception {
- BaseMapper mapper = new BaseMapper();
-
- MappingData mappingData = new MappingData();
- CBuffer host = CBuffer.newInstance();
- host.set("test1.com");
-
- CBuffer uri = CBuffer.newInstance();
- uri.set("/foo/bar/blah/bobou/foo");
-
- String[] welcomes = new String[2];
- welcomes[0] = "index.html";
- welcomes[1] = "foo.html";
-
- for (int i = 0; i < 100; i++) {
- String hostN = "test" + i + ".com";
- mapper.addContext(hostN, "", "context0", new String[0], null, null);
- mapper.addContext(hostN, "/foo", "context1", new String[0], null, null);
- mapper.addContext(hostN, "/foo/bar", "context2", welcomes, null, null);
- mapper.addContext(hostN, "/foo/bar/bla", "context3", new String[0], null, null);
-
- mapper.addWrapper(hostN, "/foo/bar", "/fo/*", "wrapper0");
- }
- int N = 10000;
- for (int i = 0; i < N; i++) {
- mappingData.recycle();
- mapper.map(host, uri, mappingData);
- }
-
- long time = System.currentTimeMillis();
- for (int i = 0; i < N; i++) {
- mappingData.recycle();
- mapper.map(host, uri, mappingData);
- }
- // TODO: asserts
- //System.out.println("Elapsed:" + (System.currentTimeMillis() - time));
- }
-}
diff --git a/modules/tomcat-lite/test/org/apache/tomcat/lite/load/ThreadRunner.java b/modules/tomcat-lite/test/org/apache/tomcat/lite/load/ThreadRunner.java
deleted file mode 100644
index 098f5b8..0000000
--- a/modules/tomcat-lite/test/org/apache/tomcat/lite/load/ThreadRunner.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- */
-package org.apache.tomcat.lite.load;
-
-import java.util.concurrent.atomic.AtomicInteger;
-
-public class ThreadRunner {
- int tCount = 10;
- int rCount = 100;
- Thread[] threads;
- int[] ok;
-
- int sleepTime = 0;
-
- long time;
- protected AtomicInteger errors = new AtomicInteger();
-
- public ThreadRunner(int threads, int count) {
- tCount = threads;
- rCount = count;
- this.threads = new Thread[tCount];
- ok = new int[tCount];
- }
-
- public void run() {
- long t0 = System.currentTimeMillis();
- for (int i = 0; i < tCount; i++) {
- final int j = i;
- threads[i] = new Thread(new Runnable() {
- public void run() {
- makeRequests(j);
- }
- });
- threads[i].start();
- }
-
- int res = 0;
- for (int i = 0; i < tCount; i++) {
- try {
- threads[i].join();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- res += ok[i];
- }
- long t1 = System.currentTimeMillis();
- time = t1 - t0;
- }
-
- public void makeRequests(int cnt) {
- for (int i = 0; i < rCount ; i++) {
- try {
- //System.err.println("MakeReq " + t + " " + i);
- makeRequest(cnt);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
-
- public void makeRequest(int i) throws Exception {
-
- }
-}
\ No newline at end of file
diff --git a/modules/tomcat-lite/test/org/apache/tomcat/lite/proxy/LiveProxyHttp1Test.java b/modules/tomcat-lite/test/org/apache/tomcat/lite/proxy/LiveProxyHttp1Test.java
deleted file mode 100644
index d04d624..0000000
--- a/modules/tomcat-lite/test/org/apache/tomcat/lite/proxy/LiveProxyHttp1Test.java
+++ /dev/null
@@ -1,33 +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.lite.proxy;
-
-
-import java.io.IOException;
-
-import org.apache.tomcat.lite.http.LiveHttp1Test;
-
-
-public class LiveProxyHttp1Test extends LiveHttp1Test {
- public void setUp() throws IOException {
- // All tests in super, but with client pointing to
- // the proxy server, which in turn hits the real server.
- clientPort = 8903;
- super.setUp();
- }
-
-}
diff --git a/modules/tomcat-lite/test/org/apache/tomcat/lite/proxy/ProxyTest.java b/modules/tomcat-lite/test/org/apache/tomcat/lite/proxy/ProxyTest.java
deleted file mode 100644
index 9d3181c..0000000
--- a/modules/tomcat-lite/test/org/apache/tomcat/lite/proxy/ProxyTest.java
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
-n * 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.lite.proxy;
-
-import java.io.IOException;
-
-import org.apache.tomcat.lite.TestMain;
-
-import junit.framework.TestCase;
-
-
-public class ProxyTest extends TestCase {
-
- String resStr;
-
- public void setUp() throws Exception {
- TestMain.getTestServer();
- }
-
- public void tearDown() throws IOException {
- }
-
- public void xtestRequestSlowChunked() throws Exception {
- resStr =
- TestMain.get("http://localhost:8903/sleep/1c").toString();
- assertEquals("sleep 1csleep 1c", resStr);
- }
-
- public void testSingleRequest() throws Exception {
- String resStr =
- TestMain.get("http://localhost:8903/hello").toString();
- assertEquals("Hello world", resStr);
- }
-
-
- public void test2Requests() throws Exception {
- String resStr =
- TestMain.get("http://localhost:8903/hello").toString();
- assertEquals("Hello world", resStr);
- resStr =
- TestMain.get("http://localhost:8903/hello?a=b").toString();
- assertEquals("Hello world", resStr);
- }
-
- public void testRequestSimple() throws Exception {
- resStr =
- TestMain.get("http://localhost:8903/hello").toString();
- assertEquals("Hello world", resStr);
- resStr =
- TestMain.get("http://localhost:8903/hello").toString();
- assertEquals("Hello world", resStr);
- resStr =
- TestMain.get("http://localhost:8903/hello").toString();
- assertEquals(resStr, "Hello world");
-
- }
-
- public void testExtAdapter() throws Exception {
- String res =
- TestMain.get("http://www.apache.org/").toString();
- assertTrue(res.indexOf("Apache") > 0);
-
- Thread.currentThread().sleep(100);
- // second time - are we reusing ?
- res =
- TestMain.get("http://www.apache.org/").toString();
-
- assertTrue(res.indexOf("Apache") > 0);
-
- }
-
- public void testStaticAdapter() throws Exception {
-
- assertEquals("Hello world",
- TestMain.get("http://localhost:8802/hello").toString());
- assertEquals("Hello world2",
- TestMain.get("http://localhost:8802/2nd").toString());
-
- }
-
- public void testRequestParams() throws Exception {
- // qry string
- String resStr =
- TestMain.get("http://localhost:8903/echo/foo?q=a&b")
- .toString();
- assertTrue(resStr, resStr.indexOf("foo?q=a&b") > 0);
- }
-
-
- public void testRequestChunked() throws Exception {
- // Chunked encoding
- String resStr =
- TestMain.get("http://localhost:8903/chunked/test")
- .toString();
- assertEquals(4, resStr.length());
- assertTrue(resStr.indexOf("AAA") >= 0);
- }
-
-
- public void testRequestSlow() throws Exception {
- // Slow
- String resStr =
- TestMain.get("http://localhost:8903/sleep/1").toString();
- assertEquals("sleep 1sleep 1", resStr.toString());
- }
-}
diff --git a/modules/tomcat-lite/test/org/apache/tomcat/lite/proxy/SmallProxyTest.java b/modules/tomcat-lite/test/org/apache/tomcat/lite/proxy/SmallProxyTest.java
deleted file mode 100644
index bc8bac9..0000000
--- a/modules/tomcat-lite/test/org/apache/tomcat/lite/proxy/SmallProxyTest.java
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- */
-package org.apache.tomcat.lite.proxy;
-
-import java.io.IOException;
-
-import junit.framework.TestCase;
-
-import org.apache.tomcat.lite.http.HttpChannel;
-import org.apache.tomcat.lite.http.HttpConnector;
-import org.apache.tomcat.lite.http.HttpConnector.HttpConnection;
-import org.apache.tomcat.lite.io.MemoryIOConnector;
-import org.apache.tomcat.lite.io.MemoryIOConnector.MemoryIOChannel;
-
-public class SmallProxyTest extends TestCase {
-
- MemoryIOConnector memoryServerConnector =
- new MemoryIOConnector();
-
- MemoryIOConnector memoryClientConnector =
- new MemoryIOConnector().withServer(memoryServerConnector);
-
-
- HttpConnector httpCon = new HttpConnector(memoryServerConnector) {
- @Override
- public HttpChannel get(CharSequence target) throws IOException {
- throw new IOException();
- }
- public HttpChannel getServer() {
- lastServer = new HttpChannel().serverMode(true);
- lastServer.setConnector(this);
- //lastServer.withIOConnector(memoryServerConnector);
- return lastServer;
- }
- };
-
- HttpConnector httpClient = new HttpConnector(memoryClientConnector) {
- @Override
- public HttpChannel get(CharSequence target) throws IOException {
- lastClient = new HttpChannel();
- lastClient.setConnector(this);
- return lastClient;
- }
- public HttpChannel get(String host, int port) throws IOException {
- lastClient = new HttpChannel();
- lastClient.setConnector(this);
- return lastClient;
- }
- public HttpChannel getServer() {
- throw new RuntimeException();
- }
- };
-
- HttpChannel lastServer;
- HttpChannel lastClient;
-
- boolean hasBody = false;
- boolean bodyDone = false;
- boolean bodySentDone = false;
- boolean headersDone = false;
- boolean allDone = false;
-
-
- //MemoryIOChannel clientNet = new MemoryIOChannel();
-
- MemoryIOConnector.MemoryIOChannel net = new MemoryIOChannel();
- HttpChannel http;
-
- HttpConnection serverConnection;
-
- public void setUp() throws IOException {
- http = httpCon.getServer();
- serverConnection = httpCon.handleAccepted(net);
- }
-
- /**
- * More complicated test..
- * @throws IOException
- */
- public void testProxy() throws IOException {
- httpCon.setHttpService(new HttpProxyService()
- .withSelector(memoryClientConnector)
- .withHttpClient(httpClient));
-
- net.getIn().append("GET http://www.apache.org/ HTTP/1.0\n" +
- "Connection: Close\n\n");
- net.getIn().close();
-
- // lastClient.rawSendBuffers has the request sent by proxy
- lastClient.getNet().getIn()
- .append("HTTP/1.0 200 OK\n\nHi\n");
- lastClient.getNet().getIn()
- .append("world\n");
-
- // TODO: check what the proxy sent
- // lastClient.getOut();
-
- // will also trigger 'release' - both sides are closed.
- lastClient.getNet().getIn().close();
-
- // wait response...
- // http.sendBody.close();
- String res = net.out.toString();
- assertTrue(res.indexOf("Hi\nworld\n") > 0);
- assertTrue(res.indexOf("HTTP/1.0 200 OK") == 0);
- assertTrue(res.indexOf("tomcatproxy") > 0);
-
- }
-}
diff --git a/modules/tomcat-lite/test/org/apache/tomcat/lite/util/UEncoderTest.java b/modules/tomcat-lite/test/org/apache/tomcat/lite/util/UEncoderTest.java
deleted file mode 100644
index ba606f0..0000000
--- a/modules/tomcat-lite/test/org/apache/tomcat/lite/util/UEncoderTest.java
+++ /dev/null
@@ -1,51 +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.lite.util;
-
-import junit.framework.TestCase;
-
-public class UEncoderTest extends TestCase {
- URLEncoder enc=new URLEncoder();
-
- /*
- *
- * Test method for 'org.apache.tomcat.util.buf.UEncoder.encodeURL(String)'
- * TODO: find the relevant rfc and apache tests and add more
- */
- public void testEncodeURL() {
-
- String eurl1=enc.encodeURL("test");
- assertEquals("test", eurl1);
-
- eurl1=enc.encodeURL("/test");
- assertEquals("/test", eurl1);
-
- // safe ranges
- eurl1=enc.encodeURL("test$-_.");
- assertEquals("test$-_.", eurl1);
-
- eurl1=enc.encodeURL("test$-_.!*'(),");
- assertEquals("test$-_.!*'(),", eurl1);
-
- eurl1=enc.encodeURL("//test");
- assertEquals("//test", eurl1);
-
-
- }
-
-}
diff --git a/modules/tomcat-lite/test/org/apache/tomcat/test/watchdog/AntProperties.java b/modules/tomcat-lite/test/org/apache/tomcat/test/watchdog/AntProperties.java
deleted file mode 100644
index 7e1b03b..0000000
--- a/modules/tomcat-lite/test/org/apache/tomcat/test/watchdog/AntProperties.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- */
-package org.apache.tomcat.test.watchdog;
-
-import java.util.Hashtable;
-
-/**
- * Extracted from IntrospectionHelper - a simple utility class to
- * do ant style ${property} replacements on a string, using a map
- * holding properties. Also allows a hook for dynamic, on-demand
- * properties.
- *
- * @author Costin Manolache
- */
-public class AntProperties {
- public static interface PropertySource {
- public String getProperty(String key);
- }
-
- /**
- * Replace ${NAME} with the property value
- */
- public static String replaceProperties(String value,
- Hashtable<Object,Object> staticProp, PropertySource dynamicProp[]) {
- if (value.indexOf("$") < 0) {
- return value;
- }
- StringBuffer sb = new StringBuffer();
- int prev = 0;
- // assert value!=nil
- int pos;
- while ((pos = value.indexOf("$", prev)) >= 0) {
- if (pos > 0) {
- sb.append(value.substring(prev, pos));
- }
- if (pos == (value.length() - 1)) {
- sb.append('$');
- prev = pos + 1;
- } else if (value.charAt(pos + 1) != '{') {
- sb.append('$');
- prev = pos + 1; // XXX
- } else {
- int endName = value.indexOf('}', pos);
- if (endName < 0) {
- sb.append(value.substring(pos));
- prev = value.length();
- continue;
- }
- String n = value.substring(pos + 2, endName);
- String v = null;
- if (staticProp != null) {
- v = (String) staticProp.get(n);
- }
- if (v == null && dynamicProp != null) {
- for (int i = 0; i < dynamicProp.length; i++) {
- v = dynamicProp[i].getProperty(n);
- if (v != null) {
- break;
- }
- }
- }
- if (v == null)
- v = "${" + n + "}";
-
- sb.append(v);
- prev = endName + 1;
- }
- }
- if (prev < value.length())
- sb.append(value.substring(prev));
- return sb.toString();
- }
-
-
-}
diff --git a/modules/tomcat-lite/test/org/apache/tomcat/test/watchdog/CookieController.java b/modules/tomcat-lite/test/org/apache/tomcat/test/watchdog/CookieController.java
deleted file mode 100644
index 0c7e64c..0000000
--- a/modules/tomcat-lite/test/org/apache/tomcat/test/watchdog/CookieController.java
+++ /dev/null
@@ -1,636 +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.test.watchdog;
-
-import java.io.BufferedReader;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.FileReader;
-import java.io.FileWriter;
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.net.URL;
-import java.util.Enumeration;
-import java.util.Hashtable;
-import java.util.Properties;
-import java.util.Vector;
-
-
-/**
- * Represents a collection of Cookie instances.
- * <p>
- * Fires events when the cookies have been changed internally. Deals
- * with management of cookies in terms of saving and loading them,
- * and disabling them.
- *
- * @author Ramesh.Mandava
- */
-public class CookieController {
-
-// private VetoableChangeSupport vceListeners;
-
- private static Hashtable cookieJar = new Hashtable();
-
- /* public no arg constructor for bean */
- public CookieController() {
- }
-
-/////////////////////////////////////////////////////////////
- /**
- * Records any cookies which have been sent as part of an HTTP response.
- * The connection parameter must be already have been opened, so that
- * the response headers are available. It's ok to pass a non-HTTP
- * URL connection, or one which does not have any set-cookie headers.
- */
- public void recordAnyCookies(Vector rcvVectorOfCookies , URL url ) {
-
- if ((rcvVectorOfCookies == null) || ( rcvVectorOfCookies.size()== 0) ) {
- // no headers here
- return;
- }
- try {
- /*
- Properties properties = new Properties();
- FileInputStream fin = new FileInputStream("ServerAutoRun.properties");
- properties.load(fin);
-
- String cookiepolicy = properties.getProperty("cookie.acceptpolicy");
- if (cookiepolicy == null || cookiepolicy.equals("none")) {
- return;
- }
- */
-
-
- for (int hi = 0; hi<rcvVectorOfCookies.size(); hi++) {
-
- String cookieValue = (String)rcvVectorOfCookies.elementAt(hi) ;
- recordCookie(url, cookieValue); // What to do here
- }
-
- }
- catch( Exception e )
- {
- System.out.println("exception : " + e );
- }
- }
-
-
- /**
- * Create a cookie from the cookie, and use the HttpURLConnection to
- * fill in unspecified values in the cookie with defaults.
- */
- public void recordCookie(URL url,
- String cookieValue) {
- HttpCookie cookie = new HttpCookie(url, cookieValue);
-
- // First, check to make sure the cookie's domain matches the
- // server's, and has the required number of '.'s
- String twodot[]=
- {"com", "edu", "net", "org", "gov", "mil", "int"};
- String domain = cookie.getDomain();
- if( domain == null )
- return;
- int index = domain.indexOf(':');
- if (index != -1) {
- int portCookie;
- try {
- portCookie = (Integer.valueOf(domain.substring(index+1,domain.length()))).intValue();
- } catch (Exception e) {
- return;
- }
- portCookie = ( portCookie == -1 ) ? 80 : portCookie;
- domain=domain.substring(0,index);
- }
- domain.toLowerCase();
-
- String host = url.getHost();
- host.toLowerCase();
-
- boolean domainOK = host.equals(domain);
- if( !domainOK && host.endsWith( domain ) ) {
- int dotsNeeded = 2;
- for( int i = 0; i < twodot.length; i++ ) {
- if( domain.endsWith( twodot[i] ) ) {
- dotsNeeded = 1;
- }
- }
-
- int lastChar = domain.length();
- for( ; lastChar > 0 && dotsNeeded > 0; dotsNeeded-- ) {
- lastChar = domain.lastIndexOf( '.', lastChar-1 );
- }
-
- if( lastChar > 0 )
- domainOK = true;
- }
-
- if( domainOK ) {
- recordCookie(cookie);
-
- }
- }
-
-
- /**
- * Record the cookie in the in-memory container of cookies. If there
- * is already a cookie which is in the exact same domain with the
- * exact same
- */
- public void recordCookie(HttpCookie cookie) {
- if (!checkIfCookieOK(cookie)) {
- return;
- }
- synchronized (cookieJar) {
-
- String domain = cookie.getDomain().toLowerCase();
-
- Vector cookieList = (Vector)cookieJar.get(domain);
- if (cookieList == null) {
- cookieList = new Vector();
- }
-
- addOrReplaceCookie(cookieList, cookie);
- cookieJar.put(domain, cookieList);
-
- }
-
- }
-
- public boolean checkIfCookieOK(HttpCookie cookie) {
- return true;
- }
-
- /**
- * Scans the vector of cookies looking for an exact match with the
- * given cookie. Replaces it if there is one, otherwise adds
- * one at the end. The vector is presumed to have cookies which all
- * have the same domain, so the domain of the cookie is not checked.
- * <p>
- * <p>
- * If this is called, it is assumed that the cookie jar is exclusively
- * held by the current thread.
- *
- */
- private void addOrReplaceCookie(Vector cookies,
- HttpCookie cookie) {
- int numCookies = cookies.size();
-
- String path = cookie.getPath();
- String name = cookie.getName();
- HttpCookie replaced = null;
- int replacedIndex = -1;
-
- for (int i = 0; i < numCookies; i++) {
- HttpCookie existingCookie = (HttpCookie)cookies.elementAt(i);
-
- String existingPath = existingCookie.getPath();
- if (path.equals(existingPath)) {
- String existingName = existingCookie.getName();
- if (name.equals(existingName)) {
- // need to replace this one!
- replaced = existingCookie;
- replacedIndex = i;
- break;
- }
- }
- }
-
-
- // Do the replace - if cookie has already expired, remove
- // the replaced cookie.
- if (replaced != null) {
- if (cookie.isSaveableInMemory()) {
- cookies.setElementAt(cookie, replacedIndex);
- //System.out.println("REPLACED existing cookie with " + cookie);
- } else {
- cookies.removeElementAt(replacedIndex);
- //System.out.println("Removed cookie b/c or expr " + cookie);
- }
-
- } else { // only save the cookie in memory if it is non persistent
- // or not expired.
- if (cookie.isSaveableInMemory()) {
- cookies.addElement(cookie);
- //System.out.println("RECORDED new cookie " + cookie);
- }
-
- }
-
- }
-
- public String applyRelevantCookies(URL url ) {
-
- try {
- /*
- Properties properties = new Properties();
- FileInputStream fin = new FileInputStream("ServerAutoRun.properties");
- properties.load(fin);
- // check current accept policy instead enableCookies
- String cookiepolicy = properties.getProperty("cookie.acceptpolicy");
- if (cookiepolicy == null || cookiepolicy.equals("none")) {
- return null;
- }
-
- */
-
- return applyCookiesForHost(url);
-
- }
- catch ( Exception e )
- {
- System.out.println("Exception : " +e );
- return null;
- }
-
-
- }
-
-
- /**
- * Host may be a FQDN, or a partial domain name starting with a dot.
- * Adds any cookies which match the host and path to the
- * cookie set on the URL connection.
- */
- private String applyCookiesForHost(URL url ){
- String cookieString = null;
- Vector cookieVector = getAllRelevantCookies(url);
-
- if (cookieVector != null) {
-
- for (Enumeration e = cookieVector.elements(); e.hasMoreElements();) {
- HttpCookie cookie = (HttpCookie)e.nextElement();
- if( cookieString == null ) {
- cookieString = cookie.getNameValue();
- } else {
- cookieString = cookieString + "; " + cookie.getNameValue();
- }
- }
-
- /*
-
- if( cookieString != null ) {
- httpConn.setRequestProperty("Cookie", cookieString);
-
-// System.out.println("Returned cookie string: " + cookieString + " for HOST = " + host);
- }
-
- */
-
-
- }
-// System.out.println("Returned cookie string: " + cookieString + " for HOST = " + host);
- return cookieString;
-
- }
-
- private Vector getAllRelevantCookies(URL url) {
- String host = url.getHost();
- Vector cookieVector = getSubsetRelevantCookies(host, url);
-
- Vector tempVector;
- int index;
-
- while ((index = host.indexOf('.', 1)) >= 0) {
- // trim off everything up to, and including the dot.
- host = host.substring(index+1);
-
- // add onto cookieVector
- tempVector = getSubsetRelevantCookies(host,url);
- if (tempVector != null ) {
- for (Enumeration e = tempVector.elements(); e.hasMoreElements(); ) {
- if (cookieVector == null) {
- cookieVector = new Vector(2);
- }
-
- cookieVector.addElement(e.nextElement());
-
- }
- }
- }
- return cookieVector;
- }
-
- private Vector getSubsetRelevantCookies(String host, URL url) {
-
- Vector cookieList = (Vector)cookieJar.get(host);
-
-// System.out.println("getRelevantCookies() .. for host, url " + host +", "+url);
- Vector cookiePortList = (Vector)cookieJar.get(host+":"+((url.getPort() == -1) ? 80 : url.getPort()));
- if (cookiePortList != null) {
- if (cookieList == null) {
- cookieList = new Vector(10);
- }
- Enumeration cookies = cookiePortList.elements();
- while (cookies.hasMoreElements()) {
- cookieList.addElement(cookies.nextElement());
- }
- }
-
-
- if (cookieList == null) {
- return null;
- }
-
- String path = url.getFile();
-// System.out.println(" path is " + path + "; protocol = " + url.getProtocol());
-
-
- int queryInd = path.indexOf('?');
- if (queryInd > 0) {
- // strip off the part following the ?
- path = path.substring(0, queryInd);
- }
-
- Enumeration cookies = cookieList.elements();
- Vector cookiesToSend = new Vector(10);
-
- while (cookies.hasMoreElements()) {
- HttpCookie cookie = (HttpCookie)cookies.nextElement();
-
- String cookiePath = cookie.getPath();
-
- if (path.startsWith(cookiePath)) {
- // larrylf: Actually, my documentation (from Netscape)
- // says that /foo should
- // match /foobar and /foo/bar. Yuck!!!
-
- if (!cookie.hasExpired()) {
- cookiesToSend.addElement(cookie);
- }
-
-/*
- We're keeping this piece of commented out code around just in
- case we decide to put it back. the spec does specify the above,
- but it is so disgusting!
-
- int cookiePathLen = cookiePath.length();
-
- // verify that /foo does not match /foobar by mistake
- if ((path.length() == cookiePathLen)
- || (path.length() > cookiePathLen &&
- path.charAt(cookiePathLen) == '/')) {
-
- // We have a matching cookie!
-
- if (!cookie.hasExpired()) {
- cookiesToSend.addElement(cookie);
- }
- }
-*/
- }
- }
-
- // Now, sort the cookies in most to least specific order
- // Yes, its the deaded bubblesort!! Wah Ha-ha-ha-ha....
- // (it should be a small vector, so perf is not an issue...)
- if( cookiesToSend.size() > 1 ) {
- for( int i = 0; i < cookiesToSend.size()-1; i++ ) {
- HttpCookie headC = (HttpCookie)cookiesToSend.elementAt(i);
- String head = headC.getPath();
- // This little excercise is a cheap way to get
- // '/foo' to read more specfic then '/'
- if( !head.endsWith("/") ) {
- head = head + "/";
- }
- for( int j = i+1; j < cookiesToSend.size(); j++ ) {
- HttpCookie scanC = (HttpCookie)cookiesToSend.elementAt(j);
- String scan = scanC.getPath();
- if( !scan.endsWith("/") ) {
- scan = scan + "/";
- }
-
- int headCount = 0;
- int index = -1;
- while( (index=head.indexOf('/', index+1)) != -1 ) {
- headCount++;
- }
- index = -1;
-
- int scanCount = 0;
- while( (index=scan.indexOf('/', index+1)) != -1 ) {
- scanCount++;
- }
-
- if( scanCount > headCount ) {
- cookiesToSend.setElementAt(headC, j);
- cookiesToSend.setElementAt(scanC, i);
- headC = scanC;
- head = scan;
- }
- }
- }
- }
-
-
- return cookiesToSend;
-
- }
-
- /*
- * Writes cookies out to PrintWriter if they are persistent
- * (i.e. have a expr date)
- * and haven't expired. Will remove cookies that have expired as well
- */
- private void saveCookiesToStream(PrintWriter pw) {
-
- Enumeration cookieLists = cookieJar.elements();
-
- while (cookieLists.hasMoreElements()) {
- Vector cookieList = (Vector)cookieLists.nextElement();
-
- Enumeration cookies = cookieList.elements();
-
- while (cookies.hasMoreElements()) {
- HttpCookie cookie = (HttpCookie)cookies.nextElement();
-
- if (cookie.getExpirationDate() != null) {
- if (cookie.isSaveable()) {
- pw.println(cookie);
- } else { // the cookie must have expired,
- //remove from Vector cookieList
- cookieList.removeElement(cookie);
- }
-
- }
- }
- }
- // Must print something to the printwriter in the case that
- // the cookieJar has been cleared - otherwise the old cookie
- // file will continue to exist.
- pw.print("");
- }
-/////////////////////////////////////////////////////////////
- /* adds cookieList to the existing cookie jar*/
- public void addToCookieJar(HttpCookie[] cookieList) {
-
- if (cookieList != null) {
- for (int i = 0; i < cookieList.length; i++) {
-
- recordCookie(cookieList[i]);
- }
- }
-
- }
-
- /*adds one cookie to the Cookie Jar */
- public void addToCookieJar(String cookieString, URL docURL) {
- recordCookie(new HttpCookie(docURL, cookieString));
- }
-
- /* loads the cookies from the given filename */
- public void loadCookieJarFromFile(String cookieFileName) {
- try {
- FileReader fr = new FileReader(cookieFileName);
-
- BufferedReader in = new BufferedReader(fr);
-
- try {
- String cookieString;
- while ((cookieString = in.readLine()) != null) {
- HttpCookie cookie = new HttpCookie(cookieString);
- // Record the cookie, without notification. We don't
- // do a notification for cookies that are read at
- // program start-up.
- recordCookie(cookie);
- }
- } finally {
- in.close();
- }
-
-
- } catch (IOException e) {
- // do nothing; it's not an error not to have persistent cookies
- }
-
- }
-
- /* saves the cookies to the given file specified by fname */
- public void saveCookieJarToFile(String cookieFileName) {
- try {
- FileWriter fw = new FileWriter(cookieFileName);
- PrintWriter pw = new PrintWriter(fw, false);
-
- try {
- saveCookiesToStream(pw);
- } finally {
- pw.close();
- }
-
- } catch (IOException e) {
- // REMIND: I18N
- System.err.println("Saving cookies failed " + e.getMessage());
- }
- }
-
- /**
- * Return an array with all of the cookies represented by this
- * jar. This is useful when the bean is shutting down, and the client
- * wants to make the cookie jar persist.
- */
- public HttpCookie[] getAllCookies() {
-
- Vector result = new Vector();
- Hashtable jar;
- jar = (Hashtable) cookieJar.clone();
-
- synchronized (jar) {
-
- for (Enumeration e = jar.elements(); e.hasMoreElements() ;) {
- Vector v = (Vector) e.nextElement();
- for (int i = 0; i < v.size(); i++) {
- HttpCookie hc = (HttpCookie) v.elementAt(i);
- result.addElement(hc);
-
- }
-
- }
- }
-
- HttpCookie[] resultA = new HttpCookie[result.size()];
- for (int i = 0; i < result.size(); i++) {
- resultA[i] = (HttpCookie) result.elementAt(i);
- }
- return resultA;
- }
-
- /* Gets all cookies that applies for the URL */
- public HttpCookie[] getCookiesForURL(URL url) {
-
- Vector cookieVector = getAllRelevantCookies(url);
-
- if (cookieVector == null) {
- return null;
- }
-
- int i = 0;
- HttpCookie[] cookieArr = new HttpCookie[cookieVector.size()];
-
- for (Enumeration e = cookieVector.elements(); e.hasMoreElements(); ) {
-
- cookieArr[i++] = (HttpCookie)e.nextElement();
-// System.out.println("cookieArr["+(i-1)+"] = " +cookieArr[i-1].toString());
- }
-
- return cookieArr;
- }
-
- /* this will set the property of enableCookies to isDisabled */
- public void setCookieDisable(boolean isDisabled) {
-
- // Pending visit back this again
- try {
- Properties properties = new Properties();
- properties.load(new FileInputStream("ServerAutoRun.properties") );
-
-
- properties.put("enableCookies", isDisabled ? "false" : "true");
- properties.store(new FileOutputStream("ServerAutoRun.properties"),"comments");
- }
- catch ( Exception e )
- {
- System.out.println("Exception : " + e );
- }
- }
-
- public void discardAllCookies() {
- cookieJar.clear();
-
- }
-
- /*
- * purges any expired cookies in the Cookie hashtable.
- */
- public void purgeExpiredCookies() {
- Enumeration cookieLists = cookieJar.elements();
-
- while (cookieLists.hasMoreElements()) {
- Vector cookieList = (Vector)cookieLists.nextElement();
-
- Enumeration cookies = cookieList.elements();
-
- while (cookies.hasMoreElements()) {
- HttpCookie cookie = (HttpCookie)cookies.nextElement();
-
- if (cookie.hasExpired()) {
- cookieList.removeElement(cookie);
- }
- }
- }
-
- }
-
-}
diff --git a/modules/tomcat-lite/test/org/apache/tomcat/test/watchdog/DynamicObject.java b/modules/tomcat-lite/test/org/apache/tomcat/test/watchdog/DynamicObject.java
deleted file mode 100644
index c01b5be..0000000
--- a/modules/tomcat-lite/test/org/apache/tomcat/test/watchdog/DynamicObject.java
+++ /dev/null
@@ -1,385 +0,0 @@
-/*
- */
-package org.apache.tomcat.test.watchdog;
-
-import java.lang.reflect.AccessibleObject;
-import java.lang.reflect.Field;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
-import java.math.BigDecimal;
-import java.math.BigInteger;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.atomic.AtomicLong;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-/**
- * Refactoring of IntrospectionUtils and modeler dynamic bean.
- *
- * Unlike IntrospectionUtils, the method informations can be cached.
- * Also I hope this class will be simpler to use.
- * There is no static cache.
- *
- * @author Costin Manolache
- */
-public class DynamicObject {
- // Based on MbeansDescriptorsIntrospectionSource
-
- private static Logger log = Logger.getLogger(DynamicObject.class.getName());
-
- private static Class<?> NO_PARAMS[] = new Class[0];
-
-
- private static String strArray[] = new String[0];
-
- private static 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, strArray.getClass(),
- BigDecimal.class, BigInteger.class, AtomicInteger.class,
- AtomicLong.class, java.io.File.class, };
-
-
- private Class realClass;
-
- // Method or Field
- private Map<String, AccessibleObject> getAttMap;
-
- public DynamicObject(Class beanClass) {
- this.realClass = beanClass;
- initCache();
- }
-
- private void initCache() {
- Method methods[] = null;
-
- getAttMap = new HashMap<String, AccessibleObject>();
-
- methods = realClass.getMethods();
- for (int j = 0; j < methods.length; ++j) {
- if (ignorable(methods[j])) {
- continue;
- }
- String name = methods[j].getName();
-
- Class<?> params[] = methods[j].getParameterTypes();
-
- if (name.startsWith("get") && params.length == 0) {
- Class<?> ret = methods[j].getReturnType();
- if (!supportedType(ret)) {
- if (log.isLoggable(Level.FINE))
- log.fine("Unsupported type " + methods[j]);
- continue;
- }
- name = unCapitalize(name.substring(3));
-
- getAttMap.put(name, methods[j]);
- } else if (name.startsWith("is") && params.length == 0) {
- Class<?> ret = methods[j].getReturnType();
- if (Boolean.TYPE != ret) {
- if (log.isLoggable(Level.FINE))
- log.fine("Unsupported type " + methods[j] + " " + ret);
- continue;
- }
- name = unCapitalize(name.substring(2));
-
- getAttMap.put(name, methods[j]);
- }
- }
- // non-private AtomicInteger and AtomicLong - stats
- Field fields[] = realClass.getFields();
- for (int j = 0; j < fields.length; ++j) {
- if (fields[j].getType() == AtomicInteger.class) {
- getAttMap.put(fields[j].getName(), fields[j]);
- }
- }
-
- }
-
- public List<String> attributeNames() {
- return new ArrayList<String>(getAttMap.keySet());
- }
-
-
- public Object invoke(Object proxy, String method) throws Exception {
- Method executeM = null;
- Class<?> c = proxy.getClass();
- executeM = c.getMethod(method, NO_PARAMS);
- if (executeM == null) {
- throw new RuntimeException("No execute in " + proxy.getClass());
- }
- return executeM.invoke(proxy, (Object[]) null);
- }
-
- // TODO
-// public Object invoke(String method, Object[] params) {
-// return null;
-// }
-
- public Object getAttribute(Object o, String att) {
- AccessibleObject m = getAttMap.get(att);
- if (m instanceof Method) {
- try {
- return ((Method) m).invoke(o);
- } catch (Throwable e) {
- log.log(Level.INFO, "Error getting attribute " + realClass + " "
- + att, e);
- return null;
- }
- } if (m instanceof Field) {
- if (((Field) m).getType() == AtomicInteger.class) {
- try {
- Object value = ((Field) m).get(o);
- return ((AtomicInteger) value).get();
- } catch (Throwable e) {
- return null;
- }
- } else {
- return null;
- }
- } else {
- return null;
- }
- }
-
- /**
- * Set an object-type attribute.
- *
- * Use setProperty to use a string value and convert it to the
- * specific (primitive) type.
- */
- public boolean setAttribute(Object proxy, String name, Object value) {
- // TODO: use the cache...
- String methodName = "set" + capitalize(name);
- Method[] methods = proxy.getClass().getMethods();
- for (Method m : methods) {
- Class<?>[] paramT = m.getParameterTypes();
- if (methodName.equals(m.getName())
- && paramT.length == 1
- && (value == null || paramT[0].isAssignableFrom(value
- .getClass()))) {
- try {
- m.invoke(proxy, value);
- return true;
- } catch (IllegalArgumentException e) {
- log.severe("Error setting: " + name + " "
- + proxy.getClass().getName() + " " + e);
- } catch (IllegalAccessException e) {
- log.severe("Error setting: " + name + " "
- + proxy.getClass().getName() + " " + e);
- } catch (InvocationTargetException e) {
- log.severe("Error setting: " + name + " "
- + proxy.getClass().getName() + " " + e);
- }
- }
- }
- return false;
- }
-
- public boolean setProperty(Object proxy, String name, String value) {
- // TODO: use the cache...
- String setter = "set" + capitalize(name);
-
- try {
- Method methods[] = proxy.getClass().getMethods();
-
- Method setPropertyMethod = null;
-
- // First, the ideal case - a setFoo( String ) method
- for (int i = 0; i < methods.length; i++) {
- if (ignorable(methods[i])) {
- continue;
- }
- Class<?> paramT[] = methods[i].getParameterTypes();
- if (setter.equals(methods[i].getName()) && paramT.length == 1) {
- if ("java.lang.String".equals(paramT[0].getName())) {
- methods[i].invoke(proxy, new Object[] { value });
- return true;
- } else {
- // match - find the type and invoke it
- Class<?> paramType = methods[i].getParameterTypes()[0];
- Object params[] = new Object[1];
- params[0] = convert(value, paramType);
- if (params[0] != null) {
- methods[i].invoke(proxy, params);
- return true;
- }
- }
- }
- // save "setProperty" for later
- if ("setProperty".equals(methods[i].getName()) &&
- paramT.length == 2 &&
- paramT[0] == String.class &&
- paramT[1] == String.class) {
- setPropertyMethod = methods[i];
- }
- }
-
- try {
- Field field = proxy.getClass().getField(name);
- if (field != null) {
- Object conv = convert(value, field.getType());
- if (conv != null) {
- field.set(proxy, conv);
- return true;
- }
- }
- } catch (NoSuchFieldException e) {
- // ignore
- }
-
- // Ok, no setXXX found, try a setProperty("name", "value")
- if (setPropertyMethod != null) {
- Object params[] = new Object[2];
- params[0] = name;
- params[1] = value;
- setPropertyMethod.invoke(proxy, params);
- return true;
- }
-
- } catch (Throwable ex2) {
- log.log(Level.WARNING, "IAE " + proxy + " " + name + " " + value,
- ex2);
- }
- return false;
- }
-
- // ----------- Helpers ------------------
-
- static Object convert(String object, Class<?> paramType) {
- Object result = null;
- if ("java.lang.String".equals(paramType.getName())) {
- result = object;
- } else if ("java.lang.Long".equals(paramType.getName())
- || "long".equals(paramType.getName())) {
- try {
- result = Long.parseLong(object);
- } catch (NumberFormatException ex) {
- }
- // Try a setFoo ( boolean )
- } else if ("java.lang.Integer".equals(paramType.getName())
- || "int".equals(paramType.getName())) {
- try {
- result = new Integer(object);
- } catch (NumberFormatException ex) {
- }
- // Try a setFoo ( boolean )
- } else if ("java.lang.Boolean".equals(paramType.getName())
- || "boolean".equals(paramType.getName())) {
- result = new Boolean(object);
- } else {
- log.info("Unknown type " + paramType.getName());
- }
- if (result == null) {
- throw new IllegalArgumentException("Can't convert argument: "
- + object + " to " + paramType );
- }
- return result;
- }
-
- /**
- * Converts the first character of the given String into lower-case.
- *
- * @param name
- * The string to convert
- * @return String
- */
- static String unCapitalize(String name) {
- if (name == null || name.length() == 0) {
- return name;
- }
- char chars[] = name.toCharArray();
- chars[0] = Character.toLowerCase(chars[0]);
- return new String(chars);
- }
-
- /**
- * 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
- */
- static boolean supportedType(Class<?> ret) {
- for (int i = 0; i < supportedTypes.length; i++) {
- if (ret == supportedTypes[i]) {
- return true;
- }
- }
- if (isBeanCompatible(ret)) {
- return true;
- }
- return false;
- }
-
- /**
- * 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.
- */
- static boolean isBeanCompatible(Class<?> javaType) {
- // Must be a non-primitive and non array
- if (javaType.isArray() || javaType.isPrimitive()) {
- return false;
- }
-
- // 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.")) {
- return false;
- }
-
- try {
- javaType.getConstructor(new Class[] {});
- } catch (java.lang.NoSuchMethodException e) {
- return false;
- }
-
- // Make sure superclass is compatible
- Class<?> superClass = javaType.getSuperclass();
- if (superClass != null && superClass != java.lang.Object.class
- && superClass != java.lang.Exception.class
- && superClass != java.lang.Throwable.class) {
- if (!isBeanCompatible(superClass)) {
- return false;
- }
- }
- return true;
- }
-
- /**
- * Reverse of Introspector.decapitalize
- */
- static String capitalize(String name) {
- if (name == null || name.length() == 0) {
- return name;
- }
- char chars[] = name.toCharArray();
- chars[0] = Character.toUpperCase(chars[0]);
- return new String(chars);
- }
-
- private boolean ignorable(Method method) {
- if (Modifier.isStatic(method.getModifiers()))
- return true;
- if (!Modifier.isPublic(method.getModifiers())) {
- return true;
- }
- if (method.getDeclaringClass() == Object.class)
- return true;
- return false;
- }
-
-
-}
diff --git a/modules/tomcat-lite/test/org/apache/tomcat/test/watchdog/HttpCookie.java b/modules/tomcat-lite/test/org/apache/tomcat/test/watchdog/HttpCookie.java
deleted file mode 100644
index 0a7e9c9..0000000
--- a/modules/tomcat-lite/test/org/apache/tomcat/test/watchdog/HttpCookie.java
+++ /dev/null
@@ -1,290 +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.test.watchdog;
-
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.net.URL;
-import java.util.Date;
-import java.util.Properties;
-import java.util.StringTokenizer;
-
-/**
- * An object which represents an HTTP cookie. Can be constructed by
- * parsing a string from the set-cookie: header.
- *
- * Syntax: Set-Cookie: NAME=VALUE; expires=DATE;
- * path=PATH; domain=DOMAIN_NAME; secure
- *
- * All but the first field are optional.
- *
- * @author Ramesh.Mandava
- */
-
-public class HttpCookie {
- private Date expirationDate = null;
- private String nameAndValue;
- private String path;
- private String domain;
- private boolean isSecure = false;
- private static boolean defaultSet = true;
- private static long defExprTime = 100;
-
- public HttpCookie(String cookieString) {
- /*
- System.out.println("Calling default expiration :");
- getDefaultExpiration();
- */
- parseCookieString(cookieString);
- }
-
- //
- // Constructor for use by the bean
- //
- public HttpCookie(Date expirationDate,
- String nameAndValue,
- String path,
- String domain,
- boolean isSecure) {
- this.expirationDate = expirationDate;
- this.nameAndValue = nameAndValue;
- this.path = path;
- this.domain = domain;
- this.isSecure = isSecure;
- }
-
- public HttpCookie(URL url, String cookieString) {
- parseCookieString(cookieString);
- applyDefaults(url);
- }
-
- /**
- * Fills in default values for domain, path, etc. from the URL
- * after creation of the cookie.
- */
- private void applyDefaults(URL url) {
- if (domain == null) {
- domain = url.getHost()+":"+((url.getPort() == -1) ? 80 : url.getPort());
- }
-
- if (path == null) {
- path = url.getFile();
-
- // larrylf: The documentation for cookies say that the path is
- // by default, the path of the document, not the filename of the
- // document. This could be read as not including that document
- // name itself, just its path (this is how NetScape intrprets it)
- // so amputate the document name!
- int last = path.lastIndexOf("/");
- if( last > -1 ) {
- path = path.substring(0, last);
- }
- }
- }
-
-
- /**
- * Parse the given string into its individual components, recording them
- * in the member variables of this object.
- */
- private void parseCookieString(String cookieString) {
- StringTokenizer tokens = new StringTokenizer(cookieString, ";");
-
- if (!tokens.hasMoreTokens()) {
- // REMIND: make this robust against parse errors
- nameAndValue="=";
- return;
- }
-
- nameAndValue = tokens.nextToken().trim();
-
- while (tokens.hasMoreTokens()) {
- String token = tokens.nextToken().trim();
-
- if (token.equalsIgnoreCase("secure")) {
- isSecure = true;
- } else {
- int equIndex = token.indexOf("=");
-
- if (equIndex < 0) {
- continue;
- // REMIND: malformed cookie
- }
-
- String attr = token.substring(0, equIndex);
- String val = token.substring(equIndex+1);
-
- if (attr.equalsIgnoreCase("path")) {
- path = val;
- } else if (attr.equalsIgnoreCase("domain")) {
- if( val.indexOf(".") == 0 ) {
- // spec seems to allow for setting the domain in
- // the form 'domain=.eng.sun.com'. We want to
- // trim off the leading '.' so we can allow for
- // both leading dot and non leading dot forms
- // without duplicate storage.
- domain = val.substring(1);
- } else {
- domain = val;
- }
- } else if (attr.equalsIgnoreCase("expires")) {
- expirationDate = parseExpireDate(val);
- } else {
- // unknown attribute -- do nothing
- }
- }
- }
-
- // commented the following out, b/c ok to have no expirationDate
- // that means that the cookie should last only for that particular
- // session.
- // if (expirationDate == null) {
- // expirationDate = getDefaultExpiration();
- // }
- }
-
- /* Returns the default expiration, which is the current time + default
- expiration as specified in the properties file.
- This uses reflection to get at the properties file, since Globals is
- not in the utils/ directory
- */
- private Date getDefaultExpiration() {
- if (defaultSet == false) {
- Properties props = new Properties();
-
- try {
- FileInputStream fin = new FileInputStream("ServerAutoRun.properties");
- props.load( fin );
-
- System.out.println("Got properties from ServerAutoRun.properties");
- props.list(System.out);
-
- } catch (IOException ex) {
- System.out.println("HttpCookie getDefaultExpiration : ServerAutoRun.properties not found!" + ex);
- }
-
- // defExprTime = props.getProperty("cookies.default.expiration");
- defExprTime = Long.parseLong( props.getProperty("cookies.default.expiration") );
-
- }
- defaultSet = true;
-
- return (new Date(System.currentTimeMillis() + defExprTime));
-
- }
-
- //======================================================================
- //
- // Accessor functions
- //
-
-
-
- public String getNameValue() {
- return nameAndValue;
- }
-
- /**
- * Returns just the name part of the cookie
- */
- public String getName() {
-
- // it probably can't have null value, but doesn't hurt much
- // to check.
- if (nameAndValue == null) {
- return "=";
- }
- int index = nameAndValue.indexOf("=");
- return (index < 0) ? "=" : nameAndValue.substring(0, index);
- }
-
-
- /**
- * Returns the domain of the cookie as it was presented
- */
- public String getDomain() {
- // REMIND: add port here if appropriate
- return domain;
- }
-
- public String getPath() {
- return path;
- }
-
- public Date getExpirationDate() {
- return expirationDate;
- }
-
- public boolean hasExpired() {
- if(expirationDate == null) {
- return false;
- }
- return (expirationDate.getTime() <= System.currentTimeMillis());
- }
-
- /**
- * Returns true if the cookie has an expiration date (meaning it's
- * persistent), and if the date nas not expired;
- */
- public boolean isSaveable() {
- return (expirationDate != null)
- && (expirationDate.getTime() > System.currentTimeMillis());
- }
-
- public boolean isSaveableInMemory() {
- return ((expirationDate == null) ||
- (expirationDate != null && expirationDate.getTime() > System.currentTimeMillis()));
- }
-
- public boolean isSecure() {
- return isSecure;
- }
-
- private Date parseExpireDate(String dateString) {
- // format is wdy, DD-Mon-yyyy HH:mm:ss GMT
- RfcDateParser parser = new RfcDateParser(dateString);
- Date theDate = parser.getDate();
- if (theDate == null) {
- // Expire in some intelligent default time
- theDate = getDefaultExpiration();
- }
- return theDate;
- }
-
- public String toString() {
-
- String result = (nameAndValue == null) ? "=" : nameAndValue;
- if (expirationDate != null) {
- result += "; expires=" + expirationDate;
- }
-
- if (path != null) {
- result += "; path=" + path;
- }
-
- if (domain != null) {
- result += "; domain=" + domain;
- }
-
- if (isSecure) {
- result += "; secure";
- }
-
- return result;
- }
-}
diff --git a/modules/tomcat-lite/test/org/apache/tomcat/test/watchdog/RfcDateParser.java b/modules/tomcat-lite/test/org/apache/tomcat/test/watchdog/RfcDateParser.java
deleted file mode 100644
index 9a7f604..0000000
--- a/modules/tomcat-lite/test/org/apache/tomcat/test/watchdog/RfcDateParser.java
+++ /dev/null
@@ -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.tomcat.test.watchdog;
-
-import java.util.Date;
-import java.util.Locale;
-import java.util.TimeZone;
-
-/**
- * A parser for date strings commonly found in http and email headers that
- * follow various RFC conventions. Given a date-string, the parser will
- * attempt to parse it by trying matches with a set of patterns, returning
- * null on failure, a Date object on success.
- *
- * @author Ramesh.Mandava
- */
-public class RfcDateParser {
-
- private boolean isGMT = false;
-
- static final String[] standardFormats = {
- "EEEE', 'dd-MMM-yy HH:mm:ss z", // RFC 850 (obsoleted by 1036)
- "EEEE', 'dd-MMM-yy HH:mm:ss", // ditto but no tz. Happens too often
- "EEE', 'dd-MMM-yyyy HH:mm:ss z", // RFC 822/1123
- "EEE', 'dd MMM yyyy HH:mm:ss z", // REMIND what rfc? Apache/1.1
- "EEEE', 'dd MMM yyyy HH:mm:ss z", // REMIND what rfc? Apache/1.1
- "EEE', 'dd MMM yyyy hh:mm:ss z", // REMIND what rfc? Apache/1.1
- "EEEE', 'dd MMM yyyy hh:mm:ss z", // REMIND what rfc? Apache/1.1
- "EEE MMM dd HH:mm:ss z yyyy", // Date's string output format
- "EEE MMM dd HH:mm:ss yyyy", // ANSI C asctime format()
- "EEE', 'dd-MMM-yy HH:mm:ss", // No time zone 2 digit year RFC 1123
- "EEE', 'dd-MMM-yyyy HH:mm:ss" // No time zone RFC 822/1123
- };
-
- /* because there are problems with JDK1.1.6/SimpleDateFormat with
- * recognizing GMT, we have to create this workaround with the following
- * hardcoded strings */
- static final String[] gmtStandardFormats = {
- "EEEE',' dd-MMM-yy HH:mm:ss 'GMT'", // RFC 850 (obsoleted by 1036)
- "EEE',' dd-MMM-yyyy HH:mm:ss 'GMT'", // RFC 822/1123
- "EEE',' dd MMM yyyy HH:mm:ss 'GMT'", // REMIND what rfc? Apache/1.1
- "EEEE',' dd MMM yyyy HH:mm:ss 'GMT'", // REMIND what rfc? Apache/1.1
- "EEE',' dd MMM yyyy hh:mm:ss 'GMT'", // REMIND what rfc? Apache/1.1
- "EEEE',' dd MMM yyyy hh:mm:ss 'GMT'", // REMIND what rfc? Apache/1.1
- "EEE MMM dd HH:mm:ss 'GMT' yyyy" // Date's string output format
- };
-
- String dateString;
-
- public RfcDateParser(String dateString) {
- this.dateString = dateString.trim();
- if (this.dateString.indexOf("GMT") != -1) {
- isGMT = true;
- }
- }
-
- public Date getDate() {
-
- int arrayLen = isGMT ? gmtStandardFormats.length : standardFormats.length;
- for (int i = 0; i < arrayLen; i++) {
- Date d = null;
-
- if (isGMT) {
- d = tryParsing(gmtStandardFormats[i]);
- } else {
- d = tryParsing(standardFormats[i]);
- }
- if (d != null) {
- return d;
- }
-
- }
-
- return null;
- }
-
- private Date tryParsing(String format) {
-
- java.text.SimpleDateFormat df = new java.text.SimpleDateFormat(format, Locale.US);
- if (isGMT) {
- df.setTimeZone(TimeZone.getTimeZone("GMT"));
- }
- try {
- return df.parse(dateString);
- } catch (Exception e) {
- return null;
- }
- }
-} /* class RfcDateParser */
diff --git a/modules/tomcat-lite/test/org/apache/tomcat/test/watchdog/WatchdogClient.java b/modules/tomcat-lite/test/org/apache/tomcat/test/watchdog/WatchdogClient.java
deleted file mode 100644
index 05b481c..0000000
--- a/modules/tomcat-lite/test/org/apache/tomcat/test/watchdog/WatchdogClient.java
+++ /dev/null
@@ -1,213 +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.test.watchdog;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.StringReader;
-import java.util.Properties;
-
-import javax.xml.parsers.DocumentBuilder;
-import javax.xml.parsers.DocumentBuilderFactory;
-import javax.xml.parsers.ParserConfigurationException;
-
-import junit.framework.Test;
-import junit.framework.TestResult;
-import junit.framework.TestSuite;
-
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-import org.w3c.dom.NodeList;
-import org.xml.sax.EntityResolver;
-import org.xml.sax.InputSource;
-import org.xml.sax.SAXException;
-
-public class WatchdogClient {
-
- protected String goldenDir;
- protected String testMatch;
- protected String file;
- protected String[] exclude = null;
- protected String[] slow =
- {
- "SingleModelTest" // slow
- };
-
- protected String targetMatch;
-
- protected int port;
-
- Properties props = new Properties();
-
- protected void beforeSuite() {
- }
-
- protected void afterSuite(TestResult res) {
- }
-
- public Test getSuite() {
- return getSuite(port);
- }
-
- public static class NullResolver implements EntityResolver {
- public InputSource resolveEntity (String publicId,
- String systemId)
- throws SAXException, IOException
- {
- return new InputSource(new StringReader(""));
- }
- }
-
- /** Read XML as DOM.
- */
- public static Document readXml(InputStream is)
- throws SAXException, IOException, ParserConfigurationException
- {
- DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
- dbf.setValidating(false);
- dbf.setIgnoringComments(false);
- dbf.setIgnoringElementContentWhitespace(true);
- DocumentBuilder db = null;
- db = dbf.newDocumentBuilder();
- db.setEntityResolver( new NullResolver() );
- Document doc = db.parse(is);
- return doc;
- }
-
- /**
- * Return a test suite for running a watchdog-like
- * test file.
- *
- * @param base base dir for the watchdog dir
- * @param testMatch Prefix of tests to be run
- * @return
- */
- public Test getSuite(int port) {
- TestSuite tests = new WatchdogTests();
- tests.setName(this.getClass().getSimpleName());
-
- props.setProperty("port", Integer.toString(port));
- props.setProperty("host", "localhost");
- props.setProperty("wgdir",
- goldenDir);
-
-
- try {
- Document doc = readXml(new FileInputStream(file));
- Element docE = doc.getDocumentElement();
- NodeList targetsL = docE.getElementsByTagName("target");
- for (int i = 0; i < targetsL.getLength(); i++) {
- Element target = (Element) targetsL.item(i);
- String targetName = target.getAttribute("name");
- if (targetMatch != null && !targetName.equals(targetMatch)) {
- continue;
- }
-
- // Tests are duplicated
- //TestSuite targetSuite = new TestSuite(targetName);
-
- NodeList watchDogL = target.getElementsByTagName("watchdog");
- for (int j = 0; j < watchDogL.getLength(); j++) {
- Element watchE = (Element) watchDogL.item(j);
- String testName = watchE.getAttribute("testName");
- if (single != null && !testName.equals(single)) {
- continue;
- }
- if (testMatch != null) {
- if (!testName.startsWith(testMatch)) {
- continue;
- }
- }
- if (exclude != null) {
- boolean found = false;
- for (String e: exclude) {
- if (e.equals(testName)) {
- found = true;
- break;
- }
- }
- if (found) {
- continue;
- }
- }
- testName = testName + "(" + this.getClass().getName() + ")";
- WatchdogTestCase test = new WatchdogTestCase(watchE, props, testName);
- tests.addTest(test);
- if (single != null) {
- singleTest = test;
- break;
- }
- }
- }
-
- } catch (IOException e) {
- e.printStackTrace();
- } catch (SAXException e) {
- e.printStackTrace();
- } catch (ParserConfigurationException e) {
- e.printStackTrace();
- }
- return tests;
- }
-
- // --------- Inner classes -------------
-
- protected String getWatchdogdir() {
- String path = System.getProperty("watchdog.home");
- if (path != null) {
- return path;
- }
- path = "..";
- for (int i = 0; i < 10; i++) {
- File f = new File(path + "/watchdog");
- if (f.exists()) {
- return f.getAbsolutePath();
- }
- path = path + "/..";
- }
- return null;
- }
-
- public class WatchdogTests extends TestSuite {
- public void run(TestResult res) {
- beforeSuite();
- super.run(res);
- afterSuite(res);
- }
- }
-
- // Support for running a single test in the suite
-
- protected String single;
- WatchdogTestCase singleTest;
-
- public int countTestCases() {
- return 1;
- }
-
- public void run(TestResult result) {
- getSuite();
- if (singleTest != null) {
- beforeSuite();
- singleTest.run(result);
- afterSuite(result);
- }
- }
-
-}
diff --git a/modules/tomcat-lite/test/org/apache/tomcat/test/watchdog/WatchdogHttpClient.java b/modules/tomcat-lite/test/org/apache/tomcat/test/watchdog/WatchdogHttpClient.java
deleted file mode 100644
index 2464f33..0000000
--- a/modules/tomcat-lite/test/org/apache/tomcat/test/watchdog/WatchdogHttpClient.java
+++ /dev/null
@@ -1,411 +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.test.watchdog;
-
-import java.io.BufferedInputStream;
-import java.io.BufferedOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.net.Socket;
-import java.net.SocketException;
-import java.net.URL;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Vector;
-
-
-public class WatchdogHttpClient {
- private static final String CRLF = "\r\n";
- private static final int LINE_FEED = 10;
-
- static int debug = 0;
-
- public static void dispatch(WatchdogTestImpl client) throws Exception {
- HashMap requestHeaders = client.requestHeaders;
- String host = client.host;
- int port = client.port;
- String content = client.content;
- String request = client.request;
-
- // XXX headers are ignored
- Socket socket;
- try {
- socket = new Socket( host, port );
- } catch (IOException ex) {
- System.out.println( " Socket Exception: " + ex );
- return;
- }
- socket.setSoTimeout(10000);
-
- //socket obtained, rebuild the request.
- rebuildRequest(client, client.request, socket);
-
- InputStream in = new CRBufferedInputStream( socket.getInputStream() );
-
- // Write the request
- socket.setSoLinger( true, 1000 );
-
- OutputStream out = new BufferedOutputStream(
- socket.getOutputStream() );
- StringBuffer reqbuf = new StringBuffer( 128 );
-
- // set the Host header
- client.setHeaderDetails( "Host:" + host + ":" + port, requestHeaders, true );
-
- // set the Content-Length header
- if ( content != null ) {
- client.setHeaderDetails( "Content-Length:" + content.length(),
- requestHeaders, true );
- }
-
- // set the Cookie header
- if ( client.testSession != null ) {
- client.cookieController = ( CookieController ) client.sessionHash.get( client.testSession );
-
- if ( client.cookieController != null ) {
-
- String releventCookieString = client.cookieController.applyRelevantCookies( client.requestURL );
-
- if ( ( releventCookieString != null ) && ( !releventCookieString.trim().equals( "" ) ) ) {
- client.setHeaderDetails( "Cookie:" + releventCookieString, requestHeaders, true );
- }
- }
- }
-
- if ( debug > 0 ) {
- System.out.println( " REQUEST: " + request );
- }
- reqbuf.append( client.request ).append( CRLF );
-
- // append all request headers
- if ( !requestHeaders.isEmpty() ) {
- Iterator iter = requestHeaders.keySet().iterator();
-
- while ( iter.hasNext() ) {
- StringBuffer tmpBuf = new StringBuffer(32);
- String headerKey = ( String ) iter.next();
- ArrayList values = (ArrayList) requestHeaders.get( headerKey );
- String[] value = (String[]) values.toArray( new String[ values.size() ] );
- tmpBuf.append( headerKey ).append(": ");
- for ( int i = 0; i < value.length; i++ ) {
- if ((i + 1) == value.length) {
- tmpBuf.append( value[ i ] );
- } else {
- tmpBuf.append( value[ i ] ).append(", ");
- }
- }
- if ( debug > 0 ) {
- System.out.println( " REQUEST HEADER: " + tmpBuf.toString());
- }
- tmpBuf.append( CRLF );
- reqbuf.append(tmpBuf.toString());
- }
- }
-
- /*
-
- if ( ( testSession != null ) && ( sessionHash.get( testSession ) != null ) ) {
- System.out.println("Sending Session Id : " + (String)sessionHash.get( testSession ) );
- pw.println("JSESSIONID:" + (String)sessionHash.get( testSession) );
- }
-
- */
-
- if ( request.indexOf( "HTTP/1." ) > -1 ) {
- reqbuf.append( "" ).append( CRLF );
- }
-
- // append request content
- if ( content != null ) {
- reqbuf.append( content );
- // XXX no CRLF at the end -see HTTP specs!
- }
-
- byte[] reqbytes = reqbuf.toString().getBytes();
-
- try {
- // write the request
- out.write( reqbytes, 0, reqbytes.length );
- out.flush();
- } catch ( Exception ex1 ) {
- System.out.println( " Error writing request " + ex1 );
- if ( debug > 0 ) {
- System.out.println( "Message: " + ex1.getMessage() );
- ex1.printStackTrace();
- }
- }
-
- // read the response
- try {
-
- client.responseLine = read( in );
-
- if ( debug > 0 ) {
- System.out.println( " RESPONSE STATUS-LINE: " + client.responseLine );
- }
-
- client.headers = parseHeaders( client, in );
-
- byte[] result = readBody( in );
-
- if ( result != null ) {
- client.responseBody = result;
- if ( debug > 0 ) {
- System.out.println( " RESPONSE BODY:\n" + new String( client.responseBody ) );
- }
- }
-
- } catch ( SocketException ex ) {
- System.out.println( " Socket Exception: " + ex );
- } finally {
- if ( debug > 0 ) {
- System.out.println( " closing socket" );
- }
- socket.close();
- socket = null;
- }
-
- }
-
- /**
- * <code>readBody</code> reads the body of the response
- * from the InputStream.
- *
- * @param input an <code>InputStream</code>
- * @return a <code>byte[]</code> representation of the response
- */
- private static byte[] readBody( InputStream input ) {
- StringBuffer sb = new StringBuffer( 255 );
- while ( true ) {
- try {
- int ch = input.read();
-
- if ( ch < 0 ) {
- if ( sb.length() == 0 ) {
- return ( null );
- } else {
- break;
- }
- }
- sb.append( ( char ) ch );
-
- } catch ( IOException ex ) {
- return null;
- }
- }
- return sb.toString().getBytes();
- }
-
-
- /**
- * Read a line from the specified servlet input stream, and strip off
- * the trailing carriage return and newline (if any). Return the remaining
- * characters that were read as a string.7
- *
- * @returns The line that was read, or <code>null</code> if end of file
- * was encountered
- *
- * @exception IOException if an input/output error occurred
- */
- private static String read( InputStream input ) throws IOException {
- // Read the next line from the input stream
- StringBuffer sb = new StringBuffer();
-
- while ( true ) {
- try {
- int ch = input.read();
- // System.out.println("XXX " + (char)ch );
- if ( ch < 0 ) {
- if ( sb.length() == 0 ) {
- if ( debug > 0 )
- System.out.println( " Error reading line " + ch + " " + sb.toString() );
- return "";
- } else {
- break;
- }
- } else if ( ch == LINE_FEED ) {
- break;
- }
-
- sb.append( ( char ) ch );
- } catch ( IOException ex ) {
- System.out.println( " Error reading : " + ex );
- debug = 1;
-
- if ( debug > 0 ) {
- System.out.println( "Partial read: " + sb.toString() );
- ex.printStackTrace();
- }
- input.close();
- break;
- }
- }
- return sb.toString();
- }
-
-
- // ==================== Code from JSERV !!! ====================
- /**
- * Parse the incoming HTTP request headers, and set the corresponding
- * request properties.
- *
- *
- * @exception IOException if an input/output error occurs
- */
- private static HashMap parseHeaders( WatchdogTestImpl client, InputStream is ) throws IOException {
- HashMap headers = new HashMap();
- client.cookieVector = new Vector();
-
- while ( true ) {
- // Read the next header line
- String line = read( is );
-
- if ( ( line == null ) || ( line.length() < 1 ) ) {
- break;
- }
-
- client.parseHeader( line, headers, false );
-
- if ( debug > 0 ) {
- System.out.println( " RESPONSE HEADER: " + line );
- }
-
- }
-
- if ( client.testSession != null ) {
- client.cookieController = ( CookieController ) client.sessionHash.get( client.testSession );
-
- if ( client.cookieController != null ) {
- client.cookieController.recordAnyCookies( client.cookieVector, client.requestURL );
- }
- }
-
- return headers;
- }
-
-
- /**
- * Private utility method to 'massage' a request string that
- * may or may not have replacement markers for the request parameters.
- *
- * @param req the request to manipulate
- * @param socket local socket. Used to rebuild specified query strings.
- *
- * @exception Exception if an error occurs
- */
- private static void rebuildRequest(WatchdogTestImpl client, String req, Socket socket) throws Exception {
- client.request = client.replaceMarkers(req, socket );
- String addressString = client.request.substring( client.request.indexOf( "/" ), client.request.indexOf( "HTTP" ) ).trim();
-
- if ( addressString.indexOf( "?" ) > -1 ) {
- addressString = addressString.substring( 0, addressString.indexOf( "?" ) ) ;
- }
-
- client.requestURL = new URL( "http", client.host, client.port, addressString );
- }
-
-
- /**
- * <code>CRBufferedInputStream</code> is a modified version of
- * the java.io.BufferedInputStream class. The fill code is
- * the same, but the read is modified in that if a carriage return
- * is found in the response stream from the target server,
- * it will skip that byte and return the next in the stream.
- */
- private static class CRBufferedInputStream extends BufferedInputStream {
- private static final int CARRIAGE_RETURN = 13;
-
- private static final int DEFAULT_BUFFER = 2048;
-
- /**
- * Creates a new <code>CRBufferedInputStream</code> instance.
- *
- * @param in an <code>InputStream</code> value
- */
- public CRBufferedInputStream( InputStream in ) {
- super( in, DEFAULT_BUFFER );
- }
-
- /**
- * <code>read</code> reads a single byte value per call.
- * If, the byte read, is a carriage return, the next byte
- * in the stream in returned instead.
- *
- * @return an <code>int</code> value
- * @exception IOException if an error occurs
- */
- public int read() throws IOException {
- if ( in == null ) {
- throw new IOException ( "Stream closed" );
- }
- if ( pos >= count ) {
- fill();
- if ( pos >= count ) {
- return -1;
- }
- }
- int val = buf[pos++] & 0xff;
- if ( val == CARRIAGE_RETURN ) {
- if (pos >= count) {
- fill();
- if (pos >= count) {
- return -1;
- }
- }
- return buf[pos++] & 0xff;
- }
- return val;
- }
-
- /**
- * <code>fill</code> is used to fill the internal
- * buffer used by this BufferedInputStream class.
- *
- * @exception IOException if an error occurs
- */
- private void fill() throws IOException {
- if (markpos < 0) {
- pos = 0; /* no mark: throw away the buffer */
- } else if (pos >= buf.length) {/* no room left in buffer */
- if (markpos > 0) { /* can throw away early part of the buffer */
- int sz = pos - markpos;
- System.arraycopy(buf, markpos, buf, 0, sz);
- pos = sz;
- markpos = 0;
- } else if (buf.length >= marklimit) {
- markpos = -1; /* buffer got too big, invalidate mark */
- pos = 0; /* drop buffer contents */
- } else { /* grow buffer */
- int nsz = pos * 2;
- if (nsz > marklimit)
- nsz = marklimit;
- byte nbuf[] = new byte[nsz];
- System.arraycopy(buf, 0, nbuf, 0, pos);
- buf = nbuf;
- }
- }
- count = pos;
- int n = in.read(buf, pos, buf.length - pos);
- if (n > 0) {
- count = n + pos;
- }
- }
- }
-
-}
diff --git a/modules/tomcat-lite/test/org/apache/tomcat/test/watchdog/WatchdogTestCase.java b/modules/tomcat-lite/test/org/apache/tomcat/test/watchdog/WatchdogTestCase.java
deleted file mode 100644
index 8da9b35..0000000
--- a/modules/tomcat-lite/test/org/apache/tomcat/test/watchdog/WatchdogTestCase.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- */
-package org.apache.tomcat.test.watchdog;
-
-import java.util.Properties;
-
-import junit.framework.AssertionFailedError;
-import junit.framework.Test;
-import junit.framework.TestResult;
-
-import org.w3c.dom.Element;
-import org.w3c.dom.NamedNodeMap;
-import org.w3c.dom.Node;
-
-public class WatchdogTestCase implements Test {
- String testName;
-
- Element watchE;
-
- private Properties props;
-
- private WatchdogClient wc;
-
- public WatchdogTestCase() {
-
- }
-
- public WatchdogTestCase(Element watchE, Properties props, String testName) {
- this.testName = testName;
- this.watchE = watchE;
- this.props = props;
- }
-
- public int countTestCases() {
- return 1;
- }
-
- public String getName() {
- return testName == null ? "WatchdogTest" : testName;
- }
-
- public String toString() {
- return getName();
- }
-
- public void testDummy() {
- }
-
- public void run(TestResult res) {
- if (watchE == null) {
- res.endTest(this);
- return;
- }
- WatchdogTestImpl test = new WatchdogTestImpl();
- NamedNodeMap attrs = watchE.getAttributes();
-
- for (int i = 0; i < attrs.getLength(); i++) {
- Node n = attrs.item(i);
- String name = n.getNodeName();
- String value = n.getNodeValue();
- value = AntProperties.replaceProperties(value, props, null);
- try {
- new DynamicObject(test.getClass()).setProperty(test,
- name, value);
- } catch (Exception e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
-
- try {
- res.startTest(this);
- new DynamicObject(test.getClass()).invoke(test, "execute");
- } catch (Throwable e) {
- res.addError(this, e);
- // res.stop();
- }
-
- if (test.passCount == 1) {
- res.endTest(this);
- return;
- } else {
- if (test.lastError == null) {
- res.addFailure(this, new AssertionFailedError(test.request
- + " " + test.description + "\n" + test.resultOut));
- } else {
- res.addError(this, test.lastError);
- }
- }
- res.endTest(this);
- }
-
-}
diff --git a/modules/tomcat-lite/test/org/apache/tomcat/test/watchdog/WatchdogTestImpl.java b/modules/tomcat-lite/test/org/apache/tomcat/test/watchdog/WatchdogTestImpl.java
deleted file mode 100644
index cda165b..0000000
--- a/modules/tomcat-lite/test/org/apache/tomcat/test/watchdog/WatchdogTestImpl.java
+++ /dev/null
@@ -1,1172 +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.
- */
-
-/**
- * @Author Costin, Ramesh.Mandava
- */
-
-package org.apache.tomcat.test.watchdog;
-
-import java.io.BufferedInputStream;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.InetAddress;
-import java.net.Socket;
-import java.net.URL;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Hashtable;
-import java.util.Iterator;
-import java.util.StringTokenizer;
-import java.util.Vector;
-
-import org.apache.tomcat.lite.io.Hex;
-
-// derived from Jsp
-
-public class WatchdogTestImpl {
-
- int failureCount = 0;
-
- int passCount = 0;
-
- Throwable lastError;
-
- boolean hasFailed = false;
-
- String prefix = "http";
-
- String host = "localhost";
-
- String localHost = null;
-
- String localIP = null;
-
- int port = 8080;
-
- int debug = 0;
-
- String description = "No description";
-
- String request;
-
- HashMap requestHeaders = new HashMap();
-
- String content;
-
- // true if task is nested
- private boolean nested = false;
-
- // Expected response
- boolean magnitude = true;
-
- boolean exactMatch = false;
-
- // expect a response body
- boolean expectResponseBody = true;
-
- // Match the body against a golden file
- String goldenFile;
-
- // Match the body against a string
- String responseMatch;
-
- // the response should include the following headers
- HashMap expectHeaders = new HashMap();
-
- // Headers that should not be found in response
- HashMap unexpectedHeaders = new HashMap();
-
- // Match request line
- String returnCode = "";
-
- String returnCodeMsg = "";
-
- // Actual response
- String responseLine;
-
- byte[] responseBody;
-
- HashMap headers;
-
- // For Report generation
- StringBuffer resultOut = new StringBuffer();
-
- boolean firstTask = false;
-
- boolean lastTask = false;
-
- String expectedString;
-
- String actualString;
-
- String testName;
-
- String assertion;
-
- String testStrategy;
-
- // For Session Tracking
- static Hashtable sessionHash;
-
- static Hashtable cookieHash;
-
- String testSession;
-
- Vector cookieVector;
-
- URL requestURL;
-
- CookieController cookieController;
-
- /**
- * Creates a new <code>GTest</code> instance.
- *
- */
- public WatchdogTestImpl() {
- }
-
- /**
- * <code>setTestSession</code> adds a CookieController for the value of
- * sessionName
- *
- * @param sessionName
- * a <code>String</code> value
- */
- public void setTestSession(String sessionName) {
- testSession = sessionName;
-
- if (sessionHash == null) {
- sessionHash = new Hashtable();
- } else if (sessionHash.get(sessionName) == null) {
- sessionHash.put(sessionName, new CookieController());
- }
- }
-
- /**
- * <code>setTestName</code> sets the current test name.
- *
- * @param tn
- * current testname.
- */
- public void setTestName(String tn) {
- testName = tn;
- }
-
- /**
- * <code>setAssertion</code> sets the assertion text for the current test.
- *
- * @param assertion
- * assertion text
- */
- public void setAssertion(String assertion) {
- this.assertion = assertion;
- }
-
- /**
- * <code>setTestStrategy</code> sets the test strategy for the current test.
- *
- * @param strategy
- * test strategy text
- */
- public void setTestStrategy(String strategy) {
- testStrategy = strategy;
- }
-
- /**
- * <code>getTestName</code> returns the current test name.
- *
- * @return a <code>String</code> value
- */
- public String getTestName() {
- return testName;
- }
-
- /**
- * <code>getAssertion</code> returns the current assertion text.
- *
- * @return a <code>String</code> value
- */
- public String getAssertion() {
- return assertion;
- }
-
- /**
- * <code>getTestStrategy</code> returns the current test strategy test.
- *
- * @return a <code>String</code> value
- */
- public String getTestStrategy() {
- return testStrategy;
- }
-
- /**
- * <code>setFirstTask</code> denotes that current task being executed is the
- * first task within the list.
- *
- * @param a
- * <code>boolean</code> value
- */
- public void setFirstTask(boolean val) {
- firstTask = val;
- }
-
- /**
- * <code>setLastTask</code> denotes that the current task being executed is
- * the last task within the list.
- *
- * @param a
- * <code>boolean</code> value
- */
- public void setLastTask(boolean val) {
- lastTask = val;
- }
-
- /**
- * <code>setPrefix</code> sets the protocol prefix. Defaults to "http"
- *
- * @param prefix
- * Either http or https
- */
- public void setPrefix(String prefix) {
- this.prefix = prefix;
- }
-
- /**
- * <code>setHost</code> sets hostname where the target server is running.
- * Defaults to "localhost"
- *
- * @param h
- * a <code>String</code> value
- */
- public void setHost(String h) {
- this.host = h;
- }
-
- /**
- * <code>setPort</code> sets the port that the target server is listening
- * on. Defaults to "8080"
- *
- * @param portS
- * a <code>String</code> value
- */
- public void setPort(String portS) {
- this.port = Integer.valueOf(portS).intValue();
- }
-
- /**
- * <code>setExactMatch</code> determines if a byte-by-byte comparsion is
- * made of the server's response and the test's goldenFile, or if a token
- * comparison is made. By default, only a token comparison is made
- * ("false").
- *
- * @param exact
- * a <code>String</code> value
- */
- public void setExactMatch(String exact) {
- exactMatch = Boolean.valueOf(exact).booleanValue();
- }
-
- /**
- * <code>setContent</code> String value upon which the request header
- * Content-Length is based upon.
- *
- * @param s
- * a <code>String</code> value
- */
- public void setContent(String s) {
- this.content = s;
- }
-
- /**
- * <code>setDebug</code> enables debug output. By default, this is disabled
- * ( value of "0" ).
- *
- * @param debugS
- * a <code>String</code> value
- */
- public void setDebug(String debugS) {
- debug = Integer.valueOf(debugS).intValue();
- }
-
- /**
- * <code>setMagnitude</code> Expected return value of the test execution.
- * Defaults to "true"
- *
- * @param magnitudeS
- * a <code>String</code> value
- */
- public void setMagnitude(String magnitudeS) {
- magnitude = Boolean.valueOf(magnitudeS).booleanValue();
- }
-
- /**
- * <code>setGoldenFile</code> Sets the goldenfile that will be used to
- * validate the server's response.
- *
- * @param s
- * fully qualified path and filename
- */
- public void setGoldenFile(String s) {
- this.goldenFile = s;
- }
-
- /**
- * <code>setExpectResponseBody</code> sets a flag to indicate if a response
- * body is expected from the server or not
- *
- * @param b
- * a <code>boolean</code> value
- */
- public void setExpectResponseBody(boolean b) {
- this.expectResponseBody = b;
- }
-
- /**
- * <code>setExpectHeaders</code> Configures GTest to look for the header
- * passed in the server's response.
- *
- * @param s
- * a <code>String</code> value in the format of
- * <header-field>:<header-value>
- */
- public void setExpectHeaders(String s) {
- this.expectHeaders = new HashMap();
- StringTokenizer tok = new StringTokenizer(s, "|");
- while (tok.hasMoreElements()) {
- String header = (String) tok.nextElement();
- setHeaderDetails(header, expectHeaders, false);
- }
- }
-
- /**
- * <code>setUnexpectedHeaders</code> Configures GTest to look for the header
- * passed to validate that it doesn't exist in the server's response.
- *
- * @param s
- * a <code>String</code> value in the format of
- * <header-field>:<header-value>
- */
- public void setUnexpectedHeaders(String s) {
- this.unexpectedHeaders = new HashMap();
- setHeaderDetails(s, unexpectedHeaders, false);
- }
-
- public void setNested(String s) {
- nested = Boolean.valueOf(s).booleanValue();
- }
-
- /**
- * <code>setResponseMatch</code> Match the passed value in the server's
- * response.
- *
- * @param s
- * a <code>String</code> value
- */
- public void setResponseMatch(String s) {
- this.responseMatch = s;
- }
-
- /**
- * <code>setRequest</code> Sets the HTTP/HTTPS request to be sent to the
- * target server Ex. GET /servlet_path/val HTTP/1.0
- *
- * @param s
- * a <code>String</code> value in the form of METHOD PATH
- * HTTP_VERSION
- */
- public void setRequest(String s) {
- this.request = s;
- }
-
- /**
- * <code>setReturnCode</code> Sets the expected return code from the
- * server's response.
- *
- * @param code
- * a valid HTTP response status code
- */
- public void setReturnCode(String code) {
- this.returnCode = code;
- }
-
- /**
- * Describe <code>setReturnCodeMsg</code> Sets the expected return message
- * to be found in the server's response.
- *
- * @param code
- * a valid HTTP resonse status code
- * @param message
- * a <code>String</code> value
- */
- public void setReturnCodeMsg(String message) {
- this.returnCodeMsg = message;
- }
-
- /**
- * <code>setRequestHeaders</code> Configures the request headers GTest
- * should send to the target server.
- *
- * @param s
- * a <code>String</code> value in for format of
- * <field-name>:<field-value>
- */
- public void setRequestHeaders(String s) {
- requestHeaders = new HashMap();
- StringTokenizer tok = new StringTokenizer(s, "|");
- while (tok.hasMoreElements()) {
- String header = (String) tok.nextElement();
- setHeaderDetails(header, requestHeaders, true);
- }
- }
-
- // Inner tests are not used currently, can be reworked
-
- // /**
- // * Add a Task to this container
- // *
- // * @param Task to add
- // */
- // public void addTask(Task task) {
- // children.add(task);
- // }
-
- /**
- * <code>execute</code> Executes the test.
- *
- * @exception BuildException
- * if an error occurs
- */
- public void execute() {
-
- try {
-
- if (resultOut != null && !nested) {
- resultOut.append("\ntestName: " + testName);
- resultOut.append("\nreq: " + request);
- resultOut.append("\nassertion: " + assertion);
- resultOut.append("\ntestStrategy: " + testStrategy);
- }
-
- WatchdogHttpClient.dispatch(this);
-
- hasFailed = !checkResponse(magnitude);
-
- // if ( !children.isEmpty() ) {
- // Iterator iter = children.iterator();
- // while (iter.hasNext()) {
- // Task task = (Task) iter.next();
- // task.perform();
- // }
- // }
-
- if (!hasFailed && !nested) {
- passCount++;
- if (resultOut != null) {
- resultOut.append("<result>PASS</result>\n");
- }
-// System.out.println(" PASSED " + testName + " ("
-// + request + ")");
- } else if (hasFailed && !nested) {
- failureCount++;
- if (resultOut != null) {
- resultOut.append("<result>FAIL</result>\n");
- }
- System.out.println(" FAILED " + testName + "\n ("
- + request + ")\n" + resultOut.toString());
- }
-
- } catch (Exception ex) {
- failureCount++;
- System.out.println(" FAIL " + description + " (" + request + ")");
- lastError = ex;
- ex.printStackTrace();
- } finally {
- if (!nested) {
- hasFailed = false;
- }
- }
- }
-
- /**
- * <code>checkResponse</code> Executes various response checking mechanisms
- * against the server's response. Checks include:
- * <ul>
- * <li>expected headers
- * <li>unexpected headers
- * <li>return codes and messages in the Status-Line
- * <li>response body comparison againt a goldenfile
- * </ul>
- *
- * @param testCondition
- * a <code>boolean</code> value
- * @return a <code>boolean</code> value
- * @exception Exception
- * if an error occurs
- */
- private boolean checkResponse(boolean testCondition) throws Exception {
- boolean match = false;
-
- if (responseLine != null && !"".equals(responseLine)) {
- // If returnCode doesn't match
- if (responseLine.indexOf("HTTP/1.") > -1) {
-
- if (!returnCode.equals("")) {
- boolean resCode = (responseLine.indexOf(returnCode) > -1);
- boolean resMsg = (responseLine.indexOf(returnCodeMsg) > -1);
-
- if (returnCodeMsg.equals("")) {
- match = resCode;
- } else {
- match = (resCode && resMsg);
- }
-
- if (match != testCondition) {
-
- if (resultOut != null) {
- String expectedStatusCode = "<expectedStatusCode>"
- + returnCode + "</expectedReturnCode>\n";
- String expectedReasonPhrase = "<expectedReasonPhrase>"
- + returnCodeMsg + "</expectedReasonPhrase>";
- actualString = "<actualStatusLine>" + responseLine
- + "</actualStatusLine>\n";
- resultOut.append(expectedStatusCode);
- resultOut.append(expectedReasonPhrase);
- resultOut.append(actualString);
- }
-
- return false;
- }
- }
- } else {
- resultOut.append("\n<failure>No response or invalid response: "
- + responseLine + "</failure>");
- return false;
- }
- } else {
- resultOut.append("\n<failure>No response from server</failure>");
- return false;
- }
-
- /*
- * Check for headers the test expects to be in the server's response
- */
-
- // Duplicate set of response headers
- HashMap copiedHeaders = cloneHeaders(headers);
-
- // used for error reporting
- String currentHeaderField = null;
- String currentHeaderValue = null;
-
- if (!expectHeaders.isEmpty()) {
- boolean found = false;
- String expHeader = null;
-
- if (!headers.isEmpty()) {
- Iterator expectIterator = expectHeaders.keySet().iterator();
- while (expectIterator.hasNext()) {
- found = false;
- String expFieldName = (String) expectIterator.next();
- currentHeaderField = expFieldName;
- ArrayList expectValues = (ArrayList) expectHeaders
- .get(expFieldName);
- Iterator headersIterator = copiedHeaders.keySet()
- .iterator();
-
- while (headersIterator.hasNext()) {
- String headerFieldName = (String) headersIterator
- .next();
- ArrayList headerValues = (ArrayList) copiedHeaders
- .get(headerFieldName);
-
- // compare field names and values in an HTTP 1.x
- // compliant fashion
- if ((headerFieldName.equalsIgnoreCase(expFieldName))) {
- int hSize = headerValues.size();
- int eSize = expectValues.size();
-
- // number of expected headers found in server
- // response
- int numberFound = 0;
-
- for (int i = 0; i < eSize; i++) {
- currentHeaderValue = (String) expectValues
- .get(i);
-
- /*
- * Handle the Content-Type header appropriately
- * based on the the test is configured to look
- * for.
- */
- if (currentHeaderField
- .equalsIgnoreCase("content-type")) {
- String resVal = (String) headerValues
- .get(0);
- if (currentHeaderValue.indexOf(';') > -1) {
- if (currentHeaderValue.equals(resVal)) {
- numberFound++;
- headerValues.remove(0);
- }
- } else if (resVal
- .indexOf(currentHeaderValue) > -1) {
- numberFound++;
- headerValues.remove(0);
- }
- } else if (currentHeaderField
- .equalsIgnoreCase("location")) {
- String resVal = (String) headerValues
- .get(0);
- int idx = currentHeaderValue
- .indexOf(":80/");
- if (idx > -1) {
- String tempValue = currentHeaderValue
- .substring(0, idx)
- + currentHeaderValue
- .substring(idx + 3);
- if (currentHeaderValue.equals(resVal)
- || tempValue.equals(resVal)) {
- numberFound++;
- headerValues.remove(0);
- }
- } else {
- if (currentHeaderValue.equals(resVal)) {
- numberFound++;
- headerValues.remove(0);
- }
- }
- } else if (headerValues
- .contains(currentHeaderValue)) {
- numberFound++;
- headerValues.remove(headerValues
- .indexOf(currentHeaderValue));
- }
- }
- if (numberFound == eSize) {
- found = true;
- }
- }
- }
- if (!found) {
- /*
- * Expected headers not found in server response. Break
- * the processing loop.
- */
- break;
- }
- }
- }
-
- if (!found) {
- StringBuffer actualBuffer = new StringBuffer(128);
- if (resultOut != null) {
- expectedString = "<expectedHeaderNotFound>"
- + currentHeaderField + ": " + currentHeaderValue
- + "</expectedHeader>\n";
- }
- if (!headers.isEmpty()) {
- Iterator iter = headers.keySet().iterator();
- while (iter.hasNext()) {
- String headerName = (String) iter.next();
- ArrayList vals = (ArrayList) headers.get(headerName);
- String[] val = (String[]) vals.toArray(new String[vals
- .size()]);
- for (int i = 0; i < val.length; i++) {
- if (resultOut != null) {
- actualBuffer.append("<actualHeader>"
- + headerName + ": " + val[i]
- + "</actualHeader>\n");
- }
- }
- }
- if (resultOut != null) {
- resultOut.append(expectedString);
- resultOut.append(actualBuffer.toString());
- }
- }
- return false;
- }
- }
-
- /*
- * Check to see if we're looking for unexpected headers. If we are,
- * compare the values in the unexectedHeaders ArrayList against the
- * headers from the server response. if the unexpected header is found,
- * then return false.
- */
-
- if (!unexpectedHeaders.isEmpty()) {
- boolean found = false;
- String unExpHeader = null;
- // Check if we got any unexpected headers
-
- if (!copiedHeaders.isEmpty()) {
- Iterator unexpectedIterator = unexpectedHeaders.keySet()
- .iterator();
- while (unexpectedIterator.hasNext()) {
- found = false;
- String unexpectedFieldName = (String) unexpectedIterator
- .next();
- ArrayList unexpectedValues = (ArrayList) unexpectedHeaders
- .get(unexpectedFieldName);
- Iterator headersIterator = copiedHeaders.keySet()
- .iterator();
-
- while (headersIterator.hasNext()) {
- String headerFieldName = (String) headersIterator
- .next();
- ArrayList headerValues = (ArrayList) copiedHeaders
- .get(headerFieldName);
-
- // compare field names and values in an HTTP 1.x
- // compliant fashion
- if ((headerFieldName
- .equalsIgnoreCase(unexpectedFieldName))) {
- int hSize = headerValues.size();
- int eSize = unexpectedValues.size();
- int numberFound = 0;
- for (int i = 0; i < eSize; i++) {
- if (headerValues.contains(unexpectedValues
- .get(i))) {
- numberFound++;
- if (headerValues.indexOf(headerFieldName) >= 0) {
- headerValues.remove(headerValues
- .indexOf(headerFieldName));
- }
- }
- }
- if (numberFound == eSize) {
- found = true;
- }
- }
- }
- if (!found) {
- /*
- * Expected headers not found in server response. Break
- * the processing loop.
- */
- break;
- }
- }
- }
-
- if (found) {
- resultOut.append("\n Unexpected header received from server: "
- + unExpHeader);
- return false;
- }
- }
-
- if (responseMatch != null) {
- // check if we got the string we wanted
- if (expectResponseBody && responseBody == null) {
- resultOut.append("\n ERROR: got no response, expecting "
- + responseMatch);
- return false;
- }
- String responseBodyString = new String(responseBody);
- if (responseBodyString.indexOf(responseMatch) < 0) {
- resultOut.append("\n ERROR: expecting match on "
- + responseMatch);
- resultOut.append("\n Received: \n" + responseBodyString);
- }
- }
-
- if (!expectResponseBody && responseBody != null) {
- resultOut
- .append("Received a response body from the server where none was expected");
- return false;
- }
-
- // compare the body
- if (goldenFile == null)
- return true;
-
- // Get the expected result from the "golden" file.
- byte[] expResult = getExpectedResult();
- String expResultS = (expResult == null) ? "" : new String(expResult);
- // Compare the results and set the status
- boolean cmp = true;
-
- if (exactMatch) {
- cmp = compare(responseBody, expResult);
- } else {
- cmp = compareWeak(responseBody, expResult);
- }
-
- if (cmp != testCondition) {
-
- if (resultOut != null) {
- expectedString = "<expectedBody>" + new String(expResult)
- + "</expectedBody>\n";
- actualString = "<actualBody>"
- + (responseBody != null ? new String(responseBody)
- : "null") + "</actualBody>\n";
- resultOut.append(expectedString);
- resultOut.append(actualString);
- }
-
- return false;
- }
-
- return true;
- }
-
- /**
- * Replaces any |client.ip| and |client.host| parameter marks with the host
- * and IP values of the host upon which Watchdog is running.
- *
- * @param request
- * An HTTP request.
- */
- String replaceMarkers(String req, Socket socket) {
-
- final String CLIENT_IP = "client.ip";
- final String CLIENT_HOME = "client.host";
-
- if (localIP == null || localHost == null) {
- InetAddress addr = socket.getLocalAddress();
- localHost = addr.getHostName();
- localIP = addr.getHostAddress();
- }
-
- if (req.indexOf('|') > -1) {
- StringTokenizer tok = new StringTokenizer(request, "|");
- StringBuffer sb = new StringBuffer(50);
-
- while (tok.hasMoreElements()) {
- String token = tok.nextToken();
- if (token.equals(CLIENT_IP)) {
- sb.append(localIP);
- } else if (token.equals(CLIENT_HOME)) {
- sb.append(localHost);
- } else {
- sb.append(token);
- }
- }
- return sb.toString();
- } else {
- return req;
- }
- }
-
- /**
- * <code>getExpectedResult</code> returns a byte array containing the
- * content of the configured goldenfile
- *
- * @return goldenfile as a byte[]
- * @exception IOException
- * if an error occurs
- */
- private byte[] getExpectedResult() throws IOException {
- byte[] expResult = { 'N', 'O', ' ', 'G', 'O', 'L', 'D', 'E', 'N', 'F',
- 'I', 'L', 'E', ' ', 'F', 'O', 'U', 'N', 'D' };
-
- try {
- InputStream in = new BufferedInputStream(new FileInputStream(
- goldenFile));
- return readBody(in);
- } catch (Exception ex) {
- System.out.println("Golden file not found: " + goldenFile);
- return expResult;
- }
- }
-
- /**
- * <code>compare</code> compares the two byte arrays passed in to verify
- * that the lengths of the arrays are equal, and that the content of the two
- * arrays, byte for byte are equal.
- *
- * @param fromServer
- * a <code>byte[]</code> value
- * @param fromGoldenFile
- * a <code>byte[]</code> value
- * @return <code>boolean</code> true if equal, otherwise false
- */
- private boolean compare(byte[] fromServer, byte[] fromGoldenFile) {
- if (fromServer == null || fromGoldenFile == null) {
- return false;
- }
-
- /*
- * Check to see that the respose and golden file lengths are equal. If
- * they are not, dump the hex and don't bother comparing the bytes. If
- * they are equal, iterate through the byte arrays and compare each
- * byte. If the bytes don't match, dump the hex representation of the
- * server response and the goldenfile and return false.
- */
- if (fromServer.length != fromGoldenFile.length) {
- StringBuffer sb = new StringBuffer(50);
- sb.append(" Response and golden files lengths do not match!\n");
- sb.append(" Server response length: ");
- sb.append(fromServer.length);
- sb.append("\n Goldenfile length: ");
- sb.append(fromGoldenFile.length);
- resultOut.append(sb.toString());
- sb = null;
- // dump the hex representation of the byte arrays
- dumpHex(fromServer, fromGoldenFile);
-
- return false;
- } else {
-
- int i = 0;
- int j = 0;
-
- while ((i < fromServer.length) && (j < fromGoldenFile.length)) {
- if (fromServer[i] != fromGoldenFile[j]) {
- resultOut.append("\n Error at position " + (i + 1));
- // dump the hex representation of the byte arrays
- dumpHex(fromServer, fromGoldenFile);
-
- return false;
- }
-
- i++;
- j++;
- }
- }
-
- return true;
- }
-
- /**
- * <code>compareWeak</code> creates new Strings from the passed arrays and
- * then uses a StringTokenizer to compare non-whitespace tokens.
- *
- * @param fromServer
- * a <code>byte[]</code> value
- * @param fromGoldenFile
- * a <code>byte[]</code> value
- * @return a <code>boolean</code> value
- */
- private boolean compareWeak(byte[] fromServer, byte[] fromGoldenFile) {
- if (fromServer == null || fromGoldenFile == null) {
- return false;
- }
-
- boolean status = true;
-
- String server = new String(fromServer);
- String golden = new String(fromGoldenFile);
-
- StringTokenizer st1 = new StringTokenizer(server);
-
- StringTokenizer st2 = new StringTokenizer(golden);
-
- while (st1.hasMoreTokens() && st2.hasMoreTokens()) {
- String tok1 = st1.nextToken();
- String tok2 = st2.nextToken();
-
- if (!tok1.equals(tok2)) {
- resultOut.append("\t FAIL*** : Rtok1 = " + tok1 + ", Etok2 = "
- + tok2);
- status = false;
- }
- }
-
- if (st1.hasMoreTokens() || st2.hasMoreTokens()) {
- status = false;
- }
-
- if (!status) {
- StringBuffer sb = new StringBuffer(255);
- sb
- .append("ERROR: Server's response and configured goldenfile do not match!\n");
- sb.append("Response received from server:\n");
- sb
- .append("---------------------------------------------------------\n");
- sb.append(server);
- sb.append("\nContent of Goldenfile:\n");
- sb
- .append("---------------------------------------------------------\n");
- sb.append(golden);
- sb.append("\n");
- resultOut.append(sb.toString());
- }
- return status;
- }
-
- /**
- * <code>readBody</code> reads the body of the response from the
- * InputStream.
- *
- * @param input
- * an <code>InputStream</code>
- * @return a <code>byte[]</code> representation of the response
- */
- private byte[] readBody(InputStream input) {
- StringBuffer sb = new StringBuffer(255);
- while (true) {
- try {
- int ch = input.read();
-
- if (ch < 0) {
- if (sb.length() == 0) {
- return (null);
- } else {
- break;
- }
- }
- sb.append((char) ch);
-
- } catch (IOException ex) {
- return null;
- }
- }
- return sb.toString().getBytes();
- }
-
- /**
- * <code>setHeaderDetails</code> Wrapper method for parseHeader. Allows easy
- * addition of headers to the specified HashMap
- *
- * @param line
- * a <code>String</code> value
- * @param headerMap
- * a <code>HashMap</code> value
- * @param isRequest
- * a <code>boolean</code> indicating if the passed Header HashMap
- * is for request headers
- */
- void setHeaderDetails(String line, HashMap headerHash, boolean isRequest) {
- StringTokenizer stk = new StringTokenizer(line, "##");
-
- while (stk.hasMoreElements()) {
- String presentHeader = stk.nextToken();
- parseHeader(presentHeader, headerHash, isRequest);
- }
- }
-
- /**
- * <code>parseHeader</code> parses input headers in format of "key:value"
- * The parsed header field-name will be used as a key in the passed HashMap
- * object, and the values found will be stored in an ArrayList associated
- * with the field-name key.
- *
- * @param line
- * String representation of an HTTP header line.
- * @param headers
- * a<code>HashMap</code> to store key/value header objects.
- * @param isRequest
- * set to true if the headers being processed are requestHeaders.
- */
- void parseHeader(String line, HashMap headerMap, boolean isRequest) {
- // Parse the header name and value
- int colon = line.indexOf(":");
-
- if (colon < 0) {
- resultOut
- .append("\n ERROR: Header is in incorrect format: " + line);
- return;
- }
-
- String name = line.substring(0, colon).trim();
- String value = line.substring(colon + 1).trim();
-
- if ((cookieVector != null) && (name.equalsIgnoreCase("Set-Cookie"))) {
- cookieVector.addElement(value);
- /*
- * if ( ( value.indexOf("JSESSIONID") > -1 ) ||
- * (value.indexOf("jsessionid") > -1 ) ) { String sessionId=
- * value.substring( value.indexOf("=")+1); if ( testSession != null
- * ) { sessionHash.put( testSession, sessionId ); }
- * System.out.println("Got Session-ID : " + sessionId ); }
- */
- }
-
- // System.out.println("HEADER: " +name + " " + value);
-
- ArrayList values = (ArrayList) headerMap.get(name);
- if (values == null) {
- values = new ArrayList();
- }
- // HACK
- if (value.indexOf(',') > -1 && !isRequest
- && !name.equalsIgnoreCase("Date")) {
- StringTokenizer st = new StringTokenizer(value, ",");
- while (st.hasMoreElements()) {
- values.add(st.nextToken());
- }
- } else {
- values.add(value);
- }
-
- headerMap.put(name, values);
- }
-
- /**
- * <code>dumpHex</code> helper method to dump formatted hex output of the
- * server response and the goldenfile.
- *
- * @param serverResponse
- * a <code>byte[]</code> value
- * @param goldenFile
- * a <code>byte[]</code> value
- */
- private void dumpHex(byte[] serverResponse, byte[] goldenFile) {
- StringBuffer outBuf = new StringBuffer(
- (serverResponse.length + goldenFile.length) * 2);
-
- String fromServerString = Hex.getHexDump(serverResponse, 0,
- serverResponse.length, true);
- String fromGoldenFileString = Hex.getHexDump(goldenFile, 0,
- goldenFile.length, true);
-
- outBuf
- .append(" Hex dump of server response and goldenfile below.\n\n### RESPONSE FROM SERVER ###\n");
- outBuf.append("----------------------------\n");
- outBuf.append(fromServerString);
- outBuf.append("\n\n### GOLDEN FILE ###\n");
- outBuf.append("-------------------\n");
- outBuf.append(fromGoldenFileString);
- outBuf.append("\n\n### END OF DUMP ###\n");
-
- resultOut.append(outBuf.toString());
-
- }
-
- /**
- * <code>cloneHeaders</code> returns a "cloned" HashMap of the map passed
- * in.
- *
- * @param map
- * a <code>HashMap</code> value
- * @return a <code>HashMap</code> value
- */
- private HashMap cloneHeaders(HashMap map) {
- HashMap dupMap = new HashMap();
- Iterator iter = map.keySet().iterator();
-
- while (iter.hasNext()) {
- String key = new String((String) iter.next());
- ArrayList origValues = (ArrayList) map.get(key);
- ArrayList dupValues = new ArrayList();
-
- String[] dupVal = (String[]) origValues
- .toArray(new String[origValues.size()]);
- for (int i = 0; i < dupVal.length; i++) {
- dupValues.add(new String(dupVal[i]));
- }
-
- dupMap.put(key, dupValues);
- }
- return dupMap;
- }
-
-}
diff --git a/res/INSTALLLICENSE b/res/INSTALLLICENSE
index 112c0b4..e6a6baf 100644
--- a/res/INSTALLLICENSE
+++ b/res/INSTALLLICENSE
@@ -725,6 +725,10 @@ For the following XML Schemas for Java EE Deployment Descriptors:
- web-app_3_1.xsd
- web-common_3_1.xsd
- web-fragment_3_1.xsd
+ - javaee_8.xsd
+ - web-app_4_0.xsd
+ - web-common_4_0.xsd
+ - web-fragment_4_0.xsd
COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
diff --git a/res/META-INF/jasper.jar/services/javax.servlet.ServletContainerInitializer b/res/META-INF/jasper.jar/services/javax.servlet.ServletContainerInitializer
index 33a59da..2eb8f6d 100644
--- a/res/META-INF/jasper.jar/services/javax.servlet.ServletContainerInitializer
+++ b/res/META-INF/jasper.jar/services/javax.servlet.ServletContainerInitializer
@@ -1 +1,16 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
org.apache.jasper.servlet.JasperInitializer
\ No newline at end of file
diff --git a/res/META-INF/jasper.jar/web-fragment.xml b/res/META-INF/jasper.jar/web-fragment.xml
index dbf1885..67953f9 100644
--- a/res/META-INF/jasper.jar/web-fragment.xml
+++ b/res/META-INF/jasper.jar/web-fragment.xml
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="ISO-8859-1"?>
+<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
diff --git a/res/META-INF/jaspic-api.jar.manifest b/res/META-INF/jaspic-api.jar.manifest
new file mode 100644
index 0000000..a071675
--- /dev/null
+++ b/res/META-INF/jaspic-api.jar.manifest
@@ -0,0 +1,11 @@
+Manifest-version: 1.0
+X-Compile-Source-JDK: @source.jdk@
+X-Compile-Target-JDK: @target.jdk@
+
+Name: javax/security/auth/message
+Specification-Title: Java Authentication SPI for Containers
+Specification-Version: 1.1
+Specification-Vendor: Sun Microsystems, Inc.
+Implementation-Title: javax.security.auth.message
+Implementation-Version: 1.1. at jaspic.revision@
+Implementation-Vendor: Apache Software Foundation
\ No newline at end of file
diff --git a/res/META-INF/tomcat-websocket.jar/services/javax.servlet.ServletContainerInitializer b/res/META-INF/tomcat-websocket.jar/services/javax.servlet.ServletContainerInitializer
index 85ee1c0..c850c01 100644
--- a/res/META-INF/tomcat-websocket.jar/services/javax.servlet.ServletContainerInitializer
+++ b/res/META-INF/tomcat-websocket.jar/services/javax.servlet.ServletContainerInitializer
@@ -1 +1,16 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
org.apache.tomcat.websocket.server.WsSci
\ No newline at end of file
diff --git a/res/META-INF/tomcat-websocket.jar/services/javax.websocket.ContainerProvider b/res/META-INF/tomcat-websocket.jar/services/javax.websocket.ContainerProvider
index 91c8204..abdaee2 100644
--- a/res/META-INF/tomcat-websocket.jar/services/javax.websocket.ContainerProvider
+++ b/res/META-INF/tomcat-websocket.jar/services/javax.websocket.ContainerProvider
@@ -1 +1,16 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
org.apache.tomcat.websocket.WsContainerProvider
\ No newline at end of file
diff --git a/res/META-INF/tomcat-websocket.jar/services/javax.websocket.server.ServerEndpointConfig$Configurator b/res/META-INF/tomcat-websocket.jar/services/javax.websocket.server.ServerEndpointConfig$Configurator
index 38046d2..6453734 100644
--- a/res/META-INF/tomcat-websocket.jar/services/javax.websocket.server.ServerEndpointConfig$Configurator
+++ b/res/META-INF/tomcat-websocket.jar/services/javax.websocket.server.ServerEndpointConfig$Configurator
@@ -1 +1,16 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
org.apache.tomcat.websocket.server.DefaultServerEndpointConfigurator
\ No newline at end of file
diff --git a/res/META-INF/tomcat-websocket.jar/web-fragment.xml b/res/META-INF/tomcat-websocket.jar/web-fragment.xml
index d5d302f..f965e90 100644
--- a/res/META-INF/tomcat-websocket.jar/web-fragment.xml
+++ b/res/META-INF/tomcat-websocket.jar/web-fragment.xml
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="ISO-8859-1"?>
+<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
diff --git a/res/checkstyle/checkstyle.xml b/res/checkstyle/checkstyle.xml
index ebfe0a0..64c61d8 100644
--- a/res/checkstyle/checkstyle.xml
+++ b/res/checkstyle/checkstyle.xml
@@ -1,4 +1,4 @@
-<?xml version="1.0"?>
+<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
diff --git a/res/checkstyle/javax-checkstyle.xml b/res/checkstyle/javax-checkstyle.xml
index 08501f9..bb7568c 100644
--- a/res/checkstyle/javax-checkstyle.xml
+++ b/res/checkstyle/javax-checkstyle.xml
@@ -1,4 +1,4 @@
-<?xml version="1.0"?>
+<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
diff --git a/res/checkstyle/javax-import-control.xml b/res/checkstyle/javax-import-control.xml
index 2e7201d..252cd96 100644
--- a/res/checkstyle/javax-import-control.xml
+++ b/res/checkstyle/javax-import-control.xml
@@ -1,4 +1,4 @@
-<?xml version="1.0"?>
+<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
@@ -36,6 +36,27 @@
<subpackage name="persistence">
<allow pkg="javax.persistence"/>
</subpackage>
+ <subpackage name="security">
+ <subpackage name="auth">
+ <subpackage name="message">
+ <allow pkg="javax.crypto" />
+ <allow class="javax.security.auth.Subject"/>
+ <allow pkg="javax.security.auth.callback"/>
+ <allow pkg="javax.security.auth.login"/>
+ <allow pkg="javax.security.auth.x500"/>
+ <disallow pkg="javax.security.auth.message.callback" local-only="true"/>
+ <disallow pkg="javax.security.auth.message.config" local-only="true"/>
+ <disallow pkg="javax.security.auth.message.module" local-only="true"/>
+ <subpackage name="config">
+ <allow class="javax.security.auth.AuthPermission"/>
+ <allow pkg="javax.security.auth.message"/>
+ </subpackage>
+ <subpackage name="module">
+ <allow pkg="javax.security.auth.message"/>
+ </subpackage>
+ </subpackage>
+ </subpackage>
+ </subpackage>
<subpackage name="servlet">
<allow pkg="javax.servlet"/>
<disallow pkg="javax.servlet.jsp" local-only="true"/>
diff --git a/res/checkstyle/org-checkstyle.xml b/res/checkstyle/org-checkstyle.xml
index a9e240f..a8bb3b1 100644
--- a/res/checkstyle/org-checkstyle.xml
+++ b/res/checkstyle/org-checkstyle.xml
@@ -1,4 +1,4 @@
-<?xml version="1.0"?>
+<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
diff --git a/res/checkstyle/org-import-control.xml b/res/checkstyle/org-import-control.xml
index 34ca912..8ccd08f 100644
--- a/res/checkstyle/org-import-control.xml
+++ b/res/checkstyle/org-import-control.xml
@@ -1,4 +1,4 @@
-<?xml version="1.0"?>
+<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
@@ -113,6 +113,7 @@
<subpackage name="naming">
<allow pkg="javax.mail"/>
<allow pkg="javax.wsdl"/>
+ <allow pkg="org.apache.juli"/>
<allow pkg="org.apache.naming"/>
<allow class="org.apache.tomcat.util.buf.UDecoder"/>
<allow class="org.apache.tomcat.util.buf.UEncoder"/>
@@ -170,6 +171,7 @@
</subpackage>
<subpackage name="server">
<allow pkg="javax.servlet"/>
+ <allow pkg="org.apache.coyote.http11.upgrade"/>
<allow pkg="org.apache.tomcat.websocket"/>
</subpackage>
</subpackage>
diff --git a/res/cobertura/logback.xml b/res/cobertura/logback.xml
new file mode 100644
index 0000000..72d947b
--- /dev/null
+++ b/res/cobertura/logback.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+-->
+<configuration>
+
+ <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+ <!-- encoders are assigned the type
+ ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
+ <encoder>
+ <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
+ </encoder>
+ </appender>
+
+ <root level="INFO">
+ <appender-ref ref="STDOUT" />
+ </root>
+</configuration>
\ No newline at end of file
diff --git a/res/deployer/build.xml b/res/deployer/build.xml
index bf84c2f..a1fc439 100644
--- a/res/deployer/build.xml
+++ b/res/deployer/build.xml
@@ -1,4 +1,4 @@
-<?xml version='1.0' encoding='utf-8'?>
+<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
diff --git a/res/findbugs/filter-false-positives.xml b/res/findbugs/filter-false-positives.xml
index 7d7523f..d8c67c2 100644
--- a/res/findbugs/filter-false-positives.xml
+++ b/res/findbugs/filter-false-positives.xml
@@ -400,6 +400,24 @@
<Bug code="RCN"/>
</Match>
<Match>
+ <!-- Use of synchronisation is required to make a sequence of calls in -->
+ <!-- one method appear to be atomic. -->
+ <Class name="org.apache.coyote.AbstractProcessorLight"/>
+ <Or>
+ <Method name="addDispatch"/>
+ <Method name="getIteratorAndClearDispatches"/>
+ <Method name="clearDispatches"/>
+ </Or>
+ <Bug pattern="JLM_JSR166_UTILCONCURRENT_MONITORENTER" />
+ </Match>
+ <Match>
+ <!-- Correct behaviour does not assume sequential operations on concurrent
+ hash map are atomic. -->
+ <Class name="org.apache.coyote.AbstractProtocol$AbstractConnectionHandler" />
+ <Method name="process" />
+ <Bug pattern="AT_OPERATION_SEQUENCE_ON_CONCURRENT_ABSTRACTION" />
+ </Match>
+ <Match>
<!-- Locks are always released. Non-standard pattern is required because -->
<!-- of lock upgrade that is used. -->
<Class name="org.apache.coyote.http11.upgrade.AprServletInputStream" />
@@ -415,8 +433,8 @@
</Match>
<Match>
<!-- Fall-through expected -->
- <Class name="org.apache.coyote.http11.AbstractHttp11Processor" />
- <Method name="process"/>
+ <Class name="org.apache.coyote.http11.Http11Processor" />
+ <Method name="service"/>
<Bug code="SF" />
</Match>
<Match>
@@ -434,6 +452,30 @@
<Bug code="UL" />
</Match>
<Match>
+ <!-- Number being tested is unsigned. -->
+ <Class name="org.apache.coyote.http2.Http2UpgradeHandler" />
+ <Method name="createRemoteStream" />
+ <Bug pattern="IM_BAD_CHECK_FOR_ODD" />
+ </Match>
+ <Match>
+ <!-- Loss of the occasional increment is acceptable. -->
+ <Class name="org.apache.coyote.http2.Http2UpgradeHandler" />
+ <Method name="pruneClosedStreams" />
+ <Bug pattern="VO_VOLATILE_INCREMENT" />
+ </Match>
+ <Match>
+ <!-- Notify is correct. Condition changed outside of this method. -->
+ <Class name="org.apache.coyote.http2.Http2UpgradeHandler" />
+ <Mehtod name="releaseBackLog" />
+ <Bug pattern="NN_NAKED_NOTIFY" />
+ </Match>
+ <Match>
+ <!-- Notify is correct. Condition changed outside of this method. -->
+ <Class name="org.apache.coyote.http2.Stream" />
+ <Mehtod name="incrementWindowSize" />
+ <Bug pattern="NN_NAKED_NOTIFY" />
+ </Match>
+ <Match>
<!-- Returning null is required by the EL specification -->
<Class name="org.apache.el.lang.ELSupport" />
<Method name="coerceToBoolean"/>
@@ -681,8 +723,8 @@
</Match>
<Match>
<!-- Fall-through expected -->
- <Class name="org.apache.tomcat.util.http.Parameters" />
- <Method name="processParameters"/>
+ <Class name="org.apache.tomcat.util.http.LegacyCookieProcessor" />
+ <Method name="processCookieHeader"/>
<Bug code="SF" />
</Match>
<Match>
@@ -704,6 +746,15 @@
<Bug code="Dm" />
</Match>
<Match>
+ <!-- Fall-through expected -->
+ <Class name="org.apache.tomcat.util.http.parser.Cookie" />
+ <Or>
+ <Method name="logInvalidHeader"/>
+ <Method name="logInvalidVersion"/>
+ </Or>
+ <Bug code="SF" />
+ </Match>
+ <Match>
<!-- Hiding of field in superclass is deliberate -->
<Class name="org.apache.tomcat.util.modeler.NotificationInfo"/>
<Field name="info" />
@@ -764,13 +815,16 @@
<Match>
<!-- Return value is ignored at this point but logic further up call -->
<!-- stack will ensure that a SocketTimeoutException is thrown -->
- <Class name="org.apache.tomcat.util.net.NioEndpoint$KeyAttachment"/>
+ <Class name="org.apache.tomcat.util.net.NioEndpoint$NioSocketWrapper"/>
<Method name="awaitLatch"/>
<Bug code="RV"/>
</Match>
<Match>
<!-- Object is only ever set to null, sync therefore is still valid -->
- <Class name="org.apache.tomcat.util.net.NioEndpoint$SocketProcessor"/>
+ <Or>
+ <Class name="org.apache.tomcat.util.net.NioEndpoint$SocketProcessor"/>
+ <Class name="org.apache.tomcat.util.net.Nio2Endpoint$SocketProcessor"/>
+ </Or>
<Method name="run"/>
<Bug code="ML"/>
</Match>
@@ -780,15 +834,16 @@
<Bug code="DE" />
</Match>
<Match>
- <!-- Use of synchronisation is required to make a sequence of calls in -->
- <!-- one method appear to be atomic. -->
- <Class name="org.apache.tomcat.util.net.SocketWrapper"/>
- <Or>
- <Method name="addDispatch"/>
- <Method name="getIteratorAndClearDispatches"/>
- <Method name="clearDispatches"/>
- </Or>
- <Bug pattern="JLM_JSR166_UTILCONCURRENT_MONITORENTER" />
+ <!-- Fall-through expected -->
+ <Class name="org.apache.tomcat.util.net.SecureNioChannel" />
+ <Method name="processSNI"/>
+ <Bug code="SF" />
+ </Match>
+ <Match>
+ <!-- Fall-through expected -->
+ <Class name="org.apache.tomcat.util.net.SecureNio2Channel" />
+ <Method name="processSNI"/>
+ <Bug code="SF" />
</Match>
<Match>
<!-- Yes the simple name is the same as the super class. Accept it. -->
@@ -801,6 +856,12 @@
<Method name="connectToServer" />
<Bug code="DLS" />
</Match>
+ <Match>
+ <!-- Fall-through expected -->
+ <Class name="org.apache.tomcat.websocket.server.WsHttpUpgradeHandler" />
+ <Method name="upgradeDispatch"/>
+ <Bug code="SF" />
+ </Match>
<!-- Example code -->
<Match>
diff --git a/res/ide-support/eclipse/eclipse.project b/res/ide-support/eclipse/eclipse.project
index d480797..994e94b 100644
--- a/res/ide-support/eclipse/eclipse.project
+++ b/res/ide-support/eclipse/eclipse.project
@@ -16,7 +16,7 @@
limitations under the License.
-->
<projectDescription>
- <name>tomcat-8.0.x</name>
+ <name>tomcat-8.5.x</name>
<comment></comment>
<projects>
</projects>
diff --git a/res/ide-support/eclipse/java-compiler-errors-warnings.txt b/res/ide-support/eclipse/java-compiler-errors-warnings.txt
index aae6dbe..06b4abd 100644
--- a/res/ide-support/eclipse/java-compiler-errors-warnings.txt
+++ b/res/ide-support/eclipse/java-compiler-errors-warnings.txt
@@ -63,6 +63,7 @@ Unnecessary code
([x] on all additional check boxes)
except the following:
+ - Value of exception parameter is... - I
- Unnecessary else - I
Generic types
@@ -88,6 +89,23 @@ Eclipse IDE is documented here:
4.2: http://help.eclipse.org/juno/topic/org.eclipse.jdt.doc.user/tasks/task-suppress_warnings.htm
+# Java -> Compiler -> Javadoc
+=============================
+
+Enable 'Process Javadoc comments'
+ - Malformed Javadoc comments - W
+ - Only consider members visible as - Public
+ [X] Validate tag arguments
+ [ ] Report non-visible references
+ [X] Report deprecated references
+ - Missing tag descriptions - Validate all standard tags
+ - Missing Javadoc tags - W
+ - Only consider members visible as - Public
+ [X] - Ignore in overriding and implementing methods
+ [ ] - Ignore method type parameters
+ - Missing Javadoc comments - I
+
+
# Java -> Code Style -> Organize Imports
==========================================
diff --git a/res/ide-support/eclipse/org.eclipse.jdt.core.prefs.properties b/res/ide-support/eclipse/org.eclipse.jdt.core.prefs.properties
index bdbfe41..c7fcfab 100644
--- a/res/ide-support/eclipse/org.eclipse.jdt.core.prefs.properties
+++ b/res/ide-support/eclipse/org.eclipse.jdt.core.prefs.properties
@@ -15,6 +15,6 @@
# limitations under the License.
# -----------------------------------------------------------------------------
eclipse.preferences.version=1
-org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
-org.eclipse.jdt.core.compiler.compliance=1.7
-org.eclipse.jdt.core.compiler.source=1.7
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
+org.eclipse.jdt.core.compiler.compliance=1.8
+org.eclipse.jdt.core.compiler.source=1.8
diff --git a/res/ide-support/eclipse/start-tomcat.launch b/res/ide-support/eclipse/start-tomcat.launch
index a4d4d86..97930f5 100644
--- a/res/ide-support/eclipse/start-tomcat.launch
+++ b/res/ide-support/eclipse/start-tomcat.launch
@@ -17,13 +17,13 @@
-->
<launchConfiguration type="org.eclipse.jdt.launching.localJavaApplication">
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
-<listEntry value="/tomcat-8.0.x/java/org/apache/catalina/startup/Bootstrap.java"/>
+<listEntry value="/tomcat-8.5.x/java/org/apache/catalina/startup/Bootstrap.java"/>
</listAttribute>
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
<listEntry value="1"/>
</listAttribute>
<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="org.apache.catalina.startup.Bootstrap"/>
<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="start"/>
-<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="tomcat-8.0.x"/>
-<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Dcatalina.home=${project_loc:/tomcat-8.0.x/java/org/apache/catalina/startup/Bootstrap.java}/output/build"/>
+<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="tomcat-8.5.x"/>
+<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Dcatalina.home=${project_loc:/tomcat-8.5.x/java/org/apache/catalina/startup/Bootstrap.java}/output/build"/>
</launchConfiguration>
diff --git a/res/ide-support/eclipse/stop-tomcat.launch b/res/ide-support/eclipse/stop-tomcat.launch
index 4ead818..72700b3 100644
--- a/res/ide-support/eclipse/stop-tomcat.launch
+++ b/res/ide-support/eclipse/stop-tomcat.launch
@@ -17,13 +17,13 @@
-->
<launchConfiguration type="org.eclipse.jdt.launching.localJavaApplication">
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
-<listEntry value="/tomcat-8.0.x/java/org/apache/catalina/startup/Bootstrap.java"/>
+<listEntry value="/tomcat-8.5.x/java/org/apache/catalina/startup/Bootstrap.java"/>
</listAttribute>
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
<listEntry value="1"/>
</listAttribute>
<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="org.apache.catalina.startup.Bootstrap"/>
<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="stop"/>
-<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="tomcat-8.0.x"/>
-<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Dcatalina.home=${project_loc:/tomcat-8.0.x/java/org/apache/catalina/startup/Bootstrap.java}/output/build"/>
+<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="tomcat-8.5.x"/>
+<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Dcatalina.home=${project_loc:/tomcat-8.5.x/java/org/apache/catalina/startup/Bootstrap.java}/output/build"/>
</launchConfiguration>
diff --git a/res/maven/mvn-pub.xml b/res/maven/mvn-pub.xml
index 1bd7454..cfaa8fb 100644
--- a/res/maven/mvn-pub.xml
+++ b/res/maven/mvn-pub.xml
@@ -1,4 +1,4 @@
-<?xml version="1.0"?>
+<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
@@ -15,7 +15,7 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<project name="Tomcat 8.0 Maven Deployment" default="" basedir="."
+<project name="Tomcat 8.5 Maven Deployment" default="" basedir="."
xmlns:artifact="urn:maven-artifact-ant">
<!--
Built for using Maven Ant Tasks (version 2.1.0 is known to work)
@@ -321,6 +321,10 @@
jarFileName="websocket-api.jar"
srcJarFileName="websocket-api-src.jar"/>
+ <doMavenDeploy artifactId="tomcat-jaspic-api"
+ jarFileName="jaspic-api.jar"
+ srcJarFileName="jaspic-api-src.jar"/>
+
<doMavenDeploy artifactId="tomcat-api"/>
<doMavenDeploy artifactId="tomcat-jni"/>
<doMavenDeploy artifactId="tomcat-util"/>
@@ -333,14 +337,6 @@
<doMavenDeployNoSrc artifactId="tomcat-i18n-ja"/>
<!-- Extras jars -->
- <doMavenDeploy artifactId="tomcat-extras-juli"
- groupId="org.apache.tomcat.extras"
- file="${tomcat.extras.path}/tomcat-juli.jar"
- src="${tomcat.extras.src.path}/tomcat-juli-src.jar" />
- <doMavenDeploy artifactId="tomcat-extras-juli-adapters"
- groupId="org.apache.tomcat.extras"
- file="${tomcat.extras.path}/tomcat-juli-adapters.jar"
- src="${tomcat.extras.src.path}/tomcat-juli-adapters-src.jar" />
<doMavenDeploy artifactId="tomcat-catalina-jmx-remote"
groupId="org.apache.tomcat.extras"
file="${tomcat.extras.path}/catalina-jmx-remote.jar"
@@ -372,16 +368,6 @@
file="${tomcat.embed.path}/tomcat-embed-websocket.jar"
pom="tomcat-embed-websocket.pom"
src="${tomcat.embed.src.path}/tomcat-embed-websocket-src.jar"/>
- <doMavenDeploy artifactId="tomcat-embed-logging-juli"
- groupId="org.apache.tomcat.embed"
- file="${tomcat.embed.path}/tomcat-embed-logging-juli.jar"
- pom="tomcat-embed-logging-juli.pom"
- src="${tomcat.embed.src.path}/tomcat-embed-logging-juli-src.jar"/>
- <doMavenDeploy artifactId="tomcat-embed-logging-log4j"
- groupId="org.apache.tomcat.embed"
- file="${tomcat.embed.path}/tomcat-embed-logging-log4j.jar"
- pom="tomcat-embed-logging-log4j.pom"
- src="${tomcat.embed.src.path}/tomcat-embed-logging-log4j-src.jar"/>
<!-- Binaries -->
<doMavenDeployBinaries
@@ -395,7 +381,7 @@
<param name="maven.repo.repositoryId"
value="${maven.snapshot.repo.repositoryId}"/>
<param name="maven.repo.url" value="${maven.snapshot.repo.url}"/>
- <param name="maven.deploy.version" value="8.0-SNAPSHOT"/>
+ <param name="maven.deploy.version" value="8.5-SNAPSHOT"/>
<param name="maven.deploy.binary.version"
value="${maven.asf.release.deploy.version}-dev"/>
</antcall>
diff --git a/res/maven/mvn.properties.default b/res/maven/mvn.properties.default
index 3c61b80..bdbbc38 100644
--- a/res/maven/mvn.properties.default
+++ b/res/maven/mvn.properties.default
@@ -35,7 +35,7 @@ maven.asf.release.repo.url=https://repository.apache.org/service/local/staging/d
maven.asf.release.repo.repositoryId=apache.releases
# Release version info
-maven.asf.release.deploy.version=8.0.39
+maven.asf.release.deploy.version=8.5.8
#Where do we load the libraries from
tomcat.lib.path=../../output/build/lib
diff --git a/res/maven/tomcat-embed-core.pom b/res/maven/tomcat-embed-core.pom
index 4a9d77f..10b08e4 100644
--- a/res/maven/tomcat-embed-core.pom
+++ b/res/maven/tomcat-embed-core.pom
@@ -29,20 +29,4 @@
<distribution>repo</distribution>
</license>
</licenses>
- <dependencies>
- <dependency>
- <groupId>org.apache.tomcat.embed</groupId>
- <artifactId>tomcat-embed-logging-juli</artifactId>
- <version>@MAVEN.DEPLOY.VERSION@</version>
- <scope>compile</scope>
- <optional>true</optional>
- </dependency>
- <dependency>
- <groupId>org.apache.tomcat.embed</groupId>
- <artifactId>tomcat-embed-logging-log4j</artifactId>
- <version>@MAVEN.DEPLOY.VERSION@</version>
- <scope>compile</scope>
- <optional>true</optional>
- </dependency>
- </dependencies>
</project>
diff --git a/res/maven/tomcat-embed-logging-juli.pom b/res/maven/tomcat-embed-logging-juli.pom
deleted file mode 100644
index 18af0ce..0000000
--- a/res/maven/tomcat-embed-logging-juli.pom
+++ /dev/null
@@ -1,32 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- 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.
--->
-<project>
- <modelVersion>4.0.0</modelVersion>
- <groupId>org.apache.tomcat.embed</groupId>
- <artifactId>tomcat-embed-logging-juli</artifactId>
- <version>@MAVEN.DEPLOY.VERSION@</version>
- <description>JULI logging implementation for embedded Tomcat</description>
- <url>http://tomcat.apache.org/</url>
- <licenses>
- <license>
- <name>Apache License, Version 2.0</name>
- <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
- <distribution>repo</distribution>
- </license>
- </licenses>
-</project>
diff --git a/res/maven/tomcat-embed-logging-log4j.pom b/res/maven/tomcat-embed-logging-log4j.pom
deleted file mode 100644
index dd9d6ba..0000000
--- a/res/maven/tomcat-embed-logging-log4j.pom
+++ /dev/null
@@ -1,32 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- 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.
--->
-<project>
- <modelVersion>4.0.0</modelVersion>
- <groupId>org.apache.tomcat.embed</groupId>
- <artifactId>tomcat-embed-logging-log4j</artifactId>
- <version>@MAVEN.DEPLOY.VERSION@</version>
- <description>log4j logging implementation for embedded Tomcat</description>
- <url>http://tomcat.apache.org/</url>
- <licenses>
- <license>
- <name>Apache License, Version 2.0</name>
- <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
- <distribution>repo</distribution>
- </license>
- </licenses>
-</project>
diff --git a/res/maven/tomcat-extras-juli-adapters.pom b/res/maven/tomcat-extras-juli-adapters.pom
deleted file mode 100644
index 398ffdc..0000000
--- a/res/maven/tomcat-extras-juli-adapters.pom
+++ /dev/null
@@ -1,47 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- 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.
--->
-<project>
- <modelVersion>4.0.0</modelVersion>
- <groupId>org.apache.tomcat.extras</groupId>
- <artifactId>tomcat-extras-juli-adapters</artifactId>
- <version>@MAVEN.DEPLOY.VERSION@</version>
- <description>Adapters to plug in other logging frameworks in Tomcat</description>
- <url>http://tomcat.apache.org/</url>
- <licenses>
- <license>
- <name>Apache License, Version 2.0</name>
- <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
- <distribution>repo</distribution>
- </license>
- </licenses>
- <dependencies>
- <dependency>
- <groupId>org.apache.tomcat.extras</groupId>
- <artifactId>tomcat-extras-juli</artifactId>
- <version>@MAVEN.DEPLOY.VERSION@</version>
- <scope>compile</scope>
- </dependency>
- <dependency>
- <groupId>org.apache.tomcat</groupId>
- <artifactId>tomcat-servlet-api</artifactId>
- <version>@MAVEN.DEPLOY.VERSION@</version>
- <scope>compile</scope>
- <optional>true</optional>
- </dependency>
- </dependencies>
-</project>
diff --git a/res/maven/tomcat-extras-juli.pom b/res/maven/tomcat-extras-juli.pom
deleted file mode 100644
index 243330f..0000000
--- a/res/maven/tomcat-extras-juli.pom
+++ /dev/null
@@ -1,32 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- 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.
--->
-<project>
- <modelVersion>4.0.0</modelVersion>
- <groupId>org.apache.tomcat.extras</groupId>
- <artifactId>tomcat-extras-juli</artifactId>
- <version>@MAVEN.DEPLOY.VERSION@</version>
- <description>Replacement for Tomcat Core Logging Package</description>
- <url>http://tomcat.apache.org/</url>
- <licenses>
- <license>
- <name>Apache License, Version 2.0</name>
- <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
- <distribution>repo</distribution>
- </license>
- </licenses>
-</project>
diff --git a/res/maven/tomcat-jaspic-api.pom b/res/maven/tomcat-jaspic-api.pom
new file mode 100644
index 0000000..100f5f1
--- /dev/null
+++ b/res/maven/tomcat-jaspic-api.pom
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+-->
+<project>
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>org.apache.tomcat</groupId>
+ <artifactId>tomcat-jaspic-api</artifactId>
+ <version>@MAVEN.DEPLOY.VERSION@</version>
+ <description>javax.security.auth.message package</description>
+ <url>http://tomcat.apache.org/</url>
+ <licenses>
+ <license>
+ <name>Apache License, Version 2.0</name>
+ <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
+ <distribution>repo</distribution>
+ </license>
+ </licenses>
+</project>
diff --git a/res/maven/tomcat-servlet-api.pom b/res/maven/tomcat-servlet-api.pom
index 0f9e1de..f13188c 100644
--- a/res/maven/tomcat-servlet-api.pom
+++ b/res/maven/tomcat-servlet-api.pom
@@ -38,6 +38,7 @@
javaee_5.xsd,
javaee_6.xsd,
javaee_7.xsd,
+ javaee_8.xsd,
javaee_web_services_1_2.xsd,
javaee_web_services_client_1_2.xsd,
javaee_web_services_1_3.xsd,
@@ -50,8 +51,11 @@
web-common_3_0.xsd,
web-fragment_3_0.xsd,
web-app_3_1.xsd,
- web-common_3_1.xsd and
- web-fragment_3_1.xsd
+ web-common_3_1.xsd,
+ web-fragment_3_1.xsd,
+ web-app_4_0.xsd,
+ web-common_4_0.xsd and
+ web-fragment_4_0.xsd
to which the CDDL version 1.0 applies.
</comments>
</license>
diff --git a/res/rat/rat-excludes.txt b/res/rat/rat-excludes.txt
index 6b421d8..d4e743e 100644
--- a/res/rat/rat-excludes.txt
+++ b/res/rat/rat-excludes.txt
@@ -27,7 +27,8 @@
- *.manifest, MANIFEST.MF JAR manifest files cannot contain license
- - package-list files in API documentation (javadoc) are generated
+ - package-list and script.js files in API documentation (javadoc) are
+ generated
- other test files, such as trivial textual files containing only "OK' string
or compressed files are also excluded.
@@ -39,6 +40,8 @@
- file fragments that are combined during the build process and therefore can
not contain a license header in every fragment
+ - JavaEE XML schemas that are CDDL licensed
+
- Checkstyle configuration file that defines how to check for the presence of
ALv2 headers
@@ -48,8 +51,6 @@
(*.gif, *.jpg are also binary, but are automatically detected by RAT as
ones, so no explicit configuration is needed)
- - unused modules
-
output/build/logs/*
output/test-tmp/**
@@ -64,13 +65,22 @@ output/deployer/deployer-howto.html
**/*.manifest
output/dist/webapps/docs/*/package-list
+output/dist/webapps/docs/*/script.js
output/dist/src/test/webapp/index.html.br
output/dist/src/test/webapp/bug53257/*.txt
+output/dist/src/test/webapp/bug53257/foo bar/foobar.txt
output/dist/src/test/webapp-fragments/WEB-INF/classes/*.txt
+output/dist/src/test/webresources/dir1/d1/d1-f1.txt
+output/dist/src/test/webresources/dir1/d2/d2-f1.txt
+output/dist/src/test/webresources/dir1/*.txt
test/webapp/index.html.br
test/webapp/bug53257/*.txt
+test/webapp/bug53257/foo bar/foobar.txt
test/webapp-fragments/WEB-INF/classes/*.txt
+test/webresources/dir1/d1/d1-f1.txt
+test/webresources/dir1/d2/d2-f1.txt
+test/webresources/dir1/*.txt
**/*.json
@@ -80,6 +90,70 @@ output/dist/confinstall/tomcat-users_2.xml
output/dist/src/res/confinstall/tomcat-users_2.xml
res/confinstall/tomcat-users_2.xml
+java/javax/servlet/resources/javaee_5.xsd
+java/javax/servlet/resources/javaee_6.xsd
+java/javax/servlet/resources/javaee_7.xsd
+java/javax/servlet/resources/javaee_8.xsd
+java/javax/servlet/resources/javaee_web_services_1_2.xsd
+java/javax/servlet/resources/javaee_web_services_1_3.xsd
+java/javax/servlet/resources/javaee_web_services_1_4.xsd
+java/javax/servlet/resources/javaee_web_services_client_1_2.xsd
+java/javax/servlet/resources/javaee_web_services_client_1_3.xsd
+java/javax/servlet/resources/javaee_web_services_client_1_4.xsd
+java/javax/servlet/resources/jsp_2_2.xsd
+java/javax/servlet/resources/jsp_2_3.xsd
+java/javax/servlet/resources/web-app_3_0.xsd
+java/javax/servlet/resources/web-app_3_1.xsd
+java/javax/servlet/resources/web-app_4_0.xsd
+java/javax/servlet/resources/web-common_3_0.xsd
+java/javax/servlet/resources/web-common_3_1.xsd
+java/javax/servlet/resources/web-common_4_0.xsd
+java/javax/servlet/resources/web-fragment_3_0.xsd
+java/javax/servlet/resources/web-fragment_3_1.xsd
+java/javax/servlet/resources/web-fragment_4_0.xsd
+output/classes/javax/servlet/resources/javaee_5.xsd
+output/classes/javax/servlet/resources/javaee_6.xsd
+output/classes/javax/servlet/resources/javaee_7.xsd
+output/classes/javax/servlet/resources/javaee_8.xsd
+output/classes/javax/servlet/resources/javaee_web_services_1_2.xsd
+output/classes/javax/servlet/resources/javaee_web_services_1_3.xsd
+output/classes/javax/servlet/resources/javaee_web_services_1_4.xsd
+output/classes/javax/servlet/resources/javaee_web_services_client_1_2.xsd
+output/classes/javax/servlet/resources/javaee_web_services_client_1_3.xsd
+output/classes/javax/servlet/resources/javaee_web_services_client_1_4.xsd
+output/classes/javax/servlet/resources/jsp_2_2.xsd
+output/classes/javax/servlet/resources/jsp_2_3.xsd
+output/classes/javax/servlet/resources/web-app_3_0.xsd
+output/classes/javax/servlet/resources/web-app_3_1.xsd
+output/classes/javax/servlet/resources/web-app_4_0.xsd
+output/classes/javax/servlet/resources/web-common_3_0.xsd
+output/classes/javax/servlet/resources/web-common_3_1.xsd
+output/classes/javax/servlet/resources/web-common_4_0.xsd
+output/classes/javax/servlet/resources/web-fragment_3_0.xsd
+output/classes/javax/servlet/resources/web-fragment_3_1.xsd
+output/classes/javax/servlet/resources/web-fragment_4_0.xsd
+output/dist/src/java/javax/servlet/resources/javaee_5.xsd
+output/dist/src/java/javax/servlet/resources/javaee_6.xsd
+output/dist/src/java/javax/servlet/resources/javaee_7.xsd
+output/dist/src/java/javax/servlet/resources/javaee_8.xsd
+output/dist/src/java/javax/servlet/resources/javaee_web_services_1_2.xsd
+output/dist/src/java/javax/servlet/resources/javaee_web_services_1_3.xsd
+output/dist/src/java/javax/servlet/resources/javaee_web_services_1_4.xsd
+output/dist/src/java/javax/servlet/resources/javaee_web_services_client_1_2.xsd
+output/dist/src/java/javax/servlet/resources/javaee_web_services_client_1_3.xsd
+output/dist/src/java/javax/servlet/resources/javaee_web_services_client_1_4.xsd
+output/dist/src/java/javax/servlet/resources/jsp_2_2.xsd
+output/dist/src/java/javax/servlet/resources/jsp_2_3.xsd
+output/dist/src/java/javax/servlet/resources/web-app_3_0.xsd
+output/dist/src/java/javax/servlet/resources/web-app_3_1.xsd
+output/dist/src/java/javax/servlet/resources/web-app_4_0.xsd
+output/dist/src/java/javax/servlet/resources/web-common_3_0.xsd
+output/dist/src/java/javax/servlet/resources/web-common_3_1.xsd
+output/dist/src/java/javax/servlet/resources/web-common_4_0.xsd
+output/dist/src/java/javax/servlet/resources/web-fragment_3_0.xsd
+output/dist/src/java/javax/servlet/resources/web-fragment_3_1.xsd
+output/dist/src/java/javax/servlet/resources/web-fragment_4_0.xsd
+
output/dist/src/res/checkstyle/header-al2.txt
res/checkstyle/header-al2.txt
@@ -87,5 +161,3 @@ output/dist/temp/safeToDelete.tmp
**/*.bmp
**/*.dia
-
-modules/tomcat-lite/**
diff --git a/res/tomcat.nsi b/res/tomcat.nsi
index a498141..f2d7f87 100644
--- a/res/tomcat.nsi
+++ b/res/tomcat.nsi
@@ -318,7 +318,7 @@ Section -post
${If} $ServiceInstallLog != ""
FileWrite $ServiceInstallLog '"$INSTDIR\bin\$TomcatServiceFileName" //US//$TomcatServiceName --Classpath "$INSTDIR\bin\bootstrap.jar;$INSTDIR\bin\tomcat-juli.jar" --StartClass org.apache.catalina.startup.Bootstrap --StopClass org.apache.catalina.startup.Bootstrap --StartParams start --StopParams stop --StartMode jvm --StopMode jvm'
FileWrite $ServiceInstallLog "$\r$\n"
- FileWrite $ServiceInstallLog '"$INSTDIR\bin\$TomcatServiceFileName" //US//$TomcatServiceName --JvmOptions "-Dcatalina.home=$INSTDIR#-Dcatalina.base=$INSTDIR#-Djava.endorsed.dirs=$INSTDIR\endorsed#-Djava.io.tmpdir=$INSTDIR\temp#-Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager#-Djava.util.logging.config.file=$INSTDIR\conf\logging.properties"'
+ FileWrite $ServiceInstallLog '"$INSTDIR\bin\$TomcatServiceFileName" //US//$TomcatServiceName --JvmOptions "-Dcatalina.home=$INSTDIR#-Dcatalina.base=$INSTDIR#-Djava.io.tmpdir=$INSTDIR\temp#-Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager#-Djava.util.logging.config.file=$INSTDIR\conf\logging.properties"'
FileWrite $ServiceInstallLog "$\r$\n"
FileWrite $ServiceInstallLog '"$INSTDIR\bin\$TomcatServiceFileName" //US//$TomcatServiceName --StdOutput auto --StdError auto --JvmMs 128 --JvmMx 256'
FileWrite $ServiceInstallLog "$\r$\n"
@@ -327,7 +327,7 @@ Section -post
DetailPrint "Configuring $TomcatServiceName service"
nsExec::ExecToLog '"$INSTDIR\bin\$TomcatServiceFileName" //US//$TomcatServiceName --Classpath "$INSTDIR\bin\bootstrap.jar;$INSTDIR\bin\tomcat-juli.jar" --StartClass org.apache.catalina.startup.Bootstrap --StopClass org.apache.catalina.startup.Bootstrap --StartParams start --StopParams stop --StartMode jvm --StopMode jvm'
- nsExec::ExecToLog '"$INSTDIR\bin\$TomcatServiceFileName" //US//$TomcatServiceName --JvmOptions "-Dcatalina.home=$INSTDIR#-Dcatalina.base=$INSTDIR#-Djava.endorsed.dirs=$INSTDIR\endorsed#-Djava.io.tmpdir=$INSTDIR\temp#-Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager#-Djava.util.logging.config.file=$INSTDIR\conf\logging.properties"'
+ nsExec::ExecToLog '"$INSTDIR\bin\$TomcatServiceFileName" //US//$TomcatServiceName --JvmOptions "-Dcatalina.home=$INSTDIR#-Dcatalina.base=$INSTDIR#-Djava.io.tmpdir=$INSTDIR\temp#-Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager#-Djava.util.logging.config.file=$INSTDIR\conf\logging.properties"'
nsExec::ExecToLog '"$INSTDIR\bin\$TomcatServiceFileName" //US//$TomcatServiceName --StdOutput auto --StdError auto --JvmMs 128 --JvmMx 256'
${If} $TomcatShortcutAllUsers == "1"
diff --git a/test/META-INF/services/javax.servlet.ServletContainerInitializer b/test/META-INF/services/javax.servlet.ServletContainerInitializer
index a24853a..6cb967c 100644
--- a/test/META-INF/services/javax.servlet.ServletContainerInitializer
+++ b/test/META-INF/services/javax.servlet.ServletContainerInitializer
@@ -1,2 +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.
+
# SCIs that should be added when running tests
org.apache.jasper.servlet.JasperInitializer
\ No newline at end of file
diff --git a/test/conf/jaspic-test-01.xml b/test/conf/jaspic-test-01.xml
new file mode 100644
index 0000000..8e0adbd
--- /dev/null
+++ b/test/conf/jaspic-test-01.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+-->
+<jaspic-providers xmlns="http://tomcat.apache.org/xml"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://tomcat.apache.org/xml jaspic-providers.xsd"
+ version="1.0">
+</jaspic-providers>
\ No newline at end of file
diff --git a/test/conf/jaspic-test-02.xml b/test/conf/jaspic-test-02.xml
new file mode 100644
index 0000000..3f26d56
--- /dev/null
+++ b/test/conf/jaspic-test-02.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+-->
+<jaspic-providers xmlns="http://tomcat.apache.org/xml"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://tomcat.apache.org/xml jaspic-providers.xsd"
+ version="1.0">
+ <provider className="a" layer="b" appContext="c" description="d">
+ <property name="e" value="f"/>
+ <property name="g" value="h"/>
+ </provider>
+</jaspic-providers>
\ No newline at end of file
diff --git a/test/javax/el/TesterClass.java b/test/javax/el/TesterClass.java
index 24c4555..699b3ec 100644
--- a/test/javax/el/TesterClass.java
+++ b/test/javax/el/TesterClass.java
@@ -18,7 +18,7 @@ package javax.el;
public class TesterClass {
- public static String publicStaticString = "publicStaticString";
+ public static final String publicStaticString = "publicStaticString";
public String publicString = "publicString";
@SuppressWarnings("unused") // Used in TestStaticFieldELResolver
private static String privateStaticString = "privateStaticString";
diff --git a/test/javax/servlet/http/TestCookie.java b/test/javax/servlet/http/TestCookie.java
index 7b447f3..358f4b9 100644
--- a/test/javax/servlet/http/TestCookie.java
+++ b/test/javax/servlet/http/TestCookie.java
@@ -129,9 +129,9 @@ public class TestCookie {
@Test
public void strictNamingImpliesRFC2109() {
- // Not using strict naming here so this should be OK
+ // Needs to be something RFC6265 allows, but strict naming does not.
@SuppressWarnings("unused")
- Cookie cookie = new Cookie("@Foo", null);
+ Cookie cookie = new Cookie("$Foo", null);
}
public static void checkCharInName(CookieNameValidator validator, BitSet allowed) {
diff --git a/test/javax/servlet/http/TestCookieNetscapeValidator.java b/test/javax/servlet/http/TestCookieNetscapeValidator.java
deleted file mode 100644
index 52093c5..0000000
--- a/test/javax/servlet/http/TestCookieNetscapeValidator.java
+++ /dev/null
@@ -1,43 +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 javax.servlet.http;
-
-import java.util.BitSet;
-
-import org.junit.Test;
-
-/**
- * Basic tests for Cookie in default configuration.
- */
-public class TestCookieNetscapeValidator {
-
- private NetscapeValidator validator = new NetscapeValidator();
-
- @Test
- public void actualCharactersAllowedInName() {
- // "any character except comma, semicolon and whitespace"
- // also disallow '=' as that is interpreted as a delimiter by browsers
- BitSet allowed = new BitSet(256);
- allowed.or(TestCookie.CHAR);
- allowed.andNot(TestCookie.CTL);
- allowed.clear(';');
- allowed.clear(',');
- allowed.clear(' ');
- allowed.clear('=');
- TestCookie.checkCharInName(validator, allowed);
- }
-}
diff --git a/test/javax/servlet/http/TestHttpServlet.java b/test/javax/servlet/http/TestHttpServlet.java
index 4f7c69d..feca2c9 100644
--- a/test/javax/servlet/http/TestHttpServlet.java
+++ b/test/javax/servlet/http/TestHttpServlet.java
@@ -71,7 +71,7 @@ public class TestHttpServlet extends TomcatBaseTest {
}
- /**
+ /*
* Verifies that the same Content-Length is returned for both GET and HEAD
* operations when a Servlet includes content from another Servlet
*/
diff --git a/test/org/apache/catalina/authenticator/ResponseDescriptor.java b/test/org/apache/catalina/authenticator/ResponseDescriptor.java
new file mode 100644
index 0000000..d79a42c
--- /dev/null
+++ b/test/org/apache/catalina/authenticator/ResponseDescriptor.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.catalina.authenticator;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * This class incorporates test response data
+ */
+class ResponseDescriptor {
+ private Map<String, List<String>> headers;
+ private String body;
+ private int responseCode;
+
+
+ public Map<String, List<String>> getHeaders() {
+ return headers;
+ }
+
+
+ public void setHeaders(Map<String, List<String>> headers) {
+ this.headers = headers;
+ }
+
+
+ public String getBody() {
+ return body;
+ }
+
+
+ public void setBody(String body) {
+ this.body = body;
+ }
+
+
+ public int getResponseCode() {
+ return responseCode;
+ }
+
+
+ public void setResponseCode(int responseCode) {
+ this.responseCode = responseCode;
+ }
+}
\ No newline at end of file
diff --git a/test/org/apache/catalina/authenticator/TestDigestAuthenticator.java b/test/org/apache/catalina/authenticator/TestDigestAuthenticator.java
index f257051..bea39b4 100644
--- a/test/org/apache/catalina/authenticator/TestDigestAuthenticator.java
+++ b/test/org/apache/catalina/authenticator/TestDigestAuthenticator.java
@@ -37,6 +37,7 @@ import org.apache.catalina.startup.TesterServlet;
import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.startup.TomcatBaseTest;
import org.apache.tomcat.unittest.TesterContext;
+import org.apache.tomcat.unittest.TesterServletContext;
import org.apache.tomcat.util.buf.ByteChunk;
import org.apache.tomcat.util.descriptor.web.LoginConfig;
import org.apache.tomcat.util.descriptor.web.SecurityCollection;
@@ -63,7 +64,9 @@ public class TestDigestAuthenticator extends TomcatBaseTest {
@Test
public void bug54521() throws LifecycleException {
DigestAuthenticator digestAuthenticator = new DigestAuthenticator();
- digestAuthenticator.setContainer(new TesterContext());
+ TesterContext context = new TesterContext();
+ context.setServletContext(new TesterServletContext());
+ digestAuthenticator.setContainer(context);
digestAuthenticator.start();
Request request = new TesterRequest();
final int count = 1000;
@@ -245,7 +248,6 @@ public class TestDigestAuthenticator extends TomcatBaseTest {
// Third request should succeed if we increment nc
auth.clear();
bc.recycle();
- bc.reset();
auth.add(buildDigestResponse(user, pwd, digestUri, realm,
getNonce(respHeaders), getOpaque(respHeaders), nc2, cnonce,
qop));
diff --git a/test/org/apache/catalina/authenticator/TestFormAuthenticator.java b/test/org/apache/catalina/authenticator/TestFormAuthenticator.java
index 4f9de86..bf2df01 100644
--- a/test/org/apache/catalina/authenticator/TestFormAuthenticator.java
+++ b/test/org/apache/catalina/authenticator/TestFormAuthenticator.java
@@ -95,9 +95,8 @@ public class TestFormAuthenticator extends TomcatBaseTest {
protected static final boolean SERVER_FREEZE_SESSID = !SERVER_CHANGE_SESSID;
// minimum session timeout
- private static final int TIMEOUT_MINS = 1;
- private static final long TIMEOUT_DELAY_MSECS =
- (((TIMEOUT_MINS * 60) + 10) * 1000);
+ private static final int SHORT_SESSION_TIMEOUT_SECS = 1;
+ private static final long TIMEOUT_DELAY_MSECS = ((SHORT_SESSION_TIMEOUT_SECS + 10) * 1000);
private FormAuthClient client;
@@ -239,6 +238,10 @@ public class TestFormAuthenticator extends TomcatBaseTest {
CLIENT_NO_COOKIES, SERVER_USE_COOKIES,
SERVER_FREEZE_SESSID);
+ // Force session to expire one second from now
+ Context context = (Context) getTomcatInstance().getHost().findChildren()[0];
+ forceSessionMaxInactiveInterval(context, SHORT_SESSION_TIMEOUT_SECS);
+
// wait long enough for my session to expire
Thread.sleep(TIMEOUT_DELAY_MSECS);
@@ -656,9 +659,6 @@ public class TestFormAuthenticator extends TomcatBaseTest {
tomcat.start();
- // perhaps this does not work until tomcat has started?
- ctx.setSessionTimeout(TIMEOUT_MINS);
-
// Valve pipeline is only established after tomcat starts
Valve[] valves = ctx.getPipeline().getValves();
for (Valve valve : valves) {
@@ -734,9 +734,6 @@ public class TestFormAuthenticator extends TomcatBaseTest {
tomcat.start();
- // perhaps this does not work until tomcat has started?
- ctx.setSessionTimeout(TIMEOUT_MINS);
-
// Valve pipeline is only established after tomcat starts
Valve[] valves = ctx.getPipeline().getValves();
for (Valve valve : valves) {
diff --git a/test/org/apache/catalina/authenticator/TestNonLoginAndBasicAuthenticator.java b/test/org/apache/catalina/authenticator/TestNonLoginAndBasicAuthenticator.java
index 1cc6440..bb23f37 100644
--- a/test/org/apache/catalina/authenticator/TestNonLoginAndBasicAuthenticator.java
+++ b/test/org/apache/catalina/authenticator/TestNonLoginAndBasicAuthenticator.java
@@ -75,15 +75,14 @@ public class TestNonLoginAndBasicAuthenticator extends TomcatBaseTest {
private static final String URI_PROTECTED = "/protected";
private static final String URI_PUBLIC = "/anyoneCanAccess";
- private static final int SHORT_SESSION_TIMEOUT_MINS = 1;
- private static final int LONG_SESSION_TIMEOUT_MINS = 2;
- private static final int MANAGER_SCAN_INTERVAL_SECS = 10;
+ private static final int SHORT_SESSION_TIMEOUT_SECS = 1;
+ 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_MINS * 60)
- + (MANAGER_SCAN_INTERVAL_SECS * MANAGER_EXPIRE_SESSIONS_FAST)
- + EXTRA_DELAY_SECS) * 1000);
+ ((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";
@@ -287,6 +286,11 @@ public class TestNonLoginAndBasicAuthenticator extends TomcatBaseTest {
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),
+ SHORT_SESSION_TIMEOUT_SECS);
+
// allow the session to time out and lose authentication
Thread.sleep(TIMEOUT_DELAY_MSECS);
@@ -356,8 +360,8 @@ public class TestNonLoginAndBasicAuthenticator extends TomcatBaseTest {
Map<String,List<String>> reqHeaders = new HashMap<>();
Map<String,List<String>> respHeaders = new HashMap<>();
- if (useCookie && (cookies != null)) {
- reqHeaders.put(CLIENT_COOKIE_HEADER, cookies);
+ if (useCookie) {
+ addCookies(reqHeaders);
}
ByteChunk bc = new ByteChunk();
@@ -379,8 +383,8 @@ public class TestNonLoginAndBasicAuthenticator extends TomcatBaseTest {
Map<String,List<String>> reqHeaders = new HashMap<>();
Map<String,List<String>> respHeaders = new HashMap<>();
- if (useCookie && (cookies != null)) {
- reqHeaders.put(CLIENT_COOKIE_HEADER, cookies);
+ if (useCookie) {
+ addCookies(reqHeaders);
}
else {
if (credentials != null) {
@@ -415,7 +419,7 @@ public class TestNonLoginAndBasicAuthenticator extends TomcatBaseTest {
List<String> newCookies = respHeaders.get(SERVER_COOKIE_HEADER);
if (newCookies != null) {
// harvest cookies whenever the server sends some new ones
- cookies = newCookies;
+ saveCookies(respHeaders);
}
}
}
@@ -451,7 +455,6 @@ public class TestNonLoginAndBasicAuthenticator extends TomcatBaseTest {
// Must have a real docBase for webapps - just use temp
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 TesterServlet());
@@ -488,7 +491,6 @@ public class TestNonLoginAndBasicAuthenticator extends TomcatBaseTest {
// Must have a real docBase for webapps - just use temp
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 TesterServlet());
@@ -536,7 +538,8 @@ public class TestNonLoginAndBasicAuthenticator extends TomcatBaseTest {
* 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);
}
@@ -568,4 +571,41 @@ public class TestNonLoginAndBasicAuthenticator extends TomcatBaseTest {
return credentials;
}
}
-}
+
+ /*
+ * extract and save the server cookies from the incoming response
+ */
+ protected void saveCookies(Map<String,List<String>> respHeaders) {
+ // we only save the Cookie values, not header prefix
+ List<String> 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(';')));
+ }
+ }
+ }
+
+ /*
+ * add all saved cookies to the outgoing request
+ */
+ protected void addCookies(Map<String,List<String>> 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<String> cookieHeaderList = new ArrayList<>(1);
+ cookieHeaderList.add(cookieHeader.toString());
+ reqHeaders.put(CLIENT_COOKIE_HEADER, cookieHeaderList);
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/org/apache/catalina/authenticator/TestSSOnonLoginAndBasicAuthenticator.java b/test/org/apache/catalina/authenticator/TestSSOnonLoginAndBasicAuthenticator.java
index 128bad8..8adb213 100644
--- a/test/org/apache/catalina/authenticator/TestSSOnonLoginAndBasicAuthenticator.java
+++ b/test/org/apache/catalina/authenticator/TestSSOnonLoginAndBasicAuthenticator.java
@@ -355,7 +355,7 @@ public class TestSSOnonLoginAndBasicAuthenticator extends TomcatBaseTest {
Map<String,List<String>> respHeaders = new HashMap<>();
if (useCookie && (cookies != null)) {
- reqHeaders.put(CLIENT_COOKIE_HEADER, cookies);
+ addCookies(reqHeaders);
}
ByteChunk bc = new ByteChunk();
@@ -379,7 +379,7 @@ public class TestSSOnonLoginAndBasicAuthenticator extends TomcatBaseTest {
Map<String,List<String>> respHeaders = new HashMap<>();
if (useCookie && (cookies != null)) {
- reqHeaders.put(CLIENT_COOKIE_HEADER, cookies);
+ addCookies(reqHeaders);
}
else {
if (credentials != null) {
@@ -554,18 +554,36 @@ public class TestSSOnonLoginAndBasicAuthenticator extends TomcatBaseTest {
* extract and save the server cookies from the incoming response
*/
protected void saveCookies(Map<String,List<String>> respHeaders) {
-
// we only save the Cookie values, not header prefix
- cookies = respHeaders.get(SERVER_COOKIE_HEADER);
+ List<String> 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(';')));
+ }
+ }
}
/*
* add all saved cookies to the outgoing request
*/
protected void addCookies(Map<String,List<String>> reqHeaders) {
-
if ((cookies != null) && (cookies.size() > 0)) {
- reqHeaders.put(CLIENT_COOKIE_HEADER, cookies);
+ StringBuilder cookieHeader = new StringBuilder();
+ boolean first = true;
+ for (String cookie : cookies) {
+ if (!first) {
+ cookieHeader.append(';');
+ } else {
+ first = false;
+ }
+ cookieHeader.append(cookie);
+ }
+ List<String> cookieHeaderList = new ArrayList<>(1);
+ cookieHeaderList.add(cookieHeader.toString());
+ reqHeaders.put(CLIENT_COOKIE_HEADER, cookieHeaderList);
}
}
@@ -666,4 +684,4 @@ public class TestSSOnonLoginAndBasicAuthenticator extends TomcatBaseTest {
return credentials;
}
}
-}
+}
\ No newline at end of file
diff --git a/test/org/apache/catalina/authenticator/TestSSOnonLoginAndDigestAuthenticator.java b/test/org/apache/catalina/authenticator/TestSSOnonLoginAndDigestAuthenticator.java
index ead14d2..775321c 100644
--- a/test/org/apache/catalina/authenticator/TestSSOnonLoginAndDigestAuthenticator.java
+++ b/test/org/apache/catalina/authenticator/TestSSOnonLoginAndDigestAuthenticator.java
@@ -470,16 +470,35 @@ public class TestSSOnonLoginAndDigestAuthenticator extends TomcatBaseTest {
protected void saveCookies(Map<String,List<String>> respHeaders) {
// we only save the Cookie values, not header prefix
- cookies = respHeaders.get(SERVER_COOKIES);
+ List<String> cookieHeaders = respHeaders.get(SERVER_COOKIES);
+ if (cookieHeaders == null) {
+ cookies = null;
+ } else {
+ cookies = new ArrayList<>(cookieHeaders.size());
+ for (String cookieHeader : cookieHeaders) {
+ cookies.add(cookieHeader.substring(0, cookieHeader.indexOf(';')));
+ }
+ }
}
/*
* add all saved cookies to the outgoing request
*/
protected void addCookies(Map<String,List<String>> reqHeaders) {
-
if ((cookies != null) && (cookies.size() > 0)) {
- reqHeaders.put(BROWSER_COOKIES, cookies);
+ StringBuilder cookieHeader = new StringBuilder();
+ boolean first = true;
+ for (String cookie : cookies) {
+ if (!first) {
+ cookieHeader.append(';');
+ } else {
+ first = false;
+ }
+ cookieHeader.append(cookie);
+ }
+ List<String> cookieHeaderList = new ArrayList<>(1);
+ cookieHeaderList.add(cookieHeader.toString());
+ reqHeaders.put(BROWSER_COOKIES, cookieHeaderList);
}
}
-}
+}
\ No newline at end of file
diff --git a/test/org/apache/catalina/authenticator/jaspic/TestPersistentProviderRegistrations.java b/test/org/apache/catalina/authenticator/jaspic/TestPersistentProviderRegistrations.java
new file mode 100644
index 0000000..5728e4b
--- /dev/null
+++ b/test/org/apache/catalina/authenticator/jaspic/TestPersistentProviderRegistrations.java
@@ -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.catalina.authenticator.jaspic;
+
+import java.io.File;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import org.apache.catalina.authenticator.jaspic.PersistentProviderRegistrations.Provider;
+import org.apache.catalina.authenticator.jaspic.PersistentProviderRegistrations.Providers;
+
+public class TestPersistentProviderRegistrations {
+
+ @Test
+ public void testLoadEmpty() {
+ File f = new File("test/conf/jaspic-test-01.xml");
+ Providers result = PersistentProviderRegistrations.loadProviders(f);
+ Assert.assertEquals(0, result.getProviders().size());
+ }
+
+
+ @Test
+ public void testLoadSimple() {
+ File f = new File("test/conf/jaspic-test-02.xml");
+ Providers result = PersistentProviderRegistrations.loadProviders(f);
+ validateSimple(result);
+ }
+
+
+ private void validateSimple(Providers providers) {
+ 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("f", p.getProperties().get("e"));
+ Assert.assertEquals("h", p.getProperties().get("g"));
+ }
+
+
+ @Test
+ public void testSaveSimple() {
+ File f = new File("test/conf/jaspic-test-03.xml");
+ if (f.exists()) {
+ Assert.assertTrue(f.delete());
+ }
+
+ // Create a config and write it out
+ Providers start = new Providers();
+ Provider p = new Provider();
+ p.setClassName("a");
+ p.setLayer("b");
+ p.setAppContext("c");
+ p.setDescription("d");
+ p.addProperty("e", "f");
+ p.addProperty("g", "h");
+ start.addProvider(p);
+ PersistentProviderRegistrations.writeProviders(start, f);
+
+ // Read it back
+ Providers end = PersistentProviderRegistrations.loadProviders(f);
+
+ validateSimple(end);
+
+ if (f.exists()) {
+ f.delete();
+ }
+ }
+}
diff --git a/test/org/apache/catalina/authenticator/jaspic/TestSimpleServerAuthConfig.java b/test/org/apache/catalina/authenticator/jaspic/TestSimpleServerAuthConfig.java
new file mode 100644
index 0000000..b663c87
--- /dev/null
+++ b/test/org/apache/catalina/authenticator/jaspic/TestSimpleServerAuthConfig.java
@@ -0,0 +1,74 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.catalina.authenticator.jaspic;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.security.auth.message.AuthException;
+import javax.security.auth.message.MessageInfo;
+import javax.security.auth.message.config.ServerAuthConfig;
+import javax.security.auth.message.config.ServerAuthContext;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class TestSimpleServerAuthConfig {
+
+ private static final String SERVER_AUTH_MODULE_KEY_PREFIX =
+ "org.apache.catalina.authenticator.jaspic.ServerAuthModule.";
+
+ private static final Map<String,String> CONFIG_PROPERTIES;
+ static {
+ CONFIG_PROPERTIES = new HashMap<>();
+ 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);
+ ServerAuthContext serverAuthContext = serverAuthConfig.getAuthContext(null, null, null);
+
+ validateServerAuthContext(serverAuthContext);
+ }
+
+
+ @Test
+ public void testConfigOnGetAuthContext() throws Exception {
+ ServerAuthConfig serverAuthConfig = new SimpleServerAuthConfig(null, null, null, null);
+ ServerAuthContext serverAuthContext =
+ serverAuthConfig.getAuthContext(null, null, CONFIG_PROPERTIES);
+
+ validateServerAuthContext(serverAuthContext);
+ }
+
+
+ @Test(expected=AuthException.class)
+ public void testConfigNone() throws Exception {
+ ServerAuthConfig serverAuthConfig = new SimpleServerAuthConfig(null, null, null, null);
+ serverAuthConfig.getAuthContext(null, null, null);
+ }
+
+
+ private void validateServerAuthContext(ServerAuthContext serverAuthContext) throws Exception {
+ MessageInfo msgInfo = new TesterMessageInfo();
+ serverAuthContext.cleanSubject(msgInfo, null);
+ Assert.assertEquals("init()-cleanSubject()-", msgInfo.getMap().get("trace"));
+ }
+}
diff --git a/test/org/apache/catalina/authenticator/jaspic/TesterMessageInfo.java b/test/org/apache/catalina/authenticator/jaspic/TesterMessageInfo.java
new file mode 100644
index 0000000..ba8592e
--- /dev/null
+++ b/test/org/apache/catalina/authenticator/jaspic/TesterMessageInfo.java
@@ -0,0 +1,55 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.catalina.authenticator.jaspic;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.security.auth.message.MessageInfo;
+
+public class TesterMessageInfo implements MessageInfo {
+
+ private Object requestMessage;
+ private Object responseMessage;
+ private final Map<String,String> map = new HashMap<>();
+
+ @Override
+ public Object getRequestMessage() {
+ return requestMessage;
+ }
+
+ @Override
+ public Object getResponseMessage() {
+ return responseMessage;
+ }
+
+ @Override
+ public void setRequestMessage(Object request) {
+ requestMessage = request;
+ }
+
+ @Override
+ public void setResponseMessage(Object response) {
+ responseMessage = response;
+ }
+
+ @SuppressWarnings("rawtypes")
+ @Override
+ public Map getMap() {
+ return map;
+ }
+}
diff --git a/test/org/apache/catalina/authenticator/jaspic/TesterServerAuthModuleA.java b/test/org/apache/catalina/authenticator/jaspic/TesterServerAuthModuleA.java
new file mode 100644
index 0000000..e1bf7fe
--- /dev/null
+++ b/test/org/apache/catalina/authenticator/jaspic/TesterServerAuthModuleA.java
@@ -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.catalina.authenticator.jaspic;
+
+import java.util.Map;
+
+import javax.security.auth.Subject;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.message.AuthException;
+import javax.security.auth.message.AuthStatus;
+import javax.security.auth.message.MessageInfo;
+import javax.security.auth.message.MessagePolicy;
+import javax.security.auth.message.module.ServerAuthModule;
+
+public class TesterServerAuthModuleA implements ServerAuthModule {
+
+ private StringBuilder trace = new StringBuilder("init()-");
+
+ @Override
+ public AuthStatus validateRequest(MessageInfo messageInfo, Subject clientSubject,
+ Subject serviceSubject) throws AuthException {
+ return null;
+ }
+
+ @Override
+ public AuthStatus secureResponse(MessageInfo messageInfo, Subject serviceSubject)
+ throws AuthException {
+ return null;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public void cleanSubject(MessageInfo messageInfo, Subject subject) throws AuthException {
+ trace.append("cleanSubject()-");
+ messageInfo.getMap().put("trace", trace.toString());
+ }
+
+ @Override
+ public void initialize(MessagePolicy requestPolicy, MessagePolicy responsePolicy,
+ CallbackHandler handler, @SuppressWarnings("rawtypes") Map options)
+ throws AuthException {
+ // NO-OP
+ }
+
+ @SuppressWarnings("rawtypes")
+ @Override
+ public Class[] getSupportedMessageTypes() {
+ return null;
+ }
+}
diff --git a/test/org/apache/catalina/comet/TestCometProcessor.java b/test/org/apache/catalina/comet/TestCometProcessor.java
deleted file mode 100644
index 82d9b17..0000000
--- a/test/org/apache/catalina/comet/TestCometProcessor.java
+++ /dev/null
@@ -1,672 +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.comet;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.net.Socket;
-
-import javax.net.SocketFactory;
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServlet;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import javax.servlet.http.HttpSession;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
-import org.junit.Assert;
-import org.junit.Assume;
-import org.junit.Test;
-
-import org.apache.catalina.Context;
-import org.apache.catalina.Wrapper;
-import org.apache.catalina.comet.CometEvent.EventType;
-import org.apache.catalina.connector.CometEventImpl;
-import org.apache.catalina.connector.Request;
-import org.apache.catalina.connector.Response;
-import org.apache.catalina.startup.Tomcat;
-import org.apache.catalina.startup.TomcatBaseTest;
-import org.apache.catalina.valves.TesterAccessLogValve;
-import org.apache.catalina.valves.ValveBase;
-
-public class TestCometProcessor extends TomcatBaseTest {
-
- @Test
- public void testAsyncClose() throws Exception {
- Assume.assumeTrue(
- "This test is skipped, because this connector does not support Comet.",
- isCometSupported());
-
- // Setup Tomcat instance
- Tomcat tomcat = getTomcatInstance();
- Context root = tomcat.addContext("", TEMP_DIR);
- Tomcat.addServlet(root, "comet", new SimpleCometServlet());
- root.addServletMappingDecoded("/comet", "comet");
- Tomcat.addServlet(root, "hello", new HelloWorldServlet());
- root.addServletMappingDecoded("/hello", "hello");
- root.getPipeline().addValve(new AsyncCometCloseValve());
- tomcat.getConnector().setProperty("connectionTimeout", "5000");
- tomcat.start();
-
- // Create connection to Comet servlet
- final Socket socket =
- SocketFactory.getDefault().createSocket("localhost", getPort());
- socket.setSoTimeout(5000);
-
- final OutputStream os = socket.getOutputStream();
- String requestLine = "POST http://localhost:" + getPort() +
- "/comet HTTP/1.1\r\n";
- os.write(requestLine.getBytes());
- os.write("transfer-encoding: chunked\r\n".getBytes());
- os.write("\r\n".getBytes());
-
- InputStream is = socket.getInputStream();
- ResponseReaderThread readThread = new ResponseReaderThread(is);
- readThread.start();
-
- // Wait for the comet request/response to finish
- int count = 0;
- while (count < 10 && !readThread.getResponse().endsWith("0\r\n\r\n")) {
- Thread.sleep(500);
- count++;
- }
-
- if (count == 10) {
- fail("Comet request did not complete");
- }
-
- // Send a standard HTTP request on the same connection
- requestLine = "GET http://localhost:" + getPort() +
- "/hello HTTP/1.1\r\n";
- os.write(requestLine.getBytes());
- os.write("\r\n".getBytes());
-
- // Check for the expected response
- count = 0;
- while (count < 10 && !readThread.getResponse().contains(
- HelloWorldServlet.RESPONSE_TEXT)) {
- Thread.sleep(500);
- count++;
- }
-
- if (count == 10) {
- fail("Non-comet request did not complete");
- }
-
- readThread.join();
- os.close();
- is.close();
- }
-
- @Test
- public void testSyncClose() throws Exception {
- Assume.assumeTrue(
- "This test is skipped, because this connector does not support Comet.",
- isCometSupported());
-
- // Setup Tomcat instance
- Tomcat tomcat = getTomcatInstance();
- Context root = tomcat.addContext("", TEMP_DIR);
- Tomcat.addServlet(root, "comet", new CometCloseServlet());
- root.addServletMappingDecoded("/comet", "comet");
- Tomcat.addServlet(root, "hello", new HelloWorldServlet());
- root.addServletMappingDecoded("/hello", "hello");
- tomcat.getConnector().setProperty("connectionTimeout", "5000");
- tomcat.start();
-
- // Create connection to Comet servlet
- final Socket socket =
- SocketFactory.getDefault().createSocket("localhost", getPort());
- socket.setSoTimeout(5000);
-
- final OutputStream os = socket.getOutputStream();
- String requestLine = "POST http://localhost:" + getPort() +
- "/comet HTTP/1.1\r\n";
- os.write(requestLine.getBytes());
- os.write("transfer-encoding: chunked\r\n".getBytes());
- os.write("\r\n".getBytes());
- // Don't send any data
- os.write("0\r\n\r\n".getBytes());
-
- InputStream is = socket.getInputStream();
- ResponseReaderThread readThread = new ResponseReaderThread(is);
- readThread.start();
-
- // Wait for the comet request/response to finish
- int count = 0;
- while (count < 10 && !readThread.getResponse().endsWith("0\r\n\r\n")) {
- Thread.sleep(500);
- count++;
- }
-
- Assert.assertTrue(readThread.getResponse().contains("2\r\nOK"));
-
- if (count == 10) {
- fail("Comet request did not complete");
- }
-
- // Send a standard HTTP request on the same connection
- requestLine = "GET http://localhost:" + getPort() +
- "/hello HTTP/1.1\r\n";
- os.write(requestLine.getBytes());
- os.write("connection: close\r\n".getBytes());
- os.write("\r\n".getBytes());
-
- // Check for the expected response
- count = 0;
- while (count < 10 && !readThread.getResponse().contains(
- HelloWorldServlet.RESPONSE_TEXT)) {
- Thread.sleep(500);
- count++;
- }
-
- if (count == 10) {
- fail("Non-comet request did not complete");
- }
-
- readThread.join();
- os.close();
- is.close();
- }
-
- @Test
- public void testConnectionClose() throws Exception {
- Assume.assumeTrue(
- "This test is skipped, because this connector does not support Comet.",
- isCometSupported());
-
- // Setup Tomcat instance
- Tomcat tomcat = getTomcatInstance();
- Context root = tomcat.addContext("", TEMP_DIR);
- Tomcat.addServlet(root, "comet", new ConnectionCloseServlet());
- root.addServletMappingDecoded("/comet", "comet");
- Tomcat.addServlet(root, "hello", new HelloWorldServlet());
- root.addServletMappingDecoded("/hello", "hello");
- tomcat.getConnector().setProperty("connectionTimeout", "5000");
- tomcat.start();
-
- // Create connection to Comet servlet
- final Socket socket =
- SocketFactory.getDefault().createSocket("localhost", getPort());
- socket.setSoTimeout(5000);
-
- final OutputStream os = socket.getOutputStream();
- String requestLine = "POST http://localhost:" + getPort() +
- "/comet HTTP/1.1\r\n";
- os.write(requestLine.getBytes());
- os.write("transfer-encoding: chunked\r\n".getBytes());
- os.write("\r\n".getBytes());
- // Don't send any data
- os.write("0\r\n\r\n".getBytes());
-
- InputStream is = socket.getInputStream();
- ResponseReaderThread readThread = new ResponseReaderThread(is);
- readThread.start();
-
- // Wait for the comet request/response to finish
- int count = 0;
- while (count < 10 && !readThread.getResponse().endsWith("OK")) {
- Thread.sleep(500);
- count++;
- }
-
- if (count == 10) {
- fail("Comet request did not complete");
- }
-
- // Read thread should have terminated cleanly when the server closed the
- // socket
- Assert.assertFalse(readThread.isAlive());
- Assert.assertNull(readThread.getException());
-
- os.close();
- is.close();
- }
-
- @Test
- public void testSimpleCometClient() throws Exception {
- doSimpleCometTest(null);
- }
-
- @Test
- public void testSimpleCometClientBeginFail() throws Exception {
- doSimpleCometTest(SimpleCometServlet.FAIL_ON_BEGIN);
- }
-
- @Test
- public void testSimpleCometClientReadFail() throws Exception {
- doSimpleCometTest(SimpleCometServlet.FAIL_ON_READ);
- }
-
- @Test
- public void testSimpleCometClientEndFail() throws Exception {
- doSimpleCometTest(SimpleCometServlet.FAIL_ON_END);
- }
-
- private void doSimpleCometTest(String initParam) throws Exception {
- Assume.assumeTrue(
- "This test is skipped, because this connector does not support Comet.",
- isCometSupported());
-
- // Setup Tomcat instance
- Tomcat tomcat = getTomcatInstance();
- Context root = tomcat.addContext("", TEMP_DIR);
- Wrapper w = Tomcat.addServlet(root, "comet", new SimpleCometServlet());
- if (initParam != null) {
- w.addInitParameter(initParam, "true");
- }
- root.addServletMappingDecoded("/", "comet");
-
- TesterAccessLogValve alv = new TesterAccessLogValve();
- root.getPipeline().addValve(alv);
-
- tomcat.start();
-
- // Create connection to Comet servlet
- final Socket socket =
- SocketFactory.getDefault().createSocket("localhost", getPort());
- socket.setSoTimeout(60000);
-
- final OutputStream os = socket.getOutputStream();
- String requestLine = "POST http://localhost:" + getPort() +
- "/ HTTP/1.1\r\n";
- os.write(requestLine.getBytes());
- os.write("transfer-encoding: chunked\r\n".getBytes());
- os.write("\r\n".getBytes());
-
- PingWriterThread writeThread = new PingWriterThread(4, os);
- writeThread.start();
-
- socket.setSoTimeout(25000);
- InputStream is = socket.getInputStream();
- ResponseReaderThread readThread = new ResponseReaderThread(is);
- readThread.start();
- readThread.join();
- os.close();
- is.close();
-
- String[] response = readThread.getResponse().split("\r\n");
- if (initParam == null) {
- // Normal response expected
- // Validate response
- assertEquals("HTTP/1.1 200 OK", response[0]);
- assertEquals("Server: Apache-Coyote/1.1", response[1]);
- assertTrue(response[2].startsWith("Set-Cookie: JSESSIONID="));
- assertEquals("Content-Type: text/plain;charset=ISO-8859-1", response[3]);
- assertEquals("Transfer-Encoding: chunked", response[4]);
- assertTrue(response[5].startsWith("Date: "));
- assertEquals("", response[6]);
- assertEquals("7", response[7]);
- assertEquals("BEGIN", response[8]);
- assertEquals("", response[9]);
- assertEquals("17", response[10]);
- assertEquals("Client: READ: 4 bytes", response[11]);
- assertEquals("", response[12]);
- assertEquals("17", response[13]);
- assertEquals("Client: READ: 4 bytes", response[14]);
- assertEquals("", response[15]);
- assertEquals("17", response[16]);
- assertEquals("Client: READ: 4 bytes", response[17]);
- assertEquals("", response[18]);
- assertEquals("17", response[19]);
- assertEquals("Client: READ: 4 bytes", response[20]);
- assertEquals("", response[21]);
- assertEquals("d", response[22]);
- assertEquals("Client: END", response[23]);
- assertEquals("", response[24]);
- assertEquals("0", response[25]);
- // Expect 26 lines
- assertEquals(26, response.length);
- } else {
- // Failure expected only expected for the fail on begin
- // Failure at any later stage and the response headers (including
- // the 200 response code will already have been sent to the client
- if (SimpleCometServlet.FAIL_ON_BEGIN.equals(initParam)) {
- assertEquals("500", getStatusCode(response[0]));
- alv.validateAccessLog(1, 500, 0, 1000);
- } else {
- assertEquals("HTTP/1.1 200 OK", response[0]);
- alv.validateAccessLog(1, 200, 0, 5000);
- }
-
- }
- }
-
- /*
- * Tests if the Comet connection is closed if the Tomcat connector is
- * stopped.
- */
- @Test
- public void testCometConnectorStop() throws Exception {
- Assume.assumeTrue(
- "This test is skipped, because this connector does not support Comet.",
- isCometSupported());
-
- // Setup Tomcat instance
- SimpleCometServlet servlet = new SimpleCometServlet();
- Tomcat tomcat = getTomcatInstance();
- Context root = tomcat.addContext("", TEMP_DIR);
- Tomcat.addServlet(root, "comet", servlet);
- root.addServletMappingDecoded("/", "comet");
- tomcat.start();
-
- // Create connection to Comet servlet
- final Socket socket =
- SocketFactory.getDefault().createSocket("localhost", getPort());
- socket.setSoTimeout(10000);
-
- final OutputStream os = socket.getOutputStream();
- String requestLine = "POST http://localhost:" + getPort() +
- "/ HTTP/1.1\r\n";
- os.write(requestLine.getBytes());
- os.write("transfer-encoding: chunked\r\n".getBytes());
- os.write("\r\n".getBytes());
-
- PingWriterThread writeThread = new PingWriterThread(100, os);
- writeThread.start();
-
- InputStream is = socket.getInputStream();
- ResponseReaderThread readThread = new ResponseReaderThread(is);
- readThread.start();
-
- // Allow the first couple of PING messages to be written
- Thread.sleep(3000);
-
- tomcat.getConnector().stop();
-
- // Wait for the read and write threads to stop
- readThread.join(5000);
- writeThread.join(5000);
-
- // Destroy the connector once the executor has sent the end event
- tomcat.getConnector().destroy();
-
- String[] response = readThread.getResponse().split("\r\n");
- String lastMessage = "";
- String lastResponseLine = "";
- for (int i = response.length; --i >= 0;) {
- lastMessage = response[i];
- if (lastMessage.startsWith("Client:")) {
- break;
- }
- }
- for (int i = response.length; --i >= 0;) {
- lastResponseLine = response[i];
- if (lastResponseLine.length() > 0) {
- break;
- }
- }
- StringBuilder status = new StringBuilder();
- // Expected, but is not 100% reliable:
- // WriteThread exception: java.net.SocketException
- // ReaderThread exception: null
- // Last message: [Client: END]
- // Last response line: [0] (empty chunk)
- // Last comet event: [END]
- // END event occurred: [true]
- status.append("Status:");
- status.append("\nWriterThread exception: " + writeThread.getException());
- status.append("\nReaderThread exception: " + readThread.getException());
- status.append("\nLast message: [" + lastMessage + "]");
- status.append("\nLast response line: [" + lastResponseLine + "]");
- status.append("\nLast comet event: [" + servlet.getLastEvent() + "]");
- status.append("\nEND event occurred: [" + servlet.getEndEventOccurred() + "]");
- if (writeThread.getException() == null
- || !lastMessage.contains("Client: END")
- || !EventType.END.equals(servlet.getLastEvent())) {
- log.error(status);
- } else {
- log.info(status);
- }
- assertTrue("Comet END event not received", servlet.getEndEventOccurred());
- assertTrue("Comet END event not last event received",
- EventType.END.equals(servlet.getLastEvent()));
- }
-
- private boolean isCometSupported() {
- String protocol =
- getTomcatInstance().getConnector().getProtocolHandlerClassName();
- return (protocol.contains("Nio") || protocol.contains("Apr"));
- }
-
- private static class SimpleCometServlet extends HttpServlet
- implements CometProcessor {
-
- private static final long serialVersionUID = 1L;
-
- public static final String FAIL_ON_BEGIN = "failOnBegin";
- public static final String FAIL_ON_READ = "failOnRead";
- public static final String FAIL_ON_END = "failOnEnd";
-
- private boolean failOnBegin = false;
- private boolean failOnRead = false;
- private boolean failOnEnd = false;
-
- private volatile EventType lastEvent;
-
- private volatile boolean endEventOccurred = false;
-
- public EventType getLastEvent() {
- return lastEvent;
- }
-
- public boolean getEndEventOccurred() {
- return endEventOccurred;
- }
-
- @Override
- public void init() throws ServletException {
- failOnBegin = Boolean.parseBoolean(getServletConfig().getInitParameter(
- FAIL_ON_BEGIN));
- failOnRead = Boolean.parseBoolean(getServletConfig().getInitParameter(
- FAIL_ON_READ));
- failOnEnd = Boolean.parseBoolean(getServletConfig().getInitParameter(
- FAIL_ON_END));
- }
-
-
- @Override
- public void event(CometEvent event) throws IOException,
- ServletException {
-
- HttpServletRequest request = event.getHttpServletRequest();
- HttpServletResponse response = event.getHttpServletResponse();
-
- HttpSession session = request.getSession(true);
- session.setMaxInactiveInterval(30);
-
- lastEvent = event.getEventType();
-
- if (event.getEventType() == EventType.BEGIN) {
- if (failOnBegin) {
- throw new IOException("Fail on begin");
- }
- response.setContentType("text/plain");
- response.getWriter().print("BEGIN" + "\r\n");
- } else if (event.getEventType() == EventType.READ) {
- if (failOnRead) {
- throw new IOException("Fail on read");
- }
- InputStream is = request.getInputStream();
- int count = 0;
- while (is.available() > 0) {
- is.read();
- count ++;
- }
- String msg = "READ: " + count + " bytes";
- response.getWriter().print("Client: " + msg + "\r\n");
- } else if (event.getEventType() == EventType.END) {
- endEventOccurred = true;
- if (failOnEnd) {
- throw new IOException("Fail on end");
- }
- String msg = "END";
- response.getWriter().print("Client: " + msg + "\r\n");
- event.close();
- } else {
- String msg = event.getEventType() + ":" + event.getEventSubType() + "\r\n";
- System.out.print(msg);
- response.getWriter().print(msg);
- event.close();
- }
- response.getWriter().flush();
- }
- }
-
- private static class CometCloseServlet extends HttpServlet
- implements CometProcessor {
-
- private static final long serialVersionUID = 1L;
-
- @Override
- public void event(CometEvent event) throws IOException,
- ServletException {
- HttpServletResponse response = event.getHttpServletResponse();
- response.setContentType("text/plain");
- // Force a chunked response since that is what the test client
- // expects
- response.flushBuffer();
- response.getWriter().print("OK");
- event.close();
- }
-
- }
-
- private static class ConnectionCloseServlet extends HttpServlet
- implements CometProcessor {
-
- private static final long serialVersionUID = 1L;
-
- @Override
- public void event(CometEvent event) throws IOException,
- ServletException {
- HttpServletResponse response = event.getHttpServletResponse();
- response.setContentType("text/plain");
- // Disable keep-alive
- response.setHeader("Connection", "close");
- response.flushBuffer();
- response.getWriter().print("OK");
- event.close();
- }
- }
-
- private static class PingWriterThread extends Thread {
-
- private final int pingCount;
- private final OutputStream os;
- private volatile Exception e = null;
-
- public PingWriterThread(int pingCount, OutputStream os) {
- this.pingCount = pingCount;
- this.os = os;
- }
-
- public Exception getException() {
- return e;
- }
-
- @Override
- public void run() {
- try {
- for (int i = 0; i < pingCount; i++) {
- os.write("4\r\n".getBytes());
- os.write("PING\r\n".getBytes());
- os.flush();
- Thread.sleep(1000);
- }
- os.write("0\r\n".getBytes());
- os.write("\r\n".getBytes());
- } catch (Exception e) {
- this.e = e;
- }
- }
- }
-
- private static class ResponseReaderThread extends Thread {
-
- private final InputStream is;
- private StringBuilder response = new StringBuilder();
-
- private volatile Exception e = null;
-
- public ResponseReaderThread(InputStream is) {
- this.is = is;
- }
-
- public Exception getException() {
- return e;
- }
-
- public String getResponse() {
- return response.toString();
- }
-
- @Override
- public void run() {
- try {
- int c = is.read();
- while (c > -1) {
- response.append((char) c);
- c = is.read();
- }
- } catch (Exception e) {
- this.e = e;
- }
- }
- }
-
- private static class AsyncCometCloseValve extends ValveBase {
-
- @Override
- public void invoke(Request request, Response response)
- throws IOException, ServletException {
-
- CometEventImpl event = new CometEventImpl(request, response);
-
- getNext().invoke(request, response);
-
- if (request.isComet()) {
- Thread t = new AsyncCometCloseThread(event);
- t.start();
- }
- }
- }
-
- private static class AsyncCometCloseThread extends Thread {
-
- private final CometEvent event;
-
- public AsyncCometCloseThread(CometEvent event) {
- this.event = event;
- }
-
- @Override
- public void run() {
- try {
- Thread.sleep(2000);
- event.close();
- } catch (Exception e) {
- // Test should fail. Report what went wrong.
- e.printStackTrace();
- }
- }
- }
-}
diff --git a/test/org/apache/catalina/connector/TestCoyoteInputStream.java b/test/org/apache/catalina/connector/TestCoyoteInputStream.java
new file mode 100644
index 0000000..e092835
--- /dev/null
+++ b/test/org/apache/catalina/connector/TestCoyoteInputStream.java
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.catalina.connector;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import org.apache.catalina.Context;
+import org.apache.catalina.startup.Tomcat;
+import org.apache.catalina.startup.TomcatBaseTest;
+import org.apache.tomcat.util.buf.ByteChunk;
+
+public class TestCoyoteInputStream extends TomcatBaseTest {
+
+ @Test
+ public void testReadWithByteBuffer() throws Exception {
+ Tomcat tomcat = getTomcatInstance();
+
+ Context root = tomcat.addContext("", TEMP_DIR);
+ Tomcat.addServlet(root, "testServlet", new TestServlet());
+ root.addServletMappingDecoded("/", "testServlet");
+
+ tomcat.start();
+
+ ByteChunk bc = new ByteChunk();
+ String requestBody = "HelloWorld";
+ 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()));
+ }
+
+ private static final class TestServlet extends HttpServlet {
+
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ protected void doPost(HttpServletRequest req, HttpServletResponse resp)
+ throws ServletException, IOException {
+ CoyoteInputStream is = (CoyoteInputStream) req.getInputStream();
+ ByteBuffer buffer = ByteBuffer.allocate(256);
+ is.read(buffer);
+ CoyoteOutputStream os = (CoyoteOutputStream) resp.getOutputStream();
+ os.write(buffer);
+ }
+
+ }
+
+}
diff --git a/test/org/apache/catalina/connector/TestCoyoteOutputStream.java b/test/org/apache/catalina/connector/TestCoyoteOutputStream.java
index 3089994..ff04e47 100644
--- a/test/org/apache/catalina/connector/TestCoyoteOutputStream.java
+++ b/test/org/apache/catalina/connector/TestCoyoteOutputStream.java
@@ -16,7 +16,10 @@
*/
package org.apache.catalina.connector;
+import java.io.File;
import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.nio.channels.FileChannel.MapMode;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.atomic.AtomicInteger;
@@ -99,6 +102,27 @@ public class TestCoyoteOutputStream extends TomcatBaseTest {
doNonBlockingTest(2, 1, false);
}
+ @Test
+ public void testWriteWithByteBuffer() throws Exception {
+ Tomcat tomcat = getTomcatInstance();
+
+ Context root = tomcat.addContext("", TEMP_DIR);
+ Tomcat.addServlet(root, "testServlet", new TestServlet());
+ root.addServletMappingDecoded("/", "testServlet");
+
+ tomcat.start();
+
+ ByteChunk bc = new ByteChunk();
+ int rc = getUrl("http://localhost:" + getPort() + "/", bc, null, null);
+ Assert.assertEquals(HttpServletResponse.SC_OK, rc);
+ File file = new File("test/org/apache/catalina/connector/test_content.txt");
+ try (RandomAccessFile raf = new RandomAccessFile(file, "r");) {
+ ByteChunk expected = new ByteChunk();
+ expected.append(raf.getChannel().map(MapMode.READ_ONLY, 0, file.length()));
+ Assert.assertTrue(expected.equals(bc));
+ }
+ }
+
private void doNonBlockingTest(int asyncWriteTarget, int syncWriteTarget,
boolean useContainerThreadToSetListener) throws Exception {
@@ -218,7 +242,8 @@ public class TestCoyoteOutputStream extends TomcatBaseTest {
@Override
public void onError(Throwable throwable) {
- // TODO Auto-generated method stub
+ // Not expected.
+ throwable.printStackTrace();
}
}
}
@@ -249,4 +274,20 @@ public class TestCoyoteOutputStream extends TomcatBaseTest {
}
}
}
+
+ private static final class TestServlet extends HttpServlet {
+
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ 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");) {
+ os.write(raf.getChannel().map(MapMode.READ_ONLY, 0, file.length()));
+ }
+ }
+
+ }
}
diff --git a/test/org/apache/catalina/connector/TestMaxConnections.java b/test/org/apache/catalina/connector/TestMaxConnections.java
index 8191173..16904da 100644
--- a/test/org/apache/catalina/connector/TestMaxConnections.java
+++ b/test/org/apache/catalina/connector/TestMaxConnections.java
@@ -24,6 +24,7 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.junit.Assert;
+import org.junit.Assume;
import org.junit.Test;
import org.apache.catalina.core.StandardContext;
@@ -39,6 +40,8 @@ public class TestMaxConnections extends TomcatBaseTest {
@Test
public void testConnector() throws Exception {
init();
+ Assume.assumeFalse("This feature is not available for NIO2 (BZ58103)",
+ getTomcatInstance().getConnector().getProtocolHandlerClassName().contains("Nio2"));
ConnectThread[] t = new ConnectThread[10];
for (int i=0; i<t.length; i++) {
t[i] = new ConnectThread();
diff --git a/test/org/apache/catalina/connector/TestResponse.java b/test/org/apache/catalina/connector/TestResponse.java
index 9612780..204b54d 100644
--- a/test/org/apache/catalina/connector/TestResponse.java
+++ b/test/org/apache/catalina/connector/TestResponse.java
@@ -574,7 +574,10 @@ public class TestResponse extends TomcatBaseTest {
@Test
public void testEncodeRedirectURL16() throws Exception {
doTestEncodeURL("./..#/../..", "./..;jsessionid=1234#/../..");
- } @Test
+ }
+
+
+ @Test
public void testSendRedirect01() throws Exception {
doTestSendRedirect("../foo", "../foo");
}
diff --git a/test/org/apache/catalina/connector/TestResponsePerformance.java b/test/org/apache/catalina/connector/TestResponsePerformance.java
index efc10f0..131d3aa 100644
--- a/test/org/apache/catalina/connector/TestResponsePerformance.java
+++ b/test/org/apache/catalina/connector/TestResponsePerformance.java
@@ -38,6 +38,10 @@ public class TestResponsePerformance extends LoggingBaseTest {
doHomebrew(resp);
doUri();
+ // Note: Java 9 on my OSX laptop consistently shows doUri() is faster
+ // than doHomebrew(). Worth a closer look for Tomcat 10 on the
+ // assumption it will require java 9
+
// To allow for timing differences between runs, a "best of n" approach
// is taken for this test
final int bestOf = 5;
diff --git a/test/org/apache/catalina/connector/test_content.txt b/test/org/apache/catalina/connector/test_content.txt
new file mode 100644
index 0000000..e49c4e8
--- /dev/null
+++ b/test/org/apache/catalina/connector/test_content.txt
@@ -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.
+
+# This is a test file for the WebappServiceLoader
+# It contains comment lines and blank lines
+
+test content
diff --git a/test/org/apache/catalina/core/TestApplicationContext.java b/test/org/apache/catalina/core/TestApplicationContext.java
index 20d961a..8df7da6 100644
--- a/test/org/apache/catalina/core/TestApplicationContext.java
+++ b/test/org/apache/catalina/core/TestApplicationContext.java
@@ -246,4 +246,86 @@ public class TestApplicationContext extends TomcatBaseTest {
}
}
}
+
+
+ /*
+ * 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 {
+ // Setup Tomcat instance
+ Tomcat tomcat = getTomcatInstance();
+
+ // No file system docBase required
+ Context ctx2 = tomcat.addContext("/second", null);
+ GetAttributeServlet getAttributeServlet = new GetAttributeServlet();
+ Tomcat.addServlet(ctx2, "getAttributeServlet", getAttributeServlet);
+ ctx2.addServletMappingDecoded("/test", "getAttributeServlet");
+
+ // No file system docBase required
+ Context ctx1 = tomcat.addContext("/first", null);
+ ctx1.setCrossContext(true);
+ SetAttributeServlet setAttributeServlet = new SetAttributeServlet("/test", "/second");
+ Tomcat.addServlet(ctx1, "setAttributeServlet", setAttributeServlet);
+ ctx1.addServletMappingDecoded("/test", "setAttributeServlet");
+
+ tomcat.start();
+
+ ByteChunk bc = new ByteChunk();
+ int rc = getUrl("http://localhost:" + getPort() + "/first/test", bc, null);
+
+ Assert.assertEquals(200, rc);
+ Assert.assertEquals("01-PASS", bc.toString());
+ }
+
+
+ private static class SetAttributeServlet extends HttpServlet {
+
+ private static final long serialVersionUID = 1L;
+ private static final String ATTRIBUTE_NAME = "setAttributeTest";
+ private static final String ATTRIBUTE_VALUE = "abcde";
+
+ private final String targetContextPath;
+ private final String targetPath;
+
+ public SetAttributeServlet(String targetPath, String targetContextPath) {
+ this.targetPath = targetPath;
+ this.targetContextPath = targetContextPath;
+ }
+
+ @Override
+ protected void doGet(HttpServletRequest req, HttpServletResponse resp)
+ throws ServletException, IOException {
+ ServletContext sc;
+ if (targetContextPath == null) {
+ sc = req.getServletContext();
+ } else {
+ sc = req.getServletContext().getContext(targetContextPath);
+ }
+ sc.setAttribute(ATTRIBUTE_NAME, ATTRIBUTE_VALUE);
+ sc.getRequestDispatcher(targetPath).forward(req, resp);
+ }
+ }
+
+
+ private static class GetAttributeServlet extends HttpServlet {
+
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ 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);
+ if (SetAttributeServlet.ATTRIBUTE_VALUE.equals(value)) {
+ pw.print("01-PASS");
+ } else {
+ pw.print("01-FAIL");
+ }
+ }
+ }
}
diff --git a/test/org/apache/catalina/core/TestApplicationMapping.java b/test/org/apache/catalina/core/TestApplicationMapping.java
new file mode 100644
index 0000000..80726b8
--- /dev/null
+++ b/test/org/apache/catalina/core/TestApplicationMapping.java
@@ -0,0 +1,312 @@
+/*
+ * 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.io.PrintWriter;
+
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import org.apache.catalina.Context;
+import org.apache.catalina.servlet4preview.http.Mapping;
+import org.apache.catalina.startup.Tomcat;
+import org.apache.catalina.startup.TomcatBaseTest;
+import org.apache.tomcat.util.buf.ByteChunk;
+
+public class TestApplicationMapping extends TomcatBaseTest {
+
+ @Test
+ public void testContextNonRootMappingContextRoot() throws Exception {
+ doTestMapping("/dummy", "", "", "", "CONTEXT_ROOT");
+ }
+
+ @Test
+ public void testContextNonRootMappingDefault() throws Exception {
+ doTestMapping("/dummy", "/", "/foo", "/", "DEFAULT");
+ }
+
+ @Test
+ public void testContextNonRootMappingExtension() throws Exception {
+ doTestMapping("/dummy", "*.test", "/foo/bar.test", "/foo/bar", "EXTENSION");
+ }
+
+ @Test
+ public void testContextNonRootMappingExact() throws Exception {
+ doTestMapping("/dummy", "/foo/bar", "/foo/bar", "/foo/bar", "EXACT");
+ }
+
+ @Test
+ public void testContextNonRootMappingPath() throws Exception {
+ doTestMapping("/dummy", "/foo/bar/*", "/foo/bar/foo2", "/foo2", "PATH");
+ }
+
+ @Test
+ public void testContextRootMappingContextRoot() throws Exception {
+ doTestMapping("", "", "", "", "CONTEXT_ROOT");
+ }
+
+ @Test
+ public void testContextRootMappingDefault() throws Exception {
+ doTestMapping("", "/", "/foo", "/", "DEFAULT");
+ }
+
+ @Test
+ public void testContextRootMappingExtension() throws Exception {
+ doTestMapping("", "*.test", "/foo/bar.test", "/foo/bar", "EXTENSION");
+ }
+
+ @Test
+ public void testContextRootMappingExact() throws Exception {
+ doTestMapping("", "/foo/bar", "/foo/bar", "/foo/bar", "EXACT");
+ }
+
+ @Test
+ public void testContextRootMappingPath() throws Exception {
+ doTestMapping("", "/foo/bar/*", "/foo/bar/foo2", "/foo2", "PATH");
+ }
+
+ private void doTestMapping(String contextPath, String mapping, String requestPath,
+ String matchValue, String matchType) throws Exception {
+ doTestMappingDirect(contextPath, mapping, requestPath, matchValue, matchType);
+ tearDown();
+ setUp();
+ doTestMappingInclude(contextPath, mapping, requestPath, matchValue, matchType);
+ tearDown();
+ setUp();
+ doTestMappingNamedInclude(contextPath, mapping, requestPath, matchValue, matchType);
+ tearDown();
+ setUp();
+ doTestMappingForward(contextPath, mapping, requestPath, matchValue, matchType);
+ tearDown();
+ setUp();
+ doTestMappingNamedForward(contextPath, mapping, requestPath, matchValue, matchType);
+ }
+
+ private void doTestMappingDirect(String contextPath, String mapping, String requestPath,
+ String matchValue, String matchType) throws Exception {
+ Tomcat tomcat = getTomcatInstance();
+
+ // No file system docBase required
+ Context ctx = tomcat.addContext(contextPath, null);
+
+ Tomcat.addServlet(ctx, "Mapping", new MappingServlet());
+ ctx.addServletMappingDecoded(mapping, "Mapping");
+
+ tomcat.start();
+
+ ByteChunk bc = getUrl("http://localhost:" + getPort() + contextPath + requestPath);
+ String body = bc.toString();
+
+ Assert.assertTrue(body, body.contains("MatchValue=[" + matchValue + "]"));
+ Assert.assertTrue(body, body.contains("Pattern=[" + mapping + "]"));
+ Assert.assertTrue(body, body.contains("MatchType=[" + matchType + "]"));
+ Assert.assertTrue(body, body.contains("ServletName=[Mapping]"));
+ }
+
+ private void doTestMappingInclude(String contextPath, String mapping, String requestPath,
+ String matchValue, String matchType) throws Exception {
+ Tomcat tomcat = getTomcatInstance();
+
+ // No file system docBase required
+ Context ctx = tomcat.addContext(contextPath, null);
+
+ Tomcat.addServlet(ctx, "Include", new IncludeServlet());
+ ctx.addServletMappingDecoded(mapping, "Include");
+ Tomcat.addServlet(ctx, "Mapping", new MappingServlet());
+ ctx.addServletMappingDecoded("/mapping", "Mapping");
+
+ tomcat.start();
+
+ ByteChunk bc = getUrl("http://localhost:" + getPort() + contextPath + requestPath);
+ String body = bc.toString();
+
+ Assert.assertTrue(body, body.contains("MatchValue=[" + matchValue + "]"));
+ Assert.assertTrue(body, body.contains("Pattern=[" + mapping + "]"));
+ Assert.assertTrue(body, body.contains("MatchType=[" + matchType + "]"));
+ Assert.assertTrue(body, body.contains("ServletName=[Include]"));
+
+ Assert.assertTrue(body, body.contains("IncludeMatchValue=[/mapping]"));
+ Assert.assertTrue(body, body.contains("IncludePattern=[/mapping]"));
+ Assert.assertTrue(body, body.contains("IncludeMatchType=[EXACT]"));
+ Assert.assertTrue(body, body.contains("IncludeServletName=[Mapping]"));
+ }
+
+ private void doTestMappingNamedInclude(String contextPath, String mapping, String requestPath,
+ String matchValue, String matchType) throws Exception {
+ Tomcat tomcat = getTomcatInstance();
+
+ // No file system docBase required
+ Context ctx = tomcat.addContext(contextPath, null);
+
+ Tomcat.addServlet(ctx, "Include", new NamedIncludeServlet());
+ ctx.addServletMappingDecoded(mapping, "Include");
+ Tomcat.addServlet(ctx, "Mapping", new MappingServlet());
+ ctx.addServletMappingDecoded("/mapping", "Mapping");
+
+ tomcat.start();
+
+ ByteChunk bc = getUrl("http://localhost:" + getPort() + contextPath + requestPath);
+ String body = bc.toString();
+
+ Assert.assertTrue(body, body.contains("MatchValue=[" + matchValue + "]"));
+ Assert.assertTrue(body, body.contains("Pattern=[" + mapping + "]"));
+ Assert.assertTrue(body, body.contains("MatchType=[" + matchType + "]"));
+ Assert.assertTrue(body, body.contains("ServletName=[Include]"));
+ }
+
+ private void doTestMappingForward(String contextPath, String mapping, String requestPath,
+ String matchValue, String matchType) throws Exception {
+ Tomcat tomcat = getTomcatInstance();
+
+ // No file system docBase required
+ Context ctx = tomcat.addContext(contextPath, null);
+
+ Tomcat.addServlet(ctx, "Forward", new ForwardServlet());
+ ctx.addServletMappingDecoded(mapping, "Forward");
+ Tomcat.addServlet(ctx, "Mapping", new MappingServlet());
+ ctx.addServletMappingDecoded("/mapping", "Mapping");
+
+ tomcat.start();
+
+ ByteChunk bc = getUrl("http://localhost:" + getPort() + contextPath + requestPath);
+ String body = bc.toString();
+
+ Assert.assertTrue(body, body.contains("MatchValue=[/mapping]"));
+ Assert.assertTrue(body, body.contains("Pattern=[/mapping]"));
+ Assert.assertTrue(body, body.contains("MatchType=[EXACT]"));
+ Assert.assertTrue(body, body.contains("ServletName=[Mapping]"));
+
+ Assert.assertTrue(body, body.contains("ForwardMatchValue=[" + matchValue + "]"));
+ Assert.assertTrue(body, body.contains("ForwardPattern=[" + mapping + "]"));
+ Assert.assertTrue(body, body.contains("ForwardMatchType=[" + matchType + "]"));
+ Assert.assertTrue(body, body.contains("ForwardServletName=[Forward]"));
+ }
+
+ private void doTestMappingNamedForward(String contextPath, String mapping, String requestPath,
+ String matchValue, String matchType) throws Exception {
+ Tomcat tomcat = getTomcatInstance();
+
+ // No file system docBase required
+ Context ctx = tomcat.addContext(contextPath, null);
+
+ Tomcat.addServlet(ctx, "Forward", new NamedForwardServlet());
+ ctx.addServletMappingDecoded(mapping, "Forward");
+ Tomcat.addServlet(ctx, "Mapping", new MappingServlet());
+ ctx.addServletMappingDecoded("/mapping", "Mapping");
+
+ tomcat.start();
+
+ ByteChunk bc = getUrl("http://localhost:" + getPort() + contextPath + requestPath);
+ String body = bc.toString();
+
+ Assert.assertTrue(body, body.contains("MatchValue=[" + matchValue + "]"));
+ Assert.assertTrue(body, body.contains("Pattern=[" + mapping + "]"));
+ Assert.assertTrue(body, body.contains("MatchType=[" + matchType + "]"));
+ Assert.assertTrue(body, body.contains("ServletName=[Forward]"));
+ }
+
+
+ private static class IncludeServlet extends HttpServlet {
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ protected void doGet(HttpServletRequest req, HttpServletResponse resp)
+ throws ServletException, IOException {
+ RequestDispatcher rd = req.getRequestDispatcher("/mapping");
+ rd.include(req, resp);
+ }
+ }
+
+
+ private static class NamedIncludeServlet extends HttpServlet {
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ protected void doGet(HttpServletRequest req, HttpServletResponse resp)
+ throws ServletException, IOException {
+ RequestDispatcher rd = req.getServletContext().getNamedDispatcher("Mapping");
+ rd.include(req, resp);
+ }
+ }
+
+
+ private static class NamedForwardServlet extends HttpServlet {
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ protected void doGet(HttpServletRequest req, HttpServletResponse resp)
+ throws ServletException, IOException {
+ RequestDispatcher rd = req.getServletContext().getNamedDispatcher("Mapping");
+ rd.forward(req, resp);
+ }
+ }
+
+
+ private static class ForwardServlet extends HttpServlet {
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ protected void doGet(HttpServletRequest req, HttpServletResponse resp)
+ throws ServletException, IOException {
+ RequestDispatcher rd = req.getRequestDispatcher("/mapping");
+ rd.forward(req, resp);
+ }
+ }
+
+
+ private static class MappingServlet extends HttpServlet {
+
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ protected void doGet(HttpServletRequest req, HttpServletResponse resp)
+ throws ServletException, IOException {
+ resp.setContentType("text/plain;charset=UTF-8");
+ PrintWriter pw = resp.getWriter();
+ Mapping mapping = ((org.apache.catalina.servlet4preview.http.HttpServletRequest)
+ req).getMapping();
+ pw.println("MatchValue=[" + mapping.getMatchValue() + "]");
+ pw.println("Pattern=[" + mapping.getPattern() + "]");
+ pw.println("MatchType=[" + mapping.getMappingMatch() + "]");
+ pw.println("ServletName=[" + mapping.getServletName() + "]");
+ Mapping includeMapping = (Mapping) req.getAttribute(
+ org.apache.catalina.servlet4preview.RequestDispatcher.INCLUDE_MAPPING);
+ if (includeMapping != null) {
+ pw.println("IncludeMatchValue=[" + includeMapping.getMatchValue() + "]");
+ pw.println("IncludePattern=[" + includeMapping.getPattern() + "]");
+ pw.println("IncludeMatchType=[" + includeMapping.getMappingMatch() + "]");
+ pw.println("IncludeServletName=[" + includeMapping.getServletName() + "]");
+
+ }
+ Mapping forwardMapping = (Mapping) req.getAttribute(
+ org.apache.catalina.servlet4preview.RequestDispatcher.FORWARD_MAPPING);
+ if (forwardMapping != null) {
+ pw.println("ForwardMatchValue=[" + forwardMapping.getMatchValue() + "]");
+ pw.println("ForwardPattern=[" + forwardMapping.getPattern() + "]");
+ pw.println("ForwardMatchType=[" + forwardMapping.getMappingMatch() + "]");
+ pw.println("ForwardServletName=[" + forwardMapping.getServletName() + "]");
+ }
+ }
+ }
+}
diff --git a/test/org/apache/catalina/core/TestApplicationPushBuilder.java b/test/org/apache/catalina/core/TestApplicationPushBuilder.java
new file mode 100644
index 0000000..d283314
--- /dev/null
+++ b/test/org/apache/catalina/core/TestApplicationPushBuilder.java
@@ -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 org.apache.catalina.core;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class TestApplicationPushBuilder {
+
+ @Test
+ public void test01() {
+ doTest("foo", "utf-8", "foo");
+ }
+
+ @Test
+ public void test02() {
+ doTest("/foo", "utf-8", "/foo");
+ }
+
+ @Test
+ public void test03() {
+ doTest("%20foo", "utf-8", " foo");
+ }
+
+ @Test
+ public void test04() {
+ doTest("fo%20o", "utf-8", "fo o");
+ }
+
+ @Test
+ public void test05() {
+ doTest("foo%20", "utf-8", "foo ");
+ }
+
+
+ private void doTest(String input, String charset, String expected) {
+ String result = ApplicationPushBuilder.decode(input, charset);
+ Assert.assertEquals(expected, result);
+ }
+}
diff --git a/test/org/apache/catalina/core/TestAsyncContextImpl.java b/test/org/apache/catalina/core/TestAsyncContextImpl.java
index 5a831b9..3bd4606 100644
--- a/test/org/apache/catalina/core/TestAsyncContextImpl.java
+++ b/test/org/apache/catalina/core/TestAsyncContextImpl.java
@@ -72,7 +72,7 @@ public class TestAsyncContextImpl extends TomcatBaseTest {
// Timeout thread (where used) checks for timeout every second
private static final long TIMEOUT_MARGIN = 1000;
// Default timeout for these tests
- private static final long TIMEOUT = 3000;
+ private static final long TIMEOUT = 5000;
private static StringBuilder tracker;
@@ -548,8 +548,8 @@ public class TestAsyncContextImpl extends TomcatBaseTest {
long timeoutDelay = TimeoutServlet.ASYNC_TIMEOUT;
if (asyncDispatch != null && asyncDispatch.booleanValue() &&
!completeOnTimeout.booleanValue()) {
- // Extra timeout in this case
- timeoutDelay += TimeoutServlet.ASYNC_TIMEOUT;
+ // The async dispatch includes a sleep
+ timeoutDelay += AsyncStartRunnable.THREAD_SLEEP_TIME;
}
alvGlobal.validateAccessLog(1, 200, timeoutDelay,
timeoutDelay + TIMEOUT_MARGIN + REQUEST_TIME);
@@ -564,7 +564,7 @@ public class TestAsyncContextImpl extends TomcatBaseTest {
private final Boolean completeOnTimeout;
private final String dispatchUrl;
- public static final long ASYNC_TIMEOUT = 3000;
+ public static final long ASYNC_TIMEOUT = 100;
public TimeoutServlet(Boolean completeOnTimeout, String dispatchUrl) {
this.completeOnTimeout = completeOnTimeout;
@@ -2223,6 +2223,7 @@ public class TestAsyncContextImpl extends TomcatBaseTest {
}
}
+ // https://bz.apache.org/bugzilla/show_bug.cgi?id=57559
@Test
public void testAsyncRequestURI() throws Exception {
// Setup Tomcat instance
diff --git a/test/org/apache/catalina/core/TestStandardContext.java b/test/org/apache/catalina/core/TestStandardContext.java
index 846e36c..a84f132 100644
--- a/test/org/apache/catalina/core/TestStandardContext.java
+++ b/test/org/apache/catalina/core/TestStandardContext.java
@@ -184,7 +184,6 @@ public class TestStandardContext extends TomcatBaseTest {
new ClassNotFoundException());
}
}
-
}
@Test
diff --git a/test/org/apache/catalina/core/TestStandardContextAliases.java b/test/org/apache/catalina/core/TestStandardContextAliases.java
index f6bb310..15bb020 100644
--- a/test/org/apache/catalina/core/TestStandardContextAliases.java
+++ b/test/org/apache/catalina/core/TestStandardContextAliases.java
@@ -63,6 +63,9 @@ public class TestStandardContextAliases extends TomcatBaseTest {
ByteChunk res = getUrl("http://localhost:" + getPort() + "/");
String result = res.toString();
+ if (result == null) {
+ result = "";
+ }
assertTrue(result.indexOf("00-PASS") > -1);
assertTrue(result.indexOf("01-PASS") > -1);
diff --git a/test/org/apache/catalina/core/TestStandardContextResources.java b/test/org/apache/catalina/core/TestStandardContextResources.java
index 10caeb5..e8ad7c9 100644
--- a/test/org/apache/catalina/core/TestStandardContextResources.java
+++ b/test/org/apache/catalina/core/TestStandardContextResources.java
@@ -211,6 +211,7 @@ public class TestStandardContextResources extends TomcatBaseTest {
// app dir is relative to server home
StandardContext ctx = (StandardContext) tomcat.addWebapp(null, "/test",
appDir.getAbsolutePath());
+ skipTldsForResourceJars(ctx);
Tomcat.addServlet(ctx, "getresource", new GetResourceServlet());
ctx.addServletMappingDecoded("/getresource", "getresource");
diff --git a/test/org/apache/catalina/core/TestStandardWrapper.java b/test/org/apache/catalina/core/TestStandardWrapper.java
index 1c6fd2b..c8356b4 100644
--- a/test/org/apache/catalina/core/TestStandardWrapper.java
+++ b/test/org/apache/catalina/core/TestStandardWrapper.java
@@ -153,7 +153,8 @@ public class TestStandardWrapper extends TomcatBaseTest {
Tomcat tomcat = getTomcatInstance();
File appDir = new File("test/webapp-fragments");
- tomcat.addWebapp(null, "", appDir.getAbsolutePath());
+ Context ctx = tomcat.addWebapp(null, "", appDir.getAbsolutePath());
+ skipTldsForResourceJars(ctx);
tomcat.start();
@@ -410,7 +411,7 @@ public class TestStandardWrapper extends TomcatBaseTest {
public static final int BUG51445_THREAD_COUNT = 5;
public static CountDownLatch latch = null;
- public static AtomicInteger counter = new AtomicInteger(0);
+ public static final AtomicInteger counter = new AtomicInteger(0);
public static void initLatch() {
latch = new CountDownLatch(BUG51445_THREAD_COUNT);
diff --git a/test/org/apache/catalina/core/TestSwallowAbortedUploads.java b/test/org/apache/catalina/core/TestSwallowAbortedUploads.java
index 8a6fc1e..5082942 100644
--- a/test/org/apache/catalina/core/TestSwallowAbortedUploads.java
+++ b/test/org/apache/catalina/core/TestSwallowAbortedUploads.java
@@ -131,7 +131,7 @@ public class TestSwallowAbortedUploads extends TomcatBaseTest {
AbortedUploadClient client = new AbortedUploadClient();
Exception ex = doAbortedUploadTest(client, true, false);
assertTrue("Limited upload with swallow disabled does not generate client exception",
- ex != null && ex instanceof java.net.SocketException);
+ ex instanceof java.net.SocketException);
client.reset();
}
@@ -177,7 +177,7 @@ public class TestSwallowAbortedUploads extends TomcatBaseTest {
AbortedPOSTClient client = new AbortedPOSTClient();
Exception ex = doAbortedPOSTTest(client, HttpServletResponse.SC_REQUEST_ENTITY_TOO_LARGE, false);
assertTrue("Limited upload with swallow disabled does not generate client exception",
- ex != null && ex instanceof java.net.SocketException);
+ ex instanceof java.net.SocketException);
client.reset();
}
diff --git a/test/org/apache/catalina/filters/TestCorsFilter.java b/test/org/apache/catalina/filters/TestCorsFilter.java
index bcece1b..f7acfd2 100644
--- a/test/org/apache/catalina/filters/TestCorsFilter.java
+++ b/test/org/apache/catalina/filters/TestCorsFilter.java
@@ -17,11 +17,14 @@
package org.apache.catalina.filters;
import java.io.IOException;
+import java.util.Enumeration;
import java.util.LinkedHashSet;
import java.util.Locale;
import java.util.Set;
import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@@ -704,7 +707,24 @@ public class TestCorsFilter {
TesterHttpServletResponse response = new TesterHttpServletResponse();
CorsFilter corsFilter = new CorsFilter();
- corsFilter.init(null);
+ corsFilter.init(new FilterConfig() {
+ @Override
+ public ServletContext getServletContext() {
+ return null;
+ }
+ @Override
+ public Enumeration<String> getInitParameterNames() {
+ return null;
+ }
+ @Override
+ public String getInitParameter(String name) {
+ return null;
+ }
+ @Override
+ public String getFilterName() {
+ return null;
+ }
+ });
corsFilter.doFilter(request, response, filterChain);
Assert.assertTrue(response.getHeader(
diff --git a/test/org/apache/catalina/filters/TestRestCsrfPreventionFilter2.java b/test/org/apache/catalina/filters/TestRestCsrfPreventionFilter2.java
index e10acbe..6846474 100644
--- a/test/org/apache/catalina/filters/TestRestCsrfPreventionFilter2.java
+++ b/test/org/apache/catalina/filters/TestRestCsrfPreventionFilter2.java
@@ -57,7 +57,7 @@ public class TestRestCsrfPreventionFilter2 extends TomcatBaseTest {
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 CONTEXT_PATH_LOGIN = "/";
private static final String URI_PROTECTED = "/services/*";
private static final String URI_CSRF_PROTECTED = "/services/customers/*";
private static final String LIST_CUSTOMERS = "/services/customers/";
@@ -78,7 +78,7 @@ public class TestRestCsrfPreventionFilter2 extends TomcatBaseTest {
private static final String USER = "user";
private static final String PWD = "pwd";
private static final String ROLE = "role";
- private static final String METHOD = "Basic";
+ private static final String METHOD = "BASIC";
private static final BasicCredentials CREDENTIALS = new BasicCredentials(METHOD, USER, PWD);
private static final String CLIENT_AUTH_HEADER = "authorization";
@@ -206,13 +206,13 @@ public class TestRestCsrfPreventionFilter2 extends TomcatBaseTest {
Map<String, List<String>> reqHeaders = new HashMap<>();
Map<String, List<String>> respHeaders = new HashMap<>();
- addNonce(reqHeaders, nonce);
+ addNonce(reqHeaders, nonce, nonNullPredicate(String.class));
if (useCookie) {
- addCookies(reqHeaders);
+ addCookies(reqHeaders, notEmptyPredicate());
}
- addCredentials(reqHeaders, credentials);
+ addCredentials(reqHeaders, credentials, nonNullPredicate(BasicCredentials.class));
ByteChunk bc = new ByteChunk();
int rc;
@@ -227,7 +227,7 @@ public class TestRestCsrfPreventionFilter2 extends TomcatBaseTest {
if (expectedRC == HttpServletResponse.SC_OK) {
assertEquals(expectedResponse, bc.toString());
List<String> newCookies = respHeaders.get(SERVER_COOKIE_HEADER);
- saveCookies(newCookies);
+ saveCookies(newCookies, notEmptyPredicate());
}
if (!expectCsrfRH) {
@@ -235,7 +235,7 @@ public class TestRestCsrfPreventionFilter2 extends TomcatBaseTest {
} else {
List<String> respHeaderValue = respHeaders.get(Constants.CSRF_REST_NONCE_HEADER_NAME);
assertNotNull(respHeaderValue);
- if (expectedCsrfRHV != null) {
+ if (nonNull(expectedCsrfRHV)) {
assertTrue(respHeaderValue.contains(expectedCsrfRHV));
} else {
validNonce = respHeaderValue.get(0);
@@ -243,16 +243,16 @@ public class TestRestCsrfPreventionFilter2 extends TomcatBaseTest {
}
}
- private void saveCookies(List<String> newCookies) {
- if (newCookies != null && newCookies.size() > 0) {
- for (String header : newCookies) {
- cookies.add(header.substring(0, header.indexOf(';')));
+ private void saveCookies(List<String> newCookies, Predicate<List<String>> tester) {
+ if (tester.test(newCookies)) {
+ for (String newCookie: newCookies) {
+ cookies.add(newCookie.substring(0, newCookie.indexOf(';')));
}
}
}
- private void addCookies(Map<String, List<String>> reqHeaders) {
- if (cookies != null && cookies.size() > 0) {
+ private void addCookies(Map<String, List<String>> reqHeaders, Predicate<List<String>> tester) {
+ if (tester.test(cookies)) {
StringBuilder cookieHeader = new StringBuilder();
boolean first = true;
for (String cookie : cookies) {
@@ -267,14 +267,16 @@ public class TestRestCsrfPreventionFilter2 extends TomcatBaseTest {
}
}
- private void addNonce(Map<String, List<String>> reqHeaders, String nonce) {
- if (nonce != null) {
+ private void addNonce(Map<String, List<String>> reqHeaders, String nonce,
+ Predicate<String> tester) {
+ if (tester.test(nonce)) {
addRequestHeader(reqHeaders, Constants.CSRF_REST_NONCE_HEADER_NAME, nonce);
}
}
- private void addCredentials(Map<String, List<String>> reqHeaders, BasicCredentials credentials) {
- if (credentials != null) {
+ private void addCredentials(Map<String, List<String>> reqHeaders, BasicCredentials credentials,
+ Predicate<BasicCredentials> tester) {
+ if (tester.test(credentials)) {
addRequestHeader(reqHeaders, CLIENT_AUTH_HEADER, credentials.getCredentials());
}
}
@@ -363,10 +365,44 @@ public class TestRestCsrfPreventionFilter2 extends TomcatBaseTest {
private String getRequestedPath(HttpServletRequest request) {
String path = request.getServletPath();
- if (request.getPathInfo() != null) {
+ if (nonNull(request.getPathInfo())) {
path = path + request.getPathInfo();
}
return path;
}
}
+
+ private interface Predicate<T> {
+ boolean test(T x);
+ }
+
+ private static boolean nonNull(Object o) {
+ return o != null;
+ }
+
+ /**
+ * @param clazz
+ * class parameter to enable use of generics
+ * @return a Predicate to test for non null-ness
+ */
+ private static <T> Predicate<T> nonNullPredicate(Class<T> clazz) {
+ return new Predicate<T>() {
+ @Override
+ public boolean test(T x) {
+ return x != null;
+ }
+ };
+ }
+
+ /**
+ * @return a Predicate to check for non emptiness of a List of Strings
+ */
+ private static Predicate<List<String>> notEmptyPredicate() {
+ return new Predicate<List<String>>() {
+ @Override
+ public boolean test(List<String> x) {
+ return x != null && !x.isEmpty();
+ }
+ };
+ }
}
diff --git a/test/org/apache/catalina/filters/TesterHttpServletRequest.java b/test/org/apache/catalina/filters/TesterHttpServletRequest.java
index deb7c8f..d486519 100644
--- a/test/org/apache/catalina/filters/TesterHttpServletRequest.java
+++ b/test/org/apache/catalina/filters/TesterHttpServletRequest.java
@@ -44,6 +44,8 @@ import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpUpgradeHandler;
import javax.servlet.http.Part;
+import org.apache.catalina.core.ApplicationPushBuilder;
+
public class TesterHttpServletRequest implements HttpServletRequest {
private Map<String, Object> attributes = new HashMap<>();
@@ -66,19 +68,16 @@ public class TesterHttpServletRequest implements HttpServletRequest {
@Override
public String getCharacterEncoding() {
-
throw new RuntimeException("Not implemented");
}
@Override
public void setCharacterEncoding(String env)
throws UnsupportedEncodingException {
-
}
@Override
public int getContentLength() {
-
throw new RuntimeException("Not implemented");
}
@@ -93,37 +92,31 @@ public class TesterHttpServletRequest implements HttpServletRequest {
@Override
public ServletInputStream getInputStream() throws IOException {
-
throw new RuntimeException("Not implemented");
}
@Override
public String getParameter(String name) {
-
throw new RuntimeException("Not implemented");
}
@Override
public Enumeration<String> getParameterNames() {
-
throw new RuntimeException("Not implemented");
}
@Override
public String[] getParameterValues(String name) {
-
throw new RuntimeException("Not implemented");
}
@Override
public Map<String,String[]> getParameterMap() {
-
throw new RuntimeException("Not implemented");
}
@Override
public String getProtocol() {
-
throw new RuntimeException("Not implemented");
}
@@ -157,19 +150,16 @@ public class TesterHttpServletRequest implements HttpServletRequest {
@Override
public BufferedReader getReader() throws IOException {
-
throw new RuntimeException("Not implemented");
}
@Override
public String getRemoteAddr() {
-
throw new RuntimeException("Not implemented");
}
@Override
public String getRemoteHost() {
-
throw new RuntimeException("Not implemented");
}
@@ -185,37 +175,31 @@ public class TesterHttpServletRequest implements HttpServletRequest {
@Override
public Locale getLocale() {
-
throw new RuntimeException("Not implemented");
}
@Override
public Enumeration<Locale> getLocales() {
-
throw new RuntimeException("Not implemented");
}
@Override
public boolean isSecure() {
-
throw new RuntimeException("Not implemented");
}
@Override
public RequestDispatcher getRequestDispatcher(String path) {
-
throw new RuntimeException("Not implemented");
}
@Override
public String getRealPath(String path) {
-
throw new RuntimeException("Not implemented");
}
@Override
public int getRemotePort() {
-
throw new RuntimeException("Not implemented");
}
@@ -226,31 +210,26 @@ public class TesterHttpServletRequest implements HttpServletRequest {
@Override
public String getLocalAddr() {
-
throw new RuntimeException("Not implemented");
}
@Override
public int getLocalPort() {
-
throw new RuntimeException("Not implemented");
}
@Override
public String getAuthType() {
-
throw new RuntimeException("Not implemented");
}
@Override
public Cookie[] getCookies() {
-
throw new RuntimeException("Not implemented");
}
@Override
public long getDateHeader(String name) {
-
throw new RuntimeException("Not implemented");
}
@@ -272,7 +251,6 @@ public class TesterHttpServletRequest implements HttpServletRequest {
@Override
public Enumeration<String> getHeaders(String name) {
-
throw new RuntimeException("Not implemented");
}
@@ -283,7 +261,6 @@ public class TesterHttpServletRequest implements HttpServletRequest {
@Override
public int getIntHeader(String name) {
-
throw new RuntimeException("Not implemented");
}
@@ -298,103 +275,86 @@ public class TesterHttpServletRequest implements HttpServletRequest {
@Override
public String getPathInfo() {
-
throw new RuntimeException("Not implemented");
}
@Override
public String getPathTranslated() {
-
throw new RuntimeException("Not implemented");
}
@Override
public String getContextPath() {
-
throw new RuntimeException("Not implemented");
}
@Override
public String getQueryString() {
-
throw new RuntimeException("Not implemented");
}
@Override
public String getRemoteUser() {
-
throw new RuntimeException("Not implemented");
}
@Override
public boolean isUserInRole(String role) {
-
throw new RuntimeException("Not implemented");
}
@Override
public Principal getUserPrincipal() {
-
throw new RuntimeException("Not implemented");
}
@Override
public String getRequestedSessionId() {
-
throw new RuntimeException("Not implemented");
}
@Override
public String getRequestURI() {
-
throw new RuntimeException("Not implemented");
}
@Override
public StringBuffer getRequestURL() {
-
throw new RuntimeException("Not implemented");
}
@Override
public String getServletPath() {
-
throw new RuntimeException("Not implemented");
}
@Override
public HttpSession getSession(boolean create) {
-
throw new RuntimeException("Not implemented");
}
@Override
public HttpSession getSession() {
-
throw new RuntimeException("Not implemented");
}
@Override
public boolean isRequestedSessionIdValid() {
-
throw new RuntimeException("Not implemented");
}
@Override
public boolean isRequestedSessionIdFromCookie() {
-
throw new RuntimeException("Not implemented");
}
@Override
public boolean isRequestedSessionIdFromURL() {
-
throw new RuntimeException("Not implemented");
}
@Override
public boolean isRequestedSessionIdFromUrl() {
-
throw new RuntimeException("Not implemented");
}
@@ -477,4 +437,7 @@ public class TesterHttpServletRequest implements HttpServletRequest {
throw new RuntimeException("Not implemented");
}
+ public ApplicationPushBuilder getPushBuilder() {
+ throw new RuntimeException("Not implemented");
+ }
}
diff --git a/test/org/apache/catalina/loader/TestWebappClassLoader.java b/test/org/apache/catalina/loader/TestWebappClassLoader.java
index 3f1fc2d..3bba4cc 100644
--- a/test/org/apache/catalina/loader/TestWebappClassLoader.java
+++ b/test/org/apache/catalina/loader/TestWebappClassLoader.java
@@ -102,7 +102,8 @@ public class TestWebappClassLoader extends TomcatBaseTest {
"org.apache.tomcat",
"javax.el",
"javax.servlet",
- "javax.websocket"
+ "javax.websocket",
+ "javax.security.auth.message"
};
try (WebappClassLoader loader = new WebappClassLoader()) {
diff --git a/test/org/apache/catalina/loader/TestWebappClassLoaderWeaving.java b/test/org/apache/catalina/loader/TestWebappClassLoaderWeaving.java
index 24e07f6..c3ac821 100644
--- a/test/org/apache/catalina/loader/TestWebappClassLoaderWeaving.java
+++ b/test/org/apache/catalina/loader/TestWebappClassLoaderWeaving.java
@@ -27,6 +27,7 @@ import java.security.ProtectionDomain;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import org.junit.After;
@@ -69,7 +70,7 @@ public class TestWebappClassLoaderWeaving extends TomcatBaseTest {
private Tomcat tomcat;
private Context context;
- private WebappClassLoader loader;
+ private WebappClassLoaderBase loader;
@Before
@Override
@@ -83,9 +84,9 @@ public class TestWebappClassLoaderWeaving extends TomcatBaseTest {
ClassLoader loader = this.context.getLoader().getClassLoader();
assertNotNull("The class loader should not be null.", loader);
- assertSame("The class loader is not correct.", WebappClassLoader.class, loader.getClass());
+ assertTrue("The class loader is not correct.", loader instanceof WebappClassLoaderBase);
- this.loader = (WebappClassLoader) loader;
+ this.loader = (WebappClassLoaderBase) loader;
}
@@ -238,7 +239,6 @@ public class TestWebappClassLoaderWeaving extends TomcatBaseTest {
}
- @SuppressWarnings("deprecation")
@Test
public void testCopiedClassLoaderExcludesResourcesAndTransformers() throws Exception {
@@ -251,7 +251,7 @@ public class TestWebappClassLoaderWeaving extends TomcatBaseTest {
result = invokeDoMethodOnClass(this.loader, "TesterUnweavedClass");
assertEquals("The second result is not correct.", "Hello, Weaver #2!", result);
- WebappClassLoader copiedLoader = this.loader.copyWithoutTransformers();
+ WebappClassLoaderBase copiedLoader = (WebappClassLoaderBase) this.loader.copyWithoutTransformers();
result = invokeDoMethodOnClass(copiedLoader, "TesterNeverWeavedClass");
assertEquals("The third result is not correct.", "This will never be weaved.", result);
@@ -265,9 +265,6 @@ public class TestWebappClassLoaderWeaving extends TomcatBaseTest {
assertEquals("getClearReferencesLogFactoryRelease did not match.",
Boolean.valueOf(this.loader.getClearReferencesLogFactoryRelease()),
Boolean.valueOf(copiedLoader.getClearReferencesLogFactoryRelease()));
- assertEquals("getClearReferencesStatic did not match.",
- Boolean.valueOf(this.loader.getClearReferencesStatic()),
- Boolean.valueOf(copiedLoader.getClearReferencesStatic()));
assertEquals("getClearReferencesStopThreads did not match.",
Boolean.valueOf(this.loader.getClearReferencesStopThreads()),
Boolean.valueOf(copiedLoader.getClearReferencesStopThreads()));
@@ -300,7 +297,7 @@ public class TestWebappClassLoaderWeaving extends TomcatBaseTest {
}
}
- private static String invokeDoMethodOnClass(WebappClassLoader loader, String className)
+ private static String invokeDoMethodOnClass(WebappClassLoaderBase loader, String className)
throws Exception {
Class<?> c = loader.findClass("org.apache.catalina.loader." + className);
diff --git a/test/org/apache/catalina/mapper/TestMapper.java b/test/org/apache/catalina/mapper/TestMapper.java
index 0b7d666..3fd74b1 100644
--- a/test/org/apache/catalina/mapper/TestMapper.java
+++ b/test/org/apache/catalina/mapper/TestMapper.java
@@ -88,6 +88,9 @@ public class TestMapper extends LoggingBaseTest {
mapper.addHost("zzzuyopjvewpovewjhfewoih", new String[0], createHost("blah12"));
mapper.addHost("xxxxgqwiwoih", new String[0], createHost("blah13"));
mapper.addHost("qwigqwiwoih", new String[0], createHost("blah14"));
+ mapper.addHost("qwerty.net", new String[0], createHost("blah15"));
+ mapper.addHost("*.net", new String[0], createHost("blah16"));
+ mapper.addHost("zzz.com", new String[0], createHost("blah17"));
mapper.addHostAlias("iowejoiejfoiew", "iowejoiejfoiew_alias");
mapper.setDefaultHostName("ylwrehirkuewh");
@@ -135,6 +138,14 @@ public class TestMapper extends LoggingBaseTest {
null,
Arrays.asList(new WrapperMappingInfo[] { new WrapperMappingInfo(
"/bobou/*", createWrapper("wrapper7"), false, false) }));
+
+ host = createHost("blah16");
+ mapper.addContextVersion("*.net", host, "", "0", createContext("context4"),
+ new String[0], null, null);
+ mapper.addWrappers("*.net", "", "0", Arrays
+ .asList(new WrapperMappingInfo[] {
+ new WrapperMappingInfo("/",
+ createWrapper("context4-defaultWrapper"), false, false) }));
}
@Test
@@ -153,14 +164,14 @@ public class TestMapper extends LoggingBaseTest {
mapper.addHostAlias("iowejoiejfoiew", "iowejoiejfoiew_alias");
// Check we have the right number
- // (added 16 including one host alias. Three duplicates do not increase the count.)
- assertEquals(16, mapper.hosts.length);
+ // (added 17 including one host alias. Three duplicates do not increase the count.)
+ assertEquals(19, mapper.hosts.length);
// Make sure adding a duplicate *does not* overwrite
- final int iowPos = 3;
+ final int iowPos = 4;
assertEquals("blah7", mapper.hosts[iowPos].object.getName());
- final int qwigPos = 8;
+ final int qwigPos = 10;
assertEquals("blah14", mapper.hosts[qwigPos].object.getName());
// Check for alphabetical order of host names
@@ -185,38 +196,38 @@ public class TestMapper extends LoggingBaseTest {
Host hostZ = createHost("zzzz");
Context contextZ = createContext("contextZ");
- assertEquals(16, mapper.hosts.length);
+ assertEquals(19, mapper.hosts.length);
mapper.addContextVersion("zzzz", hostZ, "/", "", contextZ, null, null,
null);
- assertEquals(17, mapper.hosts.length);
+ assertEquals(20, mapper.hosts.length);
mapper.addHost("zzzz", new String[] { "zzzz_alias1", "zzzz_alias2" },
hostZ);
- assertEquals(19, mapper.hosts.length);
+ assertEquals(22, mapper.hosts.length);
- assertEquals("zzzz", mapper.hosts[16].name);
- assertEquals("zzzz_alias1", mapper.hosts[17].name);
- assertEquals("zzzz_alias2", mapper.hosts[18].name);
- assertEquals(2, mapper.hosts[16].getAliases().size());
+ assertEquals("zzzz", mapper.hosts[19].name);
+ assertEquals("zzzz_alias1", mapper.hosts[20].name);
+ assertEquals("zzzz_alias2", mapper.hosts[21].name);
+ assertEquals(2, mapper.hosts[19].getAliases().size());
assertSame(contextZ,
- mapper.hosts[16].contextList.contexts[0].versions[0].object);
+ mapper.hosts[19].contextList.contexts[0].versions[0].object);
assertSame(contextZ,
- mapper.hosts[18].contextList.contexts[0].versions[0].object);
+ mapper.hosts[21].contextList.contexts[0].versions[0].object);
}
@Test
public void testRemoveHost() {
- assertEquals(16, mapper.hosts.length);
+ assertEquals(19, mapper.hosts.length);
mapper.removeHostAlias("iowejoiejfoiew");
mapper.removeHost("iowejoiejfoiew_alias");
- assertEquals(16, mapper.hosts.length); // No change
+ assertEquals(19, mapper.hosts.length); // No change
mapper.removeHostAlias("iowejoiejfoiew_alias");
- assertEquals(15, mapper.hosts.length); // Removed
+ assertEquals(18, mapper.hosts.length); // Removed
mapper.addHostAlias("iowejoiejfoiew", "iowejoiejfoiew_alias");
- assertEquals(16, mapper.hosts.length);
+ assertEquals(19, mapper.hosts.length);
- final int iowPos = 3;
+ final int iowPos = 4;
Mapper.MappedHost hostMapping = mapper.hosts[iowPos];
Mapper.MappedHost aliasMapping = mapper.hosts[iowPos + 1];
assertEquals("iowejoiejfoiew_alias", aliasMapping.name);
@@ -229,7 +240,7 @@ public class TestMapper extends LoggingBaseTest {
assertSame(hostMapping, aliasMapping.getRealHost());
mapper.removeHost("iowejoiejfoiew");
- assertEquals(14, mapper.hosts.length); // Both host and alias removed
+ assertEquals(17, mapper.hosts.length); // Both host and alias removed
for (Mapper.MappedHost host : mapper.hosts) {
assertTrue(host.name, !host.name.startsWith("iowejoiejfoiew"));
}
@@ -240,6 +251,8 @@ public class TestMapper extends LoggingBaseTest {
MappingData mappingData = new MappingData();
MessageBytes host = MessageBytes.newInstance();
host.setString("iowejoiejfoiew");
+ MessageBytes wildcard = MessageBytes.newInstance();
+ wildcard.setString("foo.net");
MessageBytes alias = MessageBytes.newInstance();
alias.setString("iowejoiejfoiew_alias");
MessageBytes uri = MessageBytes.newInstance();
@@ -271,6 +284,20 @@ public class TestMapper extends LoggingBaseTest {
assertTrue(mappingData.redirectPath.isNull());
mappingData.recycle();
+ uri.recycle();
+ uri.setString("/foo/bar/bla/bobou/foo");
+ uri.toChars();
+ uri.getCharChunk().setLimit(-1);
+ mapper.map(wildcard, uri, null, mappingData);
+ assertEquals("blah16", mappingData.host.getName());
+ assertEquals("context4", mappingData.context.getName());
+ assertEquals("context4-defaultWrapper", mappingData.wrapper.getName());
+ assertEquals("", mappingData.contextPath.toString());
+ assertEquals("/foo/bar/bla/bobou/foo", mappingData.wrapperPath.toString());
+ assertTrue(mappingData.pathInfo.isNull());
+ assertTrue(mappingData.redirectPath.isNull());
+
+ mappingData.recycle();
uri.setString("/foo/bar/bla/bobou/foo");
uri.toChars();
uri.getCharChunk().setLimit(-1);
@@ -287,7 +314,7 @@ public class TestMapper extends LoggingBaseTest {
@Test
public void testAddRemoveContextVersion() throws Exception {
final String hostName = "iowejoiejfoiew";
- final int iowPos = 3;
+ final int iowPos = 4;
final String contextPath = "/foo/bar";
final int contextPos = 2;
@@ -394,7 +421,7 @@ public class TestMapper extends LoggingBaseTest {
@Test
public void testReloadContextVersion() throws Exception {
final String hostName = "iowejoiejfoiew";
- final int iowPos = 3;
+ final int iowPos = 4;
final String contextPath = "/foo/bar";
final int contextPos = 2;
diff --git a/test/org/apache/catalina/mapper/TestMapperPerformance.java b/test/org/apache/catalina/mapper/TestMapperPerformance.java
index f609394..2683362 100644
--- a/test/org/apache/catalina/mapper/TestMapperPerformance.java
+++ b/test/org/apache/catalina/mapper/TestMapperPerformance.java
@@ -26,24 +26,42 @@ public class TestMapperPerformance extends TestMapper {
@Test
public void testPerformance() throws Exception {
+ String[] requestedHostNames = new String[] {
+ "xxxxxxxxxxx",
+ "iowejoiejfoiew",
+ "iowejoiejfoiex",
+ "owefojiwefoi",
+ "owefojiwefoix",
+ "qwerty.net",
+ "foo.net",
+ "zzz.com",
+ "abc.com"};
+
+ for (String requestedHostName : requestedHostNames) {
+ testPerformance(requestedHostName);
+ }
+ }
+
+ private void testPerformance(String requestedHostName) throws Exception {
// Takes ~1s on markt's laptop. If this takes more than 5s something
// probably needs looking at. If this fails repeatedly then we may need
// to increase this limit.
final long maxTime = 5000;
- long time = testPerformanceImpl();
+ long time = testPerformanceImpl(requestedHostName);
+ log.info("Host [" + requestedHostName + "], Time [" + time + "]ms");
if (time >= maxTime) {
// Rerun to reject occasional failures, e.g. because of gc
log.warn("testPerformance() test completed in " + time + " ms");
- time = testPerformanceImpl();
+ time = testPerformanceImpl(requestedHostName);
log.warn("testPerformance() test rerun completed in " + time + " ms");
}
assertTrue(String.valueOf(time), time < maxTime);
}
- private long testPerformanceImpl() throws Exception {
+ private long testPerformanceImpl(String requestedHostName) throws Exception {
MappingData mappingData = new MappingData();
MessageBytes host = MessageBytes.newInstance();
- host.setString("iowejoiejfoiew");
+ host.setString(requestedHostName);
MessageBytes uri = MessageBytes.newInstance();
uri.setString("/foo/bar/blah/bobou/foo");
uri.toChars();
diff --git a/test/org/apache/catalina/mbeans/TestRegistration.java b/test/org/apache/catalina/mbeans/TestRegistration.java
index d4c76b6..e357a85 100644
--- a/test/org/apache/catalina/mbeans/TestRegistration.java
+++ b/test/org/apache/catalina/mbeans/TestRegistration.java
@@ -117,7 +117,7 @@ public class TestRegistration extends TomcatBaseTest {
",name=NonLoginAuthenticator",
"Tomcat:type=Valve,host=" + host + ",context=" + context +
",name=StandardContextValve",
- "Tomcat:type=WebappClassLoader,host=" + host + ",context=" + context,
+ "Tomcat:type=ParallelWebappClassLoader,host=" + host + ",context=" + context,
"Tomcat:type=WebResourceRoot,host=" + host + ",context=" + context,
"Tomcat:type=WebResourceRoot,host=" + host + ",context=" + context +
",name=Cache",
@@ -170,11 +170,6 @@ public class TestRegistration extends TomcatBaseTest {
combinedRealm.addRealm(nullRealm);
ctx.setRealm(combinedRealm);
- // Disable keep-alive otherwise request processing threads in keep-alive
- // won't shut down fast enough with BIO to de-register the processor
- // triggering a test failure
- tomcat.getConnector().setAttribute("maxKeepAliveRequests", Integer.valueOf(1));
-
tomcat.start();
getUrl("http://localhost:" + getPort());
@@ -195,12 +190,10 @@ public class TestRegistration extends TomcatBaseTest {
String protocol = tomcat.getConnector().getProtocolHandlerClassName();
if (protocol.indexOf("Nio2") > 0) {
protocol = "nio2";
- } else if (protocol.indexOf("Nio") > 0) {
- protocol = "nio";
} else if (protocol.indexOf("Apr") > 0) {
protocol = "apr";
} else {
- protocol = "bio";
+ protocol = "nio";
}
String index = tomcat.getConnector().getProperty("nameIndex").toString();
ArrayList<String> expected = new ArrayList<>(Arrays.asList(basicMBeanNames()));
diff --git a/test/org/apache/catalina/nonblocking/TestNonBlockingAPI.java b/test/org/apache/catalina/nonblocking/TestNonBlockingAPI.java
index ccf2be8..af33a9e 100644
--- a/test/org/apache/catalina/nonblocking/TestNonBlockingAPI.java
+++ b/test/org/apache/catalina/nonblocking/TestNonBlockingAPI.java
@@ -41,7 +41,6 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.junit.Assert;
-import org.junit.Assume;
import org.junit.Test;
import org.apache.catalina.Context;
@@ -51,14 +50,10 @@ import org.apache.catalina.startup.TesterServlet;
import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.startup.TomcatBaseTest;
import org.apache.catalina.valves.TesterAccessLogValve;
-import org.apache.juli.logging.Log;
-import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.buf.ByteChunk;
public class TestNonBlockingAPI extends TomcatBaseTest {
- private static final Log log = LogFactory.getLog(TestNonBlockingAPI.class);
-
private static final int CHUNK_SIZE = 1024 * 1024;
private static final int WRITE_SIZE = CHUNK_SIZE * 10;
private static final byte[] DATA = new byte[WRITE_SIZE];
@@ -91,12 +86,6 @@ public class TestNonBlockingAPI extends TomcatBaseTest {
@Test(expected=IOException.class)
public void testNonBlockingReadIgnoreIsReady() throws Exception {
- // TODO Investigate options to get this test to pass with the HTTP BIO
- // connector.
- Assume.assumeFalse(
- "Skipping as this test requires true non-blocking IO",
- getTomcatInstance().getConnector().getProtocol()
- .equals("org.apache.coyote.http11.Http11Protocol"));
doTestNonBlockingRead(true);
}
@@ -125,6 +114,15 @@ public class TestNonBlockingAPI extends TomcatBaseTest {
@Test
public void testNonBlockingWrite() throws Exception {
+ testNonBlockingWriteInternal(false);
+ }
+
+ @Test
+ public void testNonBlockingWriteWithKeepAlive() throws Exception {
+ testNonBlockingWriteInternal(true);
+ }
+
+ private void testNonBlockingWriteInternal(boolean keepAlive) throws Exception {
Tomcat tomcat = getTomcatInstance();
// No file system docBase required
Context ctx = tomcat.addContext("", null);
@@ -139,17 +137,25 @@ public class TestNonBlockingAPI extends TomcatBaseTest {
SocketFactory factory = SocketFactory.getDefault();
Socket s = factory.createSocket("localhost", getPort());
+ InputStream is = s.getInputStream();
+ byte[] buffer = new byte[8192];
+
ByteChunk result = new ByteChunk();
+
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.flush();
+ is.read(buffer);
+ }
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();
- byte[] buffer = new byte[8192];
-
int read = 0;
int readSinceLastPause = 0;
while (read != -1) {
@@ -175,7 +181,7 @@ public class TestNonBlockingAPI extends TomcatBaseTest {
int lineStart = 0;
int lineEnd = resultString.indexOf('\n', 0);
String line = resultString.substring(lineStart, lineEnd + 1);
- Assert.assertEquals("HTTP/1.1 200 OK\r\n", line);
+ Assert.assertEquals("HTTP/1.1 200 \r\n", line);
// Check headers - looking to see if response is chunked (it should be)
boolean chunked = false;
@@ -228,19 +234,19 @@ public class TestNonBlockingAPI extends TomcatBaseTest {
boolean found = false;
for (int i = totalBodyRead; i < (totalBodyRead + line.length()); i++) {
if (DATA[i] != resultBytes[lineStart + i - totalBodyRead]) {
- int dataStart = i - 16;
+ int dataStart = i - 64;
if (dataStart < 0) {
dataStart = 0;
}
- int dataEnd = i + 16;
+ int dataEnd = i + 64;
if (dataEnd > DATA.length) {
dataEnd = DATA.length;
}
- int resultStart = lineStart + i - totalBodyRead - 16;
+ int resultStart = lineStart + i - totalBodyRead - 64;
if (resultStart < 0) {
resultStart = 0;
}
- int resultEnd = lineStart + i - totalBodyRead + 16;
+ int resultEnd = lineStart + i - totalBodyRead + 64;
if (resultEnd > resultString.length()) {
resultEnd = resultString.length();
}
@@ -327,7 +333,7 @@ public class TestNonBlockingAPI extends TomcatBaseTest {
int lineStart = 0;
int lineEnd = resultString.indexOf('\n', 0);
String line = resultString.substring(lineStart, lineEnd + 1);
- Assert.assertEquals("HTTP/1.1 200 OK\r\n", line);
+ Assert.assertEquals("HTTP/1.1 200 \r\n", line);
// Listeners are invoked and access valve entries created on a different
// thread so give that thread a chance to complete its work.
@@ -499,25 +505,21 @@ public class TestNonBlockingAPI extends TomcatBaseTest {
@Override
public void onTimeout(AsyncEvent event) throws IOException {
log.info("onTimeout");
-
}
@Override
public void onStartAsync(AsyncEvent event) throws IOException {
log.info("onStartAsync");
-
}
@Override
public void onError(AsyncEvent event) throws IOException {
log.info("AsyncListener.onError");
-
}
@Override
public void onComplete(AsyncEvent event) throws IOException {
log.info("onComplete");
-
}
});
// step 2 - notify on read
diff --git a/test/org/apache/catalina/servlets/TestDefaultServlet.java b/test/org/apache/catalina/servlets/TestDefaultServlet.java
index 0c45f5c..f05831e 100644
--- a/test/org/apache/catalina/servlets/TestDefaultServlet.java
+++ b/test/org/apache/catalina/servlets/TestDefaultServlet.java
@@ -118,19 +118,21 @@ public class TestDefaultServlet extends TomcatBaseTest {
tomcat.start();
- TestGzipClient gzipClient = new TestGzipClient(getPort());
+ TestCompressedClient gzipClient = new TestCompressedClient(getPort());
gzipClient.reset();
gzipClient.setRequest(new String[] {
"GET /index.html HTTP/1.1" + CRLF +
"Host: localhost" + CRLF +
"Connection: Close" + CRLF +
- "Accept-Encoding: gzip" + CRLF + CRLF });
+ "Accept-Encoding: gzip, br" + CRLF + CRLF });
gzipClient.connect();
gzipClient.processRequest();
assertTrue(gzipClient.isResponse200());
List<String> responseHeaders = gzipClient.getResponseHeaders();
+ assertTrue(responseHeaders.contains("Content-Encoding: gzip"));
assertTrue(responseHeaders.contains("Content-Length: " + gzipSize));
+ assertTrue(responseHeaders.contains("Vary: accept-encoding"));
gzipClient.reset();
gzipClient.setRequest(new String[] {
@@ -144,6 +146,228 @@ public class TestDefaultServlet extends TomcatBaseTest {
assertTrue(responseHeaders.contains("Content-Type: text/html"));
assertFalse(responseHeaders.contains("Content-Encoding: gzip"));
assertTrue(responseHeaders.contains("Content-Length: " + indexSize));
+ assertTrue(responseHeaders.contains("Vary: accept-encoding"));
+ }
+
+ /*
+ * Verify serving of brotli compressed resources from context root.
+ */
+ @Test
+ public void testBrotliCompressedFile() throws Exception {
+
+ Tomcat tomcat = getTomcatInstance();
+
+ File appDir = new File("test/webapp");
+
+ long brSize = new File(appDir, "index.html.br").length();
+ long indexSize = new File(appDir, "index.html").length();
+
+ // app dir is relative to server home
+ Context ctxt = tomcat.addContext("", appDir.getAbsolutePath());
+ Wrapper defaultServlet = Tomcat.addServlet(ctxt, "default",
+ "org.apache.catalina.servlets.DefaultServlet");
+ defaultServlet.addInitParameter("precompressed", "true");
+
+ ctxt.addServletMappingDecoded("/", "default");
+ ctxt.addMimeMapping("html", "text/html");
+
+ tomcat.start();
+
+ TestCompressedClient client = new TestCompressedClient(getPort());
+
+ client.reset();
+ client.setRequest(new String[] {
+ "GET /index.html HTTP/1.1" + CRLF +
+ "Host: localhost" + CRLF +
+ "Connection: Close" + CRLF +
+ "Accept-Encoding: br, gzip" + CRLF + CRLF });
+ client.connect();
+ client.processRequest();
+ assertTrue(client.isResponse200());
+ List<String> responseHeaders = client.getResponseHeaders();
+ assertTrue(responseHeaders.contains("Content-Encoding: br"));
+ assertTrue(responseHeaders.contains("Content-Length: " + brSize));
+ assertTrue(responseHeaders.contains("Vary: accept-encoding"));
+
+ client.reset();
+ client.setRequest(new String[] {
+ "GET /index.html HTTP/1.1" + CRLF +
+ "Host: localhost" + CRLF +
+ "Connection: Close" + CRLF+ CRLF });
+ client.connect();
+ client.processRequest();
+ assertTrue(client.isResponse200());
+ responseHeaders = client.getResponseHeaders();
+ assertTrue(responseHeaders.contains("Content-Type: text/html"));
+ assertFalse(responseHeaders.contains("Content-Encoding"));
+ assertTrue(responseHeaders.contains("Content-Length: " + indexSize));
+ assertTrue(responseHeaders.contains("Vary: accept-encoding"));
+ }
+
+ /*
+ * Verify serving of custom compressed resources from context root.
+ */
+ @Test
+ public void testCustomCompressedFile() throws Exception {
+
+ Tomcat tomcat = getTomcatInstance();
+
+ File appDir = new File("test/webapp");
+
+ long brSize = new File(appDir, "index.html.br").length();
+ long gzSize = new File(appDir, "index.html.gz").length();
+
+ // app dir is relative to server home
+ Context ctxt = tomcat.addContext("", appDir.getAbsolutePath());
+ Wrapper defaultServlet = Tomcat.addServlet(ctxt, "default",
+ DefaultServlet.class.getName());
+ defaultServlet.addInitParameter("precompressed", "gzip=.gz,custom=.br");
+
+ ctxt.addServletMappingDecoded("/", "default");
+ ctxt.addMimeMapping("html", "text/html");
+
+ tomcat.start();
+
+ TestCompressedClient client = new TestCompressedClient(getPort());
+
+ client.reset();
+ 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 });
+ client.connect();
+ client.processRequest();
+ assertTrue(client.isResponse200());
+ List<String> responseHeaders = client.getResponseHeaders();
+ assertTrue(responseHeaders.contains("Content-Encoding: custom"));
+ assertTrue(responseHeaders.contains("Content-Length: " + brSize));
+ assertTrue(responseHeaders.contains("Vary: accept-encoding"));
+
+ client.reset();
+ 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 });
+ client.connect();
+ client.processRequest();
+ assertTrue(client.isResponse200());
+ responseHeaders = client.getResponseHeaders();
+ assertTrue(responseHeaders.contains("Content-Encoding: gzip"));
+ assertTrue(responseHeaders.contains("Content-Length: " + gzSize));
+ assertTrue(responseHeaders.contains("Vary: accept-encoding"));
+ }
+
+ /*
+ * Verify that "*" and "identity" values are handled correctly in accept-encoding header.
+ */
+ @Test
+ public void testIdentityAndStarAcceptEncodings() throws Exception {
+
+ Tomcat tomcat = getTomcatInstance();
+
+ File appDir = new File("test/webapp");
+
+ long brSize = new File(appDir, "index.html.br").length();
+ long indexSize = new File(appDir, "index.html").length();
+
+ // app dir is relative to server home
+ Context ctxt = tomcat.addContext("", appDir.getAbsolutePath());
+ Wrapper defaultServlet = Tomcat.addServlet(ctxt, "default",
+ DefaultServlet.class.getName());
+ defaultServlet.addInitParameter("precompressed", "br=.br,gzip=.gz");
+
+ ctxt.addServletMappingDecoded("/", "default");
+ ctxt.addMimeMapping("html", "text/html");
+
+ tomcat.start();
+
+ TestCompressedClient client = new TestCompressedClient(getPort());
+
+ client.reset();
+ 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 });
+ client.connect();
+ client.processRequest();
+ assertTrue(client.isResponse200());
+ List<String> responseHeaders = client.getResponseHeaders();
+ assertTrue(responseHeaders.contains("Content-Encoding: br"));
+ assertTrue(responseHeaders.contains("Content-Length: " + brSize));
+ assertTrue(responseHeaders.contains("Vary: accept-encoding"));
+
+ client.reset();
+ 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 });
+ client.connect();
+ client.processRequest();
+ assertTrue(client.isResponse200());
+ responseHeaders = client.getResponseHeaders();
+ assertFalse(responseHeaders.contains("Content-Encoding"));
+ assertTrue(responseHeaders.contains("Content-Length: " + indexSize));
+ assertTrue(responseHeaders.contains("Vary: accept-encoding"));
+ }
+
+ /*
+ * Verify preferring of brotli in default configuration for actual Firefox and Chrome requests.
+ */
+ @Test
+ public void testBrotliPreference() throws Exception {
+
+ Tomcat tomcat = getTomcatInstance();
+
+ File appDir = new File("test/webapp");
+
+ long brSize = new File(appDir, "index.html.br").length();
+
+ // app dir is relative to server home
+ Context ctxt = tomcat.addContext("", appDir.getAbsolutePath());
+ Wrapper defaultServlet = Tomcat.addServlet(ctxt, "default",
+ DefaultServlet.class.getName());
+ defaultServlet.addInitParameter("precompressed", "true");
+
+ ctxt.addServletMappingDecoded("/", "default");
+ ctxt.addMimeMapping("html", "text/html");
+
+ tomcat.start();
+
+ TestCompressedClient client = new TestCompressedClient(getPort());
+
+ // Firefox 45 Accept-Encoding
+ client.reset();
+ client.setRequest(new String[] {
+ "GET /index.html HTTP/1.1" + CRLF +
+ "Host: localhost" + CRLF +
+ "Connection: Close" + CRLF +
+ "Accept-Encoding: gzip, deflate, br" + CRLF + CRLF });
+ client.connect();
+ client.processRequest();
+ assertTrue(client.isResponse200());
+ List<String> responseHeaders = client.getResponseHeaders();
+ assertTrue(responseHeaders.contains("Content-Encoding: br"));
+ assertTrue(responseHeaders.contains("Content-Length: " + brSize));
+ assertTrue(responseHeaders.contains("Vary: accept-encoding"));
+
+ // Chrome 50 Accept-Encoding
+ client.reset();
+ 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 });
+ client.connect();
+ client.processRequest();
+ assertTrue(client.isResponse200());
+ responseHeaders = client.getResponseHeaders();
+ assertTrue(responseHeaders.contains("Content-Encoding: br"));
+ assertTrue(responseHeaders.contains("Content-Length: " + brSize));
+ assertTrue(responseHeaders.contains("Vary: accept-encoding"));
}
/*
@@ -342,7 +566,7 @@ public class TestDefaultServlet extends TomcatBaseTest {
assertTrue(client.isResponse404());
}
- /**
+ /*
* Verifies that the same Content-Length is returned for both GET and HEAD
* operations when a static resource served by the DefaultServlet is
* included.
@@ -387,9 +611,9 @@ public class TestDefaultServlet extends TomcatBaseTest {
}
}
- private static class TestGzipClient extends SimpleHttpClient {
+ private static class TestCompressedClient extends SimpleHttpClient {
- public TestGzipClient(int port) {
+ public TestCompressedClient(int port) {
setPort(port);
}
diff --git a/test/org/apache/catalina/session/Benchmarks.java b/test/org/apache/catalina/session/Benchmarks.java
index f2131f2..704a4fa 100644
--- a/test/org/apache/catalina/session/Benchmarks.java
+++ b/test/org/apache/catalina/session/Benchmarks.java
@@ -26,9 +26,12 @@ import static org.junit.Assert.fail;
import org.junit.Test;
+import org.apache.catalina.Context;
+import org.apache.catalina.Host;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.Session;
import org.apache.catalina.core.StandardContext;
+import org.apache.catalina.core.StandardHost;
/**
* Named Benchmarks so it is not automatically executed as part of the unit
@@ -152,8 +155,8 @@ public class Benchmarks {
* 16 threads - ~45,600ms
*/
@Test
- public void testManagerBaseCreateSession() {
- doTestManagerBaseCreateSession(1, 1000000);
+ public void testManagerBaseCreateSession() throws LifecycleException {
+ doTestManagerBaseCreateSession(1, 100000);
doTestManagerBaseCreateSession(2, 1000000);
doTestManagerBaseCreateSession(4, 1000000);
doTestManagerBaseCreateSession(16, 1000000);
@@ -164,16 +167,18 @@ public class Benchmarks {
private void doTestManagerBaseCreateSession(int threadCount,
- int iterCount) {
+ int iterCount) throws LifecycleException {
// Create a default session manager
StandardManager mgr = new StandardManager();
- try {
- mgr.startInternal();
- } catch (LifecycleException e) {
- // Ignore - this is expected
- }
- mgr.setContext(new StandardContext());
+ mgr.setPathname(null);
+ Host host = new StandardHost();
+ host.setName("unittest");
+ Context context = new StandardContext();
+ context.setPath("");
+ context.setParent(host);
+ mgr.setContext(context);
+ mgr.start();
mgr.generateSessionId();
while (mgr.sessionCreationTiming.size() <
ManagerBase.TIMING_STATS_CACHE_SIZE) {
diff --git a/test/org/apache/catalina/startup/SimpleHttpClient.java b/test/org/apache/catalina/startup/SimpleHttpClient.java
index d5e3a3b..f366475 100644
--- a/test/org/apache/catalina/startup/SimpleHttpClient.java
+++ b/test/org/apache/catalina/startup/SimpleHttpClient.java
@@ -394,52 +394,60 @@ public abstract class SimpleHttpClient {
responseBody = null;
}
+ public boolean responseLineStartsWith(String expected) {
+ String line = getResponseLine();
+ if (line == null) {
+ return false;
+ }
+ return line.startsWith(expected);
+ }
+
public boolean isResponse100() {
- return getResponseLine().startsWith(INFO_100);
+ return responseLineStartsWith(INFO_100);
}
public boolean isResponse200() {
- return getResponseLine().startsWith(OK_200);
+ return responseLineStartsWith(OK_200);
}
public boolean isResponse302() {
- return getResponseLine().startsWith(REDIRECT_302);
+ return responseLineStartsWith(REDIRECT_302);
}
public boolean isResponse303() {
- return getResponseLine().startsWith(REDIRECT_303);
+ return responseLineStartsWith(REDIRECT_303);
}
public boolean isResponse400() {
- return getResponseLine().startsWith(FAIL_400);
+ return responseLineStartsWith(FAIL_400);
}
public boolean isResponse404() {
- return getResponseLine().startsWith(FAIL_404);
+ return responseLineStartsWith(FAIL_404);
}
public boolean isResponse408() {
- return getResponseLine().startsWith(TIMEOUT_408);
+ return responseLineStartsWith(TIMEOUT_408);
}
public boolean isResponse413() {
- return getResponseLine().startsWith(FAIL_413);
+ return responseLineStartsWith(FAIL_413);
}
public boolean isResponse417() {
- return getResponseLine().startsWith(FAIL_417);
+ return responseLineStartsWith(FAIL_417);
}
public boolean isResponse50x() {
- return getResponseLine().startsWith(FAIL_50X);
+ return responseLineStartsWith(FAIL_50X);
}
public boolean isResponse500() {
- return getResponseLine().startsWith(FAIL_500);
+ return responseLineStartsWith(FAIL_500);
}
public boolean isResponse501() {
- return getResponseLine().startsWith(FAIL_501);
+ return responseLineStartsWith(FAIL_501);
}
public Socket getSocket() {
diff --git a/test/org/apache/catalina/startup/TestContextConfig.java b/test/org/apache/catalina/startup/TestContextConfig.java
index 322f694..e3dae05 100644
--- a/test/org/apache/catalina/startup/TestContextConfig.java
+++ b/test/org/apache/catalina/startup/TestContextConfig.java
@@ -73,7 +73,8 @@ public class TestContextConfig extends TomcatBaseTest {
File appDir = new File("test/webapp-fragments");
// app dir is relative to server home
- tomcat.addWebapp(null, "/test", appDir.getAbsolutePath());
+ Context ctx = tomcat.addWebapp(null, "/test", appDir.getAbsolutePath());
+ skipTldsForResourceJars(ctx);
tomcat.start();
@@ -201,4 +202,5 @@ public class TestContextConfig extends TomcatBaseTest {
Assert.assertTrue(result, result.indexOf(expectedBody) > -1);
}
}
+
}
diff --git a/test/org/apache/catalina/startup/TestContextConfigAnnotation.java b/test/org/apache/catalina/startup/TestContextConfigAnnotation.java
index ffc6744..a88f383 100644
--- a/test/org/apache/catalina/startup/TestContextConfigAnnotation.java
+++ b/test/org/apache/catalina/startup/TestContextConfigAnnotation.java
@@ -20,7 +20,9 @@ import java.beans.PropertyChangeListener;
import java.io.File;
import java.net.URISyntaxException;
import java.net.URL;
+import java.util.HashMap;
import java.util.HashSet;
+import java.util.Map;
import java.util.Set;
import javax.servlet.DispatcherType;
@@ -40,6 +42,7 @@ import org.junit.Test;
import org.apache.catalina.Context;
import org.apache.catalina.Loader;
import org.apache.catalina.core.StandardContext;
+import org.apache.catalina.startup.ContextConfig.JavaClassCacheEntry;
import org.apache.tomcat.util.descriptor.web.FilterDef;
import org.apache.tomcat.util.descriptor.web.FilterMap;
import org.apache.tomcat.util.descriptor.web.ServletDef;
@@ -56,11 +59,12 @@ public class TestContextConfigAnnotation {
@Test
public void testAnnotation() throws Exception {
WebXml webxml = new WebXml();
+ Map<String,JavaClassCacheEntry> javaClassCache = new HashMap<>();
ContextConfig config = new ContextConfig();
File pFile = paramClassResource(
"org/apache/catalina/startup/ParamServlet");
assertTrue(pFile.exists());
- config.processAnnotationsFile(pFile, webxml, false);
+ config.processAnnotationsFile(pFile, webxml, false, javaClassCache);
ServletDef servletDef = webxml.getServlets().get("param");
assertNotNull(servletDef);
assertEquals("Hello", servletDef.getParameterMap().get("foo"));
@@ -82,6 +86,7 @@ public class TestContextConfigAnnotation {
@Test
public void testOverwriteAnnotation() throws Exception {
WebXml webxml = new WebXml();
+ Map<String,JavaClassCacheEntry> javaClassCache = new HashMap<>();
ServletDef servletDef = new ServletDef();
servletDef.setServletName("param");
servletDef.setServletClass("org.apache.catalina.startup.ParamServlet");
@@ -99,7 +104,7 @@ public class TestContextConfigAnnotation {
File pFile = paramClassResource(
"org/apache/catalina/startup/ParamServlet");
assertTrue(pFile.exists());
- config.processAnnotationsFile(pFile, webxml, false);
+ config.processAnnotationsFile(pFile, webxml, false, javaClassCache);
assertEquals(servletDef, webxml.getServlets().get("param"));
@@ -122,16 +127,17 @@ public class TestContextConfigAnnotation {
@Test
public void testNoMapping() throws Exception {
WebXml webxml = new WebXml();
+ Map<String,JavaClassCacheEntry> javaClassCache = new HashMap<>();
ContextConfig config = new ContextConfig();
File pFile = paramClassResource(
"org/apache/catalina/startup/NoMappingParamServlet");
assertTrue(pFile.exists());
- config.processAnnotationsFile(pFile, webxml, false);
+ config.processAnnotationsFile(pFile, webxml, false, javaClassCache);
ServletDef servletDef = webxml.getServlets().get("param1");
assertNull(servletDef);
webxml.addServletMapping("/param", "param1");
- config.processAnnotationsFile(pFile, webxml, false);
+ config.processAnnotationsFile(pFile, webxml, false, javaClassCache);
servletDef = webxml.getServlets().get("param1");
assertNull(servletDef);
@@ -140,6 +146,7 @@ public class TestContextConfigAnnotation {
@Test
public void testSetupWebXMLNoMapping() throws Exception {
WebXml webxml = new WebXml();
+ Map<String,JavaClassCacheEntry> javaClassCache = new HashMap<>();
ServletDef servletDef = new ServletDef();
servletDef.setServletName("param1");
servletDef.setServletClass(
@@ -152,7 +159,7 @@ public class TestContextConfigAnnotation {
File pFile = paramClassResource(
"org/apache/catalina/startup/NoMappingParamServlet");
assertTrue(pFile.exists());
- config.processAnnotationsFile(pFile, webxml, false);
+ config.processAnnotationsFile(pFile, webxml, false, javaClassCache);
assertEquals("tomcat", servletDef.getParameterMap().get("foo"));
assertEquals("World!", servletDef.getParameterMap().get("bar"));
ServletDef servletDef1 = webxml.getServlets().get("param1");
@@ -163,12 +170,13 @@ public class TestContextConfigAnnotation {
@Test
public void testDuplicateMapping() throws Exception {
WebXml webxml = new WebXml();
+ Map<String,JavaClassCacheEntry> javaClassCache = new HashMap<>();
ContextConfig config = new ContextConfig();
File pFile = paramClassResource(
"org/apache/catalina/startup/DuplicateMappingParamServlet");
assertTrue(pFile.exists());
try {
- config.processAnnotationsFile(pFile, webxml, false);
+ config.processAnnotationsFile(pFile, webxml, false, javaClassCache);
fail();
} catch (IllegalArgumentException ex) {
// ignore
@@ -180,13 +188,14 @@ public class TestContextConfigAnnotation {
@Test
public void testFilterMapping() throws Exception {
WebXml webxml = new WebXml();
+ Map<String,JavaClassCacheEntry> javaClassCache = new HashMap<>();
ContextConfig config = new ContextConfig();
File sFile = paramClassResource(
"org/apache/catalina/startup/ParamServlet");
- config.processAnnotationsFile(sFile, webxml, false);
+ config.processAnnotationsFile(sFile, webxml, false, javaClassCache);
File fFile = paramClassResource(
"org/apache/catalina/startup/ParamFilter");
- config.processAnnotationsFile(fFile, webxml, false);
+ config.processAnnotationsFile(fFile, webxml, false, javaClassCache);
FilterDef fdef = webxml.getFilters().get("paramFilter");
assertNotNull(fdef);
assertEquals("Servlet says: ",fdef.getParameterMap().get("message"));
@@ -195,6 +204,7 @@ public class TestContextConfigAnnotation {
@Test
public void testOverwriteFilterMapping() throws Exception {
WebXml webxml = new WebXml();
+ Map<String,JavaClassCacheEntry> javaClassCache = new HashMap<>();
FilterDef filterDef = new FilterDef();
filterDef.setFilterName("paramFilter");
filterDef.setFilterClass("org.apache.catalina.startup.ParamFilter");
@@ -215,10 +225,10 @@ public class TestContextConfigAnnotation {
ContextConfig config = new ContextConfig();
File sFile = paramClassResource(
"org/apache/catalina/startup/ParamServlet");
- config.processAnnotationsFile(sFile, webxml, false);
+ config.processAnnotationsFile(sFile, webxml, false, javaClassCache);
File fFile = paramClassResource(
"org/apache/catalina/startup/ParamFilter");
- config.processAnnotationsFile(fFile, webxml, false);
+ config.processAnnotationsFile(fFile, webxml, false, javaClassCache);
FilterDef fdef = webxml.getFilters().get("paramFilter");
assertNotNull(fdef);
assertEquals(filterDef,fdef);
@@ -250,12 +260,13 @@ public class TestContextConfigAnnotation {
@Test
public void testDuplicateFilterMapping() throws Exception {
WebXml webxml = new WebXml();
+ Map<String,JavaClassCacheEntry> javaClassCache = new HashMap<>();
ContextConfig config = new ContextConfig();
File pFile = paramClassResource(
"org/apache/catalina/startup/DuplicateMappingParamFilter");
assertTrue(pFile.exists());
try {
- config.processAnnotationsFile(pFile, webxml, false);
+ config.processAnnotationsFile(pFile, webxml, false, javaClassCache);
fail();
} catch (IllegalArgumentException ex) {
// ignore
@@ -266,6 +277,7 @@ public class TestContextConfigAnnotation {
@Test
public void testCheckHandleTypes() throws Exception {
+ Map<String,JavaClassCacheEntry> javaClassCache = new HashMap<>();
ContextConfig config = new ContextConfig();
config.handlesTypesAnnotations = true;
config.handlesTypesNonAnnotations = true;
@@ -297,13 +309,13 @@ public class TestContextConfigAnnotation {
WebXml ignore = new WebXml();
File file = paramClassResource(
"org/apache/catalina/startup/ParamServlet");
- config.processAnnotationsFile(file, ignore, false);
+ config.processAnnotationsFile(file, ignore, false, javaClassCache);
file = paramClassResource("org/apache/catalina/startup/ParamFilter");
- config.processAnnotationsFile(file, ignore, false);
+ config.processAnnotationsFile(file, ignore, false, javaClassCache);
file = paramClassResource("org/apache/catalina/startup/TesterServlet");
- config.processAnnotationsFile(file, ignore, false);
+ config.processAnnotationsFile(file, ignore, false, javaClassCache);
file = paramClassResource("org/apache/catalina/startup/TestListener");
- config.processAnnotationsFile(file, ignore, false);
+ config.processAnnotationsFile(file, ignore, false, javaClassCache);
// Check right number of classes were noted to be handled
assertEquals(0, config.initializerClassMap.get(sciNone).size());
diff --git a/test/org/apache/catalina/startup/TestTomcatClassLoader.java b/test/org/apache/catalina/startup/TestTomcatClassLoader.java
index 9d3029e..2cec79c 100644
--- a/test/org/apache/catalina/startup/TestTomcatClassLoader.java
+++ b/test/org/apache/catalina/startup/TestTomcatClassLoader.java
@@ -31,7 +31,7 @@ import static org.junit.Assert.assertEquals;
import org.junit.Test;
import org.apache.catalina.Context;
-import org.apache.catalina.loader.WebappClassLoader;
+import org.apache.catalina.loader.WebappClassLoaderBase;
import org.apache.tomcat.util.buf.ByteChunk;
public class TestTomcatClassLoader extends TomcatBaseTest {
@@ -98,7 +98,7 @@ public class TestTomcatClassLoader extends TomcatBaseTest {
out.print("SYSTEM,");
} else if (custom == cl) {
out.print("CUSTOM,");
- } else if (cl instanceof WebappClassLoader) {
+ } else if (cl instanceof WebappClassLoaderBase) {
out.print("WEBAPP,");
} else {
out.print("OTHER,");
diff --git a/test/org/apache/catalina/startup/TomcatBaseTest.java b/test/org/apache/catalina/startup/TomcatBaseTest.java
index 9856c56..8b1b1fb 100644
--- a/test/org/apache/catalina/startup/TomcatBaseTest.java
+++ b/test/org/apache/catalina/startup/TomcatBaseTest.java
@@ -55,6 +55,7 @@ import org.apache.catalina.LifecycleState;
import org.apache.catalina.Manager;
import org.apache.catalina.Server;
import org.apache.catalina.Service;
+import org.apache.catalina.Session;
import org.apache.catalina.WebResourceRoot;
import org.apache.catalina.connector.Connector;
import org.apache.catalina.core.AprLifecycleListener;
@@ -66,13 +67,16 @@ import org.apache.catalina.valves.AccessLogValve;
import org.apache.catalina.webresources.StandardRoot;
import org.apache.coyote.http11.Http11NioProtocol;
import org.apache.tomcat.util.buf.ByteChunk;
-import org.apache.tomcat.websocket.CaseInsensitiveKeyMap;
+import org.apache.tomcat.util.collections.CaseInsensitiveKeyMap;
+import org.apache.tomcat.util.scan.StandardJarScanFilter;
+import org.apache.tomcat.util.scan.StandardJarScanner;
/**
* Base test case that provides a Tomcat instance for each test - mainly so we
* don't have to keep writing the cleanup code.
*/
public abstract class TomcatBaseTest extends LoggingBaseTest {
+ private static final int DEFAULT_CLIENT_TIMEOUT_MS = 300_000;
private Tomcat tomcat;
private boolean accessLogEnabled = false;
@@ -104,6 +108,11 @@ public abstract class TomcatBaseTest extends LoggingBaseTest {
File appDir = new File("test/webapp");
Context ctx = tomcat.addWebapp(null, "/test", appDir.getAbsolutePath());
+ StandardJarScanner scanner = (StandardJarScanner) ctx.getJarScanner();
+ StandardJarScanFilter filter = (StandardJarScanFilter) scanner.getJarScanFilter();
+ filter.setTldSkip(filter.getTldSkip() + ",testclasses");
+ filter.setPluggabilitySkip(filter.getPluggabilitySkip() + ",testclasses");
+
if (addJstl) {
File lib = new File("webapps/examples/WEB-INF/lib");
ctx.setResources(new StandardRoot(ctx));
@@ -622,13 +631,13 @@ public abstract class TomcatBaseTest extends LoggingBaseTest {
public static int headUrl(String path, ByteChunk out,
Map<String, List<String>> resHead) throws IOException {
- return methodUrl(path, out, 1000000, null, resHead, "HEAD");
+ return methodUrl(path, out, DEFAULT_CLIENT_TIMEOUT_MS, null, resHead, "HEAD");
}
public static int getUrl(String path, ByteChunk out,
Map<String, List<String>> reqHead,
Map<String, List<String>> resHead) throws IOException {
- return getUrl(path, out, 1000000, reqHead, resHead);
+ return getUrl(path, out, DEFAULT_CLIENT_TIMEOUT_MS, reqHead, resHead);
}
public static int getUrl(String path, ByteChunk out, int readTimeout,
@@ -762,10 +771,6 @@ public abstract class TomcatBaseTest extends LoggingBaseTest {
os.write(next);
os.flush();
}
- } catch (IOException ioe) {
- // Failed to write the request body. Server may have closed the
- // connection.
- ioe.printStackTrace();
}
int rc = connection.getResponseCode();
@@ -805,7 +810,6 @@ public abstract class TomcatBaseTest extends LoggingBaseTest {
// Use fast, insecure session ID generation for all tests
Server server = getServer();
for (Service service : server.findServices()) {
- @SuppressWarnings("deprecation")
Container e = service.getContainer();
for (Container h : e.findChildren()) {
for (Container c : h.findChildren()) {
@@ -862,4 +866,19 @@ public abstract class TomcatBaseTest extends LoggingBaseTest {
return FileVisitResult.CONTINUE;
}});
}
+
+
+ public static void skipTldsForResourceJars(Context context) {
+ StandardJarScanner scanner = (StandardJarScanner) context.getJarScanner();
+ StandardJarScanFilter filter = (StandardJarScanFilter) scanner.getJarScanFilter();
+ filter.setTldSkip(filter.getTldSkip() + ",resources*.jar");
+ }
+
+
+ public static void forceSessionMaxInactiveInterval(Context context, int newIntervalSecs) {
+ Session[] sessions = context.getManager().findSessions();
+ for (Session session : sessions) {
+ session.setMaxInactiveInterval(newIntervalSecs);
+ }
+ }
}
diff --git a/test/org/apache/catalina/startup/web-1lifecyclecallback.xml b/test/org/apache/catalina/startup/web-1lifecyclecallback.xml
index 1553e1e..2e14aa0 100644
--- a/test/org/apache/catalina/startup/web-1lifecyclecallback.xml
+++ b/test/org/apache/catalina/startup/web-1lifecyclecallback.xml
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="ISO-8859-1"?>
+<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
diff --git a/test/org/apache/catalina/startup/web-1ordering.xml b/test/org/apache/catalina/startup/web-1ordering.xml
index 427b365..54ec51f 100644
--- a/test/org/apache/catalina/startup/web-1ordering.xml
+++ b/test/org/apache/catalina/startup/web-1ordering.xml
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="ISO-8859-1"?>
+<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
diff --git a/test/org/apache/catalina/startup/web-2lifecyclecallback.xml b/test/org/apache/catalina/startup/web-2lifecyclecallback.xml
index 47ca857..5b06d00 100644
--- a/test/org/apache/catalina/startup/web-2lifecyclecallback.xml
+++ b/test/org/apache/catalina/startup/web-2lifecyclecallback.xml
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="ISO-8859-1"?>
+<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
diff --git a/test/org/apache/catalina/startup/web-2ordering.xml b/test/org/apache/catalina/startup/web-2ordering.xml
index 9f56ca1..da6d22b 100644
--- a/test/org/apache/catalina/startup/web-2ordering.xml
+++ b/test/org/apache/catalina/startup/web-2ordering.xml
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="ISO-8859-1"?>
+<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
diff --git a/test/org/apache/catalina/startup/web-fragment-1name.xml b/test/org/apache/catalina/startup/web-fragment-1name.xml
index f3742ed..fc3be43 100644
--- a/test/org/apache/catalina/startup/web-fragment-1name.xml
+++ b/test/org/apache/catalina/startup/web-fragment-1name.xml
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="ISO-8859-1"?>
+<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
diff --git a/test/org/apache/catalina/startup/web-fragment-1ordering.xml b/test/org/apache/catalina/startup/web-fragment-1ordering.xml
index e8dc16b..c534bf4 100644
--- a/test/org/apache/catalina/startup/web-fragment-1ordering.xml
+++ b/test/org/apache/catalina/startup/web-fragment-1ordering.xml
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="ISO-8859-1"?>
+<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
diff --git a/test/org/apache/catalina/startup/web-fragment-2name.xml b/test/org/apache/catalina/startup/web-fragment-2name.xml
index af71de1..ffa5e04 100644
--- a/test/org/apache/catalina/startup/web-fragment-2name.xml
+++ b/test/org/apache/catalina/startup/web-fragment-2name.xml
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="ISO-8859-1"?>
+<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
diff --git a/test/org/apache/catalina/startup/web-fragment-2ordering.xml b/test/org/apache/catalina/startup/web-fragment-2ordering.xml
index e57407b..46fe430 100644
--- a/test/org/apache/catalina/startup/web-fragment-2ordering.xml
+++ b/test/org/apache/catalina/startup/web-fragment-2ordering.xml
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="ISO-8859-1"?>
+<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
diff --git a/test/org/apache/catalina/tribes/demos/EchoRpcTest.java b/test/org/apache/catalina/tribes/demos/EchoRpcTest.java
index 5133bd2..39de1db 100644
--- a/test/org/apache/catalina/tribes/demos/EchoRpcTest.java
+++ b/test/org/apache/catalina/tribes/demos/EchoRpcTest.java
@@ -53,8 +53,6 @@ public class EchoRpcTest implements RpcCallback, Runnable {
*
* @param msg Serializable
* @param sender Member
- * TODO Implement this org.apache.catalina.tribes.tipis.RpcCallback
- * method
*/
@Override
public void leftOver(Serializable msg, Member sender) {
@@ -66,8 +64,6 @@ public class EchoRpcTest implements RpcCallback, Runnable {
* @param msg Serializable
* @param sender Member
* @return Serializable - null if no reply should be sent
- * TODO Implement this org.apache.catalina.tribes.tipis.RpcCallback
- * method
*/
@Override
public Serializable replyRequest(Serializable msg, Member sender) {
diff --git a/test/org/apache/catalina/tribes/demos/IntrospectionUtils.java b/test/org/apache/catalina/tribes/demos/IntrospectionUtils.java
index 62f5672..258d481 100644
--- a/test/org/apache/catalina/tribes/demos/IntrospectionUtils.java
+++ b/test/org/apache/catalina/tribes/demos/IntrospectionUtils.java
@@ -23,14 +23,16 @@ import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Hashtable;
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
+
/**
* Utils for introspection and reflection
*/
public final class IntrospectionUtils {
- private static final org.apache.juli.logging.Log log=
- org.apache.juli.logging.LogFactory.getLog( IntrospectionUtils.class );
+ private static final Log log = LogFactory.getLog(IntrospectionUtils.class);
/*
* Find a method with the right name If found, call the method ( if param is
diff --git a/test/org/apache/catalina/tribes/demos/LoadTest.java b/test/org/apache/catalina/tribes/demos/LoadTest.java
index d4d21ef..b139ca8 100644
--- a/test/org/apache/catalina/tribes/demos/LoadTest.java
+++ b/test/org/apache/catalina/tribes/demos/LoadTest.java
@@ -32,7 +32,7 @@ import org.apache.juli.logging.LogFactory;
public class LoadTest implements MembershipListener,ChannelListener, Runnable {
private static final Log log = LogFactory.getLog(LoadTest.class);
public static int size = 24000;
- public static Object mutex = new Object();
+ public static final Object mutex = new Object();
public boolean doRun = true;
public long bytesReceived = 0;
@@ -241,7 +241,7 @@ public class LoadTest implements MembershipListener,ChannelListener, Runnable {
public static class LoadMessage extends ByteMessage {
public static byte[] outdata = new byte[size];
- public static Random r = new Random();
+ public static final Random r = new Random();
public static int getMessageSize (LoadMessage msg) {
return msg.getMessage().length;
}
diff --git a/test/org/apache/catalina/tribes/demos/MapDemo.java b/test/org/apache/catalina/tribes/demos/MapDemo.java
index 3993954..6d0aefc 100644
--- a/test/org/apache/catalina/tribes/demos/MapDemo.java
+++ b/test/org/apache/catalina/tribes/demos/MapDemo.java
@@ -411,7 +411,7 @@ public class MapDemo implements ChannelListener, MembershipListener{
dataModel.getValueAt(-1,-1);
}
- public static Random random = new Random();
+ public static final Random random = new Random();
public static String random(int count, int start, int end, boolean letters, boolean numbers,
char[] chars ) {
if (count == 0) {
diff --git a/test/org/apache/catalina/tribes/test/NioSenderTest.java b/test/org/apache/catalina/tribes/test/NioSenderTest.java
index 01edaa0..43c7112 100644
--- a/test/org/apache/catalina/tribes/test/NioSenderTest.java
+++ b/test/org/apache/catalina/tribes/test/NioSenderTest.java
@@ -50,12 +50,7 @@ public class NioSenderTest {
}
public void init() throws Exception {
- synchronized (Selector.class) {
- // Selector.open() isn't thread safe
- // http://bugs.sun.com/view_bug.do?bug_id=6427854
- // Affects 1.6.0_29, fixed in 1.7.0_01
- selector = Selector.open();
- }
+ selector = Selector.open();
mbr = new MemberImpl("localhost",4444,0);
NioSender sender = new NioSender();
sender.setDestination(mbr);
diff --git a/test/org/apache/catalina/tribes/test/channel/TestDataIntegrity.java b/test/org/apache/catalina/tribes/test/channel/TestDataIntegrity.java
index c2d2d06..06806f9 100644
--- a/test/org/apache/catalina/tribes/test/channel/TestDataIntegrity.java
+++ b/test/org/apache/catalina/tribes/test/channel/TestDataIntegrity.java
@@ -171,7 +171,7 @@ public class TestDataIntegrity {
public int length;
public byte[] data;
public byte key;
- public static Random r = new Random();
+ public static final Random r = new Random();
public static Data createRandomData() {
int i = r.nextInt();
i = ( i % 127 );
diff --git a/test/org/apache/catalina/tribes/test/channel/TestMulticastPackages.java b/test/org/apache/catalina/tribes/test/channel/TestMulticastPackages.java
index ce13a68..7186e54 100644
--- a/test/org/apache/catalina/tribes/test/channel/TestMulticastPackages.java
+++ b/test/org/apache/catalina/tribes/test/channel/TestMulticastPackages.java
@@ -214,7 +214,7 @@ public class TestMulticastPackages {
public byte[] data;
public byte key;
public boolean hasNr = false;
- public static Random r = new Random();
+ public static final Random r = new Random();
public static Data createRandomData() {
return createRandomData(ChannelReceiver.MAX_UDP_SIZE);
}
diff --git a/test/org/apache/catalina/tribes/test/channel/TestRemoteProcessException.java b/test/org/apache/catalina/tribes/test/channel/TestRemoteProcessException.java
index 4cc30d3..b70a292 100644
--- a/test/org/apache/catalina/tribes/test/channel/TestRemoteProcessException.java
+++ b/test/org/apache/catalina/tribes/test/channel/TestRemoteProcessException.java
@@ -136,7 +136,7 @@ public class TestRemoteProcessException {
public byte[] data;
public byte key;
public boolean error = false;
- public static Random r = new Random();
+ public static final Random r = new Random();
public static Data createRandomData(boolean error) {
int i = r.nextInt();
i = ( i % 127 );
diff --git a/test/org/apache/catalina/tribes/test/channel/TestUdpPackages.java b/test/org/apache/catalina/tribes/test/channel/TestUdpPackages.java
index 74fabf7..dc12e44 100644
--- a/test/org/apache/catalina/tribes/test/channel/TestUdpPackages.java
+++ b/test/org/apache/catalina/tribes/test/channel/TestUdpPackages.java
@@ -255,7 +255,7 @@ public class TestUdpPackages {
public byte[] data;
public byte key;
public boolean hasNr = false;
- public static Random r = new Random();
+ public static final Random r = new Random();
public static Data createRandomData() {
return createRandomData(ChannelReceiver.MAX_UDP_SIZE);
}
diff --git a/test/org/apache/catalina/tribes/test/transport/SocketNioReceive.java b/test/org/apache/catalina/tribes/test/transport/SocketNioReceive.java
index 2ea3a18..5ce21fe 100644
--- a/test/org/apache/catalina/tribes/test/transport/SocketNioReceive.java
+++ b/test/org/apache/catalina/tribes/test/transport/SocketNioReceive.java
@@ -35,7 +35,7 @@ public class SocketNioReceive {
static DecimalFormat df = new DecimalFormat("##.00");
static double seconds = 0;
- protected static Object mutex = new Object();
+ protected static final Object mutex = new Object();
public static void main(String[] args) throws Exception {
Member mbr = new MemberImpl("localhost", 9999, 0);
ChannelData data = new ChannelData();
diff --git a/test/org/apache/catalina/tribes/test/transport/SocketNioSend.java b/test/org/apache/catalina/tribes/test/transport/SocketNioSend.java
index 5c71c6d..e865dd7 100644
--- a/test/org/apache/catalina/tribes/test/transport/SocketNioSend.java
+++ b/test/org/apache/catalina/tribes/test/transport/SocketNioSend.java
@@ -32,13 +32,7 @@ import org.apache.catalina.tribes.transport.nio.NioSender;
public class SocketNioSend {
public static void main(String[] args) throws Exception {
- Selector selector;
- synchronized (Selector.class) {
- // Selector.open() isn't thread safe
- // http://bugs.sun.com/view_bug.do?bug_id=6427854
- // Affects 1.6.0_29, fixed in 1.7.0_01
- selector = Selector.open();
- }
+ Selector selector = Selector.open();
Member mbr = new MemberImpl("localhost", 9999, 0);
ChannelData data = new ChannelData();
data.setOptions(Channel.SEND_OPTIONS_BYTE_MESSAGE);
diff --git a/test/org/apache/catalina/tribes/test/transport/SocketNioValidateSend.java b/test/org/apache/catalina/tribes/test/transport/SocketNioValidateSend.java
index f15f2f9..0a95826 100644
--- a/test/org/apache/catalina/tribes/test/transport/SocketNioValidateSend.java
+++ b/test/org/apache/catalina/tribes/test/transport/SocketNioValidateSend.java
@@ -30,13 +30,7 @@ import org.apache.catalina.tribes.transport.nio.NioSender;
public class SocketNioValidateSend {
public static void main(String[] args) throws Exception {
- Selector selector;
- synchronized (Selector.class) {
- // Selector.open() isn't thread safe
- // http://bugs.sun.com/view_bug.do?bug_id=6427854
- // Affects 1.6.0_29, fixed in 1.7.0_01
- selector = Selector.open();
- }
+ Selector selector = Selector.open();
Member mbr = new MemberImpl("localhost", 9999, 0);
byte seq = 0;
byte[] buf = new byte[50000];
diff --git a/test/org/apache/catalina/util/TestConversions.java b/test/org/apache/catalina/util/TestConversions.java
deleted file mode 100644
index 110a0e5..0000000
--- a/test/org/apache/catalina/util/TestConversions.java
+++ /dev/null
@@ -1,37 +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.io.IOException;
-
-import static org.junit.Assert.assertEquals;
-import org.junit.Test;
-
-public class TestConversions {
-
- @Test
- public void testByteArrayToLong() throws IOException {
- assertEquals(0L, Conversions.byteArrayToLong(new byte[] { 0 }));
- assertEquals(1L, Conversions.byteArrayToLong(new byte[] { 1 }));
- assertEquals(0xFF, Conversions.byteArrayToLong(new byte[] { -1 }));
- assertEquals(0xFFFF,
- Conversions.byteArrayToLong(new byte[] { -1, -1 }));
- assertEquals(0xFFFFFF,
- Conversions.byteArrayToLong(new byte[] { -1, -1, -1 }));
- }
-
-}
diff --git a/test/org/apache/catalina/valves/Benchmarks.java b/test/org/apache/catalina/valves/Benchmarks.java
index ad0c81a..be5a6ab 100644
--- a/test/org/apache/catalina/valves/Benchmarks.java
+++ b/test/org/apache/catalina/valves/Benchmarks.java
@@ -193,7 +193,7 @@ public class Benchmarks {
try {
index = Integer.parseInt(month) - 1;
} catch (Throwable t) {
- index = 0; // Can not happen, in theory
+ index = 0; // cannot happen, in theory
}
return (months[index]);
}
diff --git a/test/org/apache/catalina/webresources/war/TestHandlerIntegration.java b/test/org/apache/catalina/webresources/war/TestHandlerIntegration.java
index 835e70c..15f75a3 100644
--- a/test/org/apache/catalina/webresources/war/TestHandlerIntegration.java
+++ b/test/org/apache/catalina/webresources/war/TestHandlerIntegration.java
@@ -35,6 +35,7 @@ public class TestHandlerIntegration extends TomcatBaseTest {
File docBase = new File("test/webresources/war-url-connection.war");
Context ctx = tomcat.addWebapp("/test", docBase.getAbsolutePath());
+ skipTldsForResourceJars(ctx);
((StandardHost) tomcat.getHost()).setUnpackWARs(false);
diff --git a/test/org/apache/coyote/TestIoTimeouts.java b/test/org/apache/coyote/TestIoTimeouts.java
new file mode 100644
index 0000000..a400bc4
--- /dev/null
+++ b/test/org/apache/coyote/TestIoTimeouts.java
@@ -0,0 +1,241 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.coyote;
+
+import java.io.IOException;
+
+import javax.servlet.AsyncContext;
+import javax.servlet.ReadListener;
+import javax.servlet.ServletException;
+import javax.servlet.ServletInputStream;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.WriteListener;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+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;
+
+public class TestIoTimeouts extends TomcatBaseTest {
+
+ @Test
+ public void testNonBlockingReadWithNoTimeout() {
+ // Sends complete request in 3 packets
+ ChunkedClient client = new ChunkedClient(true);
+ client.doRequest();
+ Assert.assertTrue(client.isResponse200());
+ Assert.assertTrue(client.isResponseBodyOK());
+ Assert.assertNull(EchoListener.t);
+ }
+
+
+ @Test
+ public void testNonBlockingReadTimeout() {
+ // Sends incomplete request (no end chunk) so read times out
+ ChunkedClient client = new ChunkedClient(false);
+ client.doRequest();
+ Assert.assertFalse(client.isResponse200());
+ Assert.assertFalse(client.isResponseBodyOK());
+ // Socket will be closed before the error handler runs. Closing the
+ // socket triggers the client code's return from the doRequest() method
+ // above so we need to wait at this point for the error handler to be
+ // triggered.
+ int count = 0;
+ // Shouldn't need to wait long but allow plenty of time as the CI
+ // systems are sometimes slow.
+ while (count < 100 && EchoListener.t == null) {
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException e) {
+ // Ignore
+ }
+ count++;
+ }
+ Assert.assertNotNull(EchoListener.t);
+ }
+
+
+ private class ChunkedClient extends SimpleHttpClient {
+
+ private final boolean sendEndChunk;
+
+
+ public ChunkedClient(boolean sendEndChunk) {
+ this.sendEndChunk = sendEndChunk;
+ }
+
+
+ private Exception doRequest() {
+
+ Tomcat tomcat = getTomcatInstance();
+
+ Context root = tomcat.addContext("", TEMP_DIR);
+ Wrapper w = Tomcat.addServlet(root, "Test", new NonBlockingEchoServlet());
+ w.setAsyncSupported(true);
+ root.addServletMappingDecoded("/test", "Test");
+
+ try {
+ tomcat.start();
+ setPort(tomcat.getConnector().getLocalPort());
+
+ // Open connection
+ connect();
+
+ int packetCount = 2;
+ if (sendEndChunk) {
+ packetCount++;
+ }
+
+ String[] request = new String[packetCount];
+ request[0] =
+ "POST /test HTTP/1.1" + CRLF +
+ "Host: localhost:8080" + CRLF +
+ "Transfer-Encoding: chunked" + CRLF +
+ "Connection: close" + CRLF +
+ CRLF;
+ request[1] =
+ "b8" + CRLF +
+ "{" + CRLF +
+ " \"tenantId\": \"dotCom\", " + CRLF +
+ " \"locale\": \"en-US\", " + CRLF +
+ " \"defaultZoneId\": \"25\", " + CRLF +
+ " \"itemIds\": [\"StaplesUSCAS/en-US/2/<EOF>/<EOF>\"] , " + CRLF +
+ " \"assetStoreId\": \"5051\", " + CRLF +
+ " \"zipCode\": \"98109\"" + CRLF +
+ "}" + CRLF;
+ if (sendEndChunk) {
+ request[2] =
+ "0" + CRLF +
+ CRLF;
+ }
+
+ setRequest(request);
+ processRequest(); // blocks until response has been read
+
+ // Close the connection
+ disconnect();
+ } catch (Exception e) {
+ return e;
+ }
+ return null;
+ }
+
+ @Override
+ public boolean isResponseBodyOK() {
+ if (getResponseBody() == null) {
+ return false;
+ }
+ if (!getResponseBody().contains("98109")) {
+ return false;
+ }
+ return true;
+ }
+
+ }
+
+
+ private static class NonBlockingEchoServlet extends HttpServlet {
+
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ protected void doPost(HttpServletRequest req, HttpServletResponse resp)
+ throws ServletException, IOException {
+
+ // Need to be in async mode to use non-blocking I/O
+ AsyncContext ac = req.startAsync();
+ ac.setTimeout(10000);
+
+ ServletInputStream sis = null;
+ ServletOutputStream sos = null;
+
+ try {
+ sis = req.getInputStream();
+ sos = resp.getOutputStream();
+ } catch (IOException ioe) {
+ throw new ServletException(ioe);
+ }
+
+ EchoListener listener = new EchoListener(ac, sis, sos);
+ sis.setReadListener(listener);
+ sos.setWriteListener(listener);
+ }
+ }
+
+
+ private static class EchoListener implements ReadListener, WriteListener {
+
+ private static volatile Throwable t;
+
+ private final AsyncContext ac;
+ private final ServletInputStream sis;
+ private final ServletOutputStream sos;
+ private final byte[] buffer = new byte[8192];
+
+ public EchoListener(AsyncContext ac, ServletInputStream sis, ServletOutputStream sos) {
+ t = null;
+ this.ac = ac;
+ this.sis = sis;
+ this.sos = sos;
+ }
+
+ @Override
+ public void onWritePossible() throws IOException {
+ if (sis.isFinished()) {
+ sos.flush();
+ ac.complete();
+ return;
+ }
+ while (sis.isReady()) {
+ int read = sis.read(buffer);
+ if (read > 0) {
+ sos.write(buffer, 0, read);
+ if (!sos.isReady()) {
+ break;
+ }
+ }
+ }
+ }
+
+ @Override
+ public void onDataAvailable() throws IOException {
+ if (sos.isReady()) {
+ onWritePossible();
+ }
+ }
+
+ @Override
+ public void onAllDataRead() throws IOException {
+ if (sos.isReady()) {
+ onWritePossible();
+ }
+ }
+
+ @Override
+ public void onError(Throwable throwable) {
+ t = throwable;
+ ac.complete();
+ }
+ }
+}
diff --git a/test/org/apache/coyote/ajp/SimpleAjpClient.java b/test/org/apache/coyote/ajp/SimpleAjpClient.java
index b70e3a2..bb34d04 100644
--- a/test/org/apache/coyote/ajp/SimpleAjpClient.java
+++ b/test/org/apache/coyote/ajp/SimpleAjpClient.java
@@ -388,9 +388,8 @@ public class SimpleAjpClient {
TesterAjpMessage message = new TesterAjpMessage(packetSize);
byte[] buf = message.getBuffer();
- int headerLength = message.getHeaderLength();
- read(is, buf, 0, headerLength);
+ read(is, buf, 0, Constants.H_SIZE);
int messageLength = message.processHeader(false);
if (messageLength < 0) {
@@ -404,7 +403,7 @@ public class SimpleAjpClient {
"] for buffer length [" +
Integer.valueOf(buf.length) + "]");
}
- read(is, buf, headerLength, messageLength);
+ read(is, buf, Constants.H_SIZE, messageLength);
return message;
}
}
diff --git a/test/org/apache/coyote/ajp/TestAbstractAjpProcessor.java b/test/org/apache/coyote/ajp/TestAbstractAjpProcessor.java
index ecf06c0..9f9f434 100644
--- a/test/org/apache/coyote/ajp/TestAbstractAjpProcessor.java
+++ b/test/org/apache/coyote/ajp/TestAbstractAjpProcessor.java
@@ -50,17 +50,15 @@ public class TestAbstractAjpProcessor extends TomcatBaseTest {
// Has a protocol been specified
String protocol = System.getProperty("tomcat.test.protocol");
- // Use BIO by default
+ // Use NIO by default
if (protocol == null) {
- protocol = "org.apache.coyote.ajp.AjpProtocol";
+ protocol = "org.apache.coyote.ajp.AjpNioProtocol";
} else if (protocol.contains("Nio2")) {
protocol = "org.apache.coyote.ajp.AjpNio2Protocol";
- } else if (protocol.contains("Nio")) {
- protocol = "org.apache.coyote.ajp.AjpNioProtocol";
} else if (protocol.contains("Apr")) {
protocol = "org.apache.coyote.ajp.AjpAprProtocol";
} else {
- protocol = "org.apache.coyote.ajp.AjpProtocol";
+ protocol = "org.apache.coyote.ajp.AjpNioProtocol";
}
return protocol;
@@ -312,7 +310,7 @@ public class TestAbstractAjpProcessor extends TomcatBaseTest {
}
// Expect 3 packets: headers, body, end
- validateResponseHeaders(responseHeaders, 200, "OK");
+ validateResponseHeaders(responseHeaders, 200, "200");
String body = extractResponseBody(ajpClient.readMessage());
RequestDescriptor result = SnoopResult.parse(body);
@@ -513,7 +511,7 @@ public class TestAbstractAjpProcessor extends TomcatBaseTest {
TesterAjpMessage responseHeaders = ajpClient.sendMessage(forwardMessage);
// Expect 3 packets: headers, body, end
- validateResponseHeaders(responseHeaders, 403, "Forbidden");
+ validateResponseHeaders(responseHeaders, 403, "403");
//TesterAjpMessage responseBody = ajpClient.readMessage();
//validateResponseBody(responseBody, HelloWorldServlet.RESPONSE_TEXT);
validateResponseEnd(ajpClient.readMessage(), false);
@@ -527,7 +525,7 @@ public class TestAbstractAjpProcessor extends TomcatBaseTest {
responseHeaders = ajpClient.sendMessage(forwardMessage);
// Expect 3 packets: headers, body, end
- validateResponseHeaders(responseHeaders, 403, "Forbidden");
+ validateResponseHeaders(responseHeaders, 403, "403");
//responseBody = ajpClient.readMessage();
//validateResponseBody(responseBody, HelloWorldServlet.RESPONSE_TEXT);
validateResponseEnd(ajpClient.readMessage(), false);
@@ -541,7 +539,7 @@ public class TestAbstractAjpProcessor extends TomcatBaseTest {
responseHeaders = ajpClient.sendMessage(forwardMessage);
// Expect 3 packets: headers, body, end
- validateResponseHeaders(responseHeaders, 200, "OK");
+ validateResponseHeaders(responseHeaders, 200, "200");
TesterAjpMessage responseBody = ajpClient.readMessage();
validateResponseBody(responseBody, HelloWorldServlet.RESPONSE_TEXT);
validateResponseEnd(ajpClient.readMessage(), true);
@@ -578,7 +576,7 @@ public class TestAbstractAjpProcessor extends TomcatBaseTest {
for (int i = 0; i < 2; i++) {
TesterAjpMessage responseHeaders = ajpClient.sendMessage(forwardMessage);
// Expect 3 packets: headers, body, end
- validateResponseHeaders(responseHeaders, 200, "OK");
+ validateResponseHeaders(responseHeaders, 200, "200");
TesterAjpMessage responseBody = ajpClient.readMessage();
validateResponseBody(responseBody, HelloWorldServlet.RESPONSE_TEXT);
validateResponseEnd(ajpClient.readMessage(), true);
@@ -595,14 +593,14 @@ public class TestAbstractAjpProcessor extends TomcatBaseTest {
@Test
public void testPost() throws Exception {
- doTestPost(false, HttpServletResponse.SC_OK, "OK");
+ doTestPost(false, HttpServletResponse.SC_OK, "200");
}
@Test
public void testPostMultipleContentLength() throws Exception {
// Multiple content lengths
- doTestPost(true, HttpServletResponse.SC_BAD_REQUEST, "Bad Request");
+ doTestPost(true, HttpServletResponse.SC_BAD_REQUEST, "400");
}
@@ -681,7 +679,7 @@ public class TestAbstractAjpProcessor extends TomcatBaseTest {
ajpClient.sendMessage(forwardMessage, null);
// Expect 2 messages: headers, end
- validateResponseHeaders(responseHeaders, 304, "Not Modified");
+ validateResponseHeaders(responseHeaders, 304, "304");
validateResponseEnd(ajpClient.readMessage(), true);
// Double check the connection is still open
@@ -740,7 +738,7 @@ public class TestAbstractAjpProcessor extends TomcatBaseTest {
ajpClient.sendMessage(forwardMessage, null);
// Expect 3 messages: headers, body, end
- validateResponseHeaders(responseHeaders, 200, "OK");
+ validateResponseHeaders(responseHeaders, 200, "200");
validateResponseBody(ajpClient.readMessage(),
"Request Body length in bytes: 0");
validateResponseEnd(ajpClient.readMessage(), true);
@@ -795,7 +793,7 @@ public class TestAbstractAjpProcessor extends TomcatBaseTest {
TesterAjpMessage responseHeaders = ajpClient.sendMessage(forwardMessage);
// Expect 3 messages: headers, body, end for a valid request
- validateResponseHeaders(responseHeaders, 200, "OK");
+ validateResponseHeaders(responseHeaders, 200, "200");
TesterAjpMessage responseBody = ajpClient.readMessage();
Assert.assertTrue(responseBody.len > 15000);
validateResponseEnd(ajpClient.readMessage(), true);
diff --git a/test/org/apache/coyote/http11/TestAbstractHttp11Processor.java b/test/org/apache/coyote/http11/TestAbstractHttp11Processor.java
deleted file mode 100644
index b97b34c..0000000
--- a/test/org/apache/coyote/http11/TestAbstractHttp11Processor.java
+++ /dev/null
@@ -1,948 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.coyote.http11;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.OutputStream;
-import java.io.OutputStreamWriter;
-import java.io.PrintWriter;
-import java.io.Reader;
-import java.io.Writer;
-import java.net.InetSocketAddress;
-import java.net.Socket;
-import java.net.SocketAddress;
-import java.nio.CharBuffer;
-import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.concurrent.CountDownLatch;
-
-import javax.servlet.AsyncContext;
-import javax.servlet.DispatcherType;
-import javax.servlet.ServletException;
-import javax.servlet.http.Cookie;
-import javax.servlet.http.HttpServlet;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
-import org.junit.Assert;
-import org.junit.Test;
-
-import org.apache.catalina.Context;
-import org.apache.catalina.Wrapper;
-import org.apache.catalina.startup.SimpleHttpClient;
-import org.apache.catalina.startup.TesterServlet;
-import org.apache.catalina.startup.Tomcat;
-import org.apache.catalina.startup.TomcatBaseTest;
-import org.apache.tomcat.util.buf.B2CConverter;
-import org.apache.tomcat.util.buf.ByteChunk;
-import org.apache.tomcat.util.descriptor.web.SecurityCollection;
-import org.apache.tomcat.util.descriptor.web.SecurityConstraint;
-
-public class TestAbstractHttp11Processor extends TomcatBaseTest {
-
- @Test
- public void testResponseWithErrorChunked() throws Exception {
- Tomcat tomcat = getTomcatInstance();
-
- // This setting means the connection will be closed at the end of the
- // request
- tomcat.getConnector().setAttribute("maxKeepAliveRequests", "1");
-
- // No file system docBase required
- Context ctx = tomcat.addContext("", null);
-
- // Add protected servlet
- Tomcat.addServlet(ctx, "ChunkedResponseWithErrorServlet",
- new ResponseWithErrorServlet(true));
- ctx.addServletMappingDecoded("/*", "ChunkedResponseWithErrorServlet");
-
- tomcat.start();
-
- String request =
- "GET /anything HTTP/1.1" + SimpleHttpClient.CRLF +
- "Host: any" + SimpleHttpClient.CRLF +
- SimpleHttpClient.CRLF;
-
- Client client = new Client(tomcat.getConnector().getLocalPort());
- client.setRequest(new String[] {request});
-
- client.connect();
- client.processRequest();
-
- // Expected response is a 200 response followed by an incomplete chunked
- // body.
- assertTrue(client.isResponse200());
- // Should use chunked encoding
- String transferEncoding = null;
- for (String header : client.getResponseHeaders()) {
- if (header.startsWith("Transfer-Encoding:")) {
- transferEncoding = header.substring(18).trim();
- }
- }
- Assert.assertEquals("chunked", transferEncoding);
- // There should not be an end chunk
- assertFalse(client.getResponseBody().endsWith("0"));
- // The last portion of text should be there
- assertTrue(client.getResponseBody().endsWith("line03"));
- }
-
- private static class ResponseWithErrorServlet extends HttpServlet {
-
- private static final long serialVersionUID = 1L;
-
- private final boolean useChunks;
-
- public ResponseWithErrorServlet(boolean useChunks) {
- this.useChunks = useChunks;
- }
-
- @Override
- protected void doGet(HttpServletRequest req, HttpServletResponse resp)
- throws ServletException, IOException {
-
- resp.setContentType("text/plain");
- resp.setCharacterEncoding("UTF-8");
- if (!useChunks) {
- // Longer than it needs to be because response will fail before
- // it is complete
- resp.setContentLength(100);
- }
- PrintWriter pw = resp.getWriter();
- pw.print("line01");
- pw.flush();
- resp.flushBuffer();
- pw.print("line02");
- pw.flush();
- resp.flushBuffer();
- pw.print("line03");
-
- // Now throw a RuntimeException to end this request
- throw new ServletException("Deliberate failure");
- }
- }
-
-
- @Test
- public void testWithUnknownExpectation() throws Exception {
- getTomcatInstanceTestWebapp(false, true);
-
- String request =
- "POST /echo-params.jsp HTTP/1.1" + SimpleHttpClient.CRLF +
- "Host: any" + SimpleHttpClient.CRLF +
- "Expect: unknoen" + SimpleHttpClient.CRLF +
- SimpleHttpClient.CRLF;
-
- Client client = new Client(getPort());
- client.setRequest(new String[] {request});
-
- client.connect();
- client.processRequest();
- assertTrue(client.isResponse417());
- }
-
-
- @Test
- 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 +
- "Content-Type: application/x-www-form-urlencoded" +
- SimpleHttpClient.CRLF +
- SimpleHttpClient.CRLF +
- "test=data";
-
- Client client = new Client(getPort());
- client.setRequest(new String[] {request});
-
- client.connect();
- client.processRequest();
- assertTrue(client.isResponse501());
- }
-
-
- @Test
- 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 +
- "Content-Type: application/x-www-form-urlencoded" +
- SimpleHttpClient.CRLF +
- SimpleHttpClient.CRLF +
- "test=data";
-
- Client client = new Client(getPort());
- client.setRequest(new String[] {request});
-
- client.connect();
- client.processRequest();
- assertTrue(client.isResponse501());
- }
-
-
- @Test
- public void testWithTEChunked() throws Exception {
- doTestWithTEChunked(false);
- }
-
-
- @Test
- public void testWithTEChunkedWithCL() throws Exception {
- // Should be ignored
- doTestWithTEChunked(true);
- }
-
-
- private void doTestWithTEChunked(boolean withCL) throws Exception {
-
- 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 +
- "Content-Type: application/x-www-form-urlencoded" +
- SimpleHttpClient.CRLF +
- "Connection: close" + SimpleHttpClient.CRLF +
- SimpleHttpClient.CRLF +
- "9" + SimpleHttpClient.CRLF +
- "test=data" + SimpleHttpClient.CRLF +
- "0" + SimpleHttpClient.CRLF +
- SimpleHttpClient.CRLF;
-
- Client client = new Client(getPort());
- client.setRequest(new String[] {request});
-
- client.connect();
- client.processRequest();
- assertTrue(client.isResponse200());
- assertTrue(client.getResponseBody().contains("test - data"));
- }
-
-
- @Test
- public void testWithTEIdentity() throws Exception {
- getTomcatInstanceTestWebapp(false, true);
-
- String request =
- "POST /test/echo-params.jsp HTTP/1.1" + SimpleHttpClient.CRLF +
- "Host: any" + SimpleHttpClient.CRLF +
- "Transfer-encoding: identity" + SimpleHttpClient.CRLF +
- "Content-Length: 9" + SimpleHttpClient.CRLF +
- "Content-Type: application/x-www-form-urlencoded" +
- SimpleHttpClient.CRLF +
- "Connection: close" + SimpleHttpClient.CRLF +
- SimpleHttpClient.CRLF +
- "test=data";
-
- Client client = new Client(getPort());
- client.setRequest(new String[] {request});
-
- client.connect();
- client.processRequest();
- assertTrue(client.isResponse200());
- assertTrue(client.getResponseBody().contains("test - data"));
- }
-
-
- @Test
- 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 +
- "Content-Type: application/x-www-form-urlencoded" +
- SimpleHttpClient.CRLF +
- SimpleHttpClient.CRLF +
- "test=data";
-
- Client client = new Client(getPort());
- client.setRequest(new String[] {request});
-
- client.connect();
- client.processRequest();
- assertTrue(client.isResponse501());
- }
-
-
- @Test
- 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 +
- "Content-Type: application/x-www-form-urlencoded" +
- SimpleHttpClient.CRLF +
- SimpleHttpClient.CRLF +
- "test=data";
-
- Client client = new Client(getPort());
- client.setRequest(new String[] {request});
-
- client.connect();
- client.processRequest();
- assertTrue(client.isResponse501());
- }
-
-
- @Test
- public void testPipelining() throws Exception {
- Tomcat tomcat = getTomcatInstance();
-
- // No file system docBase required
- Context ctx = tomcat.addContext("", null);
-
- // Add protected servlet
- Tomcat.addServlet(ctx, "TesterServlet", new TesterServlet());
- ctx.addServletMappingDecoded("/foo", "TesterServlet");
-
- tomcat.start();
-
- String requestPart1 =
- "GET /foo HTTP/1.1" + SimpleHttpClient.CRLF;
- String requestPart2 =
- "Host: any" + SimpleHttpClient.CRLF +
- SimpleHttpClient.CRLF;
-
- final Client client = new Client(tomcat.getConnector().getLocalPort());
- client.setRequest(new String[] {requestPart1, requestPart2});
- client.setRequestPause(1000);
- client.setUseContentLength(true);
- client.connect();
-
- Runnable send = new Runnable() {
- @Override
- public void run() {
- try {
- client.sendRequest();
- client.sendRequest();
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- }
- };
- Thread t = new Thread(send);
- t.start();
-
- // Sleep for 1500 ms which should mean the all of request 1 has been
- // sent and half of request 2
- Thread.sleep(1500);
-
- // Now read the first response
- client.readResponse(true);
- assertFalse(client.isResponse50x());
- assertTrue(client.isResponse200());
- assertEquals("OK", client.getResponseBody());
-
- // Read the second response. No need to sleep, read will block until
- // there is data to process
- client.readResponse(true);
- assertFalse(client.isResponse50x());
- assertTrue(client.isResponse200());
- assertEquals("OK", client.getResponseBody());
- }
-
-
- @Test
- public void testChunking11NoContentLength() throws Exception {
- Tomcat tomcat = getTomcatInstance();
-
- // No file system docBase required
- Context ctx = tomcat.addContext("", null);
-
- Tomcat.addServlet(ctx, "NoContentLengthFlushingServlet",
- new NoContentLengthFlushingServlet());
- ctx.addServletMappingDecoded("/test", "NoContentLengthFlushingServlet");
-
- tomcat.start();
-
- ByteChunk responseBody = new ByteChunk();
- Map<String,List<String>> responseHeaders = new HashMap<>();
- int rc = getUrl("http://localhost:" + getPort() + "/test", responseBody,
- responseHeaders);
-
- assertEquals(HttpServletResponse.SC_OK, rc);
- assertTrue(responseHeaders.containsKey("Transfer-Encoding"));
- List<String> encodings = responseHeaders.get("Transfer-Encoding");
- assertEquals(1, encodings.size());
- assertEquals("chunked", encodings.get(0));
- }
-
- @Test
- public void testNoChunking11NoContentLengthConnectionClose()
- throws Exception {
-
- Tomcat tomcat = getTomcatInstance();
-
- // No file system docBase required
- Context ctx = tomcat.addContext("", null);
-
- Tomcat.addServlet(ctx, "NoContentLengthConnectionCloseFlushingServlet",
- new NoContentLengthConnectionCloseFlushingServlet());
- ctx.addServletMappingDecoded("/test",
- "NoContentLengthConnectionCloseFlushingServlet");
-
- tomcat.start();
-
- ByteChunk responseBody = new ByteChunk();
- Map<String,List<String>> responseHeaders = new HashMap<>();
- int rc = getUrl("http://localhost:" + getPort() + "/test", responseBody,
- responseHeaders);
-
- assertEquals(HttpServletResponse.SC_OK, rc);
-
- assertTrue(responseHeaders.containsKey("Connection"));
- List<String> connections = responseHeaders.get("Connection");
- assertEquals(1, connections.size());
- assertEquals("close", connections.get(0));
-
- assertFalse(responseHeaders.containsKey("Transfer-Encoding"));
-
- assertEquals("OK", responseBody.toString());
- }
-
- @Test
- public void testBug53677a() throws Exception {
- doTestBug53677(false);
- }
-
- @Test
- public void testBug53677b() throws Exception {
- doTestBug53677(true);
- }
-
- private void doTestBug53677(boolean flush) throws Exception {
- Tomcat tomcat = getTomcatInstance();
-
- // No file system docBase required
- Context ctx = tomcat.addContext("", null);
-
- Tomcat.addServlet(ctx, "LargeHeaderServlet",
- new LargeHeaderServlet(flush));
- ctx.addServletMappingDecoded("/test", "LargeHeaderServlet");
-
- tomcat.start();
-
- ByteChunk responseBody = new ByteChunk();
- Map<String,List<String>> responseHeaders = new HashMap<>();
- int rc = getUrl("http://localhost:" + getPort() + "/test", responseBody,
- responseHeaders);
-
- assertEquals(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, rc);
- if (responseBody.getLength() > 0) {
- // It will be >0 if the standard error page handling has been
- // triggered
- assertFalse(responseBody.toString().contains("FAIL"));
- }
- }
-
-
- private static CountDownLatch bug55772Latch1 = new CountDownLatch(1);
- private static CountDownLatch bug55772Latch2 = new CountDownLatch(1);
- private static CountDownLatch bug55772Latch3 = new CountDownLatch(1);
- private static boolean bug55772IsSecondRequest = false;
- private static boolean bug55772RequestStateLeaked = false;
-
-
- @Test
- public void testBug55772() throws Exception {
- Tomcat tomcat = getTomcatInstance();
- tomcat.getConnector().setProperty("processorCache", "1");
- tomcat.getConnector().setProperty("maxThreads", "1");
-
- // No file system docBase required
- Context ctx = tomcat.addContext("", null);
-
- Tomcat.addServlet(ctx, "async", new Bug55772Servlet());
- ctx.addServletMappingDecoded("/*", "async");
-
- tomcat.start();
-
- String request1 = "GET /async?1 HTTP/1.1\r\n" +
- "Host: localhost:" + getPort() + "\r\n" +
- "Connection: keep-alive\r\n" +
- "Cache-Control: max-age=0\r\n" +
- "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8\r\n" +
- "User-Agent: Request1\r\n" +
- "Accept-Encoding: gzip,deflate,sdch\r\n" +
- "Accept-Language: en-US,en;q=0.8,fr;q=0.6,es;q=0.4\r\n" +
- "Cookie: something.that.should.not.leak=true\r\n" +
- "\r\n";
-
- String request2 = "GET /async?2 HTTP/1.1\r\n" +
- "Host: localhost:" + getPort() + "\r\n" +
- "Connection: keep-alive\r\n" +
- "Cache-Control: max-age=0\r\n" +
- "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8\r\n" +
- "User-Agent: Request2\r\n" +
- "Accept-Encoding: gzip,deflate,sdch\r\n" +
- "Accept-Language: en-US,en;q=0.8,fr;q=0.6,es;q=0.4\r\n" +
- "\r\n";
-
- try (final Socket connection = new Socket("localhost", getPort())) {
- connection.setSoLinger(true, 0);
- Writer writer = new OutputStreamWriter(connection.getOutputStream(),
- StandardCharsets.US_ASCII);
- writer.write(request1);
- writer.flush();
-
- bug55772Latch1.await();
- connection.close();
- }
-
- bug55772Latch2.await();
- bug55772IsSecondRequest = true;
-
- try (final Socket connection = new Socket("localhost", getPort())) {
- connection.setSoLinger(true, 0);
- Writer writer = new OutputStreamWriter(connection.getOutputStream(),
- B2CConverter.getCharset("US-ASCII"));
- writer.write(request2);
- writer.flush();
- connection.getInputStream().read();
- }
-
- bug55772Latch3.await();
- if (bug55772RequestStateLeaked) {
- Assert.fail("State leaked between requests!");
- }
- }
-
-
- // https://bz.apache.org/bugzilla/show_bug.cgi?id=57324
- @Test
- public void testNon2xxResponseWithExpectation() throws Exception {
- doTestNon2xxResponseAndExpectation(true);
- }
-
- @Test
- public void testNon2xxResponseWithoutExpectation() throws Exception {
- doTestNon2xxResponseAndExpectation(false);
- }
-
- private void doTestNon2xxResponseAndExpectation(boolean useExpectation) throws Exception {
- Tomcat tomcat = getTomcatInstance();
-
- // No file system docBase required
- Context ctx = tomcat.addContext("", null);
-
- Tomcat.addServlet(ctx, "echo", new EchoBodyServlet());
- ctx.addServletMappingDecoded("/echo", "echo");
-
- SecurityCollection collection = new SecurityCollection("All", "");
- collection.addPattern("/*");
- SecurityConstraint constraint = new SecurityConstraint();
- constraint.addAuthRole("Any");
- constraint.addCollection(collection);
- ctx.addConstraint(constraint);
-
- tomcat.start();
-
- byte[] requestBody = "HelloWorld".getBytes(StandardCharsets.UTF_8);
- Map<String,List<String>> reqHeaders = null;
- if (useExpectation) {
- reqHeaders = new HashMap<>();
- List<String> expectation = new ArrayList<>();
- expectation.add("100-continue");
- reqHeaders.put("Expect", expectation);
- }
- ByteChunk responseBody = new ByteChunk();
- Map<String,List<String>> responseHeaders = new HashMap<>();
- int rc = postUrl(requestBody, "http://localhost:" + getPort() + "/echo",
- responseBody, reqHeaders, responseHeaders);
-
- Assert.assertEquals(HttpServletResponse.SC_FORBIDDEN, rc);
- List<String> connectionHeaders = responseHeaders.get("Connection");
- if (useExpectation) {
- Assert.assertEquals(1, connectionHeaders.size());
- Assert.assertEquals("close", connectionHeaders.get(0).toLowerCase(Locale.ENGLISH));
- } else {
- Assert.assertNull(connectionHeaders);
- }
- }
-
-
- private static class Bug55772Servlet extends HttpServlet {
-
- private static final long serialVersionUID = 1L;
-
- @Override
- protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
- if (bug55772IsSecondRequest) {
- Cookie[] cookies = req.getCookies();
- if (cookies != null && cookies.length > 0) {
- for (Cookie cookie : req.getCookies()) {
- if (cookie.getName().equalsIgnoreCase("something.that.should.not.leak")) {
- bug55772RequestStateLeaked = true;
- }
- }
- }
- bug55772Latch3.countDown();
- } else {
- req.getCookies(); // We have to do this so Tomcat will actually parse the cookies from the request
- }
-
- req.setAttribute("org.apache.catalina.ASYNC_SUPPORTED", Boolean.TRUE);
- AsyncContext asyncContext = req.startAsync();
- asyncContext.setTimeout(5000);
-
- bug55772Latch1.countDown();
-
- PrintWriter writer = asyncContext.getResponse().getWriter();
- writer.print('\n');
- writer.flush();
-
- bug55772Latch2.countDown();
- }
- }
-
-
- private static final class LargeHeaderServlet extends HttpServlet {
-
- private static final long serialVersionUID = 1L;
-
- boolean flush = false;
-
- public LargeHeaderServlet(boolean flush) {
- this.flush = flush;
- }
-
- @Override
- protected void doGet(HttpServletRequest req, HttpServletResponse resp)
- throws ServletException, IOException {
- String largeValue =
- CharBuffer.allocate(10000).toString().replace('\0', 'x');
- resp.setHeader("x-Test", largeValue);
- if (flush) {
- resp.flushBuffer();
- }
- resp.setContentType("text/plain");
- resp.getWriter().print("FAIL");
- }
-
- }
-
- // flushes with no content-length set
- // should result in chunking on HTTP 1.1
- private static final class NoContentLengthFlushingServlet
- extends HttpServlet {
-
- private static final long serialVersionUID = 1L;
-
- @Override
- protected void doGet(HttpServletRequest req, HttpServletResponse resp)
- throws ServletException, IOException {
- resp.setStatus(HttpServletResponse.SC_OK);
- resp.setContentType("text/plain");
- resp.getWriter().write("OK");
- resp.flushBuffer();
- }
- }
-
- // flushes with no content-length set but sets Connection: close header
- // should no result in chunking on HTTP 1.1
- private static final class NoContentLengthConnectionCloseFlushingServlet
- extends HttpServlet {
-
- private static final long serialVersionUID = 1L;
-
- @Override
- protected void doGet(HttpServletRequest req, HttpServletResponse resp)
- throws ServletException, IOException {
- resp.setStatus(HttpServletResponse.SC_OK);
- resp.setContentType("text/event-stream");
- resp.addHeader("Connection", "close");
- resp.flushBuffer();
- resp.getWriter().write("OK");
- resp.flushBuffer();
- }
- }
-
- private static final class Client extends SimpleHttpClient {
-
- public Client(int port) {
- setPort(port);
- }
-
- @Override
- public boolean isResponseBodyOK() {
- return getResponseBody().contains("test - data");
- }
- }
-
-
- /*
- * Partially read chunked input is not swallowed when it is read during
- * async processing.
- */
- @Test
- public void testBug57621() throws Exception {
-
- Tomcat tomcat = getTomcatInstance();
- Context root = tomcat.addContext("", null);
- Wrapper w = Tomcat.addServlet(root, "Bug57621", new Bug57621Servlet());
- w.setAsyncSupported(true);
- root.addServletMappingDecoded("/test", "Bug57621");
-
- tomcat.start();
-
- Bug57621Client client = new Bug57621Client();
- client.setPort(tomcat.getConnector().getLocalPort());
-
- client.setUseContentLength(true);
-
- client.connect();
-
- client.doRequest();
- assertTrue(client.getResponseLine(), client.isResponse200());
- assertTrue(client.isResponseBodyOK());
-
- // Do the request again to ensure that the remaining body was swallowed
- client.resetResponse();
- client.processRequest();
- assertTrue(client.isResponse200());
- assertTrue(client.isResponseBodyOK());
-
- client.disconnect();
- }
-
-
- private static class Bug57621Servlet extends HttpServlet {
-
- private static final long serialVersionUID = 1L;
-
- @Override
- protected void doPut(HttpServletRequest req, final HttpServletResponse resp)
- throws ServletException, IOException {
- final AsyncContext ac = req.startAsync();
- ac.start(new Runnable() {
- @Override
- public void run() {
- resp.setContentType("text/plain");
- resp.setCharacterEncoding("UTF-8");
- try {
- resp.getWriter().print("OK");
- } catch (IOException e) {
- // Should never happen. Test will fail if it does.
- }
- ac.complete();
- }
- });
- }
- }
-
-
- private static class Bug57621Client extends SimpleHttpClient {
-
- private Exception doRequest() {
- try {
- String[] request = new String[2];
- request[0] =
- "PUT http://localhost:8080/test HTTP/1.1" + CRLF +
- "Transfer-encoding: chunked" + CRLF +
- CRLF +
- "2" + CRLF +
- "OK";
-
- request[1] =
- CRLF +
- "0" + CRLF +
- CRLF;
-
- setRequest(request);
- processRequest(); // blocks until response has been read
- } catch (Exception e) {
- return e;
- }
- return null;
- }
-
- @Override
- public boolean isResponseBodyOK() {
- if (getResponseBody() == null) {
- return false;
- }
- if (!getResponseBody().contains("OK")) {
- return false;
- }
- return true;
- }
- }
-
-
- @Test
- public void testBug59310() throws Exception {
- Tomcat tomcat = getTomcatInstance();
-
- // No file system docBase required
- Context ctx = tomcat.addContext("", null);
-
- Tomcat.addServlet(ctx, "Bug59310", new Bug59310Servlet());
- ctx.addServletMappingDecoded("/test", "Bug59310");
-
- tomcat.start();
-
- ByteChunk responseBody = new ByteChunk();
- Map<String,List<String>> responseHeaders = new HashMap<>();
-
- int rc = headUrl("http://localhost:" + getPort() + "/test", responseBody,
- responseHeaders);
-
- assertEquals(HttpServletResponse.SC_OK, rc);
- assertEquals(0, responseBody.getLength());
- assertFalse(responseHeaders.containsKey("Content-Length"));
- }
-
-
- private static class Bug59310Servlet extends HttpServlet {
-
- private static final long serialVersionUID = 1L;
-
- @Override
- protected void doGet(HttpServletRequest req, HttpServletResponse resp)
- throws ServletException, IOException {
- super.doGet(req, resp);
- }
-
- @Override
- protected void doHead(HttpServletRequest req, HttpServletResponse resp)
- throws ServletException, IOException {
- }
- }
-
-
- /*
- * Tests what happens if a request is completed during a dispatch but the
- * request body has not been fully read.
- */
- @Test
- public void testRequestBodySwallowing() throws Exception {
- Tomcat tomcat = getTomcatInstance();
-
- // No file system docBase required
- Context ctx = tomcat.addContext("", null);
-
- SempahoreServlet servlet = new SempahoreServlet();
- Wrapper w = Tomcat.addServlet(ctx, "Test", servlet);
- w.setAsyncSupported(true);
- ctx.addServletMappingDecoded("/test", "Test");
-
- tomcat.start();
-
- // Hand-craft the client so we have complete control over the timing
- SocketAddress addr = new InetSocketAddress("localhost", getPort());
- Socket socket = new Socket();
- socket.setSoTimeout(300000);
- socket.connect(addr,300000);
- OutputStream os = socket.getOutputStream();
- Writer writer = new OutputStreamWriter(os, "ISO-8859-1");
- InputStream is = socket.getInputStream();
- Reader r = new InputStreamReader(is, "ISO-8859-1");
- BufferedReader reader = new BufferedReader(r);
-
- // Write the headers
- writer.write("POST /test HTTP/1.1\r\n");
- writer.write("Host: localhost:8080\r\n");
- writer.write("Transfer-Encoding: chunked\r\n");
- writer.write("\r\n");
- writer.flush();
-
- validateResponse(reader);
-
- // Write the request body
- writer.write("2\r\n");
- writer.write("AB\r\n");
- writer.write("0\r\n");
- writer.write("\r\n");
- writer.flush();
-
- // Write the 2nd request
- writer.write("POST /test HTTP/1.1\r\n");
- writer.write("Host: localhost:8080\r\n");
- writer.write("Transfer-Encoding: chunked\r\n");
- writer.write("\r\n");
- writer.flush();
-
- // Read the 2nd response
- validateResponse(reader);
-
- // Write the 2nd request body
- writer.write("2\r\n");
- writer.write("AB\r\n");
- writer.write("0\r\n");
- writer.write("\r\n");
- writer.flush();
-
- // Done
- socket.close();
- }
-
-
- private void validateResponse(BufferedReader reader) throws IOException {
- // First line has the response code and should always be 200
- String line = reader.readLine();
- Assert.assertEquals("HTTP/1.1 200 OK", line);
- while (!"OK".equals(line)) {
- line = reader.readLine();
- }
- }
-
-
- private static class SempahoreServlet extends HttpServlet {
-
- private static final long serialVersionUID = 1L;
-
- @Override
- protected void doPost(HttpServletRequest req, HttpServletResponse resp)
- throws ServletException, IOException {
- if (DispatcherType.ASYNC.equals(req.getDispatcherType())) {
- resp.setContentType("text/plain");
- resp.setCharacterEncoding("UTF-8");
- resp.getWriter().write("OK\n");
- } else {
- req.startAsync().dispatch();
- }
- }
- }
-}
diff --git a/test/org/apache/coyote/http11/TestGzipOutputFilter.java b/test/org/apache/coyote/http11/TestGzipOutputFilter.java
deleted file mode 100644
index a57c297..0000000
--- a/test/org/apache/coyote/http11/TestGzipOutputFilter.java
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.coyote.http11;
-
-import java.io.ByteArrayOutputStream;
-import java.util.zip.GZIPOutputStream;
-
-import static org.junit.Assert.assertTrue;
-
-import org.junit.Test;
-
-import org.apache.coyote.Response;
-import org.apache.coyote.http11.filters.GzipOutputFilter;
-import org.apache.tomcat.util.buf.ByteChunk;
-
-/**
- * Test case to demonstrate the interaction between gzip and flushing in the
- * output filter.
- */
-public class TestGzipOutputFilter {
-
- /*
- * Test the interaction betwen gzip and flushing. The idea is to: 1. create
- * a internal output buffer, response, and attach an active gzipoutputfilter
- * to the output buffer 2. set the output stream of the internal buffer to
- * be a ByteArrayOutputStream so we can inspect the output bytes 3. write a
- * chunk out using the gzipoutputfilter and invoke a flush on the
- * InternalOutputBuffer 4. read from the ByteArrayOutputStream to find out
- * what's being written out (flushed) 5. find out what's expected by wrting
- * to GZIPOutputStream and close it (to force flushing) 6. Compare the size
- * of the two arrays, they should be close (instead of one being much
- * shorter than the other one)
- *
- * @throws Exception
- */
- @Test
- public void testFlushingWithGzip() throws Exception {
- // set up response, InternalOutputBuffer, and ByteArrayOutputStream
- Response res = new Response();
- InternalOutputBuffer iob = new InternalOutputBuffer(res, 8 * 1024);
- ByteArrayOutputStream bos = new ByteArrayOutputStream();
- iob.outputStream = bos;
- res.setOutputBuffer(iob);
-
- // set up GzipOutputFilter to attach to the InternalOutputBuffer
- GzipOutputFilter gf = new GzipOutputFilter();
- iob.addFilter(gf);
- iob.addActiveFilter(gf);
-
- // write a chunk out
- ByteChunk chunk = new ByteChunk(1024);
- byte[] d = "Hello there tomcat developers, there is a bug in JDK".getBytes();
- chunk.append(d, 0, d.length);
- iob.doWrite(chunk, res);
-
- // flush the InternalOutputBuffer
- iob.flush();
-
- // read from the ByteArrayOutputStream to find out what's being written
- // out (flushed)
- byte[] dataFound = bos.toByteArray();
-
- // find out what's expected by wrting to GZIPOutputStream and close it
- // (to force flushing)
- ByteArrayOutputStream gbos = new ByteArrayOutputStream(1024);
- GZIPOutputStream gos = new GZIPOutputStream(gbos);
- gos.write(d);
- gos.close();
-
- // read the expected data
- byte[] dataExpected = gbos.toByteArray();
-
- // most of the data should have been flushed out
- assertTrue(dataFound.length >= (dataExpected.length - 20));
- }
-}
diff --git a/test/org/apache/coyote/http11/TestHttp11InputBuffer.java b/test/org/apache/coyote/http11/TestHttp11InputBuffer.java
new file mode 100644
index 0000000..60a3129
--- /dev/null
+++ b/test/org/apache/coyote/http11/TestHttp11InputBuffer.java
@@ -0,0 +1,604 @@
+/*
+ * 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.http11;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Enumeration;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import org.apache.catalina.Context;
+import org.apache.catalina.startup.SimpleHttpClient;
+import org.apache.catalina.startup.TesterServlet;
+import org.apache.catalina.startup.Tomcat;
+import org.apache.catalina.startup.TomcatBaseTest;
+
+public class TestHttp11InputBuffer extends TomcatBaseTest {
+
+ /**
+ * Test case for https://bz.apache.org/bugzilla/show_bug.cgi?id=48839
+ */
+ @Test
+ public void testBug48839() {
+
+ Bug48839Client client = new Bug48839Client();
+
+ client.doRequest();
+ assertTrue(client.isResponse200());
+ assertTrue(client.isResponseBodyOK());
+ }
+
+
+ /**
+ * Bug 48839 test client.
+ */
+ private class Bug48839Client extends SimpleHttpClient {
+
+ private Exception doRequest() {
+
+ Tomcat tomcat = getTomcatInstance();
+
+ Context root = tomcat.addContext("", TEMP_DIR);
+ Tomcat.addServlet(root, "Bug48839", new Bug48839Servlet());
+ root.addServletMappingDecoded("/test", "Bug48839");
+
+ try {
+ tomcat.start();
+ setPort(tomcat.getConnector().getLocalPort());
+
+ // Open connection
+ connect();
+
+ String[] request = new String[1];
+ request[0] =
+ "GET http://localhost:8080/test HTTP/1.1" + CRLF +
+ "X-Bug48839: abcd" + CRLF +
+ "\tefgh" + CRLF +
+ "Connection: close" + CRLF +
+ CRLF;
+
+ setRequest(request);
+ processRequest(); // blocks until response has been read
+
+ // Close the connection
+ disconnect();
+ } catch (Exception e) {
+ return e;
+ }
+ return null;
+ }
+
+ @Override
+ public boolean isResponseBodyOK() {
+ if (getResponseBody() == null) {
+ return false;
+ }
+ if (!getResponseBody().contains("abcd\tefgh")) {
+ return false;
+ }
+ return true;
+ }
+
+ }
+
+ private static class Bug48839Servlet extends HttpServlet {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Only interested in the request headers from a GET request
+ */
+ @Override
+ protected void doGet(HttpServletRequest req, HttpServletResponse resp)
+ throws ServletException, IOException {
+ // Just echo the header value back as plain text
+ resp.setContentType("text/plain");
+
+ PrintWriter out = resp.getWriter();
+
+ Enumeration<String> values = req.getHeaders("X-Bug48839");
+ while (values.hasMoreElements()) {
+ out.println(values.nextElement());
+ }
+ }
+ }
+
+
+ @Test
+ public void testBug51557NoColon() {
+
+ Bug51557Client client = new Bug51557Client("X-Bug51557NoColon");
+
+ client.doRequest();
+ assertTrue(client.isResponse200());
+ assertEquals("abcd", client.getResponseBody());
+ assertTrue(client.isResponseBodyOK());
+ }
+
+
+ @Test
+ public void testBug51557Separators() throws Exception {
+ char httpSeparators[] = new char[] {
+ '\t', ' ', '\"', '(', ')', ',', '/', ':', ';', '<',
+ '=', '>', '?', '@', '[', '\\', ']', '{', '}' };
+
+ for (char s : httpSeparators) {
+ doTestBug51557Char(s);
+ tearDown();
+ setUp();
+ }
+ }
+
+
+ @Test
+ public void testBug51557Ctl() throws Exception {
+ for (int i = 0; i < 31; i++) {
+ doTestBug51557Char((char) i);
+ tearDown();
+ setUp();
+ }
+ doTestBug51557Char((char) 127);
+ }
+
+
+ @Test
+ public void testBug51557Continuation() {
+
+ Bug51557Client client = new Bug51557Client("X-Bug=51557NoColon",
+ "foo" + SimpleHttpClient.CRLF + " bar");
+
+ client.doRequest();
+ assertTrue(client.isResponse200());
+ assertEquals("abcd", client.getResponseBody());
+ assertTrue(client.isResponseBodyOK());
+ }
+
+
+ @Test
+ public void testBug51557BoundaryStart() {
+
+ Bug51557Client client = new Bug51557Client("=X-Bug51557",
+ "invalid");
+
+ client.doRequest();
+ assertTrue(client.isResponse200());
+ assertEquals("abcd", client.getResponseBody());
+ assertTrue(client.isResponseBodyOK());
+ }
+
+
+ @Test
+ public void testBug51557BoundaryEnd() {
+
+ Bug51557Client client = new Bug51557Client("X-Bug51557=",
+ "invalid");
+
+ client.doRequest();
+ assertTrue(client.isResponse200());
+ assertEquals("abcd", client.getResponseBody());
+ assertTrue(client.isResponseBodyOK());
+ }
+
+
+ private void doTestBug51557Char(char s) {
+ Bug51557Client client =
+ new Bug51557Client("X-Bug" + s + "51557", "invalid");
+
+ client.doRequest();
+ assertTrue(client.isResponse200());
+ assertEquals("abcd", client.getResponseBody());
+ assertTrue(client.isResponseBodyOK());
+ }
+
+ /**
+ * Bug 51557 test client.
+ */
+ private class Bug51557Client extends SimpleHttpClient {
+
+ private String headerName;
+ private String headerLine;
+
+ public Bug51557Client(String headerName) {
+ this.headerName = headerName;
+ this.headerLine = headerName;
+ }
+
+ public Bug51557Client(String headerName, String headerValue) {
+ this.headerName = headerName;
+ this.headerLine = headerName + ": " + headerValue;
+ }
+
+ private Exception doRequest() {
+
+ Tomcat tomcat = getTomcatInstance();
+
+ Context root = tomcat.addContext("", TEMP_DIR);
+ Tomcat.addServlet(root, "Bug51557",
+ new Bug51557Servlet(headerName));
+ root.addServletMappingDecoded("/test", "Bug51557");
+
+ try {
+ tomcat.start();
+ setPort(tomcat.getConnector().getLocalPort());
+
+ // Open connection
+ connect();
+
+ String[] request = new String[1];
+ request[0] =
+ "GET http://localhost:8080/test HTTP/1.1" + CRLF +
+ headerLine + CRLF +
+ "X-Bug51557: abcd" + CRLF +
+ "Connection: close" + CRLF +
+ CRLF;
+
+ setRequest(request);
+ processRequest(); // blocks until response has been read
+
+ // Close the connection
+ disconnect();
+ } catch (Exception e) {
+ return e;
+ }
+ return null;
+ }
+
+ @Override
+ public boolean isResponseBodyOK() {
+ if (getResponseBody() == null) {
+ return false;
+ }
+ if (!getResponseBody().contains("abcd")) {
+ return false;
+ }
+ return true;
+ }
+
+ }
+
+ private static class Bug51557Servlet extends HttpServlet {
+
+ private static final long serialVersionUID = 1L;
+
+ private String invalidHeaderName;
+
+ /**
+ * @param invalidHeaderName The header name should be invalid and
+ * therefore ignored by the header parsing code
+ */
+ public Bug51557Servlet(String invalidHeaderName) {
+ this.invalidHeaderName = invalidHeaderName;
+ }
+
+ /**
+ * Only interested in the request headers from a GET request
+ */
+ @Override
+ protected void doGet(HttpServletRequest req, HttpServletResponse resp)
+ throws ServletException, IOException {
+ // Just echo the header value back as plain text
+ resp.setContentType("text/plain");
+
+ PrintWriter out = resp.getWriter();
+
+ processHeaders(invalidHeaderName, req, out);
+ processHeaders("X-Bug51557", req, out);
+ }
+
+ private void processHeaders(String header, HttpServletRequest req,
+ PrintWriter out) {
+ Enumeration<String> values = req.getHeaders(header);
+ while (values.hasMoreElements()) {
+ out.println(values.nextElement());
+ }
+ }
+ }
+
+
+ /**
+ * Test case for new lines at the start of a request. RFC2616
+ * does not permit any, but Tomcat is tolerant of them if they are present.
+ */
+ @Test
+ public void testNewLines() {
+
+ NewLinesClient client = new NewLinesClient(10);
+
+ client.doRequest();
+ assertTrue(client.isResponse200());
+ assertTrue(client.isResponseBodyOK());
+ }
+
+
+ /**
+ * Test case for new lines at the start of a request. RFC2616
+ * does not permit any, but Tomcat is tolerant of them if they are present.
+ */
+ @Test
+ public void testNewLinesExcessive() {
+
+ NewLinesClient client = new NewLinesClient(10000);
+
+ // If the connection is closed fast enough, writing the request will
+ // fail and the response won't be read.
+ Exception e = client.doRequest();
+ if (e == null) {
+ assertTrue(client.getResponseLine(), client.isResponse400());
+ }
+ assertFalse(client.isResponseBodyOK());
+ }
+
+
+ private class NewLinesClient extends SimpleHttpClient {
+
+ private final String newLines;
+
+ private NewLinesClient(int count) {
+ StringBuilder sb = new StringBuilder(count * 2);
+ for (int i = 0; i < count; i++) {
+ sb.append(CRLF);
+ }
+ newLines = sb.toString();
+ }
+
+ private Exception doRequest() {
+
+ Tomcat tomcat = getTomcatInstance();
+
+ Context root = tomcat.addContext("", TEMP_DIR);
+ Tomcat.addServlet(root, "test", new TesterServlet());
+ root.addServletMappingDecoded("/test", "test");
+
+ try {
+ tomcat.start();
+ setPort(tomcat.getConnector().getLocalPort());
+
+ // Open connection
+ connect();
+
+ String[] request = new String[1];
+ request[0] =
+ newLines +
+ "GET http://localhost:8080/test HTTP/1.1" + CRLF +
+ "X-Bug48839: abcd" + CRLF +
+ "\tefgh" + CRLF +
+ "Connection: close" + CRLF +
+ CRLF;
+
+ setRequest(request);
+ processRequest(); // blocks until response has been read
+
+ // Close the connection
+ disconnect();
+ } catch (Exception e) {
+ return e;
+ }
+ return null;
+ }
+
+ @Override
+ public boolean isResponseBodyOK() {
+ if (getResponseBody() == null) {
+ return false;
+ }
+ if (!getResponseBody().contains("OK")) {
+ return false;
+ }
+ return true;
+ }
+
+ }
+
+
+ /**
+ * Test case for https://bz.apache.org/bugzilla/show_bug.cgi?id=54947
+ */
+ @Test
+ public void testBug54947() {
+
+ Bug54947Client client = new Bug54947Client();
+
+ client.doRequest();
+ assertTrue(client.isResponse200());
+ assertTrue(client.isResponseBodyOK());
+ }
+
+
+ /**
+ * Bug 54947 test client.
+ */
+ private class Bug54947Client extends SimpleHttpClient {
+
+ private Exception doRequest() {
+
+ Tomcat tomcat = getTomcatInstance();
+
+ Context root = tomcat.addContext("", TEMP_DIR);
+ Tomcat.addServlet(root, "Bug54947", new TesterServlet());
+ root.addServletMappingDecoded("/test", "Bug54947");
+
+ try {
+ tomcat.start();
+ setPort(tomcat.getConnector().getLocalPort());
+
+ // Open connection
+ connect();
+
+ String[] request = new String[2];
+ request[0] = "GET http://localhost:8080/test HTTP/1.1" + CR;
+ request[1] = LF +
+ "Connection: close" + CRLF +
+ CRLF;
+
+ setRequest(request);
+ processRequest(); // blocks until response has been read
+
+ // Close the connection
+ disconnect();
+ } catch (Exception e) {
+ return e;
+ }
+ return null;
+ }
+
+ @Override
+ public boolean isResponseBodyOK() {
+ if (getResponseBody() == null) {
+ return false;
+ }
+ if (!getResponseBody().contains("OK")) {
+ return false;
+ }
+ return true;
+ }
+
+ }
+
+
+ /**
+ * Test case for https://bz.apache.org/bugzilla/show_bug.cgi?id=59089
+ */
+ @Test
+ public void testBug59089() {
+
+ Bug59089Client client = new Bug59089Client();
+
+ client.doRequest();
+ assertTrue(client.isResponse200());
+ assertTrue(client.isResponseBodyOK());
+ }
+
+
+ /**
+ * Bug 59089 test client.
+ */
+ private class Bug59089Client extends SimpleHttpClient {
+
+ private Exception doRequest() {
+
+ // Ensure body is read correctly
+ setUseContentLength(true);
+
+ Tomcat tomcat = getTomcatInstance();
+
+ Context root = tomcat.addContext("", TEMP_DIR);
+ Tomcat.addServlet(root, "Bug59089", new TesterServlet());
+ root.addServletMappingDecoded("/test", "Bug59089");
+
+ try {
+ tomcat.start();
+ setPort(tomcat.getConnector().getLocalPort());
+
+ // Open connection
+ connect();
+
+ String[] request = new String[1];
+ request[0] = "GET http://localhost:8080/test HTTP/1.1" + CRLF +
+ "X-Header: Ignore" + CRLF +
+ "X-Header" + (char) 130 + ": Broken" + CRLF + CRLF;
+
+ setRequest(request);
+ processRequest(); // blocks until response has been read
+
+ // Close the connection
+ disconnect();
+ } catch (Exception e) {
+ return e;
+ }
+ return null;
+ }
+
+ @Override
+ public boolean isResponseBodyOK() {
+ if (getResponseBody() == null) {
+ return false;
+ }
+ if (!getResponseBody().contains("OK")) {
+ return false;
+ }
+ return true;
+ }
+ }
+
+
+ @Test
+ public void testInvalidMethod() {
+
+ InvalidMethodClient client = new InvalidMethodClient();
+
+ client.doRequest();
+ assertTrue(client.getResponseLine(), client.isResponse400());
+ assertTrue(client.isResponseBodyOK());
+ }
+
+
+ /**
+ * Bug 48839 test client.
+ */
+ private class InvalidMethodClient extends SimpleHttpClient {
+
+ private Exception doRequest() {
+
+ Tomcat tomcat = getTomcatInstance();
+
+ tomcat.addContext("", TEMP_DIR);
+
+ try {
+ tomcat.start();
+ setPort(tomcat.getConnector().getLocalPort());
+
+ // Open connection
+ connect();
+
+ String[] request = new String[1];
+ request[0] =
+ "GET" + (char) 0 + " /test HTTP/1.1" + CRLF +
+ "Host: localhost:8080" + CRLF +
+ "Connection: close" + CRLF +
+ CRLF;
+
+ setRequest(request);
+ processRequest(); // blocks until response has been read
+
+ // Close the connection
+ disconnect();
+ } catch (Exception e) {
+ return e;
+ }
+ return null;
+ }
+
+ @Override
+ public boolean isResponseBodyOK() {
+ if (getResponseBody() == null) {
+ return false;
+ }
+ return true;
+ }
+ }
+}
diff --git a/test/org/apache/coyote/http11/TestHttp11OutputBuffer.java b/test/org/apache/coyote/http11/TestHttp11OutputBuffer.java
new file mode 100644
index 0000000..3e2a652
--- /dev/null
+++ b/test/org/apache/coyote/http11/TestHttp11OutputBuffer.java
@@ -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.coyote.http11;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import org.apache.catalina.Context;
+import org.apache.catalina.startup.SimpleHttpClient;
+import org.apache.catalina.startup.Tomcat;
+import org.apache.catalina.startup.TomcatBaseTest;
+
+public class TestHttp11OutputBuffer extends TomcatBaseTest {
+
+ @Test
+ public void testSendAck() throws Exception {
+ Tomcat tomcat = getTomcatInstance();
+
+ // No file system docBase required
+ Context ctx = tomcat.addContext("", null);
+
+ Tomcat.addServlet(ctx, "echo", new EchoBodyServlet());
+ ctx.addServletMappingDecoded("/echo", "echo");
+
+ tomcat.start();
+
+ ExpectationClient client = new ExpectationClient();
+
+ client.setPort(tomcat.getConnector().getLocalPort());
+ // Expected content doesn't end with a CR-LF so if it isn't chunked make
+ // sure the content length is used as reading it line-by-line will fail
+ // since there is no "line".
+ client.setUseContentLength(true);
+
+ client.connect();
+
+ client.doRequestHeaders();
+ Assert.assertTrue(client.isResponse100());
+
+ client.doRequestBody();
+ Assert.assertTrue(client.isResponse200());
+ Assert.assertTrue(client.isResponseBodyOK());
+ }
+
+ private static class ExpectationClient extends SimpleHttpClient {
+
+ private static final String BODY = "foo=bar";
+
+ public void doRequestHeaders() throws Exception {
+ StringBuilder requestHeaders = new StringBuilder();
+ requestHeaders.append("POST /echo HTTP/1.1").append(CRLF);
+ requestHeaders.append("Host: localhost").append(CRLF);
+ requestHeaders.append("Expect: 100-continue").append(CRLF);
+ requestHeaders.append("Content-Type: application/x-www-form-urlencoded").append(CRLF);
+ String len = Integer.toString(BODY.length());
+ requestHeaders.append("Content-length: ").append(len).append(CRLF);
+ requestHeaders.append(CRLF);
+
+ setRequest(new String[] {requestHeaders.toString()});
+
+ processRequest(false);
+ }
+
+ public void doRequestBody() throws Exception {
+ setRequest(new String[] { BODY });
+
+ processRequest(true);
+ }
+
+ @Override
+ public boolean isResponseBodyOK() {
+ return BODY.equals(getResponseBody());
+ }
+ }
+}
diff --git a/test/org/apache/coyote/http11/TestHttp11Processor.java b/test/org/apache/coyote/http11/TestHttp11Processor.java
new file mode 100644
index 0000000..5bc1726
--- /dev/null
+++ b/test/org/apache/coyote/http11/TestHttp11Processor.java
@@ -0,0 +1,975 @@
+/*
+ * 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.http11;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.io.Reader;
+import java.io.Writer;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.net.SocketAddress;
+import java.nio.CharBuffer;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.concurrent.CountDownLatch;
+
+import javax.servlet.AsyncContext;
+import javax.servlet.DispatcherType;
+import javax.servlet.ServletException;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import org.apache.catalina.Context;
+import org.apache.catalina.Wrapper;
+import org.apache.catalina.startup.SimpleHttpClient;
+import org.apache.catalina.startup.TesterServlet;
+import org.apache.catalina.startup.Tomcat;
+import org.apache.catalina.startup.TomcatBaseTest;
+import org.apache.tomcat.util.buf.B2CConverter;
+import org.apache.tomcat.util.buf.ByteChunk;
+import org.apache.tomcat.util.descriptor.web.SecurityCollection;
+import org.apache.tomcat.util.descriptor.web.SecurityConstraint;
+
+public class TestHttp11Processor extends TomcatBaseTest {
+
+ @Test
+ public void testResponseWithErrorChunked() throws Exception {
+ Tomcat tomcat = getTomcatInstance();
+
+ // This setting means the connection will be closed at the end of the
+ // request
+ tomcat.getConnector().setAttribute("maxKeepAliveRequests", "1");
+
+ // No file system docBase required
+ Context ctx = tomcat.addContext("", null);
+
+ // Add protected servlet
+ Tomcat.addServlet(ctx, "ChunkedResponseWithErrorServlet",
+ new ResponseWithErrorServlet(true));
+ ctx.addServletMappingDecoded("/*", "ChunkedResponseWithErrorServlet");
+
+ tomcat.start();
+
+ String request =
+ "GET /anything HTTP/1.1" + SimpleHttpClient.CRLF +
+ "Host: any" + SimpleHttpClient.CRLF +
+ SimpleHttpClient.CRLF;
+
+ Client client = new Client(tomcat.getConnector().getLocalPort());
+ client.setRequest(new String[] {request});
+
+ client.connect();
+ client.processRequest();
+
+ // Expected response is a 200 response followed by an incomplete chunked
+ // body.
+ assertTrue(client.isResponse200());
+ // Should use chunked encoding
+ String transferEncoding = null;
+ for (String header : client.getResponseHeaders()) {
+ if (header.startsWith("Transfer-Encoding:")) {
+ transferEncoding = header.substring(18).trim();
+ }
+ }
+ Assert.assertEquals("chunked", transferEncoding);
+ // There should not be an end chunk
+ assertFalse(client.getResponseBody().endsWith("0"));
+ // The last portion of text should be there
+ assertTrue(client.getResponseBody().endsWith("line03"));
+ }
+
+ private static class ResponseWithErrorServlet extends HttpServlet {
+
+ private static final long serialVersionUID = 1L;
+
+ private final boolean useChunks;
+
+ public ResponseWithErrorServlet(boolean useChunks) {
+ this.useChunks = useChunks;
+ }
+
+ @Override
+ protected void doGet(HttpServletRequest req, HttpServletResponse resp)
+ throws ServletException, IOException {
+
+ resp.setContentType("text/plain");
+ resp.setCharacterEncoding("UTF-8");
+ if (!useChunks) {
+ // Longer than it needs to be because response will fail before
+ // it is complete
+ resp.setContentLength(100);
+ }
+ PrintWriter pw = resp.getWriter();
+ pw.print("line01");
+ pw.flush();
+ resp.flushBuffer();
+ pw.print("line02");
+ pw.flush();
+ resp.flushBuffer();
+ pw.print("line03");
+
+ // Now throw a RuntimeException to end this request
+ throw new ServletException("Deliberate failure");
+ }
+ }
+
+
+ @Test
+ 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;
+
+ Client client = new Client(getPort());
+ client.setRequest(new String[] {request});
+
+ client.connect();
+ client.processRequest();
+ assertTrue(client.isResponse417());
+ }
+
+
+ @Test
+ 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 +
+ "Content-Type: application/x-www-form-urlencoded" +
+ SimpleHttpClient.CRLF +
+ SimpleHttpClient.CRLF +
+ "test=data";
+
+ Client client = new Client(getPort());
+ client.setRequest(new String[] {request});
+
+ client.connect();
+ client.processRequest();
+ assertTrue(client.isResponse501());
+ }
+
+
+ @Test
+ 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 +
+ "Content-Type: application/x-www-form-urlencoded" +
+ SimpleHttpClient.CRLF +
+ SimpleHttpClient.CRLF +
+ "test=data";
+
+ Client client = new Client(getPort());
+ client.setRequest(new String[] {request});
+
+ client.connect();
+ client.processRequest();
+ assertTrue(client.isResponse501());
+ }
+
+
+ @Test
+ public void testWithTEChunked() throws Exception {
+ doTestWithTEChunked(false);
+ }
+
+
+ @Test
+ public void testWithTEChunkedWithCL() throws Exception {
+ // Should be ignored
+ doTestWithTEChunked(true);
+ }
+
+
+ private void doTestWithTEChunked(boolean withCL) throws Exception {
+
+ 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 +
+ "Content-Type: application/x-www-form-urlencoded" +
+ SimpleHttpClient.CRLF +
+ "Connection: close" + SimpleHttpClient.CRLF +
+ SimpleHttpClient.CRLF +
+ "9" + SimpleHttpClient.CRLF +
+ "test=data" + SimpleHttpClient.CRLF +
+ "0" + SimpleHttpClient.CRLF +
+ SimpleHttpClient.CRLF;
+
+ Client client = new Client(getPort());
+ client.setRequest(new String[] {request});
+
+ client.connect();
+ client.processRequest();
+ assertTrue(client.isResponse200());
+ assertTrue(client.getResponseBody().contains("test - data"));
+ }
+
+
+ @Test
+ public void testWithTEIdentity() throws Exception {
+ getTomcatInstanceTestWebapp(false, true);
+
+ String request =
+ "POST /test/echo-params.jsp HTTP/1.1" + SimpleHttpClient.CRLF +
+ "Host: any" + SimpleHttpClient.CRLF +
+ "Transfer-encoding: identity" + SimpleHttpClient.CRLF +
+ "Content-Length: 9" + SimpleHttpClient.CRLF +
+ "Content-Type: application/x-www-form-urlencoded" +
+ SimpleHttpClient.CRLF +
+ "Connection: close" + SimpleHttpClient.CRLF +
+ SimpleHttpClient.CRLF +
+ "test=data";
+
+ Client client = new Client(getPort());
+ client.setRequest(new String[] {request});
+
+ client.connect();
+ client.processRequest();
+ assertTrue(client.isResponse200());
+ assertTrue(client.getResponseBody().contains("test - data"));
+ }
+
+
+ @Test
+ 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 +
+ "Content-Type: application/x-www-form-urlencoded" +
+ SimpleHttpClient.CRLF +
+ SimpleHttpClient.CRLF +
+ "test=data";
+
+ Client client = new Client(getPort());
+ client.setRequest(new String[] {request});
+
+ client.connect();
+ client.processRequest();
+ assertTrue(client.isResponse501());
+ }
+
+
+ @Test
+ 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 +
+ "Content-Type: application/x-www-form-urlencoded" +
+ SimpleHttpClient.CRLF +
+ SimpleHttpClient.CRLF +
+ "test=data";
+
+ Client client = new Client(getPort());
+ client.setRequest(new String[] {request});
+
+ client.connect();
+ client.processRequest();
+ assertTrue(client.isResponse501());
+ }
+
+
+ @Test
+ public void testPipelining() throws Exception {
+ Tomcat tomcat = getTomcatInstance();
+
+ // No file system docBase required
+ Context ctx = tomcat.addContext("", null);
+
+ // Add protected servlet
+ Tomcat.addServlet(ctx, "TesterServlet", new TesterServlet());
+ ctx.addServletMappingDecoded("/foo", "TesterServlet");
+
+ tomcat.start();
+
+ String requestPart1 =
+ "GET /foo HTTP/1.1" + SimpleHttpClient.CRLF;
+ String requestPart2 =
+ "Host: any" + SimpleHttpClient.CRLF +
+ SimpleHttpClient.CRLF;
+
+ final Client client = new Client(tomcat.getConnector().getLocalPort());
+ client.setRequest(new String[] {requestPart1, requestPart2});
+ client.setRequestPause(1000);
+ client.setUseContentLength(true);
+ client.connect();
+
+ Runnable send = new Runnable() {
+ @Override
+ public void run() {
+ try {
+ client.sendRequest();
+ client.sendRequest();
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ };
+ Thread t = new Thread(send);
+ t.start();
+
+ // Sleep for 1500 ms which should mean the all of request 1 has been
+ // sent and half of request 2
+ Thread.sleep(1500);
+
+ // Now read the first response
+ client.readResponse(true);
+ assertFalse(client.isResponse50x());
+ assertTrue(client.isResponse200());
+ assertEquals("OK", client.getResponseBody());
+
+ // Read the second response. No need to sleep, read will block until
+ // there is data to process
+ client.readResponse(true);
+ assertFalse(client.isResponse50x());
+ assertTrue(client.isResponse200());
+ assertEquals("OK", client.getResponseBody());
+ }
+
+
+ @Test
+ public void testChunking11NoContentLength() throws Exception {
+ Tomcat tomcat = getTomcatInstance();
+
+ // No file system docBase required
+ Context ctx = tomcat.addContext("", null);
+
+ Tomcat.addServlet(ctx, "NoContentLengthFlushingServlet",
+ new NoContentLengthFlushingServlet());
+ ctx.addServletMappingDecoded("/test", "NoContentLengthFlushingServlet");
+
+ tomcat.start();
+
+ ByteChunk responseBody = new ByteChunk();
+ Map<String,List<String>> responseHeaders = new HashMap<>();
+ int rc = getUrl("http://localhost:" + getPort() + "/test", responseBody,
+ responseHeaders);
+
+ assertEquals(HttpServletResponse.SC_OK, rc);
+ assertTrue(responseHeaders.containsKey("Transfer-Encoding"));
+ List<String> encodings = responseHeaders.get("Transfer-Encoding");
+ assertEquals(1, encodings.size());
+ assertEquals("chunked", encodings.get(0));
+ }
+
+ @Test
+ public void testNoChunking11NoContentLengthConnectionClose()
+ throws Exception {
+
+ Tomcat tomcat = getTomcatInstance();
+
+ // No file system docBase required
+ Context ctx = tomcat.addContext("", null);
+
+ Tomcat.addServlet(ctx, "NoContentLengthConnectionCloseFlushingServlet",
+ new NoContentLengthConnectionCloseFlushingServlet());
+ ctx.addServletMappingDecoded("/test",
+ "NoContentLengthConnectionCloseFlushingServlet");
+
+ tomcat.start();
+
+ ByteChunk responseBody = new ByteChunk();
+ Map<String,List<String>> responseHeaders = new HashMap<>();
+ int rc = getUrl("http://localhost:" + getPort() + "/test", responseBody,
+ responseHeaders);
+
+ assertEquals(HttpServletResponse.SC_OK, rc);
+
+ assertTrue(responseHeaders.containsKey("Connection"));
+ List<String> connections = responseHeaders.get("Connection");
+ assertEquals(1, connections.size());
+ assertEquals("close", connections.get(0));
+
+ assertFalse(responseHeaders.containsKey("Transfer-Encoding"));
+
+ assertEquals("OK", responseBody.toString());
+ }
+
+ @Test
+ public void testBug53677a() throws Exception {
+ doTestBug53677(false);
+ }
+
+ @Test
+ public void testBug53677b() throws Exception {
+ doTestBug53677(true);
+ }
+
+ private void doTestBug53677(boolean flush) throws Exception {
+ Tomcat tomcat = getTomcatInstance();
+
+ // No file system docBase required
+ Context ctx = tomcat.addContext("", null);
+
+ Tomcat.addServlet(ctx, "LargeHeaderServlet",
+ new LargeHeaderServlet(flush));
+ ctx.addServletMappingDecoded("/test", "LargeHeaderServlet");
+
+ tomcat.start();
+
+ ByteChunk responseBody = new ByteChunk();
+ Map<String,List<String>> responseHeaders = new HashMap<>();
+ int rc = getUrl("http://localhost:" + getPort() + "/test", responseBody,
+ responseHeaders);
+
+ assertEquals(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, rc);
+ if (responseBody.getLength() > 0) {
+ // It will be >0 if the standard error page handling has been
+ // triggered
+ assertFalse(responseBody.toString().contains("FAIL"));
+ }
+ }
+
+
+ private static CountDownLatch bug55772Latch1 = new CountDownLatch(1);
+ private static CountDownLatch bug55772Latch2 = new CountDownLatch(1);
+ private static CountDownLatch bug55772Latch3 = new CountDownLatch(1);
+ private static boolean bug55772IsSecondRequest = false;
+ private static boolean bug55772RequestStateLeaked = false;
+
+
+ @Test
+ public void testBug55772() throws Exception {
+ Tomcat tomcat = getTomcatInstance();
+ tomcat.getConnector().setProperty("processorCache", "1");
+ tomcat.getConnector().setProperty("maxThreads", "1");
+
+ // No file system docBase required
+ Context ctx = tomcat.addContext("", null);
+
+ Tomcat.addServlet(ctx, "async", new Bug55772Servlet());
+ ctx.addServletMappingDecoded("/*", "async");
+
+ tomcat.start();
+
+ String request1 = "GET /async?1 HTTP/1.1\r\n" +
+ "Host: localhost:" + getPort() + "\r\n" +
+ "Connection: keep-alive\r\n" +
+ "Cache-Control: max-age=0\r\n" +
+ "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8\r\n" +
+ "User-Agent: Request1\r\n" +
+ "Accept-Encoding: gzip,deflate,sdch\r\n" +
+ "Accept-Language: en-US,en;q=0.8,fr;q=0.6,es;q=0.4\r\n" +
+ "Cookie: something.that.should.not.leak=true\r\n" +
+ "\r\n";
+
+ String request2 = "GET /async?2 HTTP/1.1\r\n" +
+ "Host: localhost:" + getPort() + "\r\n" +
+ "Connection: keep-alive\r\n" +
+ "Cache-Control: max-age=0\r\n" +
+ "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8\r\n" +
+ "User-Agent: Request2\r\n" +
+ "Accept-Encoding: gzip,deflate,sdch\r\n" +
+ "Accept-Language: en-US,en;q=0.8,fr;q=0.6,es;q=0.4\r\n" +
+ "\r\n";
+
+ try (final Socket connection = new Socket("localhost", getPort())) {
+ connection.setSoLinger(true, 0);
+ Writer writer = new OutputStreamWriter(connection.getOutputStream(),
+ StandardCharsets.US_ASCII);
+ writer.write(request1);
+ writer.flush();
+
+ bug55772Latch1.await();
+ connection.close();
+ }
+
+ bug55772Latch2.await();
+ bug55772IsSecondRequest = true;
+
+ try (final Socket connection = new Socket("localhost", getPort())) {
+ connection.setSoLinger(true, 0);
+ Writer writer = new OutputStreamWriter(connection.getOutputStream(),
+ B2CConverter.getCharset("US-ASCII"));
+ writer.write(request2);
+ writer.flush();
+ connection.getInputStream().read();
+ }
+
+ bug55772Latch3.await();
+ if (bug55772RequestStateLeaked) {
+ Assert.fail("State leaked between requests!");
+ }
+ }
+
+
+ // https://bz.apache.org/bugzilla/show_bug.cgi?id=57324
+ @Test
+ public void testNon2xxResponseWithExpectation() throws Exception {
+ doTestNon2xxResponseAndExpectation(true);
+ }
+
+ @Test
+ public void testNon2xxResponseWithoutExpectation() throws Exception {
+ doTestNon2xxResponseAndExpectation(false);
+ }
+
+ private void doTestNon2xxResponseAndExpectation(boolean useExpectation) throws Exception {
+ Tomcat tomcat = getTomcatInstance();
+
+ // No file system docBase required
+ Context ctx = tomcat.addContext("", null);
+
+ Tomcat.addServlet(ctx, "echo", new EchoBodyServlet());
+ ctx.addServletMappingDecoded("/echo", "echo");
+
+ SecurityCollection collection = new SecurityCollection("All", "");
+ collection.addPatternDecoded("/*");
+ SecurityConstraint constraint = new SecurityConstraint();
+ constraint.addAuthRole("Any");
+ constraint.addCollection(collection);
+ ctx.addConstraint(constraint);
+
+ tomcat.start();
+
+ byte[] requestBody = "HelloWorld".getBytes(StandardCharsets.UTF_8);
+ Map<String,List<String>> reqHeaders = null;
+ if (useExpectation) {
+ reqHeaders = new HashMap<>();
+ List<String> expectation = new ArrayList<>();
+ expectation.add("100-continue");
+ reqHeaders.put("Expect", expectation);
+ }
+ ByteChunk responseBody = new ByteChunk();
+ Map<String,List<String>> responseHeaders = new HashMap<>();
+ int rc = postUrl(requestBody, "http://localhost:" + getPort() + "/echo",
+ responseBody, reqHeaders, responseHeaders);
+
+ Assert.assertEquals(HttpServletResponse.SC_FORBIDDEN, rc);
+ List<String> connectionHeaders = responseHeaders.get("Connection");
+ if (useExpectation) {
+ Assert.assertEquals(1, connectionHeaders.size());
+ Assert.assertEquals("close", connectionHeaders.get(0).toLowerCase(Locale.ENGLISH));
+ } else {
+ Assert.assertNull(connectionHeaders);
+ }
+ }
+
+
+ private static class Bug55772Servlet extends HttpServlet {
+
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+ if (bug55772IsSecondRequest) {
+ Cookie[] cookies = req.getCookies();
+ if (cookies != null && cookies.length > 0) {
+ for (Cookie cookie : req.getCookies()) {
+ if (cookie.getName().equalsIgnoreCase("something.that.should.not.leak")) {
+ bug55772RequestStateLeaked = true;
+ }
+ }
+ }
+ bug55772Latch3.countDown();
+ } else {
+ req.getCookies(); // We have to do this so Tomcat will actually parse the cookies from the request
+ }
+
+ req.setAttribute("org.apache.catalina.ASYNC_SUPPORTED", Boolean.TRUE);
+ AsyncContext asyncContext = req.startAsync();
+ asyncContext.setTimeout(5000);
+
+ bug55772Latch1.countDown();
+
+ PrintWriter writer = asyncContext.getResponse().getWriter();
+ writer.print('\n');
+ writer.flush();
+
+ bug55772Latch2.countDown();
+ }
+ }
+
+
+ private static final class LargeHeaderServlet extends HttpServlet {
+
+ private static final long serialVersionUID = 1L;
+
+ boolean flush = false;
+
+ public LargeHeaderServlet(boolean flush) {
+ this.flush = flush;
+ }
+
+ @Override
+ protected void doGet(HttpServletRequest req, HttpServletResponse resp)
+ throws ServletException, IOException {
+ String largeValue =
+ CharBuffer.allocate(10000).toString().replace('\0', 'x');
+ resp.setHeader("x-Test", largeValue);
+ if (flush) {
+ resp.flushBuffer();
+ }
+ resp.setContentType("text/plain");
+ resp.getWriter().print("FAIL");
+ }
+
+ }
+
+ // flushes with no content-length set
+ // should result in chunking on HTTP 1.1
+ private static final class NoContentLengthFlushingServlet
+ extends HttpServlet {
+
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ protected void doGet(HttpServletRequest req, HttpServletResponse resp)
+ throws ServletException, IOException {
+ resp.setStatus(HttpServletResponse.SC_OK);
+ resp.setContentType("text/plain");
+ resp.getWriter().write("OK");
+ resp.flushBuffer();
+ }
+ }
+
+ // flushes with no content-length set but sets Connection: close header
+ // should no result in chunking on HTTP 1.1
+ private static final class NoContentLengthConnectionCloseFlushingServlet
+ extends HttpServlet {
+
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ protected void doGet(HttpServletRequest req, HttpServletResponse resp)
+ throws ServletException, IOException {
+ resp.setStatus(HttpServletResponse.SC_OK);
+ resp.setContentType("text/event-stream");
+ resp.addHeader("Connection", "close");
+ resp.flushBuffer();
+ resp.getWriter().write("OK");
+ resp.flushBuffer();
+ }
+ }
+
+ private static final class Client extends SimpleHttpClient {
+
+ public Client(int port) {
+ setPort(port);
+ }
+
+ @Override
+ public boolean isResponseBodyOK() {
+ return getResponseBody().contains("test - data");
+ }
+ }
+
+
+ /*
+ * Partially read chunked input is not swallowed when it is read during
+ * async processing.
+ */
+ @Test
+ public void testBug57621a() throws Exception {
+ doTestBug57621(true);
+ }
+
+
+ @Test
+ public void testBug57621b() throws Exception {
+ doTestBug57621(false);
+ }
+
+
+ private void doTestBug57621(boolean delayAsyncThread) throws Exception {
+ Tomcat tomcat = getTomcatInstance();
+ Context root = tomcat.addContext("", null);
+ Wrapper w = Tomcat.addServlet(root, "Bug57621", new Bug57621Servlet(delayAsyncThread));
+ w.setAsyncSupported(true);
+ root.addServletMappingDecoded("/test", "Bug57621");
+
+ tomcat.start();
+
+ Bug57621Client client = new Bug57621Client();
+ client.setPort(tomcat.getConnector().getLocalPort());
+
+ client.setUseContentLength(true);
+
+ client.connect();
+
+ client.doRequest();
+ assertTrue(client.getResponseLine(), client.isResponse200());
+ assertTrue(client.isResponseBodyOK());
+
+ // Do the request again to ensure that the remaining body was swallowed
+ client.resetResponse();
+ client.processRequest();
+ assertTrue(client.isResponse200());
+ assertTrue(client.isResponseBodyOK());
+
+ client.disconnect();
+ }
+
+
+ private static class Bug57621Servlet extends HttpServlet {
+
+ private static final long serialVersionUID = 1L;
+
+ private final boolean delayAsyncThread;
+
+
+ public Bug57621Servlet(boolean delayAsyncThread) {
+ this.delayAsyncThread = delayAsyncThread;
+ }
+
+
+ @Override
+ protected void doPut(HttpServletRequest req, final HttpServletResponse resp)
+ throws ServletException, IOException {
+ final AsyncContext ac = req.startAsync();
+ ac.start(new Runnable() {
+ @Override
+ public void run() {
+ if (delayAsyncThread) {
+ // Makes the difference between calling complete before
+ // the request body is received of after.
+ try {
+ Thread.sleep(1500);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+ resp.setContentType("text/plain");
+ resp.setCharacterEncoding("UTF-8");
+ try {
+ resp.getWriter().print("OK");
+ } catch (IOException e) {
+ // Should never happen. Test will fail if it does.
+ }
+ ac.complete();
+ }
+ });
+ }
+ }
+
+
+ private static class Bug57621Client extends SimpleHttpClient {
+
+ private Exception doRequest() {
+ try {
+ String[] request = new String[2];
+ request[0] =
+ "PUT http://localhost:8080/test HTTP/1.1" + CRLF +
+ "Transfer-encoding: chunked" + CRLF +
+ CRLF +
+ "2" + CRLF +
+ "OK";
+
+ request[1] =
+ CRLF +
+ "0" + CRLF +
+ CRLF;
+
+ setRequest(request);
+ processRequest(); // blocks until response has been read
+ } catch (Exception e) {
+ return e;
+ }
+ return null;
+ }
+
+ @Override
+ public boolean isResponseBodyOK() {
+ if (getResponseBody() == null) {
+ return false;
+ }
+ if (!getResponseBody().contains("OK")) {
+ return false;
+ }
+ return true;
+ }
+ }
+
+
+ @Test
+ public void testBug59310() throws Exception {
+ Tomcat tomcat = getTomcatInstance();
+
+ // No file system docBase required
+ Context ctx = tomcat.addContext("", null);
+
+ Tomcat.addServlet(ctx, "Bug59310", new Bug59310Servlet());
+ ctx.addServletMappingDecoded("/test", "Bug59310");
+
+ tomcat.start();
+
+ ByteChunk responseBody = new ByteChunk();
+ Map<String,List<String>> responseHeaders = new HashMap<>();
+
+ int rc = headUrl("http://localhost:" + getPort() + "/test", responseBody,
+ responseHeaders);
+
+ assertEquals(HttpServletResponse.SC_OK, rc);
+ assertEquals(0, responseBody.getLength());
+ assertFalse(responseHeaders.containsKey("Content-Length"));
+ }
+
+
+ private static class Bug59310Servlet extends HttpServlet {
+
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ protected void doGet(HttpServletRequest req, HttpServletResponse resp)
+ throws ServletException, IOException {
+ super.doGet(req, resp);
+ }
+
+ @Override
+ protected void doHead(HttpServletRequest req, HttpServletResponse resp)
+ throws ServletException, IOException {
+ }
+ }
+
+
+ /*
+ * Tests what happens if a request is completed during a dispatch but the
+ * request body has not been fully read.
+ */
+ @Test
+ public void testRequestBodySwallowing() throws Exception {
+ Tomcat tomcat = getTomcatInstance();
+
+ // No file system docBase required
+ Context ctx = tomcat.addContext("", null);
+
+ DispatchingServlet servlet = new DispatchingServlet();
+ Wrapper w = Tomcat.addServlet(ctx, "Test", servlet);
+ w.setAsyncSupported(true);
+ ctx.addServletMappingDecoded("/test", "Test");
+
+ tomcat.start();
+
+ // Hand-craft the client so we have complete control over the timing
+ SocketAddress addr = new InetSocketAddress("localhost", getPort());
+ Socket socket = new Socket();
+ socket.setSoTimeout(300000);
+ socket.connect(addr,300000);
+ OutputStream os = socket.getOutputStream();
+ Writer writer = new OutputStreamWriter(os, "ISO-8859-1");
+ InputStream is = socket.getInputStream();
+ Reader r = new InputStreamReader(is, "ISO-8859-1");
+ BufferedReader reader = new BufferedReader(r);
+
+ // Write the headers
+ writer.write("POST /test HTTP/1.1\r\n");
+ writer.write("Host: localhost:8080\r\n");
+ writer.write("Transfer-Encoding: chunked\r\n");
+ writer.write("\r\n");
+ writer.flush();
+
+ validateResponse(reader);
+
+ // Write the request body
+ writer.write("2\r\n");
+ writer.write("AB\r\n");
+ writer.write("0\r\n");
+ writer.write("\r\n");
+ writer.flush();
+
+ // Write the 2nd request
+ writer.write("POST /test HTTP/1.1\r\n");
+ writer.write("Host: localhost:8080\r\n");
+ writer.write("Transfer-Encoding: chunked\r\n");
+ writer.write("\r\n");
+ writer.flush();
+
+ // Read the 2nd response
+ validateResponse(reader);
+
+ // Write the 2nd request body
+ writer.write("2\r\n");
+ writer.write("AB\r\n");
+ writer.write("0\r\n");
+ writer.write("\r\n");
+ writer.flush();
+
+ // Done
+ socket.close();
+ }
+
+
+ private void validateResponse(BufferedReader reader) throws IOException {
+ // First line has the response code and should always be 200
+ String line = reader.readLine();
+ Assert.assertEquals("HTTP/1.1 200 ", line);
+ while (!"OK".equals(line)) {
+ line = reader.readLine();
+ }
+ }
+
+
+ private static class DispatchingServlet extends HttpServlet {
+
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ protected void doPost(HttpServletRequest req, HttpServletResponse resp)
+ throws ServletException, IOException {
+ if (DispatcherType.ASYNC.equals(req.getDispatcherType())) {
+ resp.setContentType("text/plain");
+ resp.setCharacterEncoding("UTF-8");
+ resp.getWriter().write("OK\n");
+ } else {
+ req.startAsync().dispatch();
+ }
+ }
+ }
+}
diff --git a/test/org/apache/coyote/http11/TestInternalInputBuffer.java b/test/org/apache/coyote/http11/TestInternalInputBuffer.java
deleted file mode 100644
index 3c31e08..0000000
--- a/test/org/apache/coyote/http11/TestInternalInputBuffer.java
+++ /dev/null
@@ -1,603 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.coyote.http11;
-
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.util.Enumeration;
-
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServlet;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
-import org.junit.Test;
-
-import org.apache.catalina.Context;
-import org.apache.catalina.startup.SimpleHttpClient;
-import org.apache.catalina.startup.TesterServlet;
-import org.apache.catalina.startup.Tomcat;
-import org.apache.catalina.startup.TomcatBaseTest;
-
-public class TestInternalInputBuffer extends TomcatBaseTest {
-
- /**
- * Test case for https://bz.apache.org/bugzilla/show_bug.cgi?id=48839
- */
- @Test
- public void testBug48839() {
-
- Bug48839Client client = new Bug48839Client();
-
- client.doRequest();
- assertTrue(client.isResponse200());
- assertTrue(client.isResponseBodyOK());
- }
-
-
- /**
- * Bug 48839 test client.
- */
- private class Bug48839Client extends SimpleHttpClient {
-
- private Exception doRequest() {
-
- Tomcat tomcat = getTomcatInstance();
-
- Context root = tomcat.addContext("", TEMP_DIR);
- Tomcat.addServlet(root, "Bug48839", new Bug48839Servlet());
- root.addServletMappingDecoded("/test", "Bug48839");
-
- try {
- tomcat.start();
- setPort(tomcat.getConnector().getLocalPort());
-
- // Open connection
- connect();
-
- String[] request = new String[1];
- request[0] =
- "GET http://localhost:8080/test HTTP/1.1" + CRLF +
- "X-Bug48839: abcd" + CRLF +
- "\tefgh" + CRLF +
- "Connection: close" + CRLF +
- CRLF;
-
- setRequest(request);
- processRequest(); // blocks until response has been read
-
- // Close the connection
- disconnect();
- } catch (Exception e) {
- return e;
- }
- return null;
- }
-
- @Override
- public boolean isResponseBodyOK() {
- if (getResponseBody() == null) {
- return false;
- }
- if (!getResponseBody().contains("abcd\tefgh")) {
- return false;
- }
- return true;
- }
-
- }
-
- private static class Bug48839Servlet extends HttpServlet {
-
- private static final long serialVersionUID = 1L;
-
- /**
- * Only interested in the request headers from a GET request
- */
- @Override
- protected void doGet(HttpServletRequest req, HttpServletResponse resp)
- throws ServletException, IOException {
- // Just echo the header value back as plain text
- resp.setContentType("text/plain");
-
- PrintWriter out = resp.getWriter();
-
- Enumeration<String> values = req.getHeaders("X-Bug48839");
- while (values.hasMoreElements()) {
- out.println(values.nextElement());
- }
- }
- }
-
-
- @Test
- public void testBug51557NoColon() {
-
- Bug51557Client client = new Bug51557Client("X-Bug51557NoColon");
-
- client.doRequest();
- assertTrue(client.isResponse200());
- assertEquals("abcd", client.getResponseBody());
- assertTrue(client.isResponseBodyOK());
- }
-
-
- @Test
- public void testBug51557Separators() throws Exception {
- char httpSeparators[] = new char[] {
- '\t', ' ', '\"', '(', ')', ',', '/', ':', ';', '<',
- '=', '>', '?', '@', '[', '\\', ']', '{', '}' };
-
- for (char s : httpSeparators) {
- doTestBug51557Char(s);
- tearDown();
- setUp();
- }
- }
-
-
- @Test
- public void testBug51557Ctl() throws Exception {
- for (int i = 0; i < 31; i++) {
- doTestBug51557Char((char) i);
- tearDown();
- setUp();
- }
- doTestBug51557Char((char) 127);
- }
-
-
- @Test
- public void testBug51557Continuation() {
-
- Bug51557Client client = new Bug51557Client("X-Bug=51557NoColon",
- "foo" + SimpleHttpClient.CRLF + " bar");
-
- client.doRequest();
- assertTrue(client.isResponse200());
- assertEquals("abcd", client.getResponseBody());
- assertTrue(client.isResponseBodyOK());
- }
-
-
- @Test
- public void testBug51557BoundaryStart() {
-
- Bug51557Client client = new Bug51557Client("=X-Bug51557",
- "invalid");
-
- client.doRequest();
- assertTrue(client.isResponse200());
- assertEquals("abcd", client.getResponseBody());
- assertTrue(client.isResponseBodyOK());
- }
-
-
- @Test
- public void testBug51557BoundaryEnd() {
-
- Bug51557Client client = new Bug51557Client("X-Bug51557=",
- "invalid");
-
- client.doRequest();
- assertTrue(client.isResponse200());
- assertEquals("abcd", client.getResponseBody());
- assertTrue(client.isResponseBodyOK());
- }
-
-
- private void doTestBug51557Char(char s) {
- Bug51557Client client =
- new Bug51557Client("X-Bug" + s + "51557", "invalid");
-
- client.doRequest();
- assertTrue(client.isResponse200());
- assertEquals("abcd", client.getResponseBody());
- assertTrue(client.isResponseBodyOK());
- }
-
- /**
- * Bug 51557 test client.
- */
- private class Bug51557Client extends SimpleHttpClient {
-
- private String headerName;
- private String headerLine;
-
- public Bug51557Client(String headerName) {
- this.headerName = headerName;
- this.headerLine = headerName;
- }
-
- public Bug51557Client(String headerName, String headerValue) {
- this.headerName = headerName;
- this.headerLine = headerName + ": " + headerValue;
- }
-
- private Exception doRequest() {
-
- Tomcat tomcat = getTomcatInstance();
-
- Context root = tomcat.addContext("", TEMP_DIR);
- Tomcat.addServlet(root, "Bug51557",
- new Bug51557Servlet(headerName));
- root.addServletMappingDecoded("/test", "Bug51557");
-
- try {
- tomcat.start();
- setPort(tomcat.getConnector().getLocalPort());
-
- // Open connection
- connect();
-
- String[] request = new String[1];
- request[0] =
- "GET http://localhost:8080/test HTTP/1.1" + CRLF +
- headerLine + CRLF +
- "X-Bug51557: abcd" + CRLF +
- "Connection: close" + CRLF +
- CRLF;
-
- setRequest(request);
- processRequest(); // blocks until response has been read
-
- // Close the connection
- disconnect();
- } catch (Exception e) {
- return e;
- }
- return null;
- }
-
- @Override
- public boolean isResponseBodyOK() {
- if (getResponseBody() == null) {
- return false;
- }
- if (!getResponseBody().contains("abcd")) {
- return false;
- }
- return true;
- }
-
- }
-
- private static class Bug51557Servlet extends HttpServlet {
-
- private static final long serialVersionUID = 1L;
-
- private String invalidHeaderName;
-
- /**
- * @param invalidHeaderName The header name should be invalid and
- * therefore ignored by the header parsing code
- */
- public Bug51557Servlet(String invalidHeaderName) {
- this.invalidHeaderName = invalidHeaderName;
- }
-
- /**
- * Only interested in the request headers from a GET request
- */
- @Override
- protected void doGet(HttpServletRequest req, HttpServletResponse resp)
- throws ServletException, IOException {
- // Just echo the header value back as plain text
- resp.setContentType("text/plain");
-
- PrintWriter out = resp.getWriter();
-
- processHeaders(invalidHeaderName, req, out);
- processHeaders("X-Bug51557", req, out);
- }
-
- private void processHeaders(String header, HttpServletRequest req,
- PrintWriter out) {
- Enumeration<String> values = req.getHeaders(header);
- while (values.hasMoreElements()) {
- out.println(values.nextElement());
- }
- }
- }
-
-
- /**
- * Test case for new lines at the start of a request. RFC2616
- * does not permit any, but Tomcat is tolerant of them if they are present.
- */
- @Test
- public void testNewLines() {
-
- NewLinesClient client = new NewLinesClient(10);
-
- client.doRequest();
- assertTrue(client.isResponse200());
- assertTrue(client.isResponseBodyOK());
- }
-
-
- /**
- * Test case for new lines at the start of a request. RFC2616
- * does not permit any, but Tomcat is tolerant of them if they are present.
- */
- @Test
- public void testNewLinesExcessive() {
-
- NewLinesClient client = new NewLinesClient(10000);
-
- // If the connection is closed fast enough, writing the request will
- // fail and the response won't be read.
- Exception e = client.doRequest();
- if (e == null) {
- assertTrue(client.isResponse400());
- }
- assertFalse(client.isResponseBodyOK());
- }
-
-
- private class NewLinesClient extends SimpleHttpClient {
-
- private final String newLines;
-
- private NewLinesClient(int count) {
- StringBuilder sb = new StringBuilder(count * 2);
- for (int i = 0; i < count; i++) {
- sb.append(CRLF);
- }
- newLines = sb.toString();
- }
-
- private Exception doRequest() {
-
- Tomcat tomcat = getTomcatInstance();
-
- Context root = tomcat.addContext("", TEMP_DIR);
- Tomcat.addServlet(root, "test", new TesterServlet());
- root.addServletMappingDecoded("/test", "test");
-
- try {
- tomcat.start();
- setPort(tomcat.getConnector().getLocalPort());
-
- // Open connection
- connect();
-
- String[] request = new String[1];
- request[0] =
- newLines +
- "GET http://localhost:8080/test HTTP/1.1" + CRLF +
- "X-Bug48839: abcd" + CRLF +
- "\tefgh" + CRLF +
- "Connection: close" + CRLF +
- CRLF;
-
- setRequest(request);
- processRequest(); // blocks until response has been read
-
- // Close the connection
- disconnect();
- } catch (Exception e) {
- return e;
- }
- return null;
- }
-
- @Override
- public boolean isResponseBodyOK() {
- if (getResponseBody() == null) {
- return false;
- }
- if (!getResponseBody().contains("OK")) {
- return false;
- }
- return true;
- }
-
- }
-
-
- /**
- * Test case for https://bz.apache.org/bugzilla/show_bug.cgi?id=54947
- */
- @Test
- public void testBug54947() {
-
- Bug54947Client client = new Bug54947Client();
-
- client.doRequest();
- assertTrue(client.isResponse200());
- assertTrue(client.isResponseBodyOK());
- }
-
-
- /**
- * Bug 54947 test client.
- */
- private class Bug54947Client extends SimpleHttpClient {
-
- private Exception doRequest() {
-
- Tomcat tomcat = getTomcatInstance();
-
- Context root = tomcat.addContext("", TEMP_DIR);
- Tomcat.addServlet(root, "Bug54947", new TesterServlet());
- root.addServletMappingDecoded("/test", "Bug54947");
-
- try {
- tomcat.start();
- setPort(tomcat.getConnector().getLocalPort());
-
- // Open connection
- connect();
-
- String[] request = new String[2];
- request[0] = "GET http://localhost:8080/test HTTP/1.1" + CR;
- request[1] = LF +
- "Connection: close" + CRLF +
- CRLF;
-
- setRequest(request);
- processRequest(); // blocks until response has been read
-
- // Close the connection
- disconnect();
- } catch (Exception e) {
- return e;
- }
- return null;
- }
-
- @Override
- public boolean isResponseBodyOK() {
- if (getResponseBody() == null) {
- return false;
- }
- if (!getResponseBody().contains("OK")) {
- return false;
- }
- return true;
- }
- }
-
-
- /**
- * Test case for https://bz.apache.org/bugzilla/show_bug.cgi?id=59089
- */
- @Test
- public void testBug59089() {
-
- Bug59089Client client = new Bug59089Client();
-
- client.doRequest();
- assertTrue(client.isResponse200());
- assertTrue(client.isResponseBodyOK());
- }
-
-
- /**
- * Bug 59089 test client.
- */
- private class Bug59089Client extends SimpleHttpClient {
-
- private Exception doRequest() {
-
- // Ensure body is read correctly
- setUseContentLength(true);
-
- Tomcat tomcat = getTomcatInstance();
-
- Context root = tomcat.addContext("", TEMP_DIR);
- Tomcat.addServlet(root, "Bug59089", new TesterServlet());
- root.addServletMappingDecoded("/test", "Bug59089");
-
- try {
- tomcat.start();
- setPort(tomcat.getConnector().getLocalPort());
-
- // Open connection
- connect();
-
- String[] request = new String[1];
- request[0] = "GET http://localhost:8080/test HTTP/1.1" + CRLF +
- "X-Header: Ignore" + CRLF +
- "X-Header" + (char) 130 + ": Broken" + CRLF + CRLF;
-
- setRequest(request);
- processRequest(); // blocks until response has been read
-
- // Close the connection
- disconnect();
- } catch (Exception e) {
- return e;
- }
- return null;
- }
-
- @Override
- public boolean isResponseBodyOK() {
- if (getResponseBody() == null) {
- return false;
- }
- if (!getResponseBody().contains("OK")) {
- return false;
- }
- return true;
- }
- }
-
-
- @Test
- public void testInvalidMethod() {
-
- InvalidMethodClient client = new InvalidMethodClient();
-
- client.doRequest();
- assertTrue(client.getResponseLine(), client.isResponse400());
- assertTrue(client.isResponseBodyOK());
- }
-
-
- /**
- * Bug 48839 test client.
- */
- private class InvalidMethodClient extends SimpleHttpClient {
-
- private Exception doRequest() {
-
- Tomcat tomcat = getTomcatInstance();
-
- tomcat.addContext("", TEMP_DIR);
-
- try {
- tomcat.start();
- setPort(tomcat.getConnector().getLocalPort());
-
- // Open connection
- connect();
-
- String[] request = new String[1];
- request[0] =
- "GET" + (char) 0 + " /test HTTP/1.1" + CRLF +
- "Host: localhost:8080" + CRLF +
- "Connection: close" + CRLF +
- CRLF;
-
- setRequest(request);
- processRequest(); // blocks until response has been read
-
- // Close the connection
- disconnect();
- } catch (Exception e) {
- return e;
- }
- return null;
- }
-
- @Override
- public boolean isResponseBodyOK() {
- if (getResponseBody() == null) {
- return false;
- }
- return true;
- }
- }
-}
diff --git a/test/org/apache/coyote/http11/filters/TestChunkedInputFilter.java b/test/org/apache/coyote/http11/filters/TestChunkedInputFilter.java
index daea905..09d9917 100644
--- a/test/org/apache/coyote/http11/filters/TestChunkedInputFilter.java
+++ b/test/org/apache/coyote/http11/filters/TestChunkedInputFilter.java
@@ -465,7 +465,7 @@ public class TestChunkedInputFilter extends TomcatBaseTest {
throw ioe;
}
- pw.write(Integer.valueOf(count).toString());
+ pw.write(Integer.toString(count));
// Headers should be visible now
dumpHeader("x-trailer1", req, pw);
@@ -521,7 +521,7 @@ public class TestChunkedInputFilter extends TomcatBaseTest {
throw ioe;
}
- pw.write(Integer.valueOf(countRead).toString());
+ pw.write(Integer.toString(countRead));
}
public boolean getExceptionDuringRead() {
diff --git a/test/org/apache/coyote/http11/filters/TestGzipOutputFilter.java b/test/org/apache/coyote/http11/filters/TestGzipOutputFilter.java
new file mode 100644
index 0000000..6bc8020
--- /dev/null
+++ b/test/org/apache/coyote/http11/filters/TestGzipOutputFilter.java
@@ -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.coyote.http11.filters;
+
+import java.io.ByteArrayOutputStream;
+import java.nio.ByteBuffer;
+import java.util.zip.GZIPOutputStream;
+
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import org.apache.coyote.Response;
+
+/**
+ * Test case to demonstrate the interaction between gzip and flushing in the
+ * output filter.
+ */
+public class TestGzipOutputFilter {
+
+ /*
+ * Test the interaction between gzip and flushing. The idea is to: 1. create
+ * a internal output buffer, response, and attach an active gzipoutputfilter
+ * to the output buffer 2. set the output stream of the internal buffer to
+ * be a ByteArrayOutputStream so we can inspect the output bytes 3. write a
+ * chunk out using the gzipoutputfilter and invoke a flush on the
+ * InternalOutputBuffer 4. read from the ByteArrayOutputStream to find out
+ * what's being written out (flushed) 5. find out what's expected by wrting
+ * to GZIPOutputStream and close it (to force flushing) 6. Compare the size
+ * of the two arrays, they should be close (instead of one being much
+ * shorter than the other one)
+ *
+ * @throws Exception
+ */
+ @Test
+ public void testFlushingWithGzip() throws Exception {
+ // set up response, InternalOutputBuffer, and ByteArrayOutputStream
+ Response res = new Response();
+ TesterOutputBuffer tob = new TesterOutputBuffer(res, 8 * 1024);
+ res.setOutputBuffer(tob);
+
+ // set up GzipOutputFilter to attach to the TesterOutputBuffer
+ GzipOutputFilter gf = new GzipOutputFilter();
+ tob.addFilter(gf);
+ tob.addActiveFilter(gf);
+
+ // write a chunk out
+ byte[] d = "Hello there tomcat developers, there is a bug in JDK".getBytes();
+ ByteBuffer bb = ByteBuffer.wrap(d);
+ tob.doWrite(bb);
+
+ // flush the InternalOutputBuffer
+ tob.flush();
+
+ // read from the ByteArrayOutputStream to find out what's being written
+ // out (flushed)
+ byte[] dataFound = tob.toByteArray();
+
+ // find out what's expected by writing to GZIPOutputStream and close it
+ // (to force flushing)
+ ByteArrayOutputStream gbos = new ByteArrayOutputStream(1024);
+ GZIPOutputStream gos = new GZIPOutputStream(gbos);
+ gos.write(d);
+ gos.close();
+
+ // read the expected data
+ byte[] dataExpected = gbos.toByteArray();
+
+ // most of the data should have been flushed out
+ assertTrue(dataFound.length >= (dataExpected.length - 20));
+ }
+}
diff --git a/test/org/apache/coyote/http11/filters/TesterOutputBuffer.java b/test/org/apache/coyote/http11/filters/TesterOutputBuffer.java
new file mode 100644
index 0000000..7a05c5d
--- /dev/null
+++ b/test/org/apache/coyote/http11/filters/TesterOutputBuffer.java
@@ -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 org.apache.coyote.http11.filters;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+import org.apache.coyote.OutputBuffer;
+import org.apache.coyote.Response;
+import org.apache.coyote.http11.Http11OutputBuffer;
+import org.apache.tomcat.util.buf.ByteChunk;
+import org.apache.tomcat.util.net.SocketWrapperBase;
+
+/**
+ * Output buffer for use in unit tests. This is a minimal implementation.
+ */
+public class TesterOutputBuffer extends Http11OutputBuffer {
+
+ /**
+ * Underlying output stream.
+ */
+ private ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+
+
+ public TesterOutputBuffer(Response response, int headerBufferSize) {
+ super(response, headerBufferSize);
+ outputStreamOutputBuffer = new OutputStreamOutputBuffer();
+ }
+
+
+ // --------------------------------------------------------- Public Methods
+
+ @Override
+ public void init(SocketWrapperBase<?> socketWrapper) {
+ // NO-OP: Unused
+ }
+
+
+ /**
+ * Recycle the output buffer. This should be called when closing the
+ * connection.
+ */
+ @Override
+ public void recycle() {
+ super.recycle();
+ outputStream = null;
+ }
+
+
+ // ------------------------------------------------ HTTP/1.1 Output Methods
+
+ /**
+ * Send an acknowledgement.
+ */
+ @Override
+ public void sendAck() {
+ // NO-OP: Unused
+ }
+
+
+ @Override
+ protected void commit() {
+ // NO-OP: Unused
+ }
+
+
+ @Override
+ protected boolean flushBuffer(boolean block) throws IOException {
+ // Blocking IO so ignore block parameter as this will always use
+ // blocking IO.
+ // Always blocks so never any data left over.
+ return false;
+ }
+
+
+ /*
+ * Expose data written for use by unit tests.
+ */
+ byte[] toByteArray() {
+ return outputStream.toByteArray();
+ }
+
+
+ /**
+ * This class is an output buffer which will write data to an output
+ * stream.
+ */
+ protected class OutputStreamOutputBuffer implements OutputBuffer {
+
+ @Override
+ public int doWrite(ByteChunk chunk) throws IOException {
+ int length = chunk.getLength();
+ outputStream.write(chunk.getBuffer(), chunk.getStart(), length);
+ byteCount += chunk.getLength();
+ return chunk.getLength();
+ }
+
+ @Override
+ public int doWrite(ByteBuffer chunk) throws IOException {
+ int length = chunk.remaining();
+ outputStream.write(chunk.array(), chunk.arrayOffset() + chunk.position(), length);
+ byteCount += length;
+ return length;
+ }
+
+ @Override
+ public long getBytesWritten() {
+ return byteCount;
+ }
+ }
+}
diff --git a/test/org/apache/coyote/http11/upgrade/TestUpgrade.java b/test/org/apache/coyote/http11/upgrade/TestUpgrade.java
index ef765fb..1e542c0 100644
--- a/test/org/apache/coyote/http11/upgrade/TestUpgrade.java
+++ b/test/org/apache/coyote/http11/upgrade/TestUpgrade.java
@@ -47,7 +47,6 @@ import static org.apache.catalina.startup.SimpleHttpClient.CRLF;
import org.apache.catalina.Context;
import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.startup.TomcatBaseTest;
-import org.apache.catalina.util.IOTools;
public class TestUpgrade extends TomcatBaseTest {
@@ -139,6 +138,8 @@ public class TestUpgrade extends TomcatBaseTest {
pw.println(MESSAGE);
pw.flush();
+ uc.shutdownOutput();
+
// Note: BufferedReader.readLine() strips new lines
// ServletInputStream.readLine() does not strip new lines
String response = reader.readLine();
@@ -146,7 +147,6 @@ public class TestUpgrade extends TomcatBaseTest {
response = reader.readLine();
Assert.assertEquals(MESSAGE, response);
- uc.shutdownOutput();
uc.shutdownInput();
}
@@ -260,8 +260,12 @@ public class TestUpgrade extends TomcatBaseTest {
try (ServletInputStream sis = connection.getInputStream();
ServletOutputStream sos = connection.getOutputStream()){
-
- IOTools.flow(sis, sos);
+ byte[] buffer = new byte[8192];
+ int read;
+ while ((read = sis.read(buffer)) >= 0) {
+ sos.write(buffer, 0, read);
+ sos.flush();
+ }
} catch (IOException ioe) {
throw new IllegalStateException(ioe);
}
@@ -276,11 +280,10 @@ public class TestUpgrade extends TomcatBaseTest {
public static class EchoNonBlocking implements HttpUpgradeHandler {
- private ServletInputStream sis;
- private ServletOutputStream sos;
-
@Override
public void init(WebConnection connection) {
+ ServletInputStream sis;
+ ServletOutputStream sos;
try {
sis = connection.getInputStream();
@@ -289,8 +292,9 @@ public class TestUpgrade extends TomcatBaseTest {
throw new IllegalStateException(ioe);
}
- sis.setReadListener(new EchoReadListener());
- sos.setWriteListener(new NoOpWriteListener());
+ EchoListener echoListener = new EchoListener(sis, sos);
+ sis.setReadListener(echoListener);
+ sos.setWriteListener(echoListener);
}
@Override
@@ -298,28 +302,53 @@ public class TestUpgrade extends TomcatBaseTest {
// NO-OP
}
- private class EchoReadListener extends NoOpReadListener {
- private byte[] buffer = new byte[8096];
+ private class EchoListener implements ReadListener, WriteListener {
+
+ private final ServletInputStream sis;
+ private final ServletOutputStream sos;
+ private final byte[] buffer = new byte[8192];
+
+ public EchoListener(ServletInputStream sis, ServletOutputStream sos) {
+ this.sis = sis;
+ this.sos = sos;
+ }
@Override
- public void onDataAvailable() {
- try {
- while (sis.isReady()) {
- int read = sis.read(buffer);
- if (read > 0) {
- if (sos.isReady()) {
- sos.write(buffer, 0, read);
- } else {
- throw new IOException("Unable to echo data. " +
- "isReady() returned false");
- }
+ public void onWritePossible() throws IOException {
+ if (sis.isFinished()) {
+ sis.close();
+ sos.close();
+ }
+ while (sis.isReady()) {
+ int read = sis.read(buffer);
+ if (read > 0) {
+ sos.write(buffer, 0, read);
+ if (!sos.isReady()) {
+ break;
}
}
- } catch (IOException ioe) {
- throw new RuntimeException(ioe);
}
}
+
+ @Override
+ public void onDataAvailable() throws IOException {
+ if (sos.isReady()) {
+ onWritePossible();
+ }
+ }
+
+ @Override
+ public void onAllDataRead() throws IOException {
+ if (sos.isReady()) {
+ onWritePossible();
+ }
+ }
+
+ @Override
+ public void onError(Throwable throwable) {
+ throwable.printStackTrace();
+ }
}
}
diff --git a/test/org/apache/coyote/http11/upgrade/TestUpgradeInternalHandler.java b/test/org/apache/coyote/http11/upgrade/TestUpgradeInternalHandler.java
new file mode 100644
index 0000000..6e26fde
--- /dev/null
+++ b/test/org/apache/coyote/http11/upgrade/TestUpgradeInternalHandler.java
@@ -0,0 +1,267 @@
+/*
+ * 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.http11.upgrade;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.io.Writer;
+import java.net.Socket;
+import java.nio.ByteBuffer;
+import java.nio.channels.CompletionHandler;
+import java.util.concurrent.TimeUnit;
+
+import javax.net.SocketFactory;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpUpgradeHandler;
+import javax.servlet.http.WebConnection;
+
+import org.junit.Assert;
+import org.junit.Assume;
+import org.junit.Test;
+
+import static org.apache.catalina.startup.SimpleHttpClient.CRLF;
+
+import org.apache.catalina.Context;
+import org.apache.catalina.startup.Tomcat;
+import org.apache.catalina.startup.TomcatBaseTest;
+import org.apache.tomcat.util.net.AbstractEndpoint.Handler.SocketState;
+import org.apache.tomcat.util.net.SSLSupport;
+import org.apache.tomcat.util.net.SocketEvent;
+import org.apache.tomcat.util.net.SocketWrapperBase;
+import org.apache.tomcat.util.net.SocketWrapperBase.CompletionState;
+
+public class TestUpgradeInternalHandler extends TomcatBaseTest {
+
+ private static final String MESSAGE = "This is a test.";
+
+ @Test
+ public void testUpgradeInternal() throws Exception {
+ Assume.assumeTrue(
+ "Only supported on NIO 2",
+ getTomcatInstance().getConnector().getProtocolHandlerClassName().contains("Nio2"));
+
+ UpgradeConnection uc = doUpgrade(EchoAsync.class);
+ PrintWriter pw = new PrintWriter(uc.getWriter());
+ BufferedReader reader = uc.getReader();
+
+ // Add extra sleep to avoid completing inline
+ Thread.sleep(500);
+ pw.println(MESSAGE);
+ pw.flush();
+ Thread.sleep(500);
+ uc.shutdownOutput();
+
+ // Note: BufferedReader.readLine() strips new lines
+ // ServletInputStream.readLine() does not strip new lines
+ String response = reader.readLine();
+ Assert.assertEquals(MESSAGE, response);
+
+ uc.shutdownInput();
+ }
+
+ private UpgradeConnection doUpgrade(
+ Class<? extends HttpUpgradeHandler> upgradeHandlerClass) throws Exception {
+ // Setup Tomcat instance
+ Tomcat tomcat = getTomcatInstance();
+
+ // No file system docBase required
+ Context ctx = tomcat.addContext("", null);
+
+ UpgradeServlet servlet = new UpgradeServlet(upgradeHandlerClass);
+ Tomcat.addServlet(ctx, "servlet", servlet);
+ ctx.addServletMappingDecoded("/", "servlet");
+
+ tomcat.start();
+
+ // Use raw socket so the necessary control is available after the HTTP
+ // upgrade
+ Socket socket =
+ SocketFactory.getDefault().createSocket("localhost", getPort());
+
+ socket.setSoTimeout(5000);
+
+ UpgradeConnection uc = new UpgradeConnection(socket);
+
+ uc.getWriter().write("GET / HTTP/1.1" + CRLF);
+ uc.getWriter().write("Host: whatever" + CRLF);
+ uc.getWriter().write(CRLF);
+ uc.getWriter().flush();
+
+ String status = uc.getReader().readLine();
+
+ Assert.assertNotNull(status);
+ Assert.assertEquals("101", getStatusCode(status));
+
+ // Skip the remaining response headers
+ String line = uc.getReader().readLine();
+ while (line != null && line.length() > 0) {
+ // Skip
+ line = uc.getReader().readLine();
+ }
+
+ return uc;
+ }
+
+ private static class UpgradeServlet extends HttpServlet {
+
+ private static final long serialVersionUID = 1L;
+
+ private final Class<? extends HttpUpgradeHandler> upgradeHandlerClass;
+
+ public UpgradeServlet(Class<? extends HttpUpgradeHandler> upgradeHandlerClass) {
+ this.upgradeHandlerClass = upgradeHandlerClass;
+ }
+
+ @Override
+ protected void doGet(HttpServletRequest req, HttpServletResponse resp)
+ throws ServletException, IOException {
+
+ req.upgrade(upgradeHandlerClass);
+ }
+ }
+
+ private static class UpgradeConnection {
+ private final Socket socket;
+ private final Writer writer;
+ private final BufferedReader reader;
+
+ public UpgradeConnection(Socket socket) {
+ this.socket = socket;
+ InputStream is;
+ OutputStream os;
+ try {
+ is = socket.getInputStream();
+ os = socket.getOutputStream();
+ } catch (IOException ioe) {
+ throw new IllegalArgumentException(ioe);
+ }
+
+ BufferedReader reader = new BufferedReader(new InputStreamReader(is));
+ Writer writer = new OutputStreamWriter(os);
+
+ this.writer = writer;
+ this.reader = reader;
+ }
+
+ public Writer getWriter() {
+ return writer;
+ }
+
+ public BufferedReader getReader() {
+ return reader;
+ }
+
+ public void shutdownOutput() throws IOException {
+ writer.flush();
+ socket.shutdownOutput();
+ }
+
+ public void shutdownInput() throws IOException {
+ socket.shutdownInput();
+ }
+ }
+
+
+ public static class EchoAsync implements InternalHttpUpgradeHandler {
+ private SocketWrapperBase<?> wrapper;
+ @Override
+ public void init(WebConnection connection) {
+ System.out.println("Init: " + connection);
+ // Arbitrarily located in the init, could be in the initial read event, asynchronous, etc.
+ // Note: the completion check used will not call the completion handler if the IO completed inline and without error.
+ // Using a completion check that always calls complete would be easier here since the action is the same even with inline completion.
+ final ByteBuffer buffer = ByteBuffer.allocate(1024);
+ CompletionState state = wrapper.read(false, 10, TimeUnit.SECONDS, null, SocketWrapperBase.READ_DATA, new CompletionHandler<Long, Void>() {
+ @Override
+ public void completed(Long result, Void attachment) {
+ System.out.println("Read: " + result.longValue());
+ write(buffer);
+ }
+ @Override
+ public void failed(Throwable exc, Void attachment) {
+ exc.printStackTrace();
+ }
+ }, buffer);
+ System.out.println("CompletionState: " + state);
+ if (state == CompletionState.INLINE) {
+ write(buffer);
+ }
+ }
+
+ private void write(ByteBuffer buffer) {
+ buffer.flip();
+ CompletionState state = wrapper.write(true, 10, TimeUnit.SECONDS, null, SocketWrapperBase.COMPLETE_WRITE, new CompletionHandler<Long, Void>() {
+ @Override
+ public void completed(Long result, Void attachment) {
+ System.out.println("Write: " + result.longValue());
+ }
+ @Override
+ public void failed(Throwable exc, Void attachment) {
+ exc.printStackTrace();
+ }
+ }, buffer);
+ System.out.println("CompletionState: " + state);
+ }
+
+ @Override
+ public void pause() {
+ // NO-OP
+ }
+
+ @Override
+ public void destroy() {
+ // NO-OP
+ }
+
+ @Override
+ public SocketState upgradeDispatch(SocketEvent status) {
+ System.out.println("Processing: " + status);
+ switch (status) {
+ case OPEN_READ:
+ // Note: there's always an initial read event at the moment (reading should be skipped since it ends up in the internal buffer)
+ break;
+ case OPEN_WRITE:
+ break;
+ case STOP:
+ case DISCONNECT:
+ case ERROR:
+ case TIMEOUT:
+ return SocketState.CLOSED;
+ }
+ return SocketState.UPGRADED;
+ }
+
+ @Override
+ public void setSocketWrapper(SocketWrapperBase<?> wrapper) {
+ this.wrapper = wrapper;
+ }
+
+ @Override
+ public void setSslSupport(SSLSupport sslSupport) {
+ // NO-OP
+ }
+ }
+
+}
diff --git a/test/org/apache/coyote/http2/Http2TestBase.java b/test/org/apache/coyote/http2/Http2TestBase.java
new file mode 100644
index 0000000..2b50ae7
--- /dev/null
+++ b/test/org/apache/coyote/http2/Http2TestBase.java
@@ -0,0 +1,1178 @@
+/*
+ * 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.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.Socket;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+
+import javax.net.SocketFactory;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.junit.Assert;
+
+import org.apache.catalina.Context;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.connector.Connector;
+import org.apache.catalina.startup.Tomcat;
+import org.apache.catalina.startup.TomcatBaseTest;
+import org.apache.catalina.util.IOTools;
+import org.apache.coyote.http2.HpackDecoder.HeaderEmitter;
+import org.apache.coyote.http2.Http2Parser.Input;
+import org.apache.coyote.http2.Http2Parser.Output;
+import org.apache.tomcat.util.codec.binary.Base64;
+import org.apache.tomcat.util.http.MimeHeaders;
+
+/**
+ * Tests for compliance with the <a href="https://tools.ietf.org/html/rfc7540">
+ * HTTP/2 specification</a>.
+ */
+public abstract class Http2TestBase extends TomcatBaseTest {
+
+ // Nothing special about this date apart from it being the date I ran the
+ // test that demonstrated that most HTTP/2 tests were failing because the
+ // response now included a date header
+ protected static final String DEFAULT_DATE = "Wed, 11 Nov 2015 19:18:42 GMT";
+
+ static final String DEFAULT_CONNECTION_HEADER_VALUE = "Upgrade, HTTP2-Settings";
+ private static final byte[] EMPTY_SETTINGS_FRAME =
+ { 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00 };
+ static final String EMPTY_HTTP2_SETTINGS_HEADER;
+
+ static {
+ byte[] empty = new byte[0];
+ EMPTY_HTTP2_SETTINGS_HEADER = "HTTP2-Settings: " + Base64.encodeBase64String(empty) + "\r\n";
+ }
+
+ protected static final String TRAILER_HEADER_NAME = "X-TrailerTest";
+ protected static final String TRAILER_HEADER_VALUE = "test";
+
+ private Socket s;
+ protected HpackEncoder hpackEncoder;
+ protected Input input;
+ protected TestOutput output;
+ protected Http2Parser parser;
+ protected OutputStream os;
+
+ private long pingAckDelayMillis = 0;
+
+
+ protected void setPingAckDelayMillis(long delay) {
+ pingAckDelayMillis = delay;
+ }
+
+ /**
+ * Standard setup. Creates HTTP/2 connection via HTTP upgrade and ensures
+ * that the first response is correctly received.
+ */
+ protected void http2Connect() throws Exception {
+ enableHttp2();
+ configureAndStartWebApplication();
+ openClientConnection();
+ doHttpUpgrade();
+ sendClientPreface();
+ validateHttp2InitialResponse();
+ }
+
+
+ protected void validateHttp2InitialResponse() throws Exception {
+ // - 101 response acts as acknowledgement of the HTTP2-Settings header
+ // Need to read 5 frames
+ // - settings (server settings - must be first)
+ // - settings ack (for the settings frame in the client preface)
+ // - ping
+ // - headers (for response)
+ // - data (for response body)
+ parser.readFrame(true);
+ parser.readFrame(true);
+ parser.readFrame(true);
+ parser.readFrame(true);
+ parser.readFrame(true);
+
+ Assert.assertEquals("0-Settings-[3]-[200]\n" +
+ "0-Settings-End\n" +
+ "0-Settings-Ack\n" +
+ "0-Ping-[0,0,0,0,0,0,0,1]\n" +
+ getSimpleResponseTrace(1)
+ , output.getTrace());
+ output.clearTrace();
+ }
+
+
+ protected void sendEmptyGetRequest(int streamId) throws IOException {
+ byte[] frameHeader = new byte[9];
+ ByteBuffer headersPayload = ByteBuffer.allocate(128);
+
+ buildEmptyGetRequest(frameHeader, headersPayload, null, streamId);
+ writeFrame(frameHeader, headersPayload);
+ }
+
+
+ protected void sendSimpleGetRequest(int streamId) throws IOException {
+ sendSimpleGetRequest(streamId, null);
+ }
+
+
+ protected void sendSimpleGetRequest(int streamId, byte[] padding) throws IOException {
+ byte[] frameHeader = new byte[9];
+ ByteBuffer headersPayload = ByteBuffer.allocate(128);
+
+ buildSimpleGetRequest(frameHeader, headersPayload, padding, streamId);
+ writeFrame(frameHeader, headersPayload);
+ }
+
+
+ protected void sendLargeGetRequest(int streamId) throws IOException {
+ byte[] frameHeader = new byte[9];
+ ByteBuffer headersPayload = ByteBuffer.allocate(128);
+
+ buildLargeGetRequest(frameHeader, headersPayload, streamId);
+ writeFrame(frameHeader, headersPayload);
+ }
+
+
+ protected void buildEmptyGetRequest(byte[] frameHeader, ByteBuffer headersPayload,
+ byte[] padding, int streamId) {
+ buildGetRequest(frameHeader, headersPayload, padding, streamId, "/empty");
+ }
+
+
+ protected void buildSimpleGetRequest(byte[] frameHeader, ByteBuffer headersPayload,
+ byte[] padding, int streamId) {
+ buildGetRequest(frameHeader, headersPayload, padding, streamId, "/simple");
+ }
+
+
+ protected void buildLargeGetRequest(byte[] frameHeader, ByteBuffer headersPayload, int streamId) {
+ buildGetRequest(frameHeader, headersPayload, null, streamId, "/large");
+ }
+
+
+ protected void buildGetRequest(byte[] frameHeader, ByteBuffer headersPayload, byte[] padding,
+ int streamId, String url) {
+ List<Header> headers = new ArrayList<>(3);
+ headers.add(new Header(":method", "GET"));
+ headers.add(new Header(":path", url));
+ headers.add(new Header(":authority", "localhost:" + getPort()));
+
+ buildGetRequest(frameHeader, headersPayload, padding, headers, streamId);
+ }
+
+
+ protected void buildGetRequest(byte[] frameHeader, ByteBuffer headersPayload, byte[] padding,
+ List<Header> headers, int streamId) {
+ if (padding != null) {
+ headersPayload.put((byte) (0xFF & padding.length));
+ }
+ MimeHeaders mimeHeaders = new MimeHeaders();
+ for (Header header : headers) {
+ mimeHeaders.addValue(header.getName()).setString(header.getValue());
+ }
+ hpackEncoder.encode(mimeHeaders, headersPayload);
+ if (padding != null) {
+ headersPayload.put(padding);
+ }
+ headersPayload.flip();
+
+ ByteUtil.setThreeBytes(frameHeader, 0, headersPayload.limit());
+ frameHeader[3] = FrameType.HEADERS.getIdByte();
+ // Flags. end of headers (0x04). end of stream (0x01)
+ frameHeader[4] = 0x05;
+ if (padding != null) {
+ frameHeader[4] += 0x08;
+ }
+ // Stream id
+ ByteUtil.set31Bits(frameHeader, 5, streamId);
+ }
+
+
+ protected void buildSimpleGetRequestPart1(byte[] frameHeader, ByteBuffer headersPayload,
+ int streamId) {
+ List<Header> headers = new ArrayList<>(3);
+ headers.add(new Header(":method", "GET"));
+ headers.add(new Header(":path", "/simple"));
+
+ buildSimpleGetRequestPart1(frameHeader, headersPayload, headers, streamId);
+ }
+
+
+ protected void buildSimpleGetRequestPart1(byte[] frameHeader, ByteBuffer headersPayload,
+ List<Header> headers, int streamId) {
+ MimeHeaders mimeHeaders = new MimeHeaders();
+ for (Header header : headers) {
+ mimeHeaders.addValue(header.getName()).setString(header.getValue());
+ }
+ hpackEncoder.encode(mimeHeaders, headersPayload);
+
+ headersPayload.flip();
+
+ ByteUtil.setThreeBytes(frameHeader, 0, headersPayload.limit());
+ frameHeader[3] = FrameType.HEADERS.getIdByte();
+ // Flags. end of stream (0x01)
+ frameHeader[4] = 0x01;
+ // Stream id
+ ByteUtil.set31Bits(frameHeader, 5, streamId);
+ }
+
+
+ protected void buildSimpleGetRequestPart2(byte[] frameHeader, ByteBuffer headersPayload,
+ int streamId) {
+ List<Header> headers = new ArrayList<>(3);
+ headers.add(new Header(":authority", "localhost:" + getPort()));
+
+ buildSimpleGetRequestPart2(frameHeader, headersPayload, headers, streamId);
+ }
+
+
+ protected void buildSimpleGetRequestPart2(byte[] frameHeader, ByteBuffer headersPayload,
+ List<Header> headers, int streamId) {
+ MimeHeaders mimeHeaders = new MimeHeaders();
+ for (Header header : headers) {
+ mimeHeaders.addValue(header.getName()).setString(header.getValue());
+ }
+ hpackEncoder.encode(mimeHeaders, headersPayload);
+
+ headersPayload.flip();
+
+ ByteUtil.setThreeBytes(frameHeader, 0, headersPayload.limit());
+ frameHeader[3] = FrameType.CONTINUATION.getIdByte();
+ // Flags. end of headers (0x04)
+ frameHeader[4] = 0x04;
+ // Stream id
+ ByteUtil.set31Bits(frameHeader, 5, streamId);
+ }
+
+
+ protected void sendSimplePostRequest(int streamId, byte[] padding) throws IOException {
+ sendSimplePostRequest(streamId, padding, true);
+ }
+
+
+ protected void sendSimplePostRequest(int streamId, byte[] padding, boolean writeBody)
+ throws IOException {
+ sendSimplePostRequest(streamId, padding, writeBody, false);
+ }
+
+ protected void sendSimplePostRequest(int streamId, byte[] padding, boolean writeBody,
+ boolean useExpectation) throws IOException {
+ byte[] headersFrameHeader = new byte[9];
+ ByteBuffer headersPayload = ByteBuffer.allocate(128);
+ byte[] dataFrameHeader = new byte[9];
+ ByteBuffer dataPayload = ByteBuffer.allocate(128);
+
+ buildPostRequest(headersFrameHeader, headersPayload, useExpectation,
+ dataFrameHeader, dataPayload, padding, streamId);
+ writeFrame(headersFrameHeader, headersPayload);
+ if (writeBody) {
+ writeFrame(dataFrameHeader, dataPayload);
+ }
+ }
+
+
+ protected void buildPostRequest(byte[] headersFrameHeader, ByteBuffer headersPayload,
+ boolean useExpectation, byte[] dataFrameHeader, ByteBuffer dataPayload, byte[] padding,
+ int streamId) {
+ buildPostRequest(headersFrameHeader, headersPayload, useExpectation, dataFrameHeader,
+ dataPayload, padding, null, null, streamId);
+ }
+
+ protected void buildPostRequest(byte[] headersFrameHeader, ByteBuffer headersPayload,
+ boolean useExpectation, byte[] dataFrameHeader, ByteBuffer dataPayload, byte[] padding,
+ byte[] trailersFrameHeader, ByteBuffer trailersPayload, int streamId) {
+ MimeHeaders headers = new MimeHeaders();
+ headers.addValue(":method").setString("POST");
+ headers.addValue(":path").setString("/simple");
+ headers.addValue(":authority").setString("localhost:" + getPort());
+ if (useExpectation) {
+ headers.addValue("expect").setString("100-continue");
+ }
+ hpackEncoder.encode(headers, headersPayload);
+
+ headersPayload.flip();
+
+ ByteUtil.setThreeBytes(headersFrameHeader, 0, headersPayload.limit());
+ headersFrameHeader[3] = FrameType.HEADERS.getIdByte();
+ // Flags. end of headers (0x04)
+ headersFrameHeader[4] = 0x04;
+ // Stream id
+ ByteUtil.set31Bits(headersFrameHeader, 5, streamId);
+
+ // Data
+ if (padding != null) {
+ dataPayload.put((byte) (padding.length & 0xFF));
+ dataPayload.limit(dataPayload.capacity() - padding.length);
+ }
+
+ while (dataPayload.hasRemaining()) {
+ dataPayload.put((byte) 'x');
+ }
+ if (padding != null && padding.length > 0) {
+ dataPayload.limit(dataPayload.capacity());
+ dataPayload.put(padding);
+ }
+
+ dataPayload.flip();
+
+ // Size
+ ByteUtil.setThreeBytes(dataFrameHeader, 0, dataPayload.limit());
+ // Data is type 0
+ // Flags: End of stream 1, Padding 8
+ if (trailersPayload == null) {
+ dataFrameHeader[4] = 0x01;
+ } else {
+ dataFrameHeader[4] = 0x00;
+ }
+ if (padding != null) {
+ dataFrameHeader[4] += 0x08;
+ }
+ ByteUtil.set31Bits(dataFrameHeader, 5, streamId);
+
+ // Trailers
+ if (trailersPayload != null) {
+ MimeHeaders trailerHeaders = new MimeHeaders();
+ trailerHeaders.addValue(TRAILER_HEADER_NAME).setString(TRAILER_HEADER_VALUE);
+ hpackEncoder.encode(trailerHeaders, trailersPayload);
+
+ trailersPayload.flip();
+
+ ByteUtil.setThreeBytes(trailersFrameHeader, 0, trailersPayload.limit());
+ trailersFrameHeader[3] = FrameType.HEADERS.getIdByte();
+ // Flags. end of headers (0x04) and end of stream (0x01)
+ trailersFrameHeader[4] = 0x05;
+ // Stream id
+ ByteUtil.set31Bits(trailersFrameHeader, 5, streamId);
+ }
+ }
+
+
+ protected void writeFrame(byte[] header, ByteBuffer payload)
+ throws IOException {
+ writeFrame(header, payload, 0, payload.limit());
+ }
+
+
+ protected void writeFrame(byte[] header, ByteBuffer payload, int offset, int len)
+ throws IOException {
+ writeFrame(header, payload, offset, len, 0);
+ }
+
+
+ protected void writeFrame(byte[] header, ByteBuffer payload, int offset, int len, int delayms)
+ throws IOException {
+ os.write(header);
+ os.write(payload.array(), payload.arrayOffset() + offset, len);
+ os.flush();
+ if (delayms > 0) {
+ try {
+ Thread.sleep(delayms);
+ } catch (InterruptedException e) {
+ // Ignore
+ }
+ }
+ }
+
+
+ protected void readSimpleGetResponse() throws Http2Exception, IOException {
+ // Headers
+ parser.readFrame(true);
+ // Body
+ parser.readFrame(true);
+ }
+
+
+ protected void readSimplePostResponse(boolean padding) throws Http2Exception, IOException {
+ if (padding) {
+ // Window updates for padding
+ parser.readFrame(true);
+ parser.readFrame(true);
+ }
+ // Connection window update after reading request body
+ parser.readFrame(true);
+ // Stream window update after reading request body
+ parser.readFrame(true);
+ // Headers
+ parser.readFrame(true);
+ // Body
+ parser.readFrame(true);
+ }
+
+
+ protected String getEmptyResponseTrace(int streamId) {
+ return getResponseBodyFrameTrace(streamId, "0");
+ }
+
+
+ protected String getSimpleResponseTrace(int streamId) {
+ return getResponseBodyFrameTrace(streamId, "8192");
+ }
+
+
+ protected String getCookieResponseTrace(int streamId, int cookieCount) {
+ return getResponseBodyFrameTrace(streamId, "text/plain;charset=UTF-8",
+ "Cookie count: " + cookieCount);
+ }
+
+
+ private String getResponseBodyFrameTrace(int streamId, String body) {
+ return getResponseBodyFrameTrace(streamId, "application/octet-stream", body);
+ }
+
+ private String getResponseBodyFrameTrace(int streamId, String contentType, String body) {
+ StringBuilder result = new StringBuilder();
+ result.append(streamId);
+ result.append("-HeadersStart\n");
+ result.append(streamId);
+ result.append("-Header-[:status]-[200]\n");
+ result.append(streamId);
+ result.append("-Header-[content-type]-[");
+ result.append(contentType);
+ result.append("]\n");
+ result.append(streamId);
+ result.append("-Header-[date]-[");
+ result.append(DEFAULT_DATE);
+ result.append("]\n");
+ result.append(streamId);
+ result.append("-HeadersEnd\n");
+ result.append(streamId);
+ result.append("-Body-");
+ result.append(body);
+ result.append("\n");
+ result.append(streamId);
+ result.append("-EndOfStream\n");
+
+ return result.toString();
+ }
+
+
+ protected void enableHttp2() {
+ enableHttp2(200);
+ }
+
+ protected void enableHttp2(long maxConcurrentStreams) {
+ Connector connector = getTomcatInstance().getConnector();
+ Http2Protocol http2Protocol = new Http2Protocol();
+ // Short timeouts for now. May need to increase these for CI systems.
+ http2Protocol.setReadTimeout(2000);
+ http2Protocol.setKeepAliveTimeout(5000);
+ http2Protocol.setWriteTimeout(2000);
+ http2Protocol.setMaxConcurrentStreams(maxConcurrentStreams);
+ connector.addUpgradeProtocol(http2Protocol);
+ }
+
+
+ protected void configureAndStartWebApplication() throws LifecycleException {
+ Tomcat tomcat = getTomcatInstance();
+
+ Context ctxt = tomcat.addContext("", null);
+ Tomcat.addServlet(ctxt, "empty", new EmptyServlet());
+ ctxt.addServletMappingDecoded("/empty", "empty");
+ Tomcat.addServlet(ctxt, "simple", new SimpleServlet());
+ ctxt.addServletMappingDecoded("/simple", "simple");
+ Tomcat.addServlet(ctxt, "large", new LargeServlet());
+ ctxt.addServletMappingDecoded("/large", "large");
+ Tomcat.addServlet(ctxt, "cookie", new CookieServlet());
+ ctxt.addServletMappingDecoded("/cookie", "cookie");
+
+ tomcat.start();
+ }
+
+
+ protected void openClientConnection() throws IOException {
+ // Open a connection
+ s = SocketFactory.getDefault().createSocket("localhost", getPort());
+ s.setSoTimeout(30000);
+
+ os = s.getOutputStream();
+ InputStream is = s.getInputStream();
+
+ input = new TestInput(is);
+ output = new TestOutput();
+ parser = new Http2Parser("-1", input, output);
+ hpackEncoder = new HpackEncoder(ConnectionSettingsBase.DEFAULT_HEADER_TABLE_SIZE);
+ }
+
+
+ protected void doHttpUpgrade() throws IOException {
+ doHttpUpgrade(DEFAULT_CONNECTION_HEADER_VALUE, "h2c", EMPTY_HTTP2_SETTINGS_HEADER, true);
+ }
+
+ protected void doHttpUpgrade(String connection, String upgrade, String settings,
+ boolean validate) throws IOException {
+ byte[] upgradeRequest = ("GET /simple HTTP/1.1\r\n" +
+ "Host: localhost:" + getPort() + "\r\n" +
+ "Connection: "+ connection + "\r\n" +
+ "Upgrade: " + upgrade + "\r\n" +
+ settings +
+ "\r\n").getBytes(StandardCharsets.ISO_8859_1);
+ os.write(upgradeRequest);
+ os.flush();
+
+ if (validate) {
+ Assert.assertTrue("Failed to read HTTP Upgrade response",
+ readHttpUpgradeResponse());
+ }
+ }
+
+
+ boolean readHttpUpgradeResponse() throws IOException {
+ String[] responseHeaders = readHttpResponseHeaders();
+
+ if (responseHeaders.length < 3) {
+ return false;
+ }
+ if (!responseHeaders[0].startsWith("HTTP/1.1 101")) {
+ return false;
+ }
+
+ if (!validateHeader(responseHeaders, "Connection: Upgrade")) {
+ return false;
+ }
+ if (!validateHeader(responseHeaders, "Upgrade: h2c")) {
+ return false;
+ }
+
+ return true;
+ }
+
+
+ private boolean validateHeader(String[] responseHeaders, String header) {
+ boolean found = false;
+ for (String responseHeader : responseHeaders) {
+ if (responseHeader.equalsIgnoreCase(header)) {
+ found = true;
+ break;
+ }
+ }
+ return found;
+ }
+
+
+ String[] readHttpResponseHeaders() throws IOException {
+ // Only used by test code so safe to keep this just a little larger than
+ // we are expecting.
+ ByteBuffer data = ByteBuffer.allocate(256);
+ byte[] singleByte = new byte[1];
+ // Looking for \r\n\r\n
+ int seen = 0;
+ while (seen < 4) {
+ input.fill(true, singleByte);
+ switch (seen) {
+ case 0:
+ case 2: {
+ if (singleByte[0] == '\r') {
+ seen++;
+ } else {
+ seen = 0;
+ }
+ break;
+ }
+ case 1:
+ case 3: {
+ if (singleByte[0] == '\n') {
+ seen++;
+ } else {
+ seen = 0;
+ }
+ break;
+ }
+ }
+ data.put(singleByte[0]);
+ }
+
+ if (seen != 4) {
+ throw new IOException("End of headers not found");
+ }
+
+ String response = new String(data.array(), data.arrayOffset(),
+ data.arrayOffset() + data.position(), StandardCharsets.ISO_8859_1);
+
+ return response.split("\r\n");
+ }
+
+
+ void parseHttp11Response() throws IOException {
+ String[] responseHeaders = readHttpResponseHeaders();
+ Assert.assertTrue(responseHeaders[0], responseHeaders[0].startsWith("HTTP/1.1 200"));
+
+ // Find the content length (chunked responses not handled)
+ for (int i = 1; i < responseHeaders.length; i++) {
+ if (responseHeaders[i].toLowerCase(Locale.ENGLISH).startsWith("content-length")) {
+ String cl = responseHeaders[i];
+ int pos = cl.indexOf(':');
+ if (pos == -1) {
+ throw new IOException("Invalid: [" + cl + "]");
+ }
+ int len = Integer.parseInt(cl.substring(pos + 1).trim());
+ byte[] content = new byte[len];
+ input.fill(true, content);
+ return;
+ }
+ }
+ Assert.fail("No content-length in response");
+ }
+
+
+ void sendClientPreface() throws IOException {
+ os.write(Http2Parser.CLIENT_PREFACE_START);
+ os.write(EMPTY_SETTINGS_FRAME);
+ os.flush();
+ }
+
+
+ void sendRst(int streamId, long errorCode) throws IOException {
+ byte[] rstFrame = new byte[13];
+ // length is always 4
+ rstFrame[2] = 0x04;
+ rstFrame[3] = FrameType.RST.getIdByte();
+ // no flags
+ // Stream ID
+ ByteUtil.set31Bits(rstFrame, 5, streamId);
+ // Payload
+ ByteUtil.setFourBytes(rstFrame, 9, errorCode);
+
+ os.write(rstFrame);
+ os.flush();
+ }
+
+
+ void sendPing() throws IOException {
+ sendPing(0, false, new byte[8]);
+ }
+
+
+ void sendPing(int streamId, boolean ack, byte[] payload) throws IOException {
+ if (ack && pingAckDelayMillis > 0) {
+ try {
+ Thread.sleep(pingAckDelayMillis);
+ } catch (InterruptedException e) {
+ // Ignore
+ }
+ }
+ byte[] pingHeader = new byte[9];
+ // length
+ ByteUtil.setThreeBytes(pingHeader, 0, payload.length);
+ // Type
+ pingHeader[3] = FrameType.PING.getIdByte();
+ // Flags
+ if (ack) {
+ ByteUtil.setOneBytes(pingHeader, 4, 0x01);
+ }
+ // Stream
+ ByteUtil.set31Bits(pingHeader, 5, streamId);
+ os.write(pingHeader);
+ os.write(payload);
+ os.flush();
+ }
+
+
+ void sendGoaway(int streamId, int lastStreamId, long errorCode, byte[] debug)
+ throws IOException {
+ byte[] goawayFrame = new byte[17];
+ int len = 8;
+ if (debug != null) {
+ len += debug.length;
+ }
+ ByteUtil.setThreeBytes(goawayFrame, 0, len);
+ // Type
+ goawayFrame[3] = FrameType.GOAWAY.getIdByte();
+ // No flags
+ // Stream
+ ByteUtil.set31Bits(goawayFrame, 5, streamId);
+ // Last stream
+ ByteUtil.set31Bits(goawayFrame, 9, lastStreamId);
+ ByteUtil.setFourBytes(goawayFrame, 13, errorCode);
+ os.write(goawayFrame);
+ if (debug != null && debug.length > 0) {
+ os.write(debug);
+ }
+ os.flush();
+ }
+
+
+ void sendWindowUpdate(int streamId, int increment) throws IOException {
+ byte[] updateFrame = new byte[13];
+ // length is always 4
+ updateFrame[2] = 0x04;
+ updateFrame[3] = FrameType.WINDOW_UPDATE.getIdByte();
+ // no flags
+ // Stream ID
+ ByteUtil.set31Bits(updateFrame, 5, streamId);
+ // Payload
+ ByteUtil.set31Bits(updateFrame, 9, increment);
+
+ os.write(updateFrame);
+ os.flush();
+ }
+
+
+ void sendData(int streamId, byte[] payload) throws IOException {
+ byte[] header = new byte[9];
+ // length
+ ByteUtil.setThreeBytes(header, 0, payload.length);
+ // Type is zero
+ // No flags
+ // Stream ID
+ ByteUtil.set31Bits(header, 5, streamId);
+
+ os.write(header);
+ os.write(payload);
+ os.flush();
+ }
+
+
+ void sendPriority(int streamId, int streamDependencyId, int weight) throws IOException {
+ byte[] priorityFrame = new byte[14];
+ // length
+ ByteUtil.setThreeBytes(priorityFrame, 0, 5);
+ // type
+ priorityFrame[3] = FrameType.PRIORITY.getIdByte();
+ // No flags
+ // Stream ID
+ ByteUtil.set31Bits(priorityFrame, 5, streamId);
+
+ // Payload
+ ByteUtil.set31Bits(priorityFrame, 9, streamDependencyId);
+ ByteUtil.setOneBytes(priorityFrame, 13, weight);
+
+ os.write(priorityFrame);
+ os.flush();
+ }
+
+
+ void sendSettings(int streamId, boolean ack, SettingValue... settings) throws IOException {
+ // length
+ int settingsCount;
+ if (settings == null) {
+ settingsCount = 0;
+ } else {
+ settingsCount = settings.length;
+ }
+
+ byte[] settingFrame = new byte[9 + 6 * settingsCount];
+
+ ByteUtil.setThreeBytes(settingFrame, 0, 6 * settingsCount);
+ // type
+ settingFrame[3] = FrameType.SETTINGS.getIdByte();
+
+ if (ack) {
+ settingFrame[4] = 0x01;
+ }
+
+ // Stream
+ ByteUtil.set31Bits(settingFrame, 5, streamId);
+
+ // Payload
+ for (int i = 0; i < settingsCount; i++) {
+ // Stops IDE complaining about possible NPE
+ Assert.assertNotNull(settings);
+ ByteUtil.setTwoBytes(settingFrame, (i * 6) + 9, settings[i].getSetting());
+ ByteUtil.setFourBytes(settingFrame, (i * 6) + 11, settings[i].getValue());
+ }
+
+ os.write(settingFrame);
+ os.flush();
+ }
+
+
+ private static class TestInput implements Http2Parser.Input {
+
+ private final InputStream is;
+
+
+ public TestInput(InputStream is) {
+ this.is = is;
+ }
+
+
+ @Override
+ public boolean fill(boolean block, byte[] data) throws IOException {
+ return fill(block, data, 0, data.length); }
+
+
+ @Override
+ public boolean fill(boolean block, ByteBuffer data, int len) throws IOException {
+ boolean result = fill(block, data.array(), data.arrayOffset() + data.position(), len);
+ if (result) {
+ data.position(data.position() + len);
+ }
+ return result;
+ }
+
+
+ @Override
+ public boolean fill(boolean block, byte[] data, int offset, int length) throws IOException {
+ // Note: Block is ignored for this test class. Reads always block.
+ int off = offset;
+ int len = length;
+ while (len > 0) {
+ int read = is.read(data, off, len);
+ if (read == -1) {
+ throw new IOException("End of input stream");
+ }
+ off += read;
+ len -= read;
+ }
+ return true;
+ }
+
+
+ @Override
+ public int getMaxFrameSize() {
+ // Hard-coded to use the default
+ return ConnectionSettingsBase.DEFAULT_MAX_FRAME_SIZE;
+ }
+ }
+
+
+ class TestOutput implements Output, HeaderEmitter {
+
+ private StringBuffer trace = new StringBuffer();
+ private String lastStreamId = "0";
+ private ConnectionSettingsRemote remoteSettings = new ConnectionSettingsRemote("-1");
+ private boolean traceBody = false;
+ private ByteBuffer bodyBuffer = null;
+
+ public void setTraceBody(boolean traceBody) {
+ this.traceBody = traceBody;
+ }
+
+
+ @Override
+ public HpackDecoder getHpackDecoder() {
+ return new HpackDecoder(remoteSettings.getHeaderTableSize());
+ }
+
+
+ @Override
+ public ByteBuffer startRequestBodyFrame(int streamId, int payloadSize) {
+ lastStreamId = Integer.toString(streamId);
+ if (traceBody) {
+ bodyBuffer = ByteBuffer.allocate(payloadSize);
+ return bodyBuffer;
+ } else {
+ trace.append(lastStreamId + "-Body-" + payloadSize + "\n");
+ return null;
+ }
+ }
+
+
+ @Override
+ public void endRequestBodyFrame(int streamId) throws Http2Exception {
+ if (bodyBuffer != null) {
+ if (bodyBuffer.limit() > 0) {
+ trace.append(lastStreamId + "-Body-");
+ bodyBuffer.flip();
+ while (bodyBuffer.hasRemaining()) {
+ trace.append((char) bodyBuffer.get());
+ }
+ trace.append("\n");
+ bodyBuffer = null;
+ }
+ }
+ }
+
+
+ @Override
+ public void receivedEndOfStream(int streamId) {
+ lastStreamId = Integer.toString(streamId);
+ trace.append(lastStreamId + "-EndOfStream\n");
+ }
+
+
+ @Override
+ public HeaderEmitter headersStart(int streamId, boolean headersEndStream) {
+ lastStreamId = Integer.toString(streamId);
+ trace.append(lastStreamId + "-HeadersStart\n");
+ return this;
+ }
+
+ @Override
+ public void reprioritise(int streamId, int parentStreamId, boolean exclusive, int weight) {
+ lastStreamId = Integer.toString(streamId);
+ trace.append(lastStreamId + "-Reprioritise-[" + parentStreamId + "]-[" + exclusive +
+ "]-[" + weight + "]\n");
+ }
+
+
+ @Override
+ public void emitHeader(String name, String value) {
+ // Date headers will always change so use a hard-coded default
+ if ("date".equals(name)) {
+ value = DEFAULT_DATE;
+ }
+ trace.append(lastStreamId + "-Header-[" + name + "]-[" + value + "]\n");
+ }
+
+
+ @Override
+ public void validateHeaders() {
+ // NO-OP: Accept anything the server sends for the unit tests
+ }
+
+
+ @Override
+ public void headersEnd(int streamId) {
+ trace.append(streamId + "-HeadersEnd\n");
+ }
+
+
+ @Override
+ public void reset(int streamId, long errorCode) {
+ trace.append(streamId + "-RST-[" + errorCode + "]\n");
+ }
+
+
+ @Override
+ public void setting(Setting setting, long value) throws ConnectionException {
+ trace.append("0-Settings-[" + setting + "]-[" + value + "]\n");
+ remoteSettings.set(setting, value);
+ }
+
+
+ @Override
+ public void settingsEnd(boolean ack) throws IOException {
+ if (ack) {
+ trace.append("0-Settings-Ack\n");
+ } else {
+ trace.append("0-Settings-End\n");
+ sendSettings(0, true);
+ }
+ }
+
+
+ @Override
+ public void pingReceive(byte[] payload, boolean ack) throws IOException {
+ trace.append("0-Ping-");
+ if (ack) {
+ trace.append("Ack-");
+ } else {
+ sendPing(0, true, payload);
+ }
+ trace.append('[');
+ boolean first = true;
+ for (byte b : payload) {
+ if (first) {
+ first = false;
+ } else {
+ trace.append(',');
+ }
+ trace.append(b & 0xFF);
+ }
+ trace.append("]\n");
+ }
+
+
+ @Override
+ public void goaway(int lastStreamId, long errorCode, String debugData) {
+ trace.append("0-Goaway-[" + lastStreamId + "]-[" + errorCode + "]-[" + debugData + "]");
+ }
+
+
+ @Override
+ public void incrementWindowSize(int streamId, int increment) {
+ trace.append(streamId + "-WindowSize-[" + increment + "]\n");
+ }
+
+
+ @Override
+ public void swallowed(int streamId, FrameType frameType, int flags, int size) {
+ trace.append(streamId);
+ trace.append(",");
+ trace.append(frameType);
+ trace.append(",");
+ trace.append(flags);
+ trace.append(",");
+ trace.append(size);
+ trace.append("\n");
+ }
+
+
+ @Override
+ public void swallowedPadding(int streamId, int paddingLength) {
+ trace.append(streamId);
+ trace.append("-SwallowedPadding-[");
+ trace.append(paddingLength);
+ trace.append("]\n");
+ }
+
+
+ public void clearTrace() {
+ trace = new StringBuffer();
+ }
+
+
+ public String getTrace() {
+ return trace.toString();
+ }
+
+
+ public int getMaxFrameSize() {
+ return remoteSettings.getMaxFrameSize();
+ }
+ }
+
+
+ private static class EmptyServlet extends HttpServlet {
+
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ protected void doGet(HttpServletRequest req, HttpServletResponse resp)
+ throws ServletException, IOException {
+ // Generate an empty response
+ resp.setContentType("application/octet-stream");
+ resp.setContentLength(0);
+ resp.flushBuffer();
+ }
+ }
+
+
+ private static class SimpleServlet extends HttpServlet {
+
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ protected void doGet(HttpServletRequest req, HttpServletResponse resp)
+ throws ServletException, IOException {
+ // Generate content with a simple known format.
+ resp.setContentType("application/octet-stream");
+
+ int count = 4 * 1024;
+ // Two bytes per entry
+ resp.setContentLengthLong(count * 2);
+
+ OutputStream os = resp.getOutputStream();
+ byte[] data = new byte[2];
+ for (int i = 0; i < count; i++) {
+ data[0] = (byte) (i & 0xFF);
+ data[1] = (byte) ((i >> 8) & 0xFF);
+ os.write(data);
+ }
+ }
+
+
+ @Override
+ protected void doPost(HttpServletRequest req, HttpServletResponse resp)
+ throws ServletException, IOException {
+ // Do not do this at home. The unconstrained buffer is a DoS risk.
+
+ // Have to read into a buffer because clients typically do not start
+ // to read the response until the request is fully written.
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ IOTools.flow(req.getInputStream(), baos);
+
+ ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
+ IOTools.flow(bais, resp.getOutputStream());
+
+ // Check for trailer headers
+ String trailerValue = req.getHeader(TRAILER_HEADER_NAME);
+ if (trailerValue != null) {
+ resp.getOutputStream().write(trailerValue.getBytes(StandardCharsets.UTF_8));
+ }
+ }
+ }
+
+
+ private static class LargeServlet extends HttpServlet {
+
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ protected void doGet(HttpServletRequest req, HttpServletResponse resp)
+ throws ServletException, IOException {
+ // Generate content with a simple known format that will exceed the
+ // default flow control window for a stream.
+ resp.setContentType("application/octet-stream");
+
+ int count = 128 * 1024;
+ // Two bytes per entry
+ resp.setContentLengthLong(count * 2);
+
+ OutputStream os = resp.getOutputStream();
+ byte[] data = new byte[2];
+ for (int i = 0; i < count; i++) {
+ data[0] = (byte) (i & 0xFF);
+ data[1] = (byte) ((i >> 8) & 0xFF);
+ os.write(data);
+ }
+ }
+ }
+
+
+ private static class CookieServlet 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("Cookie count: " + req.getCookies().length);
+ resp.flushBuffer();
+ }
+ }
+
+
+ static class SettingValue {
+ private final int setting;
+ private final long value;
+
+ public SettingValue(int setting, long value) {
+ this.setting = setting;
+ this.value = value;
+ }
+
+ public int getSetting() {
+ return setting;
+ }
+
+ public long getValue() {
+ return value;
+ }
+ }
+
+
+ static class Header {
+ private final String name;
+ private final String value;
+
+ public Header(String name, String value) {
+ this.name = name;
+ this.value = value;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getValue() {
+ return value;
+ }
+ }
+}
diff --git a/test/org/apache/coyote/http2/TestAbstractStream.java b/test/org/apache/coyote/http2/TestAbstractStream.java
new file mode 100644
index 0000000..ca242ed
--- /dev/null
+++ b/test/org/apache/coyote/http2/TestAbstractStream.java
@@ -0,0 +1,249 @@
+/*
+ * 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;
+
+/*
+ * This tests use A=1, B=2, etc to map stream IDs to the names used in the
+ * figures.
+ */
+public class TestAbstractStream {
+
+ @Test
+ public void testDependenciesFig3() {
+ // Setup
+ Http2UpgradeHandler handler = new Http2UpgradeHandler(null, null);
+ Stream a = new Stream(Integer.valueOf(1), handler);
+ Stream b = new Stream(Integer.valueOf(2), handler);
+ Stream c = new Stream(Integer.valueOf(3), handler);
+ Stream d = new Stream(Integer.valueOf(4), handler);
+ b.rePrioritise(a, false, 16);
+ c.rePrioritise(a, false, 16);
+
+ // Action
+ d.rePrioritise(a, false, 16);
+
+ // Check parents
+ Assert.assertEquals(handler, a.getParentStream());
+ Assert.assertEquals(a, b.getParentStream());
+ Assert.assertEquals(a, c.getParentStream());
+ Assert.assertEquals(a, d.getParentStream());
+
+ // Check children
+ Assert.assertEquals(3, a.getChildStreams().size());
+ Assert.assertTrue(a.getChildStreams().contains(b));
+ Assert.assertTrue(a.getChildStreams().contains(c));
+ Assert.assertTrue(a.getChildStreams().contains(d));
+ Assert.assertEquals(0, b.getChildStreams().size());
+ Assert.assertEquals(0, c.getChildStreams().size());
+ Assert.assertEquals(0, d.getChildStreams().size());
+ }
+
+
+ @Test
+ public void testDependenciesFig4() {
+ // Setup
+ Http2UpgradeHandler handler = new Http2UpgradeHandler(null, null);
+ Stream a = new Stream(Integer.valueOf(1), handler);
+ Stream b = new Stream(Integer.valueOf(2), handler);
+ Stream c = new Stream(Integer.valueOf(3), handler);
+ Stream d = new Stream(Integer.valueOf(4), handler);
+ b.rePrioritise(a, false, 16);
+ c.rePrioritise(a, false, 16);
+
+ // Action
+ d.rePrioritise(a, true, 16);
+
+ // Check parents
+ Assert.assertEquals(handler, a.getParentStream());
+ Assert.assertEquals(d, b.getParentStream());
+ Assert.assertEquals(d, c.getParentStream());
+ Assert.assertEquals(a, d.getParentStream());
+
+ // Check children
+ Assert.assertEquals(1, a.getChildStreams().size());
+ Assert.assertTrue(a.getChildStreams().contains(d));
+ Assert.assertEquals(2, d.getChildStreams().size());
+ Assert.assertTrue(d.getChildStreams().contains(b));
+ Assert.assertTrue(d.getChildStreams().contains(c));
+ Assert.assertEquals(0, b.getChildStreams().size());
+ Assert.assertEquals(0, c.getChildStreams().size());
+ }
+
+
+ @Test
+ public void testDependenciesFig5NonExclusive() {
+ // Setup
+ Http2UpgradeHandler handler = new Http2UpgradeHandler(null, null);
+ Stream a = new Stream(Integer.valueOf(1), handler);
+ Stream b = new Stream(Integer.valueOf(2), handler);
+ Stream c = new Stream(Integer.valueOf(3), handler);
+ Stream d = new Stream(Integer.valueOf(4), handler);
+ Stream e = new Stream(Integer.valueOf(5), handler);
+ Stream f = new Stream(Integer.valueOf(6), handler);
+ b.rePrioritise(a, false, 16);
+ c.rePrioritise(a, false, 16);
+ d.rePrioritise(c, false, 16);
+ e.rePrioritise(c, false, 16);
+ f.rePrioritise(d, false, 16);
+
+ // Action
+ a.rePrioritise(d, false, 16);
+
+ // Check parents
+ Assert.assertEquals(handler, d.getParentStream());
+ Assert.assertEquals(d, f.getParentStream());
+ Assert.assertEquals(d, a.getParentStream());
+ Assert.assertEquals(a, b.getParentStream());
+ Assert.assertEquals(a, c.getParentStream());
+ Assert.assertEquals(c, e.getParentStream());
+
+ // Check children
+ Assert.assertEquals(2, d.getChildStreams().size());
+ Assert.assertTrue(d.getChildStreams().contains(a));
+ Assert.assertTrue(d.getChildStreams().contains(f));
+ Assert.assertEquals(0, f.getChildStreams().size());
+ Assert.assertEquals(2, a.getChildStreams().size());
+ Assert.assertTrue(a.getChildStreams().contains(b));
+ Assert.assertTrue(a.getChildStreams().contains(c));
+ Assert.assertEquals(0, b.getChildStreams().size());
+ Assert.assertEquals(1, c.getChildStreams().size());
+ Assert.assertTrue(c.getChildStreams().contains(e));
+ Assert.assertEquals(0, e.getChildStreams().size());
+ }
+
+
+ @Test
+ public void testDependenciesFig5Exclusive() {
+ // Setup
+ Http2UpgradeHandler handler = new Http2UpgradeHandler(null, null);
+ Stream a = new Stream(Integer.valueOf(1), handler);
+ Stream b = new Stream(Integer.valueOf(2), handler);
+ Stream c = new Stream(Integer.valueOf(3), handler);
+ Stream d = new Stream(Integer.valueOf(4), handler);
+ Stream e = new Stream(Integer.valueOf(5), handler);
+ Stream f = new Stream(Integer.valueOf(6), handler);
+ b.rePrioritise(a, false, 16);
+ c.rePrioritise(a, false, 16);
+ d.rePrioritise(c, false, 16);
+ e.rePrioritise(c, false, 16);
+ f.rePrioritise(d, false, 16);
+
+ // Action
+ a.rePrioritise(d, true, 16);
+
+ // Check parents
+ Assert.assertEquals(handler, d.getParentStream());
+ Assert.assertEquals(d, a.getParentStream());
+ Assert.assertEquals(a, b.getParentStream());
+ Assert.assertEquals(a, c.getParentStream());
+ Assert.assertEquals(a, f.getParentStream());
+ Assert.assertEquals(c, e.getParentStream());
+
+ // Check children
+ Assert.assertEquals(1, d.getChildStreams().size());
+ Assert.assertTrue(d.getChildStreams().contains(a));
+ Assert.assertEquals(3, a.getChildStreams().size());
+ Assert.assertTrue(a.getChildStreams().contains(b));
+ Assert.assertTrue(a.getChildStreams().contains(c));
+ Assert.assertTrue(a.getChildStreams().contains(f));
+ Assert.assertEquals(0, b.getChildStreams().size());
+ Assert.assertEquals(0, f.getChildStreams().size());
+ Assert.assertEquals(1, c.getChildStreams().size());
+ Assert.assertTrue(c.getChildStreams().contains(e));
+ Assert.assertEquals(0, e.getChildStreams().size());
+ }
+
+
+ @Test
+ public void testCircular01() {
+ // Setup
+ Http2UpgradeHandler handler = new Http2UpgradeHandler(null, null);
+ Stream a = new Stream(Integer.valueOf(1), handler);
+ Stream b = new Stream(Integer.valueOf(2), handler);
+ Stream c = new Stream(Integer.valueOf(3), handler);
+
+ b.rePrioritise(a, false, 16);
+ c.rePrioritise(b, false, 16);
+
+ // Action
+ a.rePrioritise(c, false, 16);
+
+ // Check parents
+ Assert.assertEquals(c, a.getParentStream());
+ Assert.assertEquals(a, b.getParentStream());
+ Assert.assertEquals(handler, c.getParentStream());
+
+ // Check children
+ Assert.assertEquals(1, handler.getChildStreams().size());
+ Assert.assertTrue(handler.getChildStreams().contains(c));
+ Assert.assertEquals(1, a.getChildStreams().size());
+ Assert.assertTrue(a.getChildStreams().contains(b));
+ Assert.assertEquals(0, b.getChildStreams().size());
+ Assert.assertEquals(1, c.getChildStreams().size());
+ Assert.assertTrue(c.getChildStreams().contains(a));
+ }
+
+
+ @Test
+ public void testCircular02() {
+ // Setup
+ Http2UpgradeHandler handler = new Http2UpgradeHandler(null, null);
+ Stream a = new Stream(Integer.valueOf(1), handler);
+ Stream b = new Stream(Integer.valueOf(2), handler);
+ Stream c = new Stream(Integer.valueOf(3), handler);
+ Stream d = new Stream(Integer.valueOf(4), handler);
+ Stream e = new Stream(Integer.valueOf(5), handler);
+ Stream f = new Stream(Integer.valueOf(6), handler);
+
+ b.rePrioritise(a, false, 16);
+ c.rePrioritise(b, false, 16);
+ e.rePrioritise(d, false, 16);
+ f.rePrioritise(e, false, 16);
+
+ // Action
+ a.rePrioritise(f, false, 16);
+ d.rePrioritise(c, false, 16);
+
+ // Check parents
+ Assert.assertEquals(f, a.getParentStream());
+ Assert.assertEquals(a, b.getParentStream());
+ Assert.assertEquals(handler, c.getParentStream());
+ Assert.assertEquals(c, d.getParentStream());
+ Assert.assertEquals(d, e.getParentStream());
+ Assert.assertEquals(e, f.getParentStream());
+
+ // Check children
+ Assert.assertEquals(1, handler.getChildStreams().size());
+ Assert.assertTrue(handler.getChildStreams().contains(c));
+ Assert.assertEquals(1, a.getChildStreams().size());
+ Assert.assertTrue(a.getChildStreams().contains(b));
+ Assert.assertEquals(0, b.getChildStreams().size());
+ Assert.assertEquals(1, c.getChildStreams().size());
+ Assert.assertTrue(c.getChildStreams().contains(d));
+ Assert.assertEquals(1, d.getChildStreams().size());
+ Assert.assertTrue(d.getChildStreams().contains(e));
+ Assert.assertEquals(1, e.getChildStreams().size());
+ Assert.assertTrue(e.getChildStreams().contains(f));
+ Assert.assertEquals(1, f.getChildStreams().size());
+ Assert.assertTrue(f.getChildStreams().contains(a));
+
+ }
+
+}
diff --git a/test/org/apache/coyote/http2/TestByteUtil.java b/test/org/apache/coyote/http2/TestByteUtil.java
new file mode 100644
index 0000000..3382439
--- /dev/null
+++ b/test/org/apache/coyote/http2/TestByteUtil.java
@@ -0,0 +1,39 @@
+/*
+ * 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 TestByteUtil {
+
+ @Test
+ public void testGet31Bits() {
+ byte[] input = new byte[] { (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff };
+ int result = ByteUtil.get31Bits(input, 0);
+ Assert.assertEquals(0x7fffffff, result);
+ }
+
+
+ @Test
+ public void testGetFourBytes() {
+ byte[] input = new byte[] { (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff };
+ long result = ByteUtil.getFourBytes(input, 0);
+ Assert.assertEquals(0xffffffffL, result);
+ }
+
+}
diff --git a/test/org/apache/coyote/http2/TestHpack.java b/test/org/apache/coyote/http2/TestHpack.java
new file mode 100644
index 0000000..afa5dbd
--- /dev/null
+++ b/test/org/apache/coyote/http2/TestHpack.java
@@ -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.coyote.http2;
+
+import java.nio.ByteBuffer;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import org.apache.tomcat.util.http.MimeHeaders;
+
+public class TestHpack {
+
+ @Test
+ public void testEncode() throws Exception {
+ MimeHeaders headers = new MimeHeaders();
+ headers.setValue("header1").setString("value1");
+ headers.setValue(":status").setString("200");
+ headers.setValue("header2").setString("value2");
+ ByteBuffer output = ByteBuffer.allocate(512);
+ HpackEncoder encoder = new HpackEncoder(1024);
+ encoder.encode(headers, output);
+ output.flip();
+ // Size is supposed to be 33 without huffman, or 27 with it
+ // TODO: use the HpackHeaderFunction to enable huffman predictably
+ Assert.assertEquals(27, output.remaining());
+ output.clear();
+ encoder.encode(headers, output);
+ output.flip();
+ // Size is now 3 after using the table
+ Assert.assertEquals(3, output.remaining());
+ }
+
+ @Test
+ public void testDecode() throws Exception {
+ MimeHeaders headers = new MimeHeaders();
+ headers.setValue("header1").setString("value1");
+ headers.setValue(":status").setString("200");
+ headers.setValue("header2").setString("value2");
+ ByteBuffer output = ByteBuffer.allocate(512);
+ HpackEncoder encoder = new HpackEncoder(1024);
+ encoder.encode(headers, output);
+ output.flip();
+ MimeHeaders headers2 = new MimeHeaders();
+ HpackDecoder decoder = new HpackDecoder();
+ decoder.setHeaderEmitter(new HeadersListener(headers2));
+ decoder.decode(output);
+ // Redo (table is supposed to be updated)
+ output.clear();
+ encoder.encode(headers, output);
+ output.flip();
+ headers2.recycle();
+ Assert.assertEquals(3, output.remaining());
+ // Check that the decoder is using the table right
+ decoder.decode(output);
+ Assert.assertEquals("value2", headers2.getHeader("header2"));
+ }
+
+ private static class HeadersListener implements HpackDecoder.HeaderEmitter {
+ private final MimeHeaders headers;
+ public HeadersListener(MimeHeaders headers) {
+ this.headers = headers;
+ }
+ @Override
+ public void emitHeader(String name, String value) {
+ headers.setValue(name).setString(value);
+ }
+ @Override
+ public void validateHeaders() throws StreamException {
+ // NO-OP
+ }
+ }
+
+ // TODO: Write more complete tests
+
+}
diff --git a/test/org/apache/coyote/http2/TestHttp2Limits.java b/test/org/apache/coyote/http2/TestHttp2Limits.java
new file mode 100644
index 0000000..9af34a3
--- /dev/null
+++ b/test/org/apache/coyote/http2/TestHttp2Limits.java
@@ -0,0 +1,474 @@
+/*
+ * 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.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import org.apache.catalina.connector.Connector;
+import org.apache.coyote.http2.HpackEncoder.State;
+import org.apache.tomcat.util.http.MimeHeaders;
+
+public class TestHttp2Limits extends Http2TestBase {
+
+ @Test
+ public void testHeaderLimits1x128() throws Exception {
+ // Well within limits
+ doTestHeaderLimits(1, 128, 0);
+ }
+
+
+ @Test
+ public void testHeaderLimits100x32() throws Exception {
+ // Just within default maxHeaderCount
+ // Note request has 3 standard headers
+ doTestHeaderLimits(97, 32, 0);
+ }
+
+
+ @Test
+ public void testHeaderLimits101x32() throws Exception {
+ // Just above default maxHeaderCount
+ doTestHeaderLimits(98, 32, 1);
+ }
+
+
+ @Test
+ public void testHeaderLimits20x32WithLimit10() throws Exception {
+ // Check lower count limit is enforced
+ doTestHeaderLimits(20, 32, -1, 10, Constants.DEFAULT_MAX_HEADER_SIZE, 0, 1);
+ }
+
+
+ @Test
+ public void testHeaderLimits8x1001() throws Exception {
+ // Just within default maxHttpHeaderSize
+ // per header overhead plus standard 2 headers
+ doTestHeaderLimits(8, 1001, 0);
+ }
+
+
+ @Test
+ public void testHeaderLimits8x1002() throws Exception {
+ // Just above default maxHttpHeaderSize
+ doTestHeaderLimits(8, 1002, 1);
+ }
+
+
+ @Test
+ public void testHeaderLimits3x1024WithLimit2048() throws Exception {
+ // Check lower size limit is enforced
+ doTestHeaderLimits(3, 1024, -1, Constants.DEFAULT_MAX_HEADER_COUNT, 2 * 1024, 0, 1);
+ }
+
+
+ @Test
+ public void testHeaderLimits1x12k() throws Exception {
+ // Bug 60232
+ doTestHeaderLimits(1, 12*1024, 1);
+ }
+
+
+ @Test
+ public void testHeaderLimits1x12kin1kChunks() throws Exception {
+ // Bug 60232
+ doTestHeaderLimits(1, 12*1024, 1024, 1);
+ }
+
+
+ @Test
+ public void testHeaderLimits1x12kin1kChunksThenNewRequest() throws Exception {
+ // Bug 60232
+ doTestHeaderLimits(1, 12*1024, 1024, 1);
+
+ output.clearTrace();
+ sendSimpleGetRequest(5);
+ parser.readFrame(true);
+ parser.readFrame(true);
+ Assert.assertEquals(getSimpleResponseTrace(5), output.getTrace());
+ }
+
+
+ @Test
+ public void testHeaderLimits1x32k() throws Exception {
+ // Bug 60232
+ doTestHeaderLimits(1, 32*1024, 1);
+ }
+
+
+ @Test
+ public void testHeaderLimits1x32kin1kChunks() throws Exception {
+ // Bug 60232
+ // 500ms per frame write delay to give server a chance to process the
+ // stream reset and the connection reset before the request is fully
+ // sent.
+ doTestHeaderLimits(1, 32*1024, 1024, 500, 2);
+ }
+
+
+ @Test
+ public void testHeaderLimits1x128k() throws Exception {
+ // Bug 60232
+ doTestHeaderLimits(1, 128*1024, 2);
+ }
+
+
+ @Test
+ public void testHeaderLimits1x512k() throws Exception {
+ // Bug 60232
+ doTestHeaderLimits(1, 512*1024, 2);
+ }
+
+
+ @Test
+ public void testHeaderLimits10x512k() throws Exception {
+ // Bug 60232
+ doTestHeaderLimits(10, 512*1024, 2);
+ }
+
+
+ private void doTestHeaderLimits(int headerCount, int headerSize, int failMode) throws Exception {
+ doTestHeaderLimits(headerCount, headerSize, -1, failMode);
+ }
+
+
+ private void doTestHeaderLimits(int headerCount, int headerSize, int maxHeaderPayloadSize,
+ int failMode) throws Exception {
+ doTestHeaderLimits(headerCount, headerSize, maxHeaderPayloadSize, 0, failMode);
+ }
+
+
+ private void doTestHeaderLimits(int headerCount, int headerSize, int maxHeaderPayloadSize,
+ int delayms, int failMode) throws Exception {
+ doTestHeaderLimits(headerCount, headerSize, maxHeaderPayloadSize,
+ Constants.DEFAULT_MAX_HEADER_COUNT, Constants.DEFAULT_MAX_HEADER_SIZE, delayms,
+ failMode);
+ }
+
+
+ private void doTestHeaderLimits(int headerCount, int headerSize, int maxHeaderPayloadSize,
+ int maxHeaderCount, int maxHeaderSize, int delayms, int failMode) throws Exception {
+
+ // Build the custom headers
+ List<String[]> customHeaders = new ArrayList<>();
+ StringBuilder headerValue = new StringBuilder(headerSize);
+ // Does not need to be secure
+ Random r = new Random();
+ for (int i = 0; i < headerSize; i++) {
+ // Random lower case characters
+ headerValue.append((char) ('a' + r.nextInt(26)));
+ }
+ String v = headerValue.toString();
+ for (int i = 0; i < headerCount; i++) {
+ customHeaders.add(new String[] {"X-TomcatTest" + i, v});
+ }
+
+ enableHttp2();
+
+ Http2Protocol http2Protocol =
+ (Http2Protocol) getTomcatInstance().getConnector().findUpgradeProtocols()[0];
+ http2Protocol.setMaxHeaderCount(maxHeaderCount);
+ http2Protocol.setMaxHeaderSize(maxHeaderSize);
+
+ configureAndStartWebApplication();
+ openClientConnection();
+ doHttpUpgrade();
+ sendClientPreface();
+ validateHttp2InitialResponse();
+
+ if (maxHeaderPayloadSize == -1) {
+ maxHeaderPayloadSize = output.getMaxFrameSize();
+ }
+
+ // Build the simple request
+ byte[] frameHeader = new byte[9];
+ // Assumes at least one custom header and that all headers are the same
+ // length. These assumptions are valid for these tests.
+ ByteBuffer headersPayload = ByteBuffer.allocate(200 + (int) (customHeaders.size() *
+ customHeaders.iterator().next()[1].length() * 1.2));
+
+ populateHeadersPayload(headersPayload, customHeaders, "/simple");
+
+ Exception e = null;
+ try {
+ int written = 0;
+ int left = headersPayload.limit() - written;
+ while (left > 0) {
+ int thisTime = Math.min(left, maxHeaderPayloadSize);
+ populateFrameHeader(frameHeader, written, left, thisTime, 3);
+ writeFrame(frameHeader, headersPayload, headersPayload.limit() - left,
+ thisTime, delayms);
+ left -= thisTime;
+ written += thisTime;
+ }
+ } catch (IOException ioe) {
+ e = ioe;
+ }
+
+ switch (failMode) {
+ case 0: {
+ // Expect a normal response
+ readSimpleGetResponse();
+ Assert.assertEquals(getSimpleResponseTrace(3), output.getTrace());
+ Assert.assertNull(e);
+ break;
+ }
+ case 1: {
+ // Expect a stream reset
+ parser.readFrame(true);
+ Assert.assertEquals("3-RST-[11]\n", output.getTrace());
+ Assert.assertNull(e);
+ break;
+ }
+ case 2: {
+ // Behaviour depends on timing. If reset is processed fast enough,
+ // frames will be swallowed before the connection reset limit is
+ // reached
+ if (e == null) {
+ parser.readFrame(true);
+ Assert.assertEquals("3-RST-[11]\n", output.getTrace());
+ Assert.assertNull(e);
+ }
+ // Else is non-null as expected for a connection reset
+ break;
+ }
+ default: {
+ Assert.fail("Unknown failure mode");
+ }
+ }
+ }
+
+
+ private void populateHeadersPayload(ByteBuffer headersPayload, List<String[]> customHeaders,
+ String path) throws Exception {
+ MimeHeaders headers = new MimeHeaders();
+ headers.addValue(":method").setString("GET");
+ headers.addValue(":path").setString(path);
+ headers.addValue(":authority").setString("localhost:" + getPort());
+ for (String[] customHeader : customHeaders) {
+ headers.addValue(customHeader[0]).setString(customHeader[1]);
+ }
+ State state = hpackEncoder.encode(headers, headersPayload);
+ if (state != State.COMPLETE) {
+ throw new Exception("Unable to build headers");
+ }
+ headersPayload.flip();
+
+ log.debug("Headers payload generated of size [" + headersPayload.limit() + "]");
+ }
+
+
+ private void populateFrameHeader(byte[] frameHeader, int written, int left, int thisTime,
+ int streamId) throws Exception {
+ ByteUtil.setThreeBytes(frameHeader, 0, thisTime);
+ if (written == 0) {
+ frameHeader[3] = FrameType.HEADERS.getIdByte();
+ // Flags. End of stream
+ frameHeader[4] = 0x01;
+ } else {
+ frameHeader[3] = FrameType.CONTINUATION.getIdByte();
+ }
+ if (left == thisTime) {
+ // Flags. End of headers
+ frameHeader[4] = (byte) (frameHeader[4] + 0x04);
+ }
+
+ // Stream id
+ ByteUtil.set31Bits(frameHeader, 5, streamId);
+ }
+
+
+ @Test
+ public void testCookieLimit1() throws Exception {
+ doTestCookieLimit(1, 0);
+ }
+
+
+ @Test
+ public void testCookieLimit2() throws Exception {
+ doTestCookieLimit(2, 0);
+ }
+
+
+ @Test
+ public void testCookieLimit100() throws Exception {
+ doTestCookieLimit(100, 0);
+ }
+
+
+ @Test
+ public void testCookieLimit100WithLimit50() throws Exception {
+ doTestCookieLimit(100, 50, 1);
+ }
+
+
+ @Test
+ public void testCookieLimit200() throws Exception {
+ doTestCookieLimit(200, 0);
+ }
+
+
+ @Test
+ public void testCookieLimit201() throws Exception {
+ doTestCookieLimit(201, 1);
+ }
+
+
+ private void doTestCookieLimit(int cookieCount, int failMode) throws Exception {
+ doTestCookieLimit(cookieCount, Constants.DEFAULT_MAX_COOKIE_COUNT, failMode);
+ }
+
+
+ private void doTestCookieLimit(int cookieCount, int maxCookieCount, int failMode)
+ throws Exception {
+
+ enableHttp2();
+
+ Connector connector = getTomcatInstance().getConnector();
+ connector.setMaxCookieCount(maxCookieCount);
+
+ configureAndStartWebApplication();
+ openClientConnection();
+ doHttpUpgrade();
+ sendClientPreface();
+ validateHttp2InitialResponse();
+
+ output.setTraceBody(true);
+
+ byte[] frameHeader = new byte[9];
+ ByteBuffer headersPayload = ByteBuffer.allocate(8192);
+
+ List<String[]> customHeaders = new ArrayList<>();
+ for (int i = 0; i < cookieCount; i++) {
+ customHeaders.add(new String[] {"Cookie", "a" + cookieCount + "=b" + cookieCount});
+ }
+
+ populateHeadersPayload(headersPayload, customHeaders, "/cookie");
+ populateFrameHeader(frameHeader, 0, headersPayload.limit(), headersPayload.limit(), 3);
+
+ writeFrame(frameHeader, headersPayload);
+
+ switch (failMode) {
+ case 0: {
+ parser.readFrame(true);
+ parser.readFrame(true);
+ parser.readFrame(true);
+ System.out.println(output.getTrace());
+ Assert.assertEquals(getCookieResponseTrace(3, cookieCount), output.getTrace());
+ break;
+ }
+ case 1: {
+ // Check status is 500
+ parser.readFrame(true);
+ Assert.assertTrue(output.getTrace(), output.getTrace().startsWith(
+ "3-HeadersStart\n3-Header-[:status]-[500]"));
+ output.clearTrace();
+ // Check EOS followed by reset is next
+ parser.readFrame(true);
+ parser.readFrame(true);
+ Assert.assertEquals("3-EndOfStream\n3-RST-[2]\n", output.getTrace());
+ break;
+ }
+ default: {
+ Assert.fail("Unknown failure mode specified");
+ }
+ }
+ }
+
+
+ @Test
+ public void doTestPostWithTrailerHeadersDefaultLimit() throws Exception{
+ doTestPostWithTrailerHeaders(Constants.DEFAULT_MAX_TRAILER_COUNT,
+ Constants.DEFAULT_MAX_TRAILER_SIZE, true);
+ }
+
+
+ @Test
+ public void doTestPostWithTrailerHeadersCount0() throws Exception{
+ doTestPostWithTrailerHeaders(0, Constants.DEFAULT_MAX_TRAILER_SIZE, false);
+ }
+
+
+ @Test
+ public void doTestPostWithTrailerHeadersSize0() throws Exception{
+ doTestPostWithTrailerHeaders(Constants.DEFAULT_MAX_TRAILER_COUNT, 0, false);
+ }
+
+
+ private void doTestPostWithTrailerHeaders(int maxTrailerCount, int maxTrailerSize, boolean ok)
+ throws Exception {
+ enableHttp2();
+
+ Http2Protocol http2Protocol =
+ (Http2Protocol) getTomcatInstance().getConnector().findUpgradeProtocols()[0];
+ http2Protocol.setAllowedTrailerHeaders(TRAILER_HEADER_NAME);
+ http2Protocol.setMaxTrailerCount(maxTrailerCount);
+ http2Protocol.setMaxTrailerSize(maxTrailerSize);
+
+ configureAndStartWebApplication();
+ openClientConnection();
+ doHttpUpgrade();
+ sendClientPreface();
+ validateHttp2InitialResponse();
+
+ byte[] headersFrameHeader = new byte[9];
+ ByteBuffer headersPayload = ByteBuffer.allocate(128);
+ byte[] dataFrameHeader = new byte[9];
+ ByteBuffer dataPayload = ByteBuffer.allocate(256);
+ byte[] trailerFrameHeader = new byte[9];
+ ByteBuffer trailerPayload = ByteBuffer.allocate(256);
+
+ buildPostRequest(headersFrameHeader, headersPayload, false, dataFrameHeader, dataPayload,
+ null, trailerFrameHeader, trailerPayload, 3);
+
+ // Write the headers
+ writeFrame(headersFrameHeader, headersPayload);
+ // Body
+ writeFrame(dataFrameHeader, dataPayload);
+ // Trailers
+ writeFrame(trailerFrameHeader, trailerPayload);
+
+ parser.readFrame(true);
+ if (ok) {
+ parser.readFrame(true);
+ parser.readFrame(true);
+ parser.readFrame(true);
+
+ String len = Integer.toString(256 + TRAILER_HEADER_VALUE.length());
+
+ Assert.assertEquals("0-WindowSize-[256]\n" +
+ "3-WindowSize-[256]\n" +
+ "3-HeadersStart\n" +
+ "3-Header-[:status]-[200]\n" +
+ "3-Header-[date]-["+ DEFAULT_DATE + "]\n" +
+ "3-HeadersEnd\n" +
+ "3-Body-" +
+ len +
+ "\n" +
+ "3-EndOfStream\n",
+ output.getTrace());
+ } else {
+ Assert.assertEquals("3-RST-[11]\n", output.getTrace());
+ }
+ }
+}
diff --git a/test/org/apache/coyote/http2/TestHttp2Section_3_2.java b/test/org/apache/coyote/http2/TestHttp2Section_3_2.java
new file mode 100644
index 0000000..885e72c
--- /dev/null
+++ b/test/org/apache/coyote/http2/TestHttp2Section_3_2.java
@@ -0,0 +1,180 @@
+/*
+ * 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.nio.charset.StandardCharsets;
+
+import org.junit.Test;
+
+/**
+ * Unit tests for Section 3.2 of
+ * <a href="https://tools.ietf.org/html/rfc7540">RFC 7540</a>.
+ * <br>
+ * The order of tests in this class is aligned with the order of the
+ * requirements in the RFC.
+ */
+public class TestHttp2Section_3_2 extends Http2TestBase {
+
+ // Note: Tests for zero/multiple HTTP2-Settings fields can be found below
+ // in the tests for section 3.2.1
+
+ // TODO: Test initial requests with bodies of various sizes
+
+ @Test
+ public void testConnectionNoHttp2Support() throws Exception {
+ configureAndStartWebApplication();
+ openClientConnection();
+ doHttpUpgrade(DEFAULT_CONNECTION_HEADER_VALUE, "h2c", EMPTY_HTTP2_SETTINGS_HEADER, false);
+ parseHttp11Response();
+ }
+
+
+ @Test
+ public void testConnectionUpgradeWrongProtocol() throws Exception {
+ enableHttp2();
+ configureAndStartWebApplication();
+ openClientConnection();
+ doHttpUpgrade(DEFAULT_CONNECTION_HEADER_VALUE, "h2", EMPTY_HTTP2_SETTINGS_HEADER, false);
+ parseHttp11Response();
+ }
+
+
+ @Test(timeout=10000)
+ public void testConnectionNoPreface() throws Exception {
+ setupAsFarAsUpgrade();
+
+ // If we don't send the preface the server should kill the connection.
+ try {
+ // Make the parser read something.
+ parser.readFrame(true);
+ } catch (IOException ioe) {
+ // Expected because the server is going to drop the connection.
+ }
+ }
+
+
+ @Test(timeout=10000)
+ public void testConnectionIncompletePrefaceStart() throws Exception {
+ setupAsFarAsUpgrade();
+
+ // If we send an incomplete preface the server should kill the
+ // connection.
+ os.write("PRI * HTTP/2.0\r\n\r\n".getBytes(StandardCharsets.ISO_8859_1));
+ os.flush();
+ try {
+ // Make the parser read something.
+ parser.readFrame(true);
+ } catch (IOException ioe) {
+ // Expected because the server is going to drop the connection.
+ }
+ }
+
+
+ @Test(timeout=10000)
+ public void testConnectionInvalidPrefaceStart() throws Exception {
+ setupAsFarAsUpgrade();
+
+ // If we send an incomplete preface the server should kill the
+ // connection.
+ os.write("xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxxx".getBytes(
+ StandardCharsets.ISO_8859_1));
+ os.flush();
+ try {
+ // Make the parser read something.
+ parser.readFrame(true);
+ } catch (IOException ioe) {
+ // Expected because the server is going to drop the connection.
+ }
+ }
+
+
+ @Test
+ public void testConnectionUpgradeFirstResponse() throws Exception{
+ super.http2Connect();
+ }
+
+
+ private void setupAsFarAsUpgrade() throws Exception {
+ enableHttp2();
+ configureAndStartWebApplication();
+ openClientConnection();
+ doHttpUpgrade();
+ }
+
+
+ //------------------------------------------------------------ Section 3.2.1
+
+ @Test
+ public void testZeroHttp2Settings() throws Exception {
+ enableHttp2();
+ configureAndStartWebApplication();
+ openClientConnection();
+ doHttpUpgrade(Http2TestBase.DEFAULT_CONNECTION_HEADER_VALUE, "h2c", "", false);
+ parseHttp11Response();
+ }
+
+
+ @Test
+ public void testMultipleHttp2Settings() throws Exception {
+ enableHttp2();
+ configureAndStartWebApplication();
+ openClientConnection();
+ doHttpUpgrade(Http2TestBase.DEFAULT_CONNECTION_HEADER_VALUE, "h2c",
+ Http2TestBase.EMPTY_HTTP2_SETTINGS_HEADER +
+ Http2TestBase.EMPTY_HTTP2_SETTINGS_HEADER, false);
+ parseHttp11Response();
+ }
+
+
+ @Test
+ public void testMissingConnectionValue() throws Exception {
+ enableHttp2();
+ configureAndStartWebApplication();
+ openClientConnection();
+ doHttpUpgrade("Upgrade", "h2c", Http2TestBase.EMPTY_HTTP2_SETTINGS_HEADER, false);
+ parseHttp11Response();
+ }
+
+
+ @Test
+ public void testSplitConnectionValue01() throws Exception {
+ enableHttp2();
+ configureAndStartWebApplication();
+ openClientConnection();
+ doHttpUpgrade("Upgrade\r\nConnection: HTTP2-Settings", "h2c",
+ Http2TestBase.EMPTY_HTTP2_SETTINGS_HEADER, true);
+ sendClientPreface();
+ validateHttp2InitialResponse();
+ }
+
+
+ @Test
+ public void testSplitConnectionValue02() throws Exception {
+ enableHttp2();
+ configureAndStartWebApplication();
+ openClientConnection();
+ doHttpUpgrade("HTTP2-Settings\r\nConnection: Upgrade", "h2c",
+ Http2TestBase.EMPTY_HTTP2_SETTINGS_HEADER, true);
+ sendClientPreface();
+ validateHttp2InitialResponse();
+ }
+
+ // No need to test how trailing '=' are handled here. HTTP2Settings payloads
+ // are always a multiple of 6 long which means valid payloads never end in
+ // '='. Invalid payloads will be rejected anyway.
+}
diff --git a/test/org/apache/coyote/http2/TestHttp2Section_3_5.java b/test/org/apache/coyote/http2/TestHttp2Section_3_5.java
new file mode 100644
index 0000000..dc09ca4
--- /dev/null
+++ b/test/org/apache/coyote/http2/TestHttp2Section_3_5.java
@@ -0,0 +1,39 @@
+/*
+ * 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 org.junit.Test;
+
+public class TestHttp2Section_3_5 extends Http2TestBase {
+
+ @Test(expected=IOException.class)
+ public void testNoConnectionPreface() throws Exception {
+ enableHttp2();
+ configureAndStartWebApplication();
+ openClientConnection();
+ doHttpUpgrade();
+ // Should send client preface here
+ sendPing();
+ // Send several pings else server will block waiting for the client
+ // preface which is longer than a single ping.
+ sendPing();
+ sendPing();
+ validateHttp2InitialResponse();
+ }
+}
diff --git a/test/org/apache/coyote/http2/TestHttp2Section_4_1.java b/test/org/apache/coyote/http2/TestHttp2Section_4_1.java
new file mode 100644
index 0000000..73a31d5
--- /dev/null
+++ b/test/org/apache/coyote/http2/TestHttp2Section_4_1.java
@@ -0,0 +1,72 @@
+/*
+ * 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.nio.ByteBuffer;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Unit tests for Section 4.1 of
+ * <a href="https://tools.ietf.org/html/rfc7540">RFC 7540</a>.
+ * <br>
+ * The order of tests in this class is aligned with the order of the
+ * requirements in the RFC.
+ */
+public class TestHttp2Section_4_1 extends Http2TestBase {
+
+ private static final byte[] UNKNOWN_FRAME = new byte[] {
+ 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+ // TODO: Tests for over-sized frames. Better located in tests for section 6?
+
+
+ @Test
+ public void testUnknownFrameType() throws Exception {
+ http2Connect();
+ os.write(UNKNOWN_FRAME);
+ os.flush();
+ sendSimpleGetRequest(3);
+ readSimpleGetResponse();
+ Assert.assertEquals(getSimpleResponseTrace(3), output.getTrace());
+ }
+
+
+ // TODO: Tests for unexpected flags. Better located in tests for section 6?
+
+
+ @Test
+ public void testReservedBitIgnored() throws Exception {
+ // HTTP2 upgrade
+ http2Connect();
+
+ // Build the simple request
+ byte[] frameHeader = new byte[9];
+ ByteBuffer headersPayload = ByteBuffer.allocate(128);
+ buildSimpleGetRequest(frameHeader, headersPayload, null, 3);
+
+ // Tweak the header to set the reserved bit
+ frameHeader[5] = (byte) (frameHeader[5] | 0x80);
+
+ // Process the request
+ writeFrame(frameHeader, headersPayload);
+
+ readSimpleGetResponse();
+ Assert.assertEquals(getSimpleResponseTrace(3), output.getTrace());
+ }
+}
diff --git a/test/org/apache/coyote/http2/TestHttp2Section_4_2.java b/test/org/apache/coyote/http2/TestHttp2Section_4_2.java
new file mode 100644
index 0000000..f99dd9f
--- /dev/null
+++ b/test/org/apache/coyote/http2/TestHttp2Section_4_2.java
@@ -0,0 +1,143 @@
+/*
+ * 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;
+
+/**
+ * Unit tests for Section 4.2 of
+ * <a href="https://tools.ietf.org/html/rfc7540">RFC 7540</a>.
+ * <br>
+ * The order of tests in this class is aligned with the order of the
+ * requirements in the RFC.
+ */
+public class TestHttp2Section_4_2 extends Http2TestBase {
+
+ @Test
+ public void testFrameSizeLimitsTooBig() throws Exception {
+ // HTTP2 upgrade
+ http2Connect();
+
+ // Overly large settings
+ // Settings have to be a multiple of six
+ int settingsCount = (ConnectionSettingsBase.DEFAULT_MAX_FRAME_SIZE / 6) + 1;
+ int size = settingsCount * 6;
+ byte[] settings = new byte[size + 9];
+ // Header
+ // Length
+ ByteUtil.setThreeBytes(settings, 0, size);
+ // Type
+ settings[3] = FrameType.SETTINGS.getIdByte();
+ // No flags
+ // Stream 0
+
+ // payload
+ for (int i = 0; i < settingsCount; i++) {
+ // Enable server push over and over again
+ ByteUtil.setTwoBytes(settings, (i * 6) + 9, 2);
+ ByteUtil.setFourBytes(settings, (i * 6) + 9 + 2, 1);
+ }
+
+ os.write(settings);
+
+ // Read GOAWAY frame
+ parser.readFrame(true);
+
+ Assert.assertTrue(output.getTrace(), output.getTrace().startsWith(
+ "0-Goaway-[1]-[" + Http2Error.FRAME_SIZE_ERROR.getCode() + "]-["));
+ }
+
+ @Test
+ public void testFrameTypeLimitsTooBig() throws Exception {
+ // HTTP2 upgrade
+ http2Connect();
+
+ // Overly large ping
+ byte[] ping = new byte[109];
+
+ // Header
+ // Length
+ ByteUtil.setThreeBytes(ping, 0, 100);
+ // Type
+ ping[3] = FrameType.PING.getIdByte();
+ // No flags
+ // Stream 0
+ // Empty payload
+
+ os.write(ping);
+
+ // Read GOAWAY frame
+ parser.readFrame(true);
+
+ Assert.assertTrue(output.getTrace(), output.getTrace().startsWith(
+ "0-Goaway-[1]-[" + Http2Error.FRAME_SIZE_ERROR.getCode() + "]-["));
+ }
+
+
+ @Test
+ public void testFrameTypeLimitsTooSmall() throws Exception {
+ // HTTP2 upgrade
+ http2Connect();
+
+ // Too small ping
+ byte[] ping = new byte[9];
+
+ // Header
+ // Length 0
+ // Type
+ ping[3] = FrameType.PING.getIdByte();
+ // No flags
+ // Stream 0
+ // Empty payload
+
+ os.write(ping);
+
+ // Read GOAWAY frame
+ parser.readFrame(true);
+
+ Assert.assertTrue(output.getTrace(), output.getTrace().startsWith(
+ "0-Goaway-[1]-[" + Http2Error.FRAME_SIZE_ERROR.getCode() + "]-["));
+ }
+
+
+ @Test
+ public void testFrameTypeLimitsStream() throws Exception {
+ // HTTP2 upgrade
+ http2Connect();
+
+ // Invalid priority
+ byte[] priority = new byte[9];
+
+ // Header
+ // Length 0
+ // Type
+ priority[3] = FrameType.PRIORITY.getIdByte();
+ // No flags
+ // Stream 3
+ ByteUtil.set31Bits(priority, 5, 3);
+ // Empty payload
+
+ os.write(priority);
+
+ // Read GOAWAY frame
+ parser.readFrame(true);
+
+ Assert.assertTrue(output.getTrace(),
+ output.getTrace().startsWith("3-RST-[6]"));
+ }
+}
diff --git a/test/org/apache/coyote/http2/TestHttp2Section_4_3.java b/test/org/apache/coyote/http2/TestHttp2Section_4_3.java
new file mode 100644
index 0000000..c581bb0
--- /dev/null
+++ b/test/org/apache/coyote/http2/TestHttp2Section_4_3.java
@@ -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.coyote.http2;
+
+import java.nio.ByteBuffer;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Unit tests for Section 4.3 of
+ * <a href="https://tools.ietf.org/html/rfc7540">RFC 7540</a>.
+ * <br>
+ * The order of tests in this class is aligned with the order of the
+ * requirements in the RFC.
+ */
+public class TestHttp2Section_4_3 extends Http2TestBase {
+
+ @Test
+ public void testHeaderDecodingError() throws Exception {
+ // HTTP2 upgrade
+ http2Connect();
+
+ // Build the simple request
+ byte[] frameHeader = new byte[9];
+ ByteBuffer headersPayload = ByteBuffer.allocate(128);
+ buildSimpleGetRequest(frameHeader, headersPayload, null, 3);
+
+ // Try and corrupt the headerPayload
+ headersPayload.put(0, (byte) (headersPayload.get(0) + 128));
+
+ // Process the request
+ writeFrame(frameHeader, headersPayload);
+
+ // Read GOAWAY frame
+ parser.readFrame(true);
+
+ Assert.assertTrue(output.getTrace(), output.getTrace().startsWith(
+ "0-Goaway-[1]-[" + Http2Error.COMPRESSION_ERROR.getCode() + "]-["));
+ }
+
+
+ @Test
+ public void testHeaderContinuationContiguous() throws Exception {
+ // HTTP2 upgrade
+ http2Connect();
+
+ // Part 1
+ byte[] frameHeader = new byte[9];
+ ByteBuffer headersPayload = ByteBuffer.allocate(128);
+ buildSimpleGetRequestPart1(frameHeader, headersPayload, 3);
+ writeFrame(frameHeader, headersPayload);
+
+ // Part 2
+ headersPayload.clear();
+ buildSimpleGetRequestPart2(frameHeader, headersPayload, 3);
+ writeFrame(frameHeader, headersPayload);
+
+ // headers, body
+ parser.readFrame(true);
+ parser.readFrame(true);
+
+ Assert.assertEquals(getSimpleResponseTrace(3), output.getTrace());
+ }
+
+
+ @Test
+ public void testHeaderContinuationNonContiguous() throws Exception {
+ // HTTP2 upgrade
+ http2Connect();
+
+ // Part 1
+ byte[] frameHeader = new byte[9];
+ ByteBuffer headersPayload = ByteBuffer.allocate(128);
+ buildSimpleGetRequestPart1(frameHeader, headersPayload, 3);
+ writeFrame(frameHeader, headersPayload);
+
+ sendPing();
+
+ // Read GOAWAY frame
+ parser.readFrame(true);
+
+ Assert.assertTrue(output.getTrace(), output.getTrace().startsWith(
+ "0-Goaway-[1]-[" + Http2Error.COMPRESSION_ERROR.getCode() + "]-["));
+ }
+}
diff --git a/test/org/apache/coyote/http2/TestHttp2Section_5_1.java b/test/org/apache/coyote/http2/TestHttp2Section_5_1.java
new file mode 100644
index 0000000..a03fdf8
--- /dev/null
+++ b/test/org/apache/coyote/http2/TestHttp2Section_5_1.java
@@ -0,0 +1,309 @@
+/*
+ * 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.nio.ByteBuffer;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Unit tests for Section 5.§ of
+ * <a href="https://tools.ietf.org/html/rfc7540">RFC 7540</a>.
+ * <br>
+ * The order of tests in this class is aligned with the order of the
+ * requirements in the RFC.
+ */
+public class TestHttp2Section_5_1 extends Http2TestBase {
+
+ @Test
+ public void testIdleStateInvalidFrame01() throws Exception {
+ http2Connect();
+
+ sendWindowUpdate(3, 200);
+
+ parser.readFrame(true);
+
+ Assert.assertTrue(output.getTrace(), output.getTrace().startsWith(
+ "0-Goaway-[1]-[" + Http2Error.PROTOCOL_ERROR.getCode() + "]-["));
+ }
+
+
+ @Test
+ public void testIdleStateInvalidFrame02() throws Exception {
+ http2Connect();
+
+ sendData(3, new byte[] {});
+
+ parser.readFrame(true);
+
+ Assert.assertTrue(output.getTrace(), output.getTrace().startsWith(
+ "0-Goaway-[1]-[" + Http2Error.PROTOCOL_ERROR.getCode() + "]-["));
+ }
+
+
+ // TODO: reserved local
+ // TODO: reserved remote
+
+
+ @Test
+ public void halfClosedRemoteInvalidFrame() throws Exception {
+ http2Connect();
+
+ // This half-closes the stream since it includes the end of stream flag
+ sendSimpleGetRequest(3);
+ readSimpleGetResponse();
+ Assert.assertEquals(getSimpleResponseTrace(3), output.getTrace());
+ output.clearTrace();
+
+ // This should trigger a stream error
+ sendData(3, new byte[] {});
+ parser.readFrame(true);
+
+ Assert.assertTrue(output.getTrace(), output.getTrace().startsWith(
+ "0-Goaway-[3]-[" + Http2Error.STREAM_CLOSED.getCode() + "]-["));
+ }
+
+
+ @Test
+ public void testClosedInvalidFrame01() throws Exception {
+ // HTTP2 upgrade
+ http2Connect();
+
+ // Build the simple request
+ byte[] frameHeader = new byte[9];
+ ByteBuffer headersPayload = ByteBuffer.allocate(128);
+ buildSimpleGetRequest(frameHeader, headersPayload, null, 3);
+
+ // Remove the end of stream and end of headers flags
+ frameHeader[4] = 0;
+
+ // Process the request
+ writeFrame(frameHeader, headersPayload);
+
+ // Send a rst
+ sendRst(3, Http2Error.INTERNAL_ERROR.getCode());
+
+ // Then try sending some data (which should fail)
+ sendData(3, new byte[] {});
+ parser.readFrame(true);
+
+ Assert.assertTrue(output.getTrace(),
+ output.getTrace().startsWith("3-RST-[" + Http2Error.STREAM_CLOSED.getCode() + "]"));
+ }
+
+
+ @Test
+ public void testClosedInvalidFrame02() throws Exception {
+ http2Connect();
+
+ // Stream 1 is closed. This should trigger a stream error
+ sendData(1, new byte[] {});
+ parser.readFrame(true);
+
+ Assert.assertTrue(output.getTrace(), output.getTrace().startsWith(
+ "0-Goaway-[1]-[" + Http2Error.STREAM_CLOSED.getCode() + "]-["));
+ }
+
+
+ // TODO: Invalid frames for each of the remaining states
+
+ // Section 5.1.1
+
+ @Test
+ public void testClientSendEvenStream() throws Exception {
+ // HTTP2 upgrade
+ http2Connect();
+
+ // Part 1
+ byte[] frameHeader = new byte[9];
+ ByteBuffer headersPayload = ByteBuffer.allocate(128);
+ buildSimpleGetRequestPart1(frameHeader, headersPayload, 4);
+ writeFrame(frameHeader, headersPayload);
+
+ // headers
+ parser.readFrame(true);
+
+ Assert.assertTrue(output.getTrace(), output.getTrace().startsWith(
+ "0-Goaway-[1]-[" + Http2Error.PROTOCOL_ERROR.getCode() + "]-["));
+ }
+
+
+ @Test
+ public void testClientSendOldStream() throws Exception {
+ http2Connect();
+ sendSimpleGetRequest(5);
+ readSimpleGetResponse();
+ Assert.assertEquals(getSimpleResponseTrace(5), output.getTrace());
+ output.clearTrace();
+
+
+ // Build the simple request on an old stream
+ byte[] frameHeader = new byte[9];
+ ByteBuffer headersPayload = ByteBuffer.allocate(128);
+ buildSimpleGetRequest(frameHeader, headersPayload, null, 3);
+
+ os.write(frameHeader);
+ os.flush();
+
+ // headers
+ parser.readFrame(true);
+
+ Assert.assertTrue(output.getTrace(), output.getTrace().startsWith(
+ "0-Goaway-[5]-[" + Http2Error.PROTOCOL_ERROR.getCode() + "]-["));
+ }
+
+
+ @Test
+ public void testImplicitClose() throws Exception {
+ http2Connect();
+
+ sendPriority(3, 0, 16);
+ sendPriority(5, 0, 16);
+
+ sendSimpleGetRequest(5);
+ readSimpleGetResponse();
+ Assert.assertEquals(getSimpleResponseTrace(5), output.getTrace());
+ output.clearTrace();
+
+ // Should trigger an error since stream 3 should have been implicitly
+ // closed.
+ sendSimpleGetRequest(3);
+
+ parser.readFrame(true);
+
+ Assert.assertTrue(output.getTrace(), output.getTrace().startsWith(
+ "0-Goaway-[5]-[" + Http2Error.PROTOCOL_ERROR.getCode() + "]-["));
+ }
+
+
+ @Test
+ public void testExceedMaxActiveStreams() throws Exception {
+ // http2Connect() - modified
+ enableHttp2(1);
+ configureAndStartWebApplication();
+ openClientConnection();
+ doHttpUpgrade();
+ sendClientPreface();
+
+ // validateHttp2InitialResponse() - modified
+ parser.readFrame(true);
+ parser.readFrame(true);
+ parser.readFrame(true);
+ parser.readFrame(true);
+ parser.readFrame(true);
+
+ Assert.assertEquals("0-Settings-[3]-[1]\n" +
+ "0-Settings-End\n" +
+ "0-Settings-Ack\n" +
+ "0-Ping-[0,0,0,0,0,0,0,1]\n" +
+ getSimpleResponseTrace(1)
+ , output.getTrace());
+ output.clearTrace();
+
+ sendLargeGetRequest(3);
+
+ sendSimpleGetRequest(5);
+
+ // Default connection window size is 64k-1.
+ // Initial request will have used 8k leaving 56k-1.
+ // Stream window will be 64k-1.
+ // Expecting
+ // 1 * headers
+ // 56k-1 of body (7 * ~8k)
+ // 1 * error (could be in any order)
+ for (int i = 0; i < 8; i++) {
+ parser.readFrame(true);
+ }
+ parser.readFrame(true);
+
+ Assert.assertTrue(output.getTrace(),
+ output.getTrace().contains("5-RST-[" +
+ Http2Error.REFUSED_STREAM.getCode() + "]"));
+ output.clearTrace();
+
+ // Connection window is zero.
+ // Stream window is 8k
+
+ // Release the remaining body
+ sendWindowUpdate(0, (1 << 31) - 2);
+ // Allow for the 8k still in the stream window
+ sendWindowUpdate(3, (1 << 31) - 8193);
+
+ // 192k of body (24 * 8k)
+ // 1 * error (could be in any order)
+ for (int i = 0; i < 24; i++) {
+ parser.readFrame(true);
+ }
+ }
+
+
+ @Test
+ public void testErrorOnWaitingStream() throws Exception {
+ // http2Connect() - modified
+ enableHttp2(1);
+ configureAndStartWebApplication();
+ openClientConnection();
+ doHttpUpgrade();
+ sendClientPreface();
+
+ // validateHttp2InitialResponse() - modified
+ parser.readFrame(true);
+ parser.readFrame(true);
+ parser.readFrame(true);
+ parser.readFrame(true);
+ parser.readFrame(true);
+
+ Assert.assertEquals("0-Settings-[3]-[1]\n" +
+ "0-Settings-End\n" +
+ "0-Settings-Ack\n" +
+ "0-Ping-[0,0,0,0,0,0,0,1]\n" +
+ getSimpleResponseTrace(1)
+ , output.getTrace());
+ output.clearTrace();
+
+ sendLargeGetRequest(3);
+
+ sendSimpleGetRequest(5);
+
+ // Default connection window size is 64k-1.
+ // Initial request will have used 8k leaving 56k-1.
+ // Stream window will be 64k-1.
+ // Expecting
+ // 1 * headers
+ // 56k-1 of body (7 * ~8k)
+ // 1 * error (could be in any order)
+ for (int i = 0; i < 8; i++) {
+ parser.readFrame(true);
+ }
+ parser.readFrame(true);
+
+ Assert.assertTrue(output.getTrace(),
+ output.getTrace().contains("5-RST-[" +
+ Http2Error.REFUSED_STREAM.getCode() + "]"));
+ output.clearTrace();
+
+ // Connection window is zero.
+ // Stream window is 8k
+
+ // Expand the stream window too much to trigger an error
+ // Allow for the 8k still in the stream window
+ sendWindowUpdate(3, (1 << 31) - 1);
+
+ parser.readFrame(true);
+ }
+}
diff --git a/test/org/apache/coyote/http2/TestHttp2Section_5_2.java b/test/org/apache/coyote/http2/TestHttp2Section_5_2.java
new file mode 100644
index 0000000..545825f
--- /dev/null
+++ b/test/org/apache/coyote/http2/TestHttp2Section_5_2.java
@@ -0,0 +1,116 @@
+/*
+ * 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.Before;
+import org.junit.Test;
+
+/**
+ * Unit tests for Section 5.2 of
+ * <a href="https://tools.ietf.org/html/rfc7540">RFC 7540</a>.
+ * <br>
+ * The order of tests in this class is aligned with the order of the
+ * requirements in the RFC.
+ */
+public class TestHttp2Section_5_2 extends Http2TestBase {
+
+ /*
+ * Get the connection to a point where 1k of 8k response body has been
+ * read and the flow control for the stream has no capacity left.
+ */
+ @Override
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+
+ http2Connect();
+
+ // Set the default window size to 1024 bytes
+ sendSettings(0, false, new SettingValue(4, 1024));
+ // Wait for the ack
+ parser.readFrame(true);
+ output.clearTrace();
+
+ // Headers + 8k response
+ sendSimpleGetRequest(3);
+
+ // Headers
+ parser.readFrame(true);
+ // First 1k of body
+ parser.readFrame(true);
+ output.clearTrace();
+ }
+
+
+ @Test
+ public void testFlowControlLimits01() throws Exception {
+ readBytes(20);
+ clearRemainder();
+ }
+
+
+ @Test
+ public void testFlowControlLimits02() throws Exception {
+ readBytes(1);
+ readBytes(1);
+ readBytes(1024);
+ readBytes(1);
+ clearRemainder();
+ }
+
+
+ @Test
+ public void testFlowControlLimits03() throws Exception {
+ readBytes(8192,7168);
+ }
+
+
+ @Test
+ public void testFlowControlLimits04() throws Exception {
+ readBytes(7168, 7168, true);
+ }
+
+
+ private void readBytes(int len) throws Exception {
+ readBytes(len, len);
+ }
+
+
+ private void readBytes(int len, int expected) throws Exception {
+ readBytes(len, expected, len > expected);
+ }
+
+
+ private void readBytes(int len, int expected, boolean eos) throws Exception {
+ sendWindowUpdate(3, len);
+ parser.readFrame(true);
+ String expectedTrace = "3-Body-" + expected + "\n";
+ if (eos) {
+ expectedTrace += "3-EndOfStream\n";
+ }
+ Assert.assertEquals(expectedTrace, output.getTrace());
+ output.clearTrace();
+ }
+
+
+ private void clearRemainder() throws Exception {
+ // Remainder
+ sendWindowUpdate(3, 8192);
+ parser.readFrame(true);
+ }
+}
diff --git a/test/org/apache/coyote/http2/TestHttp2Section_5_3.java b/test/org/apache/coyote/http2/TestHttp2Section_5_3.java
new file mode 100644
index 0000000..848acdb
--- /dev/null
+++ b/test/org/apache/coyote/http2/TestHttp2Section_5_3.java
@@ -0,0 +1,249 @@
+/*
+ * 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.util.logging.Level;
+import java.util.logging.LogManager;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Unit tests for Section 5.3 of
+ * <a href="https://tools.ietf.org/html/rfc7540">RFC 7540</a>.
+ * <br>
+ * The order of tests in this class is aligned with the order of the
+ * requirements in the RFC.
+ *
+ * Note: Unit tests for the examples described by each of the figures may be
+ * found in {@link TestAbstractStream}.
+ */
+public class TestHttp2Section_5_3 extends Http2TestBase {
+
+ // Section 5.3.1
+
+ @Test
+ public void testStreamDependsOnSelf() throws Exception {
+ http2Connect();
+
+ sendPriority(3, 3, 15);
+
+ parser.readFrame(true);
+
+ Assert.assertEquals("3-RST-[1]\n", output.getTrace());
+ }
+
+
+ // Section 5.3.2
+
+ @Test
+ public void testWeighting() throws Exception {
+
+ // Temporary debug logging for server side code that allocates capacity
+ // to streams to debug BZ 58659
+ LogManager.getLogManager().getLogger(Http2UpgradeHandler.class.getName()).setLevel(Level.ALL);
+
+ http2Connect();
+
+ // Default connection window size is 64k - 1. Initial request will have
+ // used 8k (56k -1). Increase it to 57k
+ sendWindowUpdate(0, 1 + 1024);
+
+ // Use up 56k of the connection window
+ for (int i = 3; i < 17; i += 2) {
+ sendSimpleGetRequest(i);
+ readSimpleGetResponse();
+ }
+
+ // Set the default window size to 1024 bytes
+ sendSettings(0, false, new SettingValue(4, 1024));
+ // Wait for the ack
+ parser.readFrame(true);
+ // Debugging Gump failure
+ log.info(output.getTrace());
+ output.clearTrace();
+
+ // At this point the connection window should be 1k and any new stream
+ // should have a window of 1k as well
+
+ // Set up streams A=17, B=19, C=21
+ sendPriority(17, 0, 15);
+ sendPriority(19, 17, 3);
+ sendPriority(21, 17, 11);
+
+ // First, process a request on stream 17. This should consume both
+ // stream 17's window and the connection window.
+ sendSimpleGetRequest(17);
+ // 17-headers, 17-1k-body
+ parser.readFrame(true);
+ // Debugging Gump failure
+ log.info(output.getTrace());
+ parser.readFrame(true);
+ // Debugging Gump failure
+ log.info(output.getTrace());
+ output.clearTrace();
+
+ // Send additional requests. Connection window is empty so only headers
+ // will be returned.
+ sendSimpleGetRequest(19);
+ sendSimpleGetRequest(21);
+
+ // Open up the flow control windows for stream 19 & 21 to more than the
+ // size of a simple request (8k)
+ sendWindowUpdate(19, 16*1024);
+ sendWindowUpdate(21, 16*1024);
+
+ // Read some frames
+ // 19-headers, 21-headers
+ parser.readFrame(true);
+ // Debugging Gump failure
+ log.info(output.getTrace());
+ parser.readFrame(true);
+ // Debugging Gump failure
+ log.info(output.getTrace());
+ output.clearTrace();
+
+ // At this point 17 is blocked because the stream window is zero and
+ // 19 & 21 are blocked because the connection window is zero.
+ //
+ // To test allocation, the connection window size is increased by 1.
+ // This should result in an allocation of 1 byte each to streams 19 and
+ // 21 but because each stream is processed in a separate thread it is
+ // not guaranteed that both streams will be blocked when the connection
+ // window size is increased. The test therefore sends 1 byte window
+ // updates until a small body has been seen from each stream. Then the
+ // tests sends a larger (1024 byte) window update and checks that it is
+ // correctly distributed between the streams.
+ //
+ // The test includes a margin to allow for the potential differences in
+ // response caused by timing differences on the server.
+ //
+ // The loop below handles 0, 1 or 2 stream being blocked
+ // - If 0 streams are blocked the connection window will be set to one
+ // and that will be consumed by the first stream to attempt to write.
+ // That body frame will be read by the client. The stream will then be
+ // blocked and the loop will start again.
+ // - If 1 stream is blocked, the connection window will be set to one
+ // which will then be consumed by the blocked stream. After writing
+ // the single byte the stream will again be blocked and the loop will
+ // start again.
+ // - If 2 streams are blocked the connection window will be set to one
+ // but one byte will be permitted for both streams (due to rounding in
+ // the allocation). The window size should be -1 (see below). Two
+ // frames (one for each stream will be written) one of which will be
+ // consumed by the client. The loop will start again and the Window
+ // size incremented to zero. No data will be written by the streams
+ // but the second data frame written in the last iteration of the loop
+ // will be read. The loop will then exit since frames from both
+ // streams will have been observed.
+ boolean seen19 = false;
+ boolean seen21 = false;
+ while (!seen19 || !seen21) {
+ sendWindowUpdate(0, 1);
+ parser.readFrame(true);
+ // Debugging Gump failure
+ log.info(output.getTrace());
+ int[] data = parseBodyFrame(output.getTrace());
+ if (data[0] == 19) {
+ seen19 = true;
+ } else if (data[0] == 21) {
+ seen21 = true;
+ } else {
+ // Unexpected stream
+ Assert.fail("Unexpected stream: [" + output.getTrace() + "]");
+ }
+ if (data[1] > 10) {
+ // Larger than expected body size
+ Assert.fail("Larger than expected body: [" + output.getTrace() + "]");
+ }
+ output.clearTrace();
+ }
+
+ sendWindowUpdate(0, 1024);
+ parser.readFrame(true);
+
+ // Make sure you have read the big comment before the loop above. It is
+ // possible that the timing of the server threads is such that there are
+ // still small body frames to read.
+ int[] data = parseBodyFrame(output.getTrace());
+ while (data[1] < 10) {
+ // Debugging Gump failure
+ log.info(output.getTrace());
+ output.clearTrace();
+ parser.readFrame(true);
+ data = parseBodyFrame(output.getTrace());
+ }
+
+ // Should now have two larger body frames. One has already been read.
+ seen19 = false;
+ seen21 = false;
+ while (!seen19 && !seen21) {
+ // Debugging Gump failure
+ log.info(output.getTrace());
+ if (data[0] == 19) {
+ seen19 = true;
+ // If everything works instantly this should be 256 but allow a
+ // fairly large margin for timing differences
+ if (data[1] < 236 || data[1] > 276) {
+ Assert.fail("Unexpected body size: [" + output.getTrace() + "]");
+ }
+ } else if (data[0] == 21) {
+ seen21 = true;
+ // If everything works instantly this should be 768 but allow a
+ // fairly large margin for timing differences
+ if (data[1] < 748 || data[1] > 788) {
+ Assert.fail("Unexpected body size: [" + output.getTrace() + "]");
+ }
+ } else {
+ Assert.fail("Unexpected stream: [" + output.getTrace() + "]");
+ }
+ output.clearTrace();
+ parser.readFrame(true);
+ data = parseBodyFrame(output.getTrace());
+ }
+ // Debugging Gump failure
+ log.info(output.getTrace());
+ output.clearTrace();
+
+ // Release everything and read all the remaining data
+ sendWindowUpdate(0, 1024 * 1024);
+ sendWindowUpdate(17, 1024 * 1024);
+
+ // Read remaining frames
+ // 17-7k-body, 19~8k-body, 21~8k-body
+ for (int i = 0; i < 3; i++) {
+ parser.readFrame(true);
+ // Debugging Gump failure
+ log.info(output.getTrace());
+ }
+ }
+
+
+ private int[] parseBodyFrame(String output) {
+ String[] parts = output.trim().split("-");
+ if (parts.length != 3 || !"Body".equals(parts[1])) {
+ Assert.fail("Unexpected output: [" + output + "]");
+ }
+
+ int[] result = new int[2];
+
+ result[0] = Integer.parseInt(parts[0]);
+ result[1] = Integer.parseInt(parts[2]);
+
+ return result;
+ }
+}
diff --git a/test/org/apache/coyote/http2/TestHttp2Section_5_5.java b/test/org/apache/coyote/http2/TestHttp2Section_5_5.java
new file mode 100644
index 0000000..ec212e1
--- /dev/null
+++ b/test/org/apache/coyote/http2/TestHttp2Section_5_5.java
@@ -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.coyote.http2;
+
+import java.nio.ByteBuffer;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Unit tests for Section 5.5 of
+ * <a href="https://tools.ietf.org/html/rfc7540">RFC 7540</a>.
+ * <br>
+ * The order of tests in this class is aligned with the order of the
+ * requirements in the RFC.
+ */
+public class TestHttp2Section_5_5 extends Http2TestBase {
+
+ private static final byte[] UNKNOWN_FRAME;
+
+ static {
+ // Unknown frame type
+ UNKNOWN_FRAME = new byte[29];
+ // Frame header
+ ByteUtil.setThreeBytes(UNKNOWN_FRAME, 0, 20);
+ // Type
+ UNKNOWN_FRAME[3] = (byte) 0x80;
+ // No flags
+ // Stream
+ ByteUtil.set31Bits(UNKNOWN_FRAME, 5, 5);
+ // zero payload
+ }
+
+
+ // Section 5.5
+
+ @Test
+ public void testUnknownSetting() throws Exception {
+ http2Connect();
+
+ // Unknown setting (should be ack'd)
+ sendSettings(0, false, new SettingValue(1 << 15, 0));
+
+ parser.readFrame(true);
+
+ Assert.assertEquals("0-Settings-Ack\n", output.getTrace());
+ }
+
+
+ @Test
+ public void testUnknownFrame() throws Exception {
+ http2Connect();
+
+ os.write(UNKNOWN_FRAME);
+ os.flush();
+
+ // Ping
+ sendPing();
+
+ parser.readFrame(true);
+
+ Assert.assertEquals("0-Ping-Ack-[0,0,0,0,0,0,0,0]\n", output.getTrace());
+ }
+
+
+ @Test
+ public void testNonContiguousHeaderWithUnknownFrame() throws Exception {
+ // HTTP2 upgrade
+ http2Connect();
+
+ // Part 1
+ byte[] frameHeader = new byte[9];
+ ByteBuffer headersPayload = ByteBuffer.allocate(128);
+ buildSimpleGetRequestPart1(frameHeader, headersPayload, 3);
+ writeFrame(frameHeader, headersPayload);
+
+ os.write(UNKNOWN_FRAME);
+ os.flush();
+
+ // Read GOAWAY frame
+ parser.readFrame(true);
+
+ Assert.assertTrue(output.getTrace(), output.getTrace().startsWith(
+ "0-Goaway-[1]-[" + Http2Error.COMPRESSION_ERROR.getCode() + "]-["));
+ }
+
+}
diff --git a/test/org/apache/coyote/http2/TestHttp2Section_6_1.java b/test/org/apache/coyote/http2/TestHttp2Section_6_1.java
new file mode 100644
index 0000000..99fd9ce
--- /dev/null
+++ b/test/org/apache/coyote/http2/TestHttp2Section_6_1.java
@@ -0,0 +1,168 @@
+/*
+ * 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;
+
+/**
+ * Unit tests for Section 6.1 of
+ * <a href="https://tools.ietf.org/html/rfc7540">RFC 7540</a>.
+ * <br>
+ * The order of tests in this class is aligned with the order of the
+ * requirements in the RFC.
+ */
+public class TestHttp2Section_6_1 extends Http2TestBase {
+
+ @Test
+ public void testDataFrame() throws Exception {
+ http2Connect();
+
+ sendSimplePostRequest(3, null);
+ readSimplePostResponse(false);
+
+ Assert.assertEquals("0-WindowSize-[128]\n" +
+ "3-WindowSize-[128]\n" +
+ "3-HeadersStart\n" +
+ "3-Header-[:status]-[200]\n" +
+ "3-Header-[date]-[Wed, 11 Nov 2015 19:18:42 GMT]\n" +
+ "3-HeadersEnd\n" +
+ "3-Body-128\n" +
+ "3-EndOfStream\n", output.getTrace());
+ }
+
+
+ @Test
+ public void testDataFrameWithPadding() throws Exception {
+ http2Connect();
+
+ byte[] padding = new byte[8];
+
+ sendSimplePostRequest(3, padding);
+ readSimplePostResponse(true);
+
+
+ // The window update for the padding could occur anywhere since it
+ // happens on a different thead to the response.
+ String trace = output.getTrace();
+ String paddingWindowUpdate = "0-WindowSize-[9]\n3-WindowSize-[9]\n";
+
+ Assert.assertTrue(trace, trace.contains(paddingWindowUpdate));
+ trace = trace.replace(paddingWindowUpdate, "");
+
+ Assert.assertEquals("0-WindowSize-[119]\n" +
+ "3-WindowSize-[119]\n" +
+ "3-HeadersStart\n" +
+ "3-Header-[:status]-[200]\n" +
+ "3-Header-[date]-[Wed, 11 Nov 2015 19:18:42 GMT]\n" +
+ "3-HeadersEnd\n" +
+ "3-Body-119\n" +
+ "3-EndOfStream\n", trace);
+ }
+
+
+ @Test
+ public void testDataFrameWithNonZeroPadding() throws Exception {
+ http2Connect();
+
+ byte[] padding = new byte[8];
+ padding[4] = 0x01;
+
+ sendSimplePostRequest(3, padding);
+ parser.readFrame(true);
+ // May see Window updates depending on timing
+ while (output.getTrace().contains("WindowSize")) {
+ output.clearTrace();
+ parser.readFrame(true);
+ }
+
+ String trace = output.getTrace();
+ Assert.assertTrue(trace, trace.startsWith("0-Goaway-[3]-[1]-["));
+ }
+
+
+ @Test
+ public void testDataFrameOnStreamZero() throws Exception {
+ http2Connect();
+
+ byte[] dataFrame = new byte[10];
+
+ // Header
+ // length
+ ByteUtil.setThreeBytes(dataFrame, 0, 1);
+ // type (0 for data)
+ // flags (0)
+ // stream (0)
+ // payload (0)
+
+ os.write(dataFrame);
+ os.flush();
+
+ parser.readFrame(true);
+
+ String trace = output.getTrace();
+ Assert.assertTrue(trace, trace.startsWith("0-Goaway-[1]-[1]-["));
+ }
+
+
+ @Test
+ public void testDataFrameTooMuchPadding() throws Exception {
+ http2Connect();
+
+ byte[] dataFrame = new byte[10];
+
+ // Header
+ // length
+ ByteUtil.setThreeBytes(dataFrame, 0, 1);
+ // type 0 (data)
+ // flags 8 (padded)
+ dataFrame[4] = 0x08;
+ // stream 3
+ ByteUtil.set31Bits(dataFrame, 5, 3);
+ // payload (pad length of 1)
+ dataFrame[9] = 1;
+
+ os.write(dataFrame);
+ os.flush();
+
+ parser.readFrame(true);
+
+ String trace = output.getTrace();
+ Assert.assertTrue(trace, trace.startsWith("0-Goaway-[1]-[1]-["));
+ }
+
+
+ @Test
+ public void testDataFrameWithZeroLengthPadding() throws Exception {
+ http2Connect();
+
+ byte[] padding = new byte[0];
+
+ sendSimplePostRequest(3, padding);
+ // Since padding is zero length, response looks like there is none.
+ readSimplePostResponse(false);
+
+ Assert.assertEquals("0-WindowSize-[127]\n" +
+ "3-WindowSize-[127]\n" +
+ "3-HeadersStart\n" +
+ "3-Header-[:status]-[200]\n" +
+ "3-Header-[date]-[Wed, 11 Nov 2015 19:18:42 GMT]\n" +
+ "3-HeadersEnd\n" +
+ "3-Body-127\n" +
+ "3-EndOfStream\n", output.getTrace());
+ }
+}
diff --git a/test/org/apache/coyote/http2/TestHttp2Section_6_2.java b/test/org/apache/coyote/http2/TestHttp2Section_6_2.java
new file mode 100644
index 0000000..e18e90f
--- /dev/null
+++ b/test/org/apache/coyote/http2/TestHttp2Section_6_2.java
@@ -0,0 +1,118 @@
+/*
+ * 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.nio.ByteBuffer;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Unit tests for Section 6.2 of
+ * <a href="https://tools.ietf.org/html/rfc7540">RFC 7540</a>.
+ * <br>
+ * The order of tests in this class is aligned with the order of the
+ * requirements in the RFC.
+ */
+public class TestHttp2Section_6_2 extends Http2TestBase {
+
+ @Test
+ public void testHeaderFrameOnStreamZero() throws Exception {
+ // HTTP2 upgrade
+ http2Connect();
+
+ // Part 1
+ byte[] frameHeader = new byte[9];
+ ByteBuffer headersPayload = ByteBuffer.allocate(128);
+ buildSimpleGetRequestPart1(frameHeader, headersPayload, 0);
+ writeFrame(frameHeader, headersPayload);
+
+ // Go away
+ parser.readFrame(true);
+
+ Assert.assertTrue(output.getTrace(), output.getTrace().startsWith(
+ "0-Goaway-[1]-[" + Http2Error.PROTOCOL_ERROR.getCode() + "]-["));
+ }
+
+
+ @Test
+ public void testHeaderFrameWithPadding() throws Exception {
+ http2Connect();
+
+ byte[] padding= new byte[8];
+
+ sendSimpleGetRequest(3, padding);
+ readSimpleGetResponse();
+ Assert.assertEquals(getSimpleResponseTrace(3), output.getTrace());
+ }
+
+
+ @Test
+ public void testHeaderFrameWithNonZeroPadding() throws Exception {
+ http2Connect();
+
+ byte[] padding= new byte[8];
+ padding[4] = 1;
+
+ sendSimpleGetRequest(3, padding);
+
+ // Goaway
+ parser.readFrame(true);
+
+ Assert.assertTrue(output.getTrace(), output.getTrace().startsWith(
+ "0-Goaway-[1]-[" + Http2Error.PROTOCOL_ERROR.getCode() + "]-["));
+ }
+
+
+ @Test
+ public void testHeaderFrameTooMuchPadding() throws Exception {
+ http2Connect();
+
+ byte[] headerFrame = new byte[10];
+
+ // Header
+ // length
+ ByteUtil.setThreeBytes(headerFrame, 0, 1);
+ headerFrame[3] = FrameType.HEADERS.getIdByte();
+ // flags 8 (padded)
+ headerFrame[4] = 0x08;
+ // stream 3
+ ByteUtil.set31Bits(headerFrame, 5, 3);
+ // payload (pad length of 1)
+ headerFrame[9] = 1;
+
+ os.write(headerFrame);
+ os.flush();
+
+ parser.readFrame(true);
+
+ String trace = output.getTrace();
+ Assert.assertTrue(trace, trace.startsWith("0-Goaway-[1]-[1]-["));
+ }
+
+
+ @Test
+ public void testHeaderFrameWithZeroLengthPadding() throws Exception {
+ http2Connect();
+
+ byte[] padding= new byte[0];
+
+ sendSimpleGetRequest(3, padding);
+ readSimpleGetResponse();
+ Assert.assertEquals(getSimpleResponseTrace(3), output.getTrace());
+ }
+}
diff --git a/test/org/apache/coyote/http2/TestHttp2Section_6_3.java b/test/org/apache/coyote/http2/TestHttp2Section_6_3.java
new file mode 100644
index 0000000..7b49d14
--- /dev/null
+++ b/test/org/apache/coyote/http2/TestHttp2Section_6_3.java
@@ -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.coyote.http2;
+
+import java.nio.ByteBuffer;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Unit tests for Section 6.3 of
+ * <a href="https://tools.ietf.org/html/rfc7540">RFC 7540</a>.
+ * <br>
+ * The order of tests in this class is aligned with the order of the
+ * requirements in the RFC.
+ */
+public class TestHttp2Section_6_3 extends Http2TestBase {
+
+ @Test
+ public void testPriorityFrameOnStreamZero() throws Exception {
+ // HTTP2 upgrade
+ http2Connect();
+
+ sendPriority(0, 1, 15);
+
+ // Go away
+ parser.readFrame(true);
+
+ Assert.assertTrue(output.getTrace(), output.getTrace().startsWith(
+ "0-Goaway-[1]-[" + Http2Error.PROTOCOL_ERROR.getCode() + "]-["));
+ }
+
+
+ @Test
+ public void testPriorityFrameBetweenHeaderFrames() throws Exception {
+ // HTTP2 upgrade
+ http2Connect();
+
+ // Part 1
+ byte[] frameHeader = new byte[9];
+ ByteBuffer headersPayload = ByteBuffer.allocate(128);
+ buildSimpleGetRequestPart1(frameHeader, headersPayload, 3);
+ writeFrame(frameHeader, headersPayload);
+
+ sendPriority(5, 3, 15);
+
+ // Read GOAWAY frame
+ parser.readFrame(true);
+
+ Assert.assertTrue(output.getTrace(), output.getTrace().startsWith(
+ "0-Goaway-[1]-[" + Http2Error.COMPRESSION_ERROR.getCode() + "]-["));
+ }
+
+
+ @Test
+ public void testPriorityFrameWrongLength() throws Exception {
+ // HTTP2 upgrade
+ http2Connect();
+
+ byte[] priorityFrame = new byte[10];
+ // length
+ ByteUtil.setThreeBytes(priorityFrame, 0, 1);
+ // type
+ priorityFrame[3] = FrameType.PRIORITY.getIdByte();
+ // No flags
+ // Stream ID
+ ByteUtil.set31Bits(priorityFrame, 5, 3);
+
+ // Payload - left as zero
+
+ os.write(priorityFrame);
+ os.flush();
+
+ // Read GOAWAY frame
+ parser.readFrame(true);
+
+ Assert.assertEquals("3-RST-[" + Http2Error.FRAME_SIZE_ERROR.getCode() + "]\n",
+ output.getTrace());
+ }
+}
diff --git a/test/org/apache/coyote/http2/TestHttp2Section_6_4.java b/test/org/apache/coyote/http2/TestHttp2Section_6_4.java
new file mode 100644
index 0000000..5a0d548
--- /dev/null
+++ b/test/org/apache/coyote/http2/TestHttp2Section_6_4.java
@@ -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.coyote.http2;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Unit tests for Section 6.4 of
+ * <a href="https://tools.ietf.org/html/rfc7540">RFC 7540</a>.
+ * <br>
+ * The order of tests in this class is aligned with the order of the
+ * requirements in the RFC.
+ */
+public class TestHttp2Section_6_4 extends Http2TestBase {
+
+ @Test
+ public void testResetFrameOnStreamZero() throws Exception {
+ // HTTP2 upgrade
+ http2Connect();
+
+ sendRst(0, Http2Error.NO_ERROR.getCode());
+
+ // Go away
+ parser.readFrame(true);
+
+ Assert.assertTrue(output.getTrace(), output.getTrace().startsWith(
+ "0-Goaway-[1]-[" + Http2Error.PROTOCOL_ERROR.getCode() + "]-["));
+ }
+
+
+ @Test
+ public void testResetFrameOnIdleStream() throws Exception {
+ // HTTP2 upgrade
+ http2Connect();
+
+ sendPriority(3, 0, 15);
+ sendRst(3, Http2Error.NO_ERROR.getCode());
+
+ // Go away
+ parser.readFrame(true);
+
+ Assert.assertTrue(output.getTrace(), output.getTrace().startsWith(
+ "0-Goaway-[1]-[" + Http2Error.PROTOCOL_ERROR.getCode() + "]-["));
+ }
+
+
+ @Test
+ public void testResetFrameWrongLength() throws Exception {
+ // HTTP2 upgrade
+ http2Connect();
+
+ byte[] resetFrame = new byte[10];
+ // length
+ ByteUtil.setThreeBytes(resetFrame, 0, 1);
+ // type
+ resetFrame[3] = FrameType.RST.getIdByte();
+ // No flags
+ // Stream ID
+ ByteUtil.set31Bits(resetFrame, 5, 3);
+
+ // Payload - left as zero
+
+ os.write(resetFrame);
+ os.flush();
+
+ // Read GOAWAY frame
+ parser.readFrame(true);
+
+ Assert.assertEquals("3-RST-[" + Http2Error.FRAME_SIZE_ERROR.getCode() + "]\n",
+ output.getTrace());
+ }
+}
diff --git a/test/org/apache/coyote/http2/TestHttp2Section_6_5.java b/test/org/apache/coyote/http2/TestHttp2Section_6_5.java
new file mode 100644
index 0000000..c2b102d
--- /dev/null
+++ b/test/org/apache/coyote/http2/TestHttp2Section_6_5.java
@@ -0,0 +1,151 @@
+/*
+ * 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;
+
+/**
+ * Unit tests for Section 6.5 of
+ * <a href="https://tools.ietf.org/html/rfc7540">RFC 7540</a>.
+ * <br>
+ * The order of tests in this class is aligned with the order of the
+ * requirements in the RFC.
+ */
+public class TestHttp2Section_6_5 extends Http2TestBase {
+
+
+ @Test
+ public void testSettingsFrameNonEmptAck() throws Exception {
+ // HTTP2 upgrade
+ http2Connect();
+
+ sendSettings(0, true, new SettingValue(1,1));
+
+ // Go away
+ parser.readFrame(true);
+
+ Assert.assertTrue(output.getTrace(), output.getTrace().startsWith(
+ "0-Goaway-[1]-[" + Http2Error.FRAME_SIZE_ERROR.getCode() + "]-["));
+ }
+
+
+ @Test
+ public void testSettingsFrameNonZeroStream() throws Exception {
+ // HTTP2 upgrade
+ http2Connect();
+
+ sendPriority(3, 0, 15);
+ sendSettings(3, true, new SettingValue(1,1));
+
+ // Go away
+ parser.readFrame(true);
+
+ Assert.assertTrue(output.getTrace(), output.getTrace().startsWith(
+ "0-Goaway-[1]-[" + Http2Error.PROTOCOL_ERROR.getCode() + "]-["));
+ }
+
+
+ @Test
+ public void testSettingsFrameWrongLength() throws Exception {
+ // HTTP2 upgrade
+ http2Connect();
+
+ byte[] resetFrame = new byte[10];
+ // length
+ ByteUtil.setThreeBytes(resetFrame, 0, 1);
+ // type
+ resetFrame[3] = FrameType.SETTINGS.getIdByte();
+ // No flags
+ // Stream ID 0
+
+ // Payload - left as zero
+
+ os.write(resetFrame);
+ os.flush();
+
+ // Read GOAWAY frame
+ parser.readFrame(true);
+
+ Assert.assertTrue(output.getTrace(), output.getTrace().startsWith(
+ "0-Goaway-[1]-[" + Http2Error.FRAME_SIZE_ERROR.getCode() + "]-["));
+ }
+
+
+ // Need to test sending push promise when push promise suport is disabled
+
+ @Test
+ public void testSettingsFrameInvalidPushSetting() throws Exception {
+ // HTTP2 upgrade
+ http2Connect();
+
+ sendSettings(0, false, new SettingValue(0x2,0x2));
+
+ // Go away
+ parser.readFrame(true);
+
+ Assert.assertTrue(output.getTrace(), output.getTrace().startsWith(
+ "0-Goaway-[1]-[" + Http2Error.PROTOCOL_ERROR.getCode() + "]-["));
+ }
+
+
+ @Test
+ public void testSettingsFrameInvalidWindowSizeSetting() throws Exception {
+ // HTTP2 upgrade
+ http2Connect();
+
+ sendSettings(0, false, new SettingValue(0x4,1 << 31));
+
+ // Go away
+ parser.readFrame(true);
+
+ Assert.assertTrue(output.getTrace(), output.getTrace().startsWith(
+ "0-Goaway-[1]-[" + Http2Error.FLOW_CONTROL_ERROR.getCode() + "]-["));
+ }
+
+
+ @Test
+ public void testSettingsFrameInvalidMaxFrameSizeSetting() throws Exception {
+ // HTTP2 upgrade
+ http2Connect();
+
+ sendSettings(0, false, new SettingValue(0x5,1 << 31));
+
+ // Go away
+ parser.readFrame(true);
+
+ Assert.assertTrue(output.getTrace(), output.getTrace().startsWith(
+ "0-Goaway-[1]-[" + Http2Error.PROTOCOL_ERROR.getCode() + "]-["));
+ }
+
+
+ @Test
+ public void testSettingsUnknownSetting() throws Exception {
+ // HTTP2 upgrade
+ http2Connect();
+
+ sendSettings(0, false, new SettingValue(0xFF,0xFF));
+
+ // Ack
+ parser.readFrame(true);
+
+ Assert.assertTrue(output.getTrace(), output.getTrace().startsWith(
+ "0-Settings-Ack"));
+ }
+
+ // delayed ACKs. Requires an API (TBD) for applications to send settings.
+}
diff --git a/test/org/apache/coyote/http2/TestHttp2Section_6_7.java b/test/org/apache/coyote/http2/TestHttp2Section_6_7.java
new file mode 100644
index 0000000..a7fe7d5
--- /dev/null
+++ b/test/org/apache/coyote/http2/TestHttp2Section_6_7.java
@@ -0,0 +1,92 @@
+/*
+ * 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.nio.charset.StandardCharsets;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Unit tests for Section 6.7 of
+ * <a href="https://tools.ietf.org/html/rfc7540">RFC 7540</a>.
+ * <br>
+ * The order of tests in this class is aligned with the order of the
+ * requirements in the RFC.
+ */
+public class TestHttp2Section_6_7 extends Http2TestBase {
+
+
+ @Test
+ public void testPingFrame() throws Exception {
+ // HTTP2 upgrade
+ http2Connect();
+
+ sendPing(0, false, "01234567".getBytes(StandardCharsets.ISO_8859_1));
+
+ // Ping ack
+ parser.readFrame(true);
+
+ Assert.assertEquals("0-Ping-Ack-[48,49,50,51,52,53,54,55]\n", output.getTrace());
+ }
+
+
+ @Test
+ public void testPingFrameUnexpectedAck() throws Exception {
+ // HTTP2 upgrade
+ http2Connect();
+
+ sendPing(0, true, "01234567".getBytes(StandardCharsets.ISO_8859_1));
+ sendPing(0, false, "76543210".getBytes(StandardCharsets.ISO_8859_1));
+
+ // Ping ack (only for second ping)
+ parser.readFrame(true);
+
+ Assert.assertEquals("0-Ping-Ack-[55,54,53,52,51,50,49,48]\n", output.getTrace());
+ }
+
+
+ @Test
+ public void testPingFrameNonZeroStream() throws Exception {
+ // HTTP2 upgrade
+ http2Connect();
+
+ sendPing(1, false, "76543210".getBytes(StandardCharsets.ISO_8859_1));
+
+ // Go away
+ parser.readFrame(true);
+
+ Assert.assertTrue(output.getTrace(), output.getTrace().startsWith(
+ "0-Goaway-[1]-[" + Http2Error.PROTOCOL_ERROR.getCode() + "]-["));
+ }
+
+
+ @Test
+ public void testPingFrameWrongPayloadSize() throws Exception {
+ // HTTP2 upgrade
+ http2Connect();
+
+ sendPing(0, false, "6543210".getBytes(StandardCharsets.ISO_8859_1));
+
+ // Go away
+ parser.readFrame(true);
+
+ Assert.assertTrue(output.getTrace(), output.getTrace().startsWith(
+ "0-Goaway-[1]-[" + Http2Error.FRAME_SIZE_ERROR.getCode() + "]-["));
+ }
+
+}
diff --git a/test/org/apache/coyote/http2/TestHttp2Section_6_8.java b/test/org/apache/coyote/http2/TestHttp2Section_6_8.java
new file mode 100644
index 0000000..823d696
--- /dev/null
+++ b/test/org/apache/coyote/http2/TestHttp2Section_6_8.java
@@ -0,0 +1,104 @@
+/*
+ * 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;
+
+import org.apache.catalina.connector.Connector;
+
+/**
+ * Unit tests for Section 6.8 of
+ * <a href="https://tools.ietf.org/html/rfc7540">RFC 7540</a>.
+ * <br>
+ * The order of tests in this class is aligned with the order of the
+ * requirements in the RFC.
+ */
+public class TestHttp2Section_6_8 extends Http2TestBase {
+
+ private static final boolean RELAX_TIMING = Boolean.getBoolean("tomcat.test.relaxTiming");
+
+ private static final long PNG_ACK_DELAY_MS = 2000;
+ // On slow systems (Gump) may need to be higher
+ private static final long TIMING_MARGIN_MS = RELAX_TIMING ? 1000 : 200;
+
+ @Test
+ public void testGoawayIgnoreNewStreams() throws Exception {
+ setPingAckDelayMillis(PNG_ACK_DELAY_MS);
+
+ // HTTP2 upgrade - need longer timeouts for this test
+ Connector connector = getTomcatInstance().getConnector();
+ Http2Protocol http2Protocol = new Http2Protocol();
+ // Short timeouts for now. May need to increase these for CI systems.
+ http2Protocol.setReadTimeout(5000);
+ http2Protocol.setKeepAliveTimeout(10000);
+ http2Protocol.setWriteTimeout(5000);
+ http2Protocol.setMaxConcurrentStreams(200);
+ connector.addUpgradeProtocol(http2Protocol);
+ configureAndStartWebApplication();
+ openClientConnection();
+ doHttpUpgrade();
+ sendClientPreface();
+ validateHttp2InitialResponse();
+
+ Thread.sleep(PNG_ACK_DELAY_MS + TIMING_MARGIN_MS);
+
+ getTomcatInstance().getConnector().pause();
+
+ // Go away
+ parser.readFrame(true);
+ Assert.assertEquals("0-Goaway-[2147483647]-[0]-[null]", output.getTrace());
+ output.clearTrace();
+
+ // Should be processed
+ sendSimpleGetRequest(3);
+
+ Thread.sleep(PNG_ACK_DELAY_MS + TIMING_MARGIN_MS);
+
+ // Should be ignored
+ sendSimpleGetRequest(5);
+
+ parser.readFrame(true);
+ parser.readFrame(true);
+
+ Assert.assertEquals(getSimpleResponseTrace(3), output.getTrace());
+ output.clearTrace();
+
+ // Finally the go away frame
+ parser.readFrame(true);
+ Assert.assertEquals("0-Goaway-[3]-[0]-[null]", output.getTrace());
+ }
+
+
+ @Test
+ public void testGoawayFrameNonZeroStream() throws Exception {
+ // HTTP2 upgrade
+ http2Connect();
+
+ sendGoaway(1, 1, Http2Error.NO_ERROR.getCode(), null);
+
+ // Go away
+ parser.readFrame(true);
+
+ Assert.assertTrue(output.getTrace(), output.getTrace().startsWith(
+ "0-Goaway-[1]-[" + Http2Error.PROTOCOL_ERROR.getCode() + "]-["));
+ }
+
+
+ // TODO Test header processing and window size processing for ignored
+ // streams
+}
diff --git a/test/org/apache/coyote/http2/TestHttp2Section_6_9.java b/test/org/apache/coyote/http2/TestHttp2Section_6_9.java
new file mode 100644
index 0000000..b277c84
--- /dev/null
+++ b/test/org/apache/coyote/http2/TestHttp2Section_6_9.java
@@ -0,0 +1,290 @@
+/*
+ * 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.nio.ByteBuffer;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Unit tests for Section 6.9 of
+ * <a href="https://tools.ietf.org/html/rfc7540">RFC 7540</a>.
+ * <br>
+ * The order of tests in this class is aligned with the order of the
+ * requirements in the RFC.
+ */
+public class TestHttp2Section_6_9 extends Http2TestBase {
+
+ @Test
+ public void testZeroWindowUpdateConnection() throws Exception {
+ http2Connect();
+
+ sendWindowUpdate(0, 0);
+
+ parser.readFrame(true);
+
+ Assert.assertTrue(output.getTrace(), output.getTrace().startsWith(
+ "0-Goaway-[1]-[" + Http2Error.PROTOCOL_ERROR.getCode() + "]-["));
+ }
+
+
+ @Test
+ public void testZeroWindowUpdateStream() throws Exception {
+ http2Connect();
+
+ sendSimplePostRequest(3, null, false);
+ sendWindowUpdate(3, 0);
+
+ parser.readFrame(true);
+
+ Assert.assertEquals("3-RST-[" + Http2Error.PROTOCOL_ERROR.getCode() + "]\n",
+ output.getTrace());
+ }
+
+
+ @Test
+ public void testWindowUpdateOnClosedStream() throws Exception {
+ http2Connect();
+
+ // Should not be an error so should be nothing to read
+ sendWindowUpdate(1, 200);
+
+ // So the next request should process normally
+ sendSimpleGetRequest(3);
+ readSimpleGetResponse();
+ Assert.assertEquals(getSimpleResponseTrace(3), output.getTrace());
+ }
+
+
+ // TODO: Test always accounting for changes in flow control windows even if
+ // the frame is in error.
+
+
+ @Test
+ public void testWindowUpdateWrongLength() throws Exception {
+ http2Connect();
+
+ byte[] zeroLengthWindowFrame = new byte[9];
+ // Length zero
+ ByteUtil.setOneBytes(zeroLengthWindowFrame, 3, FrameType.WINDOW_UPDATE.getIdByte());
+ // No flags
+ // Stream 1
+ ByteUtil.set31Bits(zeroLengthWindowFrame, 5, 1);
+
+ os.write(zeroLengthWindowFrame);
+ os.flush();
+
+ parser.readFrame(true);
+
+ Assert.assertTrue(output.getTrace(), output.getTrace().startsWith(
+ "0-Goaway-[1]-[" + Http2Error.FRAME_SIZE_ERROR.getCode() + "]-["));
+ }
+
+
+ @Test
+ public void testEmptyDataFrameWithNoAvailableFlowControl() throws Exception {
+ http2Connect();
+
+ // Default connection window size is 64k - 1. Initial request will have
+ // used 8k (56k -1).
+
+ // Use up the remaining connection window. These requests require 56k
+ // but there is only 56k - 1 available.
+ for (int i = 3; i < 17; i += 2) {
+ sendSimpleGetRequest(i);
+ readSimpleGetResponse();
+ }
+ output.clearTrace();
+
+ // It should be possible to send a request that generates an empty
+ // response at this point
+ sendEmptyGetRequest(17);
+ // Headers
+ parser.readFrame(true);
+ // Body
+ parser.readFrame(true);
+
+ // Release Stream 15 which is waiting for a single byte.
+ sendWindowUpdate(0, 1024);
+
+ Assert.assertEquals(getEmptyResponseTrace(17), output.getTrace());
+ }
+
+
+ @Test
+ public void testWindowSizeTooLargeStream() throws Exception {
+ http2Connect();
+
+ // Set up stream 3
+ sendSimplePostRequest(3, null, false);
+
+ // Super size the flow control window.
+ sendWindowUpdate(3, (1 << 31) - 1);
+
+ parser.readFrame(true);
+
+ Assert.assertEquals("3-RST-[" + Http2Error.FLOW_CONTROL_ERROR.getCode() + "]\n",
+ output.getTrace());
+ }
+
+
+ @Test
+ public void testWindowSizeTooLargeConnection() throws Exception {
+ http2Connect();
+
+ // Super size the flow control window.
+ sendWindowUpdate(0, (1 << 31) - 1);
+
+ parser.readFrame(true);
+
+ Assert.assertTrue(output.getTrace(), output.getTrace().startsWith(
+ "0-Goaway-[1]-[" + Http2Error.FLOW_CONTROL_ERROR.getCode() + "]-["));
+ }
+
+
+ @Test
+ public void testWindowSizeAndSettingsFrame() throws Exception {
+ http2Connect();
+
+ // Set up a POST request that echoes the body back
+ byte[] headersFrameHeader = new byte[9];
+ ByteBuffer headersPayload = ByteBuffer.allocate(128);
+ byte[] dataFrameHeader = new byte[9];
+ ByteBuffer dataPayload = ByteBuffer.allocate(8 * 1024);
+
+ buildPostRequest(headersFrameHeader, headersPayload, false,
+ dataFrameHeader, dataPayload, null, 3);
+
+ // Write the headers
+ writeFrame(headersFrameHeader, headersPayload);
+
+ // Now use a settings frame to reduce the size of the flow control
+ // window.
+ sendSettings(0, false, new SettingValue(4, 4 * 1024));
+ // Ack
+ parser.readFrame(true);
+ Assert.assertEquals("0-Settings-Ack\n", output.getTrace());
+ output.clearTrace();
+
+ // Write the body
+ writeFrame(dataFrameHeader, dataPayload);
+
+ // Window size updates after reading POST body
+ parser.readFrame(true);
+ parser.readFrame(true);
+ Assert.assertEquals(
+ "0-WindowSize-[8192]\n" +
+ "3-WindowSize-[8192]\n",
+ output.getTrace());
+ output.clearTrace();
+
+ // Read stream 3 headers and first part of body
+ parser.readFrame(true);
+ parser.readFrame(true);
+ Assert.assertEquals(
+ "3-HeadersStart\n" +
+ "3-Header-[:status]-[200]\n" +
+ "3-Header-[date]-["+ DEFAULT_DATE + "]\n" +
+ "3-HeadersEnd\n" +
+ "3-Body-4096\n", output.getTrace());
+ output.clearTrace();
+
+ // Now use a settings frame to further reduce the size of the flow
+ // control window. This should make the stream 3 window negative
+ sendSettings(0, false, new SettingValue(4, 2 * 1024));
+ // Ack
+ parser.readFrame(true);
+ Assert.assertEquals("0-Settings-Ack\n", output.getTrace());
+ output.clearTrace();
+
+ // Now use a settings frame to increase the size of the flow control
+ // window. The stream 3 window should still be negative
+ sendSettings(0, false, new SettingValue(4, 3 * 1024));
+ // Ack
+ parser.readFrame(true);
+ Assert.assertEquals("0-Settings-Ack\n", output.getTrace());
+ output.clearTrace();
+
+ // Do a POST that won't be affected by the above limit
+ sendSimplePostRequest(5, null);
+ // Window size updates after reading POST body
+ parser.readFrame(true);
+ parser.readFrame(true);
+ Assert.assertEquals(
+ "0-WindowSize-[128]\n" +
+ "5-WindowSize-[128]\n",
+ output.getTrace());
+ output.clearTrace();
+ // Headers + body
+ parser.readFrame(true);
+ parser.readFrame(true);
+ Assert.assertEquals(
+ "5-HeadersStart\n" +
+ "5-Header-[:status]-[200]\n" +
+ "5-Header-[date]-[Wed, 11 Nov 2015 19:18:42 GMT]\n" +
+ "5-HeadersEnd\n" +
+ "5-Body-128\n" +
+ "5-EndOfStream\n", output.getTrace());
+ output.clearTrace();
+
+ // Now use a settings frame to restore the size of the flow control
+ // window.
+ sendSettings(0, false, new SettingValue(4, 64 * 1024 - 1));
+
+ // Settings ack and stream 3 body are written from different threads.
+ // Order depends on server side timing. Handle both possibilities.
+ parser.readFrame(true);
+ String trace = output.getTrace();
+ String settingsAck = "0-Settings-Ack\n";
+ String endOfStreamThree = "3-Body-4096\n3-EndOfStream\n";
+
+ if (settingsAck.equals(trace)) {
+ // Ack the end of stream 3
+ output.clearTrace();
+ parser.readFrame(true);
+ Assert.assertEquals(endOfStreamThree, output.getTrace());
+ } else {
+ // End of stream 3 thenack
+ Assert.assertEquals(endOfStreamThree, output.getTrace());
+ output.clearTrace();
+ parser.readFrame(true);
+ Assert.assertEquals(settingsAck, output.getTrace());
+ }
+ output.clearTrace();
+ }
+
+
+ @Test
+ public void testWindowSizeTooLargeViaSettings() throws Exception {
+ http2Connect();
+
+ // Set up stream 3
+ sendSimplePostRequest(3, null, false);
+
+ // Increase the flow control window but keep it under the limit
+ sendWindowUpdate(3, 1 << 30);
+
+ // Now increase beyond the limit via a settings frame
+ sendSettings(0, false, new SettingValue(4, 1 << 30));
+ // Ack
+ parser.readFrame(true);
+ Assert.assertEquals("3-RST-[" + Http2Error.FLOW_CONTROL_ERROR.getCode() + "]\n",
+ output.getTrace());
+
+ }
+}
diff --git a/test/org/apache/coyote/http2/TestHttp2Section_8_1.java b/test/org/apache/coyote/http2/TestHttp2Section_8_1.java
new file mode 100644
index 0000000..4b248f0
--- /dev/null
+++ b/test/org/apache/coyote/http2/TestHttp2Section_8_1.java
@@ -0,0 +1,215 @@
+/*
+ * 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.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Unit tests for Section 8.1 of
+ * <a href="https://tools.ietf.org/html/rfc7540">RFC 7540</a>.
+ * <br>
+ * The order of tests in this class is aligned with the order of the
+ * examples in the RFC.
+ */
+public class TestHttp2Section_8_1 extends Http2TestBase {
+
+ @Test
+ public void testPostWithTrailerHeaders() throws Exception {
+ doTestPostWithTrailerHeaders(true);
+ }
+
+
+ @Test
+ public void testPostWithTrailerHeadersBlocked() throws Exception {
+ doTestPostWithTrailerHeaders(false);
+ }
+
+
+ private void doTestPostWithTrailerHeaders(boolean allowTrailerHeader) throws Exception{
+ http2Connect();
+ if (allowTrailerHeader) {
+ Http2Protocol http2Protocol =
+ (Http2Protocol) getTomcatInstance().getConnector().findUpgradeProtocols()[0];
+ http2Protocol.setAllowedTrailerHeaders(TRAILER_HEADER_NAME);
+ }
+
+ byte[] headersFrameHeader = new byte[9];
+ ByteBuffer headersPayload = ByteBuffer.allocate(128);
+ byte[] dataFrameHeader = new byte[9];
+ ByteBuffer dataPayload = ByteBuffer.allocate(256);
+ byte[] trailerFrameHeader = new byte[9];
+ ByteBuffer trailerPayload = ByteBuffer.allocate(256);
+
+ buildPostRequest(headersFrameHeader, headersPayload, false, dataFrameHeader, dataPayload,
+ null, trailerFrameHeader, trailerPayload, 3);
+
+ // Write the headers
+ writeFrame(headersFrameHeader, headersPayload);
+ // Body
+ writeFrame(dataFrameHeader, dataPayload);
+ // Trailers
+ writeFrame(trailerFrameHeader, trailerPayload);
+
+ parser.readFrame(true);
+ parser.readFrame(true);
+ parser.readFrame(true);
+ parser.readFrame(true);
+
+ String len;
+ if (allowTrailerHeader) {
+ len = Integer.toString(256 + TRAILER_HEADER_VALUE.length());
+ } else {
+ len = "256";
+ }
+
+ Assert.assertEquals("0-WindowSize-[256]\n" +
+ "3-WindowSize-[256]\n" +
+ "3-HeadersStart\n" +
+ "3-Header-[:status]-[200]\n" +
+ "3-Header-[date]-["+ DEFAULT_DATE + "]\n" +
+ "3-HeadersEnd\n" +
+ "3-Body-" +
+ len +
+ "\n" +
+ "3-EndOfStream\n",
+ output.getTrace());
+ }
+
+
+ @Test
+ public void testSendAck() throws Exception {
+ http2Connect();
+
+ byte[] headersFrameHeader = new byte[9];
+ ByteBuffer headersPayload = ByteBuffer.allocate(128);
+ byte[] dataFrameHeader = new byte[9];
+ ByteBuffer dataPayload = ByteBuffer.allocate(256);
+
+ buildPostRequest(headersFrameHeader, headersPayload, true,
+ dataFrameHeader, dataPayload, null, 3);
+
+ // Write the headers
+ writeFrame(headersFrameHeader, headersPayload);
+
+ parser.readFrame(true);
+
+ Assert.assertEquals("3-HeadersStart\n" +
+ "3-Header-[:status]-[100]\n" +
+ "3-Header-[date]-["+ DEFAULT_DATE + "]\n" +
+ "3-HeadersEnd\n",
+ output.getTrace());
+ output.clearTrace();
+
+ // Write the body
+ writeFrame(dataFrameHeader, dataPayload);
+
+ parser.readFrame(true);
+ parser.readFrame(true);
+ parser.readFrame(true);
+ parser.readFrame(true);
+
+ Assert.assertEquals("0-WindowSize-[256]\n" +
+ "3-WindowSize-[256]\n" +
+ "3-HeadersStart\n" +
+ "3-Header-[:status]-[200]\n" +
+ "3-Header-[date]-["+ DEFAULT_DATE + "]\n" +
+ "3-HeadersEnd\n" +
+ "3-Body-256\n" +
+ "3-EndOfStream\n",
+ output.getTrace());
+ }
+
+
+ @Test
+ public void testUndefinedPseudoHeader() throws Exception {
+ List<Header> headers = new ArrayList<>(3);
+ headers.add(new Header(":method", "GET"));
+ headers.add(new Header(":path", "/simple"));
+ headers.add(new Header(":authority", "localhost:" + getPort()));
+ headers.add(new Header(":foo", "bar"));
+
+ doInvalidPseudoHeaderTest(headers);
+ }
+
+
+ @Test
+ public void testInvalidPseudoHeader() throws Exception {
+ List<Header> headers = new ArrayList<>(3);
+ headers.add(new Header(":method", "GET"));
+ headers.add(new Header(":path", "/simple"));
+ headers.add(new Header(":authority", "localhost:" + getPort()));
+ headers.add(new Header(":status", "200"));
+
+ doInvalidPseudoHeaderTest(headers);
+ }
+
+
+ @Test
+ public void testPseudoHeaderOrder() throws Exception {
+ // Need to do this in two frames because HPACK encoder automatically
+ // re-orders fields
+
+ http2Connect();
+
+ List<Header> headers = new ArrayList<>(3);
+ headers.add(new Header(":method", "GET"));
+ headers.add(new Header(":path", "/simple"));
+ headers.add(new Header("x-test", "test"));
+
+ byte[] headersFrameHeader = new byte[9];
+ ByteBuffer headersPayload = ByteBuffer.allocate(128);
+
+ buildSimpleGetRequestPart1(headersFrameHeader, headersPayload, headers , 3);
+
+ writeFrame(headersFrameHeader, headersPayload);
+
+ headers.clear();
+ headers.add(new Header(":authority", "localhost:" + getPort()));
+ headersPayload.clear();
+
+ buildSimpleGetRequestPart2(headersFrameHeader, headersPayload, headers , 3);
+
+ writeFrame(headersFrameHeader, headersPayload);
+
+
+ parser.readFrame(true);
+
+ Assert.assertEquals("3-RST-[1]\n", output.getTrace());
+ }
+
+
+ private void doInvalidPseudoHeaderTest(List<Header> headers) throws Exception {
+ http2Connect();
+
+ byte[] headersFrameHeader = new byte[9];
+ ByteBuffer headersPayload = ByteBuffer.allocate(128);
+
+ buildGetRequest(headersFrameHeader, headersPayload, null, headers , 3);
+
+ // Write the headers
+ writeFrame(headersFrameHeader, headersPayload);
+
+ parser.readFrame(true);
+
+ Assert.assertEquals("3-RST-[1]\n", output.getTrace());
+ }
+}
diff --git a/test/org/apache/el/TestELInJsp.java b/test/org/apache/el/TestELInJsp.java
index 2c55238..3acf608 100644
--- a/test/org/apache/el/TestELInJsp.java
+++ b/test/org/apache/el/TestELInJsp.java
@@ -463,9 +463,9 @@ public class TestELInJsp extends TomcatBaseTest {
String result = res.toString();
// javax.servlet
assertEcho(result, "00-" + DispatcherType.ASYNC);
- // No obvious status fields for javax.servlet.http
+ // No obvious static fields for javax.servlet.http
// Could hack something with HttpUtils...
- // No obvious status fields for javax.servlet.jsp
+ // No obvious static fields for javax.servlet.jsp
// Wild card (package) import
assertEcho(result, "01-" + BigDecimal.ROUND_UP);
// Class import
diff --git a/test/org/apache/el/parser/TestAstLambdaExpression.java b/test/org/apache/el/parser/TestAstLambdaExpression.java
index 03f0c52..41dbff0 100644
--- a/test/org/apache/el/parser/TestAstLambdaExpression.java
+++ b/test/org/apache/el/parser/TestAstLambdaExpression.java
@@ -223,4 +223,14 @@ public class TestAstLambdaExpression {
Integer.class);
Assert.assertEquals(Integer.valueOf(1), result);
}
+
+
+ @Test(expected=javax.el.ELException.class)
+ public void testLambdaAsFunction08() {
+ // Using a name space for the function is not allowed
+ ELProcessor processor = new ELProcessor();
+ Object result =
+ processor.getValue("foo:v = (x)->x+1; foo:v(0)", Integer.class);
+ Assert.assertEquals(Integer.valueOf(1), result);
+ }
}
diff --git a/test/org/apache/el/parser/TestELParser.java b/test/org/apache/el/parser/TestELParser.java
index ea1afac..90a5688 100644
--- a/test/org/apache/el/parser/TestELParser.java
+++ b/test/org/apache/el/parser/TestELParser.java
@@ -17,6 +17,8 @@
package org.apache.el.parser;
+import java.io.StringReader;
+
import javax.el.ELContext;
import javax.el.ELException;
import javax.el.ExpressionFactory;
@@ -25,9 +27,11 @@ import javax.el.ValueExpression;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
+import org.junit.Ignore;
import org.junit.Test;
import org.apache.jasper.el.ELContextImpl;
+import org.apache.tomcat.util.collections.SynchronizedStack;
public class TestELParser {
@@ -229,4 +233,61 @@ public class TestELParser {
String result = (String) ve.getValue(context);
assertEquals(expected, result);
}
+
+ /*
+ * Test to explore if re-using Parser instances is faster.
+ *
+ * Tests on my laptop show:
+ * - overhead by introducing the stack is in the noise for parsing even the
+ * simplest expression
+ * - efficiency from re-using the ELParser is measurable for even a single
+ * reuse of the parser
+ * - with large numbers of parses (~10k) performance for a trivial parse is
+ * three times faster
+ * - around the 100 iterations mark GC overhead adds significant noise to
+ * the results - for consistent results you either need fewer parses to
+ * avoid triggering GC or more parses so the GC effects are evenly
+ * distributed between the runs
+ *
+ * Note that the test is single threaded.
+ */
+ @Ignore
+ @Test
+ public void testParserPerformance() throws ParseException {
+ final int runs = 20;
+ final int parseIterations = 10000;
+
+
+ for (int j = 0; j < runs; j ++) {
+ long start = System.nanoTime();
+ SynchronizedStack<ELParser> stack = new SynchronizedStack<>();
+
+ for (int i = 0; i < parseIterations; i ++) {
+ ELParser parser = stack.pop();
+ if (parser == null) {
+ parser = new ELParser(new StringReader("${'foo'}"));
+ } else {
+ parser.ReInit(new StringReader("${'foo'}"));
+ }
+ parser.CompositeExpression();
+ stack.push(parser);
+ }
+ long end = System.nanoTime();
+
+ System.out.println(parseIterations +
+ " iterations using ELParser.ReInit(...) took " + (end - start) + "ns");
+ }
+
+ for (int j = 0; j < runs; j ++) {
+ long start = System.nanoTime();
+ for (int i = 0; i < parseIterations; i ++) {
+ ELParser parser = new ELParser(new StringReader("${'foo'}"));
+ parser.CompositeExpression();
+ }
+ long end = System.nanoTime();
+
+ System.out.println(parseIterations +
+ " iterations using new ELParser(...) took " + (end - start) + "ns");
+ }
+ }
}
diff --git a/test/org/apache/jasper/compiler/TestCompiler.java b/test/org/apache/jasper/compiler/TestCompiler.java
index 33baeca..49db96f 100644
--- a/test/org/apache/jasper/compiler/TestCompiler.java
+++ b/test/org/apache/jasper/compiler/TestCompiler.java
@@ -28,6 +28,7 @@ import static org.junit.Assert.assertTrue;
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;
@@ -173,7 +174,9 @@ public class TestCompiler extends TomcatBaseTest {
Tomcat tomcat = getTomcatInstance();
File appDir = new File("test/webapp-fragments");
- tomcat.addWebapp(null, "/test", appDir.getAbsolutePath());
+ Context ctx = tomcat.addWebapp(null, "/test", appDir.getAbsolutePath());
+ skipTldsForResourceJars(ctx);
+
tomcat.start();
// No further tests required. The bug triggers an infinite loop on
diff --git a/test/org/apache/jasper/compiler/TestJspConfig.java b/test/org/apache/jasper/compiler/TestJspConfig.java
index e82e4c5..081d9d0 100644
--- a/test/org/apache/jasper/compiler/TestJspConfig.java
+++ b/test/org/apache/jasper/compiler/TestJspConfig.java
@@ -33,8 +33,7 @@ public class TestJspConfig extends TomcatBaseTest {
public void testServlet22NoEL() throws Exception {
Tomcat tomcat = getTomcatInstance();
- File appDir =
- new File("test/webapp-2.2");
+ File appDir = new File("test/webapp-2.2");
// app dir is relative to server home
tomcat.addWebapp(null, "/test", appDir.getAbsolutePath());
@@ -73,8 +72,7 @@ public class TestJspConfig extends TomcatBaseTest {
public void testServlet24NoEL() throws Exception {
Tomcat tomcat = getTomcatInstance();
- File appDir =
- new File("test/webapp-2.4");
+ File appDir = new File("test/webapp-2.4");
// app dir is relative to server home
tomcat.addWebapp(null, "/test", appDir.getAbsolutePath());
@@ -93,8 +91,7 @@ public class TestJspConfig extends TomcatBaseTest {
public void testServlet25NoEL() throws Exception {
Tomcat tomcat = getTomcatInstance();
- File appDir =
- new File("test/webapp-2.5");
+ File appDir = new File("test/webapp-2.5");
// app dir is relative to server home
tomcat.addWebapp(null, "/test", appDir.getAbsolutePath());
@@ -112,8 +109,7 @@ public class TestJspConfig extends TomcatBaseTest {
public void testServlet30NoEL() throws Exception {
Tomcat tomcat = getTomcatInstance();
- File appDir =
- new File("test/webapp-3.0");
+ File appDir = new File("test/webapp-3.0");
// app dir is relative to server home
tomcat.addWebapp(null, "/test", appDir.getAbsolutePath());
@@ -131,8 +127,7 @@ public class TestJspConfig extends TomcatBaseTest {
public void testServlet31NoEL() throws Exception {
Tomcat tomcat = getTomcatInstance();
- File appDir =
- new File("test/webapp-3.1");
+ File appDir = new File("test/webapp-3.1");
// app dir is relative to server home
tomcat.addWebapp(null, "/test", appDir.getAbsolutePath());
@@ -145,4 +140,5 @@ public class TestJspConfig extends TomcatBaseTest {
assertTrue(result.indexOf("<p>00-hello world</p>") > 0);
}
+
}
diff --git a/test/org/apache/jasper/servlet/TestJspCServletContext.java b/test/org/apache/jasper/servlet/TestJspCServletContext.java
index 50533b1..de89b8d 100644
--- a/test/org/apache/jasper/servlet/TestJspCServletContext.java
+++ b/test/org/apache/jasper/servlet/TestJspCServletContext.java
@@ -113,6 +113,7 @@ public class TestJspCServletContext {
Assert.assertEquals(1, context.getEffectiveMinorVersion());
}
+
@Test
public void testWebresources() throws Exception {
File appDir = new File("test/webresources/dir1");
diff --git a/test/org/apache/naming/resources/TestWarDirContext.java b/test/org/apache/naming/resources/TestWarDirContext.java
index 922d423..32913d9 100644
--- a/test/org/apache/naming/resources/TestWarDirContext.java
+++ b/test/org/apache/naming/resources/TestWarDirContext.java
@@ -112,6 +112,7 @@ public class TestWarDirContext extends TomcatBaseTest {
StandardRoot root = new StandardRoot();
root.setCachingAllowed(true);
ctxt.setResources(root);
+ skipTldsForResourceJars(ctxt);
tomcat.start();
diff --git a/test/org/apache/tomcat/unittest/TesterContext.java b/test/org/apache/tomcat/unittest/TesterContext.java
index 179d03f..6e1ffb1 100644
--- a/test/org/apache/tomcat/unittest/TesterContext.java
+++ b/test/org/apache/tomcat/unittest/TesterContext.java
@@ -740,11 +740,6 @@ public class TesterContext implements Context {
}
@Override
- public void addInstanceListener(String listener) {
- // NO-OP
- }
-
- @Override
public void addLocaleEncodingMappingParameter(String locale, String encoding) {
// NO-OP
}
@@ -765,13 +760,11 @@ public class TesterContext implements Context {
}
@Override
- @Deprecated
public void addServletMapping(String pattern, String name) {
// NO-OP
}
@Override
- @Deprecated
public void addServletMapping(String pattern, String name,
boolean jspWildcard) {
// NO-OP
@@ -854,11 +847,6 @@ public class TesterContext implements Context {
}
@Override
- public String[] findInstanceListeners() {
- return null;
- }
-
- @Override
public String findMimeMapping(String extension) {
return null;
}
@@ -969,11 +957,6 @@ public class TesterContext implements Context {
}
@Override
- public void removeInstanceListener(String listener) {
- // NO-OP
- }
-
- @Override
public void removeMimeMapping(String extension) {
// NO-OP
}
diff --git a/test/org/apache/tomcat/unittest/TesterLeakingServlet1.java b/test/org/apache/tomcat/unittest/TesterLeakingServlet1.java
index 814bb27..840c570 100644
--- a/test/org/apache/tomcat/unittest/TesterLeakingServlet1.java
+++ b/test/org/apache/tomcat/unittest/TesterLeakingServlet1.java
@@ -51,11 +51,4 @@ public class TesterLeakingServlet1 extends HttpServlet {
"The current thread served this servlet "
+ counter.getCount() + " times");
}
-
- @Override
- public void destroy() {
- super.destroy();
- // normally not needed, just to make my point
- myThreadLocal = null;
- }
}
\ No newline at end of file
diff --git a/test/org/apache/tomcat/util/buf/TestB2CConverter.java b/test/org/apache/tomcat/util/buf/TestB2CConverter.java
index b593e57..8c30591 100644
--- a/test/org/apache/tomcat/util/buf/TestB2CConverter.java
+++ b/test/org/apache/tomcat/util/buf/TestB2CConverter.java
@@ -18,6 +18,7 @@ package org.apache.tomcat.util.buf;
import java.nio.charset.Charset;
import java.nio.charset.MalformedInputException;
+import java.nio.charset.StandardCharsets;
import java.util.Locale;
import org.junit.Assert;
@@ -48,7 +49,7 @@ public class TestB2CConverter {
}
private void testMessages(int msgCount) throws Exception {
- B2CConverter conv = new B2CConverter("UTF-16");
+ B2CConverter conv = new B2CConverter(StandardCharsets.UTF_16);
ByteChunk bc = new ByteChunk();
CharChunk cc = new CharChunk(32);
@@ -93,11 +94,10 @@ public class TestB2CConverter {
maxLeftover <= B2CConverter.LEFTOVER_SIZE);
}
- // TODO Work-around bug in UTF8 decoder
- //@Test(expected=MalformedInputException.class)
+ @Test(expected=MalformedInputException.class)
public void testBug54602a() throws Exception {
// Check invalid input is rejected straight away
- B2CConverter conv = new B2CConverter("UTF-8");
+ B2CConverter conv = new B2CConverter(StandardCharsets.UTF_8);
ByteChunk bc = new ByteChunk();
CharChunk cc = new CharChunk();
@@ -110,7 +110,7 @@ public class TestB2CConverter {
@Test(expected=MalformedInputException.class)
public void testBug54602b() throws Exception {
// Check partial input is rejected
- B2CConverter conv = new B2CConverter("UTF-8");
+ B2CConverter conv = new B2CConverter(StandardCharsets.UTF_8);
ByteChunk bc = new ByteChunk();
CharChunk cc = new CharChunk();
@@ -123,7 +123,7 @@ public class TestB2CConverter {
@Test
public void testBug54602c() throws Exception {
// Check partial input is rejected once it is known to be all available
- B2CConverter conv = new B2CConverter("UTF-8");
+ B2CConverter conv = new B2CConverter(StandardCharsets.UTF_8);
ByteChunk bc = new ByteChunk();
CharChunk cc = new CharChunk();
diff --git a/test/org/apache/tomcat/util/buf/TestUEncoder.java b/test/org/apache/tomcat/util/buf/TestUEncoder.java
index f5a983c..188f54d 100644
--- a/test/org/apache/tomcat/util/buf/TestUEncoder.java
+++ b/test/org/apache/tomcat/util/buf/TestUEncoder.java
@@ -20,8 +20,6 @@ package org.apache.tomcat.util.buf;
import java.io.IOException;
import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
import org.junit.Test;
import org.apache.tomcat.util.buf.UEncoder.SafeCharsSet;
@@ -32,25 +30,6 @@ import org.apache.tomcat.util.buf.UEncoder.SafeCharsSet;
public class TestUEncoder {
@Test
- public void testEncodeURL() throws IOException {
- UEncoder urlEncoder = new UEncoder();
-
- String s = "a/b/c/d+e.class";
- assertTrue(urlEncoder.encodeURL(s, 0, s.length()).equals(
- "a%2fb%2fc%2fd%2be.class"));
- assertTrue(urlEncoder.encodeURL(s, 2, s.length() - 2).equals(
- "b%2fc%2fd%2be.cla"));
-
- urlEncoder.addSafeCharacter('+');
- assertTrue(urlEncoder.encodeURL(s, 0, s.length()).equals(
- "a%2fb%2fc%2fd+e.class"));
-
- s = new String(new char[] { 0xD801, 0xDC01 });
- assertTrue(urlEncoder.encodeURL(s, 0, s.length())
- .equals("%f0%90%90%81"));
- }
-
- @Test
public void testEncodeURLWithSlashInit() throws IOException {
UEncoder urlEncoder = new UEncoder(SafeCharsSet.WITH_SLASH);
@@ -60,16 +39,8 @@ public class TestUEncoder {
assertTrue(urlEncoder.encodeURL(s, 2, s.length() - 2).equals(
"b/c/d%2be.cla"));
- try {
- urlEncoder.addSafeCharacter('+');
- fail();
- } catch (IllegalStateException e) {
- // OK
- }
-
s = new String(new char[] { 0xD801, 0xDC01 });
assertTrue(urlEncoder.encodeURL(s, 0, s.length())
.equals("%f0%90%90%81"));
}
-
}
diff --git a/test/org/apache/tomcat/util/buf/TestUtf8.java b/test/org/apache/tomcat/util/buf/TestUtf8.java
index 2b3c719..6673cbb 100644
--- a/test/org/apache/tomcat/util/buf/TestUtf8.java
+++ b/test/org/apache/tomcat/util/buf/TestUtf8.java
@@ -23,6 +23,7 @@ import java.nio.charset.CoderResult;
import java.nio.charset.CodingErrorAction;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import org.junit.Assert;
@@ -54,7 +55,7 @@ public class TestUtf8 {
// Indicates that three replacement characters are missing
private static final int REPLACE_MISSING4 = 64;
- public static final List<Utf8TestCase> TEST_CASES = new ArrayList<>();
+ public static final List<Utf8TestCase> TEST_CASES;
private static int workAroundCount = 0;
@@ -71,28 +72,29 @@ public class TestUtf8 {
}
Utf8TestCase testCase = null;
+ ArrayList<Utf8TestCase> testCases = new ArrayList<>();
- TEST_CASES.add(new Utf8TestCase(
+ testCases.add(new Utf8TestCase(
"Zero length input",
new int[] {},
-1,
""));
- TEST_CASES.add(new Utf8TestCase(
+ testCases.add(new Utf8TestCase(
"Valid one byte sequence",
new int[] {0x41},
-1,
"A"));
- TEST_CASES.add(new Utf8TestCase(
+ testCases.add(new Utf8TestCase(
"Valid two byte sequence",
new int[] {0xC2, 0xA9},
-1,
"\u00A9"));
- TEST_CASES.add(new Utf8TestCase(
+ testCases.add(new Utf8TestCase(
"Valid three byte sequence",
new int[] {0xE0, 0xA4, 0x87},
-1,
"\u0907"));
- TEST_CASES.add(new Utf8TestCase(
+ testCases.add(new Utf8TestCase(
"Valid four byte sequence",
new int[] {0xF0, 0x90, 0x90, 0x80},
-1,
@@ -107,7 +109,7 @@ public class TestUtf8 {
if (javaVersion < 8) {
testCase.addForJvm(ERROR_POS_PLUS2);
}
- TEST_CASES.add(testCase);
+ testCases.add(testCase);
// Java 7 JVM decoder does not report error until all 2 bytes are available
testCase = new Utf8TestCase(
@@ -118,7 +120,7 @@ public class TestUtf8 {
if (javaVersion < 8) {
testCase.addForJvm(ERROR_POS_PLUS1);
}
- TEST_CASES.add(testCase);
+ testCases.add(testCase);
// Java 7 JVM decoder does not report error until all 3 bytes are available
testCase = new Utf8TestCase(
@@ -129,7 +131,7 @@ public class TestUtf8 {
if (javaVersion < 8) {
testCase.addForJvm(ERROR_POS_PLUS1);
}
- TEST_CASES.add(testCase);
+ testCases.add(testCase);
// Java 7 JVM decoder does not report error until all 4 bytes are
// available
@@ -141,9 +143,9 @@ public class TestUtf8 {
if (javaVersion < 8) {
testCase.addForJvm(ERROR_POS_PLUS2);
}
- TEST_CASES.add(testCase);
+ testCases.add(testCase);
- TEST_CASES.add(new Utf8TestCase(
+ testCases.add(new Utf8TestCase(
"Invalid one byte 1111 1111",
new int[] {0x41, 0xFF, 0x41},
1,
@@ -157,7 +159,7 @@ public class TestUtf8 {
if (javaVersion < 8) {
testCase.addForJvm(REPLACE_SWALLOWS_TRAILER);
}
- TEST_CASES.add(testCase);
+ testCases.add(testCase);
testCase = new Utf8TestCase(
"Invalid one byte 1110 0000",
@@ -167,7 +169,7 @@ public class TestUtf8 {
if (javaVersion < 8) {
testCase.addForJvm(REPLACE_SWALLOWS_TRAILER);
}
- TEST_CASES.add(testCase);
+ testCases.add(testCase);
testCase = new Utf8TestCase(
"Invalid one byte 1100 0000",
@@ -177,35 +179,35 @@ public class TestUtf8 {
if (javaVersion < 8) {
testCase.addForJvm(ERROR_POS_PLUS1);
}
- TEST_CASES.add(testCase);
+ testCases.add(testCase);
- TEST_CASES.add(new Utf8TestCase(
+ testCases.add(new Utf8TestCase(
"Invalid one byte 1000 000",
new int[] {0x41, 0x80, 0x41},
1,
"A\uFFFDA"));
- TEST_CASES.add(new Utf8TestCase(
+ testCases.add(new Utf8TestCase(
"Invalid sequence from unicode 6.2 spec, table 3-8",
new int[] {0x61, 0xF1, 0x80, 0x80, 0xE1, 0x80, 0xC2, 0x62, 0x80,
0x63, 0x80, 0xBF, 0x64},
4,
"a\uFFFD\uFFFD\uFFFDb\uFFFDc\uFFFD\uFFFDd"));
- TEST_CASES.add(new Utf8TestCase(
+ testCases.add(new Utf8TestCase(
"Valid 4-byte sequence truncated to 3 bytes",
new int[] {0x61, 0xF0, 0x90, 0x90},
3,
"a\uFFFD"));
- TEST_CASES.add(new Utf8TestCase(
+ testCases.add(new Utf8TestCase(
"Valid 4-byte sequence truncated to 2 bytes",
new int[] {0x61, 0xF0, 0x90},
2,
"a\uFFFD"));
- TEST_CASES.add(new Utf8TestCase(
+ testCases.add(new Utf8TestCase(
"Valid 4-byte sequence truncated to 1 byte",
new int[] {0x61, 0xF0},
1,
"a\uFFFD"));
- TEST_CASES.add(new Utf8TestCase(
+ testCases.add(new Utf8TestCase(
"Valid 4-byte sequence truncated to 3 bytes with trailer",
new int[] {0x61, 0xF0, 0x90, 0x90, 0x61},
4,
@@ -219,7 +221,7 @@ public class TestUtf8 {
if (javaVersion < 8) {
testCase.addForJvm(REPLACE_SWALLOWS_TRAILER);
}
- TEST_CASES.add(testCase);
+ testCases.add(testCase);
testCase = new Utf8TestCase(
"Valid 4-byte sequence truncated to 1 byte with trailer",
@@ -229,7 +231,7 @@ public class TestUtf8 {
if (javaVersion < 8) {
testCase.addForJvm(REPLACE_SWALLOWS_TRAILER);
}
- TEST_CASES.add(testCase);
+ testCases.add(testCase);
testCase = new Utf8TestCase(
"U+0000 zero-padded to two bytes",
@@ -239,7 +241,7 @@ public class TestUtf8 {
if (javaVersion < 8) {
testCase.addForJvm(ERROR_POS_PLUS1);
}
- TEST_CASES.add(testCase);
+ testCases.add(testCase);
testCase = new Utf8TestCase(
"U+007F zero-padded to two bytes",
@@ -249,9 +251,9 @@ public class TestUtf8 {
if (javaVersion < 8) {
testCase.addForJvm(ERROR_POS_PLUS1);
}
- TEST_CASES.add(testCase);
+ testCases.add(testCase);
- TEST_CASES.add(new Utf8TestCase(
+ testCases.add(new Utf8TestCase(
"Two bytes, all 1's",
new int[] {0x61, 0xFF, 0xFF, 0x61},
1,
@@ -265,19 +267,19 @@ public class TestUtf8 {
if (javaVersion < 8) {
testCase.addForJvm(ERROR_POS_PLUS1);
}
- TEST_CASES.add(testCase);
+ testCases.add(testCase);
- TEST_CASES.add(new Utf8TestCase(
+ testCases.add(new Utf8TestCase(
"Two bytes, 101x first byte first nibble",
new int[] {0x61, 0xA0, 0x80, 0x61},
1,
"a\uFFFD\uFFFDa"));
- TEST_CASES.add(new Utf8TestCase(
+ testCases.add(new Utf8TestCase(
"Two bytes, invalid second byte",
new int[] {0x61, 0xC2, 0x00, 0x61},
2,
"a\uFFFD\u0000a"));
- TEST_CASES.add(new Utf8TestCase(
+ testCases.add(new Utf8TestCase(
"Two bytes, invalid second byte",
new int[] {0x61, 0xC2, 0xC0, 0x61},
2,
@@ -291,7 +293,7 @@ public class TestUtf8 {
if (javaVersion < 8) {
testCase.addForJvm(ERROR_POS_PLUS1);
}
- TEST_CASES.add(testCase);
+ testCases.add(testCase);
testCase = new Utf8TestCase(
"Three bytes, U+007F zero-padded",
@@ -301,7 +303,7 @@ public class TestUtf8 {
if (javaVersion < 8) {
testCase.addForJvm(ERROR_POS_PLUS1);
}
- TEST_CASES.add(testCase);
+ testCases.add(testCase);
testCase = new Utf8TestCase(
"Three bytes, U+07FF zero-padded",
@@ -311,9 +313,9 @@ public class TestUtf8 {
if (javaVersion < 8) {
testCase.addForJvm(ERROR_POS_PLUS1);
}
- TEST_CASES.add(testCase);
+ testCases.add(testCase);
- TEST_CASES.add(new Utf8TestCase(
+ testCases.add(new Utf8TestCase(
"Three bytes, all 1's",
new int[] {0x61, 0xFF, 0xFF, 0xFF, 0x61},
1,
@@ -328,7 +330,7 @@ public class TestUtf8 {
testCase.addForJvm(REPLACE_MISSING2).addForJvm(
REPLACE_SWALLOWS_TRAILER);
}
- TEST_CASES.add(testCase);
+ testCases.add(testCase);
testCase = new Utf8TestCase(
"Three bytes, invalid second byte",
@@ -338,9 +340,9 @@ public class TestUtf8 {
if (javaVersion < 8) {
testCase.addForJvm(ERROR_POS_PLUS1);
}
- TEST_CASES.add(testCase);
+ testCases.add(testCase);
- TEST_CASES.add(new Utf8TestCase(
+ testCases.add(new Utf8TestCase(
"Three bytes, invalid third byte",
new int[] {0x61, 0xE1, 0x80, 0xC0, 0x61},
3,
@@ -353,7 +355,7 @@ public class TestUtf8 {
if (javaVersion < 8) {
testCase.addForJvm(ERROR_POS_PLUS2);
}
- TEST_CASES.add(testCase);
+ testCases.add(testCase);
testCase = new Utf8TestCase(
"Four bytes, U+007F zero-padded",
@@ -363,7 +365,7 @@ public class TestUtf8 {
if (javaVersion < 8) {
testCase.addForJvm(ERROR_POS_PLUS2);
}
- TEST_CASES.add(testCase);
+ testCases.add(testCase);
testCase = new Utf8TestCase(
"Four bytes, U+07FF zero-padded",
@@ -373,7 +375,7 @@ public class TestUtf8 {
if (javaVersion < 8) {
testCase.addForJvm(ERROR_POS_PLUS2);
}
- TEST_CASES.add(testCase);
+ testCases.add(testCase);
testCase = new Utf8TestCase(
"Four bytes, U+FFFF zero-padded",
@@ -383,9 +385,9 @@ public class TestUtf8 {
if (javaVersion < 8) {
testCase.addForJvm(ERROR_POS_PLUS2);
}
- TEST_CASES.add(testCase);
+ testCases.add(testCase);
- TEST_CASES.add(new Utf8TestCase(
+ testCases.add(new Utf8TestCase(
"Four bytes, all 1's",
new int[] {0x61, 0xFF, 0xFF, 0xFF, 0xFF, 0x61},
1,
@@ -400,7 +402,7 @@ public class TestUtf8 {
testCase.addForJvm(ERROR_POS_PLUS4).addForJvm(
REPLACE_MISSING2).addForJvm(REPLACE_MISSING1);
}
- TEST_CASES.add(testCase);
+ testCases.add(testCase);
testCase = new Utf8TestCase(
"Four bytes, invalid second byte",
@@ -410,7 +412,7 @@ public class TestUtf8 {
if (javaVersion < 8) {
testCase.addForJvm(ERROR_POS_PLUS2);
}
- TEST_CASES.add(testCase);
+ testCases.add(testCase);
testCase = new Utf8TestCase(
"Four bytes, invalid third byte",
@@ -420,9 +422,9 @@ public class TestUtf8 {
if (javaVersion < 8) {
testCase.addForJvm(ERROR_POS_PLUS1);
}
- TEST_CASES.add(testCase);
+ testCases.add(testCase);
- TEST_CASES.add(new Utf8TestCase(
+ testCases.add(new Utf8TestCase(
"Four bytes, invalid fourth byte",
new int[] {0x61, 0xF1, 0x80, 0x80, 0xC0, 0x61},
4,
@@ -436,7 +438,7 @@ public class TestUtf8 {
if (javaVersion < 8) {
testCase.addForJvm(ERROR_POS_PLUS4).addForJvm(REPLACE_MISSING4);
}
- TEST_CASES.add(testCase);
+ testCases.add(testCase);
testCase = new Utf8TestCase(
"Five bytes, U+007F zero padded",
@@ -446,7 +448,7 @@ public class TestUtf8 {
if (javaVersion < 8) {
testCase.addForJvm(ERROR_POS_PLUS4).addForJvm(REPLACE_MISSING4);
}
- TEST_CASES.add(testCase);
+ testCases.add(testCase);
testCase = new Utf8TestCase(
"Five bytes, U+07FF zero padded",
@@ -456,7 +458,7 @@ public class TestUtf8 {
if (javaVersion < 8) {
testCase.addForJvm(ERROR_POS_PLUS4).addForJvm(REPLACE_MISSING4);
}
- TEST_CASES.add(testCase);
+ testCases.add(testCase);
testCase = new Utf8TestCase(
"Five bytes, U+FFFF zero padded",
@@ -466,7 +468,7 @@ public class TestUtf8 {
if (javaVersion < 8) {
testCase.addForJvm(ERROR_POS_PLUS4).addForJvm(REPLACE_MISSING4);
}
- TEST_CASES.add(testCase);
+ testCases.add(testCase);
testCase = new Utf8TestCase(
"Six bytes, U+0000 zero padded",
@@ -478,7 +480,7 @@ public class TestUtf8 {
ERROR_POS_PLUS1).addForJvm(REPLACE_MISSING4).addForJvm(
REPLACE_MISSING1);
}
- TEST_CASES.add(testCase);
+ testCases.add(testCase);
testCase = new Utf8TestCase(
"Six bytes, U+007F zero padded",
@@ -490,7 +492,7 @@ public class TestUtf8 {
ERROR_POS_PLUS1).addForJvm(REPLACE_MISSING4).addForJvm(
REPLACE_MISSING1);
}
- TEST_CASES.add(testCase);
+ testCases.add(testCase);
testCase = new Utf8TestCase(
"Six bytes, U+07FF zero padded",
@@ -502,7 +504,7 @@ public class TestUtf8 {
ERROR_POS_PLUS1).addForJvm(REPLACE_MISSING4).addForJvm(
REPLACE_MISSING1);
}
- TEST_CASES.add(testCase);
+ testCases.add(testCase);
testCase = new Utf8TestCase(
"Six bytes, U+FFFF zero padded",
@@ -514,7 +516,7 @@ public class TestUtf8 {
ERROR_POS_PLUS1).addForJvm(REPLACE_MISSING4).addForJvm(
REPLACE_MISSING1);
}
- TEST_CASES.add(testCase);
+ testCases.add(testCase);
testCase = new Utf8TestCase(
"Original test case - derived from Autobahn?",
@@ -526,7 +528,9 @@ public class TestUtf8 {
if (javaVersion < 8) {
testCase.addForJvm(ERROR_POS_PLUS1);
}
- TEST_CASES.add(testCase);
+ testCases.add(testCase);
+
+ TEST_CASES = Collections.unmodifiableList(testCases);
}
@Test
diff --git a/test/org/apache/tomcat/util/collections/TestCaseInsensitiveKeyMap.java b/test/org/apache/tomcat/util/collections/TestCaseInsensitiveKeyMap.java
new file mode 100644
index 0000000..c094bfa
--- /dev/null
+++ b/test/org/apache/tomcat/util/collections/TestCaseInsensitiveKeyMap.java
@@ -0,0 +1,215 @@
+/*
+ * 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.collections;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class TestCaseInsensitiveKeyMap {
+
+ @Test
+ public void testPut() {
+ Object o1 = new Object();
+ Object o2 = new Object();
+
+ CaseInsensitiveKeyMap<Object> map = new CaseInsensitiveKeyMap<>();
+ map.put("a", o1);
+ Object o = map.put("A", o2);
+
+ Assert.assertEquals(o1, o);
+
+ Assert.assertEquals(o2, map.get("a"));
+ Assert.assertEquals(o2, map.get("A"));
+ }
+
+
+ @Test(expected=NullPointerException.class)
+ public void testPutNullKey() {
+ Object o1 = new Object();
+
+ CaseInsensitiveKeyMap<Object> map = new CaseInsensitiveKeyMap<>();
+ map.put(null, o1);
+ }
+
+
+ @Test
+ public void testGet() {
+ Object o1 = new Object();
+
+ CaseInsensitiveKeyMap<Object> map = new CaseInsensitiveKeyMap<>();
+ map.put("a", o1);
+
+ Assert.assertEquals(o1, map.get("a"));
+ Assert.assertEquals(o1, map.get("A"));
+ }
+
+
+ @Test
+ public void testGetNullKey() {
+ Object o1 = new Object();
+
+ CaseInsensitiveKeyMap<Object> map = new CaseInsensitiveKeyMap<>();
+ map.put("a", o1);
+
+ Assert.assertNull(map.get(null));
+ }
+
+
+ @Test
+ public void testContainsKey() {
+ Object o1 = new Object();
+
+ CaseInsensitiveKeyMap<Object> map = new CaseInsensitiveKeyMap<>();
+ map.put("a", o1);
+
+ Assert.assertTrue(map.containsKey("a"));
+ Assert.assertTrue(map.containsKey("A"));
+ }
+
+
+ @Test
+ public void testContainsKeyNonString() {
+ Object o1 = new Object();
+
+ CaseInsensitiveKeyMap<Object> map = new CaseInsensitiveKeyMap<>();
+ map.put("a", o1);
+
+ Assert.assertFalse(map.containsKey(o1));
+ }
+
+
+ @Test
+ public void testContainsKeyNull() {
+ Object o1 = new Object();
+
+ CaseInsensitiveKeyMap<Object> map = new CaseInsensitiveKeyMap<>();
+ map.put("a", o1);
+
+ Assert.assertFalse(map.containsKey(null));
+ }
+
+
+ @Test
+ public void testContainsValue() {
+ Object o1 = new Object();
+
+ CaseInsensitiveKeyMap<Object> map = new CaseInsensitiveKeyMap<>();
+ map.put("a", o1);
+
+ Assert.assertTrue(map.containsValue(o1));
+ }
+
+
+ @Test
+ public void testRemove() {
+ Object o1 = new Object();
+
+ CaseInsensitiveKeyMap<Object> map = new CaseInsensitiveKeyMap<>();
+ map.put("a", o1);
+ Assert.assertFalse(map.isEmpty());
+ map.remove("A");
+ Assert.assertTrue(map.isEmpty());
+
+ map.put("A", o1);
+ Assert.assertFalse(map.isEmpty());
+ map.remove("a");
+ Assert.assertTrue(map.isEmpty());
+ }
+
+
+ @Test
+ public void testClear() {
+ Object o1 = new Object();
+
+ CaseInsensitiveKeyMap<Object> map = new CaseInsensitiveKeyMap<>();
+ for (int i = 0; i < 10; i++) {
+ map.put(Integer.toString(i), o1);
+ }
+ Assert.assertEquals(10, map.size());
+ map.clear();
+ Assert.assertEquals(0, map.size());
+ }
+
+
+ @Test
+ public void testPutAll() {
+ Object o1 = new Object();
+ Object o2 = new Object();
+
+ Map<String,Object> source = new HashMap<>();
+ source.put("a", o1);
+ source.put("A", o2);
+
+ CaseInsensitiveKeyMap<Object> map = new CaseInsensitiveKeyMap<>();
+ map.putAll(source);
+
+ Assert.assertEquals(1, map.size());
+ Assert.assertTrue(map.containsValue(o1) != map.containsValue(o2));
+ }
+
+
+ @Test
+ public void testKeySetContains() {
+ Object o1 = new Object();
+
+ CaseInsensitiveKeyMap<Object> map = new CaseInsensitiveKeyMap<>();
+ map.put("a", o1);
+
+ Set<String> keys = map.keySet();
+
+ Assert.assertTrue(keys.contains("a"));
+ Assert.assertTrue(keys.contains("A"));
+ }
+
+
+ @Test
+ public void testKeySetRemove() {
+ Object o1 = new Object();
+
+ CaseInsensitiveKeyMap<Object> map = new CaseInsensitiveKeyMap<>();
+ map.put("a", o1);
+
+ Iterator<String> iter = map.keySet().iterator();
+ Assert.assertTrue(iter.hasNext());
+ iter.next();
+ iter.remove();
+ Assert.assertTrue(map.isEmpty());
+ }
+
+
+ @Test
+ public void testEntrySetRemove() {
+ Object o1 = new Object();
+
+ CaseInsensitiveKeyMap<Object> map = new CaseInsensitiveKeyMap<>();
+ map.put("a", o1);
+
+ Iterator<Entry<String,Object>> iter = map.entrySet().iterator();
+ Assert.assertTrue(iter.hasNext());
+ Entry<String,Object> entry = iter.next();
+ Assert.assertEquals("a", entry.getKey());
+ Assert.assertEquals(o1, entry.getValue());
+ iter.remove();
+ Assert.assertTrue(map.isEmpty());
+ }
+}
diff --git a/test/org/apache/tomcat/util/http/CookiesBaseTest.java b/test/org/apache/tomcat/util/http/CookiesBaseTest.java
index 8846bbc..df40e3d 100644
--- a/test/org/apache/tomcat/util/http/CookiesBaseTest.java
+++ b/test/org/apache/tomcat/util/http/CookiesBaseTest.java
@@ -70,6 +70,7 @@ public abstract class CookiesBaseTest extends TomcatBaseTest {
public static void addServlets(Tomcat tomcat) {
// No file system docBase required
Context ctx = tomcat.addContext("", null);
+ ctx.setCookieProcessor(new LegacyCookieProcessor());
Tomcat.addServlet(ctx, "invalid", new CookieServlet("na;me", "value"));
ctx.addServletMappingDecoded("/invalid", "invalid");
@@ -81,7 +82,7 @@ public abstract class CookiesBaseTest extends TomcatBaseTest {
new CookieServlet("na/me", "value"));
ctx.addServletMappingDecoded("/invalidFwd", "invalidFwd");
Tomcat.addServlet(ctx, "invalidStrict",
- new CookieServlet("na?me", "value"));
+ new CookieServlet("$name", "value"));
ctx.addServletMappingDecoded("/invalidStrict", "invalidStrict");
Tomcat.addServlet(ctx, "valid", new CookieServlet("name", "value"));
ctx.addServletMappingDecoded("/valid", "valid");
diff --git a/test/org/apache/tomcat/util/http/TestCookiesDefaultSysProps.java b/test/org/apache/tomcat/util/http/TestCookiesDefaultSysProps.java
index c2e220b..c8c031d 100644
--- a/test/org/apache/tomcat/util/http/TestCookiesDefaultSysProps.java
+++ b/test/org/apache/tomcat/util/http/TestCookiesDefaultSysProps.java
@@ -53,7 +53,7 @@ public class TestCookiesDefaultSysProps extends CookiesBaseTest {
res = getUrl("http://localhost:" + getPort() + "/blank");
assertEquals("Cookie name fail", res.toString());
res = getUrl("http://localhost:" + getPort() + "/invalidFwd");
- assertEquals("Cookie name ok", res.toString());
+ assertEquals("Cookie name fail", res.toString());
res = getUrl("http://localhost:" + getPort() + "/invalidStrict");
assertEquals("Cookie name ok", res.toString());
res = getUrl("http://localhost:" + getPort() + "/valid");
diff --git a/test/org/apache/tomcat/util/http/TestCookiesNoStrictNamingSysProps.java b/test/org/apache/tomcat/util/http/TestCookiesNoStrictNamingSysProps.java
index 7577d85..6a8f079 100644
--- a/test/org/apache/tomcat/util/http/TestCookiesNoStrictNamingSysProps.java
+++ b/test/org/apache/tomcat/util/http/TestCookiesNoStrictNamingSysProps.java
@@ -54,7 +54,7 @@ public class TestCookiesNoStrictNamingSysProps extends CookiesBaseTest {
res = getUrl("http://localhost:" + getPort() + "/blank");
assertEquals("Cookie name fail", res.toString());
res = getUrl("http://localhost:" + getPort() + "/invalidFwd");
- assertEquals("Cookie name ok", res.toString());
+ assertEquals("Cookie name fail", res.toString());
res = getUrl("http://localhost:" + getPort() + "/invalidStrict");
assertEquals("Cookie name ok", res.toString());
res = getUrl("http://localhost:" + getPort() + "/valid");
diff --git a/test/org/apache/tomcat/util/http/TestMimeHeadersIntegration.java b/test/org/apache/tomcat/util/http/TestMimeHeadersIntegration.java
index a7bde89..0275ba4 100644
--- a/test/org/apache/tomcat/util/http/TestMimeHeadersIntegration.java
+++ b/test/org/apache/tomcat/util/http/TestMimeHeadersIntegration.java
@@ -102,7 +102,8 @@ public class TestMimeHeadersIntegration extends TomcatBaseTest {
assertTrue("Response line is: " + client.getResponseLine(),
client.getResponseLine() == null || client.isResponse400());
}
- int maxHeaderCount = tomcat.getConnector().getMaxHeaderCount();
+ int maxHeaderCount =
+ ((Integer) tomcat.getConnector().getProperty("maxHeaderCount")).intValue();
assertEquals(expectedMaxHeaderCount, maxHeaderCount);
if (maxHeaderCount > 0) {
assertEquals(maxHeaderCount, alv.arraySize);
@@ -124,7 +125,7 @@ public class TestMimeHeadersIntegration extends TomcatBaseTest {
// Bumping into maxHttpHeaderSize
Tomcat tomcat = getTomcatInstance();
setupHeadersTest(tomcat);
- tomcat.getConnector().setMaxHeaderCount(-1);
+ tomcat.getConnector().setProperty("maxHeaderCount", "-1");
runHeadersTest(false, tomcat, 8 * 1024, -1);
}
@@ -149,7 +150,7 @@ public class TestMimeHeadersIntegration extends TomcatBaseTest {
// Can change maxHeaderCount
Tomcat tomcat = getTomcatInstance();
setupHeadersTest(tomcat);
- tomcat.getConnector().setMaxHeaderCount(-1);
+ tomcat.getConnector().setProperty("maxHeaderCount", "-1");
runHeadersTest(true, tomcat, 300, -1);
}
diff --git a/test/org/apache/tomcat/util/http/TesterHttpMessagesPerformance.java b/test/org/apache/tomcat/util/http/TesterHttpMessagesPerformance.java
deleted file mode 100644
index 8f32d1f..0000000
--- a/test/org/apache/tomcat/util/http/TesterHttpMessagesPerformance.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.tomcat.util.http;
-
-import java.util.Locale;
-
-import org.junit.Test;
-
-public class TesterHttpMessagesPerformance {
-
- @Test
- public void testGetMessage() {
- int iterations = 10000000;
- int status = 200;
-
- HttpMessages msgs = HttpMessages.getInstance(Locale.ENGLISH);
-
- for (int i = 0; i < iterations; i++) {
- msgs.getMessage(status);
- }
-
- long start = System.nanoTime();
- for (int i = 0; i < iterations; i++) {
- msgs.getMessage(status);
- }
- long end = System.nanoTime();
-
- System.out.println((end -start) + "ns");
- }
-}
diff --git a/test/org/apache/tomcat/util/net/TestCustomSsl.java b/test/org/apache/tomcat/util/net/TestCustomSsl.java
index fe0cdb2..3311036 100644
--- a/test/org/apache/tomcat/util/net/TestCustomSsl.java
+++ b/test/org/apache/tomcat/util/net/TestCustomSsl.java
@@ -58,6 +58,9 @@ public class TestCustomSsl extends TomcatBaseTest {
connector.setProperty("sslImplementationName",
"org.apache.tomcat.util.net.jsse.TesterBug50640SslImpl");
+
+ // This setting will break ssl configuration unless the custom
+ // implementation is used.
connector.setProperty(TesterBug50640SslImpl.PROPERTY_NAME,
TesterBug50640SslImpl.PROPERTY_VALUE);
diff --git a/test/org/apache/tomcat/util/net/TestSSLHostConfig.java b/test/org/apache/tomcat/util/net/TestSSLHostConfig.java
new file mode 100644
index 0000000..950b34a
--- /dev/null
+++ b/test/org/apache/tomcat/util/net/TestSSLHostConfig.java
@@ -0,0 +1,68 @@
+/*
+ * 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 org.junit.Assert;
+import org.junit.Test;
+
+import org.apache.tomcat.util.net.openssl.ciphers.Cipher;
+
+public class TestSSLHostConfig {
+
+ @Test
+ public void testCipher01() {
+ SSLHostConfig hc = new SSLHostConfig();
+ Cipher c = Cipher.TLS_RSA_WITH_NULL_MD5;
+
+ // Single JSSE name
+ hc.setCiphers(c.getJsseNames().iterator().next());
+ Assert.assertEquals(c.getOpenSSLAlias(), hc.getCiphers());
+ }
+
+
+ @Test
+ public void testCipher02() {
+ SSLHostConfig hc = new SSLHostConfig();
+ Cipher c1 = Cipher.TLS_RSA_WITH_NULL_MD5;
+ Cipher c2 = Cipher.TLS_RSA_WITH_NULL_SHA;
+
+ // Two JSSE names
+ hc.setCiphers(c1.getJsseNames().iterator().next() + "," +
+ c2.getJsseNames().iterator().next());
+ Assert.assertEquals(c1.getOpenSSLAlias() + ":" + c2.getOpenSSLAlias(), hc.getCiphers());
+ }
+
+
+ @Test
+ public void testCipher03() {
+ SSLHostConfig hc = new SSLHostConfig();
+ // Single OpenSSL alias
+ hc.setCiphers("ALL");
+ Assert.assertEquals("ALL", hc.getCiphers());
+ }
+
+
+ @Test
+ public void testCipher04() {
+ SSLHostConfig hc = new SSLHostConfig();
+ Cipher c = Cipher.TLS_RSA_WITH_NULL_MD5;
+
+ // Single OpenSSLName name
+ hc.setCiphers(c.getOpenSSLAlias());
+ Assert.assertEquals(c.getOpenSSLAlias(), hc.getCiphers());
+ }
+}
diff --git a/test/org/apache/tomcat/util/net/TestSSLHostConfigIntegration.java b/test/org/apache/tomcat/util/net/TestSSLHostConfigIntegration.java
new file mode 100644
index 0000000..0d7ed06
--- /dev/null
+++ b/test/org/apache/tomcat/util/net/TestSSLHostConfigIntegration.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tomcat.util.net;
+
+import java.io.File;
+import java.io.ObjectOutputStream;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import org.apache.catalina.startup.Tomcat;
+import org.apache.catalina.startup.TomcatBaseTest;
+import org.apache.tomcat.util.http.fileupload.ByteArrayOutputStream;
+import org.apache.tomcat.websocket.server.WsContextListener;
+
+public class TestSSLHostConfigIntegration extends TomcatBaseTest {
+
+ @Test
+ public void testSslHostConfigIsSerializable() throws Exception {
+ TesterSupport.configureClientSsl();
+
+ Tomcat tomcat = getTomcatInstance();
+
+ File appDir = new File(getBuildDirectory(), "webapps/examples");
+ org.apache.catalina.Context ctxt = tomcat.addWebapp(
+ null, "/examples", appDir.getAbsolutePath());
+ ctxt.addApplicationListener(WsContextListener.class.getName());
+
+ TesterSupport.initSsl(tomcat);
+
+ tomcat.start();
+
+ SSLHostConfig[] sslHostConfigs =
+ tomcat.getConnector().getProtocolHandler().findSslHostConfigs();
+
+ boolean written = false;
+
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ try (ObjectOutputStream oos = new ObjectOutputStream(baos)) {
+ for (SSLHostConfig sslHostConfig : sslHostConfigs) {
+ oos.writeObject(sslHostConfig);
+ written = true;
+ }
+ }
+
+ Assert.assertTrue(written);
+ }
+}
diff --git a/test/org/apache/tomcat/util/net/TestSsl.java b/test/org/apache/tomcat/util/net/TestSsl.java
index eb910e2..3d41294 100644
--- a/test/org/apache/tomcat/util/net/TestSsl.java
+++ b/test/org/apache/tomcat/util/net/TestSsl.java
@@ -95,7 +95,7 @@ public class TestSsl extends TomcatBaseTest {
Tomcat tomcat = getTomcatInstance();
Assume.assumeTrue("SSL renegotiation has to be supported for this test",
- TesterSupport.isRenegotiationSupported(getTomcatInstance()));
+ TesterSupport.isClientRenegotiationSupported(getTomcatInstance()));
Context root = tomcat.addContext("", TEMP_DIR);
Wrapper w =
@@ -147,7 +147,7 @@ public class TestSsl extends TomcatBaseTest {
}
private void doRequest(OutputStream os, Reader r) throws IOException {
- char[] expectedResponseLine = "HTTP/1.1 200 OK\r\n".toCharArray();
+ char[] expectedResponseLine = "HTTP/1.1 200 \r\n".toCharArray();
os.write("GET /tester HTTP/1.1\r\n".getBytes());
os.write("Host: localhost\r\n".getBytes());
diff --git a/test/org/apache/tomcat/util/net/TestXxxEndpoint.java b/test/org/apache/tomcat/util/net/TestXxxEndpoint.java
index 09e754d..7a1a4c1 100644
--- a/test/org/apache/tomcat/util/net/TestXxxEndpoint.java
+++ b/test/org/apache/tomcat/util/net/TestXxxEndpoint.java
@@ -30,8 +30,6 @@ import org.junit.Test;
import org.apache.catalina.connector.Connector;
import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.startup.TomcatBaseTest;
-import org.apache.juli.logging.Log;
-import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.jni.Address;
import org.apache.tomcat.jni.Error;
import org.apache.tomcat.jni.Library;
@@ -45,8 +43,6 @@ import org.apache.tomcat.jni.Socket;
*/
public class TestXxxEndpoint extends TomcatBaseTest {
- private static Log log = LogFactory.getLog(TestXxxEndpoint.class);
-
private long createAprPool() {
// Create the pool for the server socket
diff --git a/test/org/apache/tomcat/util/net/TesterSupport.java b/test/org/apache/tomcat/util/net/TesterSupport.java
index b3e3c4f..862d1a0 100644
--- a/test/org/apache/tomcat/util/net/TesterSupport.java
+++ b/test/org/apache/tomcat/util/net/TesterSupport.java
@@ -39,9 +39,9 @@ import org.apache.catalina.Context;
import org.apache.catalina.authenticator.SSLAuthenticator;
import org.apache.catalina.connector.Connector;
import org.apache.catalina.core.AprLifecycleListener;
+import org.apache.catalina.core.StandardServer;
import org.apache.catalina.startup.TesterMapRealm;
import org.apache.catalina.startup.Tomcat;
-import org.apache.tomcat.jni.SSL;
import org.apache.tomcat.util.descriptor.web.LoginConfig;
import org.apache.tomcat.util.descriptor.web.SecurityCollection;
import org.apache.tomcat.util.descriptor.web.SecurityConstraint;
@@ -60,6 +60,14 @@ public final class TesterSupport {
String protocol = tomcat.getConnector().getProtocolHandlerClassName();
if (protocol.indexOf("Apr") == -1) {
Connector connector = tomcat.getConnector();
+ String sslImplementation = System.getProperty("tomcat.test.sslImplementation");
+ if (sslImplementation != null && !"${test.sslImplementation}".equals(sslImplementation)) {
+ StandardServer server = (StandardServer) tomcat.getServer();
+ AprLifecycleListener listener = new AprLifecycleListener();
+ listener.setSSLRandomSeed("/dev/urandom");
+ server.addLifecycleListener(listener);
+ tomcat.getConnector().setAttribute("sslImplementationName", sslImplementation);
+ }
connector.setProperty("sslProtocol", "tls");
File keystoreFile =
new File("test/org/apache/tomcat/util/net/" + keystore);
@@ -87,14 +95,6 @@ public final class TesterSupport {
}
tomcat.getConnector().setSecure(true);
tomcat.getConnector().setProperty("SSLEnabled", "true");
- // OpenSSL before 1.0.1 only supports TLSv1.
- // Our default SSLProtocol setting "all" includes unsupported TLSv1.1 and 1.2
- // and would produce an error during init.
- // Trigger loading of the native library and choose old protocol
- // if we use old OpenSSL.
- if (AprLifecycleListener.isAprAvailable() && SSL.version() < 0x10001000L) {
- tomcat.getConnector().setProperty("SSLProtocol", Constants.SSL_PROTO_TLSv1);
- }
}
protected static KeyManager[] getUser1KeyManagers() throws Exception {
@@ -144,10 +144,25 @@ public final class TesterSupport {
// Disabled by default in 1.1.20 windows binary (2010-07-27)
return false;
}
+
+ return true;
+ }
+
+ protected static boolean isClientRenegotiationSupported(Tomcat tomcat) {
+ String protocol = tomcat.getConnector().getProtocolHandlerClassName();
+ if (protocol.contains("Apr")) {
+ // Disabled by default in 1.1.20 windows binary (2010-07-27)
+ return false;
+ }
if (protocol.contains("NioProtocol") || (protocol.contains("Nio2Protocol") && isMacOs())) {
// Doesn't work on all platforms - see BZ 56448.
return false;
}
+ String sslImplementation = System.getProperty("tomcat.test.sslImplementation");
+ if (sslImplementation != null && !"${test.sslImplementation}".equals(sslImplementation)) {
+ // Assume custom SSL is not supporting this
+ return false;
+ }
return true;
}
diff --git a/test/org/apache/tomcat/util/net/jsse/TesterBug50640SslImpl.java b/test/org/apache/tomcat/util/net/jsse/TesterBug50640SslImpl.java
index 0ac6a0e..b9cba53 100644
--- a/test/org/apache/tomcat/util/net/jsse/TesterBug50640SslImpl.java
+++ b/test/org/apache/tomcat/util/net/jsse/TesterBug50640SslImpl.java
@@ -16,25 +16,25 @@
*/
package org.apache.tomcat.util.net.jsse;
-import org.apache.tomcat.util.net.AbstractEndpoint;
-import org.apache.tomcat.util.net.ServerSocketFactory;
+import org.apache.tomcat.util.net.SSLHostConfig;
+import org.apache.tomcat.util.net.SSLHostConfigCertificate;
+import org.apache.tomcat.util.net.SSLUtil;
public class TesterBug50640SslImpl extends JSSEImplementation {
- public static final String PROPERTY_NAME = "bug50640";
- public static final String PROPERTY_VALUE = "pass";
+ public static final String PROPERTY_NAME = "sslEnabledProtocols";
+ public static final String PROPERTY_VALUE = "magic";
- @Override
- public ServerSocketFactory getServerSocketFactory(
- AbstractEndpoint<?> endpoint) {
- // Check the custom attribute is visible & correcly set
- String flag = endpoint.getProperty(PROPERTY_NAME);
- if (PROPERTY_VALUE.equals(flag)) {
- return super.getServerSocketFactory(endpoint);
+ @Override
+ public SSLUtil getSSLUtil(SSLHostConfigCertificate certificate) {
+ SSLHostConfig sslHostConfig = certificate.getSSLHostConfig();
+ if (sslHostConfig.getProtocols().size() == 1 &&
+ sslHostConfig.getProtocols().contains(PROPERTY_VALUE)) {
+ sslHostConfig.setProtocols("TLSv1,TLSv1.1,TLSv1.2");
+ return super.getSSLUtil(certificate);
} else {
return null;
}
}
-
}
diff --git a/test/org/apache/tomcat/util/net/jsse/openssl/TestCipher.java b/test/org/apache/tomcat/util/net/jsse/openssl/TestCipher.java
deleted file mode 100644
index 9267eea..0000000
--- a/test/org/apache/tomcat/util/net/jsse/openssl/TestCipher.java
+++ /dev/null
@@ -1,1067 +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.net.jsse.openssl;
-
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-import org.junit.Assert;
-import org.junit.Test;
-
-public class TestCipher {
-
- /*
- * Checks that every cipher suite returned by OpenSSL is mapped to at least
- * one cipher suite that is recognised by JSSE or is a cipher suite known
- * not to be supported by JSSE.
- */
- @Test
- public void testAllOpenSSLCiphersMapped() throws Exception {
- Set<String> openSSLCipherSuites = TesterOpenSSL.getOpenSSLCiphersAsSet("ALL:eNULL");
-
- StringBuilder errors = new StringBuilder();
-
- for (String openSSLCipherSuite : openSSLCipherSuites) {
- List<String> jsseCipherSuites =
- OpenSSLCipherConfigurationParser.parseExpression(openSSLCipherSuite);
-
- for (JsseImpl jsseImpl : JSSE_IMPLS) {
- boolean found = false;
- for (String jsseCipherSuite : jsseCipherSuites) {
- if (jsseImpl.getStandardNames().contains(jsseCipherSuite)) {
- found = true;
- if (jsseImpl.getOpenSslUnmapped().contains(openSSLCipherSuite)) {
- errors.append("Mapping found in " + jsseImpl.getVendor() +
- "'s JSSE implementation for " + openSSLCipherSuite +
- " when none was expected\n");
- }
- break;
- }
- }
- if (!found && !jsseImpl.getOpenSslUnmapped().contains(openSSLCipherSuite)) {
- errors.append("No mapping found in " + jsseImpl.getVendor() +
- "'s JSSE implementation for " + openSSLCipherSuite +
- " when one was expected\n");
- }
- }
- }
- Assert.assertTrue(errors.toString(), errors.length() == 0);
- }
-
-
- /*
- * Checks that the unit tests are running with a version of OpenSSL that
- * includes all the expected ciphers and does not include any unexpected
- * ones.
- */
- @Test
- public void testOpenSSLCipherAvailability() throws Exception {
- // 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.
- Set<String> availableCipherSuites = TesterOpenSSL.getOpenSSLCiphersAsSet("ALL:eNULL:aRSA");
- Set<String> expectedCipherSuites = new HashSet<>();
- for (Cipher cipher : Cipher.values()) {
- if (TesterOpenSSL.OPENSSL_UNIMPLEMENTED_CIPHERS.contains(cipher)) {
- continue;
- }
- expectedCipherSuites.add(cipher.getOpenSSLAlias() + "+" +
- cipher.getProtocol().getOpenSSLName());
- }
-
- Set<String> unavailableCipherSuites = new HashSet<>();
- unavailableCipherSuites.addAll(expectedCipherSuites);
- unavailableCipherSuites.removeAll(availableCipherSuites);
- StringBuilder unavailableList = new StringBuilder();
- for (String cipher : unavailableCipherSuites) {
- unavailableList.append(cipher);
- unavailableList.append(' ');
- }
- Assert.assertEquals(unavailableList.toString(), 0, unavailableCipherSuites.size());
-
- Set<String> unexpectedCipherSuites = new HashSet<>();
- unexpectedCipherSuites.addAll(availableCipherSuites);
- unexpectedCipherSuites.removeAll(expectedCipherSuites);
- StringBuilder unexpectedList = new StringBuilder();
- for (String cipher : unexpectedCipherSuites) {
- unexpectedList.append(cipher);
- unexpectedList.append(' ');
- }
- Assert.assertEquals(unexpectedList.toString(), 0, unexpectedCipherSuites.size());
- }
-
-
- /**
- * Check that the elements of the Cipher enumeration are all using standard
- * names from the TLS registry or are known exceptions.
- */
- @Test
- public void testNames() {
- for (Cipher cipher : Cipher.values()) {
- String name = cipher.name();
- // These do not appear in TLS registry
- if (name.contains("FORTEZZA")) {
- continue;
- }
- if (name.contains("EXPORT1024") || name.equals("TLS_DHE_DSS_WITH_RC4_128_SHA")) {
- continue;
- }
- if (name.startsWith("SSL_CK") || name.startsWith("SSL2")) {
- continue;
- }
- Assert.assertTrue("Non-registered name used in Cipher enumeration: " + cipher,
- REGISTERED_NAMES.contains(name));
- }
- }
-
-
- /**
- * These are all the Oracle standard Java names for cipher suites taken from
- * http://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#ciphersuites
- * on 15th July 2014.
- */
- private static final Set<String> CIPHER_SUITE_STANDARD_NAMES_ORACLE =
- Collections.unmodifiableSet(new HashSet<>(Arrays.asList(
- "SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA",
- "SSL_DH_anon_EXPORT_WITH_RC4_40_MD5",
- "SSL_DH_anon_WITH_3DES_EDE_CBC_SHA",
- "TLS_DH_anon_WITH_AES_128_CBC_SHA",
- "TLS_DH_anon_WITH_AES_128_CBC_SHA256",
- "TLS_DH_anon_WITH_AES_128_GCM_SHA256",
- "TLS_DH_anon_WITH_AES_256_CBC_SHA",
- "TLS_DH_anon_WITH_AES_256_CBC_SHA256",
- "TLS_DH_anon_WITH_AES_256_GCM_SHA384",
- "TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA",
- "TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA256",
- "TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA",
- "TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA256",
- "SSL_DH_anon_WITH_DES_CBC_SHA",
- "SSL_DH_anon_WITH_RC4_128_MD5",
- "TLS_DH_anon_WITH_SEED_CBC_SHA",
- "SSL_DH_DSS_EXPORT_WITH_DES40_CBC_SHA",
- "SSL_DH_DSS_WITH_3DES_EDE_CBC_SHA",
- "TLS_DH_DSS_WITH_AES_128_CBC_SHA",
- "TLS_DH_DSS_WITH_AES_128_CBC_SHA256",
- "TLS_DH_DSS_WITH_AES_128_GCM_SHA256",
- "TLS_DH_DSS_WITH_AES_256_CBC_SHA",
- "TLS_DH_DSS_WITH_AES_256_CBC_SHA256",
- "TLS_DH_DSS_WITH_AES_256_GCM_SHA384",
- "TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA",
- "TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256",
- "TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA",
- "TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256",
- "SSL_DH_DSS_WITH_DES_CBC_SHA",
- "TLS_DH_DSS_WITH_SEED_CBC_SHA",
- "SSL_DH_RSA_EXPORT_WITH_DES40_CBC_SHA",
- "SSL_DH_RSA_WITH_3DES_EDE_CBC_SHA",
- "TLS_DH_RSA_WITH_AES_128_CBC_SHA",
- "TLS_DH_RSA_WITH_AES_128_CBC_SHA256",
- "TLS_DH_RSA_WITH_AES_128_GCM_SHA256",
- "TLS_DH_RSA_WITH_AES_256_CBC_SHA",
- "TLS_DH_RSA_WITH_AES_256_CBC_SHA256",
- "TLS_DH_RSA_WITH_AES_256_GCM_SHA384",
- "TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA",
- "TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256",
- "TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA",
- "TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256",
- "SSL_DH_RSA_WITH_DES_CBC_SHA",
- "TLS_DH_RSA_WITH_SEED_CBC_SHA",
- "SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA",
- "SSL_DHE_DSS_EXPORT1024_WITH_DES_CBC_SHA",
- "SSL_DHE_DSS_EXPORT1024_WITH_RC4_56_SHA",
- "SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA",
- "TLS_DHE_DSS_WITH_AES_128_CBC_SHA",
- "TLS_DHE_DSS_WITH_AES_128_CBC_SHA256",
- "TLS_DHE_DSS_WITH_AES_128_GCM_SHA256",
- "TLS_DHE_DSS_WITH_AES_256_CBC_SHA",
- "TLS_DHE_DSS_WITH_AES_256_CBC_SHA256",
- "TLS_DHE_DSS_WITH_AES_256_GCM_SHA384",
- "TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA",
- "TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256",
- "TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA",
- "TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256",
- "SSL_DHE_DSS_WITH_DES_CBC_SHA",
- "SSL_DHE_DSS_WITH_RC4_128_SHA",
- "TLS_DHE_DSS_WITH_SEED_CBC_SHA",
- "TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA",
- "TLS_DHE_PSK_WITH_AES_128_CBC_SHA",
- "TLS_DHE_PSK_WITH_AES_128_CBC_SHA256",
- "TLS_DHE_PSK_WITH_AES_128_GCM_SHA256",
- "TLS_DHE_PSK_WITH_AES_256_CBC_SHA",
- "TLS_DHE_PSK_WITH_AES_256_CBC_SHA384",
- "TLS_DHE_PSK_WITH_AES_256_GCM_SHA384",
- "TLS_DHE_PSK_WITH_NULL_SHA",
- "TLS_DHE_PSK_WITH_NULL_SHA256",
- "TLS_DHE_PSK_WITH_NULL_SHA384",
- "TLS_DHE_PSK_WITH_RC4_128_SHA",
- "SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA",
- "SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA",
- "TLS_DHE_RSA_WITH_AES_128_CBC_SHA",
- "TLS_DHE_RSA_WITH_AES_128_CBC_SHA256",
- "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256",
- "TLS_DHE_RSA_WITH_AES_256_CBC_SHA",
- "TLS_DHE_RSA_WITH_AES_256_CBC_SHA256",
- "TLS_DHE_RSA_WITH_AES_256_GCM_SHA384",
- "TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA",
- "TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256",
- "TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA",
- "TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256",
- "SSL_DHE_RSA_WITH_DES_CBC_SHA",
- "TLS_DHE_RSA_WITH_SEED_CBC_SHA",
- "TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA",
- "TLS_ECDH_anon_WITH_AES_128_CBC_SHA",
- "TLS_ECDH_anon_WITH_AES_256_CBC_SHA",
- "TLS_ECDH_anon_WITH_NULL_SHA",
- "TLS_ECDH_anon_WITH_RC4_128_SHA",
- "TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA",
- "TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA",
- "TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256",
- "TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256",
- "TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA",
- "TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384",
- "TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384",
- "TLS_ECDH_ECDSA_WITH_NULL_SHA",
- "TLS_ECDH_ECDSA_WITH_RC4_128_SHA",
- "TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA",
- "TLS_ECDH_RSA_WITH_AES_128_CBC_SHA",
- "TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256",
- "TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256",
- "TLS_ECDH_RSA_WITH_AES_256_CBC_SHA",
- "TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384",
- "TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384",
- "TLS_ECDH_RSA_WITH_NULL_SHA",
- "TLS_ECDH_RSA_WITH_RC4_128_SHA",
- "TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA",
- "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
- "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",
- "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
- "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
- "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384",
- "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
- "TLS_ECDHE_ECDSA_WITH_NULL_SHA",
- "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA",
- "TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA",
- "TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA",
- "TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256",
- "TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA",
- "TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384",
- "TLS_ECDHE_PSK_WITH_NULL_SHA",
- "TLS_ECDHE_PSK_WITH_NULL_SHA256",
- "TLS_ECDHE_PSK_WITH_NULL_SHA384",
- "TLS_ECDHE_PSK_WITH_RC4_128_SHA",
- "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA",
- "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
- "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",
- "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
- "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
- "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384",
- "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
- "TLS_ECDHE_RSA_WITH_NULL_SHA",
- "TLS_ECDHE_RSA_WITH_RC4_128_SHA",
- "TLS_EMPTY_RENEGOTIATION_INFO_SCSV",
- "SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA",
- "SSL_FORTEZZA_DMS_WITH_NULL_SHA",
- "TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5",
- "TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA",
- "TLS_KRB5_EXPORT_WITH_RC2_CBC_40_MD5",
- "TLS_KRB5_EXPORT_WITH_RC2_CBC_40_SHA",
- "TLS_KRB5_EXPORT_WITH_RC4_40_MD5",
- "TLS_KRB5_EXPORT_WITH_RC4_40_SHA",
- "TLS_KRB5_WITH_3DES_EDE_CBC_MD5",
- "TLS_KRB5_WITH_3DES_EDE_CBC_SHA",
- "TLS_KRB5_WITH_DES_CBC_MD5",
- "TLS_KRB5_WITH_DES_CBC_SHA",
- "TLS_KRB5_WITH_IDEA_CBC_MD5",
- "TLS_KRB5_WITH_IDEA_CBC_SHA",
- "TLS_KRB5_WITH_RC4_128_MD5",
- "TLS_KRB5_WITH_RC4_128_SHA",
- "TLS_PSK_WITH_3DES_EDE_CBC_SHA",
- "TLS_PSK_WITH_AES_128_CBC_SHA",
- "TLS_PSK_WITH_AES_128_CBC_SHA256",
- "TLS_PSK_WITH_AES_128_GCM_SHA256",
- "TLS_PSK_WITH_AES_256_CBC_SHA",
- "TLS_PSK_WITH_AES_256_CBC_SHA384",
- "TLS_PSK_WITH_AES_256_GCM_SHA384",
- "TLS_PSK_WITH_NULL_SHA",
- "TLS_PSK_WITH_NULL_SHA256",
- "TLS_PSK_WITH_NULL_SHA384",
- "TLS_PSK_WITH_RC4_128_SHA",
- "SSL_RSA_EXPORT_WITH_DES40_CBC_SHA",
- "SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5",
- "SSL_RSA_EXPORT_WITH_RC4_40_MD5",
- "SSL_RSA_EXPORT1024_WITH_DES_CBC_SHA",
- "SSL_RSA_EXPORT1024_WITH_RC4_56_SHA",
- "SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA",
- "SSL_RSA_FIPS_WITH_DES_CBC_SHA",
- "TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA",
- "TLS_RSA_PSK_WITH_AES_128_CBC_SHA",
- "TLS_RSA_PSK_WITH_AES_128_CBC_SHA256",
- "TLS_RSA_PSK_WITH_AES_128_GCM_SHA256",
- "TLS_RSA_PSK_WITH_AES_256_CBC_SHA",
- "TLS_RSA_PSK_WITH_AES_256_CBC_SHA384",
- "TLS_RSA_PSK_WITH_AES_256_GCM_SHA384",
- "TLS_RSA_PSK_WITH_NULL_SHA",
- "TLS_RSA_PSK_WITH_NULL_SHA256",
- "TLS_RSA_PSK_WITH_NULL_SHA384",
- "TLS_RSA_PSK_WITH_RC4_128_SHA",
- "SSL_RSA_WITH_3DES_EDE_CBC_SHA",
- "TLS_RSA_WITH_AES_128_CBC_SHA",
- "TLS_RSA_WITH_AES_128_CBC_SHA256",
- "TLS_RSA_WITH_AES_128_GCM_SHA256",
- "TLS_RSA_WITH_AES_256_CBC_SHA",
- "TLS_RSA_WITH_AES_256_CBC_SHA256",
- "TLS_RSA_WITH_AES_256_GCM_SHA384",
- "TLS_RSA_WITH_CAMELLIA_128_CBC_SHA",
- "TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256",
- "TLS_RSA_WITH_CAMELLIA_256_CBC_SHA",
- "TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256",
- "SSL_RSA_WITH_DES_CBC_SHA",
- "SSL_RSA_WITH_IDEA_CBC_SHA",
- "SSL_RSA_WITH_NULL_MD5",
- "SSL_RSA_WITH_NULL_SHA",
- "TLS_RSA_WITH_NULL_SHA256",
- "SSL_RSA_WITH_RC4_128_MD5",
- "SSL_RSA_WITH_RC4_128_SHA",
- "TLS_RSA_WITH_SEED_CBC_SHA",
- "TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA",
- "TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA",
- "TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA",
- "TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA",
- "TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA",
- "TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA",
- "TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA",
- "TLS_SRP_SHA_WITH_AES_128_CBC_SHA",
- "TLS_SRP_SHA_WITH_AES_256_CBC_SHA")));
-
-
- /**
- * These are the cipher suites implemented by OpenSSL that are not
- * implemented by Oracle's JSSE implementation.
- */
- private static Set<String> OPENSSL_UNMAPPED_ORACLE =
- Collections.unmodifiableSet(new HashSet<>(Arrays.asList(
- "AES128-CCM+TLSv1.2",
- "AES128-CCM8+TLSv1.2",
- "AES256-CCM+TLSv1.2",
- "AES256-CCM8+TLSv1.2",
- "DES-CBC-MD5+SSLv2",
- "DES-CBC3-MD5+SSLv2",
- "DHE-PSK-AES128-CCM+TLSv1.2",
- "DHE-PSK-AES128-CCM8+TLSv1.2",
- "DHE-PSK-AES256-CCM+TLSv1.2",
- "DHE-PSK-AES256-CCM8+TLSv1.2",
- "DHE-PSK-CAMELLIA128-SHA256+TLSv1",
- "DHE-PSK-CAMELLIA256-SHA384+TLSv1",
- "DHE-PSK-CHACHA20-POLY1305+TLSv1.2",
- "DHE-RSA-AES128-CCM+TLSv1.2",
- "DHE-RSA-AES128-CCM8+TLSv1.2",
- "DHE-RSA-AES256-CCM+TLSv1.2",
- "DHE-RSA-AES256-CCM8+TLSv1.2",
- "DHE-RSA-CHACHA20-POLY1305+TLSv1.2",
- "ECDH-ECDSA-CAMELLIA128-SHA256+TLSv1.2",
- "ECDH-ECDSA-CAMELLIA256-SHA384+TLSv1.2",
- "ECDH-RSA-CAMELLIA128-SHA256+TLSv1.2",
- "ECDH-RSA-CAMELLIA256-SHA384+TLSv1.2",
- "ECDHE-ECDSA-AES128-CCM+TLSv1.2",
- "ECDHE-ECDSA-AES128-CCM8+TLSv1.2",
- "ECDHE-ECDSA-AES256-CCM+TLSv1.2",
- "ECDHE-ECDSA-AES256-CCM8+TLSv1.2",
- "ECDHE-ECDSA-CAMELLIA128-SHA256+TLSv1.2",
- "ECDHE-ECDSA-CAMELLIA256-SHA384+TLSv1.2",
- "ECDHE-ECDSA-CHACHA20-POLY1305+TLSv1.2",
- "ECDHE-PSK-CAMELLIA128-SHA256+TLSv1",
- "ECDHE-PSK-CAMELLIA256-SHA384+TLSv1",
- "ECDHE-PSK-CHACHA20-POLY1305+TLSv1.2",
- "ECDHE-RSA-CAMELLIA128-SHA256+TLSv1.2",
- "ECDHE-RSA-CAMELLIA256-SHA384+TLSv1.2",
- "ECDHE-RSA-CHACHA20-POLY1305+TLSv1.2",
- "EXP-RC2-CBC-MD5+SSLv2",
- "EXP-RC4-MD5+SSLv2",
- "IDEA-CBC-MD5+SSLv2",
- "PSK-AES128-CCM+TLSv1.2",
- "PSK-AES128-CCM8+TLSv1.2",
- "PSK-AES256-CCM+TLSv1.2",
- "PSK-AES256-CCM8+TLSv1.2",
- "PSK-CAMELLIA128-SHA256+TLSv1",
- "PSK-CAMELLIA256-SHA384+TLSv1",
- "PSK-CHACHA20-POLY1305+TLSv1.2",
- "RC2-CBC-MD5+SSLv2",
- "RC4-MD5+SSLv2",
- "RSA-PSK-CAMELLIA128-SHA256+TLSv1",
- "RSA-PSK-CAMELLIA256-SHA384+TLSv1",
- "RSA-PSK-CHACHA20-POLY1305+TLSv1.2")));
-
-
- /**
- * These are all the IBM standard Java names for cipher suites taken from
- * http://www-01.ibm.com/support/knowledgecenter/SSYKE2_7.0.0/com.ibm.java.security.component.71.doc/security-component/jsse2Docs/ciphersuites.html?lang=en
- * on 29th July 2014.
- * <br>
- * As of 16 February 2015 the list for IBM Java 7 was identical to that for
- * IBM Java 8
- * http://www-01.ibm.com/support/knowledgecenter/SSYKE2_8.0.0/com.ibm.java.security.component.80.doc/security-component/jsse2Docs/ciphersuites.html?lang=en
- * <br>
- * Note that IBM cipher suites names can begin with TLS or SSL.
- */
- private static final Set<String> CIPHER_SUITE_STANDARD_NAMES_IBM;
-
- static {
- Set<String> sslNames = new HashSet<>(Arrays.asList(
- "SSL_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384",
- "SSL_ECDHE_RSA_WITH_AES_256_CBC_SHA384",
- "SSL_RSA_WITH_AES_256_CBC_SHA256",
- "SSL_ECDH_ECDSA_WITH_AES_256_CBC_SHA384",
- "SSL_ECDH_RSA_WITH_AES_256_CBC_SHA384",
- "SSL_DHE_RSA_WITH_AES_256_CBC_SHA256",
- "SSL_DHE_DSS_WITH_AES_256_CBC_SHA256",
- "SSL_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
- "SSL_ECDHE_RSA_WITH_AES_256_CBC_SHA",
- "SSL_RSA_WITH_AES_256_CBC_SHA",
- "SSL_ECDH_ECDSA_WITH_AES_256_CBC_SHA",
- "SSL_ECDH_RSA_WITH_AES_256_CBC_SHA",
- "SSL_DHE_RSA_WITH_AES_256_CBC_SHA",
- "SSL_DHE_DSS_WITH_AES_256_CBC_SHA",
- "SSL_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",
- "SSL_ECDHE_RSA_WITH_AES_128_CBC_SHA256",
- "SSL_RSA_WITH_AES_128_CBC_SHA256",
- "SSL_ECDH_ECDSA_WITH_AES_128_CBC_SHA256",
- "SSL_ECDH_RSA_WITH_AES_128_CBC_SHA256",
- "SSL_DHE_RSA_WITH_AES_128_CBC_SHA256",
- "SSL_DHE_DSS_WITH_AES_128_CBC_SHA256",
- "SSL_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
- "SSL_ECDHE_RSA_WITH_AES_128_CBC_SHA",
- "SSL_RSA_WITH_AES_128_CBC_SHA",
- "SSL_ECDH_ECDSA_WITH_AES_128_CBC_SHA",
- "SSL_ECDH_RSA_WITH_AES_128_CBC_SHA",
- "SSL_DHE_RSA_WITH_AES_128_CBC_SHA",
- "SSL_DHE_DSS_WITH_AES_128_CBC_SHA",
- "SSL_ECDHE_ECDSA_WITH_RC4_128_SHA",
- "SSL_ECDHE_RSA_WITH_RC4_128_SHA",
- "SSL_RSA_WITH_RC4_128_SHA",
- "SSL_ECDH_ECDSA_WITH_RC4_128_SHA",
- "SSL_ECDH_RSA_WITH_RC4_128_SHA",
- "SSL_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA",
- "SSL_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA",
- "SSL_RSA_WITH_3DES_EDE_CBC_SHA",
- "SSL_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA",
- "SSL_ECDH_RSA_WITH_3DES_EDE_CBC_SHA",
- "SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA",
- "SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA",
- "SSL_RSA_WITH_RC4_128_MD5",
- "TLS_EMPTY_RENEGOTIATION_INFO_SCSV",
- "SSL_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
- "SSL_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
- "SSL_RSA_WITH_AES_256_GCM_SHA384",
- "SSL_ECDH_ECDSA_WITH_AES_256_GCM_SHA384",
- "SSL_ECDH_RSA_WITH_AES_256_GCM_SHA384",
- "SSL_DHE_DSS_WITH_AES_256_GCM_SHA384",
- "SSL_DHE_RSA_WITH_AES_256_GCM_SHA384",
- "SSL_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
- "SSL_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
- "SSL_RSA_WITH_AES_128_GCM_SHA256",
- "SSL_ECDH_ECDSA_WITH_AES_128_GCM_SHA256",
- "SSL_ECDH_RSA_WITH_AES_128_GCM_SHA256",
- "SSL_DHE_RSA_WITH_AES_128_GCM_SHA256",
- "SSL_DHE_DSS_WITH_AES_128_GCM_SHA256",
- "SSL_DH_anon_WITH_AES_256_CBC_SHA256",
- "SSL_ECDH_anon_WITH_AES_256_CBC_SHA",
- "SSL_DH_anon_WITH_AES_256_CBC_SHA",
- "SSL_DH_anon_WITH_AES_256_GCM_SHA384",
- "SSL_DH_anon_WITH_AES_128_GCM_SHA256",
- "SSL_DH_anon_WITH_AES_128_CBC_SHA256",
- "SSL_ECDH_anon_WITH_AES_128_CBC_SHA",
- "SSL_DH_anon_WITH_AES_128_CBC_SHA",
- "SSL_ECDH_anon_WITH_RC4_128_SHA",
- "SSL_DH_anon_WITH_RC4_128_MD5",
- "SSL_ECDH_anon_WITH_3DES_EDE_CBC_SHA",
- "SSL_DH_anon_WITH_3DES_EDE_CBC_SHA",
- "SSL_RSA_WITH_NULL_SHA256",
- "SSL_ECDHE_ECDSA_WITH_NULL_SHA",
- "SSL_ECDHE_RSA_WITH_NULL_SHA",
- "SSL_RSA_WITH_NULL_SHA",
- "SSL_ECDH_ECDSA_WITH_NULL_SHA",
- "SSL_ECDH_RSA_WITH_NULL_SHA",
- "SSL_ECDH_anon_WITH_NULL_SHA",
- "SSL_RSA_WITH_NULL_MD5",
- "SSL_RSA_WITH_DES_CBC_SHA",
- "SSL_DHE_RSA_WITH_DES_CBC_SHA",
- "SSL_DHE_DSS_WITH_DES_CBC_SHA",
- "SSL_DH_anon_WITH_DES_CBC_SHA",
- "SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA",
- "SSL_RSA_FIPS_WITH_DES_EDE_CBC_SHA",
- "SSL_DHE_DSS_WITH_RC4_128_SHA",
- "SSL_RSA_EXPORT_WITH_RC4_40_MD5",
- "SSL_DH_anon_EXPORT_WITH_RC4_40_MD5",
- "SSL_RSA_EXPORT_WITH_DES40_CBC_SHA",
- "SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA",
- "SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA",
- "SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA",
- "SSL_KRB5_WITH_RC4_128_SHA",
- "SSL_KRB5_WITH_RC4_128_MD5",
- "SSL_KRB5_WITH_3DES_EDE_CBC_SHA",
- "SSL_KRB5_WITH_3DES_EDE_CBC_MD5",
- "SSL_KRB5_WITH_DES_CBC_SHA",
- "SSL_KRB5_WITH_DES_CBC_MD5",
- "SSL_KRB5_EXPORT_WITH_RC4_40_SHA",
- "SSL_KRB5_EXPORT_WITH_RC4_40_MD5",
- "SSL_KRB5_EXPORT_WITH_DES_CBC_40_SHA",
- "SSL_KRB5_EXPORT_WITH_DES_CBC_40_MD5",
- "SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5"));
-
- Set<String> allNames = new HashSet<>();
-
- allNames.addAll(sslNames);
-
- for (String sslName : sslNames) {
- allNames.add("TLS" + sslName.substring(3));
- }
-
- CIPHER_SUITE_STANDARD_NAMES_IBM = Collections.unmodifiableSet(allNames);
- }
-
-
- /**
- * These are the cipher suites implemented by OpenSSL that are not
- * implemented by IBM's JSSE implementation.
- */
- private static Set<String> OPENSSL_UNMAPPED_IBM =
- Collections.unmodifiableSet(new HashSet<>(Arrays.asList(
- "ADH-CAMELLIA128-SHA+SSLv3",
- "ADH-CAMELLIA256-SHA+SSLv3",
- "ADH-CAMELLIA128-SHA256+TLSv1.2",
- "ADH-CAMELLIA256-SHA256+TLSv1.2",
- "ADH-SEED-SHA+SSLv3",
- "AES128-CCM+TLSv1.2",
- "AES128-CCM8+TLSv1.2",
- "AES256-CCM+TLSv1.2",
- "AES256-CCM8+TLSv1.2",
- "CAMELLIA128-SHA+SSLv3",
- "CAMELLIA256-SHA+SSLv3",
- "CAMELLIA128-SHA256+TLSv1.2",
- "CAMELLIA256-SHA256+TLSv1.2",
- "DES-CBC-MD5+SSLv2",
- "DES-CBC3-MD5+SSLv2",
- "DH-DSS-AES128-GCM-SHA256+TLSv1.2",
- "DH-DSS-AES256-GCM-SHA384+TLSv1.2",
- "DH-DSS-AES128-SHA+SSLv3",
- "DH-DSS-AES128-SHA256+TLSv1.2",
- "DH-DSS-AES256-SHA+SSLv3",
- "DH-DSS-AES256-SHA256+TLSv1.2",
- "DH-DSS-CAMELLIA128-SHA+SSLv3",
- "DH-DSS-CAMELLIA128-SHA256+TLSv1.2",
- "DH-DSS-CAMELLIA256-SHA+SSLv3",
- "DH-DSS-CAMELLIA256-SHA256+TLSv1.2",
- "DH-DSS-DES-CBC-SHA+SSLv3",
- "DH-DSS-DES-CBC3-SHA+SSLv3",
- "DH-DSS-SEED-SHA+SSLv3",
- "DH-RSA-AES128-GCM-SHA256+TLSv1.2",
- "DH-RSA-AES256-GCM-SHA384+TLSv1.2",
- "DH-RSA-AES128-SHA+SSLv3",
- "DH-RSA-AES128-SHA256+TLSv1.2",
- "DH-RSA-AES256-SHA+SSLv3",
- "DH-RSA-AES256-SHA256+TLSv1.2",
- "DH-RSA-CAMELLIA128-SHA+SSLv3",
- "DH-RSA-CAMELLIA128-SHA256+TLSv1.2",
- "DH-RSA-CAMELLIA256-SHA+SSLv3",
- "DH-RSA-CAMELLIA256-SHA256+TLSv1.2",
- "DH-RSA-DES-CBC-SHA+SSLv3",
- "DH-RSA-DES-CBC3-SHA+SSLv3",
- "DH-RSA-SEED-SHA+SSLv3",
- "DHE-DSS-CAMELLIA128-SHA+SSLv3",
- "DHE-DSS-CAMELLIA128-SHA256+TLSv1.2",
- "DHE-DSS-CAMELLIA256-SHA+SSLv3",
- "DHE-DSS-CAMELLIA256-SHA256+TLSv1.2",
- "DHE-DSS-SEED-SHA+SSLv3",
- "DHE-PSK-3DES-EDE-CBC-SHA+SSLv3",
- "DHE-PSK-AES128-CBC-SHA+SSLv3",
- "DHE-PSK-AES128-CBC-SHA256+TLSv1",
- "DHE-PSK-AES128-CCM+TLSv1.2",
- "DHE-PSK-AES128-CCM8+TLSv1.2",
- "DHE-PSK-AES128-GCM-SHA256+TLSv1.2",
- "DHE-PSK-AES256-CBC-SHA+SSLv3",
- "DHE-PSK-AES256-CBC-SHA384+TLSv1",
- "DHE-PSK-AES256-CCM+TLSv1.2",
- "DHE-PSK-AES256-CCM8+TLSv1.2",
- "DHE-PSK-AES256-GCM-SHA384+TLSv1.2",
- "DHE-PSK-CAMELLIA128-SHA256+TLSv1",
- "DHE-PSK-CAMELLIA256-SHA384+TLSv1",
- "DHE-PSK-CHACHA20-POLY1305+TLSv1.2",
- "DHE-PSK-NULL-SHA+SSLv3",
- "DHE-PSK-NULL-SHA256+TLSv1",
- "DHE-PSK-NULL-SHA384+TLSv1",
- "DHE-PSK-RC4-SHA+SSLv3",
- "DHE-RSA-AES128-CCM+TLSv1.2",
- "DHE-RSA-AES128-CCM8+TLSv1.2",
- "DHE-RSA-AES256-CCM+TLSv1.2",
- "DHE-RSA-AES256-CCM8+TLSv1.2",
- "DHE-RSA-CAMELLIA128-SHA+SSLv3",
- "DHE-RSA-CAMELLIA128-SHA256+TLSv1.2",
- "DHE-RSA-CAMELLIA256-SHA+SSLv3",
- "DHE-RSA-CAMELLIA256-SHA256+TLSv1.2",
- "DHE-RSA-CHACHA20-POLY1305+TLSv1.2",
- "DHE-RSA-SEED-SHA+SSLv3",
- "ECDH-ECDSA-CAMELLIA128-SHA256+TLSv1.2",
- "ECDH-ECDSA-CAMELLIA256-SHA384+TLSv1.2",
- "ECDH-RSA-CAMELLIA128-SHA256+TLSv1.2",
- "ECDH-RSA-CAMELLIA256-SHA384+TLSv1.2",
- "ECDHE-ECDSA-AES128-CCM+TLSv1.2",
- "ECDHE-ECDSA-AES128-CCM8+TLSv1.2",
- "ECDHE-ECDSA-AES256-CCM+TLSv1.2",
- "ECDHE-ECDSA-AES256-CCM8+TLSv1.2",
- "ECDHE-ECDSA-CAMELLIA128-SHA256+TLSv1.2",
- "ECDHE-ECDSA-CAMELLIA256-SHA384+TLSv1.2",
- "ECDHE-ECDSA-CHACHA20-POLY1305+TLSv1.2",
- "ECDHE-PSK-3DES-EDE-CBC-SHA+SSLv3",
- "ECDHE-PSK-AES128-CBC-SHA+SSLv3",
- "ECDHE-PSK-AES128-CBC-SHA256+TLSv1",
- "ECDHE-PSK-AES256-CBC-SHA+SSLv3",
- "ECDHE-PSK-AES256-CBC-SHA384+TLSv1",
- "ECDHE-PSK-CAMELLIA128-SHA256+TLSv1",
- "ECDHE-PSK-CAMELLIA256-SHA384+TLSv1",
- "ECDHE-PSK-CHACHA20-POLY1305+TLSv1.2",
- "ECDHE-PSK-NULL-SHA+SSLv3",
- "ECDHE-PSK-NULL-SHA256+TLSv1",
- "ECDHE-PSK-NULL-SHA384+TLSv1",
- "ECDHE-PSK-RC4-SHA+SSLv3",
- "ECDHE-RSA-CAMELLIA128-SHA256+TLSv1.2",
- "ECDHE-RSA-CAMELLIA256-SHA384+TLSv1.2",
- "ECDHE-RSA-CHACHA20-POLY1305+TLSv1.2",
- "EXP-DH-DSS-DES-CBC-SHA+SSLv3",
- "EXP-DH-RSA-DES-CBC-SHA+SSLv3",
- "EXP-RC2-CBC-MD5+SSLv2",
- "EXP-RC4-MD5+SSLv2",
- "IDEA-CBC-MD5+SSLv2",
- "IDEA-CBC-SHA+SSLv3",
- "PSK-3DES-EDE-CBC-SHA+SSLv3",
- "PSK-AES128-CBC-SHA+SSLv3",
- "PSK-AES128-CBC-SHA256+TLSv1",
- "PSK-AES128-CCM+TLSv1.2",
- "PSK-AES128-CCM8+TLSv1.2",
- "PSK-AES128-GCM-SHA256+TLSv1.2",
- "PSK-AES256-CBC-SHA+SSLv3",
- "PSK-AES256-CBC-SHA384+TLSv1",
- "PSK-AES256-CCM+TLSv1.2",
- "PSK-AES256-CCM8+TLSv1.2",
- "PSK-AES256-GCM-SHA384+TLSv1.2",
- "PSK-CAMELLIA128-SHA256+TLSv1",
- "PSK-CAMELLIA256-SHA384+TLSv1",
- "PSK-CHACHA20-POLY1305+TLSv1.2",
- "PSK-NULL-SHA+SSLv3",
- "PSK-NULL-SHA256+TLSv1",
- "PSK-NULL-SHA384+TLSv1",
- "PSK-RC4-SHA+SSLv3",
- "RC2-CBC-MD5+SSLv2",
- "RC4-MD5+SSLv2",
- "RSA-PSK-3DES-EDE-CBC-SHA+SSLv3",
- "RSA-PSK-AES128-CBC-SHA+SSLv3",
- "RSA-PSK-AES128-CBC-SHA256+TLSv1",
- "RSA-PSK-AES128-GCM-SHA256+TLSv1.2",
- "RSA-PSK-AES256-CBC-SHA+SSLv3",
- "RSA-PSK-AES256-CBC-SHA384+TLSv1",
- "RSA-PSK-AES256-GCM-SHA384+TLSv1.2",
- "RSA-PSK-CAMELLIA128-SHA256+TLSv1",
- "RSA-PSK-CAMELLIA256-SHA384+TLSv1",
- "RSA-PSK-CHACHA20-POLY1305+TLSv1.2",
- "RSA-PSK-NULL-SHA+SSLv3",
- "RSA-PSK-NULL-SHA256+TLSv1",
- "RSA-PSK-NULL-SHA384+TLSv1",
- "RSA-PSK-RC4-SHA+SSLv3",
- "SEED-SHA+SSLv3",
- "SRP-AES-128-CBC-SHA+SSLv3",
- "SRP-AES-256-CBC-SHA+SSLv3",
- "SRP-3DES-EDE-CBC-SHA+SSLv3",
- "SRP-DSS-3DES-EDE-CBC-SHA+SSLv3",
- "SRP-DSS-AES-128-CBC-SHA+SSLv3",
- "SRP-DSS-AES-256-CBC-SHA+SSLv3",
- "SRP-RSA-3DES-EDE-CBC-SHA+SSLv3",
- "SRP-RSA-AES-128-CBC-SHA+SSLv3",
- "SRP-RSA-AES-256-CBC-SHA+SSLv3")));
-
-
- private static JsseImpl ORACLE_JSSE_CIPHER_IMPL = new JsseImpl("Oracle",
- CIPHER_SUITE_STANDARD_NAMES_ORACLE, OPENSSL_UNMAPPED_ORACLE);
-
-
- private static JsseImpl IBM_JSSE_CIPHER_IMPL = new JsseImpl("IBM",
- CIPHER_SUITE_STANDARD_NAMES_IBM, OPENSSL_UNMAPPED_IBM);
-
-
- private static Set<JsseImpl> JSSE_IMPLS = Collections.unmodifiableSet(
- new HashSet<>(Arrays.asList(ORACLE_JSSE_CIPHER_IMPL, IBM_JSSE_CIPHER_IMPL)));
-
-
- private static class JsseImpl {
- private final String vendor;
- private final Set<String> standardNames;
- private final Set<String> openSslUnmapped;
-
- public JsseImpl(String vendor, Set<String> standardNames,
- Set<String> openSslUnmapped) {
- this.vendor = vendor;
- this.standardNames = standardNames;
- this.openSslUnmapped = openSslUnmapped;
- }
-
- public String getVendor() {
- return vendor;
- }
-
- public Set<String> getStandardNames() {
- return standardNames;
- }
-
- public Set<String> getOpenSslUnmapped() {
- return openSslUnmapped;
- }
- }
-
-
- // Retrieved on 30 July 2014 from
- // http://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-4
- private static Set<String> REGISTERED_NAMES = Collections.unmodifiableSet(
- new HashSet<>(Arrays.asList(
- "TLS_NULL_WITH_NULL_NULL",
- "TLS_RSA_WITH_NULL_MD5",
- "TLS_RSA_WITH_NULL_SHA",
- "TLS_RSA_EXPORT_WITH_RC4_40_MD5",
- "TLS_RSA_WITH_RC4_128_MD5",
- "TLS_RSA_WITH_RC4_128_SHA",
- "TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5",
- "TLS_RSA_WITH_IDEA_CBC_SHA",
- "TLS_RSA_EXPORT_WITH_DES40_CBC_SHA",
- "TLS_RSA_WITH_DES_CBC_SHA",
- "TLS_RSA_WITH_3DES_EDE_CBC_SHA",
- "TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA",
- "TLS_DH_DSS_WITH_DES_CBC_SHA",
- "TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA",
- "TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA",
- "TLS_DH_RSA_WITH_DES_CBC_SHA",
- "TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA",
- "TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA",
- "TLS_DHE_DSS_WITH_DES_CBC_SHA",
- "TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA",
- "TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA",
- "TLS_DHE_RSA_WITH_DES_CBC_SHA",
- "TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA",
- "TLS_DH_anon_EXPORT_WITH_RC4_40_MD5",
- "TLS_DH_anon_WITH_RC4_128_MD5",
- "TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA",
- "TLS_DH_anon_WITH_DES_CBC_SHA",
- "TLS_DH_anon_WITH_3DES_EDE_CBC_SHA",
- "TLS_KRB5_WITH_DES_CBC_SHA",
- "TLS_KRB5_WITH_3DES_EDE_CBC_SHA",
- "TLS_KRB5_WITH_RC4_128_SHA",
- "TLS_KRB5_WITH_IDEA_CBC_SHA",
- "TLS_KRB5_WITH_DES_CBC_MD5",
- "TLS_KRB5_WITH_3DES_EDE_CBC_MD5",
- "TLS_KRB5_WITH_RC4_128_MD5",
- "TLS_KRB5_WITH_IDEA_CBC_MD5",
- "TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA",
- "TLS_KRB5_EXPORT_WITH_RC2_CBC_40_SHA",
- "TLS_KRB5_EXPORT_WITH_RC4_40_SHA",
- "TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5",
- "TLS_KRB5_EXPORT_WITH_RC2_CBC_40_MD5",
- "TLS_KRB5_EXPORT_WITH_RC4_40_MD5",
- "TLS_PSK_WITH_NULL_SHA",
- "TLS_DHE_PSK_WITH_NULL_SHA",
- "TLS_RSA_PSK_WITH_NULL_SHA",
- "TLS_RSA_WITH_AES_128_CBC_SHA",
- "TLS_DH_DSS_WITH_AES_128_CBC_SHA",
- "TLS_DH_RSA_WITH_AES_128_CBC_SHA",
- "TLS_DHE_DSS_WITH_AES_128_CBC_SHA",
- "TLS_DHE_RSA_WITH_AES_128_CBC_SHA",
- "TLS_DH_anon_WITH_AES_128_CBC_SHA",
- "TLS_RSA_WITH_AES_256_CBC_SHA",
- "TLS_DH_DSS_WITH_AES_256_CBC_SHA",
- "TLS_DH_RSA_WITH_AES_256_CBC_SHA",
- "TLS_DHE_DSS_WITH_AES_256_CBC_SHA",
- "TLS_DHE_RSA_WITH_AES_256_CBC_SHA",
- "TLS_DH_anon_WITH_AES_256_CBC_SHA",
- "TLS_RSA_WITH_NULL_SHA256",
- "TLS_RSA_WITH_AES_128_CBC_SHA256",
- "TLS_RSA_WITH_AES_256_CBC_SHA256",
- "TLS_DH_DSS_WITH_AES_128_CBC_SHA256",
- "TLS_DH_RSA_WITH_AES_128_CBC_SHA256",
- "TLS_DHE_DSS_WITH_AES_128_CBC_SHA256",
- "TLS_RSA_WITH_CAMELLIA_128_CBC_SHA",
- "TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA",
- "TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA",
- "TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA",
- "TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA",
- "TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA",
- "TLS_DHE_RSA_WITH_AES_128_CBC_SHA256",
- "TLS_DH_DSS_WITH_AES_256_CBC_SHA256",
- "TLS_DH_RSA_WITH_AES_256_CBC_SHA256",
- "TLS_DHE_DSS_WITH_AES_256_CBC_SHA256",
- "TLS_DHE_RSA_WITH_AES_256_CBC_SHA256",
- "TLS_DH_anon_WITH_AES_128_CBC_SHA256",
- "TLS_DH_anon_WITH_AES_256_CBC_SHA256",
- "TLS_RSA_WITH_CAMELLIA_256_CBC_SHA",
- "TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA",
- "TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA",
- "TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA",
- "TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA",
- "TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA",
- "TLS_PSK_WITH_RC4_128_SHA",
- "TLS_PSK_WITH_3DES_EDE_CBC_SHA",
- "TLS_PSK_WITH_AES_128_CBC_SHA",
- "TLS_PSK_WITH_AES_256_CBC_SHA",
- "TLS_DHE_PSK_WITH_RC4_128_SHA",
- "TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA",
- "TLS_DHE_PSK_WITH_AES_128_CBC_SHA",
- "TLS_DHE_PSK_WITH_AES_256_CBC_SHA",
- "TLS_RSA_PSK_WITH_RC4_128_SHA",
- "TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA",
- "TLS_RSA_PSK_WITH_AES_128_CBC_SHA",
- "TLS_RSA_PSK_WITH_AES_256_CBC_SHA",
- "TLS_RSA_WITH_SEED_CBC_SHA",
- "TLS_DH_DSS_WITH_SEED_CBC_SHA",
- "TLS_DH_RSA_WITH_SEED_CBC_SHA",
- "TLS_DHE_DSS_WITH_SEED_CBC_SHA",
- "TLS_DHE_RSA_WITH_SEED_CBC_SHA",
- "TLS_DH_anon_WITH_SEED_CBC_SHA",
- "TLS_RSA_WITH_AES_128_GCM_SHA256",
- "TLS_RSA_WITH_AES_256_GCM_SHA384",
- "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256",
- "TLS_DHE_RSA_WITH_AES_256_GCM_SHA384",
- "TLS_DH_RSA_WITH_AES_128_GCM_SHA256",
- "TLS_DH_RSA_WITH_AES_256_GCM_SHA384",
- "TLS_DHE_DSS_WITH_AES_128_GCM_SHA256",
- "TLS_DHE_DSS_WITH_AES_256_GCM_SHA384",
- "TLS_DH_DSS_WITH_AES_128_GCM_SHA256",
- "TLS_DH_DSS_WITH_AES_256_GCM_SHA384",
- "TLS_DH_anon_WITH_AES_128_GCM_SHA256",
- "TLS_DH_anon_WITH_AES_256_GCM_SHA384",
- "TLS_PSK_WITH_AES_128_GCM_SHA256",
- "TLS_PSK_WITH_AES_256_GCM_SHA384",
- "TLS_DHE_PSK_WITH_AES_128_GCM_SHA256",
- "TLS_DHE_PSK_WITH_AES_256_GCM_SHA384",
- "TLS_RSA_PSK_WITH_AES_128_GCM_SHA256",
- "TLS_RSA_PSK_WITH_AES_256_GCM_SHA384",
- "TLS_PSK_WITH_AES_128_CBC_SHA256",
- "TLS_PSK_WITH_AES_256_CBC_SHA384",
- "TLS_PSK_WITH_NULL_SHA256",
- "TLS_PSK_WITH_NULL_SHA384",
- "TLS_DHE_PSK_WITH_AES_128_CBC_SHA256",
- "TLS_DHE_PSK_WITH_AES_256_CBC_SHA384",
- "TLS_DHE_PSK_WITH_NULL_SHA256",
- "TLS_DHE_PSK_WITH_NULL_SHA384",
- "TLS_RSA_PSK_WITH_AES_128_CBC_SHA256",
- "TLS_RSA_PSK_WITH_AES_256_CBC_SHA384",
- "TLS_RSA_PSK_WITH_NULL_SHA256",
- "TLS_RSA_PSK_WITH_NULL_SHA384",
- "TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256",
- "TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256",
- "TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256",
- "TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256",
- "TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256",
- "TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA256",
- "TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256",
- "TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256",
- "TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256",
- "TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256",
- "TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256",
- "TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA256",
- "TLS_EMPTY_RENEGOTIATION_INFO_SCSV",
- "TLS_ECDH_ECDSA_WITH_NULL_SHA",
- "TLS_ECDH_ECDSA_WITH_RC4_128_SHA",
- "TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA",
- "TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA",
- "TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA",
- "TLS_ECDHE_ECDSA_WITH_NULL_SHA",
- "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA",
- "TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA",
- "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
- "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
- "TLS_ECDH_RSA_WITH_NULL_SHA",
- "TLS_ECDH_RSA_WITH_RC4_128_SHA",
- "TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA",
- "TLS_ECDH_RSA_WITH_AES_128_CBC_SHA",
- "TLS_ECDH_RSA_WITH_AES_256_CBC_SHA",
- "TLS_ECDHE_RSA_WITH_NULL_SHA",
- "TLS_ECDHE_RSA_WITH_RC4_128_SHA",
- "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA",
- "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
- "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
- "TLS_ECDH_anon_WITH_NULL_SHA",
- "TLS_ECDH_anon_WITH_RC4_128_SHA",
- "TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA",
- "TLS_ECDH_anon_WITH_AES_128_CBC_SHA",
- "TLS_ECDH_anon_WITH_AES_256_CBC_SHA",
- "TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA",
- "TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA",
- "TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA",
- "TLS_SRP_SHA_WITH_AES_128_CBC_SHA",
- "TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA",
- "TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA",
- "TLS_SRP_SHA_WITH_AES_256_CBC_SHA",
- "TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA",
- "TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA",
- "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",
- "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384",
- "TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256",
- "TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384",
- "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",
- "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384",
- "TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256",
- "TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384",
- "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
- "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
- "TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256",
- "TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384",
- "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
- "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
- "TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256",
- "TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384",
- "TLS_ECDHE_PSK_WITH_RC4_128_SHA",
- "TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA",
- "TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA",
- "TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA",
- "TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256",
- "TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384",
- "TLS_ECDHE_PSK_WITH_NULL_SHA",
- "TLS_ECDHE_PSK_WITH_NULL_SHA256",
- "TLS_ECDHE_PSK_WITH_NULL_SHA384",
- "TLS_RSA_WITH_ARIA_128_CBC_SHA256",
- "TLS_RSA_WITH_ARIA_256_CBC_SHA384",
- "TLS_DH_DSS_WITH_ARIA_128_CBC_SHA256",
- "TLS_DH_DSS_WITH_ARIA_256_CBC_SHA384",
- "TLS_DH_RSA_WITH_ARIA_128_CBC_SHA256",
- "TLS_DH_RSA_WITH_ARIA_256_CBC_SHA384",
- "TLS_DHE_DSS_WITH_ARIA_128_CBC_SHA256",
- "TLS_DHE_DSS_WITH_ARIA_256_CBC_SHA384",
- "TLS_DHE_RSA_WITH_ARIA_128_CBC_SHA256",
- "TLS_DHE_RSA_WITH_ARIA_256_CBC_SHA384",
- "TLS_DH_anon_WITH_ARIA_128_CBC_SHA256",
- "TLS_DH_anon_WITH_ARIA_256_CBC_SHA384",
- "TLS_ECDHE_ECDSA_WITH_ARIA_128_CBC_SHA256",
- "TLS_ECDHE_ECDSA_WITH_ARIA_256_CBC_SHA384",
- "TLS_ECDH_ECDSA_WITH_ARIA_128_CBC_SHA256",
- "TLS_ECDH_ECDSA_WITH_ARIA_256_CBC_SHA384",
- "TLS_ECDHE_RSA_WITH_ARIA_128_CBC_SHA256",
- "TLS_ECDHE_RSA_WITH_ARIA_256_CBC_SHA384",
- "TLS_ECDH_RSA_WITH_ARIA_128_CBC_SHA256",
- "TLS_ECDH_RSA_WITH_ARIA_256_CBC_SHA384",
- "TLS_RSA_WITH_ARIA_128_GCM_SHA256",
- "TLS_RSA_WITH_ARIA_256_GCM_SHA384",
- "TLS_DHE_RSA_WITH_ARIA_128_GCM_SHA256",
- "TLS_DHE_RSA_WITH_ARIA_256_GCM_SHA384",
- "TLS_DH_RSA_WITH_ARIA_128_GCM_SHA256",
- "TLS_DH_RSA_WITH_ARIA_256_GCM_SHA384",
- "TLS_DHE_DSS_WITH_ARIA_128_GCM_SHA256",
- "TLS_DHE_DSS_WITH_ARIA_256_GCM_SHA384",
- "TLS_DH_DSS_WITH_ARIA_128_GCM_SHA256",
- "TLS_DH_DSS_WITH_ARIA_256_GCM_SHA384",
- "TLS_DH_anon_WITH_ARIA_128_GCM_SHA256",
- "TLS_DH_anon_WITH_ARIA_256_GCM_SHA384",
- "TLS_ECDHE_ECDSA_WITH_ARIA_128_GCM_SHA256",
- "TLS_ECDHE_ECDSA_WITH_ARIA_256_GCM_SHA384",
- "TLS_ECDH_ECDSA_WITH_ARIA_128_GCM_SHA256",
- "TLS_ECDH_ECDSA_WITH_ARIA_256_GCM_SHA384",
- "TLS_ECDHE_RSA_WITH_ARIA_128_GCM_SHA256",
- "TLS_ECDHE_RSA_WITH_ARIA_256_GCM_SHA384",
- "TLS_ECDH_RSA_WITH_ARIA_128_GCM_SHA256",
- "TLS_ECDH_RSA_WITH_ARIA_256_GCM_SHA384",
- "TLS_PSK_WITH_ARIA_128_CBC_SHA256",
- "TLS_PSK_WITH_ARIA_256_CBC_SHA384",
- "TLS_DHE_PSK_WITH_ARIA_128_CBC_SHA256",
- "TLS_DHE_PSK_WITH_ARIA_256_CBC_SHA384",
- "TLS_RSA_PSK_WITH_ARIA_128_CBC_SHA256",
- "TLS_RSA_PSK_WITH_ARIA_256_CBC_SHA384",
- "TLS_PSK_WITH_ARIA_128_GCM_SHA256",
- "TLS_PSK_WITH_ARIA_256_GCM_SHA384",
- "TLS_DHE_PSK_WITH_ARIA_128_GCM_SHA256",
- "TLS_DHE_PSK_WITH_ARIA_256_GCM_SHA384",
- "TLS_RSA_PSK_WITH_ARIA_128_GCM_SHA256",
- "TLS_RSA_PSK_WITH_ARIA_256_GCM_SHA384",
- "TLS_ECDHE_PSK_WITH_ARIA_128_CBC_SHA256",
- "TLS_ECDHE_PSK_WITH_ARIA_256_CBC_SHA384",
- "TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256",
- "TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384",
- "TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256",
- "TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384",
- "TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256",
- "TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384",
- "TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256",
- "TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384",
- "TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256",
- "TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384",
- "TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256",
- "TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384",
- "TLS_DH_RSA_WITH_CAMELLIA_128_GCM_SHA256",
- "TLS_DH_RSA_WITH_CAMELLIA_256_GCM_SHA384",
- "TLS_DHE_DSS_WITH_CAMELLIA_128_GCM_SHA256",
- "TLS_DHE_DSS_WITH_CAMELLIA_256_GCM_SHA384",
- "TLS_DH_DSS_WITH_CAMELLIA_128_GCM_SHA256",
- "TLS_DH_DSS_WITH_CAMELLIA_256_GCM_SHA384",
- "TLS_DH_anon_WITH_CAMELLIA_128_GCM_SHA256",
- "TLS_DH_anon_WITH_CAMELLIA_256_GCM_SHA384",
- "TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256",
- "TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384",
- "TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256",
- "TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384",
- "TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256",
- "TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384",
- "TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256",
- "TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384",
- "TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256",
- "TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384",
- "TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256",
- "TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384",
- "TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256",
- "TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384",
- "TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256",
- "TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384",
- "TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256",
- "TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384",
- "TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256",
- "TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384",
- "TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256",
- "TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384",
- "TLS_RSA_WITH_AES_128_CCM",
- "TLS_RSA_WITH_AES_256_CCM",
- "TLS_DHE_RSA_WITH_AES_128_CCM",
- "TLS_DHE_RSA_WITH_AES_256_CCM",
- "TLS_RSA_WITH_AES_128_CCM_8",
- "TLS_RSA_WITH_AES_256_CCM_8",
- "TLS_DHE_RSA_WITH_AES_128_CCM_8",
- "TLS_DHE_RSA_WITH_AES_256_CCM_8",
- "TLS_PSK_WITH_AES_128_CCM",
- "TLS_PSK_WITH_AES_256_CCM",
- "TLS_DHE_PSK_WITH_AES_128_CCM",
- "TLS_DHE_PSK_WITH_AES_256_CCM",
- "TLS_PSK_WITH_AES_128_CCM_8",
- "TLS_PSK_WITH_AES_256_CCM_8",
- "TLS_PSK_DHE_WITH_AES_128_CCM_8",
- "TLS_PSK_DHE_WITH_AES_256_CCM_8",
- "TLS_ECDHE_ECDSA_WITH_AES_128_CCM",
- "TLS_ECDHE_ECDSA_WITH_AES_256_CCM",
- "TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8",
- "TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8",
- // From https://tools.ietf.org/html/draft-ietf-tls-chacha20-poly1305-04
- // These might change.
- "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
- "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256",
- "TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
- "TLS_PSK_WITH_CHACHA20_POLY1305_SHA256",
- "TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256",
- "TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256",
- "TLS_RSA_PSK_WITH_CHACHA20_POLY1305_SHA256")));
-}
diff --git a/test/org/apache/tomcat/util/net/jsse/openssl/TestOpenSSLCipherConfigurationParser.java b/test/org/apache/tomcat/util/net/jsse/openssl/TestOpenSSLCipherConfigurationParser.java
deleted file mode 100644
index a930978..0000000
--- a/test/org/apache/tomcat/util/net/jsse/openssl/TestOpenSSLCipherConfigurationParser.java
+++ /dev/null
@@ -1,594 +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.net.jsse.openssl;
-
-import java.util.List;
-
-import org.junit.Assert;
-import org.junit.Ignore;
-import org.junit.Test;
-
-public class TestOpenSSLCipherConfigurationParser {
-
- @Test
- public void testDEFAULT() throws Exception {
- if (TesterOpenSSL.VERSION < 10100) {
- // Account for classes of ciphers removed from DEFAULT in 1.1.0
- testSpecification("DEFAULT:!RC4:!DSS:!SEED:!IDEA:!CAMELLIA:!AESCCM:!3DES");
- } else {
- testSpecification("DEFAULT");
- }
- }
-
-
- @Test
- public void testCOMPLEMENTOFDEFAULT() throws Exception {
- if (TesterOpenSSL.VERSION < 10100) {
- // Account for classes of ciphers removed from DEFAULT in 1.1.0
- testSpecification("COMPLEMENTOFDEFAULT:RC4:DSS:SEED:IDEA:CAMELLIA:AESCCM:aNULL:3DES");
- } else {
- testSpecification("COMPLEMENTOFDEFAULT");
- }
- }
-
-
- @Test
- public void testALL() throws Exception {
- testSpecification("ALL");
- }
-
-
- @Test
- public void testCOMPLEMENTOFALL() throws Exception {
- testSpecification("COMPLEMENTOFALL");
- }
-
-
- @Test
- public void testaNULL() throws Exception {
- testSpecification("aNULL");
- }
-
-
- @Test
- public void testeNULL() throws Exception {
- testSpecification("eNULL");
- }
-
-
- @Test
- public void testHIGH() throws Exception {
- testSpecification("HIGH");
- }
-
-
- @Test
- public void testMEDIUM() throws Exception {
- testSpecification("MEDIUM");
- }
-
-
- @Test
- public void testLOW() throws Exception {
- testSpecification("LOW");
- }
-
-
- @Test
- public void testEXPORT40() throws Exception {
- testSpecification("EXPORT40");
- }
-
-
- @Test
- public void testEXPORT() throws Exception {
- testSpecification("EXPORT");
- }
-
-
- @Test
- public void testRSA() throws Exception {
- testSpecification("RSA");
- }
-
-
- @Test
- public void testaRSA() throws Exception {
- testSpecification("aRSA");
- }
-
-
- @Test
- public void testkRSA() throws Exception {
- testSpecification("kRSA");
- }
-
-
- @Test
- public void testkEDH() throws Exception {
- testSpecification("kEDH");
- }
-
-
- @Test
- public void testkDHE() throws Exception {
- // This alias was introduced in 1.0.2
- if (TesterOpenSSL.VERSION >= 10002) {
- testSpecification("kDHE");
- }
- }
-
-
- @Test
- public void testEDH() throws Exception {
- testSpecification("EDH");
- }
-
-
- @Test
- public void testDHE() throws Exception {
- // This alias was introduced in 1.0.2
- if (TesterOpenSSL.VERSION >= 10002) {
- testSpecification("DHE");
- }
- }
-
-
- @Test
- public void testkDHr() throws Exception {
- testSpecification("kDHr");
- }
-
-
- @Test
- public void testkDHd() throws Exception {
- testSpecification("kDHd");
- }
-
-
- @Test
- public void testkDH() throws Exception {
- testSpecification("kDH");
- }
-
-
- @Test
- public void testkECDHr() throws Exception {
- testSpecification("kECDHr");
- }
-
-
- @Test
- public void testkECDHe() throws Exception {
- testSpecification("kECDHe");
- }
-
-
- @Test
- public void testkECDH() throws Exception {
- testSpecification("kECDH");
- }
-
-
- @Test
- public void testkEECDH() throws Exception {
- testSpecification("kEECDH");
- }
-
-
- @Test
- public void testECDH() throws Exception {
- testSpecification("ECDH");
- }
-
-
- @Test
- public void testkECDHE() throws Exception {
- testSpecification("kECDHE");
- }
-
-
- @Test
- public void testECDHE() throws Exception {
- testSpecification("ECDHE");
- }
-
-
- @Test
- @Ignore("Contrary to the docs, OpenSSL does not recognise EECDHE")
- public void testEECDHE() throws Exception {
- testSpecification("EECDHE");
- }
-
-
- @Test
- public void testAECDH() throws Exception {
- testSpecification("AECDH");
- }
-
-
- @Test
- public void testDSS() throws Exception {
- testSpecification("DSS");
- }
-
-
- @Test
- public void testaDSS() throws Exception {
- testSpecification("aDSS");
- }
-
-
- @Test
- public void testaDH() throws Exception {
- testSpecification("aDH");
- }
-
-
- @Test
- public void testaECDH() throws Exception {
- testSpecification("aECDH");
- }
-
-
- @Test
- public void testaECDSA() throws Exception {
- testSpecification("aECDSA");
- }
-
-
- @Test
- public void testECDSA() throws Exception {
- testSpecification("ECDSA");
- }
-
-
- @Test
- public void testkFZA() throws Exception {
- testSpecification("kFZA");
- }
-
-
- @Test
- public void testaFZA() throws Exception {
- testSpecification("aFZA");
- }
-
-
- @Test
- public void testeFZA() throws Exception {
- testSpecification("eFZA");
- }
-
-
- @Test
- public void testFZA() throws Exception {
- testSpecification("FZA");
- }
-
-
- @Test
- public void testTLSv1_2() throws Exception {
- testSpecification("TLSv1.2");
- }
-
-
- @Test
- public void testTLSv1() throws Exception {
- // In OpenSSL 1.1.0-dev, TLSv1 refers to those ciphers that require
- // TLSv1 rather than being an alias for SSLv3
- if (TesterOpenSSL.VERSION >= 10100) {
- testSpecification("TLSv1");
- }
- }
-
-
- @Test
- public void testSSLv2() throws Exception {
- testSpecification("SSLv2");
- }
-
-
- @Test
- public void testSSLv3() throws Exception {
- testSpecification("SSLv3");
- }
-
-
- @Test
- public void testDH() throws Exception {
- testSpecification("DH");
- }
-
-
- @Test
- public void testADH() throws Exception {
- testSpecification("ADH");
- }
-
-
- @Test
- public void testAES128() throws Exception {
- testSpecification("AES128");
- }
-
-
- @Test
- public void testAES256() throws Exception {
- testSpecification("AES256");
- }
-
-
- @Test
- public void testAES() throws Exception {
- testSpecification("AES");
- }
-
-
- @Test
- public void testAESGCM() throws Exception {
- testSpecification("AESGCM");
- }
-
-
- @Test
- public void testAESCCM() throws Exception {
- testSpecification("AESCCM");
- }
-
-
- @Test
- public void testAESCCM8() throws Exception {
- testSpecification("AESCCM8");
- }
-
-
- @Test
- public void testCAMELLIA128() throws Exception {
- testSpecification("CAMELLIA128");
- }
-
-
- @Test
- public void testCAMELLIA256() throws Exception {
- testSpecification("CAMELLIA256");
- }
-
-
- @Test
- public void testCAMELLIA() throws Exception {
- testSpecification("CAMELLIA");
- }
-
-
- @Test
- public void testCHACHA20() throws Exception {
- testSpecification("CHACHA20");
- }
-
-
- @Test
- public void test3DES() throws Exception {
- testSpecification("3DES");
- }
-
-
- @Test
- public void testDES() throws Exception {
- testSpecification("DES");
- }
-
-
- @Test
- public void testRC4() throws Exception {
- testSpecification("RC4");
- }
-
-
- @Test
- public void testRC2() throws Exception {
- testSpecification("RC2");
- }
-
-
- @Test
- public void testIDEA() throws Exception {
- testSpecification("IDEA");
- }
-
-
- @Test
- public void testSEED() throws Exception {
- testSpecification("SEED");
- }
-
-
- @Test
- public void testMD5() throws Exception {
- testSpecification("MD5");
- }
-
-
- @Test
- public void testSHA1() throws Exception {
- testSpecification("SHA1");
- }
-
-
- @Test
- public void testSHA() throws Exception {
- testSpecification("SHA");
- }
-
-
- @Test
- public void testSHA256() throws Exception {
- testSpecification("SHA256");
- }
-
-
- @Test
- public void testSHA384() throws Exception {
- testSpecification("SHA384");
- }
-
-
- @Test
- public void testKRB5() throws Exception {
- testSpecification("KRB5");
- }
-
-
- @Test
- public void testaGOST() throws Exception {
- testSpecification("aGOST");
- }
-
-
- @Test
- public void testaGOST01() throws Exception {
- testSpecification("aGOST01");
- }
-
-
- @Test
- public void testaGOST94() throws Exception {
- testSpecification("aGOST94");
- }
-
-
- @Test
- public void testkGOST() throws Exception {
- testSpecification("kGOST");
- }
-
-
- @Test
- public void testGOST94() throws Exception {
- testSpecification("GOST94");
- }
-
-
- @Test
- public void testGOST89MAC() throws Exception {
- testSpecification("GOST89MAC");
- }
-
-
- @Test
- public void testaPSK() throws Exception {
- testSpecification("aPSK");
- }
-
-
- @Test
- public void testkPSK() throws Exception {
- testSpecification("kPSK");
- }
-
-
- @Test
- public void testkRSAPSK() throws Exception {
- testSpecification("kRSAPSK");
- }
-
-
- @Test
- public void testkECDHEPSK() throws Exception {
- testSpecification("kECDHEPSK");
- }
-
-
- @Test
- public void testkDHEPSK() throws Exception {
- testSpecification("kDHEPSK");
- }
-
-
- @Test
- public void testPSK() throws Exception {
- testSpecification("PSK");
- }
-
-
- // TODO: Add tests for the individual operators
-
- @Test
- public void testSpecification01() throws Exception {
- // Tomcat 8 default as of 2014-08-04
- // This gets an A- from https://www.ssllabs.com/ssltest with no FS for
- // a number of the reference browsers
- testSpecification("HIGH:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5");
- }
-
-
- @Test
- public void testSpecification02() throws Exception {
- // Suggestion from dev list (s/ECDHE/kEECDH/, s/DHE/EDH/
- testSpecification("!aNULL:!eNULL:!EXPORT:!DSS:!DES:!SSLv2:kEECDH:ECDH:EDH:AES256-GCM-SHA384:AES128-GCM-SHA256:+RC4:HIGH:aRSA:kECDHr:MEDIUM");
- }
-
-
- @Test
- public void testSpecification03() throws Exception {
- // Reported as failing during 8.0.11 release vote by Ognjen Blagojevic
- // EDH was introduced in 1.0.0
- testSpecification("EECDH+aRSA+SHA384:EECDH:EDH+aRSA:RC4:!aNULL:!eNULL:!LOW:!3DES:!MD5:!EXP:!PSK:!SRP:!DSS");
- }
-
- private void testSpecification(String specification) throws Exception {
- // Filter out cipher suites that OpenSSL does not implement
- String openSSLCipherList = TesterOpenSSL.getOpenSSLCiphersAsExpression(specification);
- List<String> jsseCipherListFromOpenSSL =
- OpenSSLCipherConfigurationParser.parseExpression(openSSLCipherList);
- List<String> jsseCipherListFromParser =
- OpenSSLCipherConfigurationParser.parseExpression(specification);
-
- TesterOpenSSL.removeUnimplementedCiphersJsse(jsseCipherListFromParser);
-
- // First check the lists have the same entries
- // Order is NOT important at this point. It is checked below.
- Assert.assertEquals(jsseCipherListFromOpenSSL.size(), jsseCipherListFromParser.size());
- Assert.assertTrue(jsseCipherListFromOpenSSL.containsAll(jsseCipherListFromParser));
-
- // OpenSSL treats many ciphers as having equal preference. The order
- // returned depends on the order they are requested. The following code
- // checks that the Parser produces a cipher list that is consistent with
- // OpenSSL's preference order by confirming that running through OpenSSL
- // does not change the order.
- String parserOrderedExpression = listToString(jsseCipherListFromParser, ',');
- Assert.assertEquals(
- listToString(OpenSSLCipherConfigurationParser.parseExpression(
- parserOrderedExpression), ','),
- parserOrderedExpression);
- }
-
-
- private String listToString(List<String> list, char separator) {
- StringBuilder sb = new StringBuilder();
- boolean first = true;
- for (String entry : list) {
- if (first) {
- first = false;
- } else {
- sb.append(separator);
- }
- sb.append(entry);
- }
- return sb.toString();
- }
-}
diff --git a/test/org/apache/tomcat/util/net/jsse/openssl/TestOpenSSLCipherConfigurationParserOnly.java b/test/org/apache/tomcat/util/net/jsse/openssl/TestOpenSSLCipherConfigurationParserOnly.java
deleted file mode 100644
index 50996f6..0000000
--- a/test/org/apache/tomcat/util/net/jsse/openssl/TestOpenSSLCipherConfigurationParserOnly.java
+++ /dev/null
@@ -1,113 +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.net.jsse.openssl;
-
-import java.util.LinkedHashSet;
-
-import org.junit.Assert;
-import org.junit.Test;
-
-
-/*
- * The unit test is independent of OpenSSL version and does not require OpenSSL
- * to be present.
- */
-public class TestOpenSSLCipherConfigurationParserOnly {
-
- @Test
- public void testDefaultSort01() throws Exception {
- // Reproducing a failure observed on Gump with OpenSSL 1.1.x
-
- // Everything else being equal, AES is preferred
- LinkedHashSet<Cipher> input = new LinkedHashSet<>();
- input.add(Cipher.TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384);
- input.add(Cipher.TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384);
- input.add(Cipher.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384);
- input.add(Cipher.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384);
- LinkedHashSet<Cipher> result = OpenSSLCipherConfigurationParser.defaultSort(input);
-
- LinkedHashSet<Cipher> expected = new LinkedHashSet<>();
- expected.add(Cipher.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384);
- expected.add(Cipher.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384);
- expected.add(Cipher.TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384);
- expected.add(Cipher.TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384);
-
- Assert.assertEquals(expected.toString(), result.toString());
- }
-
- @Test
- public void testDefaultSort02() throws Exception {
- // Reproducing a failure observed on Gump with OpenSSL 1.1.x
-
- // ECHDE should beat AES
- LinkedHashSet<Cipher> input = new LinkedHashSet<>();
- input.add(Cipher.TLS_RSA_WITH_AES_256_CBC_SHA);
- input.add(Cipher.TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384);
- LinkedHashSet<Cipher> result = OpenSSLCipherConfigurationParser.defaultSort(input);
-
- LinkedHashSet<Cipher> expected = new LinkedHashSet<>();
- expected.add(Cipher.TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384);
- expected.add(Cipher.TLS_RSA_WITH_AES_256_CBC_SHA);
-
- Assert.assertEquals(expected.toString(), result.toString());
- }
-
- @Test
- public void testDefaultSort03() throws Exception {
- // Reproducing a failure observed on Gump with OpenSSL 1.1.x
-
- // AES should beat CAMELLIA
- LinkedHashSet<Cipher> input = new LinkedHashSet<>();
- input.add(Cipher.TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384);
- input.add(Cipher.TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384);
- LinkedHashSet<Cipher> result = OpenSSLCipherConfigurationParser.defaultSort(input);
-
- LinkedHashSet<Cipher> expected = new LinkedHashSet<>();
- expected.add(Cipher.TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384);
- expected.add(Cipher.TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384);
-
- Assert.assertEquals(expected.toString(), result.toString());
- }
-
- @Test
- public void testRename01() throws Exception {
- // EDH -> DHE
- LinkedHashSet<Cipher> result =
- OpenSSLCipherConfigurationParser.parse("EXP-EDH-DSS-DES-CBC-SHA");
- LinkedHashSet<Cipher> expected = new LinkedHashSet<>();
- expected.add(Cipher.TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA);
-
- Assert.assertEquals(expected, result);
- }
-
- @Test
- public void testCustomOrdering() throws Exception {
- // https://bz.apache.org/bugzilla/show_bug.cgi?id=59081
- LinkedHashSet<Cipher> result = OpenSSLCipherConfigurationParser.parse(
- "ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-RSA-DES-CBC3-SHA:" +
- "DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:DES-CBC3-SHA");
- LinkedHashSet<Cipher> expected = new LinkedHashSet<>();
- expected.add(Cipher.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384);
- expected.add(Cipher.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA);
- expected.add(Cipher.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA);
- expected.add(Cipher.TLS_DHE_RSA_WITH_AES_256_CBC_SHA);
- expected.add(Cipher.TLS_DHE_RSA_WITH_AES_128_CBC_SHA);
- expected.add(Cipher.TLS_RSA_WITH_3DES_EDE_CBC_SHA);
-
- Assert.assertEquals(expected.toString(), result.toString());
- }
-}
diff --git a/test/org/apache/tomcat/util/net/jsse/openssl/TesterOpenSSL.java b/test/org/apache/tomcat/util/net/jsse/openssl/TesterOpenSSL.java
deleted file mode 100644
index d4759a1..0000000
--- a/test/org/apache/tomcat/util/net/jsse/openssl/TesterOpenSSL.java
+++ /dev/null
@@ -1,439 +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.net.jsse.openssl;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-import org.apache.catalina.util.IOTools;
-import org.apache.tomcat.util.http.fileupload.ByteArrayOutputStream;
-
-public class TesterOpenSSL {
-
- public static final int VERSION;
-
- public static final Set<Cipher> OPENSSL_UNIMPLEMENTED_CIPHERS;
-
- static {
- // Note: The following lists are intended to be aligned with the most
- // recent release of each OpenSSL release branch. Running the unit
- // tests with earlier releases is likely to result in failures.
-
- String versionString = null;
- try {
- versionString = executeOpenSSLCommand("version");
- } catch (IOException e) {
- versionString = "";
- }
- if (versionString.startsWith("OpenSSL 1.1.1")) {
- // Note: Gump currently tests 9.0.x with OpenSSL master
- // (a.k.a 1.1.1-dev)
- VERSION = 10101;
- } else if (versionString.startsWith("OpenSSL 1.1.0")) {
- // Support ends 2018-04-30
- VERSION = 10100;
- } else if (versionString.startsWith("OpenSSL 1.0.2")) {
- // Support ends 2019-12-31 (LTS)
- // Note: Gump current tests 8.0.x with OpenSSL 1.0.2
- VERSION = 10002;
- } else if (versionString.startsWith("OpenSSL 1.0.1")) {
- // Support ends 2016-12-31
- VERSION = 10001;
- // Note: Release branches 1.0.0 and earlier are no longer supported by
- // the OpenSSL team so these tests don't support them either.
- } else {
- VERSION = -1;
- }
-
- HashSet<Cipher> unimplemented = new HashSet<>();
-
- // These have been removed from all supported versions.
- unimplemented.add(Cipher.TLS_DHE_DSS_WITH_RC4_128_SHA);
- unimplemented.add(Cipher.TLS_DHE_DSS_EXPORT1024_WITH_DES_CBC_SHA);
- unimplemented.add(Cipher.TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA);
- unimplemented.add(Cipher.TLS_RSA_EXPORT1024_WITH_RC2_CBC_56_MD5);
- unimplemented.add(Cipher.TLS_DHE_DSS_EXPORT1024_WITH_RC4_56_SHA);
- unimplemented.add(Cipher.TLS_RSA_EXPORT1024_WITH_RC4_56_SHA);
- unimplemented.add(Cipher.TLS_RSA_EXPORT1024_WITH_RC4_56_MD5);
- unimplemented.add(Cipher.TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA);
- unimplemented.add(Cipher.TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA);
- unimplemented.add(Cipher.TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256);
- unimplemented.add(Cipher.TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256);
- unimplemented.add(Cipher.TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256);
- unimplemented.add(Cipher.TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256);
- unimplemented.add(Cipher.TLS_DH_anon_WITH_DES_CBC_SHA);
- unimplemented.add(Cipher.TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA);
- unimplemented.add(Cipher.TLS_DH_anon_EXPORT_WITH_RC4_40_MD5);
- unimplemented.add(Cipher.TLS_DHE_RSA_WITH_DES_CBC_SHA);
- unimplemented.add(Cipher.TLS_DHE_DSS_WITH_DES_CBC_SHA);
- unimplemented.add(Cipher.TLS_DH_RSA_WITH_DES_CBC_SHA);
- unimplemented.add(Cipher.TLS_DH_DSS_WITH_DES_CBC_SHA);
- unimplemented.add(Cipher.TLS_RSA_WITH_DES_CBC_SHA);
- unimplemented.add(Cipher.SSL2_DES_64_CBC_WITH_MD5);
- unimplemented.add(Cipher.TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA);
- unimplemented.add(Cipher.TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA);
- unimplemented.add(Cipher.TLS_RSA_EXPORT_WITH_DES40_CBC_SHA);
- unimplemented.add(Cipher.TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5);
- unimplemented.add(Cipher.TLS_RSA_EXPORT_WITH_RC4_40_MD5);
- unimplemented.add(Cipher.SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5);
- unimplemented.add(Cipher.SSL_CK_RC2_128_CBC_WITH_MD5);
- unimplemented.add(Cipher.SSL_CK_RC4_128_WITH_MD5);
- 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);
-
- if (VERSION < 10002) {
- // These were implemented in 1.0.2 so won't be available in any
- // earlier version
- unimplemented.add(Cipher.TLS_DH_DSS_WITH_AES_128_CBC_SHA);
- unimplemented.add(Cipher.TLS_DH_DSS_WITH_AES_256_CBC_SHA);
- unimplemented.add(Cipher.TLS_DH_DSS_WITH_AES_128_CBC_SHA256);
- unimplemented.add(Cipher.TLS_DH_DSS_WITH_AES_256_CBC_SHA256);
- unimplemented.add(Cipher.TLS_DH_DSS_WITH_AES_128_GCM_SHA256);
- unimplemented.add(Cipher.TLS_DH_DSS_WITH_AES_256_GCM_SHA384);
- unimplemented.add(Cipher.TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA);
- unimplemented.add(Cipher.TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA);
- unimplemented.add(Cipher.TLS_DH_DSS_WITH_DES_CBC_SHA);
- unimplemented.add(Cipher.TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA);
- unimplemented.add(Cipher.TLS_DH_DSS_WITH_SEED_CBC_SHA);
- unimplemented.add(Cipher.TLS_DH_RSA_WITH_AES_128_CBC_SHA);
- unimplemented.add(Cipher.TLS_DH_RSA_WITH_AES_256_CBC_SHA);
- unimplemented.add(Cipher.TLS_DH_RSA_WITH_AES_128_CBC_SHA256);
- unimplemented.add(Cipher.TLS_DH_RSA_WITH_AES_256_CBC_SHA256);
- unimplemented.add(Cipher.TLS_DH_RSA_WITH_AES_128_GCM_SHA256);
- unimplemented.add(Cipher.TLS_DH_RSA_WITH_AES_256_GCM_SHA384);
- unimplemented.add(Cipher.TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA);
- unimplemented.add(Cipher.TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA);
- unimplemented.add(Cipher.TLS_DH_RSA_WITH_DES_CBC_SHA);
- unimplemented.add(Cipher.TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA);
- unimplemented.add(Cipher.TLS_DH_RSA_WITH_SEED_CBC_SHA);
- } else {
- // These were removed in 1.0.2 so won't be available from that
- // version onwards.
- // None at present.
- }
-
- if (VERSION < 10100) {
- // These were implemented in 1.1.0 so won't be available in any
- // earlier version
- unimplemented.add(Cipher.TLS_PSK_WITH_NULL_SHA);
- unimplemented.add(Cipher.TLS_DHE_PSK_WITH_NULL_SHA);
- unimplemented.add(Cipher.TLS_RSA_PSK_WITH_NULL_SHA);
- unimplemented.add(Cipher.TLS_DHE_PSK_WITH_AES_128_GCM_SHA256);
- unimplemented.add(Cipher.TLS_DHE_PSK_WITH_AES_256_GCM_SHA384);
- unimplemented.add(Cipher.TLS_RSA_PSK_WITH_AES_128_GCM_SHA256);
- unimplemented.add(Cipher.TLS_RSA_PSK_WITH_AES_256_GCM_SHA384);
- unimplemented.add(Cipher.TLS_PSK_WITH_AES_128_CBC_SHA256);
- unimplemented.add(Cipher.TLS_PSK_WITH_AES_256_CBC_SHA384);
- unimplemented.add(Cipher.TLS_PSK_WITH_NULL_SHA256);
- unimplemented.add(Cipher.TLS_PSK_WITH_NULL_SHA384);
- unimplemented.add(Cipher.TLS_DHE_PSK_WITH_AES_128_CBC_SHA256);
- unimplemented.add(Cipher.TLS_DHE_PSK_WITH_AES_256_CBC_SHA384);
- unimplemented.add(Cipher.TLS_DHE_PSK_WITH_NULL_SHA256);
- unimplemented.add(Cipher.TLS_DHE_PSK_WITH_NULL_SHA384);
- unimplemented.add(Cipher.TLS_RSA_PSK_WITH_AES_128_CBC_SHA256);
- unimplemented.add(Cipher.TLS_RSA_PSK_WITH_AES_256_CBC_SHA384);
- unimplemented.add(Cipher.TLS_RSA_PSK_WITH_NULL_SHA256);
- unimplemented.add(Cipher.TLS_RSA_PSK_WITH_NULL_SHA384);
- unimplemented.add(Cipher.TLS_DHE_PSK_WITH_RC4_128_SHA);
- unimplemented.add(Cipher.TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA);
- unimplemented.add(Cipher.TLS_DHE_PSK_WITH_AES_128_CBC_SHA);
- unimplemented.add(Cipher.TLS_DHE_PSK_WITH_AES_256_CBC_SHA);
- unimplemented.add(Cipher.TLS_RSA_PSK_WITH_RC4_128_SHA);
- unimplemented.add(Cipher.TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA);
- unimplemented.add(Cipher.TLS_RSA_PSK_WITH_AES_128_CBC_SHA);
- unimplemented.add(Cipher.TLS_RSA_PSK_WITH_AES_256_CBC_SHA);
- unimplemented.add(Cipher.TLS_ECDHE_PSK_WITH_RC4_128_SHA);
- unimplemented.add(Cipher.TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA);
- unimplemented.add(Cipher.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA);
- unimplemented.add(Cipher.TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA);
- unimplemented.add(Cipher.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256);
- unimplemented.add(Cipher.TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384);
- unimplemented.add(Cipher.TLS_ECDHE_PSK_WITH_NULL_SHA);
- unimplemented.add(Cipher.TLS_ECDHE_PSK_WITH_NULL_SHA256);
- unimplemented.add(Cipher.TLS_ECDHE_PSK_WITH_NULL_SHA384);
- unimplemented.add(Cipher.TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256);
- unimplemented.add(Cipher.TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384);
- unimplemented.add(Cipher.TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256);
- unimplemented.add(Cipher.TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384);
- unimplemented.add(Cipher.TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256);
- unimplemented.add(Cipher.TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384);
- unimplemented.add(Cipher.TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256);
- unimplemented.add(Cipher.TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384);
- unimplemented.add(Cipher.TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256);
- unimplemented.add(Cipher.TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256);
- unimplemented.add(Cipher.TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256);
- unimplemented.add(Cipher.TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256);
- unimplemented.add(Cipher.TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384);
- unimplemented.add(Cipher.TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384);
- unimplemented.add(Cipher.TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256);
- unimplemented.add(Cipher.TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256);
- unimplemented.add(Cipher.TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384);
- unimplemented.add(Cipher.TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256);
- unimplemented.add(Cipher.TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384);
- unimplemented.add(Cipher.TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256);
- unimplemented.add(Cipher.TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256);
- unimplemented.add(Cipher.TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256);
- unimplemented.add(Cipher.TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256);
- unimplemented.add(Cipher.TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA256);
- unimplemented.add(Cipher.TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256);
- unimplemented.add(Cipher.TLS_PSK_WITH_AES_128_GCM_SHA256);
- unimplemented.add(Cipher.TLS_PSK_WITH_AES_256_GCM_SHA384);
- unimplemented.add(Cipher.TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA256);
- unimplemented.add(Cipher.TLS_RSA_WITH_AES_128_CCM);
- unimplemented.add(Cipher.TLS_RSA_WITH_AES_256_CCM);
- unimplemented.add(Cipher.TLS_DHE_RSA_WITH_AES_128_CCM);
- unimplemented.add(Cipher.TLS_DHE_RSA_WITH_AES_256_CCM);
- unimplemented.add(Cipher.TLS_RSA_WITH_AES_128_CCM_8);
- unimplemented.add(Cipher.TLS_RSA_WITH_AES_256_CCM_8);
- unimplemented.add(Cipher.TLS_DHE_RSA_WITH_AES_128_CCM_8);
- unimplemented.add(Cipher.TLS_DHE_RSA_WITH_AES_256_CCM_8);
- unimplemented.add(Cipher.TLS_PSK_WITH_AES_128_CCM);
- unimplemented.add(Cipher.TLS_PSK_WITH_AES_256_CCM);
- unimplemented.add(Cipher.TLS_DHE_PSK_WITH_AES_128_CCM);
- unimplemented.add(Cipher.TLS_DHE_PSK_WITH_AES_256_CCM);
- unimplemented.add(Cipher.TLS_PSK_WITH_AES_128_CCM_8);
- unimplemented.add(Cipher.TLS_PSK_WITH_AES_256_CCM_8);
- unimplemented.add(Cipher.TLS_PSK_DHE_WITH_AES_128_CCM_8);
- unimplemented.add(Cipher.TLS_PSK_DHE_WITH_AES_256_CCM_8);
- unimplemented.add(Cipher.TLS_ECDHE_ECDSA_WITH_AES_128_CCM);
- unimplemented.add(Cipher.TLS_ECDHE_ECDSA_WITH_AES_256_CCM);
- unimplemented.add(Cipher.TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8);
- unimplemented.add(Cipher.TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8);
- unimplemented.add(Cipher.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256);
- unimplemented.add(Cipher.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256);
- unimplemented.add(Cipher.TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256);
- unimplemented.add(Cipher.TLS_PSK_WITH_CHACHA20_POLY1305_SHA256);
- unimplemented.add(Cipher.TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256);
- unimplemented.add(Cipher.TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256);
- unimplemented.add(Cipher.TLS_RSA_PSK_WITH_CHACHA20_POLY1305_SHA256);
- } else {
- // These were removed in 1.1.0 so won't be available from that
- // version onwards.
- unimplemented.add(Cipher.TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA);
- unimplemented.add(Cipher.TLS_DH_DSS_WITH_AES_128_CBC_SHA);
- unimplemented.add(Cipher.TLS_DH_DSS_WITH_AES_128_CBC_SHA256);
- unimplemented.add(Cipher.TLS_DH_DSS_WITH_AES_128_GCM_SHA256);
- unimplemented.add(Cipher.TLS_DH_DSS_WITH_AES_256_CBC_SHA);
- unimplemented.add(Cipher.TLS_DH_DSS_WITH_AES_256_CBC_SHA256);
- unimplemented.add(Cipher.TLS_DH_DSS_WITH_AES_256_GCM_SHA384);
- unimplemented.add(Cipher.TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA);
- unimplemented.add(Cipher.TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA);
- unimplemented.add(Cipher.TLS_DH_DSS_WITH_SEED_CBC_SHA);
- unimplemented.add(Cipher.TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA);
- unimplemented.add(Cipher.TLS_DH_RSA_WITH_AES_128_CBC_SHA);
- unimplemented.add(Cipher.TLS_DH_RSA_WITH_AES_128_CBC_SHA256);
- unimplemented.add(Cipher.TLS_DH_RSA_WITH_AES_128_GCM_SHA256);
- unimplemented.add(Cipher.TLS_DH_RSA_WITH_AES_256_CBC_SHA);
- unimplemented.add(Cipher.TLS_DH_RSA_WITH_AES_256_CBC_SHA256);
- unimplemented.add(Cipher.TLS_DH_RSA_WITH_AES_256_GCM_SHA384);
- unimplemented.add(Cipher.TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA);
- unimplemented.add(Cipher.TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA);
- unimplemented.add(Cipher.TLS_DH_RSA_WITH_SEED_CBC_SHA);
- unimplemented.add(Cipher.TLS_ECDH_ECDSA_WITH_NULL_SHA);
- unimplemented.add(Cipher.TLS_ECDH_ECDSA_WITH_RC4_128_SHA);
- unimplemented.add(Cipher.TLS_ECDH_ECDSA_WITH_RC4_128_SHA);
- unimplemented.add(Cipher.TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA);
- unimplemented.add(Cipher.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA);
- unimplemented.add(Cipher.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA);
- unimplemented.add(Cipher.TLS_ECDH_RSA_WITH_NULL_SHA);
- unimplemented.add(Cipher.TLS_ECDH_RSA_WITH_RC4_128_SHA);
- unimplemented.add(Cipher.TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA);
- unimplemented.add(Cipher.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA);
- unimplemented.add(Cipher.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA);
- unimplemented.add(Cipher.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256);
- unimplemented.add(Cipher.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384);
- unimplemented.add(Cipher.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256);
- unimplemented.add(Cipher.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384);
- unimplemented.add(Cipher.TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256);
- unimplemented.add(Cipher.TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384);
- unimplemented.add(Cipher.TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256);
- unimplemented.add(Cipher.TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384);
- unimplemented.add(Cipher.TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256);
- unimplemented.add(Cipher.TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384);
- unimplemented.add(Cipher.TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256);
- unimplemented.add(Cipher.TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384);
- unimplemented.add(Cipher.TLS_RSA_WITH_RC4_128_MD5);
- unimplemented.add(Cipher.TLS_DH_anon_WITH_RC4_128_MD5);
- unimplemented.add(Cipher.TLS_ECDHE_PSK_WITH_RC4_128_SHA);
- unimplemented.add(Cipher.TLS_RSA_PSK_WITH_RC4_128_SHA);
- unimplemented.add(Cipher.TLS_ECDHE_RSA_WITH_RC4_128_SHA);
- unimplemented.add(Cipher.TLS_RSA_WITH_RC4_128_SHA);
- unimplemented.add(Cipher.TLS_PSK_WITH_RC4_128_SHA);
- unimplemented.add(Cipher.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA);
- unimplemented.add(Cipher.TLS_DHE_PSK_WITH_RC4_128_SHA);
- unimplemented.add(Cipher.TLS_ECDH_anon_WITH_RC4_128_SHA);
- // 3DES requires a compile time switch to enable. Treat as removed.
- unimplemented.add(Cipher.TLS_PSK_WITH_3DES_EDE_CBC_SHA);
- unimplemented.add(Cipher.TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA);
- unimplemented.add(Cipher.TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA);
- unimplemented.add(Cipher.TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA);
- unimplemented.add(Cipher.TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA);
- unimplemented.add(Cipher.TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA);
- unimplemented.add(Cipher.TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA);
- unimplemented.add(Cipher.TLS_DH_anon_WITH_3DES_EDE_CBC_SHA);
- unimplemented.add(Cipher.TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA);
- unimplemented.add(Cipher.TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA);
- unimplemented.add(Cipher.TLS_RSA_WITH_3DES_EDE_CBC_SHA);
- unimplemented.add(Cipher.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA);
- unimplemented.add(Cipher.TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA);
- unimplemented.add(Cipher.TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA);
- }
- OPENSSL_UNIMPLEMENTED_CIPHERS = Collections.unmodifiableSet(unimplemented);
- }
-
-
- private TesterOpenSSL() {
- // Utility class. Hide default constructor.
- }
-
-
- public static Set<String> getOpenSSLCiphersAsSet(String specification) throws Exception {
- String[] ciphers = getOpenSSLCiphersAsExpression(specification).trim().split(":");
- Set<String> result = new HashSet<>(ciphers.length);
- for (String cipher : ciphers) {
- result.add(cipher);
- }
- return result;
-
- }
-
-
- public static String getOpenSSLCiphersAsExpression(String specification) throws Exception {
- String stdout;
- if (specification == null) {
- stdout = executeOpenSSLCommand("ciphers", "-v");
- } else {
- stdout = executeOpenSSLCommand("ciphers", "-v", specification);
- }
-
- if (stdout.length() == 0) {
- return stdout;
- }
-
- StringBuilder output = new StringBuilder();
- boolean first = true;
-
- // OpenSSL should have returned one cipher per line
- String ciphers[] = stdout.split("\n");
- for (String cipher : ciphers) {
- // Handle rename for 1.1.0 onwards
- cipher = cipher.replaceAll("EDH", "DHE");
- if (first) {
- first = false;
- } else {
- output.append(':');
- }
- // Name is first part
- int i = cipher.indexOf(' ');
- output.append(cipher.substring(0, i));
-
- // Advance i past the space
- while (Character.isWhitespace(cipher.charAt(i))) {
- i++;
- }
-
- // Protocol is the second
- int j = cipher.indexOf(' ', i);
- output.append('+');
- output.append(cipher.substring(i, j));
- }
- return output.toString();
- }
-
-
- /*
- * Use this method to filter parser results when comparing them to OpenSSL
- * results to take account of unimplemented cipher suites.
- */
- public static void removeUnimplementedCiphersJsse(List<String> list) {
- for (Cipher cipher : OPENSSL_UNIMPLEMENTED_CIPHERS) {
- for (String jsseName : cipher.getJsseNames()) {
- list.remove(jsseName);
- }
- }
- }
-
-
- private static String executeOpenSSLCommand(String... args) throws IOException {
- String openSSLPath = System.getProperty("tomcat.test.openssl.path");
- if (openSSLPath == null || openSSLPath.length() == 0) {
- openSSLPath = "openssl";
- }
- List<String> cmd = new ArrayList<>();
- cmd.add(openSSLPath);
- for (String arg : args) {
- cmd.add(arg);
- }
-
- ProcessBuilder pb = new ProcessBuilder(cmd.toArray(new String[cmd.size()]));
- Process p = pb.start();
-
- InputStreamToText stdout = new InputStreamToText(p.getInputStream());
- InputStreamToText stderr = new InputStreamToText(p.getErrorStream());
-
- Thread t1 = new Thread(stdout);
- t1.setName("OpenSSL stdout reader");
- t1.start();
-
- Thread t2 = new Thread(stderr);
- t2.setName("OpenSSL stderr reader");
- t2.start();
-
- try {
- t1.join();
- t2.join();
- } catch (InterruptedException e) {
- throw new IOException(e);
- }
-
- String errorText = stderr.getText();
- if (errorText.length() > 0) {
- System.err.println(errorText);
- }
-
- return stdout.getText().trim();
- }
-
- private static class InputStreamToText implements Runnable {
-
- private final ByteArrayOutputStream baos = new ByteArrayOutputStream();
- private final InputStream is;
-
- InputStreamToText(InputStream is) {
- this.is = is;
- }
-
- @Override
- public void run() {
- try {
- IOTools.flow(is, baos);
- } catch (IOException e) {
- // Ignore
- }
- }
-
- public String getText() {
- return baos.toString();
- }
- }
-}
diff --git a/test/org/apache/tomcat/util/net/openssl/ciphers/TestCipher.java b/test/org/apache/tomcat/util/net/openssl/ciphers/TestCipher.java
new file mode 100644
index 0000000..4d9d836
--- /dev/null
+++ b/test/org/apache/tomcat/util/net/openssl/ciphers/TestCipher.java
@@ -0,0 +1,1067 @@
+/*
+ * 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.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class TestCipher {
+
+ /*
+ * Checks that every cipher suite returned by OpenSSL is mapped to at least
+ * one cipher suite that is recognised by JSSE or is a cipher suite known
+ * not to be supported by JSSE.
+ */
+ @Test
+ public void testAllOpenSSLCiphersMapped() throws Exception {
+ Set<String> openSSLCipherSuites = TesterOpenSSL.getOpenSSLCiphersAsSet("ALL:eNULL");
+
+ StringBuilder errors = new StringBuilder();
+
+ for (String openSSLCipherSuite : openSSLCipherSuites) {
+ List<String> jsseCipherSuites =
+ OpenSSLCipherConfigurationParser.parseExpression(openSSLCipherSuite);
+
+ for (JsseImpl jsseImpl : JSSE_IMPLS) {
+ boolean found = false;
+ for (String jsseCipherSuite : jsseCipherSuites) {
+ if (jsseImpl.getStandardNames().contains(jsseCipherSuite)) {
+ found = true;
+ if (jsseImpl.getOpenSslUnmapped().contains(openSSLCipherSuite)) {
+ errors.append("Mapping found in " + jsseImpl.getVendor() +
+ "'s JSSE implementation for " + openSSLCipherSuite +
+ " when none was expected\n");
+ }
+ break;
+ }
+ }
+ if (!found && !jsseImpl.getOpenSslUnmapped().contains(openSSLCipherSuite)) {
+ errors.append("No mapping found in " + jsseImpl.getVendor() +
+ "'s JSSE implementation for " + openSSLCipherSuite +
+ " when one was expected\n");
+ }
+ }
+ }
+ Assert.assertTrue(errors.toString(), errors.length() == 0);
+ }
+
+
+ /*
+ * Checks that the unit tests are running with a version of OpenSSL that
+ * includes all the expected ciphers and does not include any unexpected
+ * ones.
+ */
+ @Test
+ public void testOpenSSLCipherAvailability() throws Exception {
+ // 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.
+ Set<String> availableCipherSuites = TesterOpenSSL.getOpenSSLCiphersAsSet("ALL:eNULL:aRSA");
+ Set<String> expectedCipherSuites = new HashSet<>();
+ for (Cipher cipher : Cipher.values()) {
+ if (TesterOpenSSL.OPENSSL_UNIMPLEMENTED_CIPHERS.contains(cipher)) {
+ continue;
+ }
+ expectedCipherSuites.add(cipher.getOpenSSLAlias() + "+" +
+ cipher.getProtocol().getOpenSSLName());
+ }
+
+ Set<String> unavailableCipherSuites = new HashSet<>();
+ unavailableCipherSuites.addAll(expectedCipherSuites);
+ unavailableCipherSuites.removeAll(availableCipherSuites);
+ StringBuilder unavailableList = new StringBuilder();
+ for (String cipher : unavailableCipherSuites) {
+ unavailableList.append(cipher);
+ unavailableList.append(' ');
+ }
+ Assert.assertEquals(unavailableList.toString(), 0, unavailableCipherSuites.size());
+
+ Set<String> unexpectedCipherSuites = new HashSet<>();
+ unexpectedCipherSuites.addAll(availableCipherSuites);
+ unexpectedCipherSuites.removeAll(expectedCipherSuites);
+ StringBuilder unexpectedList = new StringBuilder();
+ for (String cipher : unexpectedCipherSuites) {
+ unexpectedList.append(cipher);
+ unexpectedList.append(' ');
+ }
+ Assert.assertEquals(unexpectedList.toString(), 0, unexpectedCipherSuites.size());
+ }
+
+
+ /**
+ * Check that the elements of the Cipher enumeration are all using standard
+ * names from the TLS registry or are known exceptions.
+ */
+ @Test
+ public void testNames() {
+ for (Cipher cipher : Cipher.values()) {
+ String name = cipher.name();
+ // These do not appear in TLS registry
+ if (name.contains("FORTEZZA")) {
+ continue;
+ }
+ if (name.contains("EXPORT1024") || name.equals("TLS_DHE_DSS_WITH_RC4_128_SHA")) {
+ continue;
+ }
+ if (name.startsWith("SSL_CK") || name.startsWith("SSL2")) {
+ continue;
+ }
+ Assert.assertTrue("Non-registered name used in Cipher enumeration: " + cipher,
+ REGISTERED_NAMES.contains(name));
+ }
+ }
+
+
+ /**
+ * These are all the Oracle standard Java names for cipher suites taken from
+ * http://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#ciphersuites
+ * on 15th July 2014.
+ */
+ private static final Set<String> CIPHER_SUITE_STANDARD_NAMES_ORACLE =
+ Collections.unmodifiableSet(new HashSet<>(Arrays.asList(
+ "SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA",
+ "SSL_DH_anon_EXPORT_WITH_RC4_40_MD5",
+ "SSL_DH_anon_WITH_3DES_EDE_CBC_SHA",
+ "TLS_DH_anon_WITH_AES_128_CBC_SHA",
+ "TLS_DH_anon_WITH_AES_128_CBC_SHA256",
+ "TLS_DH_anon_WITH_AES_128_GCM_SHA256",
+ "TLS_DH_anon_WITH_AES_256_CBC_SHA",
+ "TLS_DH_anon_WITH_AES_256_CBC_SHA256",
+ "TLS_DH_anon_WITH_AES_256_GCM_SHA384",
+ "TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA",
+ "TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA256",
+ "TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA",
+ "TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA256",
+ "SSL_DH_anon_WITH_DES_CBC_SHA",
+ "SSL_DH_anon_WITH_RC4_128_MD5",
+ "TLS_DH_anon_WITH_SEED_CBC_SHA",
+ "SSL_DH_DSS_EXPORT_WITH_DES40_CBC_SHA",
+ "SSL_DH_DSS_WITH_3DES_EDE_CBC_SHA",
+ "TLS_DH_DSS_WITH_AES_128_CBC_SHA",
+ "TLS_DH_DSS_WITH_AES_128_CBC_SHA256",
+ "TLS_DH_DSS_WITH_AES_128_GCM_SHA256",
+ "TLS_DH_DSS_WITH_AES_256_CBC_SHA",
+ "TLS_DH_DSS_WITH_AES_256_CBC_SHA256",
+ "TLS_DH_DSS_WITH_AES_256_GCM_SHA384",
+ "TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA",
+ "TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256",
+ "TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA",
+ "TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256",
+ "SSL_DH_DSS_WITH_DES_CBC_SHA",
+ "TLS_DH_DSS_WITH_SEED_CBC_SHA",
+ "SSL_DH_RSA_EXPORT_WITH_DES40_CBC_SHA",
+ "SSL_DH_RSA_WITH_3DES_EDE_CBC_SHA",
+ "TLS_DH_RSA_WITH_AES_128_CBC_SHA",
+ "TLS_DH_RSA_WITH_AES_128_CBC_SHA256",
+ "TLS_DH_RSA_WITH_AES_128_GCM_SHA256",
+ "TLS_DH_RSA_WITH_AES_256_CBC_SHA",
+ "TLS_DH_RSA_WITH_AES_256_CBC_SHA256",
+ "TLS_DH_RSA_WITH_AES_256_GCM_SHA384",
+ "TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA",
+ "TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256",
+ "TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA",
+ "TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256",
+ "SSL_DH_RSA_WITH_DES_CBC_SHA",
+ "TLS_DH_RSA_WITH_SEED_CBC_SHA",
+ "SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA",
+ "SSL_DHE_DSS_EXPORT1024_WITH_DES_CBC_SHA",
+ "SSL_DHE_DSS_EXPORT1024_WITH_RC4_56_SHA",
+ "SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA",
+ "TLS_DHE_DSS_WITH_AES_128_CBC_SHA",
+ "TLS_DHE_DSS_WITH_AES_128_CBC_SHA256",
+ "TLS_DHE_DSS_WITH_AES_128_GCM_SHA256",
+ "TLS_DHE_DSS_WITH_AES_256_CBC_SHA",
+ "TLS_DHE_DSS_WITH_AES_256_CBC_SHA256",
+ "TLS_DHE_DSS_WITH_AES_256_GCM_SHA384",
+ "TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA",
+ "TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256",
+ "TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA",
+ "TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256",
+ "SSL_DHE_DSS_WITH_DES_CBC_SHA",
+ "SSL_DHE_DSS_WITH_RC4_128_SHA",
+ "TLS_DHE_DSS_WITH_SEED_CBC_SHA",
+ "TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA",
+ "TLS_DHE_PSK_WITH_AES_128_CBC_SHA",
+ "TLS_DHE_PSK_WITH_AES_128_CBC_SHA256",
+ "TLS_DHE_PSK_WITH_AES_128_GCM_SHA256",
+ "TLS_DHE_PSK_WITH_AES_256_CBC_SHA",
+ "TLS_DHE_PSK_WITH_AES_256_CBC_SHA384",
+ "TLS_DHE_PSK_WITH_AES_256_GCM_SHA384",
+ "TLS_DHE_PSK_WITH_NULL_SHA",
+ "TLS_DHE_PSK_WITH_NULL_SHA256",
+ "TLS_DHE_PSK_WITH_NULL_SHA384",
+ "TLS_DHE_PSK_WITH_RC4_128_SHA",
+ "SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA",
+ "SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA",
+ "TLS_DHE_RSA_WITH_AES_128_CBC_SHA",
+ "TLS_DHE_RSA_WITH_AES_128_CBC_SHA256",
+ "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256",
+ "TLS_DHE_RSA_WITH_AES_256_CBC_SHA",
+ "TLS_DHE_RSA_WITH_AES_256_CBC_SHA256",
+ "TLS_DHE_RSA_WITH_AES_256_GCM_SHA384",
+ "TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA",
+ "TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256",
+ "TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA",
+ "TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256",
+ "SSL_DHE_RSA_WITH_DES_CBC_SHA",
+ "TLS_DHE_RSA_WITH_SEED_CBC_SHA",
+ "TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA",
+ "TLS_ECDH_anon_WITH_AES_128_CBC_SHA",
+ "TLS_ECDH_anon_WITH_AES_256_CBC_SHA",
+ "TLS_ECDH_anon_WITH_NULL_SHA",
+ "TLS_ECDH_anon_WITH_RC4_128_SHA",
+ "TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA",
+ "TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA",
+ "TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256",
+ "TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256",
+ "TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA",
+ "TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384",
+ "TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384",
+ "TLS_ECDH_ECDSA_WITH_NULL_SHA",
+ "TLS_ECDH_ECDSA_WITH_RC4_128_SHA",
+ "TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA",
+ "TLS_ECDH_RSA_WITH_AES_128_CBC_SHA",
+ "TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256",
+ "TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256",
+ "TLS_ECDH_RSA_WITH_AES_256_CBC_SHA",
+ "TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384",
+ "TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384",
+ "TLS_ECDH_RSA_WITH_NULL_SHA",
+ "TLS_ECDH_RSA_WITH_RC4_128_SHA",
+ "TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA",
+ "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
+ "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",
+ "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
+ "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
+ "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384",
+ "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
+ "TLS_ECDHE_ECDSA_WITH_NULL_SHA",
+ "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA",
+ "TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA",
+ "TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA",
+ "TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256",
+ "TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA",
+ "TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384",
+ "TLS_ECDHE_PSK_WITH_NULL_SHA",
+ "TLS_ECDHE_PSK_WITH_NULL_SHA256",
+ "TLS_ECDHE_PSK_WITH_NULL_SHA384",
+ "TLS_ECDHE_PSK_WITH_RC4_128_SHA",
+ "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA",
+ "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
+ "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",
+ "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
+ "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
+ "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384",
+ "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
+ "TLS_ECDHE_RSA_WITH_NULL_SHA",
+ "TLS_ECDHE_RSA_WITH_RC4_128_SHA",
+ "TLS_EMPTY_RENEGOTIATION_INFO_SCSV",
+ "SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA",
+ "SSL_FORTEZZA_DMS_WITH_NULL_SHA",
+ "TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5",
+ "TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA",
+ "TLS_KRB5_EXPORT_WITH_RC2_CBC_40_MD5",
+ "TLS_KRB5_EXPORT_WITH_RC2_CBC_40_SHA",
+ "TLS_KRB5_EXPORT_WITH_RC4_40_MD5",
+ "TLS_KRB5_EXPORT_WITH_RC4_40_SHA",
+ "TLS_KRB5_WITH_3DES_EDE_CBC_MD5",
+ "TLS_KRB5_WITH_3DES_EDE_CBC_SHA",
+ "TLS_KRB5_WITH_DES_CBC_MD5",
+ "TLS_KRB5_WITH_DES_CBC_SHA",
+ "TLS_KRB5_WITH_IDEA_CBC_MD5",
+ "TLS_KRB5_WITH_IDEA_CBC_SHA",
+ "TLS_KRB5_WITH_RC4_128_MD5",
+ "TLS_KRB5_WITH_RC4_128_SHA",
+ "TLS_PSK_WITH_3DES_EDE_CBC_SHA",
+ "TLS_PSK_WITH_AES_128_CBC_SHA",
+ "TLS_PSK_WITH_AES_128_CBC_SHA256",
+ "TLS_PSK_WITH_AES_128_GCM_SHA256",
+ "TLS_PSK_WITH_AES_256_CBC_SHA",
+ "TLS_PSK_WITH_AES_256_CBC_SHA384",
+ "TLS_PSK_WITH_AES_256_GCM_SHA384",
+ "TLS_PSK_WITH_NULL_SHA",
+ "TLS_PSK_WITH_NULL_SHA256",
+ "TLS_PSK_WITH_NULL_SHA384",
+ "TLS_PSK_WITH_RC4_128_SHA",
+ "SSL_RSA_EXPORT_WITH_DES40_CBC_SHA",
+ "SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5",
+ "SSL_RSA_EXPORT_WITH_RC4_40_MD5",
+ "SSL_RSA_EXPORT1024_WITH_DES_CBC_SHA",
+ "SSL_RSA_EXPORT1024_WITH_RC4_56_SHA",
+ "SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA",
+ "SSL_RSA_FIPS_WITH_DES_CBC_SHA",
+ "TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA",
+ "TLS_RSA_PSK_WITH_AES_128_CBC_SHA",
+ "TLS_RSA_PSK_WITH_AES_128_CBC_SHA256",
+ "TLS_RSA_PSK_WITH_AES_128_GCM_SHA256",
+ "TLS_RSA_PSK_WITH_AES_256_CBC_SHA",
+ "TLS_RSA_PSK_WITH_AES_256_CBC_SHA384",
+ "TLS_RSA_PSK_WITH_AES_256_GCM_SHA384",
+ "TLS_RSA_PSK_WITH_NULL_SHA",
+ "TLS_RSA_PSK_WITH_NULL_SHA256",
+ "TLS_RSA_PSK_WITH_NULL_SHA384",
+ "TLS_RSA_PSK_WITH_RC4_128_SHA",
+ "SSL_RSA_WITH_3DES_EDE_CBC_SHA",
+ "TLS_RSA_WITH_AES_128_CBC_SHA",
+ "TLS_RSA_WITH_AES_128_CBC_SHA256",
+ "TLS_RSA_WITH_AES_128_GCM_SHA256",
+ "TLS_RSA_WITH_AES_256_CBC_SHA",
+ "TLS_RSA_WITH_AES_256_CBC_SHA256",
+ "TLS_RSA_WITH_AES_256_GCM_SHA384",
+ "TLS_RSA_WITH_CAMELLIA_128_CBC_SHA",
+ "TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256",
+ "TLS_RSA_WITH_CAMELLIA_256_CBC_SHA",
+ "TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256",
+ "SSL_RSA_WITH_DES_CBC_SHA",
+ "SSL_RSA_WITH_IDEA_CBC_SHA",
+ "SSL_RSA_WITH_NULL_MD5",
+ "SSL_RSA_WITH_NULL_SHA",
+ "TLS_RSA_WITH_NULL_SHA256",
+ "SSL_RSA_WITH_RC4_128_MD5",
+ "SSL_RSA_WITH_RC4_128_SHA",
+ "TLS_RSA_WITH_SEED_CBC_SHA",
+ "TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA",
+ "TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA",
+ "TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA",
+ "TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA",
+ "TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA",
+ "TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA",
+ "TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA",
+ "TLS_SRP_SHA_WITH_AES_128_CBC_SHA",
+ "TLS_SRP_SHA_WITH_AES_256_CBC_SHA")));
+
+
+ /**
+ * These are the cipher suites implemented by OpenSSL that are not
+ * implemented by Oracle's JSSE implementation.
+ */
+ private static Set<String> OPENSSL_UNMAPPED_ORACLE =
+ Collections.unmodifiableSet(new HashSet<>(Arrays.asList(
+ "AES128-CCM+TLSv1.2",
+ "AES128-CCM8+TLSv1.2",
+ "AES256-CCM+TLSv1.2",
+ "AES256-CCM8+TLSv1.2",
+ "DES-CBC-MD5+SSLv2",
+ "DES-CBC3-MD5+SSLv2",
+ "DHE-PSK-AES128-CCM+TLSv1.2",
+ "DHE-PSK-AES128-CCM8+TLSv1.2",
+ "DHE-PSK-AES256-CCM+TLSv1.2",
+ "DHE-PSK-AES256-CCM8+TLSv1.2",
+ "DHE-PSK-CAMELLIA128-SHA256+TLSv1",
+ "DHE-PSK-CAMELLIA256-SHA384+TLSv1",
+ "DHE-PSK-CHACHA20-POLY1305+TLSv1.2",
+ "DHE-RSA-AES128-CCM+TLSv1.2",
+ "DHE-RSA-AES128-CCM8+TLSv1.2",
+ "DHE-RSA-AES256-CCM+TLSv1.2",
+ "DHE-RSA-AES256-CCM8+TLSv1.2",
+ "DHE-RSA-CHACHA20-POLY1305+TLSv1.2",
+ "ECDH-ECDSA-CAMELLIA128-SHA256+TLSv1.2",
+ "ECDH-ECDSA-CAMELLIA256-SHA384+TLSv1.2",
+ "ECDH-RSA-CAMELLIA128-SHA256+TLSv1.2",
+ "ECDH-RSA-CAMELLIA256-SHA384+TLSv1.2",
+ "ECDHE-ECDSA-AES128-CCM+TLSv1.2",
+ "ECDHE-ECDSA-AES128-CCM8+TLSv1.2",
+ "ECDHE-ECDSA-AES256-CCM+TLSv1.2",
+ "ECDHE-ECDSA-AES256-CCM8+TLSv1.2",
+ "ECDHE-ECDSA-CAMELLIA128-SHA256+TLSv1.2",
+ "ECDHE-ECDSA-CAMELLIA256-SHA384+TLSv1.2",
+ "ECDHE-ECDSA-CHACHA20-POLY1305+TLSv1.2",
+ "ECDHE-PSK-CAMELLIA128-SHA256+TLSv1",
+ "ECDHE-PSK-CAMELLIA256-SHA384+TLSv1",
+ "ECDHE-PSK-CHACHA20-POLY1305+TLSv1.2",
+ "ECDHE-RSA-CAMELLIA128-SHA256+TLSv1.2",
+ "ECDHE-RSA-CAMELLIA256-SHA384+TLSv1.2",
+ "ECDHE-RSA-CHACHA20-POLY1305+TLSv1.2",
+ "EXP-RC2-CBC-MD5+SSLv2",
+ "EXP-RC4-MD5+SSLv2",
+ "IDEA-CBC-MD5+SSLv2",
+ "PSK-AES128-CCM+TLSv1.2",
+ "PSK-AES128-CCM8+TLSv1.2",
+ "PSK-AES256-CCM+TLSv1.2",
+ "PSK-AES256-CCM8+TLSv1.2",
+ "PSK-CAMELLIA128-SHA256+TLSv1",
+ "PSK-CAMELLIA256-SHA384+TLSv1",
+ "PSK-CHACHA20-POLY1305+TLSv1.2",
+ "RC2-CBC-MD5+SSLv2",
+ "RC4-MD5+SSLv2",
+ "RSA-PSK-CAMELLIA128-SHA256+TLSv1",
+ "RSA-PSK-CAMELLIA256-SHA384+TLSv1",
+ "RSA-PSK-CHACHA20-POLY1305+TLSv1.2")));
+
+
+ /**
+ * These are all the IBM standard Java names for cipher suites taken from
+ * http://www-01.ibm.com/support/knowledgecenter/SSYKE2_7.0.0/com.ibm.java.security.component.71.doc/security-component/jsse2Docs/ciphersuites.html?lang=en
+ * on 29th July 2014.
+ * <br>
+ * As of 16 February 2015 the list for IBM Java 7 was identical to that for
+ * IBM Java 8
+ * http://www-01.ibm.com/support/knowledgecenter/SSYKE2_8.0.0/com.ibm.java.security.component.80.doc/security-component/jsse2Docs/ciphersuites.html?lang=en
+ * <br>
+ * Note that IBM cipher suites names can begin with TLS or SSL.
+ */
+ private static final Set<String> CIPHER_SUITE_STANDARD_NAMES_IBM;
+
+ static {
+ Set<String> sslNames = new HashSet<>(Arrays.asList(
+ "SSL_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384",
+ "SSL_ECDHE_RSA_WITH_AES_256_CBC_SHA384",
+ "SSL_RSA_WITH_AES_256_CBC_SHA256",
+ "SSL_ECDH_ECDSA_WITH_AES_256_CBC_SHA384",
+ "SSL_ECDH_RSA_WITH_AES_256_CBC_SHA384",
+ "SSL_DHE_RSA_WITH_AES_256_CBC_SHA256",
+ "SSL_DHE_DSS_WITH_AES_256_CBC_SHA256",
+ "SSL_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
+ "SSL_ECDHE_RSA_WITH_AES_256_CBC_SHA",
+ "SSL_RSA_WITH_AES_256_CBC_SHA",
+ "SSL_ECDH_ECDSA_WITH_AES_256_CBC_SHA",
+ "SSL_ECDH_RSA_WITH_AES_256_CBC_SHA",
+ "SSL_DHE_RSA_WITH_AES_256_CBC_SHA",
+ "SSL_DHE_DSS_WITH_AES_256_CBC_SHA",
+ "SSL_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",
+ "SSL_ECDHE_RSA_WITH_AES_128_CBC_SHA256",
+ "SSL_RSA_WITH_AES_128_CBC_SHA256",
+ "SSL_ECDH_ECDSA_WITH_AES_128_CBC_SHA256",
+ "SSL_ECDH_RSA_WITH_AES_128_CBC_SHA256",
+ "SSL_DHE_RSA_WITH_AES_128_CBC_SHA256",
+ "SSL_DHE_DSS_WITH_AES_128_CBC_SHA256",
+ "SSL_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
+ "SSL_ECDHE_RSA_WITH_AES_128_CBC_SHA",
+ "SSL_RSA_WITH_AES_128_CBC_SHA",
+ "SSL_ECDH_ECDSA_WITH_AES_128_CBC_SHA",
+ "SSL_ECDH_RSA_WITH_AES_128_CBC_SHA",
+ "SSL_DHE_RSA_WITH_AES_128_CBC_SHA",
+ "SSL_DHE_DSS_WITH_AES_128_CBC_SHA",
+ "SSL_ECDHE_ECDSA_WITH_RC4_128_SHA",
+ "SSL_ECDHE_RSA_WITH_RC4_128_SHA",
+ "SSL_RSA_WITH_RC4_128_SHA",
+ "SSL_ECDH_ECDSA_WITH_RC4_128_SHA",
+ "SSL_ECDH_RSA_WITH_RC4_128_SHA",
+ "SSL_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA",
+ "SSL_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA",
+ "SSL_RSA_WITH_3DES_EDE_CBC_SHA",
+ "SSL_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA",
+ "SSL_ECDH_RSA_WITH_3DES_EDE_CBC_SHA",
+ "SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA",
+ "SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA",
+ "SSL_RSA_WITH_RC4_128_MD5",
+ "TLS_EMPTY_RENEGOTIATION_INFO_SCSV",
+ "SSL_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
+ "SSL_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
+ "SSL_RSA_WITH_AES_256_GCM_SHA384",
+ "SSL_ECDH_ECDSA_WITH_AES_256_GCM_SHA384",
+ "SSL_ECDH_RSA_WITH_AES_256_GCM_SHA384",
+ "SSL_DHE_DSS_WITH_AES_256_GCM_SHA384",
+ "SSL_DHE_RSA_WITH_AES_256_GCM_SHA384",
+ "SSL_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
+ "SSL_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
+ "SSL_RSA_WITH_AES_128_GCM_SHA256",
+ "SSL_ECDH_ECDSA_WITH_AES_128_GCM_SHA256",
+ "SSL_ECDH_RSA_WITH_AES_128_GCM_SHA256",
+ "SSL_DHE_RSA_WITH_AES_128_GCM_SHA256",
+ "SSL_DHE_DSS_WITH_AES_128_GCM_SHA256",
+ "SSL_DH_anon_WITH_AES_256_CBC_SHA256",
+ "SSL_ECDH_anon_WITH_AES_256_CBC_SHA",
+ "SSL_DH_anon_WITH_AES_256_CBC_SHA",
+ "SSL_DH_anon_WITH_AES_256_GCM_SHA384",
+ "SSL_DH_anon_WITH_AES_128_GCM_SHA256",
+ "SSL_DH_anon_WITH_AES_128_CBC_SHA256",
+ "SSL_ECDH_anon_WITH_AES_128_CBC_SHA",
+ "SSL_DH_anon_WITH_AES_128_CBC_SHA",
+ "SSL_ECDH_anon_WITH_RC4_128_SHA",
+ "SSL_DH_anon_WITH_RC4_128_MD5",
+ "SSL_ECDH_anon_WITH_3DES_EDE_CBC_SHA",
+ "SSL_DH_anon_WITH_3DES_EDE_CBC_SHA",
+ "SSL_RSA_WITH_NULL_SHA256",
+ "SSL_ECDHE_ECDSA_WITH_NULL_SHA",
+ "SSL_ECDHE_RSA_WITH_NULL_SHA",
+ "SSL_RSA_WITH_NULL_SHA",
+ "SSL_ECDH_ECDSA_WITH_NULL_SHA",
+ "SSL_ECDH_RSA_WITH_NULL_SHA",
+ "SSL_ECDH_anon_WITH_NULL_SHA",
+ "SSL_RSA_WITH_NULL_MD5",
+ "SSL_RSA_WITH_DES_CBC_SHA",
+ "SSL_DHE_RSA_WITH_DES_CBC_SHA",
+ "SSL_DHE_DSS_WITH_DES_CBC_SHA",
+ "SSL_DH_anon_WITH_DES_CBC_SHA",
+ "SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA",
+ "SSL_RSA_FIPS_WITH_DES_EDE_CBC_SHA",
+ "SSL_DHE_DSS_WITH_RC4_128_SHA",
+ "SSL_RSA_EXPORT_WITH_RC4_40_MD5",
+ "SSL_DH_anon_EXPORT_WITH_RC4_40_MD5",
+ "SSL_RSA_EXPORT_WITH_DES40_CBC_SHA",
+ "SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA",
+ "SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA",
+ "SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA",
+ "SSL_KRB5_WITH_RC4_128_SHA",
+ "SSL_KRB5_WITH_RC4_128_MD5",
+ "SSL_KRB5_WITH_3DES_EDE_CBC_SHA",
+ "SSL_KRB5_WITH_3DES_EDE_CBC_MD5",
+ "SSL_KRB5_WITH_DES_CBC_SHA",
+ "SSL_KRB5_WITH_DES_CBC_MD5",
+ "SSL_KRB5_EXPORT_WITH_RC4_40_SHA",
+ "SSL_KRB5_EXPORT_WITH_RC4_40_MD5",
+ "SSL_KRB5_EXPORT_WITH_DES_CBC_40_SHA",
+ "SSL_KRB5_EXPORT_WITH_DES_CBC_40_MD5",
+ "SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5"));
+
+ Set<String> allNames = new HashSet<>();
+
+ allNames.addAll(sslNames);
+
+ for (String sslName : sslNames) {
+ allNames.add("TLS" + sslName.substring(3));
+ }
+
+ CIPHER_SUITE_STANDARD_NAMES_IBM = Collections.unmodifiableSet(allNames);
+ }
+
+
+ /**
+ * These are the cipher suites implemented by OpenSSL that are not
+ * implemented by IBM's JSSE implementation.
+ */
+ private static Set<String> OPENSSL_UNMAPPED_IBM =
+ Collections.unmodifiableSet(new HashSet<>(Arrays.asList(
+ "ADH-CAMELLIA128-SHA+SSLv3",
+ "ADH-CAMELLIA256-SHA+SSLv3",
+ "ADH-CAMELLIA128-SHA256+TLSv1.2",
+ "ADH-CAMELLIA256-SHA256+TLSv1.2",
+ "ADH-SEED-SHA+SSLv3",
+ "AES128-CCM+TLSv1.2",
+ "AES128-CCM8+TLSv1.2",
+ "AES256-CCM+TLSv1.2",
+ "AES256-CCM8+TLSv1.2",
+ "CAMELLIA128-SHA+SSLv3",
+ "CAMELLIA256-SHA+SSLv3",
+ "CAMELLIA128-SHA256+TLSv1.2",
+ "CAMELLIA256-SHA256+TLSv1.2",
+ "DES-CBC-MD5+SSLv2",
+ "DES-CBC3-MD5+SSLv2",
+ "DH-DSS-AES128-GCM-SHA256+TLSv1.2",
+ "DH-DSS-AES256-GCM-SHA384+TLSv1.2",
+ "DH-DSS-AES128-SHA+SSLv3",
+ "DH-DSS-AES128-SHA256+TLSv1.2",
+ "DH-DSS-AES256-SHA+SSLv3",
+ "DH-DSS-AES256-SHA256+TLSv1.2",
+ "DH-DSS-CAMELLIA128-SHA+SSLv3",
+ "DH-DSS-CAMELLIA128-SHA256+TLSv1.2",
+ "DH-DSS-CAMELLIA256-SHA+SSLv3",
+ "DH-DSS-CAMELLIA256-SHA256+TLSv1.2",
+ "DH-DSS-DES-CBC-SHA+SSLv3",
+ "DH-DSS-DES-CBC3-SHA+SSLv3",
+ "DH-DSS-SEED-SHA+SSLv3",
+ "DH-RSA-AES128-GCM-SHA256+TLSv1.2",
+ "DH-RSA-AES256-GCM-SHA384+TLSv1.2",
+ "DH-RSA-AES128-SHA+SSLv3",
+ "DH-RSA-AES128-SHA256+TLSv1.2",
+ "DH-RSA-AES256-SHA+SSLv3",
+ "DH-RSA-AES256-SHA256+TLSv1.2",
+ "DH-RSA-CAMELLIA128-SHA+SSLv3",
+ "DH-RSA-CAMELLIA128-SHA256+TLSv1.2",
+ "DH-RSA-CAMELLIA256-SHA+SSLv3",
+ "DH-RSA-CAMELLIA256-SHA256+TLSv1.2",
+ "DH-RSA-DES-CBC-SHA+SSLv3",
+ "DH-RSA-DES-CBC3-SHA+SSLv3",
+ "DH-RSA-SEED-SHA+SSLv3",
+ "DHE-DSS-CAMELLIA128-SHA+SSLv3",
+ "DHE-DSS-CAMELLIA128-SHA256+TLSv1.2",
+ "DHE-DSS-CAMELLIA256-SHA+SSLv3",
+ "DHE-DSS-CAMELLIA256-SHA256+TLSv1.2",
+ "DHE-DSS-SEED-SHA+SSLv3",
+ "DHE-PSK-3DES-EDE-CBC-SHA+SSLv3",
+ "DHE-PSK-AES128-CBC-SHA+SSLv3",
+ "DHE-PSK-AES128-CBC-SHA256+TLSv1",
+ "DHE-PSK-AES128-CCM+TLSv1.2",
+ "DHE-PSK-AES128-CCM8+TLSv1.2",
+ "DHE-PSK-AES128-GCM-SHA256+TLSv1.2",
+ "DHE-PSK-AES256-CBC-SHA+SSLv3",
+ "DHE-PSK-AES256-CBC-SHA384+TLSv1",
+ "DHE-PSK-AES256-CCM+TLSv1.2",
+ "DHE-PSK-AES256-CCM8+TLSv1.2",
+ "DHE-PSK-AES256-GCM-SHA384+TLSv1.2",
+ "DHE-PSK-CAMELLIA128-SHA256+TLSv1",
+ "DHE-PSK-CAMELLIA256-SHA384+TLSv1",
+ "DHE-PSK-CHACHA20-POLY1305+TLSv1.2",
+ "DHE-PSK-NULL-SHA+SSLv3",
+ "DHE-PSK-NULL-SHA256+TLSv1",
+ "DHE-PSK-NULL-SHA384+TLSv1",
+ "DHE-PSK-RC4-SHA+SSLv3",
+ "DHE-RSA-AES128-CCM+TLSv1.2",
+ "DHE-RSA-AES128-CCM8+TLSv1.2",
+ "DHE-RSA-AES256-CCM+TLSv1.2",
+ "DHE-RSA-AES256-CCM8+TLSv1.2",
+ "DHE-RSA-CAMELLIA128-SHA+SSLv3",
+ "DHE-RSA-CAMELLIA128-SHA256+TLSv1.2",
+ "DHE-RSA-CAMELLIA256-SHA+SSLv3",
+ "DHE-RSA-CAMELLIA256-SHA256+TLSv1.2",
+ "DHE-RSA-CHACHA20-POLY1305+TLSv1.2",
+ "DHE-RSA-SEED-SHA+SSLv3",
+ "ECDH-ECDSA-CAMELLIA128-SHA256+TLSv1.2",
+ "ECDH-ECDSA-CAMELLIA256-SHA384+TLSv1.2",
+ "ECDH-RSA-CAMELLIA128-SHA256+TLSv1.2",
+ "ECDH-RSA-CAMELLIA256-SHA384+TLSv1.2",
+ "ECDHE-ECDSA-AES128-CCM+TLSv1.2",
+ "ECDHE-ECDSA-AES128-CCM8+TLSv1.2",
+ "ECDHE-ECDSA-AES256-CCM+TLSv1.2",
+ "ECDHE-ECDSA-AES256-CCM8+TLSv1.2",
+ "ECDHE-ECDSA-CAMELLIA128-SHA256+TLSv1.2",
+ "ECDHE-ECDSA-CAMELLIA256-SHA384+TLSv1.2",
+ "ECDHE-ECDSA-CHACHA20-POLY1305+TLSv1.2",
+ "ECDHE-PSK-3DES-EDE-CBC-SHA+SSLv3",
+ "ECDHE-PSK-AES128-CBC-SHA+SSLv3",
+ "ECDHE-PSK-AES128-CBC-SHA256+TLSv1",
+ "ECDHE-PSK-AES256-CBC-SHA+SSLv3",
+ "ECDHE-PSK-AES256-CBC-SHA384+TLSv1",
+ "ECDHE-PSK-CAMELLIA128-SHA256+TLSv1",
+ "ECDHE-PSK-CAMELLIA256-SHA384+TLSv1",
+ "ECDHE-PSK-CHACHA20-POLY1305+TLSv1.2",
+ "ECDHE-PSK-NULL-SHA+SSLv3",
+ "ECDHE-PSK-NULL-SHA256+TLSv1",
+ "ECDHE-PSK-NULL-SHA384+TLSv1",
+ "ECDHE-PSK-RC4-SHA+SSLv3",
+ "ECDHE-RSA-CAMELLIA128-SHA256+TLSv1.2",
+ "ECDHE-RSA-CAMELLIA256-SHA384+TLSv1.2",
+ "ECDHE-RSA-CHACHA20-POLY1305+TLSv1.2",
+ "EXP-DH-DSS-DES-CBC-SHA+SSLv3",
+ "EXP-DH-RSA-DES-CBC-SHA+SSLv3",
+ "EXP-RC2-CBC-MD5+SSLv2",
+ "EXP-RC4-MD5+SSLv2",
+ "IDEA-CBC-MD5+SSLv2",
+ "IDEA-CBC-SHA+SSLv3",
+ "PSK-3DES-EDE-CBC-SHA+SSLv3",
+ "PSK-AES128-CBC-SHA+SSLv3",
+ "PSK-AES128-CBC-SHA256+TLSv1",
+ "PSK-AES128-CCM+TLSv1.2",
+ "PSK-AES128-CCM8+TLSv1.2",
+ "PSK-AES128-GCM-SHA256+TLSv1.2",
+ "PSK-AES256-CBC-SHA+SSLv3",
+ "PSK-AES256-CBC-SHA384+TLSv1",
+ "PSK-AES256-CCM+TLSv1.2",
+ "PSK-AES256-CCM8+TLSv1.2",
+ "PSK-AES256-GCM-SHA384+TLSv1.2",
+ "PSK-CAMELLIA128-SHA256+TLSv1",
+ "PSK-CAMELLIA256-SHA384+TLSv1",
+ "PSK-CHACHA20-POLY1305+TLSv1.2",
+ "PSK-NULL-SHA+SSLv3",
+ "PSK-NULL-SHA256+TLSv1",
+ "PSK-NULL-SHA384+TLSv1",
+ "PSK-RC4-SHA+SSLv3",
+ "RC2-CBC-MD5+SSLv2",
+ "RC4-MD5+SSLv2",
+ "RSA-PSK-3DES-EDE-CBC-SHA+SSLv3",
+ "RSA-PSK-AES128-CBC-SHA+SSLv3",
+ "RSA-PSK-AES128-CBC-SHA256+TLSv1",
+ "RSA-PSK-AES128-GCM-SHA256+TLSv1.2",
+ "RSA-PSK-AES256-CBC-SHA+SSLv3",
+ "RSA-PSK-AES256-CBC-SHA384+TLSv1",
+ "RSA-PSK-AES256-GCM-SHA384+TLSv1.2",
+ "RSA-PSK-CAMELLIA128-SHA256+TLSv1",
+ "RSA-PSK-CAMELLIA256-SHA384+TLSv1",
+ "RSA-PSK-CHACHA20-POLY1305+TLSv1.2",
+ "RSA-PSK-NULL-SHA+SSLv3",
+ "RSA-PSK-NULL-SHA256+TLSv1",
+ "RSA-PSK-NULL-SHA384+TLSv1",
+ "RSA-PSK-RC4-SHA+SSLv3",
+ "SEED-SHA+SSLv3",
+ "SRP-AES-128-CBC-SHA+SSLv3",
+ "SRP-AES-256-CBC-SHA+SSLv3",
+ "SRP-3DES-EDE-CBC-SHA+SSLv3",
+ "SRP-DSS-3DES-EDE-CBC-SHA+SSLv3",
+ "SRP-DSS-AES-128-CBC-SHA+SSLv3",
+ "SRP-DSS-AES-256-CBC-SHA+SSLv3",
+ "SRP-RSA-3DES-EDE-CBC-SHA+SSLv3",
+ "SRP-RSA-AES-128-CBC-SHA+SSLv3",
+ "SRP-RSA-AES-256-CBC-SHA+SSLv3")));
+
+
+ private static JsseImpl ORACLE_JSSE_CIPHER_IMPL = new JsseImpl("Oracle",
+ CIPHER_SUITE_STANDARD_NAMES_ORACLE, OPENSSL_UNMAPPED_ORACLE);
+
+
+ private static JsseImpl IBM_JSSE_CIPHER_IMPL = new JsseImpl("IBM",
+ CIPHER_SUITE_STANDARD_NAMES_IBM, OPENSSL_UNMAPPED_IBM);
+
+
+ private static Set<JsseImpl> JSSE_IMPLS = Collections.unmodifiableSet(
+ new HashSet<>(Arrays.asList(ORACLE_JSSE_CIPHER_IMPL, IBM_JSSE_CIPHER_IMPL)));
+
+
+ private static class JsseImpl {
+ private final String vendor;
+ private final Set<String> standardNames;
+ private final Set<String> openSslUnmapped;
+
+ public JsseImpl(String vendor, Set<String> standardNames,
+ Set<String> openSslUnmapped) {
+ this.vendor = vendor;
+ this.standardNames = standardNames;
+ this.openSslUnmapped = openSslUnmapped;
+ }
+
+ public String getVendor() {
+ return vendor;
+ }
+
+ public Set<String> getStandardNames() {
+ return standardNames;
+ }
+
+ public Set<String> getOpenSslUnmapped() {
+ return openSslUnmapped;
+ }
+ }
+
+
+ // Retrieved on 30 July 2014 from
+ // http://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-4
+ private static Set<String> REGISTERED_NAMES = Collections.unmodifiableSet(
+ new HashSet<>(Arrays.asList(
+ "TLS_NULL_WITH_NULL_NULL",
+ "TLS_RSA_WITH_NULL_MD5",
+ "TLS_RSA_WITH_NULL_SHA",
+ "TLS_RSA_EXPORT_WITH_RC4_40_MD5",
+ "TLS_RSA_WITH_RC4_128_MD5",
+ "TLS_RSA_WITH_RC4_128_SHA",
+ "TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5",
+ "TLS_RSA_WITH_IDEA_CBC_SHA",
+ "TLS_RSA_EXPORT_WITH_DES40_CBC_SHA",
+ "TLS_RSA_WITH_DES_CBC_SHA",
+ "TLS_RSA_WITH_3DES_EDE_CBC_SHA",
+ "TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA",
+ "TLS_DH_DSS_WITH_DES_CBC_SHA",
+ "TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA",
+ "TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA",
+ "TLS_DH_RSA_WITH_DES_CBC_SHA",
+ "TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA",
+ "TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA",
+ "TLS_DHE_DSS_WITH_DES_CBC_SHA",
+ "TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA",
+ "TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA",
+ "TLS_DHE_RSA_WITH_DES_CBC_SHA",
+ "TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA",
+ "TLS_DH_anon_EXPORT_WITH_RC4_40_MD5",
+ "TLS_DH_anon_WITH_RC4_128_MD5",
+ "TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA",
+ "TLS_DH_anon_WITH_DES_CBC_SHA",
+ "TLS_DH_anon_WITH_3DES_EDE_CBC_SHA",
+ "TLS_KRB5_WITH_DES_CBC_SHA",
+ "TLS_KRB5_WITH_3DES_EDE_CBC_SHA",
+ "TLS_KRB5_WITH_RC4_128_SHA",
+ "TLS_KRB5_WITH_IDEA_CBC_SHA",
+ "TLS_KRB5_WITH_DES_CBC_MD5",
+ "TLS_KRB5_WITH_3DES_EDE_CBC_MD5",
+ "TLS_KRB5_WITH_RC4_128_MD5",
+ "TLS_KRB5_WITH_IDEA_CBC_MD5",
+ "TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA",
+ "TLS_KRB5_EXPORT_WITH_RC2_CBC_40_SHA",
+ "TLS_KRB5_EXPORT_WITH_RC4_40_SHA",
+ "TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5",
+ "TLS_KRB5_EXPORT_WITH_RC2_CBC_40_MD5",
+ "TLS_KRB5_EXPORT_WITH_RC4_40_MD5",
+ "TLS_PSK_WITH_NULL_SHA",
+ "TLS_DHE_PSK_WITH_NULL_SHA",
+ "TLS_RSA_PSK_WITH_NULL_SHA",
+ "TLS_RSA_WITH_AES_128_CBC_SHA",
+ "TLS_DH_DSS_WITH_AES_128_CBC_SHA",
+ "TLS_DH_RSA_WITH_AES_128_CBC_SHA",
+ "TLS_DHE_DSS_WITH_AES_128_CBC_SHA",
+ "TLS_DHE_RSA_WITH_AES_128_CBC_SHA",
+ "TLS_DH_anon_WITH_AES_128_CBC_SHA",
+ "TLS_RSA_WITH_AES_256_CBC_SHA",
+ "TLS_DH_DSS_WITH_AES_256_CBC_SHA",
+ "TLS_DH_RSA_WITH_AES_256_CBC_SHA",
+ "TLS_DHE_DSS_WITH_AES_256_CBC_SHA",
+ "TLS_DHE_RSA_WITH_AES_256_CBC_SHA",
+ "TLS_DH_anon_WITH_AES_256_CBC_SHA",
+ "TLS_RSA_WITH_NULL_SHA256",
+ "TLS_RSA_WITH_AES_128_CBC_SHA256",
+ "TLS_RSA_WITH_AES_256_CBC_SHA256",
+ "TLS_DH_DSS_WITH_AES_128_CBC_SHA256",
+ "TLS_DH_RSA_WITH_AES_128_CBC_SHA256",
+ "TLS_DHE_DSS_WITH_AES_128_CBC_SHA256",
+ "TLS_RSA_WITH_CAMELLIA_128_CBC_SHA",
+ "TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA",
+ "TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA",
+ "TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA",
+ "TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA",
+ "TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA",
+ "TLS_DHE_RSA_WITH_AES_128_CBC_SHA256",
+ "TLS_DH_DSS_WITH_AES_256_CBC_SHA256",
+ "TLS_DH_RSA_WITH_AES_256_CBC_SHA256",
+ "TLS_DHE_DSS_WITH_AES_256_CBC_SHA256",
+ "TLS_DHE_RSA_WITH_AES_256_CBC_SHA256",
+ "TLS_DH_anon_WITH_AES_128_CBC_SHA256",
+ "TLS_DH_anon_WITH_AES_256_CBC_SHA256",
+ "TLS_RSA_WITH_CAMELLIA_256_CBC_SHA",
+ "TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA",
+ "TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA",
+ "TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA",
+ "TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA",
+ "TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA",
+ "TLS_PSK_WITH_RC4_128_SHA",
+ "TLS_PSK_WITH_3DES_EDE_CBC_SHA",
+ "TLS_PSK_WITH_AES_128_CBC_SHA",
+ "TLS_PSK_WITH_AES_256_CBC_SHA",
+ "TLS_DHE_PSK_WITH_RC4_128_SHA",
+ "TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA",
+ "TLS_DHE_PSK_WITH_AES_128_CBC_SHA",
+ "TLS_DHE_PSK_WITH_AES_256_CBC_SHA",
+ "TLS_RSA_PSK_WITH_RC4_128_SHA",
+ "TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA",
+ "TLS_RSA_PSK_WITH_AES_128_CBC_SHA",
+ "TLS_RSA_PSK_WITH_AES_256_CBC_SHA",
+ "TLS_RSA_WITH_SEED_CBC_SHA",
+ "TLS_DH_DSS_WITH_SEED_CBC_SHA",
+ "TLS_DH_RSA_WITH_SEED_CBC_SHA",
+ "TLS_DHE_DSS_WITH_SEED_CBC_SHA",
+ "TLS_DHE_RSA_WITH_SEED_CBC_SHA",
+ "TLS_DH_anon_WITH_SEED_CBC_SHA",
+ "TLS_RSA_WITH_AES_128_GCM_SHA256",
+ "TLS_RSA_WITH_AES_256_GCM_SHA384",
+ "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256",
+ "TLS_DHE_RSA_WITH_AES_256_GCM_SHA384",
+ "TLS_DH_RSA_WITH_AES_128_GCM_SHA256",
+ "TLS_DH_RSA_WITH_AES_256_GCM_SHA384",
+ "TLS_DHE_DSS_WITH_AES_128_GCM_SHA256",
+ "TLS_DHE_DSS_WITH_AES_256_GCM_SHA384",
+ "TLS_DH_DSS_WITH_AES_128_GCM_SHA256",
+ "TLS_DH_DSS_WITH_AES_256_GCM_SHA384",
+ "TLS_DH_anon_WITH_AES_128_GCM_SHA256",
+ "TLS_DH_anon_WITH_AES_256_GCM_SHA384",
+ "TLS_PSK_WITH_AES_128_GCM_SHA256",
+ "TLS_PSK_WITH_AES_256_GCM_SHA384",
+ "TLS_DHE_PSK_WITH_AES_128_GCM_SHA256",
+ "TLS_DHE_PSK_WITH_AES_256_GCM_SHA384",
+ "TLS_RSA_PSK_WITH_AES_128_GCM_SHA256",
+ "TLS_RSA_PSK_WITH_AES_256_GCM_SHA384",
+ "TLS_PSK_WITH_AES_128_CBC_SHA256",
+ "TLS_PSK_WITH_AES_256_CBC_SHA384",
+ "TLS_PSK_WITH_NULL_SHA256",
+ "TLS_PSK_WITH_NULL_SHA384",
+ "TLS_DHE_PSK_WITH_AES_128_CBC_SHA256",
+ "TLS_DHE_PSK_WITH_AES_256_CBC_SHA384",
+ "TLS_DHE_PSK_WITH_NULL_SHA256",
+ "TLS_DHE_PSK_WITH_NULL_SHA384",
+ "TLS_RSA_PSK_WITH_AES_128_CBC_SHA256",
+ "TLS_RSA_PSK_WITH_AES_256_CBC_SHA384",
+ "TLS_RSA_PSK_WITH_NULL_SHA256",
+ "TLS_RSA_PSK_WITH_NULL_SHA384",
+ "TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256",
+ "TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256",
+ "TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256",
+ "TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256",
+ "TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256",
+ "TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA256",
+ "TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256",
+ "TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256",
+ "TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256",
+ "TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256",
+ "TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256",
+ "TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA256",
+ "TLS_EMPTY_RENEGOTIATION_INFO_SCSV",
+ "TLS_ECDH_ECDSA_WITH_NULL_SHA",
+ "TLS_ECDH_ECDSA_WITH_RC4_128_SHA",
+ "TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA",
+ "TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA",
+ "TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA",
+ "TLS_ECDHE_ECDSA_WITH_NULL_SHA",
+ "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA",
+ "TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA",
+ "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
+ "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
+ "TLS_ECDH_RSA_WITH_NULL_SHA",
+ "TLS_ECDH_RSA_WITH_RC4_128_SHA",
+ "TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA",
+ "TLS_ECDH_RSA_WITH_AES_128_CBC_SHA",
+ "TLS_ECDH_RSA_WITH_AES_256_CBC_SHA",
+ "TLS_ECDHE_RSA_WITH_NULL_SHA",
+ "TLS_ECDHE_RSA_WITH_RC4_128_SHA",
+ "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA",
+ "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
+ "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
+ "TLS_ECDH_anon_WITH_NULL_SHA",
+ "TLS_ECDH_anon_WITH_RC4_128_SHA",
+ "TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA",
+ "TLS_ECDH_anon_WITH_AES_128_CBC_SHA",
+ "TLS_ECDH_anon_WITH_AES_256_CBC_SHA",
+ "TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA",
+ "TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA",
+ "TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA",
+ "TLS_SRP_SHA_WITH_AES_128_CBC_SHA",
+ "TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA",
+ "TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA",
+ "TLS_SRP_SHA_WITH_AES_256_CBC_SHA",
+ "TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA",
+ "TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA",
+ "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",
+ "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384",
+ "TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256",
+ "TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384",
+ "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",
+ "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384",
+ "TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256",
+ "TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384",
+ "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
+ "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
+ "TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256",
+ "TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384",
+ "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
+ "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
+ "TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256",
+ "TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384",
+ "TLS_ECDHE_PSK_WITH_RC4_128_SHA",
+ "TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA",
+ "TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA",
+ "TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA",
+ "TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256",
+ "TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384",
+ "TLS_ECDHE_PSK_WITH_NULL_SHA",
+ "TLS_ECDHE_PSK_WITH_NULL_SHA256",
+ "TLS_ECDHE_PSK_WITH_NULL_SHA384",
+ "TLS_RSA_WITH_ARIA_128_CBC_SHA256",
+ "TLS_RSA_WITH_ARIA_256_CBC_SHA384",
+ "TLS_DH_DSS_WITH_ARIA_128_CBC_SHA256",
+ "TLS_DH_DSS_WITH_ARIA_256_CBC_SHA384",
+ "TLS_DH_RSA_WITH_ARIA_128_CBC_SHA256",
+ "TLS_DH_RSA_WITH_ARIA_256_CBC_SHA384",
+ "TLS_DHE_DSS_WITH_ARIA_128_CBC_SHA256",
+ "TLS_DHE_DSS_WITH_ARIA_256_CBC_SHA384",
+ "TLS_DHE_RSA_WITH_ARIA_128_CBC_SHA256",
+ "TLS_DHE_RSA_WITH_ARIA_256_CBC_SHA384",
+ "TLS_DH_anon_WITH_ARIA_128_CBC_SHA256",
+ "TLS_DH_anon_WITH_ARIA_256_CBC_SHA384",
+ "TLS_ECDHE_ECDSA_WITH_ARIA_128_CBC_SHA256",
+ "TLS_ECDHE_ECDSA_WITH_ARIA_256_CBC_SHA384",
+ "TLS_ECDH_ECDSA_WITH_ARIA_128_CBC_SHA256",
+ "TLS_ECDH_ECDSA_WITH_ARIA_256_CBC_SHA384",
+ "TLS_ECDHE_RSA_WITH_ARIA_128_CBC_SHA256",
+ "TLS_ECDHE_RSA_WITH_ARIA_256_CBC_SHA384",
+ "TLS_ECDH_RSA_WITH_ARIA_128_CBC_SHA256",
+ "TLS_ECDH_RSA_WITH_ARIA_256_CBC_SHA384",
+ "TLS_RSA_WITH_ARIA_128_GCM_SHA256",
+ "TLS_RSA_WITH_ARIA_256_GCM_SHA384",
+ "TLS_DHE_RSA_WITH_ARIA_128_GCM_SHA256",
+ "TLS_DHE_RSA_WITH_ARIA_256_GCM_SHA384",
+ "TLS_DH_RSA_WITH_ARIA_128_GCM_SHA256",
+ "TLS_DH_RSA_WITH_ARIA_256_GCM_SHA384",
+ "TLS_DHE_DSS_WITH_ARIA_128_GCM_SHA256",
+ "TLS_DHE_DSS_WITH_ARIA_256_GCM_SHA384",
+ "TLS_DH_DSS_WITH_ARIA_128_GCM_SHA256",
+ "TLS_DH_DSS_WITH_ARIA_256_GCM_SHA384",
+ "TLS_DH_anon_WITH_ARIA_128_GCM_SHA256",
+ "TLS_DH_anon_WITH_ARIA_256_GCM_SHA384",
+ "TLS_ECDHE_ECDSA_WITH_ARIA_128_GCM_SHA256",
+ "TLS_ECDHE_ECDSA_WITH_ARIA_256_GCM_SHA384",
+ "TLS_ECDH_ECDSA_WITH_ARIA_128_GCM_SHA256",
+ "TLS_ECDH_ECDSA_WITH_ARIA_256_GCM_SHA384",
+ "TLS_ECDHE_RSA_WITH_ARIA_128_GCM_SHA256",
+ "TLS_ECDHE_RSA_WITH_ARIA_256_GCM_SHA384",
+ "TLS_ECDH_RSA_WITH_ARIA_128_GCM_SHA256",
+ "TLS_ECDH_RSA_WITH_ARIA_256_GCM_SHA384",
+ "TLS_PSK_WITH_ARIA_128_CBC_SHA256",
+ "TLS_PSK_WITH_ARIA_256_CBC_SHA384",
+ "TLS_DHE_PSK_WITH_ARIA_128_CBC_SHA256",
+ "TLS_DHE_PSK_WITH_ARIA_256_CBC_SHA384",
+ "TLS_RSA_PSK_WITH_ARIA_128_CBC_SHA256",
+ "TLS_RSA_PSK_WITH_ARIA_256_CBC_SHA384",
+ "TLS_PSK_WITH_ARIA_128_GCM_SHA256",
+ "TLS_PSK_WITH_ARIA_256_GCM_SHA384",
+ "TLS_DHE_PSK_WITH_ARIA_128_GCM_SHA256",
+ "TLS_DHE_PSK_WITH_ARIA_256_GCM_SHA384",
+ "TLS_RSA_PSK_WITH_ARIA_128_GCM_SHA256",
+ "TLS_RSA_PSK_WITH_ARIA_256_GCM_SHA384",
+ "TLS_ECDHE_PSK_WITH_ARIA_128_CBC_SHA256",
+ "TLS_ECDHE_PSK_WITH_ARIA_256_CBC_SHA384",
+ "TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256",
+ "TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384",
+ "TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256",
+ "TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384",
+ "TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256",
+ "TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384",
+ "TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256",
+ "TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384",
+ "TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256",
+ "TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384",
+ "TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256",
+ "TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384",
+ "TLS_DH_RSA_WITH_CAMELLIA_128_GCM_SHA256",
+ "TLS_DH_RSA_WITH_CAMELLIA_256_GCM_SHA384",
+ "TLS_DHE_DSS_WITH_CAMELLIA_128_GCM_SHA256",
+ "TLS_DHE_DSS_WITH_CAMELLIA_256_GCM_SHA384",
+ "TLS_DH_DSS_WITH_CAMELLIA_128_GCM_SHA256",
+ "TLS_DH_DSS_WITH_CAMELLIA_256_GCM_SHA384",
+ "TLS_DH_anon_WITH_CAMELLIA_128_GCM_SHA256",
+ "TLS_DH_anon_WITH_CAMELLIA_256_GCM_SHA384",
+ "TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256",
+ "TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384",
+ "TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256",
+ "TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384",
+ "TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256",
+ "TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384",
+ "TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256",
+ "TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384",
+ "TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256",
+ "TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384",
+ "TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256",
+ "TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384",
+ "TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256",
+ "TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384",
+ "TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256",
+ "TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384",
+ "TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256",
+ "TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384",
+ "TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256",
+ "TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384",
+ "TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256",
+ "TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384",
+ "TLS_RSA_WITH_AES_128_CCM",
+ "TLS_RSA_WITH_AES_256_CCM",
+ "TLS_DHE_RSA_WITH_AES_128_CCM",
+ "TLS_DHE_RSA_WITH_AES_256_CCM",
+ "TLS_RSA_WITH_AES_128_CCM_8",
+ "TLS_RSA_WITH_AES_256_CCM_8",
+ "TLS_DHE_RSA_WITH_AES_128_CCM_8",
+ "TLS_DHE_RSA_WITH_AES_256_CCM_8",
+ "TLS_PSK_WITH_AES_128_CCM",
+ "TLS_PSK_WITH_AES_256_CCM",
+ "TLS_DHE_PSK_WITH_AES_128_CCM",
+ "TLS_DHE_PSK_WITH_AES_256_CCM",
+ "TLS_PSK_WITH_AES_128_CCM_8",
+ "TLS_PSK_WITH_AES_256_CCM_8",
+ "TLS_PSK_DHE_WITH_AES_128_CCM_8",
+ "TLS_PSK_DHE_WITH_AES_256_CCM_8",
+ "TLS_ECDHE_ECDSA_WITH_AES_128_CCM",
+ "TLS_ECDHE_ECDSA_WITH_AES_256_CCM",
+ "TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8",
+ "TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8",
+ // From https://tools.ietf.org/html/draft-ietf-tls-chacha20-poly1305-04
+ // These might change.
+ "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
+ "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256",
+ "TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
+ "TLS_PSK_WITH_CHACHA20_POLY1305_SHA256",
+ "TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256",
+ "TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256",
+ "TLS_RSA_PSK_WITH_CHACHA20_POLY1305_SHA256")));
+}
diff --git a/test/org/apache/tomcat/util/net/openssl/ciphers/TestOpenSSLCipherConfigurationParser.java b/test/org/apache/tomcat/util/net/openssl/ciphers/TestOpenSSLCipherConfigurationParser.java
new file mode 100644
index 0000000..2e209b8
--- /dev/null
+++ b/test/org/apache/tomcat/util/net/openssl/ciphers/TestOpenSSLCipherConfigurationParser.java
@@ -0,0 +1,594 @@
+/*
+ * 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.List;
+
+import org.junit.Assert;
+import org.junit.Ignore;
+import org.junit.Test;
+
+public class TestOpenSSLCipherConfigurationParser {
+
+ @Test
+ public void testDEFAULT() throws Exception {
+ if (TesterOpenSSL.VERSION < 10100) {
+ // Account for classes of ciphers removed from DEFAULT in 1.1.0
+ testSpecification("DEFAULT:!RC4:!DSS:!SEED:!IDEA:!CAMELLIA:!AESCCM:!3DES");
+ } else {
+ testSpecification("DEFAULT");
+ }
+ }
+
+
+ @Test
+ public void testCOMPLEMENTOFDEFAULT() throws Exception {
+ if (TesterOpenSSL.VERSION < 10100) {
+ // Account for classes of ciphers removed from DEFAULT in 1.1.0
+ testSpecification("COMPLEMENTOFDEFAULT:RC4:DSS:SEED:IDEA:CAMELLIA:AESCCM:aNULL:3DES");
+ } else {
+ testSpecification("COMPLEMENTOFDEFAULT");
+ }
+ }
+
+
+ @Test
+ public void testALL() throws Exception {
+ testSpecification("ALL");
+ }
+
+
+ @Test
+ public void testCOMPLEMENTOFALL() throws Exception {
+ testSpecification("COMPLEMENTOFALL");
+ }
+
+
+ @Test
+ public void testaNULL() throws Exception {
+ testSpecification("aNULL");
+ }
+
+
+ @Test
+ public void testeNULL() throws Exception {
+ testSpecification("eNULL");
+ }
+
+
+ @Test
+ public void testHIGH() throws Exception {
+ testSpecification("HIGH");
+ }
+
+
+ @Test
+ public void testMEDIUM() throws Exception {
+ testSpecification("MEDIUM");
+ }
+
+
+ @Test
+ public void testLOW() throws Exception {
+ testSpecification("LOW");
+ }
+
+
+ @Test
+ public void testEXPORT40() throws Exception {
+ testSpecification("EXPORT40");
+ }
+
+
+ @Test
+ public void testEXPORT() throws Exception {
+ testSpecification("EXPORT");
+ }
+
+
+ @Test
+ public void testRSA() throws Exception {
+ testSpecification("RSA");
+ }
+
+
+ @Test
+ public void testaRSA() throws Exception {
+ testSpecification("aRSA");
+ }
+
+
+ @Test
+ public void testkRSA() throws Exception {
+ testSpecification("kRSA");
+ }
+
+
+ @Test
+ public void testkEDH() throws Exception {
+ testSpecification("kEDH");
+ }
+
+
+ @Test
+ public void testkDHE() throws Exception {
+ // This alias was introduced in 1.0.2
+ if (TesterOpenSSL.VERSION >= 10002) {
+ testSpecification("kDHE");
+ }
+ }
+
+
+ @Test
+ public void testEDH() throws Exception {
+ testSpecification("EDH");
+ }
+
+
+ @Test
+ public void testDHE() throws Exception {
+ // This alias was introduced in 1.0.2
+ if (TesterOpenSSL.VERSION >= 10002) {
+ testSpecification("DHE");
+ }
+ }
+
+
+ @Test
+ public void testkDHr() throws Exception {
+ testSpecification("kDHr");
+ }
+
+
+ @Test
+ public void testkDHd() throws Exception {
+ testSpecification("kDHd");
+ }
+
+
+ @Test
+ public void testkDH() throws Exception {
+ testSpecification("kDH");
+ }
+
+
+ @Test
+ public void testkECDHr() throws Exception {
+ testSpecification("kECDHr");
+ }
+
+
+ @Test
+ public void testkECDHe() throws Exception {
+ testSpecification("kECDHe");
+ }
+
+
+ @Test
+ public void testkECDH() throws Exception {
+ testSpecification("kECDH");
+ }
+
+
+ @Test
+ public void testkEECDH() throws Exception {
+ testSpecification("kEECDH");
+ }
+
+
+ @Test
+ public void testECDH() throws Exception {
+ testSpecification("ECDH");
+ }
+
+
+ @Test
+ public void testkECDHE() throws Exception {
+ testSpecification("kECDHE");
+ }
+
+
+ @Test
+ public void testECDHE() throws Exception {
+ testSpecification("ECDHE");
+ }
+
+
+ @Test
+ @Ignore("Contrary to the docs, OpenSSL does not recognise EECDHE")
+ public void testEECDHE() throws Exception {
+ testSpecification("EECDHE");
+ }
+
+
+ @Test
+ public void testAECDH() throws Exception {
+ testSpecification("AECDH");
+ }
+
+
+ @Test
+ public void testDSS() throws Exception {
+ testSpecification("DSS");
+ }
+
+
+ @Test
+ public void testaDSS() throws Exception {
+ testSpecification("aDSS");
+ }
+
+
+ @Test
+ public void testaDH() throws Exception {
+ testSpecification("aDH");
+ }
+
+
+ @Test
+ public void testaECDH() throws Exception {
+ testSpecification("aECDH");
+ }
+
+
+ @Test
+ public void testaECDSA() throws Exception {
+ testSpecification("aECDSA");
+ }
+
+
+ @Test
+ public void testECDSA() throws Exception {
+ testSpecification("ECDSA");
+ }
+
+
+ @Test
+ public void testkFZA() throws Exception {
+ testSpecification("kFZA");
+ }
+
+
+ @Test
+ public void testaFZA() throws Exception {
+ testSpecification("aFZA");
+ }
+
+
+ @Test
+ public void testeFZA() throws Exception {
+ testSpecification("eFZA");
+ }
+
+
+ @Test
+ public void testFZA() throws Exception {
+ testSpecification("FZA");
+ }
+
+
+ @Test
+ public void testTLSv1_2() throws Exception {
+ testSpecification("TLSv1.2");
+ }
+
+
+ @Test
+ public void testTLSv1() throws Exception {
+ // In OpenSSL 1.1.0-dev, TLSv1 refers to those ciphers that require
+ // TLSv1 rather than being an alias for SSLv3
+ if (TesterOpenSSL.VERSION >= 10100) {
+ testSpecification("TLSv1");
+ }
+ }
+
+
+ @Test
+ public void testSSLv2() throws Exception {
+ testSpecification("SSLv2");
+ }
+
+
+ @Test
+ public void testSSLv3() throws Exception {
+ testSpecification("SSLv3");
+ }
+
+
+ @Test
+ public void testDH() throws Exception {
+ testSpecification("DH");
+ }
+
+
+ @Test
+ public void testADH() throws Exception {
+ testSpecification("ADH");
+ }
+
+
+ @Test
+ public void testAES128() throws Exception {
+ testSpecification("AES128");
+ }
+
+
+ @Test
+ public void testAES256() throws Exception {
+ testSpecification("AES256");
+ }
+
+
+ @Test
+ public void testAES() throws Exception {
+ testSpecification("AES");
+ }
+
+
+ @Test
+ public void testAESGCM() throws Exception {
+ testSpecification("AESGCM");
+ }
+
+
+ @Test
+ public void testAESCCM() throws Exception {
+ testSpecification("AESCCM");
+ }
+
+
+ @Test
+ public void testAESCCM8() throws Exception {
+ testSpecification("AESCCM8");
+ }
+
+
+ @Test
+ public void testCAMELLIA128() throws Exception {
+ testSpecification("CAMELLIA128");
+ }
+
+
+ @Test
+ public void testCAMELLIA256() throws Exception {
+ testSpecification("CAMELLIA256");
+ }
+
+
+ @Test
+ public void testCAMELLIA() throws Exception {
+ testSpecification("CAMELLIA");
+ }
+
+
+ @Test
+ public void testCHACHA20() throws Exception {
+ testSpecification("CHACHA20");
+ }
+
+
+ @Test
+ public void test3DES() throws Exception {
+ testSpecification("3DES");
+ }
+
+
+ @Test
+ public void testDES() throws Exception {
+ testSpecification("DES");
+ }
+
+
+ @Test
+ public void testRC4() throws Exception {
+ testSpecification("RC4");
+ }
+
+
+ @Test
+ public void testRC2() throws Exception {
+ testSpecification("RC2");
+ }
+
+
+ @Test
+ public void testIDEA() throws Exception {
+ testSpecification("IDEA");
+ }
+
+
+ @Test
+ public void testSEED() throws Exception {
+ testSpecification("SEED");
+ }
+
+
+ @Test
+ public void testMD5() throws Exception {
+ testSpecification("MD5");
+ }
+
+
+ @Test
+ public void testSHA1() throws Exception {
+ testSpecification("SHA1");
+ }
+
+
+ @Test
+ public void testSHA() throws Exception {
+ testSpecification("SHA");
+ }
+
+
+ @Test
+ public void testSHA256() throws Exception {
+ testSpecification("SHA256");
+ }
+
+
+ @Test
+ public void testSHA384() throws Exception {
+ testSpecification("SHA384");
+ }
+
+
+ @Test
+ public void testKRB5() throws Exception {
+ testSpecification("KRB5");
+ }
+
+
+ @Test
+ public void testaGOST() throws Exception {
+ testSpecification("aGOST");
+ }
+
+
+ @Test
+ public void testaGOST01() throws Exception {
+ testSpecification("aGOST01");
+ }
+
+
+ @Test
+ public void testaGOST94() throws Exception {
+ testSpecification("aGOST94");
+ }
+
+
+ @Test
+ public void testkGOST() throws Exception {
+ testSpecification("kGOST");
+ }
+
+
+ @Test
+ public void testGOST94() throws Exception {
+ testSpecification("GOST94");
+ }
+
+
+ @Test
+ public void testGOST89MAC() throws Exception {
+ testSpecification("GOST89MAC");
+ }
+
+
+ @Test
+ public void testaPSK() throws Exception {
+ testSpecification("aPSK");
+ }
+
+
+ @Test
+ public void testkPSK() throws Exception {
+ testSpecification("kPSK");
+ }
+
+
+ @Test
+ public void testkRSAPSK() throws Exception {
+ testSpecification("kRSAPSK");
+ }
+
+
+ @Test
+ public void testkECDHEPSK() throws Exception {
+ testSpecification("kECDHEPSK");
+ }
+
+
+ @Test
+ public void testkDHEPSK() throws Exception {
+ testSpecification("kDHEPSK");
+ }
+
+
+ @Test
+ public void testPSK() throws Exception {
+ testSpecification("PSK");
+ }
+
+
+ // TODO: Add tests for the individual operators
+
+ @Test
+ public void testSpecification01() throws Exception {
+ // Tomcat 8 default as of 2014-08-04
+ // This gets an A- from https://www.ssllabs.com/ssltest with no FS for
+ // a number of the reference browsers
+ testSpecification("HIGH:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5");
+ }
+
+
+ @Test
+ public void testSpecification02() throws Exception {
+ // Suggestion from dev list (s/ECDHE/kEECDH/, s/DHE/EDH/
+ testSpecification("!aNULL:!eNULL:!EXPORT:!DSS:!DES:!SSLv2:kEECDH:ECDH:EDH:AES256-GCM-SHA384:AES128-GCM-SHA256:+RC4:HIGH:aRSA:kECDHr:MEDIUM");
+ }
+
+
+ @Test
+ public void testSpecification03() throws Exception {
+ // Reported as failing during 8.0.11 release vote by Ognjen Blagojevic
+ // EDH was introduced in 1.0.0
+ testSpecification("EECDH+aRSA+SHA384:EECDH:EDH+aRSA:RC4:!aNULL:!eNULL:!LOW:!3DES:!MD5:!EXP:!PSK:!SRP:!DSS");
+ }
+
+ private void testSpecification(String specification) throws Exception {
+ // Filter out cipher suites that OpenSSL does not implement
+ String openSSLCipherList = TesterOpenSSL.getOpenSSLCiphersAsExpression(specification);
+ List<String> jsseCipherListFromOpenSSL =
+ OpenSSLCipherConfigurationParser.parseExpression(openSSLCipherList);
+ List<String> jsseCipherListFromParser =
+ OpenSSLCipherConfigurationParser.parseExpression(specification);
+
+ TesterOpenSSL.removeUnimplementedCiphersJsse(jsseCipherListFromParser);
+
+ // First check the lists have the same entries
+ // Order is NOT important at this point. It is checked below.
+ Assert.assertEquals(jsseCipherListFromOpenSSL.size(), jsseCipherListFromParser.size());
+ Assert.assertTrue(jsseCipherListFromOpenSSL.containsAll(jsseCipherListFromParser));
+
+ // OpenSSL treats many ciphers as having equal preference. The order
+ // returned depends on the order they are requested. The following code
+ // checks that the Parser produces a cipher list that is consistent with
+ // OpenSSL's preference order by confirming that running through OpenSSL
+ // does not change the order.
+ String parserOrderedExpression = listToString(jsseCipherListFromParser, ',');
+ Assert.assertEquals(
+ listToString(OpenSSLCipherConfigurationParser.parseExpression(
+ parserOrderedExpression), ','),
+ parserOrderedExpression);
+ }
+
+
+ private String listToString(List<String> list, char separator) {
+ StringBuilder sb = new StringBuilder();
+ boolean first = true;
+ for (String entry : list) {
+ if (first) {
+ first = false;
+ } else {
+ sb.append(separator);
+ }
+ sb.append(entry);
+ }
+ return sb.toString();
+ }
+}
diff --git a/test/org/apache/tomcat/util/net/openssl/ciphers/TestOpenSSLCipherConfigurationParserOnly.java b/test/org/apache/tomcat/util/net/openssl/ciphers/TestOpenSSLCipherConfigurationParserOnly.java
new file mode 100644
index 0000000..795e977
--- /dev/null
+++ b/test/org/apache/tomcat/util/net/openssl/ciphers/TestOpenSSLCipherConfigurationParserOnly.java
@@ -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.openssl.ciphers;
+
+import java.util.LinkedHashSet;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+
+/*
+ * The unit test is independent of OpenSSL version and does not require OpenSSL
+ * to be present.
+ */
+public class TestOpenSSLCipherConfigurationParserOnly {
+
+ @Test
+ public void testDefaultSort01() throws Exception {
+ // Reproducing a failure observed on Gump with OpenSSL 1.1.x
+
+ // Everything else being equal, AES is preferred
+ LinkedHashSet<Cipher> input = new LinkedHashSet<>();
+ input.add(Cipher.TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384);
+ input.add(Cipher.TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384);
+ input.add(Cipher.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384);
+ input.add(Cipher.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384);
+ LinkedHashSet<Cipher> result = OpenSSLCipherConfigurationParser.defaultSort(input);
+
+ LinkedHashSet<Cipher> expected = new LinkedHashSet<>();
+ expected.add(Cipher.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384);
+ expected.add(Cipher.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384);
+ expected.add(Cipher.TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384);
+ expected.add(Cipher.TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384);
+
+ Assert.assertEquals(expected.toString(), result.toString());
+ }
+
+ @Test
+ public void testDefaultSort02() throws Exception {
+ // Reproducing a failure observed on Gump with OpenSSL 1.1.x
+
+ // ECHDE should beat AES
+ LinkedHashSet<Cipher> input = new LinkedHashSet<>();
+ input.add(Cipher.TLS_RSA_WITH_AES_256_CBC_SHA);
+ input.add(Cipher.TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384);
+ LinkedHashSet<Cipher> result = OpenSSLCipherConfigurationParser.defaultSort(input);
+
+ LinkedHashSet<Cipher> expected = new LinkedHashSet<>();
+ expected.add(Cipher.TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384);
+ expected.add(Cipher.TLS_RSA_WITH_AES_256_CBC_SHA);
+
+ Assert.assertEquals(expected.toString(), result.toString());
+ }
+
+ @Test
+ public void testDefaultSort03() throws Exception {
+ // Reproducing a failure observed on Gump with OpenSSL 1.1.x
+
+ // AES should beat CAMELLIA
+ LinkedHashSet<Cipher> input = new LinkedHashSet<>();
+ input.add(Cipher.TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384);
+ input.add(Cipher.TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384);
+ LinkedHashSet<Cipher> result = OpenSSLCipherConfigurationParser.defaultSort(input);
+
+ LinkedHashSet<Cipher> expected = new LinkedHashSet<>();
+ expected.add(Cipher.TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384);
+ expected.add(Cipher.TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384);
+
+ Assert.assertEquals(expected.toString(), result.toString());
+ }
+
+ @Test
+ public void testRename01() throws Exception {
+ // EDH -> DHE
+ LinkedHashSet<Cipher> result =
+ OpenSSLCipherConfigurationParser.parse("EXP-EDH-DSS-DES-CBC-SHA");
+ LinkedHashSet<Cipher> expected = new LinkedHashSet<>();
+ expected.add(Cipher.TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA);
+
+ Assert.assertEquals(expected, result);
+ }
+
+ @Test
+ public void testCustomOrdering() throws Exception {
+ // https://bz.apache.org/bugzilla/show_bug.cgi?id=59081
+ LinkedHashSet<Cipher> result = OpenSSLCipherConfigurationParser.parse(
+ "ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-RSA-DES-CBC3-SHA:" +
+ "DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:DES-CBC3-SHA");
+ LinkedHashSet<Cipher> expected = new LinkedHashSet<>();
+ expected.add(Cipher.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384);
+ expected.add(Cipher.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA);
+ expected.add(Cipher.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA);
+ expected.add(Cipher.TLS_DHE_RSA_WITH_AES_256_CBC_SHA);
+ expected.add(Cipher.TLS_DHE_RSA_WITH_AES_128_CBC_SHA);
+ expected.add(Cipher.TLS_RSA_WITH_3DES_EDE_CBC_SHA);
+
+ Assert.assertEquals(expected.toString(), result.toString());
+ }
+}
diff --git a/test/org/apache/tomcat/util/net/openssl/ciphers/TesterOpenSSL.java b/test/org/apache/tomcat/util/net/openssl/ciphers/TesterOpenSSL.java
new file mode 100644
index 0000000..fbbc464
--- /dev/null
+++ b/test/org/apache/tomcat/util/net/openssl/ciphers/TesterOpenSSL.java
@@ -0,0 +1,439 @@
+/*
+ * 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.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.catalina.util.IOTools;
+import org.apache.tomcat.util.http.fileupload.ByteArrayOutputStream;
+
+public class TesterOpenSSL {
+
+ public static final int VERSION;
+
+ public static final Set<Cipher> OPENSSL_UNIMPLEMENTED_CIPHERS;
+
+ static {
+ // Note: The following lists are intended to be aligned with the most
+ // recent release of each OpenSSL release branch. Running the unit
+ // tests with earlier releases is likely to result in failures.
+
+ String versionString = null;
+ try {
+ versionString = executeOpenSSLCommand("version");
+ } catch (IOException e) {
+ versionString = "";
+ }
+ if (versionString.startsWith("OpenSSL 1.1.1")) {
+ // Note: Gump currently tests 9.0.x with OpenSSL master
+ // (a.k.a 1.1.1-dev)
+ VERSION = 10101;
+ } else if (versionString.startsWith("OpenSSL 1.1.0")) {
+ // Support ends 2018-04-30
+ VERSION = 10100;
+ } else if (versionString.startsWith("OpenSSL 1.0.2")) {
+ // Support ends 2019-12-31 (LTS)
+ // Note: Gump current tests 8.0.x with OpenSSL 1.0.2
+ VERSION = 10002;
+ } else if (versionString.startsWith("OpenSSL 1.0.1")) {
+ // Support ends 2016-12-31
+ VERSION = 10001;
+ // Note: Release branches 1.0.0 and earlier are no longer supported by
+ // the OpenSSL team so these tests don't support them either.
+ } else {
+ VERSION = -1;
+ }
+
+ HashSet<Cipher> unimplemented = new HashSet<>();
+
+ // These have been removed from all supported versions.
+ unimplemented.add(Cipher.TLS_DHE_DSS_WITH_RC4_128_SHA);
+ unimplemented.add(Cipher.TLS_DHE_DSS_EXPORT1024_WITH_DES_CBC_SHA);
+ unimplemented.add(Cipher.TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA);
+ unimplemented.add(Cipher.TLS_RSA_EXPORT1024_WITH_RC2_CBC_56_MD5);
+ unimplemented.add(Cipher.TLS_DHE_DSS_EXPORT1024_WITH_RC4_56_SHA);
+ unimplemented.add(Cipher.TLS_RSA_EXPORT1024_WITH_RC4_56_SHA);
+ unimplemented.add(Cipher.TLS_RSA_EXPORT1024_WITH_RC4_56_MD5);
+ unimplemented.add(Cipher.TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA);
+ unimplemented.add(Cipher.TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA);
+ unimplemented.add(Cipher.TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256);
+ unimplemented.add(Cipher.TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256);
+ unimplemented.add(Cipher.TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256);
+ unimplemented.add(Cipher.TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256);
+ unimplemented.add(Cipher.TLS_DH_anon_WITH_DES_CBC_SHA);
+ unimplemented.add(Cipher.TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA);
+ unimplemented.add(Cipher.TLS_DH_anon_EXPORT_WITH_RC4_40_MD5);
+ unimplemented.add(Cipher.TLS_DHE_RSA_WITH_DES_CBC_SHA);
+ unimplemented.add(Cipher.TLS_DHE_DSS_WITH_DES_CBC_SHA);
+ unimplemented.add(Cipher.TLS_DH_RSA_WITH_DES_CBC_SHA);
+ unimplemented.add(Cipher.TLS_DH_DSS_WITH_DES_CBC_SHA);
+ unimplemented.add(Cipher.TLS_RSA_WITH_DES_CBC_SHA);
+ unimplemented.add(Cipher.SSL2_DES_64_CBC_WITH_MD5);
+ unimplemented.add(Cipher.TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA);
+ unimplemented.add(Cipher.TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA);
+ unimplemented.add(Cipher.TLS_RSA_EXPORT_WITH_DES40_CBC_SHA);
+ unimplemented.add(Cipher.TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5);
+ unimplemented.add(Cipher.TLS_RSA_EXPORT_WITH_RC4_40_MD5);
+ unimplemented.add(Cipher.SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5);
+ unimplemented.add(Cipher.SSL_CK_RC2_128_CBC_WITH_MD5);
+ unimplemented.add(Cipher.SSL_CK_RC4_128_WITH_MD5);
+ 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);
+
+ if (VERSION < 10002) {
+ // These were implemented in 1.0.2 so won't be available in any
+ // earlier version
+ unimplemented.add(Cipher.TLS_DH_DSS_WITH_AES_128_CBC_SHA);
+ unimplemented.add(Cipher.TLS_DH_DSS_WITH_AES_256_CBC_SHA);
+ unimplemented.add(Cipher.TLS_DH_DSS_WITH_AES_128_CBC_SHA256);
+ unimplemented.add(Cipher.TLS_DH_DSS_WITH_AES_256_CBC_SHA256);
+ unimplemented.add(Cipher.TLS_DH_DSS_WITH_AES_128_GCM_SHA256);
+ unimplemented.add(Cipher.TLS_DH_DSS_WITH_AES_256_GCM_SHA384);
+ unimplemented.add(Cipher.TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA);
+ unimplemented.add(Cipher.TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA);
+ unimplemented.add(Cipher.TLS_DH_DSS_WITH_DES_CBC_SHA);
+ unimplemented.add(Cipher.TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA);
+ unimplemented.add(Cipher.TLS_DH_DSS_WITH_SEED_CBC_SHA);
+ unimplemented.add(Cipher.TLS_DH_RSA_WITH_AES_128_CBC_SHA);
+ unimplemented.add(Cipher.TLS_DH_RSA_WITH_AES_256_CBC_SHA);
+ unimplemented.add(Cipher.TLS_DH_RSA_WITH_AES_128_CBC_SHA256);
+ unimplemented.add(Cipher.TLS_DH_RSA_WITH_AES_256_CBC_SHA256);
+ unimplemented.add(Cipher.TLS_DH_RSA_WITH_AES_128_GCM_SHA256);
+ unimplemented.add(Cipher.TLS_DH_RSA_WITH_AES_256_GCM_SHA384);
+ unimplemented.add(Cipher.TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA);
+ unimplemented.add(Cipher.TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA);
+ unimplemented.add(Cipher.TLS_DH_RSA_WITH_DES_CBC_SHA);
+ unimplemented.add(Cipher.TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA);
+ unimplemented.add(Cipher.TLS_DH_RSA_WITH_SEED_CBC_SHA);
+ } else {
+ // These were removed in 1.0.2 so won't be available from that
+ // version onwards.
+ // None at present.
+ }
+
+ if (VERSION < 10100) {
+ // These were implemented in 1.1.0 so won't be available in any
+ // earlier version
+ unimplemented.add(Cipher.TLS_PSK_WITH_NULL_SHA);
+ unimplemented.add(Cipher.TLS_DHE_PSK_WITH_NULL_SHA);
+ unimplemented.add(Cipher.TLS_RSA_PSK_WITH_NULL_SHA);
+ unimplemented.add(Cipher.TLS_DHE_PSK_WITH_AES_128_GCM_SHA256);
+ unimplemented.add(Cipher.TLS_DHE_PSK_WITH_AES_256_GCM_SHA384);
+ unimplemented.add(Cipher.TLS_RSA_PSK_WITH_AES_128_GCM_SHA256);
+ unimplemented.add(Cipher.TLS_RSA_PSK_WITH_AES_256_GCM_SHA384);
+ unimplemented.add(Cipher.TLS_PSK_WITH_AES_128_CBC_SHA256);
+ unimplemented.add(Cipher.TLS_PSK_WITH_AES_256_CBC_SHA384);
+ unimplemented.add(Cipher.TLS_PSK_WITH_NULL_SHA256);
+ unimplemented.add(Cipher.TLS_PSK_WITH_NULL_SHA384);
+ unimplemented.add(Cipher.TLS_DHE_PSK_WITH_AES_128_CBC_SHA256);
+ unimplemented.add(Cipher.TLS_DHE_PSK_WITH_AES_256_CBC_SHA384);
+ unimplemented.add(Cipher.TLS_DHE_PSK_WITH_NULL_SHA256);
+ unimplemented.add(Cipher.TLS_DHE_PSK_WITH_NULL_SHA384);
+ unimplemented.add(Cipher.TLS_RSA_PSK_WITH_AES_128_CBC_SHA256);
+ unimplemented.add(Cipher.TLS_RSA_PSK_WITH_AES_256_CBC_SHA384);
+ unimplemented.add(Cipher.TLS_RSA_PSK_WITH_NULL_SHA256);
+ unimplemented.add(Cipher.TLS_RSA_PSK_WITH_NULL_SHA384);
+ unimplemented.add(Cipher.TLS_DHE_PSK_WITH_RC4_128_SHA);
+ unimplemented.add(Cipher.TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA);
+ unimplemented.add(Cipher.TLS_DHE_PSK_WITH_AES_128_CBC_SHA);
+ unimplemented.add(Cipher.TLS_DHE_PSK_WITH_AES_256_CBC_SHA);
+ unimplemented.add(Cipher.TLS_RSA_PSK_WITH_RC4_128_SHA);
+ unimplemented.add(Cipher.TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA);
+ unimplemented.add(Cipher.TLS_RSA_PSK_WITH_AES_128_CBC_SHA);
+ unimplemented.add(Cipher.TLS_RSA_PSK_WITH_AES_256_CBC_SHA);
+ unimplemented.add(Cipher.TLS_ECDHE_PSK_WITH_RC4_128_SHA);
+ unimplemented.add(Cipher.TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA);
+ unimplemented.add(Cipher.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA);
+ unimplemented.add(Cipher.TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA);
+ unimplemented.add(Cipher.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256);
+ unimplemented.add(Cipher.TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384);
+ unimplemented.add(Cipher.TLS_ECDHE_PSK_WITH_NULL_SHA);
+ unimplemented.add(Cipher.TLS_ECDHE_PSK_WITH_NULL_SHA256);
+ unimplemented.add(Cipher.TLS_ECDHE_PSK_WITH_NULL_SHA384);
+ unimplemented.add(Cipher.TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256);
+ unimplemented.add(Cipher.TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384);
+ unimplemented.add(Cipher.TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256);
+ unimplemented.add(Cipher.TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384);
+ unimplemented.add(Cipher.TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256);
+ unimplemented.add(Cipher.TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384);
+ unimplemented.add(Cipher.TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256);
+ unimplemented.add(Cipher.TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384);
+ unimplemented.add(Cipher.TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256);
+ unimplemented.add(Cipher.TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256);
+ unimplemented.add(Cipher.TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256);
+ unimplemented.add(Cipher.TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256);
+ unimplemented.add(Cipher.TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384);
+ unimplemented.add(Cipher.TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384);
+ unimplemented.add(Cipher.TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256);
+ unimplemented.add(Cipher.TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256);
+ unimplemented.add(Cipher.TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384);
+ unimplemented.add(Cipher.TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256);
+ unimplemented.add(Cipher.TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384);
+ unimplemented.add(Cipher.TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256);
+ unimplemented.add(Cipher.TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256);
+ unimplemented.add(Cipher.TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256);
+ unimplemented.add(Cipher.TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256);
+ unimplemented.add(Cipher.TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA256);
+ unimplemented.add(Cipher.TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256);
+ unimplemented.add(Cipher.TLS_PSK_WITH_AES_128_GCM_SHA256);
+ unimplemented.add(Cipher.TLS_PSK_WITH_AES_256_GCM_SHA384);
+ unimplemented.add(Cipher.TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA256);
+ unimplemented.add(Cipher.TLS_RSA_WITH_AES_128_CCM);
+ unimplemented.add(Cipher.TLS_RSA_WITH_AES_256_CCM);
+ unimplemented.add(Cipher.TLS_DHE_RSA_WITH_AES_128_CCM);
+ unimplemented.add(Cipher.TLS_DHE_RSA_WITH_AES_256_CCM);
+ unimplemented.add(Cipher.TLS_RSA_WITH_AES_128_CCM_8);
+ unimplemented.add(Cipher.TLS_RSA_WITH_AES_256_CCM_8);
+ unimplemented.add(Cipher.TLS_DHE_RSA_WITH_AES_128_CCM_8);
+ unimplemented.add(Cipher.TLS_DHE_RSA_WITH_AES_256_CCM_8);
+ unimplemented.add(Cipher.TLS_PSK_WITH_AES_128_CCM);
+ unimplemented.add(Cipher.TLS_PSK_WITH_AES_256_CCM);
+ unimplemented.add(Cipher.TLS_DHE_PSK_WITH_AES_128_CCM);
+ unimplemented.add(Cipher.TLS_DHE_PSK_WITH_AES_256_CCM);
+ unimplemented.add(Cipher.TLS_PSK_WITH_AES_128_CCM_8);
+ unimplemented.add(Cipher.TLS_PSK_WITH_AES_256_CCM_8);
+ unimplemented.add(Cipher.TLS_PSK_DHE_WITH_AES_128_CCM_8);
+ unimplemented.add(Cipher.TLS_PSK_DHE_WITH_AES_256_CCM_8);
+ unimplemented.add(Cipher.TLS_ECDHE_ECDSA_WITH_AES_128_CCM);
+ unimplemented.add(Cipher.TLS_ECDHE_ECDSA_WITH_AES_256_CCM);
+ unimplemented.add(Cipher.TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8);
+ unimplemented.add(Cipher.TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8);
+ unimplemented.add(Cipher.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256);
+ unimplemented.add(Cipher.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256);
+ unimplemented.add(Cipher.TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256);
+ unimplemented.add(Cipher.TLS_PSK_WITH_CHACHA20_POLY1305_SHA256);
+ unimplemented.add(Cipher.TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256);
+ unimplemented.add(Cipher.TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256);
+ unimplemented.add(Cipher.TLS_RSA_PSK_WITH_CHACHA20_POLY1305_SHA256);
+ } else {
+ // These were removed in 1.1.0 so won't be available from that
+ // version onwards.
+ unimplemented.add(Cipher.TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA);
+ unimplemented.add(Cipher.TLS_DH_DSS_WITH_AES_128_CBC_SHA);
+ unimplemented.add(Cipher.TLS_DH_DSS_WITH_AES_128_CBC_SHA256);
+ unimplemented.add(Cipher.TLS_DH_DSS_WITH_AES_128_GCM_SHA256);
+ unimplemented.add(Cipher.TLS_DH_DSS_WITH_AES_256_CBC_SHA);
+ unimplemented.add(Cipher.TLS_DH_DSS_WITH_AES_256_CBC_SHA256);
+ unimplemented.add(Cipher.TLS_DH_DSS_WITH_AES_256_GCM_SHA384);
+ unimplemented.add(Cipher.TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA);
+ unimplemented.add(Cipher.TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA);
+ unimplemented.add(Cipher.TLS_DH_DSS_WITH_SEED_CBC_SHA);
+ unimplemented.add(Cipher.TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA);
+ unimplemented.add(Cipher.TLS_DH_RSA_WITH_AES_128_CBC_SHA);
+ unimplemented.add(Cipher.TLS_DH_RSA_WITH_AES_128_CBC_SHA256);
+ unimplemented.add(Cipher.TLS_DH_RSA_WITH_AES_128_GCM_SHA256);
+ unimplemented.add(Cipher.TLS_DH_RSA_WITH_AES_256_CBC_SHA);
+ unimplemented.add(Cipher.TLS_DH_RSA_WITH_AES_256_CBC_SHA256);
+ unimplemented.add(Cipher.TLS_DH_RSA_WITH_AES_256_GCM_SHA384);
+ unimplemented.add(Cipher.TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA);
+ unimplemented.add(Cipher.TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA);
+ unimplemented.add(Cipher.TLS_DH_RSA_WITH_SEED_CBC_SHA);
+ unimplemented.add(Cipher.TLS_ECDH_ECDSA_WITH_NULL_SHA);
+ unimplemented.add(Cipher.TLS_ECDH_ECDSA_WITH_RC4_128_SHA);
+ unimplemented.add(Cipher.TLS_ECDH_ECDSA_WITH_RC4_128_SHA);
+ unimplemented.add(Cipher.TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA);
+ unimplemented.add(Cipher.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA);
+ unimplemented.add(Cipher.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA);
+ unimplemented.add(Cipher.TLS_ECDH_RSA_WITH_NULL_SHA);
+ unimplemented.add(Cipher.TLS_ECDH_RSA_WITH_RC4_128_SHA);
+ unimplemented.add(Cipher.TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA);
+ unimplemented.add(Cipher.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA);
+ unimplemented.add(Cipher.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA);
+ unimplemented.add(Cipher.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256);
+ unimplemented.add(Cipher.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384);
+ unimplemented.add(Cipher.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256);
+ unimplemented.add(Cipher.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384);
+ unimplemented.add(Cipher.TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256);
+ unimplemented.add(Cipher.TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384);
+ unimplemented.add(Cipher.TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256);
+ unimplemented.add(Cipher.TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384);
+ unimplemented.add(Cipher.TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256);
+ unimplemented.add(Cipher.TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384);
+ unimplemented.add(Cipher.TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256);
+ unimplemented.add(Cipher.TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384);
+ unimplemented.add(Cipher.TLS_RSA_WITH_RC4_128_MD5);
+ unimplemented.add(Cipher.TLS_DH_anon_WITH_RC4_128_MD5);
+ unimplemented.add(Cipher.TLS_ECDHE_PSK_WITH_RC4_128_SHA);
+ unimplemented.add(Cipher.TLS_RSA_PSK_WITH_RC4_128_SHA);
+ unimplemented.add(Cipher.TLS_ECDHE_RSA_WITH_RC4_128_SHA);
+ unimplemented.add(Cipher.TLS_RSA_WITH_RC4_128_SHA);
+ unimplemented.add(Cipher.TLS_PSK_WITH_RC4_128_SHA);
+ unimplemented.add(Cipher.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA);
+ unimplemented.add(Cipher.TLS_DHE_PSK_WITH_RC4_128_SHA);
+ unimplemented.add(Cipher.TLS_ECDH_anon_WITH_RC4_128_SHA);
+ // 3DES requires a compile time switch to enable. Treat as removed.
+ unimplemented.add(Cipher.TLS_PSK_WITH_3DES_EDE_CBC_SHA);
+ unimplemented.add(Cipher.TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA);
+ unimplemented.add(Cipher.TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA);
+ unimplemented.add(Cipher.TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA);
+ unimplemented.add(Cipher.TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA);
+ unimplemented.add(Cipher.TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA);
+ unimplemented.add(Cipher.TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA);
+ unimplemented.add(Cipher.TLS_DH_anon_WITH_3DES_EDE_CBC_SHA);
+ unimplemented.add(Cipher.TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA);
+ unimplemented.add(Cipher.TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA);
+ unimplemented.add(Cipher.TLS_RSA_WITH_3DES_EDE_CBC_SHA);
+ unimplemented.add(Cipher.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA);
+ unimplemented.add(Cipher.TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA);
+ unimplemented.add(Cipher.TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA);
+ }
+ OPENSSL_UNIMPLEMENTED_CIPHERS = Collections.unmodifiableSet(unimplemented);
+ }
+
+
+ private TesterOpenSSL() {
+ // Utility class. Hide default constructor.
+ }
+
+
+ public static Set<String> getOpenSSLCiphersAsSet(String specification) throws Exception {
+ String[] ciphers = getOpenSSLCiphersAsExpression(specification).trim().split(":");
+ Set<String> result = new HashSet<>(ciphers.length);
+ for (String cipher : ciphers) {
+ result.add(cipher);
+ }
+ return result;
+
+ }
+
+
+ public static String getOpenSSLCiphersAsExpression(String specification) throws Exception {
+ String stdout;
+ if (specification == null) {
+ stdout = executeOpenSSLCommand("ciphers", "-v");
+ } else {
+ stdout = executeOpenSSLCommand("ciphers", "-v", specification);
+ }
+
+ if (stdout.length() == 0) {
+ return stdout;
+ }
+
+ StringBuilder output = new StringBuilder();
+ boolean first = true;
+
+ // OpenSSL should have returned one cipher per line
+ String ciphers[] = stdout.split("\n");
+ for (String cipher : ciphers) {
+ // Handle rename for 1.1.0 onwards
+ cipher = cipher.replaceAll("EDH", "DHE");
+ if (first) {
+ first = false;
+ } else {
+ output.append(':');
+ }
+ // Name is first part
+ int i = cipher.indexOf(' ');
+ output.append(cipher.substring(0, i));
+
+ // Advance i past the space
+ while (Character.isWhitespace(cipher.charAt(i))) {
+ i++;
+ }
+
+ // Protocol is the second
+ int j = cipher.indexOf(' ', i);
+ output.append('+');
+ output.append(cipher.substring(i, j));
+ }
+ return output.toString();
+ }
+
+
+ /*
+ * Use this method to filter parser results when comparing them to OpenSSL
+ * results to take account of unimplemented cipher suites.
+ */
+ public static void removeUnimplementedCiphersJsse(List<String> list) {
+ for (Cipher cipher : OPENSSL_UNIMPLEMENTED_CIPHERS) {
+ for (String jsseName : cipher.getJsseNames()) {
+ list.remove(jsseName);
+ }
+ }
+ }
+
+
+ private static String executeOpenSSLCommand(String... args) throws IOException {
+ String openSSLPath = System.getProperty("tomcat.test.openssl.path");
+ if (openSSLPath == null || openSSLPath.length() == 0) {
+ openSSLPath = "openssl";
+ }
+ List<String> cmd = new ArrayList<>();
+ cmd.add(openSSLPath);
+ for (String arg : args) {
+ cmd.add(arg);
+ }
+
+ ProcessBuilder pb = new ProcessBuilder(cmd.toArray(new String[cmd.size()]));
+ Process p = pb.start();
+
+ InputStreamToText stdout = new InputStreamToText(p.getInputStream());
+ InputStreamToText stderr = new InputStreamToText(p.getErrorStream());
+
+ Thread t1 = new Thread(stdout);
+ t1.setName("OpenSSL stdout reader");
+ t1.start();
+
+ Thread t2 = new Thread(stderr);
+ t2.setName("OpenSSL stderr reader");
+ t2.start();
+
+ try {
+ t1.join();
+ t2.join();
+ } catch (InterruptedException e) {
+ throw new IOException(e);
+ }
+
+ String errorText = stderr.getText();
+ if (errorText.length() > 0) {
+ System.err.println(errorText);
+ }
+
+ return stdout.getText().trim();
+ }
+
+ private static class InputStreamToText implements Runnable {
+
+ private final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ private final InputStream is;
+
+ InputStreamToText(InputStream is) {
+ this.is = is;
+ }
+
+ @Override
+ public void run() {
+ try {
+ IOTools.flow(is, baos);
+ } catch (IOException e) {
+ // Ignore
+ }
+ }
+
+ public String getText() {
+ return baos.toString();
+ }
+ }
+}
diff --git a/test/org/apache/tomcat/util/scan/TestStandardJarScanner.java b/test/org/apache/tomcat/util/scan/TestStandardJarScanner.java
index 102ff8a..2266c6b 100644
--- a/test/org/apache/tomcat/util/scan/TestStandardJarScanner.java
+++ b/test/org/apache/tomcat/util/scan/TestStandardJarScanner.java
@@ -18,7 +18,6 @@ package org.apache.tomcat.util.scan;
import java.io.File;
import java.io.IOException;
-import java.net.JarURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
@@ -40,6 +39,12 @@ public class TestStandardJarScanner {
StandardJarScanner scanner = new StandardJarScanner();
scanner.setScanClassPath(true);
+ // When running the test on Java 9, one or more URLs to jimage files may
+ // be returned. By setting the scanAllFiles option, a callback will be
+ // generated for these files which in turn will mean the number of URLs
+ // and the number of call backs will agree and this test will pass.
+ // There is a TODO in StandardJarScanner to add 'proper' Java 9 support.
+ scanner.setScanAllFiles(true);
LoggingCallback callback = new LoggingCallback();
@@ -106,12 +111,6 @@ public class TestStandardJarScanner {
}
@Override
- public void scan(JarURLConnection urlConn, String webappPath,
- boolean isWebapp) throws IOException {
- callbacks.add(urlConn.toString() + "::" + webappPath + "::" + isWebapp);
- }
-
- @Override
public void scan(File file, String webappPath, boolean isWebapp)
throws IOException {
callbacks.add(file.toString() + "::" + webappPath + "::" + isWebapp);
diff --git a/test/org/apache/tomcat/util/threads/TestLimitLatch.java b/test/org/apache/tomcat/util/threads/TestLimitLatch.java
index dd5fa1a..a1541ca 100644
--- a/test/org/apache/tomcat/util/threads/TestLimitLatch.java
+++ b/test/org/apache/tomcat/util/threads/TestLimitLatch.java
@@ -97,6 +97,8 @@ public class TestLimitLatch {
testThreads[i].start();
}
+ // Should have 10 threads in stage 2 and 20 in stage 1
+
for (int i = 0; i < 30; i++) {
if (!waitForThreadToStart(testThreads[i])) {
Assert.fail("Test thread [" + i + "] did not start");
diff --git a/test/org/apache/tomcat/websocket/TestConnectionLimit.java b/test/org/apache/tomcat/websocket/TestConnectionLimit.java
new file mode 100644
index 0000000..85c2a0f
--- /dev/null
+++ b/test/org/apache/tomcat/websocket/TestConnectionLimit.java
@@ -0,0 +1,109 @@
+/*
+ * 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.io.IOException;
+import java.net.URI;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import javax.websocket.ClientEndpointConfig.Builder;
+import javax.websocket.ContainerProvider;
+import javax.websocket.DeploymentException;
+import javax.websocket.WebSocketContainer;
+
+import org.junit.Ignore;
+import org.junit.Test;
+
+import org.apache.catalina.Context;
+import org.apache.catalina.servlets.DefaultServlet;
+import org.apache.catalina.startup.Tomcat;
+import org.apache.catalina.startup.TomcatBaseTest;
+import org.apache.tomcat.websocket.TesterMessageCountClient.TesterProgrammaticEndpoint;
+
+ at Ignore // Not for use in normal unit test runs
+public class TestConnectionLimit extends TomcatBaseTest {
+
+ /*
+ * Simple test to see how many outgoing connections can be created on a
+ * single machine.
+ */
+ @Test
+ public void testSingleMachine() throws Exception {
+ Tomcat tomcat = getTomcatInstance();
+ // No file system docBase required
+ Context ctx = tomcat.addContext("", null);
+ ctx.addApplicationListener(TesterEchoServer.Config.class.getName());
+ Tomcat.addServlet(ctx, "default", new DefaultServlet());
+ ctx.addServletMappingDecoded("/", "default");
+
+ tomcat.getConnector().setAttribute("maxConnections", "-1");
+
+ tomcat.start();
+
+ URI uri = new URI("ws://localhost:" + getPort() +
+ TesterEchoServer.Config.PATH_ASYNC);
+ AtomicInteger counter = new AtomicInteger(0);
+
+ int threadCount = 50;
+
+ Thread[] threads = new ConnectionThread[threadCount];
+
+ for (int i = 0; i < threadCount; i++) {
+ threads[i] = new ConnectionThread(counter, uri);
+ threads[i].start();
+ }
+
+ // Wait for the threads to die
+ for (Thread thread : threads) {
+ thread.join();
+ }
+
+ System.out.println("Maximum connection count was " + counter.get());
+ }
+
+ private static class ConnectionThread extends Thread {
+
+ private final AtomicInteger counter;
+ private final URI uri;
+
+ private ConnectionThread(AtomicInteger counter, URI uri) {
+ this.counter = counter;
+ this.uri = uri;
+ }
+
+ @Override
+ public void run() {
+ WebSocketContainer wsContainer =
+ ContainerProvider.getWebSocketContainer();
+
+ int count = 0;
+
+ try {
+ while (true) {
+ wsContainer.connectToServer(TesterProgrammaticEndpoint.class,
+ Builder.create().build(), uri);
+ count = counter.incrementAndGet();
+ if (count % 100 == 0) {
+ System.out.println(count + " and counting...");
+ }
+ }
+ } catch (IOException | DeploymentException ioe) {
+ // Let thread die
+ }
+ }
+ }
+}
diff --git a/test/org/apache/tomcat/websocket/TestWebSocketFrameClientSSL.java b/test/org/apache/tomcat/websocket/TestWebSocketFrameClientSSL.java
index 37c6750..1bd4588 100644
--- a/test/org/apache/tomcat/websocket/TestWebSocketFrameClientSSL.java
+++ b/test/org/apache/tomcat/websocket/TestWebSocketFrameClientSSL.java
@@ -28,7 +28,6 @@ import javax.websocket.Session;
import javax.websocket.WebSocketContainer;
import org.junit.Assert;
-import org.junit.Assume;
import org.junit.Test;
import org.apache.catalina.Context;
@@ -41,7 +40,6 @@ import org.apache.tomcat.websocket.TesterMessageCountClient.TesterProgrammaticEn
public class TestWebSocketFrameClientSSL extends WebSocketBaseTest {
-
@Test
public void testConnectToServerEndpoint() throws Exception {
Tomcat tomcat = getTomcatInstance();
@@ -60,7 +58,7 @@ public class TestWebSocketFrameClientSSL extends WebSocketBaseTest {
ClientEndpointConfig clientEndpointConfig =
ClientEndpointConfig.Builder.create().build();
clientEndpointConfig.getUserProperties().put(
- WsWebSocketContainer.SSL_TRUSTSTORE_PROPERTY,
+ Constants.SSL_TRUSTSTORE_PROPERTY,
"test/org/apache/tomcat/util/net/ca.jks");
Session wsSession = wsContainer.connectToServer(
TesterProgrammaticEndpoint.class,
@@ -91,13 +89,6 @@ public class TestWebSocketFrameClientSSL extends WebSocketBaseTest {
@Test
public void testBug56032() throws Exception {
- // TODO Investigate options to get this test to pass with the HTTP BIO
- // connector.
- Assume.assumeFalse(
- "Skip this test on BIO. TODO: investigate options to make it pass with HTTP BIO connector",
- getTomcatInstance().getConnector().getProtocol()
- .equals("org.apache.coyote.http11.Http11Protocol"));
-
Tomcat tomcat = getTomcatInstance();
// No file system docBase required
Context ctx = tomcat.addContext("", null);
@@ -114,7 +105,7 @@ public class TestWebSocketFrameClientSSL extends WebSocketBaseTest {
ClientEndpointConfig clientEndpointConfig =
ClientEndpointConfig.Builder.create().build();
clientEndpointConfig.getUserProperties().put(
- WsWebSocketContainer.SSL_TRUSTSTORE_PROPERTY,
+ Constants.SSL_TRUSTSTORE_PROPERTY,
"test/org/apache/tomcat/util/net/ca.jks");
Session wsSession = wsContainer.connectToServer(
TesterProgrammaticEndpoint.class,
diff --git a/test/org/apache/tomcat/websocket/TestWsPingPongMessages.java b/test/org/apache/tomcat/websocket/TestWsPingPongMessages.java
index 53036a0..32af691 100644
--- a/test/org/apache/tomcat/websocket/TestWsPingPongMessages.java
+++ b/test/org/apache/tomcat/websocket/TestWsPingPongMessages.java
@@ -57,8 +57,6 @@ public class TestWsPingPongMessages extends WebSocketBaseTest {
WebSocketContainer wsContainer = ContainerProvider
.getWebSocketContainer();
- tomcat.start();
-
Session wsSession = wsContainer.connectToServer(
TesterProgrammaticEndpoint.class, ClientEndpointConfig.Builder
.create().build(), new URI("ws://localhost:"
diff --git a/test/org/apache/tomcat/websocket/TestWsRemoteEndpoint.java b/test/org/apache/tomcat/websocket/TestWsRemoteEndpoint.java
index ad65d32..8c01d1a 100644
--- a/test/org/apache/tomcat/websocket/TestWsRemoteEndpoint.java
+++ b/test/org/apache/tomcat/websocket/TestWsRemoteEndpoint.java
@@ -61,25 +61,35 @@ public class TestWsRemoteEndpoint extends WebSocketBaseTest {
@Test
public void testWriterAnnotation() throws Exception {
- doTestWriter(TesterAnnotatedEndpoint.class, true);
+ doTestWriter(TesterAnnotatedEndpoint.class, true, TEST_MESSAGE_5K);
}
@Test
public void testWriterProgrammatic() throws Exception {
- doTestWriter(TesterProgrammaticEndpoint.class, true);
+ doTestWriter(TesterProgrammaticEndpoint.class, true, TEST_MESSAGE_5K);
+ }
+
+ @Test
+ public void testWriterZeroLengthAnnotation() throws Exception {
+ doTestWriter(TesterAnnotatedEndpoint.class, true, "");
+ }
+
+ @Test
+ public void testWriterZeroLengthProgrammatic() throws Exception {
+ doTestWriter(TesterProgrammaticEndpoint.class, true, "");
}
@Test
public void testStreamAnnotation() throws Exception {
- doTestWriter(TesterAnnotatedEndpoint.class, false);
+ doTestWriter(TesterAnnotatedEndpoint.class, false, TEST_MESSAGE_5K);
}
@Test
public void testStreamProgrammatic() throws Exception {
- doTestWriter(TesterProgrammaticEndpoint.class, false);
+ doTestWriter(TesterProgrammaticEndpoint.class, false, TEST_MESSAGE_5K);
}
- private void doTestWriter(Class<?> clazz, boolean useWriter) throws Exception {
+ private void doTestWriter(Class<?> clazz, boolean useWriter, String testMessage) throws Exception {
Tomcat tomcat = getTomcatInstance();
// No file system docBase required
Context ctx = tomcat.addContext("", null);
@@ -122,7 +132,7 @@ public class TestWsRemoteEndpoint extends WebSocketBaseTest {
Writer w = wsSession.getBasicRemote().getSendWriter();
for (int i = 0; i < 8; i++) {
- w.write(TEST_MESSAGE_5K);
+ w.write(testMessage);
}
w.close();
@@ -130,7 +140,7 @@ public class TestWsRemoteEndpoint extends WebSocketBaseTest {
OutputStream s = wsSession.getBasicRemote().getSendStream();
for (int i = 0; i < 8; i++) {
- s.write(TEST_MESSAGE_5K.getBytes(StandardCharsets.UTF_8));
+ s.write(testMessage.getBytes(StandardCharsets.UTF_8));
}
s.close();
@@ -161,20 +171,77 @@ public class TestWsRemoteEndpoint extends WebSocketBaseTest {
int offset = 0;
int i = 0;
for (String result : results) {
- // First may be a fragment
- Assert.assertEquals(SEQUENCE.substring(offset, S_LEN),
- result.substring(0, S_LEN - offset));
- i = S_LEN - offset;
- while (i + S_LEN < result.length()) {
- if (!SEQUENCE.equals(result.substring(i, i + S_LEN))) {
+ if (testMessage.length() == 0) {
+ Assert.assertEquals(0, result.length());
+ } else {
+ // First may be a fragment
+ Assert.assertEquals(SEQUENCE.substring(offset, S_LEN),
+ result.substring(0, S_LEN - offset));
+ i = S_LEN - offset;
+ while (i + S_LEN < result.length()) {
+ if (!SEQUENCE.equals(result.substring(i, i + S_LEN))) {
+ Assert.fail();
+ }
+ i += S_LEN;
+ }
+ offset = result.length() - i;
+ if (!SEQUENCE.substring(0, offset).equals(result.substring(i))) {
Assert.fail();
}
- i += S_LEN;
- }
- offset = result.length() - i;
- if (!SEQUENCE.substring(0, offset).equals(result.substring(i))) {
- Assert.fail();
}
}
}
+
+ @Test
+ public void testWriterErrorAnnotation() throws Exception {
+ doTestWriterError(TesterAnnotatedEndpoint.class);
+ }
+
+ @Test
+ public void testWriterErrorProgrammatic() throws Exception {
+ doTestWriterError(TesterProgrammaticEndpoint.class);
+ }
+
+ private void doTestWriterError(Class<?> clazz) throws Exception {
+ Tomcat tomcat = getTomcatInstance();
+ // No file system docBase required
+ Context ctx = tomcat.addContext("", null);
+ ctx.addApplicationListener(TesterEchoServer.Config.class.getName());
+ Tomcat.addServlet(ctx, "default", new DefaultServlet());
+ ctx.addServletMappingDecoded("/", "default");
+
+ WebSocketContainer wsContainer = ContainerProvider.getWebSocketContainer();
+
+ tomcat.start();
+
+ Session wsSession;
+ URI uri = new URI("ws://localhost:" + getPort() + TesterEchoServer.Config.PATH_WRITER_ERROR);
+ if (Endpoint.class.isAssignableFrom(clazz)) {
+ @SuppressWarnings("unchecked")
+ Class<? extends Endpoint> endpointClazz = (Class<? extends Endpoint>) clazz;
+ wsSession = wsContainer.connectToServer(endpointClazz, Builder.create().build(), uri);
+ } else {
+ wsSession = wsContainer.connectToServer(clazz, uri);
+ }
+
+ CountDownLatch latch = new CountDownLatch(1);
+ TesterEndpoint tep = (TesterEndpoint) wsSession.getUserProperties().get("endpoint");
+ tep.setLatch(latch);
+ AsyncHandler<?> handler;
+ handler = new AsyncText(latch);
+
+ wsSession.addMessageHandler(handler);
+
+ // This should trigger the error
+ wsSession.getBasicRemote().sendText("Start");
+
+ boolean latchResult = handler.getLatch().await(10, TimeUnit.SECONDS);
+
+ Assert.assertTrue(latchResult);
+
+ @SuppressWarnings("unchecked")
+ List<String> messages = (List<String>) handler.getMessages();
+
+ Assert.assertEquals(0, messages.size());
+ }
}
diff --git a/test/org/apache/tomcat/websocket/TestWsSubprotocols.java b/test/org/apache/tomcat/websocket/TestWsSubprotocols.java
index fc1a96d..d6b848e 100644
--- a/test/org/apache/tomcat/websocket/TestWsSubprotocols.java
+++ b/test/org/apache/tomcat/websocket/TestWsSubprotocols.java
@@ -20,15 +20,12 @@ import java.net.URI;
import java.util.Arrays;
import java.util.List;
-import javax.servlet.ServletContextEvent;
import javax.websocket.ClientEndpointConfig;
import javax.websocket.ContainerProvider;
-import javax.websocket.DeploymentException;
import javax.websocket.EndpointConfig;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.WebSocketContainer;
-import javax.websocket.server.ServerContainer;
import javax.websocket.server.ServerEndpoint;
import javax.websocket.server.ServerEndpointConfig;
@@ -40,8 +37,7 @@ import org.apache.catalina.Context;
import org.apache.catalina.servlets.DefaultServlet;
import org.apache.catalina.startup.Tomcat;
import org.apache.tomcat.websocket.TesterMessageCountClient.TesterProgrammaticEndpoint;
-import org.apache.tomcat.websocket.server.Constants;
-import org.apache.tomcat.websocket.server.WsContextListener;
+import org.apache.tomcat.websocket.server.TesterEndpointConfig;
public class TestWsSubprotocols extends WebSocketBaseTest {
@@ -99,7 +95,7 @@ public class TestWsSubprotocols extends WebSocketBaseTest {
@ServerEndpoint(value = "/echo", subprotocols = {"sp1","sp2"})
public static class SubProtocolsEndpoint {
- public static String PATH_BASIC = "/echo";
+ public static final String PATH_BASIC = "/echo";
public static volatile List<String> subprotocols;
@OnOpen
@@ -114,18 +110,11 @@ public class TestWsSubprotocols extends WebSocketBaseTest {
}
- public static class Config extends WsContextListener {
+ public static class Config extends TesterEndpointConfig {
+
@Override
- public void contextInitialized(ServletContextEvent sce) {
- super.contextInitialized(sce);
- ServerContainer sc = (ServerContainer) sce.getServletContext()
- .getAttribute(Constants.
- SERVER_CONTAINER_SERVLET_CONTEXT_ATTRIBUTE);
- try {
- sc.addEndpoint(SubProtocolsEndpoint.class);
- } catch (DeploymentException e) {
- throw new IllegalStateException(e);
- }
+ protected Class<?> getEndpointClass() {
+ return SubProtocolsEndpoint.class;
}
}
}
diff --git a/test/org/apache/tomcat/websocket/TestWsWebSocketContainer.java b/test/org/apache/tomcat/websocket/TestWsWebSocketContainer.java
index c49db4d..becd4d1 100644
--- a/test/org/apache/tomcat/websocket/TestWsWebSocketContainer.java
+++ b/test/org/apache/tomcat/websocket/TestWsWebSocketContainer.java
@@ -45,13 +45,11 @@ import javax.websocket.server.ServerEndpoint;
import javax.websocket.server.ServerEndpointConfig;
import org.junit.Assert;
-import org.junit.Assume;
import org.junit.Test;
import org.apache.catalina.Context;
import org.apache.catalina.servlets.DefaultServlet;
import org.apache.catalina.startup.Tomcat;
-import org.apache.coyote.http11.Http11Protocol;
import org.apache.tomcat.util.net.TesterSupport;
import org.apache.tomcat.websocket.TesterMessageCountClient.BasicBinary;
import org.apache.tomcat.websocket.TesterMessageCountClient.BasicHandler;
@@ -396,11 +394,6 @@ public class TestWsWebSocketContainer extends WebSocketBaseTest {
private void doTestWriteTimeoutServer(boolean setTimeoutOnContainer)
throws Exception {
- // This will never work for BIO
- Assume.assumeFalse(
- "Skipping test. This feature will never work for BIO connector.",
- getProtocol().equals(Http11Protocol.class.getName()));
-
/*
* Note: There are all sorts of horrible uses of statics in this test
* because the API uses classes and the tests really need access
@@ -567,15 +560,10 @@ public class TestWsWebSocketContainer extends WebSocketBaseTest {
session.getAsyncRemote().setSendTimeout(TIMEOUT_MS);
}
- // The close message is written with a blocking write. This is going
- // to fail so reduce the timeout from the default so the test
- // completes faster
- session.getUserProperties().put(
- WsRemoteEndpointImplBase.BLOCKING_SEND_TIMEOUT_PROPERTY, Long.valueOf(5000));
+ long lastSend = 0;
// Should send quickly until the network buffers fill up and then
// block until the timeout kicks in
- long lastSend = 0;
try {
while (true) {
lastSend = System.currentTimeMillis();
@@ -846,7 +834,7 @@ public class TestWsWebSocketContainer extends WebSocketBaseTest {
ClientEndpointConfig clientEndpointConfig =
ClientEndpointConfig.Builder.create().build();
clientEndpointConfig.getUserProperties().put(
- WsWebSocketContainer.SSL_TRUSTSTORE_PROPERTY,
+ org.apache.tomcat.websocket.Constants.SSL_TRUSTSTORE_PROPERTY,
"test/org/apache/tomcat/util/net/ca.jks");
Session wsSession = wsContainer.connectToServer(
TesterProgrammaticEndpoint.class,
diff --git a/test/org/apache/tomcat/websocket/TesterConnectionLimit.java b/test/org/apache/tomcat/websocket/TesterConnectionLimit.java
deleted file mode 100644
index bab5f76..0000000
--- a/test/org/apache/tomcat/websocket/TesterConnectionLimit.java
+++ /dev/null
@@ -1,108 +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.websocket;
-
-import java.io.IOException;
-import java.net.URI;
-import java.util.concurrent.atomic.AtomicInteger;
-
-import javax.websocket.ClientEndpointConfig.Builder;
-import javax.websocket.ContainerProvider;
-import javax.websocket.DeploymentException;
-import javax.websocket.WebSocketContainer;
-
-import org.junit.Test;
-
-import org.apache.catalina.Context;
-import org.apache.catalina.servlets.DefaultServlet;
-import org.apache.catalina.startup.Tomcat;
-import org.apache.catalina.startup.TomcatBaseTest;
-import org.apache.tomcat.websocket.TesterMessageCountClient.TesterProgrammaticEndpoint;
-
-
-public class TesterConnectionLimit extends TomcatBaseTest{
-
- /*
- * Simple test to see how many outgoing connections can be created on a
- * single machine.
- */
- @Test
- public void testSingleMachine() throws Exception {
- Tomcat tomcat = getTomcatInstance();
- // No file system docBase required
- Context ctx = tomcat.addContext("", null);
- ctx.addApplicationListener(TesterEchoServer.Config.class.getName());
- Tomcat.addServlet(ctx, "default", new DefaultServlet());
- ctx.addServletMappingDecoded("/", "default");
-
- tomcat.getConnector().setAttribute("maxConnections", "-1");
-
- tomcat.start();
-
- URI uri = new URI("ws://localhost:" + getPort() +
- TesterEchoServer.Config.PATH_ASYNC);
- AtomicInteger counter = new AtomicInteger(0);
-
- int threadCount = 50;
-
- Thread[] threads = new ConnectionThread[threadCount];
-
- for (int i = 0; i < threadCount; i++) {
- threads[i] = new ConnectionThread(counter, uri);
- threads[i].start();
- }
-
- // Wait for the threads to die
- for (Thread thread : threads) {
- thread.join();
- }
-
- System.out.println("Maximum connection count was " + counter.get());
- }
-
- private static class ConnectionThread extends Thread {
-
- private final AtomicInteger counter;
- private final URI uri;
-
- private ConnectionThread(AtomicInteger counter, URI uri) {
- this.counter = counter;
- this.uri = uri;
- }
-
- @Override
- public void run() {
- WebSocketContainer wsContainer =
- ContainerProvider.getWebSocketContainer();
-
- int count = 0;
-
- try {
- while (true) {
- wsContainer.connectToServer(TesterProgrammaticEndpoint.class,
- Builder.create().build(), uri);
- count = counter.incrementAndGet();
- if (count % 100 == 0) {
- System.out.println(count + " and counting...");
- }
- }
- } catch (IOException | DeploymentException ioe) {
- // Let thread die
- }
- }
- }
-}
diff --git a/test/org/apache/tomcat/websocket/TesterEchoServer.java b/test/org/apache/tomcat/websocket/TesterEchoServer.java
index 7f22e41..c671208 100644
--- a/test/org/apache/tomcat/websocket/TesterEchoServer.java
+++ b/test/org/apache/tomcat/websocket/TesterEchoServer.java
@@ -37,6 +37,7 @@ public class TesterEchoServer {
public static final String PATH_BASIC = "/echoBasic";
public static final String PATH_BASIC_LIMIT_LOW = "/echoBasicLimitLow";
public static final String PATH_BASIC_LIMIT_HIGH = "/echoBasicLimitHigh";
+ public static final String PATH_WRITER_ERROR = "/echoWriterError";
@Override
public void contextInitialized(ServletContextEvent sce) {
@@ -49,6 +50,7 @@ public class TesterEchoServer {
sc.addEndpoint(Basic.class);
sc.addEndpoint(BasicLimitLow.class);
sc.addEndpoint(BasicLimitHigh.class);
+ sc.addEndpoint(WriterError.class);
sc.addEndpoint(RootEcho.class);
} catch (DeploymentException e) {
throw new IllegalStateException(e);
@@ -56,6 +58,7 @@ public class TesterEchoServer {
}
}
+
@ServerEndpoint("/echoAsync")
public static class Async {
@@ -188,6 +191,26 @@ public class TesterEchoServer {
}
+ @ServerEndpoint("/echoWriterError")
+ public static class WriterError {
+
+ @OnMessage
+ public void echoTextMessage(Session session, @SuppressWarnings("unused") String msg) {
+ try {
+ session.getBasicRemote().getSendWriter();
+ // Simulate an error
+ throw new RuntimeException();
+ } catch (IOException e) {
+ // Should not happen
+ try {
+ session.close();
+ } catch (IOException e1) {
+ // Ignore
+ }
+ }
+ }
+ }
+
@ServerEndpoint("/")
public static class RootEcho {
diff --git a/test/org/apache/tomcat/websocket/TesterFirehoseServer.java b/test/org/apache/tomcat/websocket/TesterFirehoseServer.java
index a4f7fff..8b4b1ef 100644
--- a/test/org/apache/tomcat/websocket/TesterFirehoseServer.java
+++ b/test/org/apache/tomcat/websocket/TesterFirehoseServer.java
@@ -19,19 +19,15 @@ package org.apache.tomcat.websocket;
import java.io.IOException;
import java.util.concurrent.atomic.AtomicInteger;
-import javax.servlet.ServletContextEvent;
-import javax.websocket.DeploymentException;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.RemoteEndpoint.Basic;
import javax.websocket.Session;
-import javax.websocket.server.ServerContainer;
import javax.websocket.server.ServerEndpoint;
-import org.apache.tomcat.websocket.server.Constants;
-import org.apache.tomcat.websocket.server.WsContextListener;
+import org.apache.tomcat.websocket.server.TesterEndpointConfig;
/**
* Sends {@link #MESSAGE_COUNT} messages of size {@link #MESSAGE_SIZE} bytes as
@@ -42,7 +38,7 @@ public class TesterFirehoseServer {
public static final int MESSAGE_COUNT = 100000;
public static final String MESSAGE;
public static final int MESSAGE_SIZE = 1024;
- public static final int WAIT_TIME_MILLIS = 60000;
+ public static final int WAIT_TIME_MILLIS = 300000;
public static final int SEND_TIME_OUT_MILLIS = 5000;
static {
@@ -54,21 +50,13 @@ public class TesterFirehoseServer {
}
- public static class Config extends WsContextListener {
+ public static class Config extends TesterEndpointConfig {
public static final String PATH = "/firehose";
@Override
- public void contextInitialized(ServletContextEvent sce) {
- super.contextInitialized(sce);
- ServerContainer sc =
- (ServerContainer) sce.getServletContext().getAttribute(
- Constants.SERVER_CONTAINER_SERVLET_CONTEXT_ATTRIBUTE);
- try {
- sc.addEndpoint(Endpoint.class);
- } catch (DeploymentException e) {
- throw new IllegalStateException(e);
- }
+ protected Class<?> getEndpointClass() {
+ return Endpoint.class;
}
}
@@ -111,7 +99,7 @@ public class TesterFirehoseServer {
System.out.println("Received " + msg + ", now sending data");
session.getUserProperties().put(
- "org.apache.tomcat.websocket.BLOCKING_SEND_TIMEOUT",
+ org.apache.tomcat.websocket.Constants.BLOCKING_SEND_TIMEOUT_PROPERTY,
Long.valueOf(SEND_TIME_OUT_MILLIS));
Basic remote = session.getBasicRemote();
diff --git a/test/org/apache/tomcat/websocket/pojo/TestEncodingDecoding.java b/test/org/apache/tomcat/websocket/pojo/TestEncodingDecoding.java
index e4eebac..47f8cd1 100644
--- a/test/org/apache/tomcat/websocket/pojo/TestEncodingDecoding.java
+++ b/test/org/apache/tomcat/websocket/pojo/TestEncodingDecoding.java
@@ -39,6 +39,7 @@ import javax.websocket.Endpoint;
import javax.websocket.EndpointConfig;
import javax.websocket.Extension;
import javax.websocket.MessageHandler;
+import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.Session;
import javax.websocket.WebSocketContainer;
@@ -47,6 +48,7 @@ import javax.websocket.server.ServerEndpoint;
import javax.websocket.server.ServerEndpointConfig;
import org.junit.Assert;
+import org.junit.Ignore;
import org.junit.Test;
import org.apache.catalina.Context;
@@ -60,9 +62,12 @@ import org.apache.tomcat.websocket.server.WsContextListener;
public class TestEncodingDecoding extends TomcatBaseTest {
private static final String MESSAGE_ONE = "message-one";
+ private static final String MESSAGE_TWO = "message-two";
private static final String PATH_PROGRAMMATIC_EP = "/echoProgrammaticEP";
private static final String PATH_ANNOTATED_EP = "/echoAnnotatedEP";
private static final String PATH_GENERICS_EP = "/echoGenericsEP";
+ private static final String PATH_MESSAGES_EP = "/echoMessagesEP";
+ private static final String PATH_BATCHED_EP = "/echoBatchedEP";
@Test
@@ -92,8 +97,8 @@ public class TestEncodingDecoding extends TomcatBaseTest {
client.received.size() > 0) {
break;
}
- Thread.sleep(100);
i++;
+ Thread.sleep(100);
}
// Check messages were received
@@ -142,6 +147,7 @@ public class TestEncodingDecoding extends TomcatBaseTest {
if (server.received.size() > 0 && client.received.size() > 0) {
break;
}
+ i++;
Thread.sleep(100);
}
@@ -202,6 +208,7 @@ public class TestEncodingDecoding extends TomcatBaseTest {
if (server.received.size() > 0 && client.received.size() > 0) {
break;
}
+ i++;
Thread.sleep(100);
}
@@ -215,6 +222,101 @@ public class TestEncodingDecoding extends TomcatBaseTest {
session.close();
}
+ @Test
+ @Ignore // TODO Investigate why this test fails
+ public void testMessagesEndPoints() throws Exception {
+ // Set up utility classes
+ MessagesServer server = new MessagesServer();
+ SingletonConfigurator.setInstance(server);
+ ServerConfigListener.setPojoClazz(MessagesServer.class);
+
+ Tomcat tomcat = getTomcatInstance();
+ // No file system docBase required
+ Context ctx = tomcat.addContext("", null);
+ ctx.addApplicationListener(ServerConfigListener.class.getName());
+ Tomcat.addServlet(ctx, "default", new DefaultServlet());
+ ctx.addServletMappingDecoded("/", "default");
+
+ WebSocketContainer wsContainer =
+ ContainerProvider.getWebSocketContainer();
+
+ tomcat.start();
+
+ StringClient client = new StringClient();
+ URI uri = new URI("ws://localhost:" + getPort() + PATH_MESSAGES_EP);
+ Session session = wsContainer.connectToServer(client, uri);
+
+ session.getBasicRemote().sendText(MESSAGE_ONE);
+
+ // Should not take very long
+ int i = 0;
+ while (i < 20) {
+ if (server.received.size() > 0 && client.received.size() > 0) {
+ break;
+ }
+ i++;
+ Thread.sleep(100);
+ }
+
+ // Check messages were received
+ Assert.assertEquals(1, server.received.size());
+ Assert.assertEquals(1, client.received.size());
+
+ // Check correct messages were received
+ Assert.assertEquals(MESSAGE_ONE, server.received.peek());
+ session.close();
+
+ Assert.assertNull(server.t);
+ }
+
+
+ @Test
+ @Ignore // TODO Investigate why this test fails
+ public void testBatchedEndPoints() throws Exception {
+ // Set up utility classes
+ BatchedServer server = new BatchedServer();
+ SingletonConfigurator.setInstance(server);
+ ServerConfigListener.setPojoClazz(BatchedServer.class);
+
+ Tomcat tomcat = getTomcatInstance();
+ // No file system docBase required
+ Context ctx = tomcat.addContext("", null);
+ ctx.addApplicationListener(ServerConfigListener.class.getName());
+ Tomcat.addServlet(ctx, "default", new DefaultServlet());
+ ctx.addServletMappingDecoded("/", "default");
+
+ WebSocketContainer wsContainer =
+ ContainerProvider.getWebSocketContainer();
+
+ tomcat.start();
+
+ StringClient client = new StringClient();
+ URI uri = new URI("ws://localhost:" + getPort() + PATH_BATCHED_EP);
+ Session session = wsContainer.connectToServer(client, uri);
+
+ session.getBasicRemote().sendText(MESSAGE_ONE);
+
+ // Should not take very long
+ int i = 0;
+ while (i++ < 20) {
+ if (server.received.size() > 0 && client.received.size() > 0) {
+ break;
+ }
+ i++;
+ Thread.sleep(100);
+ }
+
+ // Check messages were received
+ Assert.assertEquals(1, server.received.size());
+ Assert.assertEquals(2, client.received.size());
+
+ // Check correct messages were received
+ Assert.assertEquals(MESSAGE_ONE, server.received.peek());
+ session.close();
+
+ Assert.assertNull(server.t);
+ }
+
private int testEvent(String name, int count) throws InterruptedException {
int i = count;
@@ -260,6 +362,19 @@ public class TestEncodingDecoding extends TomcatBaseTest {
}
+ @ClientEndpoint
+ public static class StringClient {
+
+ private Queue<Object> received = new ConcurrentLinkedQueue<>();
+
+ @OnMessage
+ public void rx(String in) {
+ received.add(in);
+ }
+
+ }
+
+
@ServerEndpoint(value=PATH_GENERICS_EP,
decoders={ListStringDecoder.class},
encoders={ListStringEncoder.class},
@@ -278,6 +393,50 @@ public class TestEncodingDecoding extends TomcatBaseTest {
}
+ @ServerEndpoint(value=PATH_MESSAGES_EP,
+ configurator=SingletonConfigurator.class)
+ public static class MessagesServer {
+
+ private final Queue<String> received = new ConcurrentLinkedQueue<>();
+ private volatile Throwable t = null;
+
+ @OnMessage
+ public String onMessage(String message, Session session) {
+ received.add(message);
+ session.getAsyncRemote().sendText(MESSAGE_ONE);
+ return message;
+ }
+
+ @OnError
+ public void onError(@SuppressWarnings("unused") Session session, Throwable t) {
+ t.printStackTrace();
+ this.t = t;
+ }
+ }
+
+
+ @ServerEndpoint(value=PATH_BATCHED_EP,
+ configurator=SingletonConfigurator.class)
+ public static class BatchedServer {
+
+ private final Queue<String> received = new ConcurrentLinkedQueue<>();
+ private volatile Throwable t = null;
+
+ @OnMessage
+ public String onMessage(String message, Session session) throws IOException {
+ received.add(message);
+ session.getAsyncRemote().setBatchingAllowed(true);
+ session.getAsyncRemote().sendText(MESSAGE_ONE);
+ return MESSAGE_TWO;
+ }
+
+ @OnError
+ public void onError(@SuppressWarnings("unused") Session session, Throwable t) {
+ t.printStackTrace();
+ this.t = t;
+ }
+ }
+
@ServerEndpoint(value=PATH_ANNOTATED_EP,
decoders={MsgStringDecoder.class, MsgByteDecoder.class},
encoders={MsgStringEncoder.class, MsgByteEncoder.class},
@@ -315,7 +474,7 @@ public class TestEncodingDecoding extends TomcatBaseTest {
public static class MsgByteMessageHandler implements
MessageHandler.Whole<MsgByte> {
- public static Queue<Object> received = new ConcurrentLinkedQueue<>();
+ public static final Queue<Object> received = new ConcurrentLinkedQueue<>();
private final Session session;
public MsgByteMessageHandler(Session session) {
@@ -339,7 +498,7 @@ public class TestEncodingDecoding extends TomcatBaseTest {
public static class MsgStringMessageHandler implements MessageHandler.Whole<MsgString> {
- public static Queue<Object> received = new ConcurrentLinkedQueue<>();
+ public static final Queue<Object> received = new ConcurrentLinkedQueue<>();
private final Session session;
public MsgStringMessageHandler(Session session) {
diff --git a/test/org/apache/tomcat/websocket/pojo/TesterUtil.java b/test/org/apache/tomcat/websocket/pojo/TesterUtil.java
index 21c11bb..d3611a0 100644
--- a/test/org/apache/tomcat/websocket/pojo/TesterUtil.java
+++ b/test/org/apache/tomcat/websocket/pojo/TesterUtil.java
@@ -16,18 +16,14 @@
*/
package org.apache.tomcat.websocket.pojo;
-import javax.servlet.ServletContextEvent;
import javax.websocket.ClientEndpoint;
-import javax.websocket.DeploymentException;
-import javax.websocket.server.ServerContainer;
import javax.websocket.server.ServerEndpointConfig.Configurator;
-import org.apache.tomcat.websocket.server.Constants;
-import org.apache.tomcat.websocket.server.WsContextListener;
+import org.apache.tomcat.websocket.server.TesterEndpointConfig;
public class TesterUtil {
- public static class ServerConfigListener extends WsContextListener {
+ public static class ServerConfigListener extends TesterEndpointConfig {
private static Class<?> pojoClazz;
@@ -35,17 +31,10 @@ public class TesterUtil {
ServerConfigListener.pojoClazz = pojoClazz;
}
+
@Override
- public void contextInitialized(ServletContextEvent sce) {
- super.contextInitialized(sce);
- ServerContainer sc =
- (ServerContainer) sce.getServletContext().getAttribute(
- Constants.SERVER_CONTAINER_SERVLET_CONTEXT_ATTRIBUTE);
- try {
- sc.addEndpoint(pojoClazz);
- } catch (DeploymentException e) {
- throw new IllegalStateException(e);
- }
+ protected Class<?> getEndpointClass() {
+ return pojoClazz;
}
}
diff --git a/test/org/apache/tomcat/websocket/server/TestClassLoader.java b/test/org/apache/tomcat/websocket/server/TestClassLoader.java
new file mode 100644
index 0000000..b182ef6
--- /dev/null
+++ b/test/org/apache/tomcat/websocket/server/TestClassLoader.java
@@ -0,0 +1,151 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tomcat.websocket.server;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import javax.websocket.ClientEndpoint;
+import javax.websocket.ContainerProvider;
+import javax.websocket.OnMessage;
+import javax.websocket.OnOpen;
+import javax.websocket.Session;
+import javax.websocket.WebSocketContainer;
+import javax.websocket.server.ServerEndpoint;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import org.apache.catalina.Context;
+import org.apache.catalina.loader.WebappClassLoaderBase;
+import org.apache.catalina.servlets.DefaultServlet;
+import org.apache.catalina.startup.Tomcat;
+import org.apache.tomcat.websocket.WebSocketBaseTest;
+
+/**
+ * Tests endpoint methods are called with the correct class loader.
+ */
+public class TestClassLoader extends WebSocketBaseTest {
+
+ private static final String PASS = "PASS";
+ private static final String FAIL = "FAIL";
+
+
+ /*
+ * Checks class loader for the server endpoint during onOpen and onMessage
+ */
+ @Test
+ public void testSimple() throws Exception {
+ Tomcat tomcat = getTomcatInstance();
+ // No file system docBase required
+ Context ctx = tomcat.addContext("", null);
+ ctx.addApplicationListener(Config.class.getName());
+
+ Tomcat.addServlet(ctx, "default", new DefaultServlet());
+ ctx.addServletMappingDecoded("/", "default");
+
+ tomcat.start();
+
+ WebSocketContainer wsContainer = ContainerProvider.getWebSocketContainer();
+
+ Client client = new Client();
+
+ Session wsSession = wsContainer.connectToServer(client,
+ new URI("ws://localhost:" + getPort() + "/test"));
+
+ Assert.assertTrue(wsSession.isOpen());
+
+ // Wait up to 5s for a message
+ int count = 0;
+ while (count < 50 && client.getMsgCount() < 1) {
+ Thread.sleep(100);
+ }
+
+ // Check it
+ Assert.assertEquals(1, client.getMsgCount());
+ Assert.assertFalse(client.hasFailed());
+
+ wsSession.getBasicRemote().sendText("Testing");
+
+ // Wait up to 5s for a message
+ count = 0;
+ while (count < 50 && client.getMsgCount() < 2) {
+ Thread.sleep(100);
+ }
+
+ Assert.assertEquals(2, client.getMsgCount());
+ Assert.assertFalse(client.hasFailed());
+
+ wsSession.close();
+ }
+
+ @ClientEndpoint
+ public static class Client {
+
+ private final AtomicInteger msgCount = new AtomicInteger(0);
+ private boolean failed = false;
+
+ public boolean hasFailed() {
+ return failed;
+ }
+
+ public int getMsgCount() {
+ return msgCount.get();
+ }
+
+ @OnMessage
+ public void onMessage(String msg) {
+ if (!failed && !PASS.equals(msg)) {
+ failed = true;
+ }
+ msgCount.incrementAndGet();
+ }
+ }
+
+
+ @ServerEndpoint("/test")
+ public static class ClassLoaderEndpoint {
+
+ @OnOpen
+ public void onOpen(Session session) throws IOException {
+ if (Thread.currentThread().getContextClassLoader() instanceof WebappClassLoaderBase) {
+ session.getBasicRemote().sendText(PASS);
+ } else {
+ session.getBasicRemote().sendText(FAIL);
+ }
+ }
+
+ @OnMessage
+ public String onMessage(@SuppressWarnings("unused") String msg) {
+ if (Thread.currentThread().getContextClassLoader() instanceof WebappClassLoaderBase) {
+ return PASS;
+ } else {
+ return FAIL;
+ }
+ }
+ }
+
+ public static class Config extends TesterEndpointConfig {
+
+ @Override
+ protected Class<?> getEndpointClass() {
+ return ClassLoaderEndpoint.class;
+ }
+
+ }
+}
diff --git a/test/org/apache/tomcat/websocket/server/TestClose.java b/test/org/apache/tomcat/websocket/server/TestClose.java
index 7750824..97ce4c9 100644
--- a/test/org/apache/tomcat/websocket/server/TestClose.java
+++ b/test/org/apache/tomcat/websocket/server/TestClose.java
@@ -22,20 +22,18 @@ import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
-import javax.servlet.ServletContextEvent;
import javax.websocket.CloseReason;
import javax.websocket.CloseReason.CloseCode;
import javax.websocket.CloseReason.CloseCodes;
-import javax.websocket.DeploymentException;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
-import javax.websocket.server.ServerContainer;
import javax.websocket.server.ServerEndpointConfig;
import org.junit.Assert;
+import org.junit.Assume;
import org.junit.Before;
//import org.junit.Ignore;
import org.junit.Test;
@@ -81,7 +79,7 @@ public class TestClose extends WebSocketBaseTest {
private static void awaitLatch(CountDownLatch latch, String failMessage) {
try {
- if (!latch.await(30000, TimeUnit.MILLISECONDS)) {
+ if (!latch.await(5000, TimeUnit.MILLISECONDS)) {
Assert.fail(failMessage);
}
} catch (InterruptedException e) {
@@ -124,6 +122,10 @@ public class TestClose extends WebSocketBaseTest {
@Test
public void testTcpClose() throws Exception {
+ // TODO
+ Assume.assumeFalse("This test currently fails for APR",
+ getTomcatInstance().getConnector().getProtocolHandlerClassName().contains("Apr"));
+
startServer(TestEndpointConfig.class);
TesterWsCloseClient client = new TesterWsCloseClient("localhost", getPort());
@@ -183,6 +185,10 @@ public class TestClose extends WebSocketBaseTest {
@Test
public void testTcpCloseInOnMessage() throws Exception {
+ // TODO
+ Assume.assumeFalse("This test currently fails for APR",
+ getTomcatInstance().getConnector().getProtocolHandlerClassName().contains("Apr"));
+
startServer(TestEndpointConfig.class);
TesterWsCloseClient client = new TesterWsCloseClient("localhost", getPort());
@@ -243,8 +249,7 @@ public class TestClose extends WebSocketBaseTest {
client.closeSocket();
events.onMessageWait.countDown();
- // BIO will see close from client before it sees the TCP close
- awaitOnClose(CloseCodes.CLOSED_ABNORMALLY, CloseCodes.NORMAL_CLOSURE);
+ awaitOnClose(CloseCodes.CLOSED_ABNORMALLY);
}
@@ -270,9 +275,7 @@ public class TestClose extends WebSocketBaseTest {
public static class TestEndpoint {
@OnOpen
- public void onOpen(Session session) {
- session.getUserProperties().put(
- "org.apache.tomcat.websocket.BLOCKING_SEND_TIMEOUT", Long.valueOf(3000));
+ public void onOpen() {
log.info("Session opened");
}
@@ -294,7 +297,7 @@ public class TestClose extends WebSocketBaseTest {
// triggers an error here.
while (count < 10) {
count++;
- session.getBasicRemote().sendText("Test reply");
+ session.getBasicRemote().sendText("Test reply");
Thread.sleep(500);
}
} catch (IOException | InterruptedException e) {
@@ -345,29 +348,13 @@ public class TestClose extends WebSocketBaseTest {
}
- public abstract static class BaseEndpointConfig extends WsContextListener {
+ public abstract static class BaseEndpointConfig extends TesterEndpointConfig {
public static final String PATH = "/test";
- protected abstract Class<?> getEndpointClass();
-
@Override
- public void contextInitialized(ServletContextEvent sce) {
- super.contextInitialized(sce);
-
- ServerContainer sc = (ServerContainer) sce
- .getServletContext()
- .getAttribute(
- Constants.SERVER_CONTAINER_SERVLET_CONTEXT_ATTRIBUTE);
-
- ServerEndpointConfig sec = ServerEndpointConfig.Builder.create(
- getEndpointClass(), PATH).build();
-
- try {
- sc.addEndpoint(sec);
- } catch (DeploymentException e) {
- throw new RuntimeException(e);
- }
+ protected ServerEndpointConfig getServerEndpointConfig() {
+ return ServerEndpointConfig.Builder.create(getEndpointClass(), PATH).build();
}
}
}
diff --git a/test/org/apache/tomcat/websocket/server/TestShutdown.java b/test/org/apache/tomcat/websocket/server/TestShutdown.java
new file mode 100644
index 0000000..6cd7f18
--- /dev/null
+++ b/test/org/apache/tomcat/websocket/server/TestShutdown.java
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tomcat.websocket.server;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicLong;
+
+import javax.websocket.ClientEndpointConfig;
+import javax.websocket.ContainerProvider;
+import javax.websocket.EndpointConfig;
+import javax.websocket.OnMessage;
+import javax.websocket.OnOpen;
+import javax.websocket.Session;
+import javax.websocket.WebSocketContainer;
+import javax.websocket.server.ServerEndpoint;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import org.apache.catalina.Context;
+import org.apache.catalina.servlets.DefaultServlet;
+import org.apache.catalina.startup.Tomcat;
+import org.apache.tomcat.websocket.TesterMessageCountClient.BasicText;
+import org.apache.tomcat.websocket.TesterMessageCountClient.TesterProgrammaticEndpoint;
+import org.apache.tomcat.websocket.WebSocketBaseTest;
+
+/**
+ * Tests inspired by https://bz.apache.org/bugzilla/show_bug.cgi?id=58835 to
+ * check that WebSocket connections are closed gracefully on Tomcat shutdown.
+ */
+public class TestShutdown extends WebSocketBaseTest {
+
+ @Test
+ public void testShutdownBufferedMessages() throws Exception {
+ Tomcat tomcat = getTomcatInstance();
+ // No file system docBase required
+ Context ctx = tomcat.addContext("", null);
+ ctx.addApplicationListener(EchoBufferedConfig.class.getName());
+ Tomcat.addServlet(ctx, "default", new DefaultServlet());
+ ctx.addServletMappingDecoded("/", "default");
+
+ tomcat.start();
+
+ WebSocketContainer wsContainer = ContainerProvider.getWebSocketContainer();
+ ClientEndpointConfig clientEndpointConfig = ClientEndpointConfig.Builder.create().build();
+ Session wsSession = wsContainer.connectToServer(
+ TesterProgrammaticEndpoint.class,
+ clientEndpointConfig,
+ new URI("ws://localhost:" + getPort() + "/test"));
+ CountDownLatch latch = new CountDownLatch(1);
+ BasicText handler = new BasicText(latch);
+ wsSession.addMessageHandler(handler);
+ wsSession.getBasicRemote().sendText("Hello");
+
+ int count = 0;
+ while (count < 10 && EchoBufferedEndpoint.messageCount.get() == 0) {
+ Thread.sleep(200);
+ count++;
+ }
+ Assert.assertNotEquals("Message not received by server",
+ EchoBufferedEndpoint.messageCount.get(), 0);
+
+ tomcat.stop();
+
+ Assert.assertTrue("Latch expired waiting for message", latch.await(10, TimeUnit.SECONDS));
+ }
+
+ public static class EchoBufferedConfig extends TesterEndpointConfig {
+
+ @Override
+ protected Class<?> getEndpointClass() {
+ return EchoBufferedEndpoint.class;
+ }
+
+ }
+
+ @ServerEndpoint("/test")
+ public static class EchoBufferedEndpoint {
+
+ private static AtomicLong messageCount = new AtomicLong(0);
+
+ @OnOpen
+ public void onOpen(Session session, @SuppressWarnings("unused") EndpointConfig epc)
+ throws IOException {
+ session.getAsyncRemote().setBatchingAllowed(true);
+ }
+
+ @OnMessage
+ public void onMessage(Session session, String msg) throws IOException {
+ messageCount.incrementAndGet();
+ session.getBasicRemote().sendText(msg);
+ }
+ }
+}
diff --git a/test/org/apache/tomcat/websocket/server/TestWsRemoteEndpointImplServer.java b/test/org/apache/tomcat/websocket/server/TestWsRemoteEndpointImplServer.java
index 32a2817..77987ac 100644
--- a/test/org/apache/tomcat/websocket/server/TestWsRemoteEndpointImplServer.java
+++ b/test/org/apache/tomcat/websocket/server/TestWsRemoteEndpointImplServer.java
@@ -23,10 +23,8 @@ import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
-import javax.servlet.ServletContextEvent;
import javax.websocket.CloseReason;
import javax.websocket.ContainerProvider;
-import javax.websocket.DeploymentException;
import javax.websocket.EncodeException;
import javax.websocket.Encoder;
import javax.websocket.EndpointConfig;
@@ -36,7 +34,6 @@ import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.WebSocketContainer;
-import javax.websocket.server.ServerContainer;
import javax.websocket.server.ServerEndpointConfig;
import org.junit.Ignore;
@@ -91,26 +88,17 @@ public class TestWsRemoteEndpointImplServer extends WebSocketBaseTest {
session.close();
}
- public static class Bug58624Config extends WsContextListener {
+ public static class Bug58624Config extends TesterEndpointConfig {
- public static String PATH = "/bug58624";
- @Override
- public void contextInitialized(ServletContextEvent sce) {
- super.contextInitialized(sce);
+ public static final String PATH = "/bug58624";
- ServerContainer sc = (ServerContainer) sce.getServletContext().getAttribute(
- Constants.SERVER_CONTAINER_SERVLET_CONTEXT_ATTRIBUTE);
+ @Override
+ protected ServerEndpointConfig getServerEndpointConfig() {
List<Class<? extends Encoder>> encoders = new ArrayList<>();
encoders.add(Bug58624Encoder.class);
- ServerEndpointConfig sec = ServerEndpointConfig.Builder.create(
+ return ServerEndpointConfig.Builder.create(
Bug58624Endpoint.class, PATH).encoders(encoders).build();
-
- try {
- sc.addEndpoint(sec);
- } catch (DeploymentException e) {
- throw new RuntimeException(e);
- }
}
}
@@ -120,6 +108,10 @@ public class TestWsRemoteEndpointImplServer extends WebSocketBaseTest {
@OnOpen
public void onOpen(Session session) {
+ // Disabling blocking timeouts for this test
+ session.getUserProperties().put(
+ org.apache.tomcat.websocket.Constants.BLOCKING_SEND_TIMEOUT_PROPERTY,
+ Long.valueOf(-1));
ex.submit(new Bug58624SendMessage(session));
}
diff --git a/test/org/apache/tomcat/websocket/server/TestWsServerContainer.java b/test/org/apache/tomcat/websocket/server/TestWsServerContainer.java
index c786041..08acf45 100644
--- a/test/org/apache/tomcat/websocket/server/TestWsServerContainer.java
+++ b/test/org/apache/tomcat/websocket/server/TestWsServerContainer.java
@@ -21,12 +21,9 @@ import java.util.Queue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
-import javax.servlet.ServletContextEvent;
import javax.websocket.ContainerProvider;
-import javax.websocket.DeploymentException;
import javax.websocket.Session;
import javax.websocket.WebSocketContainer;
-import javax.websocket.server.ServerContainer;
import javax.websocket.server.ServerEndpointConfig;
import org.junit.Assert;
@@ -97,24 +94,12 @@ public class TestWsServerContainer extends WebSocketBaseTest {
}
- public static class Bug54807Config extends WsContextListener {
+ public static class Bug54807Config extends TesterEndpointConfig {
@Override
- public void contextInitialized(ServletContextEvent sce) {
- super.contextInitialized(sce);
-
- ServerContainer sc =
- (ServerContainer) sce.getServletContext().getAttribute(
- Constants.SERVER_CONTAINER_SERVLET_CONTEXT_ATTRIBUTE);
-
- ServerEndpointConfig sec = ServerEndpointConfig.Builder.create(
+ protected ServerEndpointConfig getServerEndpointConfig() {
+ return ServerEndpointConfig.Builder.create(
TesterEchoServer.Basic.class, "/{param}").build();
-
- try {
- sc.addEndpoint(sec);
- } catch (DeploymentException e) {
- throw new RuntimeException(e);
- }
}
}
diff --git a/test/org/apache/tomcat/websocket/server/TesterEndpointConfig.java b/test/org/apache/tomcat/websocket/server/TesterEndpointConfig.java
new file mode 100644
index 0000000..ccecf9d
--- /dev/null
+++ b/test/org/apache/tomcat/websocket/server/TesterEndpointConfig.java
@@ -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 org.apache.tomcat.websocket.server;
+
+import javax.servlet.ServletContextEvent;
+import javax.websocket.DeploymentException;
+import javax.websocket.server.ServerContainer;
+import javax.websocket.server.ServerEndpointConfig;
+
+public abstract class TesterEndpointConfig extends WsContextListener {
+
+ @Override
+ public void contextInitialized(ServletContextEvent sce) {
+ super.contextInitialized(sce);
+
+ ServerContainer sc = (ServerContainer) sce.getServletContext().getAttribute(
+ Constants.SERVER_CONTAINER_SERVLET_CONTEXT_ATTRIBUTE);
+
+ try {
+ ServerEndpointConfig sec = getServerEndpointConfig();
+ if (sec == null) {
+ sc.addEndpoint(getEndpointClass());
+ } else {
+ sc.addEndpoint(sec);
+ }
+ } catch (DeploymentException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+
+ protected Class<?> getEndpointClass() {
+ return null;
+ }
+
+
+ protected ServerEndpointConfig getServerEndpointConfig() {
+ return null;
+ }
+}
diff --git a/test/org/apache/tomcat/websocket/server/TesterWsCloseClient.java b/test/org/apache/tomcat/websocket/server/TesterWsCloseClient.java
index c31e08f..f55685c 100644
--- a/test/org/apache/tomcat/websocket/server/TesterWsCloseClient.java
+++ b/test/org/apache/tomcat/websocket/server/TesterWsCloseClient.java
@@ -66,8 +66,9 @@ public class TesterWsCloseClient {
private void readUpgradeResponse() throws IOException {
BufferedReader in = new BufferedReader(new InputStreamReader(
socket.getInputStream()));
- while (!in.readLine().isEmpty()) {
-
+ String line = in.readLine();
+ while (line != null && !line.isEmpty()) {
+ line = in.readLine();
}
}
@@ -78,7 +79,7 @@ public class TesterWsCloseClient {
socket.close();
}
- /**
+ /*
* Send a TCP RST instead of a TCP closing handshake
*/
public void forceCloseSocket() throws IOException {
diff --git a/test/webapp-2.2/WEB-INF/web.xml b/test/webapp-2.2/WEB-INF/web.xml
index cfcaccd..de11c94 100644
--- a/test/webapp-2.2/WEB-INF/web.xml
+++ b/test/webapp-2.2/WEB-INF/web.xml
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="ISO-8859-1"?>
+<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
diff --git a/test/webapp-2.3/WEB-INF/web.xml b/test/webapp-2.3/WEB-INF/web.xml
index d385917..7c6a953 100644
--- a/test/webapp-2.3/WEB-INF/web.xml
+++ b/test/webapp-2.3/WEB-INF/web.xml
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="ISO-8859-1"?>
+<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
diff --git a/test/webapp-2.4/WEB-INF/web.xml b/test/webapp-2.4/WEB-INF/web.xml
index 9d5cff6..6020a81 100644
--- a/test/webapp-2.4/WEB-INF/web.xml
+++ b/test/webapp-2.4/WEB-INF/web.xml
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="ISO-8859-1"?>
+<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
diff --git a/test/webapp-2.5/WEB-INF/web.xml b/test/webapp-2.5/WEB-INF/web.xml
index 6a3b374..53612ed 100644
--- a/test/webapp-2.5/WEB-INF/web.xml
+++ b/test/webapp-2.5/WEB-INF/web.xml
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="ISO-8859-1"?>
+<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
diff --git a/test/webapp-3.0/WEB-INF/web.xml b/test/webapp-3.0/WEB-INF/web.xml
index 2a69891..3cab0cf 100644
--- a/test/webapp-3.0/WEB-INF/web.xml
+++ b/test/webapp-3.0/WEB-INF/web.xml
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="ISO-8859-1"?>
+<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
diff --git a/test/webapp-3.1/WEB-INF/web.xml b/test/webapp-3.1/WEB-INF/web.xml
index 1958586..426f51f 100644
--- a/test/webapp-3.1/WEB-INF/web.xml
+++ b/test/webapp-3.1/WEB-INF/web.xml
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="ISO-8859-1"?>
+<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
diff --git a/test/webapp-fragments-empty-absolute-ordering/WEB-INF/web.xml b/test/webapp-fragments-empty-absolute-ordering/WEB-INF/web.xml
index ce466c7..ab025a4 100644
--- a/test/webapp-fragments-empty-absolute-ordering/WEB-INF/web.xml
+++ b/test/webapp-fragments-empty-absolute-ordering/WEB-INF/web.xml
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="ISO-8859-1"?>
+<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
diff --git a/test/webapp-fragments/WEB-INF/web.xml b/test/webapp-fragments/WEB-INF/web.xml
index 6f96c83..bdf41cd 100644
--- a/test/webapp-fragments/WEB-INF/web.xml
+++ b/test/webapp-fragments/WEB-INF/web.xml
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="ISO-8859-1"?>
+<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
diff --git a/test/webapp-servletsecurity/WEB-INF/web.xml b/test/webapp-servletsecurity/WEB-INF/web.xml
index fea216c..c41f52a 100644
--- a/test/webapp-servletsecurity/WEB-INF/web.xml
+++ b/test/webapp-servletsecurity/WEB-INF/web.xml
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="ISO-8859-1"?>
+<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
diff --git a/test/webapp-servletsecurity2/WEB-INF/web.xml b/test/webapp-servletsecurity2/WEB-INF/web.xml
index 47bf7e7..447fc9c 100644
--- a/test/webapp-servletsecurity2/WEB-INF/web.xml
+++ b/test/webapp-servletsecurity2/WEB-INF/web.xml
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="ISO-8859-1"?>
+<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
diff --git a/test/webapp-virtual-webapp/src/main/webapp/WEB-INF/web.xml b/test/webapp-virtual-webapp/src/main/webapp/WEB-INF/web.xml
index 6eb9546..7177522 100644
--- a/test/webapp-virtual-webapp/src/main/webapp/WEB-INF/web.xml
+++ b/test/webapp-virtual-webapp/src/main/webapp/WEB-INF/web.xml
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="ISO-8859-1"?>
+<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
diff --git a/test/webapp/WEB-INF/bug53545.tld b/test/webapp/WEB-INF/bug53545.tld
index 1ef23c3..41aab87 100644
--- a/test/webapp/WEB-INF/bug53545.tld
+++ b/test/webapp/WEB-INF/bug53545.tld
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="utf-8" ?>
+<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
diff --git a/test/webapp/WEB-INF/classes/META-INF/org.apache.jasper/tagPlugins.xml b/test/webapp/WEB-INF/classes/META-INF/org.apache.jasper/tagPlugins.xml
index 7ba905f..36dcf7d 100644
--- a/test/webapp/WEB-INF/classes/META-INF/org.apache.jasper/tagPlugins.xml
+++ b/test/webapp/WEB-INF/classes/META-INF/org.apache.jasper/tagPlugins.xml
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="ISO-8859-1"?>
+<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
diff --git a/test/webapp/WEB-INF/classes/org/apache/tomcat/Bug58096.class b/test/webapp/WEB-INF/classes/org/apache/tomcat/Bug58096.class
index f40991b..9e0c4d8 100644
Binary files a/test/webapp/WEB-INF/classes/org/apache/tomcat/Bug58096.class and b/test/webapp/WEB-INF/classes/org/apache/tomcat/Bug58096.class differ
diff --git a/test/webapp/WEB-INF/classes/org/apache/tomcat/Bug58096.java b/test/webapp/WEB-INF/classes/org/apache/tomcat/Bug58096.java
index ab0e763..c01a305 100644
--- a/test/webapp/WEB-INF/classes/org/apache/tomcat/Bug58096.java
+++ b/test/webapp/WEB-INF/classes/org/apache/tomcat/Bug58096.java
@@ -17,7 +17,7 @@
package org.apache.tomcat;
/**
- * Contains no functionality since it is only used tp test the code source
+ * Contains no functionality since it is only used to test the code source
* assigned to the class.
*/
public class Bug58096 {
diff --git a/test/webapp/WEB-INF/tags/bug48668.tagx b/test/webapp/WEB-INF/tags/bug48668.tagx
index db8587a..9e56e02 100644
--- a/test/webapp/WEB-INF/tags/bug48668.tagx
+++ b/test/webapp/WEB-INF/tags/bug48668.tagx
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="ISO-8859-1"?>
+<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
diff --git a/test/webapp/WEB-INF/tags/bug55198.tagx b/test/webapp/WEB-INF/tags/bug55198.tagx
index 01b042b..8d2545f 100644
--- a/test/webapp/WEB-INF/tags/bug55198.tagx
+++ b/test/webapp/WEB-INF/tags/bug55198.tagx
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="ISO-8859-1"?>
+<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
diff --git a/test/webapp/WEB-INF/tags/bug56265.tagx b/test/webapp/WEB-INF/tags/bug56265.tagx
index 22954e3..5a76883 100644
--- a/test/webapp/WEB-INF/tags/bug56265.tagx
+++ b/test/webapp/WEB-INF/tags/bug56265.tagx
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="UTF-8" ?>
+<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
diff --git a/test/webapp/WEB-INF/web.xml b/test/webapp/WEB-INF/web.xml
index 928c961..f29d270 100644
--- a/test/webapp/WEB-INF/web.xml
+++ b/test/webapp/WEB-INF/web.xml
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="ISO-8859-1"?>
+<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
diff --git a/test/webapp/bug5nnnn/bug54801a.jspx b/test/webapp/bug5nnnn/bug54801a.jspx
index b1726c5..bfd6842 100644
--- a/test/webapp/bug5nnnn/bug54801a.jspx
+++ b/test/webapp/bug5nnnn/bug54801a.jspx
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="utf-8" ?>
+<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
diff --git a/test/webapp/bug5nnnn/bug54801b.jspx b/test/webapp/bug5nnnn/bug54801b.jspx
index 23d7f5e..4187cc7 100644
--- a/test/webapp/bug5nnnn/bug54801b.jspx
+++ b/test/webapp/bug5nnnn/bug54801b.jspx
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="utf-8" ?>
+<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
diff --git a/test/webapp/bug5nnnn/bug54821a.jspx b/test/webapp/bug5nnnn/bug54821a.jspx
index 9537cbd..16b8b82 100644
--- a/test/webapp/bug5nnnn/bug54821a.jspx
+++ b/test/webapp/bug5nnnn/bug54821a.jspx
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="utf-8" ?>
+<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
diff --git a/test/webapp/bug5nnnn/bug54821b.jspx b/test/webapp/bug5nnnn/bug54821b.jspx
index b0f5463..266ae5f 100644
--- a/test/webapp/bug5nnnn/bug54821b.jspx
+++ b/test/webapp/bug5nnnn/bug54821b.jspx
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="utf-8" ?>
+<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
diff --git a/test/webapp/bug5nnnn/bug56029.jspx b/test/webapp/bug5nnnn/bug56029.jspx
index f2d6d04..84f9e3a 100644
--- a/test/webapp/bug5nnnn/bug56029.jspx
+++ b/test/webapp/bug5nnnn/bug56029.jspx
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="UTF-8" ?>
+<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
diff --git a/test/webapp/bug5nnnn/bug56334and56561.jspx b/test/webapp/bug5nnnn/bug56334and56561.jspx
index a80e5d3..12c0694 100644
--- a/test/webapp/bug5nnnn/bug56334and56561.jspx
+++ b/test/webapp/bug5nnnn/bug56334and56561.jspx
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="utf-8" ?>
+<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
diff --git a/test/webapp/index.html b/test/webapp/index.html
index 2bf0cef..392fa41 100644
--- a/test/webapp/index.html
+++ b/test/webapp/index.html
@@ -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.
+-->
<html>
<head>
<title>Index page</title>
diff --git a/test/webapp/index.html.br b/test/webapp/index.html.br
new file mode 100644
index 0000000..1428b9c
Binary files /dev/null and b/test/webapp/index.html.br differ
diff --git a/test/webapp/index.html.gz b/test/webapp/index.html.gz
index c80f8bb..5aca6e9 100644
Binary files a/test/webapp/index.html.gz and b/test/webapp/index.html.gz differ
diff --git a/webapps/ROOT/WEB-INF/web.xml b/webapps/ROOT/WEB-INF/web.xml
index 55fc211..adb10ff 100644
--- a/webapps/ROOT/WEB-INF/web.xml
+++ b/webapps/ROOT/WEB-INF/web.xml
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="ISO-8859-1"?>
+<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
diff --git a/webapps/docs/aio.xml b/webapps/docs/aio.xml
index 26aa47c..3ea4836 100644
--- a/webapps/docs/aio.xml
+++ b/webapps/docs/aio.xml
@@ -36,288 +36,17 @@
<section name="Introduction">
<p>
- With usage of APR or NIO APIs as the basis of its connectors, Tomcat is
- able to provide a number of extensions over the regular blocking IO
- as provided with support for the Servlet API.
+ <b>IMPORTANT NOTE: Usage of these features requires using the
+ HTTP connectors. The AJP connectors do not support them.</b>
</p>
- <p>
- <b>IMPORTANT NOTE: Usage of these features requires using the APR or NIO
- HTTP connectors. The classic java.io HTTP connector and the AJP connectors
- do not support them.</b>
- </p>
-
- </section>
-
- <section name="Comet support">
-
- <p>
- Comet support allows a servlet to process IO asynchronously, receiving
- events when data is available for reading on the connection (rather than
- always using a blocking read), and writing data back on connections
- asynchronously (most likely responding to some event raised from some
- other source).
- </p>
-
- <subsection name="CometEvent">
-
- <p>
- Servlets which implement the <code>org.apache.catalina.comet.CometProcessor</code>
- interface will have their event method invoked rather than the usual service
- method, according to the event which occurred. The event object gives
- access to the usual request and response objects, which may be used in the
- usual way. The main difference is that those objects remain valid and fully
- functional at any time between processing of the BEGIN event until processing
- an END or ERROR event.
- The following event types exist:
- </p>
-
- <ul>
- <li>EventType.BEGIN: will be called at the beginning
- of the processing of the connection. It can be used to initialize any relevant
- fields using the request and response objects. Between the end of the processing
- of this event, and the beginning of the processing of the end or error events,
- it is possible to use the response object to write data on the open connection.
- Note that the response object and dependent OutputStream and Writer are still
- not synchronized, so when they are accessed by multiple threads,
- synchronization is mandatory. After processing the initial event, the request
- is considered to be committed.</li>
- <li>EventType.READ: This indicates that input data is available, and that one read can be made
- without blocking. The available and ready methods of the InputStream or
- Reader may be used to determine if there is a risk of blocking: the servlet
- should read while data is reported available. When encountering a read error,
- the servlet should report it by propagating the exception properly. Throwing
- an exception will cause the error event to be invoked, and the connection
- will be closed.
- Alternately, it is also possible to catch any exception, perform clean up
- on any data structure the servlet may be using, and using the close method
- of the event. It is not allowed to attempt reading data from the request
- object outside of the execution of this method.<br/>
- On some platforms, like Windows, a client disconnect is indicated by a READ event.
- Reading from the stream may result in -1, an IOException or an EOFException.
- Make sure you properly handle all these three cases.
- If you don't catch the IOException, Tomcat will instantly invoke your event chain with an ERROR as
- it catches the error for you, and you will be notified of the error at that time.
- </li>
- <li>EventType.END: End may be called to end the processing of the request. Fields that have
- been initialized in the begin method should be reset. After this event has
- been processed, the request and response objects, as well as all their dependent
- objects will be recycled and used to process other requests. End will also be
- called when data is available and the end of file is reached on the request input
- (this usually indicates the client has pipelined a request).</li>
- <li>EventType.ERROR: Error will be called by the container in the case where an IO exception
- or a similar unrecoverable error occurs on the connection. Fields that have
- been initialized in the begin method should be reset. After this event has
- been processed, the request and response objects, as well as all their dependent
- objects will be recycled and used to process other requests.</li>
- </ul>
-
- <p>
- There are some event subtypes which allow finer processing of events (note: some of these
- events require usage of the org.apache.catalina.valves.CometConnectionManagerValve valve):
- </p>
-
- <ul>
- <li>EventSubType.TIMEOUT: The connection timed out (sub type of ERROR); note that this ERROR
- type is not fatal, and the connection will not be closed unless the servlet uses the close
- method of the event.
- </li>
- <li>EventSubType.CLIENT_DISCONNECT: The client connection was closed (sub type of ERROR).
- </li>
- <li>EventSubType.IOEXCEPTION: An IO exception occurred, such as invalid content, for example,
- an invalid chunk block (sub type of ERROR).
- </li>
- <li>EventSubType.WEBAPP_RELOAD: The web application is being reloaded (sub type of END).
- </li>
- <li>EventSubType.SESSION_END: The servlet ended the session (sub type of END).
- </li>
- </ul>
-
- <p>
- As described above, the typical lifecycle of a Comet request will consist in a series of
- events such as: BEGIN -> READ -> READ -> READ -> ERROR/TIMEOUT. At any time, the servlet
- may end processing of the request by using the close method of the event object.
- </p>
-
- </subsection>
-
- <subsection name="CometFilter">
-
- <p>
- Similar to regular filters, a filter chain is invoked when comet events are processed.
- These filters should implement the CometFilter interface (which works in the same way as
- the regular Filter interface), and should be declared and mapped in the deployment
- descriptor in the same way as a regular filter. The filter chain when processing an event
- will only include filters which match all the usual mapping rules, and also implement
- the CometFiler interface.
- </p>
-
- </subsection>
-
- <subsection name="Example code">
-
- <p>
- The following pseudo code servlet implements asynchronous chat functionality using the API
- described above:
- </p>
-
- <source><![CDATA[public class ChatServlet
- extends HttpServlet implements CometProcessor {
-
- protected ArrayList<HttpServletResponse> connections =
- new ArrayList<HttpServletResponse>();
- protected MessageSender messageSender = null;
-
- public void init() throws ServletException {
- messageSender = new MessageSender();
- Thread messageSenderThread =
- new Thread(messageSender, "MessageSender[" + getServletContext().getContextPath() + "]");
- messageSenderThread.setDaemon(true);
- messageSenderThread.start();
- }
-
- public void destroy() {
- connections.clear();
- messageSender.stop();
- messageSender = null;
- }
-
- /**
- * Process the given Comet event.
- *
- * @param event The Comet event that will be processed
- * @throws IOException
- * @throws ServletException
- */
- public void event(CometEvent event)
- throws IOException, ServletException {
- HttpServletRequest request = event.getHttpServletRequest();
- HttpServletResponse response = event.getHttpServletResponse();
- if (event.getEventType() == CometEvent.EventType.BEGIN) {
- log("Begin for session: " + request.getSession(true).getId());
- PrintWriter writer = response.getWriter();
- writer.println("<!DOCTYPE html>");
- writer.println("<head><title>JSP Chat</title></head><body>");
- writer.flush();
- synchronized(connections) {
- connections.add(response);
- }
- } else if (event.getEventType() == CometEvent.EventType.ERROR) {
- log("Error for session: " + request.getSession(true).getId());
- synchronized(connections) {
- connections.remove(response);
- }
- event.close();
- } else if (event.getEventType() == CometEvent.EventType.END) {
- log("End for session: " + request.getSession(true).getId());
- synchronized(connections) {
- connections.remove(response);
- }
- PrintWriter writer = response.getWriter();
- writer.println("</body></html>");
- event.close();
- } else if (event.getEventType() == CometEvent.EventType.READ) {
- InputStream is = request.getInputStream();
- byte[] buf = new byte[512];
- do {
- int n = is.read(buf); //can throw an IOException
- if (n > 0) {
- log("Read " + n + " bytes: " + new String(buf, 0, n)
- + " for session: " + request.getSession(true).getId());
- } else if (n < 0) {
- error(event, request, response);
- return;
- }
- } while (is.available() > 0);
- }
- }
-
- public class MessageSender implements Runnable {
-
- protected boolean running = true;
- protected ArrayList<String> messages = new ArrayList<String>();
-
- public MessageSender() {
- }
-
- public void stop() {
- running = false;
- }
-
- /**
- * Add message for sending.
- */
- public void send(String user, String message) {
- synchronized (messages) {
- messages.add("[" + user + "]: " + message);
- messages.notify();
- }
- }
-
- public void run() {
-
- while (running) {
-
- if (messages.size() == 0) {
- try {
- synchronized (messages) {
- messages.wait();
- }
- } catch (InterruptedException e) {
- // Ignore
- }
- }
-
- synchronized (connections) {
- String[] pendingMessages = null;
- synchronized (messages) {
- pendingMessages = messages.toArray(new String[0]);
- messages.clear();
- }
- // Send any pending message on all the open connections
- for (int i = 0; i < connections.size(); i++) {
- try {
- PrintWriter writer = connections.get(i).getWriter();
- for (int j = 0; j < pendingMessages.length; j++) {
- writer.println(pendingMessages[j] + "<br>");
- }
- writer.flush();
- } catch (IOException e) {
- log("IOExeption sending message", e);
- }
- }
- }
-
- }
-
- }
-
- }
-
-}]]></source>
-
- </subsection>
- <subsection name="Comet timeouts">
- <p>If you are using the NIO connector, you can set individual timeouts for your different comet connections.
- To set a timeout, simply set a request attribute like the following code shows:</p>
- <source>CometEvent event.... event.setTimeout(30*1000);</source>
- <p>or</p>
- <source>event.getHttpServletRequest().setAttribute("org.apache.tomcat.comet.timeout", new Integer(30 * 1000));</source>
- <p>
- This sets the timeout to 30 seconds.
- Important note: in order to set this timeout, it has to be done on the <code>BEGIN</code> event.
- The default value is <code>soTimeout</code>
- </p>
- <p>If you are using the APR connector, all Comet connections will have the same timeout value. It is <code>soTimeout*50</code>
- </p>
- </subsection>
-
</section>
<section name="Asynchronous writes">
<p>
- When APR or NIO is enabled, Tomcat supports using sendfile to send large static files.
+ When using HTTP connectors (based on APR or NIO/NIO2),
+ Tomcat supports using sendfile to send large static files.
These writes, as soon as the system load increases, will be performed
asynchronously in the most efficient way. Instead of sending a large response using
blocking writes, it is possible to write content to a static file, and write it
diff --git a/webapps/docs/appdev/web.xml.txt b/webapps/docs/appdev/web.xml.txt
index 593b800..b560a73 100644
--- a/webapps/docs/appdev/web.xml.txt
+++ b/webapps/docs/appdev/web.xml.txt
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="ISO-8859-1"?>
+<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
diff --git a/webapps/docs/apr.xml b/webapps/docs/apr.xml
index 165c5af..4e7fb45 100644
--- a/webapps/docs/apr.xml
+++ b/webapps/docs/apr.xml
@@ -90,7 +90,7 @@
</p>
<ul>
<li>APR 1.2+ development headers (libapr1-dev package)</li>
- <li>OpenSSL 0.9.7+ development headers (libssl-dev package)</li>
+ <li>OpenSSL 1.0.2+ development headers (libssl-dev package)</li>
<li>JNI headers from Java compatible JDK 1.4+</li>
<li>GNU development environment (gcc, make)</li>
</ul>
@@ -130,23 +130,8 @@
</section>
<section name="APR Lifecycle Listener Configuration">
- <subsection name="AprLifecycleListener">
- <attributes>
- <attribute name="SSLEngine" required="false">
- <p>
- Name of the SSLEngine to use. off: Do not use SSL, on: Use SSL but no specific ENGINE.
- The default value is <b>on</b>.
- This initializes the native SSL engine, then enable the use of this engine in the connector
- using the <code>SSLEnabled</code> attribute. Example:
- </p>
- <source><![CDATA[<Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />]]></source>
-
- <p>See the <a href="http://www.openssl.org">Official OpenSSL
- website</a> for more details on SSL hardware engines and manufacturers.
- </p>
- </attribute>
- </attributes>
- </subsection>
+ <p>See <a href="config/listeners.html#APR_Lifecycle_Listener_-_org.apache.catalina.core.AprLifecycleListener">the
+ listener configuration</a>.</p>
</section>
<section name="APR Connectors Configuration">
diff --git a/webapps/docs/building.xml b/webapps/docs/building.xml
index 5fce971..993da35 100644
--- a/webapps/docs/building.xml
+++ b/webapps/docs/building.xml
@@ -87,7 +87,7 @@ available, which will be used to actually perform the build.
<p>
Tomcat SVN repository URL:
- <a href="http://svn.apache.org/repos/asf/tomcat/tc8.0.x/trunk/">http://svn.apache.org/repos/asf/tomcat/tc8.0.x/trunk/</a>
+ <a href="http://svn.apache.org/repos/asf/tomcat/tc8.5.x/trunk/">http://svn.apache.org/repos/asf/tomcat/tc8.5.x/trunk/</a>
</p>
<p>
Tomcat source packages:
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index 4f31dbe..3f07acc 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -38,13 +38,23 @@
Fixes having an issue number are sorted by their number, ascending.
- There is no ordering by add/update/fix.
+ There is no ordering by add/update/fix/scode.
Other fixed issues are added to the end of the list, chronologically.
They eventually become mixed with the numbered issues. (I.e., numbered
issues do not "pop up" wrt. others).
-->
-<section name="Tomcat 8.0.39 (violetagg)">
+<section name="Tomcat 8.5.8">
+ <subsection name="Coyote">
+ <changelog>
+ <fix>
+ Check that threadPriority values used in AbstractProtocol are valid.
+ (fschumacher)
+ </fix>
+ </changelog>
+ </subsection>
+</section>
+<section name="Tomcat 8.5.7" rtext="not released">
<subsection name="Catalina">
<changelog>
<fix>
@@ -72,6 +82,19 @@
password. (markt)
</fix>
<fix>
+ <bug>60297</bug>: Simplify connector creation in embedded mode. (remm)
+ </fix>
+ <fix>
+ Refactor creation of containers in embedded mode for more consistency
+ and flexibility. (remm)
+ </fix>
+ <add>
+ Introduce new methods <code>read(ByteBuffer)</code>/
+ <code>write(ByteBuffer)</code> in
+ <code>o.a.catalina.connector.CoyoteInputStream</code>/
+ <code>o.a.catalina.connector.CoyoteOutputStream</code>. (violetagg)
+ </add>
+ <fix>
When configuring the JMX remote listener, specify the allowed types for
the credentials. (markt)
</fix>
@@ -80,9 +103,50 @@
<subsection name="Coyote">
<changelog>
<fix>
+ Correct the HPACK header table size configuration that transposed the
+ client and server table sizes when creating the encoder and decoder.
+ (markt)
+ </fix>
+ <fix>
+ Don't continue to process an HTTP/2 stream if it is reset during header
+ parsing. (markt)
+ </fix>
+ <fix>
+ HTTP/2 uses separate headers for each Cookie. As required by RFC 7540,
+ merge these into a single Cookie header before processing continues.
+ (markt)
+ </fix>
+ <fix>
+ Align the HTTP/2 implementation with the HTTP/1.1 implementation and
+ return a 500 response when an unhandled exception occurs during request
+ processing. (markt)
+ </fix>
+ <fix>
Correct the HTTP header parser so that DEL is not treated as a valid
token character. (markt)
</fix>
+ <add>
+ Add checks around the handling of HTTP/2 pseudo headers. (markt)
+ </add>
+ <add>
+ Add support for trailer headers to the HTTP/2 implementation. (markt)
+ </add>
+ <fix>
+ <bug>60232</bug>: When processing headers for an HTTP/2 stream, ensure
+ that the read buffer is large enough for the header being processed.
+ (markt)
+ </fix>
+ <add>
+ Add configuration options to the HTTP/2 implementation to control the
+ maximum number of headers allowed, the maximum size of headers allowed,
+ the maximum number of trailer headers allowed, the maximum size of
+ trailer headers allowed and the maximum number of cookies allowed.
+ (markt)
+ </add>
+ <fix>
+ Correctly differentiate between sending and receiving a reset frame when
+ tracking the state of an HTTP/2 stream. (markt)
+ </fix>
<fix>
<bug>60319</bug>: When using an Executor, disconnect it from the
Connector attributes <code>maxThreads</code>,
@@ -107,12 +171,19 @@
</add>
</changelog>
</subsection>
+ <subsection name="Jasper">
+ <changelog>
+ <update>
+ Update to the Eclipse JDT Compiler 4.6.1. (markt)
+ </update>
+ </changelog>
+ </subsection>
<subsection name="Web applications">
<changelog>
- <fix>
- Correct a typo in HTTP Connector How-To.
- Issue reported via comments.apache.org. (violetagg)
- </fix>
+ <add>
+ Add HTTP/2 configuration information to the documentation web
+ application. (markt)
+ </add>
<fix>
Fix default value of <code>validationInterval</code> attribute in
jdbc-pool. (kfujino)
@@ -134,9 +205,9 @@
</subsection>
<subsection name="Other">
<changelog>
- <update>
- Update the ECJ compiler to version 4.5.1. (markt)
- </update>
+ <add>
+ Add the JASPIC API jar to the Maven Central publication script. (markt)
+ </add>
<fix>
Remove classes from tomcat-util-scan.jar that are duplicates of those in
tomcat-util.jar. (markt)
@@ -144,7 +215,7 @@
</changelog>
</subsection>
</section>
-<section name="Tomcat 8.0.38 (markt)" rtext="2016-10-10">
+<section name="Tomcat 8.5.6" rtext="2016-10-10">
<subsection name="Catalina">
<changelog>
<add>
@@ -184,6 +255,12 @@
Tatsuya Bessho. (kfujino)
</fix>
<fix>
+ <bug>60138</bug>: Fix the <code>SSLHostConfig</code> so that the
+ <code>protocols</code> attribute is limited to the protocols supported
+ by the current JSSE implementation rather than the default protocols
+ used by the implementation. (markt)
+ </fix>
+ <fix>
<bug>60146</bug>: Improve performance for resource retrieval by making
calls to WebResource.getInputStream() trigger caching if the resource is
small enough. Patch provided by mohitchugh. (markt)
@@ -212,17 +289,22 @@
loader. (markt)
</fix>
<fix>
+ <bug>60196</bug>: Ensure that the <code>isMandatory</code> flag is
+ correctly set when using JASPIC authentication. (markt)
+ </fix>
+ <fix>
<bug>60199</bug>: Log a warning if deserialization issues prevent a
session attribute from being loaded. (markt)
</fix>
+ <fix>
+ <bug>60208</bug>: When using RFC6265 compliant cookies, the
+ <code>/</code> character should not be allowed in a cookie name since
+ the RFC6265 will drop such cookies as invalid. (markt)
+ </fix>
</changelog>
</subsection>
<subsection name="Coyote">
<changelog>
- <fix>
- Correctly handle a call to <code>AsyncContext.complete()</code> from a
- non-container thread when non-blocking I/O is being used. (markt)
- </fix>
<add>
Refactor the code that implements the requirement that a call to
<code>complete()</code> or <code>dispatch()</code> made from a
@@ -234,6 +316,10 @@
thread could trigger a deadlock. (markt)
</add>
<fix>
+ Fail earlier if the client closes the connection during SNI processing.
+ (markt)
+ </fix>
+ <fix>
<bug>60123</bug>: Avoid potential threading issues that could cause
excessively large vales to be returned for the processing time of
a current request. (markt)
@@ -242,6 +328,12 @@
<bug>60174</bug>: Log instances of <code>HeadersTooLargeException</code>
during request processing. (markt)
</fix>
+ <fix>
+ <bug>60173</bug>: Allow up to 64kB HTTP/2 header table size limit. (remm)
+ </fix>
+ <fix>
+ Java 9 compatibility of direct ByteBuffer cleaner. (remm)
+ </fix>
</changelog>
</subsection>
<subsection name="Jasper">
@@ -315,41 +407,14 @@
</changelog>
</subsection>
</section>
-<section name="Tomcat 8.0.37 (markt)" rtext="2016-09-05">
+<section name="Tomcat 8.5.5" rtext="2016-09-05">
<subsection name="Catalina">
<changelog>
<fix>
- <bug>57705</bug>: Add debug logging for requests denied by the remote
- host and remote address valves and filters. Based on a patch by Graham
- Leggett. (markt)
- </fix>
- <add>
- <bug>59399</bug>: Add a new option to the Realm implementations that
- ship with Tomcat that allows the HTTP status code used for HTTP -> HTTPS
- redirects to be controlled per Realm. (markt)
- </add>
- <update>
- Change the default of the
- <code>sessionCookiePathUsesTrailingSlash</code> attribute of the
- <code>Context</code> element to <code>false</code> since the problems
- caused when a Servlet is mapped to <code>/*</code> are more significant
- than the security risk of not enabling this option by default. (markt)
- </update>
- <fix>
- Do not attempt to start web resources during a web application's
- initialisation phase since the web application is not fully configured
- at that point and the web resources may not be correctly configured.
- (markt)
- </fix>
- <fix>
- <bug>59708</bug>: Modify the LockOutRealm logic. Valid authentication
- attempts during the lock out period will no longer reset the lock out
- timer to zero. (markt)
- </fix>
- <fix>
- Improve error handling around user code prior to calling
- <code>InstanceManager.destroy()</code> to ensure that the method is
- executed. (markt)
+ <bug>18500</bug>: Add limited support for wildcard host names and host
+ aliases. Names of the form <code>*.domainname</code> are now permitted.
+ Note that an exact host name match takes precedence over a wild card
+ host name match. (markt)
</fix>
<fix>
<bug>59813</bug>: Ensure that circular relations of the Class-Path
@@ -393,6 +458,10 @@
the object obtained is of the expected type. (markt)
</add>
<fix>
+ <bug>59823</bug>: Ensure that JASPIC configuration is taken into account
+ when calling <code>HttpServletRequest.authenticate()</code>. (markt)
+ </fix>
+ <fix>
<bug>59824</bug>: Mark the <code>RewriteValve</code> as supporting async
processing by default. (markt)
</fix>
@@ -418,8 +487,10 @@
(markt)
</fix>
<fix>
- <bug>59960</bug>: Fix Javadoc so it builds with Java 8. Patch by Coty
- Sutherland. (markt)
+ <bug>59913</bug>: Correct a regression introduced with the support for
+ the Servlet 4 <code>HttpServletRequest.getMapping()</code> API that
+ caused the attributes for forwarded requests to be lost if requested
+ from within a subsequent include. (markt)
</fix>
<fix>
<bug>59966</bug>: Do not start the web application if the error page
@@ -430,6 +501,10 @@
support for the debug attribute. (markt)
</fix>
<fix>
+ <bug>60012</bug>: Improvements in the log messages. Based on
+ suggestions by Nemo Chen. (violetagg)
+ </fix>
+ <fix>
Changes to the <code>allowLinking</code> attribute of a
<code>StandardRoot</code> instance now invalidate the cache if caching
is enabled. (markt)
@@ -474,19 +549,24 @@
<fix>
Make timing attacks against the Realm implementations harder. (schultz)
</fix>
+ <fix>
+ A number of the JRE memory leaks addressed by the
+ <code>JreMemoryLeakPreventionListener</code> have been fixed in Java 9
+ so the associated protection is now disabled when running on Java 9
+ onwards. (markt)
+ </fix>
</changelog>
</subsection>
<subsection name="Coyote">
<changelog>
<fix>
- Improve error handling around user code prior to calling
- <code>InstanceManager.destroy()</code> to ensure that the method is
- executed. (markt)
+ Correct a regression in refactoring to enable injection of custom
+ keystores that broke the automatic conversion of OpenSSL style PEM
+ key and certificate files for use with JSSE TLS connectors. (markt)
</fix>
<fix>
- Extend synchronization for NIO2 writes to avoid
- <code>ConcurrentModificationException</code> observed during testing.
- (markt)
+ <bug>59910</bug>: Don't hardcode key alias value to "tomcat" for JSSE.
+ When using a keystore, OpenSSL will still default to it. (remm)
</fix>
<fix>
<bug>59904</bug>: Add a limit (default 200) for the number of cookies
@@ -499,17 +579,53 @@
(markt)
</fix>
<fix>
+ <bug>59950</bug>: Correct log message when reporting that the current
+ number of HTTP/2 streams for a connection could not be pruned to below
+ the limit. (markt)
+ </fix>
+ <fix>
+ Ensure that <code>Semaphore.release</code> is called in all cases. Even
+ when there is an exception. (violetagg)
+ </fix>
+ <fix>
+ <bug>60030</bug>: Correct a potential infinite loop in the SNI parsing
+ code triggered by failing to handle an end of stream condition. (markt)
+ </fix>
+ <fix>
+ Small logging optimization in the <code>Rfc6265CookieProcessor</code>.
+ Patch provided by Svetlin Zarev. (markt)
+ </fix>
+ <fix>
OpenSSL now disables 3DES by default so reflect this when using OpenSSL
syntax to select ciphers. (markt)
</fix>
+ <fix>
+ Use the proper ERROR socket status code for async errors with NIO2.
+ (remm)
+ </fix>
+ <fix>
+ <bug>60035</bug>: Fix a potential connection leak if the client drops a
+ TLS connection before the handshake completes. (markt)
+ </fix>
+ <fix>
+ Refactor the JSSE client certificate validation so that the
+ effectiveness of the <code>certificateVerificationDepth</code>
+ configuration attribute does not depend on the presence of a certificate
+ revokation list. (markt)
+ </fix>
+ <add>
+ Log a warning at start up if a JSSE TLS connector is configured with
+ a trusted certificate that is either not yet valid or has expired.
+ (markt)
+ </add>
</changelog>
</subsection>
<subsection name="Jasper">
<changelog>
<fix>
- Improve error handling around user code prior to calling
- <code>InstanceManager.destroy()</code> to ensure that the method is
- executed. (markt)
+ When writing out a full web.xml file with JspC ensure that the encoding
+ used in the XML prolog matches the encoding used to write the contents
+ of the file. (markt)
</fix>
<fix>
Improve the error handling for custom tags to ensure that the tag is
@@ -532,42 +648,16 @@
<subsection name="WebSocket">
<changelog>
<fix>
- Improve error handling around user code prior to calling
- <code>InstanceManager.destroy()</code> to ensure that the method is
- executed. (markt)
- </fix>
- <fix>
<bug>59908</bug>: Ensure that a reason phrase is included in the close
message if a session is closed due to a timeout. (markt)
</fix>
</changelog>
</subsection>
- <subsection name="Web Applications">
+ <subsection name="Web applications">
<changelog>
<fix>
- Do not log an additional case of <code>IOException</code>s in the
- error handler for the Drawboard WebSocket example when the root cause is
- the client disconnecting since the logs add no value. (markt)
- </fix>
- <fix>
- <bug>59642</bug>: Mention the <code>localDataSource</code> in the
- <code>DataSourceRealm</code> section of the Realm How-To. (markt)
- </fix>
- <fix>
- Follow-up to the fix for <bug>59399</bug>. Ensure that the new attribute
- <code>transportGuaranteeRedirectStatus</code> is documented for all
- <strong>Realm</strong>s. Also document the <code>NullRealm</code> and
- when it is automatically created for an <strong>Engine</strong>. (markt)
- </fix>
- <fix>
- Fix the description of <code>maxAge</code> attribute in jdbc-pool doc.
- This attribute works both when a connection is returned and when a
- connection is borrowed. (kfujino)
- </fix>
- <fix>
- <bug>59774</bug>: Correct the <code>prefix</code> values in the
- documented examples for configuring the <code>AccessLogValve</code>.
- Patch provided by Mike Noordermeer. (markt)
+ <bug>59867</bug>: Correct the documentation provided by Manager's
+ 403.jsp. (violetagg)
</fix>
<fix>
<bug>59868</bug>: Clarify the documentation for the Manager web
@@ -575,6 +665,12 @@
server section are the primary host name and IP address. (markt)
</fix>
<fix>
+ <bug>59940</bug>: Correct the name of the
+ <code>truststorePassword</code> attribute of the
+ <code>SSLHostConfig</code> element in the configuration documentation.
+ (markt)
+ </fix>
+ <fix>
MBeans Descriptors How-To is moved to
<code>mbeans-descriptors-howto.html</code>. Patch provided by Radoslav
Husar. (violetagg)
@@ -589,29 +685,9 @@
</fix>
</changelog>
</subsection>
- <subsection name="Tribes">
- <changelog>
- <add>
- Add log message when the ping has timed-out. (kfujino)
- </add>
- <fix>
- If the ping message has been received at the
- <code>AbstractReplicatedMap#leftOver</code> method, ensure that notify
- the member is alive than ignore it. (kfujino)
- </fix>
- </changelog>
- </subsection>
<subsection name="jdbc-pool">
<changelog>
<fix>
- Fix the duplicated connection release when connection verification
- failed. (kfujino)
- </fix>
- <fix>
- Ensure that do not remove the abandoned connection that has been already
- released. (kfujino)
- </fix>
- <fix>
In order to avoid the unintended skip of <code>PoolCleaner</code>,
remove the check code of the execution interval in the task that has
been scheduled. (kfujino)
@@ -647,18 +723,11 @@
</subsection>
<subsection name="Other">
<changelog>
- <update>
- <bug>59276</bug>: Update optional Checkstyle library to 6.17. (kkolinko)
- </update>
<add>
- Use the mirror network rather than the ASF master site to download the
- current ASF dependencies. (markt)
+ <bug>59871</bug>: Add a property (<code>timeFormat</code>) to
+ JULI's <code>OneLineFormatter</code> to enable the format of the
+ time stamp used in log messages to be configured. (markt)
</add>
- <update>
- Update the packaged version of the Tomcat Native Library to 1.2.8 to
- pick up the latest fixes and make 1.2.8 the minimum recommended version.
- (markt)
- </update>
<fix>
<bug>59899</bug>: Update Tomcat's copy of the Java Persistence
annotations to include the changes made in 2.1 / JavaEE 7. (markt)
@@ -686,46 +755,267 @@
</changelog>
</subsection>
</section>
-<section name="Tomcat 8.0.36 (markt)" rtext="2016-06-13">
+<section name="Tomcat 8.5.4" rtext="2016-07-12">
<subsection name="Catalina">
<changelog>
<fix>
- RMI Target related memory leaks are avoidable which makes them an
- application bug that needs to be fixed rather than a JRE bug to work
- around. Therefore, start logging RMI Target related memory leaks on web
- application stop. Add an option that controls if the check for these
- leaks is made. Log a warning if running on Java 9 with this check
- enabled but without the command line option it requires. (markt)
+ <bug>57705</bug>: Add debug logging for requests denied by the remote
+ host and remote address valves and filters. Based on a patch by Graham
+ Leggett. (markt)
</fix>
<fix>
- Ensure NPE will not be thrown during deployment when scanning jar files
- without MANIFEST.MF file. (violetagg)
+ Correct a regression in the fix for <bug>58588</bug> that removed the
+ entire <code>org.apache.juli</code> package from the embedded JARs
+ rendering them unusable. (markt)
</fix>
+ <add>
+ <bug>59399</bug>: Add a new option to the Realm implementations that
+ ship with Tomcat that allows the HTTP status code used for HTTP -> HTTPS
+ redirects to be controlled per Realm. (markt)
+ </add>
+ <update>
+ Change the default of the
+ <code>sessionCookiePathUsesTrailingSlash</code> attribute of the
+ <code>Context</code> element to <code>false</code> since the problems
+ caused when a Servlet is mapped to <code>/*</code> are more significant
+ than the security risk of not enabling this option by default. (markt)
+ </update>
<fix>
- <bug>59604</bug>: Correct the assumption made in the URL decoding that
- the default platform encoding is always compatible with ISO-8859-1. This
- assumption is not always valid, e.g. on z/OS. (markt)
+ Follow-up to <bug>59655</bug>. Improve the documentation for configuring
+ permitted cookie names. Patch provided by Kyohei Nakamura. (markt)
</fix>
<fix>
- <bug>59608</bug>: Skip over any invalid <code>Class-Path</code> attribute
- from JAR manifests. Log errors at debug level due to many bad libraries.
- (remm)
+ Do not attempt to start web resources during a web application's
+ initialisation phase since the web application is not fully configured
+ at that point and the web resources may not be correctly configured.
+ (markt)
</fix>
<fix>
- Fix error message when failed to register MBean. (kfujino)
+ <bug>59708</bug>: Modify the LockOutRealm logic. Valid authentication
+ attempts during the lock out period will no longer reset the lock out
+ timer to zero. (markt)
+ </fix>
+ <fix>
+ Improve error handling around user code prior to calling
+ <code>InstanceManager.destroy()</code> to ensure that the method is
+ executed. (markt)
</fix>
</changelog>
</subsection>
<subsection name="Coyote">
<changelog>
+ <scode>
+ Refactor the certificate keystore and trust store generation to make it
+ easier for embedded users to inject their own key stores. (markt)
+ </scode>
+ <add>
+ <bug>59233</bug>: Add the ability to add TLS virtual hosts dynamically.
+ (markt)
+ </add>
+ <update>
+ Add a <code>maxConcurrentStreamExecution</code> on the HTTP/2
+ protocol handler to allow restricting the amount of concurrent stream
+ that are being executed in a single connection. The default is to
+ not limit it. (remm)
+ </update>
<fix>
- Ensure that requests with HTTP method names that are not tokens (as
- required by RFC 7231) are rejected with a 400 response. (markt)
+ Correct a problem with <code>ServletRequest.getServerPort()</code> for
+ secure HTTP/2 connections that meant an incorrect value was returned when
+ using the default port. (markt)
</fix>
<fix>
- When an asynchronous request is processed by the AJP connector, ensure
- that request processing has fully completed before starting the next
- request. (markt)
+ Improve error handling around user code prior to calling
+ <code>InstanceManager.destroy()</code> to ensure that the method is
+ executed. (markt)
+ </fix>
+ </changelog>
+ </subsection>
+ <subsection name="Jasper">
+ <changelog>
+ <fix>
+ Improve error handling around user code prior to calling
+ <code>InstanceManager.destroy()</code> to ensure that the method is
+ executed. (markt)
+ </fix>
+ </changelog>
+ </subsection>
+ <subsection name="WebSocket">
+ <changelog>
+ <scode>
+ Now the WebSocket implementation is not built directly on top of the
+ Servlet API and can use Tomcat internals, there is no need for the
+ dedicated WebSocket Executor. It has been replaced by the use of the
+ Connector/Endpoint provided Executor. (markt)
+ </scode>
+ <fix>
+ Improve error handling around user code prior to calling
+ <code>InstanceManager.destroy()</code> to ensure that the method is
+ executed. (markt)
+ </fix>
+ </changelog>
+ </subsection>
+ <subsection name="Web Applications">
+ <changelog>
+ <fix>
+ Do not log an additional case of <code>IOException</code>s in the
+ error handler for the Drawboard WebSocket example when the root cause is
+ the client disconnecting since the logs add no value. (markt)
+ </fix>
+ <fix>
+ <bug>59642</bug>: Mention the <code>localDataSource</code> in the
+ <code>DataSourceRealm</code> section of the Realm How-To. (markt)
+ </fix>
+ <fix>
+ <bug>59672</bug>: Update the security considerations page of the
+ documentation web application to take account of the fact that the
+ Manager and HostManager applications now have a
+ <code>RemoteAddrValve</code> configured by default. (markt)
+ </fix>
+ <fix>
+ Follow-up to the fix for <bug>59399</bug>. Ensure that the new attribute
+ <code>transportGuaranteeRedirectStatus</code> is documented for all
+ <strong>Realm</strong>s. Also document the <code>NullRealm</code> and
+ when it is automatically created for an <strong>Engine</strong>. (markt)
+ </fix>
+ <fix>
+ Fix the description of <code>maxAge</code> attribute in jdbc-pool doc.
+ This attribute works both when a connection is returned and when a
+ connection is borrowed. (kfujino)
+ </fix>
+ <fix>
+ <bug>59774</bug>: Correct the <code>prefix</code> values in the
+ documented examples for configuring the <code>AccessLogValve</code>.
+ Patch provided by Mike Noordermeer. (markt)
+ </fix>
+ </changelog>
+ </subsection>
+ <subsection name="Extras">
+ <changelog>
+ <scode>
+ <bug>58588</bug>: Remove the JULI extras package from the distribution.
+ It was only useful for switching Tomcat's internal logging to log4j
+ 1.2.x and that version of log4j is no longer supported. No additional
+ Tomcat code is required if switching Tomcat's internal logging to log
+ via log4j 2.x. (markt)
+ </scode>
+ </changelog>
+ </subsection>
+ <subsection name="Tribes">
+ <changelog>
+ <add>
+ Add log message when the ping has timed-out. (kfujino)
+ </add>
+ <fix>
+ If the ping message has been received at the
+ <code>AbstractReplicatedMap#leftOver</code> method, ensure that notify
+ the member is alive than ignore it. (kfujino)
+ </fix>
+ </changelog>
+ </subsection>
+ <subsection name="jdbc-pool">
+ <changelog>
+ <fix>
+ Fix the duplicated connection release when connection verification
+ failed. (kfujino)
+ </fix>
+ <fix>
+ Ensure that do not remove the abandoned connection that has been already
+ released. (kfujino)
+ </fix>
+ </changelog>
+ </subsection>
+ <subsection name="Other">
+ <changelog>
+ <update>
+ <bug>59276</bug>: Update optional Checkstyle library to 6.17. (kkolinko)
+ </update>
+ <add>
+ Use the mirror network rather than the ASF master site to download the
+ current ASF dependencies. (markt)
+ </add>
+ <update>
+ Update the packaged version of the Tomcat Native Library to 1.2.8 to
+ pick up the latest fixes and make 1.2.8 the minimum recommended version.
+ (markt)
+ </update>
+ <scode>
+ Use UTF-8 with a standard prolog for all XML files. (markt)
+ </scode>
+ </changelog>
+ </subsection>
+</section>
+<section name="Tomcat 8.5.3" rtext="2016-06-13">
+ <subsection name="Catalina">
+ <changelog>
+ <fix>
+ RMI Target related memory leaks are avoidable which makes them an
+ application bug that needs to be fixed rather than a JRE bug to work
+ around. Therefore, start logging RMI Target related memory leaks on web
+ application stop. Add an option that controls if the check for these
+ leaks is made. Log a warning if running on Java 9 with this check
+ enabled but without the command line option it requires. (markt)
+ </fix>
+ <fix>
+ Ensure NPE will not be thrown during deployment when scanning jar files
+ without MANIFEST.MF file. (violetagg)
+ </fix>
+ <scode>
+ Remove the <code>clearReferencesStatic</code> option from
+ <code>StandardContext</code>. It was known to cause problems with some
+ libraries (such as log4j) and was only linked to suspected memory leaks
+ rather than known memory leaks. It had been disabled by default with no
+ increase in the reports of memory leaks for some time. (markt)
+ </scode>
+ <fix>
+ <bug>59604</bug>: Correct the assumption made in the URL decoding that
+ the default platform encoding is always compatible with ISO-8859-1. This
+ assumption is not always valid, e.g. on z/OS. (markt)
+ </fix>
+ <fix>
+ <bug>59608</bug>: Skip over any invalid <code>Class-Path</code> attribute
+ from JAR manifests. Log errors at debug level due to many bad libraries.
+ (remm)
+ </fix>
+ <fix>
+ Fix error message when failed to register MBean. (kfujino)
+ </fix>
+ <fix>
+ <bug>59655</bug>: Configure the cookie name validation to use RFC6265 rules by default to
+ align it with the default cookie parser. Document the impact system properties have on
+ cookie name validation. (mark)
+ </fix>
+ </changelog>
+ </subsection>
+ <subsection name="Coyote">
+ <changelog>
+ <fix>
+ Ensure that requests with HTTP method names that are not tokens (as
+ required by RFC 7231) are rejected with a 400 response. (markt)
+ </fix>
+ <fix>
+ When an asynchronous request is processed by the AJP connector, ensure
+ that request processing has fully completed before starting the next
+ request. (markt)
+ </fix>
+ <fix>
+ Improve handling of HTTP/2 stream resets. (markt)
+ </fix>
+ <add>
+ <bug>58750</bug>: The HTTP Server header is no longer set by default. A
+ Server header may be configured by setting the <code>server</code>
+ attribute on the <code>Connector</code>. A new <code>Connector</code>
+ attribute, <code>serverRemoveAppProvidedValues</code> may be used to
+ remove any Server header set by a web application. (markt)
+ </add>
+ <fix>
+ <bug>59564</bug>: Correct offset when reading into HTTP/2 input buffer
+ that could cause problems reading request bodies. (violetagg/markt)
+ </fix>
+ <fix>
+ Modify the handling of read/write timeouts so that the appropriate error
+ handling (<code>ReadListener.onError()</code>,
+ <code>WriteListener.onError()</code> or
+ <code>AsycnListener.onError()</code>) is called. (markt)
</fix>
<fix>
If an async dispatch results in the completion of request processing,
@@ -733,6 +1023,9 @@
processing of the next request else the remaining body may be read as the
start of the next request leading to a 400 response. (markt)
</fix>
+ <fix>
+ Fix a cause of multiple attempts to close the same socket. (markt)
+ </fix>
</changelog>
</subsection>
<subsection name="Jasper">
@@ -747,14 +1040,19 @@
pinned in memory. (markt)
</fix>
<fix>
- <bug>59640</bug>: NPEs with not found TLDs. (remm)
- </fix>
- <fix>
<bug>59654</bug>: Improve error message when attempting to use a TLD
file from an invalid location. Patch provided by Huxing Zhang. (markt)
</fix>
</changelog>
</subsection>
+ <subsection name="WebSocket">
+ <changelog>
+ <fix>
+ <bug>59659</bug>: Fix possible memory leak in WebSocket handling of
+ unexpected client disconnects. (markt)
+ </fix>
+ </changelog>
+ </subsection>
<subsection name="Web applications">
<changelog>
<fix>
@@ -786,10 +1084,6 @@
Update the internal fork of Commons File Upload to r1743698 (1.3.1 plus
additional fixes). (markt)
</update>
- <update>
- Update the option code coverage tool Cobertura to 2.1.1 so it is easier
- to compare the change in lines of code between 8.0.x and 9.0.x. (markt)
- </update>
<fix>
<bug>58626</bug>: Add support for a new environment variable
(<code>USE_NOHUP</code>) that causes <code>nohup</code> to be used when
@@ -800,7 +1094,7 @@
</changelog>
</subsection>
</section>
-<section name="Tomcat 8.0.35 (markt)" rtext="2016-05-16">
+<section name="Tomcat 8.5.2" rtext="2016-05-16">
<subsection name="Catalina">
<changelog>
<fix>
@@ -810,7 +1104,7 @@
</changelog>
</subsection>
</section>
-<section name="Tomcat 8.0.34 (markt)" rtext="not released">
+<section name="Tomcat 8.5.1" rtext="not released">
<subsection name="Catalina">
<changelog>
<fix>
@@ -824,8 +1118,13 @@
Kyohei Nakamura. (violetagg)
</fix>
<fix>
- <bug>59213</bug>: Async dispatches should be based off a wrapped
- request. (remm)
+ Fixed possible NPE in
+ <code>o.a.catalina.loader.WebappClassLoaderBase.getResourceAsStream</code>
+ (violetagg)
+ </fix>
+ <fix>
+ <bug>59213</bug>: Async dispatches should be based off a wrapped request.
+ (remm)
</fix>
<fix>
Ensure that <code>javax.servlet.ServletRequest</code> and
@@ -868,6 +1167,11 @@
disabled. Patch is provided by Svetlin Zarev. (violetagg)
</fix>
<fix>
+ Remove unused <code>distributable</code> attribute that is defined as
+ <code>TransientAttribute</code> of <code>Manager</code> in StoreConfig.
+ (kfujino)
+ </fix>
+ <fix>
Fix handling of Cluster Receiver in StoreConfig. The <code>bind</code>
and <code>host</code> attributes define as
<code>TransientAttribute</code>. (kfujino)
@@ -884,29 +1188,32 @@
functions as designed and sessions are swapped out to keep the active
session count below <code>maxActiveSessions</code>. (markt)
</fix>
- <fix>
- <bug>59247</bug>: Preload ResourceEntry as a workaround for security
- manager issues on some JVMs. (kkolinko/remm)
- </fix>
+ <add>
+ Add the <code>org.apache.catalina.servlet4preview</code> package that
+ can be used to gain early access to Servlet 4.0 features. Note that this
+ package will <strong>not</strong> be present in Tomcat 9. (markt)
+ </add>
<fix>
Correctly configure the base path for a resources directory provided by
an expanded JAR file. Patch provided by hengyunabc. (markt)
</fix>
+ <add>
+ When multiple compressed formats are available and the client does not
+ express a preference, use the server order to determine the preferred
+ format. Based on a patch by gmokki. (markt)
+ </add>
<fix>
- Ensure that <code>/WEB-INF/classes</code> is never processed as a web
- fragment. (markt)
+ <bug>59284</bug>: Allow the Tomcat provided JASPIC
+ <code>SimpleServerAuthConfig</code> to pick up module configuration
+ properties from either the property set passed to its constructor or
+ from the properties passed in the call to <code>getAuthContext</code>.
+ Based on a patch by Thomas Maslen. (markt)
</fix>
<fix>
<bug>59310</bug>: Do not add a <code>Content-Length: 0</code> header for
custom responses to <code>HEAD</code> requests that do not set a
<code>Content-Length</code> value. (markt)
</fix>
- <add>
- Make a web application's CredentialHandler available through a context
- attribute. This allows a web application to use the same algorithm
- for validating or generating new stored credentials from cleartext
- ones. (schultz)
- </add>
<fix>
When normalizing paths, improve the handling when paths end with
<code>/.</code> or <code>/..</code> and ensure that input and output are
@@ -941,6 +1248,19 @@
<code>ALLOW-FROM</code> option is used. (violetagg)
</fix>
<fix>
+ Fix an <code>IllegalArgumentException</code> if the first use of an
+ internal <code>Response</code> object requires JASPIC authentication.
+ (markt)
+ </fix>
+ <fix>
+ Do not trigger unnecessary session ID changes when using JASPIC and the
+ user is authenticated using cached credentials. (markt)
+ </fix>
+ <fix>
+ <bug>59437</bug>: Ensure that the JASPIC <code>CallbackHandler</code> is
+ thread-safe. (markt)
+ </fix>
+ <fix>
<bug>59449</bug>: In <code>ContainerBase</code>, ensure that the process
to remove a child container is the reverse of the process to add one.
Patch provided by Huxing Zhang. (markt)
@@ -950,20 +1270,25 @@
<subsection name="Coyote">
<changelog>
<fix>
- When running on Java 7, exclude DHE ciphers from the default cipher list
- for JSSE connectors since they use weak 768 bit DH keys and cannot be
- configured to use more secure keys. (markt)
+ Align cipher configuration parsing with current OpenSSL master. (markt)
</fix>
+ <update>
+ Change the default for <code>honorCipherOrder</code> to
+ <code>false</code>. With the current default TLS configuration, it is no
+ longer necessary for this to be <code>true</code> for a reasonably
+ secure configuration. (markt)
+ </update>
<add>
Add a new environment variable <code>JSSE_OPTS</code> that is intended
to be used to pass JVM wide configuration to the JSSE implementation.
The default value is <code>-Djdk.tls.ephemeralDHKeySize=2048</code>
- which protects against weak Diffie-Hellman keys with Java 8. (markt)
+ which protects against weak Diffie-Hellman keys. (markt)
</add>
- <update>
- Exclude ciphers that use RSA keys from the default cipher list since
- they do not support forward secrecy. (markt)
- </update>
+ <fix>
+ When running on Java 7, exclude DHE ciphers from the default cipher list
+ for JSSE connectors since they use weak 768 bit DH keys and cannot be
+ configured to use more secure keys. (markt)
+ </fix>
<fix>
<bug>58970</bug>: Fix a connection counting bug in the NIO connector
that meant some dropped connections were not removed from the current
@@ -978,6 +1303,27 @@
the <code>Content-Language</code> HTTP header to ensure the locale is
correctly represented. Patch provided by zikfat. (markt)
</fix>
+ <update>
+ <bug>59295</bug>: Add support for using pem encoded certificates with
+ JSSE SSL. Submitted by Emmanuel Bourg with additional tweaks. (remm)
+ </update>
+ <fix>
+ Make the TLS certificate chain available to clients when using
+ JSSE+OpenSSL with the certificate chain stored in a Java KeyStore.
+ (markt)
+ </fix>
+ <fix>
+ Work around <a herf="https://github.com/openssl/openssl/issues/188">a
+ known issue in OpenSSL</a> that does not permit the TLS handshake to be
+ failed if the ALPN negotiation fails. (markt)
+ </fix>
+ <update>
+ <bug>59421</bug>: Add direct HTTP/2 connection support. (remm)
+ </update>
+ <fix>
+ Correctly handle a call to <code>AsyncContext.complete()</code> from a
+ non-container thread when non-blocking I/O is being used. (markt)
+ </fix>
<fix>
<bug>59451</bug>: Correct Javadoc for <code>MessageBytes</code>. Patch
provided by Kyohei Nakamura. (markt)
@@ -998,11 +1344,30 @@
JAR has been exploded into <code>WEB-INF/classes</code> and the web
application is deployed as a packed WAR. (markt)
</fix>
+ <fix>
+ <bug>59640</bug>: NPEs with not found TLDs. (remm)
+ </fix>
</changelog>
</subsection>
<subsection name="WebSocket">
<changelog>
<fix>
+ <bug>59189</bug>: Explicitly release the native memory held by the
+ <code>Inflater</code> and <code>Deflater</code> when using
+ PerMessageDeflate and the WebSocket session ends. Based on a patch by
+ Henrik Olsson. (markt)
+ </fix>
+ <fix>
+ Return back a container specific extension to the WsServerContainer
+ to allow frameworks to more easily dispatch requests to WebSocket
+ endpoints. (violetagg)
+ </fix>
+ <fix>
+ Fix a regression caused by the connector refactoring and ensure that the
+ thread context class loader is set to the web application
+ classloader when processing WebSocket messages on the server. (markt)
+ </fix>
+ <fix>
Ensure that a client disconnection triggers the error handling for the
associated WebSocket end point. (markt)
</fix>
@@ -1015,6 +1380,20 @@
<subsection name="Web applications">
<changelog>
<fix>
+ <bug>59210</bug>: Server push example has to use
+ <code>o.a.catalina.connector.RequestFacade</code> when obtaining
+ <code>o.a.catalina.core.ApplicationPushBuilder</code>. Patch is
+ provided by Huxing Zhang. (violetagg)
+ </fix>
+ <fix>
+ <bug>59218</bug>: Correct the path to <code>jaspic-providers.xml</code>
+ in Jaspic How-To. Patch is provided by Tatsuya Bessho. (violetagg)
+ </fix>
+ <fix>
+ Remove button that has accidentally been added to the host manager.
+ Submitted by Coty Sutherland. (remm)
+ </fix>
+ <fix>
Update in the documentation the link to the maven repository where
Tomcat snapshot artifacts are deployed. (markt/violetagg)
</fix>
@@ -1047,7 +1426,7 @@
</fix>
<add>
Add get/set method for the channel that is related to
- <code>ChannelInterceptorBase</code>. (kfujino)
+ <code>ChannelInterceptor</code>. (kfujino)
</add>
<fix>
As with the multicast cluster environment, in the static cluster
@@ -1088,6 +1467,14 @@
<subsection name="Other">
<changelog>
<fix>
+ <bug>59209</bug>: Remove <code>honorCipherOrder=false</code> attribute
+ from the connector example in server.xml. When the block is uncommented
+ the connector will use the default value for this attribute which is
+ <code>false</code>. If one needs to enable it, one can add it
+ explicitly to the connector definition. Use of this feature requires
+ Java 8 or later. Patch is provided by Huxing Zhang. (violetagg)
+ </fix>
+ <fix>
<bug>59211</bug>: Add hamcrest to Eclipse classpath. Patch is provided
by Huxing Zhang. (violetagg)
</fix>
@@ -1103,42 +1490,274 @@
</changelog>
</subsection>
</section>
-<section name="Tomcat 8.0.33 (markt)" rtext="2016-03-24">
+<section name="Tomcat 8.5.0" rtext="2016-03-24">
+ <subsection name="General">
+ <changelog>
+ <update>
+ Remove support for Comet. (markt)
+ </update>
+ <update>
+ Tighten up the default file permissions for the <code>.tar.gz</code>
+ distribution so no files or directories are world readable by default.
+ Configure Tomcat to run with a default umask of <code>0027</code> which
+ may be overridden by setting <code>UMASK</code> in
+ <code>setenv.sh</code>. (markt)
+ </update>
+ <update>
+ Remove native code (Windows Service Wrapper, APR/native connector)
+ support for Windows Itanium. (markt)
+ </update>
+ </changelog>
+ </subsection>
<subsection name="Catalina">
<changelog>
- <fix>
- Correct a regression in the fix for <bug>58867</bug>. When configuring a
- Context to use an external directory for the <code>docBase</code>, and
- that directory happens to be located along side the original WAR, use
- the directory as the <code>docBase</code> rather than expanding the
- WAR into the <code>appBase</code> and using the newly created expanded
- directory as the <code>docBase</code>. (markt)
+ <update>
+ The default HTTP cookie parser has been changed to
+ <code>org.apache.tomcat.util.http.Rfc6265CookieProcessor</code>. (markt)
+ </update>
+ <scode>
+ Refactor creation of <code>MapperListener</code> to ensure that the
+ <code>Mapper</code> used is the <code>Mapper</code> associated with the
+ <code>Service</code> for which the listener was created. (markt)
+ </scode>
+ <add>
+ Move the functionality that provides redirects for context roots and
+ directories where a trailing <code>/</code> is added from the Mapper to
+ the <code>DefaultServlet</code>. This enables such requests to be
+ processed by any configured Valves and Filters before the redirect is
+ made. This behaviour is configurable via the
+ <code>mapperContextRootRedirectEnabled</code> and
+ <code>mapperDirectoryRedirectEnabled</code> attributes of the Context
+ which may be used to restore the previous behaviour. (markt)
+ </add>
+ <scode>
+ Refactor <code>Service.getContainer()</code> to return an
+ <code>Engine</code> rather than a <code>Container</code>. (markt)
+ </scode>
+ <fix>
+ <bug>34319</bug>: Only load those keys in
+ <code>StoreBase.processExpire</code> from JDBCStore that are old enough
+ to be expired. Based on a patch by Tom Anderson. (fschumacher)
</fix>
<add>
<bug>58351</bug>: Make the server build date and server version number
accessible via JMX. Patch provided by Huxing Zhang. (markt)
</add>
<add>
- <bug>58988</bug>: Special characters in the substitutions for the RewriteValve
- can now be quoted with a backslash. (fschumacher)
+ <bug>56917</bug>: As per RFC7231 (HTTP/1.1), allow HTTP/1.1 and later
+ redirects to use relative URIs. This is controlled by a new attribute
+ <code>useRelativeRedirects</code> on the <strong>Context</strong> and
+ defaults to <code>true</code>. (markt)
+ </add>
+ <fix>
+ <bug>58629</bug>: Allow an embedded Tomcat instance to start when the
+ <code>Service</code> has no <code>Engine</code> configured. (markt)
+ </fix>
+ <fix>
+ Correctly notify the MapperListener associated with a Service if the
+ Engine for that Service is changed. (markt)
+ </fix>
+ <add>
+ Make a web application's CredentialHandler available through a context
+ attribute. This allows a web application to use the same algorithm
+ for validating or generating new stored credentials from cleartext
+ ones. (schultz)
</add>
<fix>
- <bug>58999</bug>: Fix class and resource name filtering in WebappClassLoader.
- It throws a StringIndexOutOfBoundsException if the name is exactly
- "org" or "javax". (rjung)
+ <bug>58635</bug>: Enable break points to be set within agent code when
+ running Tomcat with a Java agent. Based on a patch by Huxing Zhang.
+ (markt)
+ </fix>
+ <fix>
+ Fixed potential NPE in <code>HostConfig</code> while deploying an
+ application. Issue reported by coverity scan. (violetagg)
+ </fix>
+ <fix>
+ <bug>58655</bug>: Fix an <code> IllegalStateException</code> when
+ calling <code>HttpServletResponse.sendRedirect()</code> with the
+ <code>RemoteIpFilter</code>. This was caused by trying to correctly
+ generate the absolute URI for the redirect. With the fix for
+ <bug>56917</bug>, redirects may now be relative making the
+ <code>sendRedirect()</code> implementation for the
+ <code>RemoteIpFilter</code> much simpler. This also addresses issues
+ where the redirect may not have behaved as expected when redirecting
+ from http to https to from https to http. (markt)
+ </fix>
+ <fix>
+ <bug>58657</bug>: Exceptions in a Servlet 3.1 <code>ReadListener</code>
+ or <code>WriteListener</code> do not need to be immediately fatal to the
+ connection. Allow an error response to be written. (markt)
+ </fix>
+ <fix>
+ Correct implementation of
+ <code>validateClientProvidedNewSessionId</code> so client provided
+ session IDs may be rejected if validation is enabled. (markt)
+ </fix>
+ <fix>
+ <bug>58701</bug>: Reset the <code>instanceInitialized</code> field in
+ <code>StandardWrapper</code> when unloading a Servlet so that a new
+ instance may be correctly initialized. (markt)
+ </fix>
+ <update>
+ Add a new flag <code>aprPreferred</code> to the Apr listener. if set to
+ <code>false</code>, when using the connector defaults, it will use
+ NIO + OpenSSL if tomcat-native is available, rather than the APR
+ connector. (remm)
+ </update>
+ <fix>
+ Add path parameter handling to
+ <code>HttpServletRequest.getContextPath()</code>. This is a follow-up to
+ the fix for <bug>57215</bug>. (markt)
+ </fix>
+ <fix>
+ <bug>58692</bug>: Make <code>StandardJarScanner</code> more robust. Log
+ a warning if a class path entry cannot be scanned rather than triggering
+ the failure of the web application. Includes a test case written by
+ Derek Abdine. (markt)
+ </fix>
+ <fix>
+ <bug>58702</bug>: Ensure an access log entry is generated if the client
+ aborts the connection. (markt)
+ </fix>
+ <fix>
+ Fixed various issues reported by Findbugs. (violetagg)
+ </fix>
+ <fix>
+ <bug>58735</bug>: Add support for the <code>X-XSS-Protection</code>
+ header to the <code>HttpHeaderSecurityFilter</code>. Patch provided by
+ Jacopo Cappellato. (markt)
+ </fix>
+ <fix>
+ Add the <code>StatusManagerServlet</code> to the list of Servlets that
+ can only be loaded by privileged applications. (markt)
+ </fix>
+ <fix>
+ Simplify code and fix messages in
+ <code>org.apache.catalina.core.DefaultInstanceManager</code> class.
+ (kkolinko)
+ </fix>
+ <fix>
+ <bug>58751</bug>: Correctly handle the case where an
+ <code>AsyncListener</code> dispatches to a Servlet on an asynchronous
+ timeout and the Servlet uses <code>sendError()</code> to trigger an
+ error page. Includes a test case based on code provided by Andy
+ Wilkinson.(markt)
+ </fix>
+ <fix>
+ Ensure that the proper file encoding, if specified, will be used when
+ a readme file is served by DefaultServlet. (violetagg)
+ </fix>
+ <fix>
+ Fix declaration of <code>localPort</code> attribute of Connector MBean:
+ it is read-only. (kkolinko)
+ </fix>
+ <fix>
+ <bug>58766</bug>: Make skipping non-class files during annotation
+ scanning faster by checking the file name first. Improve debug logging.
+ (kkolinko)
+ </fix>
+ <fix>
+ <bug>58768</bug>: Log a warning if a redirect fails because of an
+ invalid location. (markt)
</fix>
<scode>
- Remove unnecessary code. There is no support for context level cluster.
- (kfujino)
+ <bug>58827</bug>: Remove remains of JSR-77 implementation. (markt)
+ </scode>
+ <fix>
+ <bug>58836</bug>: Correctly merge query string parameters when
+ processing a forwarded request where the target includes a query string
+ that contains a parameter with no value. (markt/kkolinko)
+ </fix>
+ <fix>
+ Make sure that shared Digester is reset in an unlikely error case
+ in <code>HostConfig.deployWAR()</code>. (kkolinko)
+ </fix>
+ <add>
+ Extend the feature available in the cluster session manager
+ implementations that enables session attribute replication to be
+ filtered based on attribute name to all session manager implementations.
+ Note that configuration attribute name has changed from
+ <code>sessionAttributeFilter</code> to
+ <code>sessionAttributeNameFilter</code>. Apply the filter on load as
+ well as unload to ensure that configuration changes made while the web
+ application is stopped are applied to any persisted data. (markt)
+ </add>
+ <add>
+ Extend the session attribute filtering options to include filtering
+ based on the implementation class of the value and optional
+ <code>WARN</code> level logging if an attribute is filtered. These
+ options are available for all of the Manager implementations that ship
+ with Tomcat. When a <code>SecurityManager</code> is used filtering will
+ be enabled by default. (markt)
+ </add>
+ <scode>
+ Remove <code>distributable</code> and <code>maxInactiveInterval</code>
+ from the <code>Manager</code> interface because the attributes are never
+ used. The equivalent attributes from the <code>Context</code> always
+ take precedence. (markt)
</scode>
+ <fix>
+ <bug>58867</bug>: Improve checking on Host start for WAR files that have
+ been modified while Tomcat has stopped and re-expand them if
+ <code>unpackWARs</code> is <code>true</code>. (markt)
+ </fix>
+ <fix>
+ <bug>58900</bug>: Correctly undeploy symlinked resources and prevent an
+ infinite cycle of deploy / undeploy. (markt)
+ </fix>
+ <fix>
+ Protect initialization of <code>ResourceLinkFactory</code> when
+ running with a SecurityManager. (kkolinko)
+ </fix>
+ <fix>
+ Correct a thread safety issue in the filtering of session attributes
+ based on the implementing class name of the value object. (markt)
+ </fix>
+ <fix>
+ Fix class loader decision on the delegation for class loading and
+ resource lookup and make it faster too. (rjung)
+ </fix>
+ <fix>
+ <bug>58905</bug>: Ensure that <code>Tomcat.silence()</code> silences the
+ correct logger and respects the current setting. (markt)
+ </fix>
+ <fix>
+ <bug>58946</bug>: Ensure that the request parameter map remains
+ immutable when processing via a RequestDispatcher. (markt)
+ </fix>
+ <fix>
+ Ensure that <code>/WEB-INF/classes</code> is never processed as a web
+ fragment. (markt)
+ </fix>
+ <update>
+ Switch default connector when native is installed. Unless configured
+ otherwise, the NIO endpoint will be used by default. If SSL is
+ configured, OpenSSL will be used rather than JSSE. (remm)
+ </update>
+ <fix>
+ Correct a regression in the fix for <bug>58867</bug>. When configuring a
+ Context to use an external directory for the <code>docBase</code>, and
+ that directory happens to be located along side the original WAR, use
+ the directory as the <code>docBase</code> rather than expanding the
+ WAR into the <code>appBase</code> and using the newly created expanded
+ directory as the <code>docBase</code>. (markt)
+ </fix>
<add>
- Make checking for var and map replacement in RewriteValve a bit stricter and
- correct detection of colon in var replacement. (fschumacher)
+ <bug>58988</bug>: Special characters in the substitutions for the
+ RewriteValve can now be quoted with a backslash. (fschumacher)
</add>
<fix>
- Fix the type of <code>InstanceManager</code> attribute of mbean
- definition of <code>StandardContext</code>. (kfujino)
+ <bug>58999</bug>: Fix class and resource name filtering in
+ WebappClassLoader. It throws a StringIndexOutOfBoundsException if the
+ name is exactly "org" or "javax". (rjung)
</fix>
+ <add>
+ Add JASPIC (JSR-196) support. (markt)
+ </add>
+ <add>
+ Make checking for var and map replacement in RewriteValve a bit stricter
+ and correct detection of colon in var replacement. (fschumacher)
+ </add>
<fix>
Refactor the web application class loader to reduce the impact of JAR
scanning on the memory footprint of the web application. (markt)
@@ -1152,9 +1771,23 @@
footprint of the web application. (markt)
</fix>
<fix>
- <bug>57809</bug>: Deprecate the custom context attribute
- <code>org.apache.tomcat.util.scan.MergedWebXml</code> which will be
- removed in Tomcat 9. (markt)
+ Refactor the web.xml parsing so a new parser is created every time the
+ web application starts rather than creating and caching the parser when
+ the Context is created. This enables the parser to take account of
+ modified Context configuration parameters and reduces (slightly) the
+ memory footprint of a running Tomcat instance. (markt)
+ </fix>
+ <update>
+ Switch the web application class loader to the
+ <code>ParallelWebappClassLoader</code> by default. (markt)
+ </update>
+ <fix>
+ <bug>57809</bug>: Remove the custom context attribute that held the
+ effective web.xml. Components needing access to configuration
+ information may access it via the Servlet API. (markt)
+ </fix>
+ <fix>
+ Refactor JAR scanning to reduce memory footprint. (markt)
</fix>
<fix>
<bug>59001</bug>: Correctly handle the case when Tomcat is installed on
@@ -1176,16 +1809,8 @@
session. (markt)
</fix>
<fix>
- Storeconfig handling of alternate cookie processors. (markt/remm)
- </fix>
- <fix>
- Storeconfig handling for socket properties. (remm)
+ Add socket properties support to storeconfig. (remm)
</fix>
- <add>
- Log a warning message if a user tries to configure the default session
- timeout via the deprecated (and ignored)
- <code>Manager.setMaxInactiveInterval()</code> method. (markt)
- </add>
<fix>
Fix incorrect parsing of the NE and NC flags in rewrite rules. (remm)
</fix>
@@ -1210,430 +1835,348 @@
the <code>JNDIRealm</code> once they are no longer required.
(fschumacher/markt)
</fix>
+ <add>
+ Implement the proposed Servlet 4.0 API to provide mapping type
+ information for the current request. (markt)
+ </add>
<fix>
<bug>59138</bug>: Correct a false positive warning for ThreadLocal
related memory leaks when the key class but not the value class has been
loaded by the web application class loader. (markt)
</fix>
+ <add>
+ <bug>59017</bug>: Make the pre-compressed file support in the Default
+ Servlet generic so any compression may be used rather than just gzip.
+ Patch provided by Mikko Tiihonen. (markt)
+ </add>
<fix>
<bug>59145</bug>: Don't log an invalid warning when a user logs out of
a session associated with SSO. (markt)
</fix>
<fix>
+ <bug>59150</bug>: Add an additional flag on APR listener to allow
+ disabling automatic use of OpenSSL. (remm)
+ </fix>
+ <fix>
<bug>59151</bug>: Fix a regression in the fix for <bug>56917</bug> that
added additional (and arguably unnecessary) validation to the provided
redirect location. (markt)
</fix>
<fix>
<bug>59154</bug>: Fix a <code>NullPointerException</code> in the
- <code>JASSMemoryLoginModue</code> resulting from the introduction of the
- <code>CredentialHandler</code> to <code>Realm</code>s. (schultz/markt)
+ <code>JAASMemoryLoginModule</code> resulting from the introduction of
+ the <code>CredentialHandler</code> to <code>Realm</code>s.
+ (schultz/markt)
</fix>
</changelog>
</subsection>
<subsection name="Coyote">
<changelog>
- <fix>
- <bug>58646</bug>: Correct a problem with sendfile that resulted in a
- Processor being added to the cache twice leading to broken responses.
+ <update>
+ Remove support for the HTTP BIO and AJP BIO connectors. (markt)
+ </update>
+ <scode>
+ Refactor HTTP upgrade and AJP implementations to reduce duplication.
(markt)
- </fix>
+ </scode>
+ <add>
+ Add support for HPACK header encoding and decoding, contributed
+ by Stuart Douglas. (remm)
+ </add>
+ <add>
+ <bug>57108</bug>: Add support for Server Name Indication (SNI). There
+ has been significant changes to the SSL configuration in server.xml to
+ support this. (markt)
+ </add>
+ <add>
+ Add SSL engine for JSSE backed by OpenSSL. Includes ALPN support.
+ Based on code contributed by Numa de Montmollin and derived from code
+ developed by Twitter and Netty. (remm)
+ </add>
<fix>
- <bug>59015</bug>: Fix potential cause of endless APR Poller loop during
- shutdown if the Poller experiences an error during the shutdown process.
- (markt)
+ RFC 7230 states that clients should ignore reason phrases in HTTP/1.1
+ response messages. Since the reason phrase is optional, Tomcat no longer
+ sends it. As a result the system property
+ <code>org.apache.coyote.USE_CUSTOM_STATUS_MSG_IN_HEADER</code> is no
+ longer used and has been removed. (markt)
</fix>
+ <update>
+ The minimum required Tomcat Native version has been increased to 1.2.2.
+ The 1.2.x branch includes ALPN and SNI support which are required for
+ HTTP/2. (markt)
+ </update>
+ <add>
+ Add support for HTTP/2 including server push. (markt)
+ </add>
<fix>
- Align cipher aliases for <code>kECDHE</code> and <code>ECDHE</code> with
- the current OpenSSL implementation. (markt)
+ <bug>58621</bug>: The certificate chain cannot be set using the main
+ certificate attribute, so restore the certificate chain property. (remm)
</fix>
<fix>
- <bug>59081</bug>: Retain the user defined cipher order when defining
- ciphers using the OpenSSL format. (markt)
+ Allow a new SSL config type where a connector can use either JSSE or
+ OpenSSL. Both could be allowed, but it would likely create support
+ issues. This type is used by the OpenSSL implementation for NIOx. (remm)
</fix>
<fix>
- <bug>59089</bug>: Correctly ignore HTTP headers that include non-token
- characters in the header name. (markt)
+ Improve upgrade context classloader handling by using Context.bind and
+ unbind. (remm)
</fix>
<add>
- Add support for additional OpenSSL cipher aliases from OpenSSL master
- when specifying ciphers using the OpenSSL syntax. (markt)
+ Improve OpenSSL keystore/truststore configuration by using the code
+ from the JSSE implementation. (remm, jfclere)
</add>
- </changelog>
- </subsection>
- <subsection name="Jasper">
- <changelog>
<fix>
- <bug>57583</bug>: Improve the performance of
- <code>javax.servlet.jsp.el.ScopedAttributeELResolver</code> when
- resolving attributes that do not exist. This improvement only works when
- Jasper is used with with Tomcat's EL implementation. (markt)
+ Fix a potential loop when a client drops the connection unexpectedly.
+ (markt)
</fix>
- <update>
- <bug>58111</bug>: Update to the Eclipse JDT Compiler 4.5. (markt)
- </update>
<add>
- Add Java 9 support for JSPs. (markt)
+ OpenSSL renegotiation support for client certificate authentication.
+ (remm)
</add>
- </changelog>
- </subsection>
- <subsection name="WebSocket">
- <changelog>
<fix>
- <bug>59014</bug>: Ensure that a WebSocket close message can be sent
- after a close message has been received. (markt)
+ Fix NIO connector renegotiation. (remm)
</fix>
<fix>
- Correctly handle compression of partial messages when the final message
- fragment has a zero length payload. (markt)
+ <bug>58659</bug>: Fix a potential deadlock during HTTP/2 processing when
+ the connection window size is limited. (markt)
</fix>
<fix>
- <bug>59119</bug>: Correct read logic for WebSocket client when using
- secure connections. (markt)
+ Correct an NPE when listing the enabled ciphers (e.g. via the Manager
+ web application) for a TLS enabled APR/native connector. (markt)
</fix>
+ <add>
+ New configuration option <code>ajpFlush</code> for the AJP connectors
+ to disable the sending of AJP flush packets. (rjung)
+ </add>
<fix>
- <bug>59134</bug>: Correct client connect logic for secure connections
- made through a proxy. (markt)
+ Handle the case in the NIO connector where the required TLS buffer sizes
+ increase after the connection has been initiated. (markt)
</fix>
<fix>
- <bug>59189</bug>: Explicitly release the native memory held by the
- <code>Inflater</code> and <code>Deflater</code> when using
- PerMessageDeflate and the WebSocket session ends. Based on a patch by
- Henrik Olsson. (markt)
+ Handle the case in the NIO2 connector where the required TLS buffer
+ sizes increase after the connection has been initiated. (markt/remm)
</fix>
- </changelog>
- </subsection>
- <subsection name="Web applications">
- <changelog>
<fix>
- Correct an error in the documentation of the expected behaviour for
- automatic deployment. If a WAR is updated and an expanded directory is
- present, the directory will always be deleted and recreated by expanding
- the WAR if <code>unpackWARs</code> is <code>true</code>. (markt)
+ Bad processing of handshake errors in NIO2. (remm)
</fix>
<fix>
- <bug>58935</bug>: Remove incorrect references in the documentation to
- using <code>jar:file:</code> URLs with the Manager application. (markt)
+ Use JSSE session configuration options with OpenSSL. (remm)
</fix>
<fix>
- Correct the description of the
- <code>ServletRequest.getServerPort()</code> in Proxy How-To.
- Issue reported via comments.apache.org. (violetagg)
+ <bug>59015</bug>: Fix potential cause of endless APR Poller loop during
+ shutdown if the Poller experiences an error during the shutdown process.
+ (markt)
+ </fix>
+ <fix>
+ Align cipher aliases for <code>kECDHE</code> and <code>ECDHE</code> with
+ the current OpenSSL implementation. (markt)
</fix>
<fix>
- Fix a potential indefinite wait in the Comet Chat servlet in the
- examples web application. (markt)
+ <bug>59081</bug>: Retain the user defined cipher order when defining
+ ciphers. (markt)
</fix>
- </changelog>
- </subsection>
- <subsection name="Tribes">
- <changelog>
<fix>
- If promoting a proxy node to a primary node when getting a session,
- notify the change of the new primary node to the original backup node.
- (kfujino)
+ <bug>59089</bug>: Correctly ignore HTTP headers that include non-token
+ characters in the header name. (markt)
</fix>
</changelog>
</subsection>
- <subsection name="Other">
+ <subsection name="Jasper">
<changelog>
<fix>
- <bug>58283</bug>: Change the default download location for libraries
- during the build process from <code>/usr/share/java</code> to
- <code>${user.home}/temp</code>. Patch provided by Ahmed Hosni. (markt)
+ <bug>57136#c25</bug>: Change default value of
+ <code>quoteAttributeEL</code> setting in Jasper to be <code>true</code>
+ for better compatibility with other implementations and older versions
+ of Tomcat. Add command line option <code>-no-quoteAttributeEL</code> in
+ JspC. (kkolinko)
</fix>
<fix>
- <bug>59031</bug>: When using the Windows uninstaller, do not remove the
- contents of any directories that have been symlinked into the Tomcat
- directory structure. (markt)
+ Fix handling of missing messages in
+ <code>org.apache.el.util.MessageFactory</code>. (violetagg)
</fix>
<update>
- Update the packaged version of the Tomcat Native Library to 1.2.5 to
- pick up the Windows binaries that are based on OpenSSL 1.0.2g and APR
- 1.5.1. (markt)
- </update>
- <update>
- Modify the default <code>tomcat-users.xml</code> file to make it harder
- for users to configure the entries intended for use with the examples
- web application for the Manager application. (markt)
+ Update to the Eclipse JDT Compiler 4.5.1. (markt)
</update>
- </changelog>
- </subsection>
-</section>
-<section name="Tomcat 8.0.32 (markt)" rtext="2016-02-08">
- <subsection name="General">
- <changelog>
- <add>
- Allow to configure multiple JUnit test class patterns with the build
- property <code>test.name</code> and document the property in
- BUILDING.txt. (rjung)
- </add>
<fix>
- <bug>58768</bug>: Log a warning if a redirect fails because of an
- invalid location. (markt)
+ <bug>57583</bug>: Improve the performance of
+ <code>javax.servlet.jsp.el.ScopedAttributeELResolver</code> when
+ resolving attributes that do not exist. This improvement only works when
+ Jasper is used with Tomcat's EL implementation. (markt)
</fix>
</changelog>
</subsection>
- <subsection name="Catalina">
+ <subsection name="Cluster">
<changelog>
<fix>
- Fix class loader decision on the delegation for class loading and
- resource lookup and make it faster too. (rjung)
- </fix>
- <fix>
- <bug>58946</bug>: Ensure that the request parameter map remains
- immutable when processing via a RequestDispatcher. (markt)
+ Enable an explicit configuration of local member in the static cluster
+ membership. (kfujino)
</fix>
<fix>
- <bug>58827</bug>: Deprecate what is left of the JSR 77 implementation.
- (markt)
+ Fix potential integer overflow in <code>DeltaSession</code>.
+ Reported by coverity scan. (fschumacher)
</fix>
<fix>
- <bug>58905</bug>: Ensure that <code>Tomcat.silence()</code> silences the
- correct logger and respects the current setting. (markt)
+ In order to avoid that the heartbeat thread and the background thread to
+ run <code>Channel.heartbeat</code> simultaneously, if
+ <code>heartbeatBackgroundEnabled</code> of <code>SimpleTcpCluster</code>
+ set to <code>true</code>, ensure that the heartbeat thread does not
+ start. (kfujino)
</fix>
</changelog>
</subsection>
- <subsection name="Coyote">
+ <subsection name="WebSocket">
<changelog>
<add>
- New configuration option <code>ajpFlush</code> for the AJP connectors
- to disable the sending of AJP flush packets. (rjung)
+ <bug>55006</bug>: The WebSocket client now honors the
+ <code>java.net.java.net.ProxySelector</code> configuration (using the
+ HTTP type) when establishing WebSocket connections to servers. Based on
+ a patch by Niki Dokovski. (markt)
</add>
- </changelog>
- </subsection>
- <subsection name="Cluster">
- <changelog>
<fix>
- Correct a regression in the session attribute filtering that prevented
- clustering from starting in the default configuration. (kfujino)
+ <bug>57489</bug>: Ensure <code>onClose()</code> is called when a
+ WebSocket connection is closed even if the sending of the close message
+ fails. Includes test cases by Barry Coughlan. (markt)
+ </fix>
+ <fix>
+ <bug>58624</bug>: Correct a potential deadlock if the WebSocket
+ connection is closed when a message write is in progress. (markt)
</fix>
- </changelog>
- </subsection>
- <subsection name="WebSocket">
- <changelog>
<fix>
Fix a timing issue on session close that could result in an exception
being thrown for an incomplete message even through the message was
completed. (markt)
</fix>
- </changelog>
- </subsection>
-</section>
-<section name="Tomcat 8.0.31 (markt)" rtext="not released">
- <subsection name="Catalina">
- <changelog>
- <fix>
- Correct implementation of
- <code>validateClientProvidedNewSessionId</code> so client provided
- session IDs may be rejected if validation is enabled. (markt)
- </fix>
<fix>
- Add path parameter handling to
- <code>HttpServletRequest.getContextPath()</code>. This is a follow-up to
- the fix for <bug>57215</bug>. (markt)
+ Correctly handle compression of partial messages when the final message
+ fragment has a zero length payload. (markt)
</fix>
<fix>
- <bug>58692</bug>: Make <code>StandardJarScanner</code> more robust. Log
- a warning if a class path entry cannot be scanned rather than triggering
- the failure of the web application. Includes a test case written by
- Derek Abdine. (markt)
+ <bug>59119</bug>: Correct read logic for WebSocket client when using
+ secure connections. (markt)
</fix>
<fix>
- <bug>58701</bug>: Reset the <code>instanceInitialized</code> field in
- <code>StandardWrapper</code> when unloading a Servlet so that a new
- instance may be correctly initialized. (markt)
+ <bug>59134</bug>: Correct client connect logic for secure connections
+ made through a proxy. (markt)
</fix>
+ </changelog>
+ </subsection>
+ <subsection name="Web applications">
+ <changelog>
<fix>
- <bug>58702</bug>: Ensure an access log entry is generated if the client
- aborts the connection. (markt)
+ <bug>48674</bug>: Implement an option within the Host Manager web
+ application to persist the current configuration. Based on a patch by
+ Coty Sutherland. (markt)
</fix>
<fix>
- Fixed various issues reported by Findbugs. (violetagg)
+ <bug>58631</bug>: Correct the continuation character use in the Windows
+ Service How-To page of the documentation web application. (markt)
</fix>
<fix>
- <bug>58735</bug>: Add support for the <code>X-XSS-Protection</code>
- header to the <code>HttpHeaderSecurityFilter</code>. Patch provided by
- Jacopo Cappellato. (markt)
+ Correct the SSL documentation for deprecated attributes to point to the
+ correct, new location for attributes related to individual certificates.
+ (markt)
</fix>
<fix>
- <bug>58751</bug>: Correctly handle the case where an
- <code>AsyncListener</code> dispatches to a Servlet on an asynchronous
- timeout and the Servlet uses <code>sendError()</code> to trigger an
- error page. Includes a test case based on code provided by Andy
- Wilkinson.(markt)
+ Correct some typos in the JNDI resources How-To. (markt)
</fix>
<fix>
- <bug>58765</bug>: Change default for
- <code>mapperContextRootRedirectEnabled</code> to <code>true</code> since
- this is required for correct session management because of the default
- for <code>sessionCookiePathUsesTrailingSlash</code>. (markt)
+ Don't create session unnecessarily in the Manager application. (markt)
</fix>
<fix>
- Add the <code>StatusManagerServlet</code> to the list of Servlets that
- can only be loaded by privileged applications. (markt)
+ Don't create session unnecessarily in the Host Manager application.
+ (markt)
</fix>
<fix>
- Simplify code and fix messages in
- <code>org.apache.catalina.core.DefaultInstanceManager</code> class.
- (kkolinko)
+ <bug>58723</bug>: Clarify documentation and error messages for the text
+ interface of the manager to make clear that version must be used with
+ path when referencing contexts deployed using parallel deployment.
+ (markt)
</fix>
- <scode>
- Deprecate InstanceListener, InstanceEvent and InstanceSupport prior to
- removal in 9.0.x. (markt)
- </scode>
+ <add>
+ Document <code>test.threads</code> option in BUILDING.txt. (kkolinko)
+ </add>
<fix>
- Ensure that the proper file encoding if specified will be used when
- a readme file is served by DefaultServlet. (violetagg)
+ Correct an error in the documentation of the expected behaviour for
+ automatic deployment. If a WAR is updated and an expanded directory is
+ present, the directory will always be deleted and recreated by expanding
+ the WAR if <code>unpackWARs</code> is <code>true</code>. (markt)
</fix>
<fix>
- Fix declaration of <code>localPort</code> attribute of Connector MBean:
- it is read-only. (kkolinko)
+ <bug>58935</bug>: Remove incorrect references in the documentation to
+ using <code>jar:file:</code> URLs with the Manager application. (markt)
</fix>
<fix>
- <bug>58766</bug>: Make skipping non-class files during annotation
- scanning faster by checking the file name first. Improve debug logging.
- (kkolinko)
+ Correct the description of the
+ <code>ServletRequest.getServerPort()</code> in Proxy How-To.
+ Issue reported via comments.apache.org. (violetagg)
</fix>
+ <add>
+ The Manager and Host Manager applications are now only accessible via
+ <code>localhost</code> by default. (markt)
+ </add>
+ </changelog>
+ </subsection>
+ <subsection name="Tribes">
+ <changelog>
<fix>
- <bug>58809</bug>: Correctly recycle cookies when mapping requests for
- parallel deployment. As a side-effect of this fix, the system property
- <code>org.apache.tomcat.util.http.ServerCookie.PRESERVE_COOKIE_HEADER</code>
- is no longer used. From this release, Tomcat will always preserve the
- cookie header. (markt)
+ Clarify the handling of Copy message and Copy nodes. (kfujino)
</fix>
<fix>
- <bug>58836</bug>: Correctly merge query string parameters when
- processing a forwarded request where the target includes a query string
- that contains a parameter with no value. (markt/kkolinko)
+ Ensure that the static member is registered to the add suspect list even
+ if the static member that is registered to the remove suspect list has
+ disappeared. (kfujino)
</fix>
<fix>
- Make sure that shared Digester is reset in an unlikely error case
- in <code>HostConfig.deployWAR()</code>. (kkolinko)
+ When using a static cluster, add the members that have been cached in
+ the membership service to the map members list in order to ensure that
+ the map member is a static member. (kfujino)
</fix>
<fix>
- <bug>58867</bug>: Improve checking on Host start for WAR files that have
- been modified while Tomcat has stopped and re-expand them if
- <code>unpackWARs</code> is <code>true</code>. (markt)
+ Add support for the startup notification of local members in the static
+ cluster. (kfujino)
</fix>
<fix>
- Fix a potential JDBC resource leak in DataSourceRealm. (schultz)
+ Ignore the unnecessary member remove operation from different domain.
+ (kfujino)
</fix>
<fix>
- <bug>58900</bug>: Correctly undeploy symlinked resources and prevent an
- infinite cycle of deploy / undeploy. (markt)
+ Add support for the shutdown notification of local members in the static
+ cluster. (kfujino)
</fix>
<fix>
- Protect initialization of <code>ResourceLinkFactory</code> when
- running with a SecurityManager. (kkolinko)
+ If promoting a proxy node to a primary node when getting a session,
+ notify the change of the new primary node to the original backup node.
+ (kfujino)
</fix>
- <add>
- Extend the feature available in the cluster session manager
- implementations that enables session attribute replication to be
- filtered based on attribute name to all session manager implementations.
- Note that configuration attribute name has changed from
- <code>sessionAttributeFilter</code> to
- <code>sessionAttributeNameFilter</code>. Apply the filter on load as
- well as unload to ensure that configuration changes made while the web
- application is stopped are applied to any persisted data. (markt)
- </add>
- <add>
- Extend the session attribute filtering options to include filtering
- based on the implementation class of the value and optional
- <code>WARN</code> level logging if an attribute is filtered. These
- options are available for all of the Manager implementations that ship
- with Tomcat. When a <code>SecurityManager</code> is used filtering will
- be enabled by default. (markt)
- </add>
</changelog>
</subsection>
- <subsection name="Jasper">
+ <subsection name="jdbc-pool">
<changelog>
<fix>
- Fix handling of missing messages in
- <code>org.apache.el.util.MessageFactory</code>. (violetagg)
+ Correct evaluation of system property
+ <code>org.apache.tomcat.jdbc.pool.onlyAttemptCurrentClassLoader</code>.
+ It was basically ignored before. Reported by coverity scan. (fschumacher)
+ </fix>
+ <fix>
+ Fix potential integer overflow in <code>ConnectionPool</code> and
+ <code>PooledConnection</code>. Reported by coverity scan. (fschumacher)
</fix>
</changelog>
</subsection>
- <subsection name="Cluster">
- <changelog>
- <fix>
- In order to avoid that the heartbeat thread and the background thread to
- run <code>Channel.heartbeat</code> simultaneously, if
- <code>heartbeatBackgroundEnabled</code> of <code>SimpleTcpCluster</code>
- set to <code>true</code>, ensure that the heartbeat thread does not
- start. (kfujino)
- </fix>
- <scode>
- Simplify the code of <code>JvmRouteBinderValve.startInternal()</code>.
- Avoid potential NPE when <code>JvmRouteBinderValve</code> is configured
- directly at <code>Engine</code> element. (kfujino)
- </scode>
- </changelog>
- </subsection>
- <subsection name="WebSocket">
- <changelog>
- <fix>
- <bug>57489</bug>: Ensure <code>onClose()</code> is called when a
- WebSocket connection is closed even if the sending of the close message
- fails. Includes test cases by Barry Coughlan. (markt)
- </fix>
- </changelog>
- </subsection>
- <subsection name="Web Applications">
+ <subsection name="Other">
<changelog>
<add>
- Add a description of the default value of
- <code>heartbeatSleeptime</code> attribute and <code>optionCheck</code>
- attribute in the cluster channel docs. (kfujino)
+ Allow to configure multiple JUnit test class patterns with the build
+ property <code>test.name</code> and document the property in
+ BUILDING.txt. (rjung)
+ </add>
+ <add>
+ Support the use of the <code>threads</code> attribute on Ant's
+ junit task. Note that using this with a value of greater than one will
+ disable Cobertura code coverage. (markt)
</add>
- <fix>
- Correct some typos in the JNDI resources How-To. (markt)
- </fix>
- <fix>
- Don't create sessions unnecessarily in the Manager application. (markt)
- </fix>
- <fix>
- Don't create sessions unnecessarily in the Host Manager application.
- (markt)
- </fix>
- <fix>
- <bug>58723</bug>: Clarify documentation and error messages for the text
- interface of the manager to make clear that version must be used with
- path when referencing contexts deployed using parallel deployment.
- (markt)
- </fix>
- </changelog>
- </subsection>
- <subsection name="Tribes">
- <changelog>
- <fix>
- Fix potential NPE in <code>AbstractReplicatedMap.breakdown()</code>.
- (kfujino)
- </fix>
- <fix>
- Add support for the startup notification of local members in the static
- cluster. (kfujino)
- </fix>
- <fix>
- Ignore the unnecessary member remove operation from different domain.
- (kfujino)
- </fix>
- <fix>
- Add support for the shutdown notification of local members in the static
- cluster. (kfujino)
- </fix>
- <fix>
- Ensure that asynchronous session replication thread is a daemon thread.
- (kfujino)
- </fix>
- </changelog>
- </subsection>
- <subsection name="Other">
- <changelog>
<update>
- Remove native code (Windows Service Wrapper, APR/native connector)
- support for Windows Itanium. (markt)
+ Update optional Checkstyle library to 6.14.1. (kkolinko)
</update>
<update>
Update the packaged version of the Tomcat Native Library to 1.2.4 to
@@ -1645,5140 +2188,41 @@
version 2.50. (markt/kkolinko)
</update>
<update>
- Update optional Checkstyle library to 6.14.1. (kkolinko)
+ Update the internal fork of Commons BCEL to r1725718 to align with the
+ refactoring for BCEL 6, the next major BCEL release. (markt)
</update>
- </changelog>
- </subsection>
-</section>
-<section name="Tomcat 8.0.30 (markt)" rtext="2015-12-06">
- <subsection name="Catalina">
- <changelog>
- <fix>
- <bug>34319</bug>: Only load those keys in
- <code>StoreBase.processExpire</code> from JDBCStore, that are old
- enough, to be expired. Based on a patch by Tom Anderson. (fschumacher)
- </fix>
- <add>
- <bug>56917</bug>: As per RFC7231 (HTTP/1.1), allow HTTP/1.1 and later
- redirects to use relative URIs. This is controlled by a new attribute
- <code>useRelativeRedirects</code> on the <strong>Context</strong> and
- defaults to <code>true</code>. (markt)
- </add>
- <fix>
- <bug>58629</bug>: Allow an embedded Tomcat instance to start when the
- <code>Service</code> has no <code>Engine</code> configured. (markt)
- </fix>
- <fix>
- <bug>58635</bug>: Enable break points to be set within agent code when
- running Tomcat with a Java agent. Based on a patch by Huxing Zhang.
- (markt)
- </fix>
- <fix>
- <bug>58660</bug>: Correct a regression in 8.0.29 caused by the change
- that moved the redirection for context roots from the Mapper to the
- Default Servlet. (markt)
- </fix>
- <fix>
- Fixed potential NPE in <code>HostConfig</code> while deploying an
- application. Issue reported by coverity scan. (violetagg)
- </fix>
- <fix>
- <bug>58655</bug>: Fix an <code> IllegalStateException</code> when
- calling <code>HttpServletResponse.sendRedirect()</code> with the
- <code>RemoteIpFilter</code>. This was caused by trying to correctly
- generate the absolute URI for the redirect. With the fix for
- <bug>56917</bug>, redirects may now be relative making the
- <code>sendRedirect()</code> implementation for the
- <code>RemoteIpFilter</code> much simpler. This also addresses issues
- where the redirect may not have behaved as expected when redirecting
- from http to https to from https to http. (markt)
- </fix>
- <fix>
- <bug>58657</bug>: Exceptions in a Servlet 3.1 <code>ReadListener</code>
- or <code>WriteListener</code> do not need to be immediately fatal to the
- connection. Allow an error response to be written. (markt)
- </fix>
- </changelog>
- </subsection>
- <subsection name="Coyote">
- <changelog>
- <fix>
- Improve upgrade context classloader handling by using Context.bind and
- unbind. (remm)
- </fix>
- </changelog>
- </subsection>
- <subsection name="Jasper">
- <changelog>
- <fix>
- <bug>57136#c25</bug>: Change default value of
- <code>quoteAttributeEL</code> setting in Jasper to be <code>true</code>
- for better compatibility with other implementations and older versions
- of Tomcat (8.0.26/7.0.64 and earlier). Add command line option
- <code>-no-quoteAttributeEL</code> in JspC. (kkolinko)
- </fix>
- </changelog>
- </subsection>
- <subsection name="Cluster">
- <changelog>
- <fix>
- Fix potential integer overflow in <code>DeltaSession</code>.
- Reported by coverity scan. (fschumacher)
- </fix>
- </changelog>
- </subsection>
- <subsection name="WebSocket">
- <changelog>
- <add>
- <bug>55006</bug>: The WebSocket client now honors the
- <code>java.net.java.net.ProxySelector</code> configuration (using the
- HTTP type) when establishing WebSocket connections to servers. Based on
- a patch by Niki Dokovski. (markt)
- </add>
- <fix>
- <bug>58624</bug>: Correct a thread safety issue that meant that blocking
- message writes could block indefinitely if the WebSocket connection was
- closed while a message write was in progress. (markt)
- </fix>
- </changelog>
- </subsection>
- <subsection name="Web Applications">
- <changelog>
- <fix>
- <bug>58631</bug>: Correct the continuation character use in the Windows
- Service How-To page of the documentation web application. (markt)
- </fix>
- </changelog>
- </subsection>
- <subsection name="Tribes">
- <changelog>
- <fix>
- Ensure that the static member is registered to the add suspect list even
- if the static member that is registered to the remove suspect list has
- disappeared. (kfujino)
- </fix>
- <fix>
- Correct the warning log of when the member that is not registered in the
- membership is detected. (kfujino)
- </fix>
- <fix>
- When using a static cluster, add the members that have been cached in
- the membership service to the map members list in order to ensure that
- the map member is a static member. (kfujino)
- </fix>
- </changelog>
- </subsection>
- <subsection name="jdbc-pool">
- <changelog>
- <fix>
- Correct evaluation of system property
- <code>org.apache.tomcat.jdbc.pool.onlyAttemptCurrentClassLoader</code>.
- It was basically ignored before. Reported by coverity scan.
- (fschumacher)
- </fix>
- <fix>
- Fix potential integer overflow in <code>ConnectionPool</code> and
- <code>PooledConnection</code>. Reported by coverity scan. (fschumacher)
- </fix>
- </changelog>
- </subsection>
- <subsection name="Other">
- <changelog>
<update>
- Update optional Checkstyle library to 6.13. (kkolinko)
+ Update the internal fork of Commons DBCP 2 to r1725730 (2.1.1 plus
+ additional fixes). (markt)
</update>
- </changelog>
- </subsection>
-</section>
-<section name="Tomcat 8.0.29 (markt)" rtext="2015-11-24">
- <subsection name="General">
- <changelog>
<update>
- <bug>58596</bug>: Clarify the description in RUNNING.txt of how
- environment variables are used. (markt)
+ Update the internal fork of Commons Pool 2 to r1725738 (2.4.2 plus
+ additional fixes). (markt)
+ </update>
+ <update>
+ Update the internal fork of Commons Codec to r1725746 (1.9 plus
+ additional fixes). (markt)
</update>
- </changelog>
- </subsection>
- <subsection name="Catalina">
- <changelog>
- <add>
- Extend the fix for <bug>57136</bug> to provide a JSP Servlet
- initialisation parameter per web application that controls whether or
- not EL in JSP attributes is processed as if it uses JSP attribute
- quoting. By default, EL does not use JSP attribute quoting. (markt)
- </add>
- <fix>
- <bug>57799</bug>: InputStream.available() was causing an IO operation
- to occur even in blocking mode, which caused problems with NIO2.
- (remm)
- </fix>
- <add>
- Extend the fix for <bug>58228</bug> to include
- <code>ServletContext.getRealPath()</code>. (markt)
- </add>
- <add>
- <bug>58486</bug>: Protect against two further possible memory leaks
- associated with XML parsing. (markt)
- </add>
- <fix>
- <bug>58490</bug>: Fixed NPE thrown when scanning for
- <code>javax.servlet.ServletContainerInitializer</code> in case the web
- application is not extracted. (violetagg)
- </fix>
- <scode>
- <bug>58497</bug>: Make <code>AbstractHttp11Processor</code> easy to
- extend. (markt)
- </scode>
- <fix>
- <bug>58508</bug>: Escape role names when generating associated MBeans in
- case the role name contains characters not permitted in an MBean name.
- (markt)
- </fix>
- <fix>
- <bug>58518</bug>: Correct a regression in the fix for <bug>56777</bug>
- that added support for URIs in config file locations. File paths on
- Windows could previously be specified with <code>\</code> or
- <code>/</code> as the separator. <bug>56777</bug> broke that. (markt)
- </fix>
- <fix>
- <bug>58519</bug>: Fix ISE thrown by web application classloader in some
- error conditions due to trying to call <code>initCause()</code> on a
- <code>ClassNotFoundException</code> which is not permitted. (markt)
- </fix>
- <fix>
- <bug>58534</bug>: Removed repeated conditional tests in
- <code>o.a.tomcat.websocket.pojo.PojoMethodMapping</code> and
- <code>o.a.tomcat.util.net.AprEndpoint</code>
- Patch provided by Anthony Whitford. (violetagg)
- </fix>
- <fix>
- <bug>58535</bug>: Use <code>Collections.reverseOrder</code>
- when a reverse ordering is needed. (violetagg)
- </fix>
- <fix>
- <bug>58537</bug>, <bug>58546</bug>: Some of the inner classes in
- <code>o.a.catalina.valves.ExtendedAccessLogValve</code>
- and <code>o.a.tomcat.util.net.SecureNio2Channel</code>
- are made static.
- Patch provided by Anthony Whitford. (violetagg)
- </fix>
- <fix>
- <bug>58540</bug>: Removed unused code from
- <code>o.a.catalina.connector.Request</code>.
- Patch provided by Anthony Whitford. (violetagg)
- </fix>
- <fix>
- <bug>58541</bug>, <bug>58544</bug>: It is more efficient to call
- <code>Integer.toString(int)</code> instead of
- <code>Integer.valueOf(int).toString()</code> when only a string
- representation of a primitive is needed. Based on a patch provided by
- Anthony Whitford. (violetagg)
- </fix>
- <fix>
- <bug>58541</bug>, <bug>58547</bug>: It is more efficient to call
- <code>valueOf(...)</code> instead of Number constructor. Based on a
- patch provided by Anthony Whitford. (violetagg)
- </fix>
- <fix>
- <bug>58545</bug>: In some use cases it is more efficient to use
- <code>Map.entrySet()</code> instead of <code>Map.keySet()</code>
- Based on a patch provided by Anthony Whitford. (violetagg)
- </fix>
<fix>
- Ensure that <code>ServletRequest.getContentLengthLong</code> is used
- instead of <code>ServletRequest.getContentLength</code> for servlets and
- valves provided by Tomcat. The API is available since Servlet
- specification 3.1. (violetagg)
+ <bug>58283</bug>: Change the default download location for libraries
+ during the build process from <code>/usr/share/java</code> to
+ <code>${user.home}/temp</code>. Patch provided by Ahmed Hosni. (markt)
</fix>
- <add>
- Add a new RestCsrfPreventionFilter that provides basic CSRF protection
- for REST APIs. (violetagg)
- </add>
<fix>
- <bug>58578</bug>: Avoid NPE accessing cookies during access logging
- for request that had no context mapping. (remm)
+ <bug>59031</bug>: When using the Windows uninstaller, do not remove the
+ contents of any directories that have been symlinked into the Tomcat
+ directory structure. (markt)
</fix>
- <fix>
- Avoid UnsupportedOperationException when releasing an user-provided
- URLStreamHandlerFactory. Patch provided by Cristian Talau. (violetagg)
- </fix>
- <fix>
- <bug>58581</bug>: If a custom error page fails, fall back to the
- standard error page rather than throwing an NPE. Based on a patch by
- Huxing Zhang. (markt)
- </fix>
- <fix>
- <bug>58582</bug>: Combined realm should perform background processing
- on its sub-realms. Based upon a patch provided by Aidan. (schultz)
- </fix>
- <fix>
- Handle the unlikely case where different versions of a web application
- are deployed with different session settings. (markt)
- </fix>
- <add>
- Add a new Context option, enabled by default, that enables an additional
- check that a client provided session ID is in use in at least one other
- web application before allowing it to be used as the ID for a new
- session in the current web application. (markt)
- </add>
- <add>
- Add support for DIGEST authentication to the JNDIRealm. Based on a patch
- by Alexis Hassler. (markt)
- </add>
- <fix>
- <bug>58603</bug>: Ensure that
- <code>HttpServletRequest.getRequestURL()</code> returns the correct
- value when using the <code>RemoteIpFilter</code>. (markt)
- </fix>
- <fix>
- Ensure that in an embedded Tomcat the logging configuration is
- not lost during garbage collection. (violetagg)
- </fix>
- <add>
- Move the functionality that provides redirects for context roots and
- directories where a trailing <code>/</code> is added from the Mapper to
- the <code>DefaultServlet</code>. This enables such requests to be
- processed by any configured Valves and Filters before the redirect is
- made. This behaviour is configurable via the
- <code>mapperContextRootRedirectEnabled</code> and
- <code>mapperDirectoryRedirectEnabled</code> attributes of the Context
- which may be used to restore the previous behaviour. (markt)
- </add>
- </changelog>
- </subsection>
- <subsection name="Coyote">
- <changelog>
- <fix>
- Cancel pending blocking IO operation following a timeout in the NIO2
- connector. (remm)
- </fix>
- <fix>
- Add instance manager support for upgrade handlers, and set context
- class loader. (remm)
- </fix>
- <update>
- Synchronize OpenSSL to JSSE cipher mapping to recent OpenSSL changes. In
- particular, <code>TLSv1.0</code> is now an alias for those ciphers that
- require TLSv1 and will not work with SSLv3. <code>TLSv1</code> remains
- an alias for <code>SSLv3</code>. (markt)
- </update>
- </changelog>
- </subsection>
- <subsection name="Jasper">
- <changelog>
- <add>
- Deprecate the <code>STRICT_QUOTE_ESCAPING</code> system property and
- replace it with an initialisation parameter for the JSP Servlet. This
- enables per web application control of this configuration setting.
- (markt)
- </add>
- </changelog>
- </subsection>
- <subsection name="Cluster">
- <changelog>
- <fix>
- Optimize the session lock range in DeltaManager.requestCompleted.
- (kfujino)
- </fix>
- <fix>
- Enable an explicit configuration of local member in the static cluster
- membership. (kfujino)
- </fix>
- </changelog>
- </subsection>
- <subsection name="Tribes">
- <changelog>
- <scode>
- Distinguish the handling of the shutdown payload and member verification
- clearly. When handling shutdown payload, verification completion message
- is not required. (kfujino)
- </scode>
- <fix>
- When starting the <code>StaticMembershipInterceptor</code>,
- <code>StaticMembershipInterceptor</code> checks the required
- Interceptors. If the required Interceptor does not exist, it issues
- warning logs. (kfujino)
- </fix>
- </changelog>
- </subsection>
- <subsection name="WebSocket">
- <changelog>
- <fix>
- Use instance manager for server endpoint instances. (remm)
- </fix>
- </changelog>
- </subsection>
- <subsection name="Web applications">
- <changelog>
- <add>
- Make it clear in the documentation for the CGI servlet that the debug
- page is not considered secure and should not be used in production.
- (markt)
- </add>
- <fix>
- The <code>domain</code> attribute of <code>StaticMember</code> is not
- required but optional. (kfujino)
- </fix>
- </changelog>
- </subsection>
- <subsection name="jdbc-pool">
- <changelog>
- <fix>
- <bug>58489</bug>: Correct QueryStatsComparator to hold up the
- general contract for Comparator. (fschumacher)
- </fix>
- <fix>
- When creating a <code>QueryStats</code> object, ensure that
- <code>maxQueries</code> is checked. If <code>maxQueries</code> is a
- value less than or equal to 0, <code>QueryStats</code> are never
- created. (kfujino)
- </fix>
- </changelog>
- </subsection>
- <subsection name="Other">
- <changelog>
- <update>
- Update optional Checkstyle library to 6.12.1. (kkolinko)
- </update>
- <add>
- Add support for creating a FindBugs report when building Tomcat. It
- is disabled by default. (violetagg)
- </add>
- </changelog>
- </subsection>
-</section>
-<section name="Tomcat 8.0.28 (markt)" rtext="2015-10-12">
- <subsection name="Catalina">
- <changelog>
- <add>
- Add support for the custom <code>classpath</code> protocol in URLs. It
- an be used anywhere Tomcat accepts a URL for a configuration parameter.
- (markt)
- </add>
- <fix>
- <bug>56777</bug>: Allow file based configuration resources (user
- database, certificate revocation lists, keystores and trust stores) to
- be configured using URLs as well as files. (markt)
- </fix>
- <fix>
- Perform null-checking on input and stored credentials in all Realms
- before passing credentials off to CredentialHandlers for matching.
- (schultz)
- </fix>
- </changelog>
- </subsection>
- <subsection name="Coyote">
- <changelog>
- <update>
- Add the new ciphers from RFC6655 and RFC7251 to the OpenSSL to JSSE
- cipher mapping. (markt)
- </update>
- <update>
- Remove DES, RC2 and RC4 from DEFAULT for the OpenSSL to JSSE cipher
- mapping to align with the OpenSSL development branch. (markt)
- </update>
- </changelog>
- </subsection>
- <subsection name="Jasper">
- <changelog>
- <fix>
- Improve the error message when JSP parser encounters an error parsing an
- attribute value. (markt)
- </fix>
- </changelog>
- </subsection>
- <subsection name="Web applications">
- <changelog>
- <update>
- <bug>58474</bug>: Provide a reference to the differences between
- <code>CATALINA_HOME</code> and <code>CATALINA_BASE</code> in the sample
- application that is part of the documentation web application. (markt)
- </update>
- </changelog>
- </subsection>
- <subsection name="Extras">
- <changelog>
- <fix>
- Ensure JULI adapters does not include the LogFactoryImpl class. Patch
- provided by Benjamin Gandon. (markt)
- </fix>
- </changelog>
- </subsection>
-</section>
-<section name="Tomcat 8.0.27 (markt)" rtext="2015-10-01">
- <subsection name="Catalina">
- <changelog>
- <fix>
- <bug>58187</bug>: Correct a regression in the fix for <bug>57765</bug>
- that meant that deployment of web applications deployed via the Manager
- application was delayed until the next execution of the automatic
- deployment background process. (markt)
- </fix>
- <fix>
- <bug>58284</bug>: Correctly implement session serialization so
- non-serializable attributes are skipped with a warning. Patch provided
- by Andrew Shore. (markt)
- </fix>
- <fix>
- <bug>58313</bug>: Fix concurrent access of encoders map when clearing
- encoders prior to switch to async. (markt)
- </fix>
- <fix>
- <bug>58320</bug>: Fix concurrent access of request attributes which is
- possible during asynchronous processing. (markt)
- </fix>
- <fix>
- <bug>58352</bug>: Always trigger a thread dump if Tomcat fails to stop
- gracefully from <code>catalina.sh</code> even if using
- <code>-force</code>. Patch provided by Alexandre Garnier. (markt)
- </fix>
- <fix>
- <bug>58368</bug>: Fix a rare data race in the code that obtains the
- <code>ApplicationFilterFactory</code> instance. (markt)
- </fix>
- <fix>
- <bug>58369</bug>: Fix a rare data race in the code that obtains the
- CookieProcessor for a StandardContext instance. (markt)
- </fix>
- <fix>
- Ensure the JAASRealm uses the configured CredentialHandler. (markt)
- </fix>
- <fix>
- <bug>58372</bug>: Fix rare data races closed and suspended flags that
- could be triggered by async and/or comet processing. (markt)
- </fix>
- <fix>
- <bug>58373</bug>: Fix rare data race with the application event
- listeners for StandardContext. (markt)
- </fix>
- <fix>
- <bug>58374</bug>: Fix a rare data race in the AsyncContext
- implementation for access to the internal Tomcat request object to which
- it holds a reference. (markt)
- </fix>
- <fix>
- <bug>58380</bug>: Fix two rare data races in the standard session
- implementation on the flag that tracks if the session is new and on the
- field that tracks the maximum inactive period. (markt)
- </fix>
- <fix>
- <bug>58385</bug>: Fix a rare data race in the internal flag Tomcat uses
- to keep track of whether or not a request is being used for Comet
- processing. (markt)
- </fix>
- <fix>
- <bug>58394</bug>: Fix a rare data race in Mapper when adding or removing
- a host. (markt)
- </fix>
- <fix>
- <bug>58398</bug>: Fix a rare data race in <code>LifecycleSupport</code>.
- (markt)
- </fix>
- <fix>
- <bug>58412</bug>: Ensure that the <code>AsyncFileHandler</code> has the
- source class and method name available for logging. (fschumacher)
- </fix>
- <fix>
- <bug>58416</bug>: Correctly detect when a forced stop fails to stop
- Tomcat because the Tomcat process is waiting on some system call or is
- uninterruptible. (markt)
- </fix>
- <fix>
- <bug>58436</bug>: Fix some rare data races in JULI's
- <code>ClassLoaderLogManager</code> during shutdown. (markt)
- </fix>
- <fix>
- <bug>58845</bug>: Fix off-by one error in calculation of valid
- characters in a cookie domain. Patch provided by Thorsten Ehlers.
- (markt)
- </fix>
- </changelog>
- </subsection>
- <subsection name="Coyote">
- <changelog>
- <fix>
- Correct some edge cases in <code>RequestUtil.normalize()</code>. (markt)
- </fix>
- <fix>
- <bug>58275</bug>: The IBM JREs accept cipher suite names starting with
- <code>TLS_</code> or <code>SSL_</code> but when listing the supported
- cipher suites only the <code>SSL_</code> version is reported. This can
- break Tomcat's check that at least one requested cipher suite is
- supported. Tomcat now includes a work-around so either form of the
- cipher suite name can be used when running on an IBM JRE. (markt)
- </fix>
- <fix>
- <bug>58357</bug>: For reasons not currently understood when the
- APR/native connector is used with OpenSSL reads can return an error code
- when there is no apparent error. This was work-around for HTTP upgrade
- connections by treating this as <code>EAGAIN</code>. The same fix has
- now been applied to the standard HTTP connector. (markt)
- </fix>
- <scode>
- Minor clean-up in NIO2 SSL handshake code to address some theoretical
- concurrency issues. (markt)
- </scode>
- <fix>
- <bug>58367</bug>: Fix a rare data race in the code that obtains the
- reason phrase for a given HTTP response code. (markt)
- </fix>
- <fix>
- <bug>58370</bug>: Fix a rare data race in the connector shutdown code.
- (markt)
- </fix>
- <fix>
- <bug>58371</bug>: Fix a rare data race when accessing request URI in
- String form when switching from non-async to async due to early
- triggering of the gathering of request statistics. (markt)
- </fix>
- <fix>
- <bug>58375</bug>: Fix a rare data race on the internal flag Tomcat uses
- to mark a response as committed. (markt)
- </fix>
- <fix>
- <bug>58377</bug>: Fix a rare data race on the internal flag Tomcat uses
- to mark a request as using HTTP keep-alive when switching to
- asynchronous processing. (markt)
- </fix>
- <fix>
- <bug>58379</bug>: Fix a rare data race on the internal reference Tomcat
- retains to the socket when switching to asynchronous processing. (markt)
- </fix>
- <fix>
- <bug>58387</bug>: Fix a rare data race when closing Comet connections.
- (markt)
- </fix>
- <fix>
- <bug>58388</bug>: Fix a data race when determining if Comet processing
- is occurring on a container or non-container thread. (markt)
- </fix>
- <fix>
- <bug>58389</bug>: Fix a rare data race while shutting down the thread
- pools on Connector stop. (markt)
- </fix>
- <scode>
- Clean up use of error flag on socket wrapper prompted by
- <bug>58390</bug>. (markt)
- </scode>
- <scode>
- Remove some unnecessary code from the NIO Poller and fix
- <bug>58396</bug> as a side-effect. (markt)
- </scode>
- <fix>
- <bug>57799</bug>: Remove useless sendfile check for NIO SSL. (remm)
- </fix>
- </changelog>
- </subsection>
- <subsection name="Jasper">
- <changelog>
- <fix>
- <bug>57136</bug>: Correct a regression in the previous fix for this
- issue. <code>\${</code> should only be an escape for <code>${</code>
- within an EL expression. Within a JSP page <code>\$</code> should be an
- escape for <code>$</code>. The EL specification applies when parsing the
- expression delimited by <code>${</code> and <code>}</code>. Parsing of
- the delimiting <code>${</code> and <code>}</code> is the responsibility
- of the JSP specification. (markt)
- </fix>
- <fix>
- <bug>58296</bug>: Fix a memory leak in the JSP unloading feature that
- meant that using a value other than <code>-1</code> for
- <code>maxLoadedJsps</code> triggered a memory leak once the limit was
- reached. (markt)
- </fix>
- <fix>
- <bug>58327</bug>: Cache the expression string for value expression
- literals since it is frequently used and may be expensive to evaluate.
- Patch provided by Andreas Kohn. (markt)
- </fix>
- <fix>
- <bug>58340</bug>: Improve error reporting for tag files packaged in
- JARs. (markt)
- </fix>
- <fix>
- <bug>58424</bug>: When parsing TLD files, allow whitespace around
- boolean configuration values. (schultz)
- </fix>
- <fix>
- Fix a possible resource leak reported by coverity scan. (fschumacher)
- </fix>
- <fix>
- <bug>58427</bug>: Enforce the JSP specification defined limitations of
- which elements are allowed in an implicit.tld file. (markt)
- </fix>
- <fix>
- <bug>58444</bug>: Ensure that JSPs work with any custom base class that
- meets the requirements defined in the JSP specification without
- requiring that base class to implement Tomcat specific code. (markt)
- </fix>
- </changelog>
- </subsection>
- <subsection name="Cluster">
- <changelog>
- <fix>
- Fix a default clusterListeners in <code>SimpleTcpCluster</code>. The
- optimal default value is different for each session manager.
- <code>ClusterSessionListener</code> is never used in
- <code>BackupManager</code>. (kfujino)
- </fix>
- <fix>
- Correct log messages in case of using <code>BackupManager</code>.
- (kfujino)
- </fix>
- </changelog>
- </subsection>
- <subsection name="WebSocket">
- <changelog>
- <fix>
- <bug>58342</bug>: Fix a copy and paste error that meant MessageHandler
- removal could fail for binary and pong MessageHandlers. Patch provided
- by DJ. (markt)
- </fix>
- <fix>
- Data races detected by RV-Predict, mostly caused by completion handlers
- running in separate threads. (markt)
- </fix>
- <fix>
- <bug>58414</bug>: Correctly handle sending zero length messages when
- using per message deflate. (markt)
- </fix>
- </changelog>
- </subsection>
- <subsection name="Web applications">
- <changelog>
- <fix>
- Correct documentation for cluster-howto. (kfujino)
- </fix>
- <fix>
- Add missing documentation for property <code>alwaysAddExpires</code> for
- the <code>LegacyCookieProcessor</code>. (markt)
- </fix>
- </changelog>
- </subsection>
- <subsection name="Tribes">
- <changelog>
- <add>
- Add support for configurations of <code>ChannelListener</code> and
- <code>MembershipListener</code> in server.xml. (kfujino)
- </add>
- <fix>
- Correct log messages in case of using <code>ReplicatedMap</code>.
- (kfujino)
- </fix>
- <fix>
- <bug>58381</bug>: Fix a rare data race in the <code>NioReceiver</code>.
- (markt)
- </fix>
- <fix>
- <bug>58382</bug>: Fix multiple rare data races in the default membership
- implementation. (markt)
- </fix>
- <fix>
- <bug>58383</bug>: Fix a data race in <code>SenderState</code>. (markt)
- </fix>
- <fix>
- <bug>58386</bug>: Fix a data race in <code>ObjectReader</code>. (markt)
- </fix>
- <fix>
- <bug>58391</bug>: Fix multiple data races in
- <code>NonBlockingCoordinator</code>, most of which were associated with
- ensuring that log messages contained the correct information. (markt)
- </fix>
- <fix>
- <bug>58392</bug>: Fix a data race in
- <code>DomainFilterInterceptor</code>. (markt)
- </fix>
- <fix>
- <bug>58393</bug>: Fix a data race on the listener in
- <code>McastService</code>. (markt)
- </fix>
- <fix>
- <bug>58395</bug>: Fix multiple data races in <code>MemberImpl</code>
- that were likely to cause issues if certain properties were updated
- concurrently (such updates are unlikely in normal usage). (markt)
- </fix>
- <scode>
- Remove some unnecessary code from <code>PooledParallelSender</code> and
- fix <bug>58397</bug>. (markt)
- </scode>
- </changelog>
- </subsection>
- <subsection name="jdbc-pool">
- <changelog>
- <fix>
- Make sure the pool has been properly configured when attributes that
- related to the pool size are changed via JMX. (kfujino)
- </fix>
- </changelog>
- </subsection>
- <subsection name="Other">
- <changelog>
- <fix>
- Ensure logging works for all tests in a class rather than just the first
- one executed. (markt)
- </fix>
- <add>
- <bug>58344</bug>: Add build properties to enable tests to be executed
- against alternative binaries. Based on a patch by Petr Sumbera. (markt)
- </add>
- </changelog>
- </subsection>
-</section>
-<section name="Tomcat 8.0.26 (markt)" rtext="2015-08-21">
- <subsection name="Web applications">
- <changelog>
- <add>
- <bug>58255</bug>: Document the Semaphore valve. Patch provided by
- Kyohei Nakamura. (markt)
- </add>
- </changelog>
- </subsection>
-</section>
-<section name="Tomcat 8.0.25 (markt)" rtext="not released">
- <subsection name="Catalina">
- <changelog>
- <fix>
- Make the WAR manifest file available for WebResource instances from an
- unpacked WAR in the same way the manifest is available if the WAR is not
- unpacked. (markt)
- </fix>
- <fix>
- Ensure that only <code>/WEB-INF/classes/</code> and
- <code>/WEB-INF/lib/</code> are excluded from the web resource caching.
- (Resources loaded from these locations are cached by the web application
- class loader.) (markt)
- </fix>
- <add>
- <bug>57741</bug>: Enable the CGI servlet to use the standard error page
- mechanism. Note that if the CGI servlet's debug init parameter is
- set to 10 or higher then the standard error page mechanism will be
- bypassed and a debug response generated by the CGI servlet will be
- returned instead. (markt)
- </add>
- <fix>
- <bug>58031</bug>: Make the (first) reason parameter parsing failed
- available as a request attribute and then use it to provide a better
- status code via the FailedRequstFilter (if configured). (markt)
- </fix>
- <fix>
- <bug>58086</bug>: Correct a regression in the fix for 58086 that
- incorrectly handled WAR URLs. (violetagg)
- </fix>
- <fix>
- <bug>58096</bug>: Classes loaded from <code>/WEB-INF/classes/</code>
- should use that directory as their code base. (markt)
- </fix>
- <fix>
- Fix possible resource leaks by closing streams properly.
- Issues reported by Coverity Scan. (violetagg)
- </fix>
- <fix>
- <bug>58116</bug>: Fix regression in the fix for <bug>57281</bug> that
- broke Comet support when running under a security manager. Based on a
- patch provided by Johno Crawford. (markt)
- </fix>
- <fix>
- <bug>58125</bug>: Avoid a possible <code>ClassCircularityError</code>
- when running under a security manager. (markt)
- </fix>
- <fix>
- <bug>58179</bug>: Fix a thread safety issues that could mean concurrent
- threads setting the same attribute on a <code>ServletContext</code>
- could both see <code>null</code> as the old value. (markt)
- </fix>
- <fix>
- Allow web archives bigger than 2G to be deployed using ANT tasks.
- (violetagg)
- </fix>
- <fix>
- <bug>58192</bug>: Correct a regression in the previous fix for
- <bug>58023</bug>. Ensure that classes are associated with their manifest
- even if the class file is first read (and cached) without the manifest.
- (markt)
- </fix>
- <fix>
- Fix thread safety issue in the <code>AsyncContext</code> implementation
- that meant a sequence of <code>start();dispatch();</code> calls using
- non-container threads could result in a previous dispatch interfering
- with a subsequent start. (markt)
- </fix>
- <fix>
- <bug>58228</bug>: Make behaviour of
- <code>ServletContext.getResource()</code> and
- <code>ServletContext.getResourceAsStream()</code> consistent with each
- other and the expected behaviour of the GET_RESOURCE_REQUIRE_SLASH
- system property. (markt)
- </fix>
- <fix>
- <bug>58230</bug>: Fix input stream corruption if non-blocking I/O is
- used and the first read is made immediately after the switch to async
- mode rather than in response to <code>onDataAvaiable()</code> and that
- read does not read all the available data. (markt)
- </fix>
- <fix>
- Ensure that <code>log4javascript*.jar</code> was not excluded from the
- standard JAR scanning by default. (markt)
- </fix>
- </changelog>
- </subsection>
- <subsection name="Coyote">
- <changelog>
- <fix>
- <bug>57943</bug>: Prevent the same socket being added to the cache
- twice. Patch based on analysis by Ian Luo / Sun Qi. (markt)
- </fix>
- <fix>
- Add <code>text/javascript,application/javascript</code> to the default
- list of compressable MIME types. (violetagg)
- </fix>
- <fix>
- <bug>58103</bug>: When pipelining requests, and the previous request was
- an async request, ensure that the socket is removed from the waiting
- requests so that the async timeout thread doesn't process it during the
- next request. (markt)
- </fix>
- <fix>
- <bug>58151</bug>: Correctly handle EOF in the AJP APR/native connector
- to prevent the connector entering a loop and generate excessive CPU
- load. (markt)
- </fix>
- <fix>
- In the AJP and HTTP NIO connectors, ensure that the socket timeout is
- correctly set before adding the socket back to the poller for read.
- (markt)
- </fix>
- <fix>
- <bug>58157</bug>: Ensure that the handling of async timeouts does not
- result in an unnecessary dispatch to a container thread that could
- result in the current socket being added to the Poller multiple times
- with multiple attempts to process the same event for the same socket.
- (markt)
- </fix>
- <fix>
- Correct a couple of edge cases in <code>RequestUtil.normalize()</code>.
- (markt)
- </fix>
- </changelog>
- </subsection>
- <subsection name="Jasper">
- <changelog>
- <fix>
- <bug>58110</bug>: Like scriptlet sections, declaration sections of JSP
- pages have a one-to-one mapping of lines to the generated .java file.
- Use this information to provide more accurate error messages if a
- compilation error occurs in a declaration section. (markt)
- </fix>
- <fix>
- <bug>58119</bug>: When tags are compiled they must be placed in the
- org/apache/jsp/tag/web directory. Correct a regression in the fix for
- 52725. (violetagg)
- </fix>
- <fix>
- Fix a resource leak in JspC identified by Eclipse. (markt)
- </fix>
- <fix>
- <bug>58178</bug>: Expressions in a tag file should use the tag
- file's <code>PageContext</code> rather than that of the containing
- page. (markt)
- </fix>
- <fix>
- Following on from the fix for <bug>58178</bug>, expressions in a tag
- file should use the tag file's imports rather than those of the
- containing page. (markt)
- </fix>
- </changelog>
- </subsection>
- <subsection name="WebSocket">
- <changelog>
- <fix>
- <bug>58166</bug>: Allow applications to send close codes in the range
- 3000-4999 inclusive. (markt)
- </fix>
- <fix>
- <bug>58232</bug>: Avoid possible NPE when adding endpoints
- programmatically to the
- <code>javax.websocket.server.ServerContainer</code>.
- Based on a patch provided by bastian.(violetagg)
- </fix>
- </changelog>
- </subsection>
- <subsection name="Web applications">
- <changelog>
- <fix>
- Correct the incorrect document of <code>QueryTimeoutInterceptor</code>.
- The setting value is not in milliseconds but in seconds. (kfujino)
- </fix>
- <fix>
- <bug>58112</bug>: Update the documentation for using the Catalina tasks
- in an Apache Ant build file. (markt)
- </fix>
- <fix>
- Improve the Javadoc for some of the APR socket read functions that have
- inconsistent behaviour for return values. (markt)
- </fix>
- </changelog>
- </subsection>
- <subsection name="jdbc-pool">
- <changelog>
- <fix>
- <bug>58042</bug>: The default value of <code>logFailed</code> attribute
- of <code>SlowQueryReport</code> is changed to <code>false</code> so that
- the failed queries are not logged by default. (kfujino)
- </fix>
- <fix>
- Fix potential NPE in <code>QueryTimeoutInterceptor</code>. (kfujino)
- </fix>
- <fix>
- Add support for stopping the pool cleaner via JMX. (kfujino)
- </fix>
- <fix>
- The <code>fairness</code> attribute and
- <code>ignoreExceptionOnPreLoad</code> attribute do not allow a change
- via JMX. (kfujino)
- </fix>
- <fix>
- If the <code>timeBetweenEvictionRunsMillis</code> attribute is changed
- via jmx, it should restart the pool cleaner because this attribute
- affects the execution interval of the pool cleaner. (kfujino)
- </fix>
- <fix>
- Eliminate the dependence on <code>maxActive</code> of busy queues and
- idle queue in order to enable the expansion of the pool size via JMX.
- (kfujino)
- </fix>
- </changelog>
- </subsection>
- <subsection name="Other">
- <changelog>
- <update>
- Update optional Checkstyle library to 6.8.1. (kkolinko)
- </update>
- <fix>
- Update sample Eclipse IDE configuration to exclude test/webapp* and
- similar paths from compiler sourcepath. (kkolinko)
- </fix>
- <update>
- Update package renamed Apache Commons Pool to Commons Pool 2.4.2.
- (markt)
- </update>
- <update>
- Update package renamed Apache Commons DBCP to Commons DBCP 2.1.1.
- (markt)
- </update>
- <add>
- Support the use of the <code>threads</code> attribute on Ant's
- junit task. Note that using this with a value of greater than one will
- disable Cobertura code coverage. (markt)
- </add>
- </changelog>
- </subsection>
-</section>
-<section name="Tomcat 8.0.24 (markt)" rtext="2015-07-06">
- <subsection name="Catalina">
- <changelog>
- <fix>
- <bug>57938</bug>: Correctly handle empty form fields when a form is
- submitted as <code>multipart/form-data</code>, the
- <code>maxPostSize</code> attribute of the Connector has been set to a
- negative value and the Context has been configured with a value of
- <code>true</code> for <code>allowCasualMultipartParsing</code>. The
- meaning of the value zero for the <code>maxPostSize</code> has also been
- changed to mean a limit of zero rather than no limit to align it with
- <code>maxSavePostSize</code> and to be more intuitive. (markt)
- </fix>
- <fix>
- <bug>57977</bug>: Correctly bind and unbind the web application class
- loader during execution of the PersistentValve. (markt)
- </fix>
- <fix>
- Remove some unnecessary code from the web application class loader and
- deprecate the now unused <code>validate()</code> method since the
- requirements of SRV.10.7.2 are met using cleaner code in
- <code>loadClass(String, boolean)</code> and <code>filter()</code>.
- (markt)
- </fix>
- <fix>
- Correct a bug that prevented the web application class loader's
- <code>filter()</code> from working correctly. It only returned
- <code>true</code> for classes in sub-packages of the listed packages,
- but not classes located in the packages themselves. (markt)
- </fix>
- <fix>
- Add the WebSocket API classes to the list of classes that the web
- application class loader will always delegate to its parent for loading
- first. (markt)
- </fix>
- <fix>
- <bug>58015</bug>: Ensure that whenever the web application class loader
- checks to see if it should delegate first, it also checks the result
- of the <code>filter()</code> method which may indicate that it should
- always delegate first for the current class/resource regardless of the
- value of the delegate configuration option. (markt)
- </fix>
- <fix>
- <bug>58023</bug>: Fix potentially excessive memory usage due to
- unnecessary caching of JAR manifests in the web application class
- loader. (markt)
- </fix>
- <fix>
- <bug>57700</bug>: Ensure that Container event
- <code>ADD_CHILD_EVENT</code> will be sent in all cases. (violetagg)
- </fix>
- <fix>
- <bug>58086</bug>: Ensure that WAR URLs are handled properly when using
- ANT for web application deployment. Based on a patch provided by Lukasz
- Jader. (violetagg)
- </fix>
- <fix>
- Fix CredentialHandler element handling in storeconfig. (remm)
- </fix>
- </changelog>
- </subsection>
- <subsection name="Coyote">
- <changelog>
- <fix>
- <bug>57265</bug>: Further fix to address a potential threading issue
- when sendfile is used in conjunction with TLS. (markt)
- </fix>
- <fix>
- <bug>57936</bug>: Improve robustness of the acceptor thread count
- parameter for NIO2, since it must be set to 1. Submitted by
- Oliver Kant. (remm)
- </fix>
- <add>
- <bug>57943</bug>: Added a work-around to catch
- <code>ConcurrentModificationException</code>s during Poller timeout
- processing that were causing the Poller thread to stop. The root cause
- of these exceptions is currently unknown. (markt)
- </add>
- <fix>
- <bug>57944</bug>: Ensure that if non-blocking I/O listeners are set on
- a non-container thread that the expected listener events are still
- triggered. (markt)
- </fix>
- <fix>
- Fix possible very long (1000 seconds) timeout with APR/native connector.
- (markt)
- </fix>
- <add>
- Support "-" separator in the SSLProtocol configuration of the
- APR/native connector for protocol exclusion. (rjung)
- </add>
- <fix>
- <bug>58004</bug>: Fix AJP buffering output data even in blocking mode.
- (remm)
- </fix>
- </changelog>
- </subsection>
- <subsection name="WebSocket">
- <changelog>
- <fix>
- <bug>57969</bug>: Provide path parameters to POJO via per session
- <code>javax.websocket.server.ServerEndpointConfig</code> as they vary
- between different requests. (violetagg)
- </fix>
- <fix>
- <bug>57974</bug>: Session.getOpenSessions should return all sessions
- associated with a given endpoint instance, rather than all sessions
- from the endpoint class. (remm)
- </fix>
- </changelog>
- </subsection>
- <subsection name="Web applications">
- <changelog>
- <fix>
- <bug>57282</bug>: Update request processing sequence diagrams. Updated
- diagrams provided by Stephen Chen. (markt)
- </fix>
- <fix>
- <bug>57971</bug>: Correct the documentation for the cluster
- configuration setting <code>recoverySleepTime</code>. (markt)
- </fix>
- <add>
- <bug>57758</bug>: Add document of <code>testOnConnect</code> attribute
- in jdbc-pool doc. (kfujino)
- </add>
- <add>
- Add description of <code>validatorClassName</code> attribute to testXXXX
- attributes in jdbc-pool docs. (kfujino)
- </add>
- </changelog>
- </subsection>
- <subsection name="Tribes">
- <changelog>
- <scode>
- Use <code>StringManager</code> to provide i18n support in the
- <code>org.apache.catalina.tribes</code> packages. (kfujino)
- </scode>
- <fix>
- Do not set the nodes that failed to replication to the backup nodes.
- Ensure that the nodes that the data has been successfully replicated are
- set to the backup node. (kfujino)
- </fix>
- <fix>
- When failed to replication, rather than all member is handled as a
- failed member, exclude the failure members from backup members.
- (kfujino)
- </fix>
- </changelog>
- </subsection>
- <subsection name="jdbc-pool">
- <changelog>
- <fix>
- Refactoring of the <code>removeOldest</code> method in
- <code>SlowQueryReport</code> to behave as expected. (kfujino)
- </fix>
- <fix>
- <bug>57783</bug>: Fix <code>NullPointerException</code> in
- <code>SlowQueryReport</code>. To avoid this NPE, Refactor
- <code>SlowQueryReport#removeOldest</code> and handle the abandoned
- connection properly. (kfujino)
- </fix>
- <fix>
- <bug>58042</bug>: In <code>SlowQueryReportJmx</code>, the
- <code>LogSlow</code> and <code>logFailed</code> attributes that
- inherited from <code>SlowQueryReport</code> are used as a condition of
- whether JMX notifications are sent. (kfujino)
- </fix>
- <fix>
- Ensure that specified <code>Boolean</code> attribute values of
- <code>SlowQueryReport</code> reflect correctly. The <code>LogSlow</code>
- and the <code>logFailed</code> are not system property, these are
- attributes of <code>SlowQueryReport</code>. (kfujino)
- </fix>
- </changelog>
- </subsection>
- <subsection name="Other">
- <changelog>
- <update>
- Update package renamed Apache Commons BCEL to r1682271 to pick up some
- some code clean up. (markt)
- </update>
- <update>
- Update package renamed Apache Commons DBCP to r1682314 to pick up the
- DBCP 2.1 release and additional fixes since then. (markt)
- </update>
- <update>
- Update package renamed Apache Commons Pool to the 2.4 release. (markt)
- </update>
- <update>
- Update package renamed Apache Commons File upload to r1682322 to pick up
- the post 1.3.1 fixes. (markt)
- </update>
- <update>
- Update package renamed Apache Commons Codec to r1682326. No functional
- changes. Javadoc only. (markt)
- </update>
- <update>
- Update optional Checkstyle library to 6.7. (kkolinko)
- </update>
- </changelog>
- </subsection>
-</section>
-<section name="Tomcat 8.0.23 (markt)" rtext="2015-05-22">
- <subsection name="Catalina">
- <changelog>
- <add>
- <bug>54618</bug>: Add a new <code>HttpHeaderSecurityFilter</code> that
- adds the <code>Strict-Transport-Security</code>,
- <code>X-Frame-Options</code> and <code>X-Content-Type-Options</code>
- HTTP headers to the response. (markt)
- </add>
- <fix>
- <bug>57875</bug>: Add <code>javax.websocket.*</code> to the classes for
- which the web application class loader always delegates first. (markt)
- </fix>
- <fix>
- <bug>57871</bug>: Ensure that setting the
- <code>allowHttpSepsInV0</code> property of a
- <code>LegacyCookieProcessor</code> to false only prevents HTTP
- separators from being used without quotes. (markt)
- </fix>
- <fix>
- Add a workaround for issues with SPNEGO authentication when running on
- Java 8 update 40 and later. The workaround should be safe for earlier
- Java versions but it can be disabled with the
- <code>applyJava8u40Fix</code> attribute of the SPNEGO authenticator if
- necessary. (markt)
- </fix>
- <fix>
- <bug>57926</bug>: Restore the original <code>X-Forwarded-By</code> and
- <code>X-Forwarded-For</code> headers after processing by the
- <code>RemoteIPValve </code>. (markt)
- </fix>
- </changelog>
- </subsection>
- <subsection name="Coyote">
- <changelog>
- <fix>
- Follow up to previous fix that removed the behavior difference between
- NIO and NIO2 for SSL, which caused corruption with NIO2.
- (remm)
- </fix>
- <fix>
- <bug>57931</bug>: Ensure that TLS connections with the NIO or NIO2 HTTP
- connectors that experience issues during the handshake (e.g. missing or
- invalid client certificate) are closed cleanly and that the client
- receives the correct error code rather than simply closing the
- connection. (markt)
- </fix>
- </changelog>
- </subsection>
- <subsection name="Jasper">
- <changelog>
- <fix>
- <bug>56438</bug>: Add debug logging to TLD discovery that logs positive
- and negative results for JARs, resource paths and directories. Patch
- provided by VIN. (markt)
- </fix>
- <fix>
- <bug>57802</bug>: Correct the default implementation of
- <code>convertToType()</code> provided by
- <code>javax.el.ELResolver</code>. (markt)
- </fix>
- <fix>
- <bug>57887</bug>: Fix compilation of recursive tag files packaged in a
- JAR. (markt)
- </fix>
- </changelog>
- </subsection>
- <subsection name="Cluster">
- <changelog>
- <fix>
- Make sure that stream is closed after using it in
- <code>DeltaSession.applyDiff()</code>. (kfujino)
- </fix>
- <scode>
- Use <code>StringManager</code> to provide i18n support in the
- <code>org.apache.catalina.ha packages</code>. (kfujino)
- </scode>
- <scode>
- Add the context name to log messages when replication context failed to
- start. (kfujino)
- </scode>
- </changelog>
- </subsection>
- <subsection name="Web applications">
- <changelog>
- <fix>
- <bug>57875</bug>: Update the web application class loader documentation
- to reflect the more relaxed approach to SRV.10.7.2 in Tomcat 8 onwards.
- (markt)
- </fix>
- <fix>
- <bug>57896</bug>: Document system property
- <code>org.apache.tomcat.util.http.ServerCookie.PRESERVE_COOKIE_HEADER</code>
- that was introduced in Tomcat 8.0.0. (kkolinko)
- </fix>
- </changelog>
- </subsection>
- <subsection name="Tribes">
- <changelog>
- <fix>
- Ensure that the state transfer flag is updated to true only when the map
- states have been transferred correctly from existing map members.
- (kfujino)
- </fix>
- </changelog>
- </subsection>
- <subsection name="Other">
- <changelog>
- <update>
- Update optional Checkstyle library to 6.6. (kkolinko)
- </update>
- </changelog>
- </subsection>
-</section>
-<section name="Tomcat 8.0.22 (markt)" rtext="2015-05-05">
- <subsection name="Catalina">
- <changelog>
- <fix>
- <bug>57736</bug>: Change the format of the Tomcat specific URLs for
- resources inside JARs that are in turn packed in a WAR. The
- <code>^/</code> sequence has been replaced by <code>*/</code> so that
- the resulting URLs are compliant with RFC 2396 and do not trigger
- exceptions when converted to URIs. The old format will continue to be
- accepted. (markt)
- </fix>
- <fix>
- <bug>57752</bug>: Exclude non-cached resources from the Cache statistics
- for resource lookups. Patch provided by Adam Mlodzinski. (markt)
- </fix>
- <add>
- Allow logging of the remote port in the access log using the format
- pattern <code>%{remote}p</code>. (rjung)
- </add>
- <fix>
- <bug>57556</bug>: Refine the previous fix for this issue so that the
- real path returned only has a trailing separator if the requested path
- ended with <code>/</code>. (markt)
- </fix>
- <fix>
- <bug>57765</bug>: When checking last modified times as part of the
- automatic deployment process, account for the fact that
- <code>File.lastModified()</code> has a resolution of one second to
- ensure that if a file has been modified within the last second, the
- latest version of the file is always used. Note that a side-effect of
- this change is that files with modification times in the future are
- treated as if they are unmodified. (markt)
- </fix>
- <fix>
- Align redeploy resource modification checking with reload modification
- checking so that now, in both cases, a change in modification time
- rather than an increase in modification time is used to determine if the
- resource has changed. (markt)
- </fix>
- <fix>
- Cleanup <code>o.a.tomcat.util.digester.Digester</code> from debug
- messages that do not give any valuable information. Patch provided
- by Polina Genova. (violetagg)
- </fix>
- <fix>
- <bug>57772</bug>: When reloading a web application and a directory
- representing an expanded WAR needs to be deleted, delete the directory
- after the web application has been stopped rather than before to avoid
- potential ClassNotFoundExceptions. (markt)
- </fix>
- <fix>
- Fix wrong logger name of
- <code>org.apache.catalina.webresources.StandardRoot</code>. (kfujino)
- </fix>
- <fix>
- <bug>57801</bug>: Improve the error message in the start script in case
- the PID read from the PID file is already owned by a process. (rjung)
- </fix>
- <fix>
- <bug>57841</bug>: Improve error logging during web application start.
- (markt)
- </fix>
- <fix>
- <bug>57856</bug>: Ensure that any scheme/port changes implemented by the
- <code>RemoteIpFilter</code> also affect
- <code>HttpServletResponse.sendRedirect()</code>. (markt)
- </fix>
- <fix>
- <bug>57863</bug>: Fix the RewriteMap support in RewriteValve that did
- not use the correct key value to look up entries. Based on a patch
- provided by Tatsuya Bessho. (markt)
- </fix>
- </changelog>
- </subsection>
- <subsection name="Coyote">
- <changelog>
- <fix>
- <bug>57779</bug>: When an I/O error occurs on a non-container thread
- only dispatch to a container thread to handle the error if using Servlet
- 3+ asynchronous processing. This avoids potential deadlocks if an
- application is performing I/O on a non-container thread without using
- the Servlet 3+ asynchronous API. (markt)
- </fix>
- <scode>
- Remove the experimental support for SPDY. No current user agent supports
- the version of SPDY that the experiment targeted. Note: HTTP/2 support
- is under development for Tomcat 9 and may be back-ported to Tomcat 8
- once complete. (markt)
- </scode>
- <fix>
- Possible incomplete writes with SSL NIO2. (remm)
- </fix>
- <fix>
- Incorrect reads with SSL NIO2 caused by a bad strategy for handling IO
- differences between NIO and NIO2 that don't seem to be justified.
- (remm)
- </fix>
- <fix>
- After some errors, the pending flags could remain set when using SSL
- NIO2. (remm)
- </fix>
- <fix>
- <bug>57833</bug>: When using JKS based keystores for NIO or NIO2, ensure
- that the key alias is always converted to lower case since that is what
- JKS key stores expect. Based on a patch by Santosh Giri Govind M.
- (markt)
- </fix>
- <fix>
- <bug>57837</bug>: Add <code>text/css</code> to the default list of
- compressable MIME types. (markt)
- </fix>
- </changelog>
- </subsection>
- <subsection name="Jasper">
- <changelog>
- <fix>
- <bug>57845</bug>: Ensure that, if the same JSP is accessed directly and
- via a <code><jsp-file></code> declaration in web.xml, updates to
- the JSP are visible (subject to the normal rules on re-compilation)
- regardless of how the JSP is accessed. (markt)
- </fix>
- <fix>
- <bug>57855</bug>: Explicitly handle the case where a
- <code>MethodExpression</code> is invoked with null or the wrong number
- of parameters. Rather than failing with an
- <code>ArrayIndexOutOfBoundsException</code> or a
- <code>NullPointerException</code> throw an
- <code>IllegalArgumentException</code> with a useful error message.
- (markt)
- </fix>
- </changelog>
- </subsection>
- <subsection name="Cluster">
- <changelog>
- <fix>
- Avoid unnecessary call of <code>DeltaRequest.addSessionListener()</code>
- in non-primary nodes. (kfujino)
- </fix>
- <add>
- Add new attribute that send all actions for session across Tomcat
- cluster nodes. (kfujino)
- </add>
- <fix>
- Remove unused <code>pathname</code> attribute in mbean definition of
- <code>BackupManager</code>. (kfujino)
- </fix>
- </changelog>
- </subsection>
- <subsection name="WebSocket">
- <changelog>
- <fix>
- <bug>57761</bug>: Ensure that the opening HTTP request is correctly
- formatted when the WebSocket client connects to a server root. (remm)
- </fix>
- <fix>
- <bug>57762</bug>: Ensure that the WebSocket client correctly detects
- when the connection to the server is dropped. (markt)
- </fix>
- <fix>
- <bug>57776</bug>: Revert the 8.0.21 fix for the
- <code>permessage-deflate</code> implementation and incorrect op-codes
- since the fix was unnecessary (the bug only affected trunk) and the fix
- broke rather than fixed <code>permessage-deflate</code> if an
- uncompressed message was converted into more than one compressed
- message. (markt)
- </fix>
- <fix>
- Fix log name typo in <code>WsRemoteEndpointImplServer</code> class,
- caused by a copy-paste. (markt/kkolinko)
- </fix>
- <fix>
- <bug>57788</bug>: Avoid NPE when looking up a class hierarchy without
- finding anything. (remm)
- </fix>
- </changelog>
- </subsection>
- <subsection name="Web applications">
- <changelog>
- <add>
- <bug>57759</bug>: Add information to the keyAlias documentation to make
- it clear that the order keys are read from the keystore is
- implementation dependent. (markt)
- </add>
- <fix>
- <bug>57864</bug>: Update the documentation web application to make it
- clearer that hex values are not valid for cluster send options. Based on
- a patch by Kyohei Nakamura. (markt)
- </fix>
- </changelog>
- </subsection>
- <subsection name="Tribes">
- <changelog>
- <fix>
- Fix a concurrency issue when a backup message that has all session data
- and a backup message that has diff data are processing at the same time.
- This fix ensures that <code>MapOwner</code> is set to
- <code>ReplicatedMapEntry</code>. (kfujino)
- </fix>
- </changelog>
- </subsection>
- <subsection name="Other">
- <changelog>
- <fix>
- Add missing pom for tomcat-storeconfig. (remm)
- </fix>
- <update>
- Update optional Checkstyle library to 6.5. (kkolinko)
- </update>
- <fix>
- <bug>57707</bug>: Improve error message when trying to run a release
- build on a non-Windows platform and Wine is not available. (markt)
- </fix>
- </changelog>
- </subsection>
-</section>
-<section name="Tomcat 8.0.21 (markt)" rtext="2015-03-26">
- <subsection name="Catalina">
- <changelog>
- <add>
- <bug>49785</bug>: Enable StartTLS connections for JNDIRealm.
- (fschumacher)
- </add>
- <fix>
- When docBase refers internal war and unpackWARs is set to false, avoid
- registration of the invalid redeploy resource that has been added ".war"
- extension in duplicate. (kfujino)
- </fix>
- <fix>
- If WAR exists, it is not necessary to trigger a reload when adding a
- Directory. (kfujino)
- </fix>
- <fix>
- <bug>55988</bug>: Add support for Java 8 JSSE server-preferred TLS
- cipher suite ordering. This feature requires Java 8
- and is controlled by <code>useServerCipherSuitesOrder</code>
- attribute on an HTTP connector.
- Based upon a patch provided by Ognjen Blagojevic. (schultz)
- </fix>
- <fix>
- <bug>56608</bug>: When deploying an external WAR, add watched resources
- in the expanded directory based on whether the expanded directory is
- expected to exist rather than if it does exist. (markt)
- </fix>
- <fix>
- When triggering a reload due to a modified watched resource, ensure
- that multiple changed watched resources only trigger one reload rather
- than a series of reloads. (markt)
- </fix>
- <fix>
- <bug>57601</bug>: Ensure that HEAD requests return the correct content
- length (i.e. the same as for a GET) when the requested resource includes
- a resource served by the Default servlet. (jboynes/markt)
- </fix>
- <fix>
- <bug>57602</bug>: Ensure that HEAD requests return the correct content
- length (i.e. the same as for a GET) when the requested resource includes
- a resource served by a servlet that extends <code>HttpServlet</code>.
- (markt)
- </fix>
- <fix>
- <bug>57621</bug>: When an async request completes, ensure that any
- remaining request body data is swallowed. (markt)
- </fix>
- <fix>
- <bug>57637</bug>: Do not create unnecessary sessions when using
- PersistentValve. (jboynes/fschumacher)
- </fix>
- <fix>
- <bug>57645</bug>: Correct a regression in the fix for
- <bug>57190</bug> that incorrectly required the path passed to
- <code>ServletContext.getContext(String)</code> to be an exact match to a
- path to an existing context. (markt)
- </fix>
- <fix>
- Make sure that <code>unpackWAR</code> attribute of <code>Context</code>
- is handled correctly in <code>HostConfig</code>. (kfujino)
- </fix>
- <fix>
- When deploying a WAR file that contains a context.xml file and
- <code>unpackWARs</code> is <code>false</code> ignore any context.xml
- file that may exist in an expanded directory associated with the WAR.
- (markt)
- </fix>
- <fix>
- <bug>57675</bug>: Correctly quote strings when using the extended
- access log. (markt)
- </fix>
- <add>
- Enable Tomcat to detect when a WAR file has been changed while Tomcat is
- not running. Tomcat does this by adding a META-INF/war-tracking file to
- the expanded directory and setting the last modified time of this file
- to the last modified time of the WAR. If Tomcat detects a modified WAR
- via this mechanism the web application will be redeployed (i.e. the
- expanded directory will be removed and the modified WAR expanded in its
- place). (markt)
- </add>
- <fix>
- <bug>57704</bug>: Fix potential NPEs during web application start/stop
- when <code>org.apache.tomcat.InstanceManager</code> is not initialized.
- (violetagg)
- </fix>
- <add>
- Use the simplified digest output for digest.bat|sh when generating
- digests with no salt and a single iteration to make it easier to use
- with DIGEST authentication. (markt)
- </add>
- <fix>
- Add support for <code>LAST_ACCESS_AT_START</code> system property to
- <code>SingleSignOn</code>. (kfujino)
- </fix>
- <scode>
- Refactor Authenticator implementations to reduce code duplication.
- (markt)
- </scode>
- <fix>
- <bug>57724</bug>: Handle the case in the CORS filter where a user agent
- includes an origin header for a non-CORS request. (markt)
- </fix>
- <fix>
- When searching for SCIs
- <code>o.a.catalina.Context.getParentClassLoader</code> will be used
- instead of <code>java.lang.ClassLoader.getParent</code>. Thus one can
- provide the correct parent class loader when running embedded Tomcat in
- other environments such as OSGi. (violetagg)
- </fix>
- <fix>
- <bug>57743</bug>: Fix a locked file / resource leak issue when a JAR is
- accessed just before or during web application undeploy. Patch provided
- by Pavel Avgustinov. (markt)
- </fix>
- </changelog>
- </subsection>
- <subsection name="Coyote">
- <changelog>
- <add>
- <bug>57540</bug>: Make TLS/SSL protocol available in a new request
- attribute
- (<code>org.apache.tomcat.util.net.secure_protocol_version</code>).
- (Note that AJP connectors will require <tt>mod_jk</tt> 1.2.41 or later,
- or an as-yet-unknown version of mod_proxy_ajp, or configure the proxy
- to send the AJP_SSL_PROTOCOL request attribute to Tomcat. Please see
- the bug comments for details.)
- Based upon a patch provided by Ralf Hauser. (schultz)
- </add>
- <fix>
- Fix a cipher ordering issue when using the OpenSSL syntax for JSSE
- cipher configuration to ensure that ephemeral ECDH with AES is preferred
- to ephemeral ECDH with anything else. (markt)
- </fix>
- <fix>
- <bug>57570</bug>: Make the processing of trailer headers with chunked
- input optional and disabled by default. (markt)
- </fix>
- <fix>
- <bug>57592</bug>: Correctly handle the case where an
- <code>AsyncContext</code> is used for non-blocking I/O and is completed
- during a write operation. (markt)
- </fix>
- <fix>
- <bug>57638</bug>: Avoid an IllegalArgumentException when an AJP request
- body chunk larger than the socket read buffer is being read. This
- typically requires a larger than default AJP packetSize. (markt)
- </fix>
- <fix>
- <bug>57674</bug>: Avoid a BufferOverflowException when an AJP response
- body chunk larger than the socket write buffer is being written. This
- typically requires a larger than default AJP packetSize. (markt)
- </fix>
- <update>
- Align the OpenSSL syntax cipher configuration with the OpenSSL 1.0.2
- branch. (markt)
- </update>
- <fix>
- Numerous fixes to the APR/native connector to improve robustness.
- (markt)
- </fix>
- <fix>
- Stop caching and re-using SocketWrapper instances. With the introduction
- of upgrade and non-blocking I/O, I/O can occur on non-container threads.
- This makes it nearly impossible to track whether a SocketWrapper is
- still being referenced or not, making re-use a risky proposition.
- (markt)
- </fix>
- <scode>
- Refactor Connector authentication (only used by AJP) into a separate
- method. (markt)
- </scode>
- <add>
- <bug>57708</bug>: Implement a new feature for AJP connectors - Tomcat
- Authorization. If the new tomcatAuthorization attribute is set to
- <code>true</code> (it is disabled by default) Tomcat will take an
- authenticated user name from the AJP protocol and use the appropriate
- Realm for the request to authorize (i.e. add roles) to that user.
- (markt)
- </add>
- <fix>
- Fix an issue that meant that any pipe-lined data read by Tomcat before
- an asynchronous request completed was lost during the completion of the
- asynchronous request. This mean that the pipe-lined request(s) would be
- lost and/or corrupted. (markt)
- </fix>
- <update>
- Update the minimum recommended version of the Tomcat Native library (if
- used) to 1.1.33. (markt)
- </update>
- </changelog>
- </subsection>
- <subsection name="Jasper">
- <changelog>
- <fix>
- <bug>57135</bug>: Package imports via
- <code>javax.el.ImportHandler</code> should only import public, concrete
- classes. (markt)
- </fix>
- <fix>
- <bug>57583</bug>: Cache 'Not Found' results in
- <code>javax.el.ImportHandler.resolveClass()</code> to save repeated
- attempts to load classes that are known not to exist to improve
- performance. (markt)
- </fix>
- <fix>
- <bug>57626</bug>: Correct a regression introduced in the 8.0.16 fix for
- ensuring Jars were closed after use, that broke recompilation of
- modified JSPs that depended on a tag file packaged in a Jar. (markt)
- </fix>
- <fix>
- <bug>57627</bug>: Correctly determine last modified times for
- dependencies when a tag file packaged in a JAR depends on a tag file
- packaged in a second JAR. (markt)
- </fix>
- <fix>
- <bug>57647</bug>: Ensure INFO message is logged when scanning jars for
- TLDs if the scan does not find a TLD in any jar. Previously a message
- would only be logged if a TLD was not found in all scanned jars. (jboynes)
- </fix>
- <update>
- <bug>57662</bug>: Update all references to the ECJ compiler to version
- 4.4.2. (violetagg)
- </update>
- </changelog>
- </subsection>
- <subsection name="Cluster">
- <changelog>
- <fix>
- Remove unnecessary method that always returns true. The domain filtering
- works on <code>DomainFilterInterceptor</code>. (kfujino)
- </fix>
- </changelog>
- </subsection>
- <subsection name="WebSocket">
- <changelog>
- <fix>
- Correct a bug in the <code>permessage-deflate</code> implementation that
- meant that the incorrect op-codes were used if an uncompressed message
- was converted into more than one compressed message. (markt)
- </fix>
- <add>
- <bug>57676</bug>: List conflicting WebSocket endpoint classes when
- there is a path conflict. Based upon a patch proposed by yangkun.
- (schultz)
- </add>
- </changelog>
- </subsection>
- <subsection name="Web applications">
- <changelog>
- <fix>
- <bug>56058</bug>: Add links to the AccessLogValve documentation for
- configuring reverse proxies and/or Tomcat to ensure that the desired
- information is used entered in the access log when Tomcat is running
- behind a reverse proxy. (markt)
- </fix>
- <fix>
- <bug>57587</bug>: Update the JNDI Datasource HOWTO for DBCP2. Patch
- provided by Phil Steitz. (markt)
- </fix>
- <fix>
- Remove incorrect note from context configuration page in the
- documentation web application that stated WAR files located outside the
- appBase were never unpacked. (markt)
- </fix>
- <update>
- <bug>57644</bug>: Update examples to use Apache Standard Taglib 1.2.5.
- (jboynes)
- </update>
- <fix>
- <bug>57683</bug>: Ensure that if a client aborts their connection to the
- stock ticker example (the only way a client can disconnect), the example
- continues to work for existing and new clients. (markt)
- </fix>
- <fix>
- Make it clear that when using digested passwords with DIGEST
- authentication that no salt and only a single iteration must be used
- when generating the digest. (markt)
- </fix>
- </changelog>
- </subsection>
- <subsection name="Extras">
- <changelog>
- <fix>
- <bug>57377</bug>: Remove the restriction that prevented the use of SSL
- when specifying a bind address with the JMXRemoteLifecycleListener. Also
- enable SSL to be configured for the registry as well as the server.
- (markt)
- </fix>
- </changelog>
- </subsection>
- <subsection name="Tribes">
- <changelog>
- <fix>
- When a map member has been added to <code>ReplicatedMap</code>, make
- sure to add it to backup nodes list of all other members. (kfujino)
- </fix>
- <fix>
- Make sure that refuse the messages from a different domain in
- <code>DomainFilterInterceptor</code>. (kfujino)
- </fix>
- </changelog>
- </subsection>
- <subsection name="Other">
- <changelog>
- <update>
- Update optional Checkstyle library to 6.4.1. (kkolinko)
- </update>
- <fix>
- <bug>57703</bug>: Update the <code>http-method</code> definition for
- web applications using a Servlet 2.5 descriptor as per Servlet 2.5 MR 6.
- (markt)
- </fix>
- <update>
- Update to Tomcat Native Library version 1.1.33 to pick up the Windows
- binaries that are based on OpenSSL 1.0.1m and APR 1.5.1. (markt)
- </update>
- </changelog>
- </subsection>
-</section>
-<section name="Tomcat 8.0.20 (markt)" rtext="2015-02-20">
- <subsection name="Coyote">
- <changelog>
- <fix>
- Fix a concurrency issue that meant that a change in socket timeout (e.g.
- when switching to asynchronous I/O) did not always take effect
- immediately. (markt)
- </fix>
- </changelog>
- </subsection>
-</section>
-<section name="Tomcat 8.0.19 (markt)" rtext="not released">
- <subsection name="Catalina">
- <changelog>
- <fix>
- Clarify threaded usage of variables by removing volatile marker
- in NonceInfo. Issue reported by Coverity Scan. (fschumacher)
- </fix>
- <fix>
- <bug>57180</bug>: Further fixes to support the use of arbitrary HTTP
- methods with the CORS filter. (markt)
- </fix>
- <fix>
- <bug>57472</bug>: Fix performance regression in resources implementation
- when signed JARs are used in a web application. (markt)
- </fix>
- <add>
- Warn about problematic setting of appBase. (fschumacher)
- </add>
- <fix>
- Fix exception while authentication in JDBCRealm. (fschumacher)
- </fix>
- <fix>
- <bug>57534</bug>: CORS Filter should only look at media type component of
- Content-Type request header. (markt)
- </fix>
- <fix>
- <bug>57556</bug>: Align <code>getRealPath()</code> behaviour with that
- of earlier versions and include a trailing separator if the real path
- refers to a directory. (markt)
- </fix>
- <fix>
- Ensure that Servlet 3.0 async requests where <code>startAsync()</code>
- is called in one container thread and <code>dispatch()</code> is called
- in a different container thread complete correctly. (markt)
- </fix>
- <fix>
- Ensure that user name checking in the optional SecurityListener is
- case-insensitive (as documented) and than the case-insensitive
- comparison is performed using the system default Locale. (markt)
- </fix>
- <add>
- <bug>57021</bug>: Improve logging in AprLifecycleListener and
- jni.Library when Tomcat-Native DLL fails to load. Based on a patch by
- Pravallika Peddi. (markt/kkolinko)
- </add>
- </changelog>
- </subsection>
- <subsection name="Coyote">
- <changelog>
- <fix>
- Fix several bugs that could cause multiple registrations for write
- events for a single socket when using Servlet 3.0 async. Typically, the
- side effects of these multiple registrations would be exceptions
- appearing in the logs. (markt)
- </fix>
- <fix>
- <bug>57432</bug>: Align <code>SSL_OP_NO_TLSv1_1</code> and
- <code>SSL_OP_NO_TLSv1_2</code> constant values with OpenSSL (they had
- been swapped). (markt)
- </fix>
- <fix>
- <bug>57509</bug>: Improve length check when writing HTTP/1.1
- response headers: reserve space for 4 extra bytes. (kkolinko)
- </fix>
- <fix>
- <bug>57544</bug>: Fix potential infinite loop when preparing a kept
- alive HTTP connection for the next request. (markt)
- </fix>
- <fix>
- <bug>57546</bug>: Ensure that a dropped network connection does not
- leave references to the UpgradeProcessor associated with the connection
- in memory. (markt)
- </fix>
- <fix>
- When applying the <code>maxSwallowSize</code> limit to a connection read
- that many bytes first before closing the connection to give the client a
- chance to read the response. (markt)
- </fix>
- <fix>
- Prevent an async timeout being processed multiple times for the same
- socket when running on slow and/or heavily loaded systems. (markt)
- </fix>
- <fix>
- <bug>57581</bug>: Change statistics byte counter in coyote Request
- object to be long to allow values above 2Gb. (kkolinko)
- </fix>
- <update>
- Use the data that supports cipher definition using OpenSSL syntax to
- improve the quality of values provided for the
- <code>javax.servlet.request.key_size</code> request attribute. (markt)
- </update>
- <fix>
- Fix a concurrency issue in the APR Poller that meant it was possible
- under low load for a socket queued to be added to the Poller not to be
- added for 10 seconds. (markt)
- </fix>
- </changelog>
- </subsection>
- <subsection name="Jasper">
- <changelog>
- <update>
- <bug>57123</bug>: Update all references to the ECJ compiler to version
- 4.4.1. With thanks to Ralph Schaer for uploading the 4.4.1 JAR to Maven
- Central. (markt)
- </update>
- <add>
- <bug>57564</bug>: Make JspC amenable to subclassing. Patch provided by
- Jan Bartel. (markt)
- </add>
- <fix>
- Simplify code in <code>ProtectedFunctionMapper</code> class of
- Jasper runtime. (kkolinko)
- </fix>
- <fix>
- <bug>57574</bug>: Do not check existence of a Java package in
- <code>javax.el.ImportHandler.importPackage()</code>. (kkolinko)
- </fix>
- </changelog>
- </subsection>
- <subsection name="WebSocket">
- <changelog>
- <fix>
- <bug>57490</bug>: Make it possible to use Tomcat's WebSocket client
- within a web application when running under a SecurityManager. Based on
- a patch by Mikael Sterner. (markt)
- </fix>
- <add>
- Add some debug logging to the WebSocket session to track session
- creation and session closure. (markt)
- </add>
- </changelog>
- </subsection>
- <subsection name="Web applications">
- <changelog>
- <update>
- Clarify documentation for <code>useBodyEncodingForURI</code>
- attribute of a connector. (kkolinko)
- </update>
- <fix>
- Fix possible resource leaks by closing streams properly. Issues
- reported by Coverity Scan. (fschumacher)
- </fix>
- <fix>
- <bug>57503</bug>: Make clear that the JULI integration for log4j only
- works with log4j 1.2.x. (markt)
- </fix>
- <fix>
- <bug>57496</bug>: Remove hard-coded URL in JSP SVG example. (markt)
- </fix>
- </changelog>
- </subsection>
- <subsection name="Tribes">
- <changelog>
- <fix>
- Fix a possible deadlock when receiver thread invokes
- <code>mapMemberAdded()</code> while ping thread invokes
- <code>memberAlive()</code>. (kfujino)
- </fix>
- </changelog>
- </subsection>
- <subsection name="Other">
- <changelog>
- <add>
- Enhance bean factory used for JNDI resources. New attribute
- <code>forceString</code> allows to support non-standard
- string argument property setters. (rjung)
- </add>
- <fix>
- Assign newly created stream to field instead of leaking it uselessly.
- Issue reported by Coverity Scan. (fschumacher)
- </fix>
- <update>
- Update optional Checkstyle library to 6.3. (kkolinko)
- </update>
- <fix>
- Guard the digester from MbeansDescriptorsDigesterSource with its own
- lock object. (fschumacher)
- </fix>
- <fix>
- Refactor the unit tests and add some new test properties to make it
- easier to exclude performance tests and relax timing tests. This is
- primarily for the ASF CI system where these tests frequently fail.
- (markt)
- </fix>
- <fix>
- <bug>57558</bug>: Add missing JAR in Ant task definition required by
- the validate task. (markt)
- </fix>
- <add>
- List names of Testsuites that have failed or skipped tests when
- running the tests with Ant. (kkolinko)
- </add>
- </changelog>
- </subsection>
-</section>
-<section name="Tomcat 8.0.18 (markt)" rtext="2015-01-26">
- <subsection name="Catalina">
- <changelog>
- <fix>
- <bug>57178</bug>: The CORS filter now treats <code>null</code> as a
- valid origin that matches <code>*</code>. Patch provided by Gregor
- Zurowski. (markt)
- </fix>
- <fix>
- <bug>57425</bug>: Don't add attributes with null value or name to the
- replicated context. (fschumacher)
- </fix>
- <add>
- <bug>57431</bug>: Enable usage of custom class for context creation when
- using embedded tomcat. (fschumacher)
- </add>
- <fix>
- <bug>57446</bug>: Ensure that <code>ServletContextListener</code>s that
- have limited access to <code>ServletContext</code> methods are called
- with the same <code>ServletContext</code> instance for both
- <code>contextInitialized()</code> and <code>contextDestroyed()</code>.
- (markt)
- </fix>
- <fix>
- <bug>57455</bug>: Explicitly block the use of the double-quote character
- when configuring the common, server and shared class loaders since
- double-quote is used to quote values that contain commas. (markt)
- </fix>
- <fix>
- <bug>57461</bug>: When an instance of
- <code>org.apache.catalina.startup.VersionLoggerListener</code> logs the
- result of <code>System.getProperty("java.home")</code> don't report it
- in a manner that makes it look like the <code>JAVA_HOME</code>
- environment variable. (markt)
- </fix>
- <fix>
- <bug>57476</bug>: Ensure the responses written as part of a forward are
- fully written. This fixes a regression in 8.0.15 caused by the fix for
- <bug>57252</bug>. (markt)
- </fix>
- <fix>
- While closing streams for given resources ensure that if an exception
- happens it will be handled properly. Issue is reported by Coverity Scan.
- (violetagg)
- </fix>
- <fix>
- <bug>57481</bug>: Fix <code>IllegalStateException</code> at the end of
- the request when using non-blocking reads with the HTTP BIO connector.
- (markt)
- </fix>
- <fix>
- Change Response to use UEncoder instances with shared safeChars.
- (fschumacher)
- </fix>
- <fix>
- Ensure that when static resources are served from JARs, only static
- resources are served. (markt)
- </fix>
- <add>
- Allow <code>VersionLoggerListener</code> to log all system properties.
- This feature is off by default. (kkolinko)
- </add>
- </changelog>
- </subsection>
- <subsection name="Jasper">
- <changelog>
- <fix>
- Ensure that classes imported via the page directive are made available
- to the EL environment via the ImportHandler. Issue is reported by
- Coverity Scan. (violetagg)
- </fix>
- <fix>
- <bug>57441</bug>: Do not trigger an error when using functions defined
- by lambdas or imported via an ImportHandler in an EL expression in a
- JSP. (markt)
- </fix>
- </changelog>
- </subsection>
- <subsection name="Cluster">
- <changelog>
- <fix>
- Fix mbean descriptor of <code>ClusterSingleSignOn</code>. (kfujino)
- </fix>
- <fix>
- <bug>57473</bug>: Add sanity check to FarmWebDeployer's WarWatcher to
- detect suspected incorrect permissions on the watch directory. (schultz)
- </fix>
- </changelog>
- </subsection>
- <subsection name="Tribes">
- <changelog>
- <fix>
- Clarify the handling of Copy message and Copy nodes. (kfujino)
- </fix>
- <fix>
- Copy node does not need to send the entry data. It is enough to send
- only the node information of the entry. (kfujino)
- </fix>
- <fix>
- ReplicatedMap should send the Copy message when replicating. (kfujino)
- </fix>
- <fix>
- Fix behavior of ReplicatedMap when member has disappeared. If map entry
- is primary, rebuild the backup members. If primary node of map entry has
- disappeared, backup node is promoted to primary. (kfujino)
- </fix>
- </changelog>
- </subsection>
-</section>
-<section name="Tomcat 8.0.17 (markt)" rtext="2015-01-16">
- <subsection name="Catalina">
- <changelog>
- <fix>
- Correct a regression in the previous fix for <bug>57252</bug> that broke
- request listeners for non-async requests that triggered an error that
- was handled by the ErrorReportingValve. (markt/violetagg)
- </fix>
- </changelog>
- </subsection>
- <subsection name="Coyote">
- <changelog>
- <fix>
- Add flushing to send ack in the NIO2 connector. (remm)
- </fix>
- </changelog>
- </subsection>
-</section>
-<section name="Tomcat 8.0.16 (markt)" rtext="not released">
- <subsection name="Catalina">
- <changelog>
- <fix>
- <bug>57172</bug>: Provide a better error message if something attempts to
- access a resource through a web application class loader that has been
- stopped. (markt/kkolinko)
- </fix>
- <fix>
- <bug>57173</bug>: Revert the fix for <bug>56953</bug> that broke
- annotation scanning in some cases. (markt)
- </fix>
- <fix>
- <bug>57180</bug>: Do not limit the CORS filter to only accepting
- requests that use an HTTP method defined in RFC 7231. (markt)
- </fix>
- <fix>
- <bug>57190</bug>: Fix <code>ServletContext.getContext(String)</code>
- when parallel deployment is used so that the correct ServletContext is
- returned. (markt)
- </fix>
- <fix>
- <bug>57208</bug>: Prevent NPE in JNDI Realm when no results are found
- in a directory context for a user with specified user name. Based on
- a patch provided by Jason McIntosh. (violetagg)
- </fix>
- <add>
- <bug>57209</bug>: Add a new attribute, userSearchAsUser to the JNDI
- Realm. (markt)
- </add>
- <fix>
- <bug>57215</bug>: Ensure that the result of calling
- <code>HttpServletRequest.getContextPath()</code> is neither decoded nor
- normalized as required by the Servlet specification. (markt)
- </fix>
- <fix>
- <bug>57216</bug>: Improve handling of invalid context paths. A context
- path should either be an empty string or start with a
- <code>'/'</code> and do not end with a
- <code>'/'</code>. Invalid context path are automatically
- corrected and a warning is logged. The <code>null</code> and
- <code>"/"</code> values are now correctly changed to
- <code>""</code>. (markt/kkolinko)
- </fix>
- <fix>
- Update storeconfig with the CredentialHandler element. (remm)
- </fix>
- <fix>
- Correct message that is logged when load-on-startup servlet fails
- to load. It was logging a wrong name. (kkolinko)
- </fix>
- <fix>
- <bug>57239</bug>: Correct several message typos. Includes patch by
- vladk. (kkolinko)
- </fix>
- <fix>
- Fix closing of Jars during annotation scanning. (schultz/kkolinko)
- </fix>
- <fix>
- Fix a concurrency issue in async processing. Ensure that a non-container
- thread can not change the async state until the container thread has
- completed. (markt)
- </fix>
- <fix>
- <bug>57252</bug>: Provide application configured error pages with a
- chance to handle an async error before the built-in error reporting.
- (markt)
- </fix>
- <fix>
- <bug>57281</bug>: Enable non-public Filter and Servlet classes to be
- configured programmatically via the Servlet 3.0 API and then used
- without error when running under a SecurityManager. (markt)
- </fix>
- <fix>
- <bug>57308</bug>: Remove unnecessary calls to
- <code>System.getProperty()</code> where more suitable API calls are
- available. (markt)
- </fix>
- <add>
- Add unit tests for RemoteAddrValve and RemoteHostValve. (rjung)
- </add>
- <add>
- Allow to configure RemoteAddrValve and RemoteHostValve to
- adopt behavior depending on the connector port. Implemented
- by optionally adding the connector port to the string compared
- with the patterns <code>allow</code> and <code>deny</code>. Configured
- using <code>addConnectorPort</code> attribute on valve. (rjung)
- </add>
- <add>
- Optionally trigger authentication instead of denial in
- RemoteAddrValve and RemoteHostValve. This only works in
- combination with <code>preemptiveAuthentication</code>
- on the application context. Configured using
- <code>invalidAuthenticationWhenDeny</code> attribute on valve. (rjung)
- </add>
- <fix>
- Remove the obsolete <code>jndi</code> protocol usage from the scanning
- process performed by StandardJarScanner. (violetagg)
- </fix>
- <fix>
- Prevent file descriptors leak and ensure that files are closed after
- retrieving the last modification time. (violetagg)
- </fix>
- <update>
- Make <code>o.a.catalina.webresources.StandardRoot</code> easier for
- extending. (violetagg)
- </update>
- <fix>
- <bug>57326</bug>: Enable <code>AsyncListener</code> implementations to
- re-register themselves during <code>AsyncListener.onStartAsync</code>.
- (markt)
- </fix>
- <fix>
- <bug>57331</bug>: Allow ExpiresFilter to use "year" as synonym for
- "years" in its configuration. (kkolinko)
- </fix>
- <fix>
- Ensure that if the RewriteValve rewrites a request that subsequent calls
- to <code>HttpServletRequest.getRequestURI()</code> return the undecoded
- URI. (markt)
- </fix>
- <fix>
- Ensure that if the RewriteValve rewrites a request to a non-normalized
- URI that the URI is normalized before the URI is mapped to ensure that
- the correct mapping is applied. (markt)
- </fix>
- <fix>
- Prevent NPEs being logged during post-processing for requests that have
- been re-written by the RewriteValve. (markt)
- </fix>
- <fix>
- Various StoreConfig improvements including removing a dependency on the
- <code>StandardServer</code> implementation, improve consistency of
- behaviour when MBean is not registered and improve error messages when
- accessed via the Manager application. (markt)
- </fix>
- <update>
- Improve SnoopServlet in unit tests. (rjung)
- </update>
- <add>
- Add RequestDescriptor class to unit tests.
- Adjust TestRewriteValve to use RequestDescriptor. (rjung)
- </add>
- <update>
- Add more AJP unit tests. (rjung)
- </update>
- <fix>
- <bug>57363</bug>: Log to stderr if LogManager is unable to read
- configuration files rather than swallowing the exception silently.
- (markt)
- </fix>
- </changelog>
- </subsection>
- <subsection name="Coyote">
- <changelog>
- <fix>
- Allow HTTP upgrade process to complete without data corruption when
- additional content is sent along with the upgrade header. (remm)
- </fix>
- <fix>
- <bug>57187</bug>: Regression handling the special * URL. (remm)
- </fix>
- <fix>
- <bug>57234</bug>: Make SSL protocol filtering to remove insecure
- protocols case insensitive. (markt)
- </fix>
- <fix>
- <bug>57265</bug>: Fix some potential concurrency issues with sendFile
- and the NIO connector. (markt)
- </fix>
- <fix>
- <bug>57324</bug>: If the client uses <code>Expect: 100-continue</code>
- and Tomcat responds with a non-2xx response code, Tomcat also closes the
- connection. If Tomcat knows the connection is going to be closed when
- committing the response, Tomcat will now also send the
- <code>Connection: close</code> response header. (markt)
- </fix>
- <fix>
- <bug>57340</bug>: When using Comet, ensure that Socket and SocketWrapper
- are only returned to their respective caches once on socket close (it is
- possible for multiple threads to call close concurrently). (markt)
- </fix>
- <fix>
- <bug>57347</bug>: AJP response contains wrong status reason phrase
- (rjung)
- </fix>
- <add>
- <bug>57391</bug>: Allow TLS Session Tickets to be disabled when using
- the APR/native HTTP connector. Patch provided by Josiah Purtlebaugh.
- (markt)
- </add>
- </changelog>
- </subsection>
- <subsection name="Jasper">
- <changelog>
- <fix>
- <bug>57142</bug>: As per the clarification from the JSP specification
- maintenance lead, classes and packages imported via the page directive
- must be made available to the EL environment via the ImportHandler.
- (markt)
- </fix>
- <fix>
- <bug>57247</bug>: Correct the default Java source and target versions in
- the JspC usage message to <code>1.7</code> for Java 7. (markt)
- </fix>
- <fix>
- <bug>57309</bug>: Ensure that the current EL Resolver is given an
- opportunity to perform type coercion before applying the default EL
- coercion rules. (markt)
- </fix>
- <fix>
- Improve the calculation of the resource's last-modified, performed by
- JspCompilationContext, in a way to support URLs with protocol different
- than <code>jar:file</code>. (violetagg)
- </fix>
- <fix>
- CVE-2014-7810:
- Do not use a privileged code block when evaluating EL expressions
- when running under a security manager, which allowed to bypass code
- restrictions. (markt)
- </fix>
- <fix>
- Fix an issue with BeanELResolver when running under a security
- manager. Some classes may not be accessible but may have accessible
- interfaces. (markt)
- </fix>
- </changelog>
- </subsection>
- <subsection name="Cluster">
- <changelog>
- <fix>
- In order to enable define in <code>Cluster</code> element,
- <code>ClusterSingleSignOn</code> implements <code>ClusterValve</code>.
- (kfujino)
- </fix>
- <fix>
- <bug>57338</bug>: Improve the ability of the
- <code>ClusterSingleSignOn</code> valve to handle nodes being added and
- removed from the Cluster at run time. (markt)
- </fix>
- </changelog>
- </subsection>
- <subsection name="WebSocket">
- <changelog>
- <fix>
- Correct multiple issues with the flushing of batched messages that could
- lead to duplicate and/or corrupt messages. (markt)
- </fix>
- <fix>
- Correctly implement headers case insensitivity. (markt/remm)
- </fix>
- <fix>
- Allow optional use of user extensions. (remm)
- </fix>
- <fix>
- Allow using partial binary message handlers. (remm)
- </fix>
- <fix>
- Limit ping/pong message size. (remm)
- </fix>
- <fix>
- Allow configuration of the time interval for the periodic event. (remm)
- </fix>
- <fix>
- More accurate annotations processing. (remm)
- </fix>
- <fix>
- Allow optional default for origin header in the client. (remm)
- </fix>
- </changelog>
- </subsection>
- <subsection name="Web applications">
- <changelog>
- <fix>
- Update documentation for CGI servlet. Recommend to copy the servlet
- declaration into web application instead of enabling it globally.
- Correct documentation for cgiPathPrefix. (kkolinko)
- </fix>
- <update>
- Improve HTML version of build instructions and align with
- BUILDING.txt. (kkolinko)
- </update>
- <update>
- Improve Tomcat Manager documentation. Rearrange, add section on
- HTML GUI, document /expire command and Server Status page. (kkolinko)
- </update>
- <update>
- <bug>57238</bug>: Update information on SSL/TLS on Security and SSL
- documentation pages. Patch by Glen Peterson. (kkolinko)
- </update>
- <fix>
- <bug>57245</bug>: Correct the reference to <code>allowLinking</code> in
- the security configuration guide since that attribute has moved from the
- Context element to the nested Resources element. (markt)
- </fix>
- <fix>
- Fix ambiguity of section links on Valves configuration reference page.
- (kkolinko)
- </fix>
- <fix>
- <bug>57261</bug>: Fix vminfo and threaddump Manager commands to start
- their output with an "OK" line. Document them. Based on a patch by
- Oleg Trokhov. (kkolinko)
- </fix>
- <fix>
- <bug>57267</bug>: Document the <code>StoreConfigLifecycleListener</code>
- and the <code>/save</code> command for the Manager application. (markt)
- </fix>
- <fix>
- <bug>57323</bug>: Correct display of outdated sessions in sessions
- count listing in Manager application. (kkolinko)
- </fix>
- <add>
- Add document of <code>ClusterSingleSignOn</code>. (kfujino)
- </add>
- </changelog>
- </subsection>
- <subsection name="Other">
- <changelog>
- <update>
- When downloading required libraries at build time, use random name
- for temporary file and automatically create destination directory
- (<code>base.path</code>). (kkolinko)
- </update>
- <update>
- Update optional Checkstyle library to 6.2. (kkolinko)
- </update>
- <update>
- Simplify <code>setproxy</code> task in <code>build.xml</code>.
- Taskdef there is not needed since Ant 1.8.2. (kkolinko)
- </update>
- <fix>
- Update "ide-eclipse" target in <code>build.xml</code> to create Eclipse
- project that uses Java 7 compliance settings instead of workspace-wide
- defaults. (kkolinko)
- </fix>
- <fix>
- Update the package renamed copy of Apache Commons Pool 2 to the 2.3
- release to pick up various fixes since the 2.2 release including one for
- a possible infinite loop. (markt)
- </fix>
- <fix>
- <bug>57285</bug>: Restore the manifest entry that marks the Windows
- uninstaller application as requiring elevated privileges. (markt)
- </fix>
- <add>
- <bug>57344</bug>: Provide sha1 checksum files for Tomcat downloads.
- Correct filename patterns for apache-tomcat-*-embed.tar.gz archive
- to exclude an *.asc file. (kkolinko)
- </add>
- </changelog>
- </subsection>
-</section>
-<section name="Tomcat 8.0.15 (markt)" rtext="2014-11-07">
- <subsection name="Catalina">
- <changelog>
- <add>
- <bug>43548</bug>: Add an XML schema for the tomcat-users.xml file.
- (markt)
- </add>
- <add>
- <bug>43682</bug>: Add support for referring to the current context, host
- and service name in per Context logging.properties files by using the
- properties <code>${classloader.webappName}</code>,
- <code>${classloader.hostName}</code> and
- <code>${classloader.serviceName}</code>. (markt)
- </add>
- <add>
- <bug>47919</bug>: Extend the information logged when Tomcat starts to
- optionally log the values of command line arguments (enabled by
- default) and environment variables (disabled by default). Note that
- the values added to CATALINA_OPTS and JAVA_OPTS environment variables
- will be logged, as they are used to build up the command line. (markt)
- </add>
- <add>
- <bug>49939</bug>: Expose the method that clears the static resource
- cache for a web application via JMX. (markt)
- </add>
- <fix>
- <bug>55951</bug>: Allow cookies to use UTF-8 encoded values in HTTP
- headers. This requires the use of the RFC6265
- <strong>CookieProcessor</strong>. (markt)
- </fix>
- <fix>
- <bug>55984</bug>: Using the allow separators in version 0 cookies option
- with the legacy cookie processor should only apply to version 0 cookies.
- Version 1 cookies with values that contain separators should not be
- affected and should continue to be quoted. (markt)
- </fix>
- <add>
- <bug>56393</bug>: Add support for RFC6265 cookie parsing and generation.
- This is currently disabled by default and may be enabled via the
- <strong>CookieProcessor</strong> element of a <strong>Context</strong>.
- (markt)
- </add>
- <add>
- <bug>56394</bug>: Introduce new configuration element CookieProcessor in
- Context to allow context-specific configuration of cookie processing
- options. Attributes of Context element that were added in Tomcat 8.0.13
- to allow configuration of a new experimental RFC6265 based cookie parser
- (<code>useRfc6265</code> and <code>cookieEncoding</code>) are
- replaced by this new configuration element. (markt)
- </add>
- <fix>
- Improve the previous fix for <bug>56401</bug>. Avoid logging version
- information in the constructor since it then gets logged at undesirable
- times such as when using <code>StoreConfig</code>. (markt)
- </fix>
- <fix>
- <bug>56403</bug>: Add pluggable password derivation support to the
- Realms via the new <code>CredentialHandler</code> interface.
- (markt/schultz)
- </fix>
- <fix>
- <bug>57016</bug>: When using the <code>PersistentValve</code> do not
- remove sessions from the store when persisting them. (markt)
- </fix>
- <add>
- Deprecate the use of system properties to control cookie parsing and
- replace them with attributes on the new <code>CookieProcessor</code>
- that may be configured on a per context basis. (markt)
- </add>
- <fix>
- Correct an edge case and allow a cookie if the value starts with an
- equals character and the <code>CookieProcessor</code> is not configured
- to allow equals characters in cookie values but is configured to allow
- name only cookies. (markt)
- </fix>
- <fix>
- <bug>57022</bug>: Ensure SPNEGO authentication continues to work with
- the JNDI Realm using delegated credentials with recent Oracle JREs.
- (markt)
- </fix>
- <fix>
- <bug>57027</bug>: Add additional validation for stored credentials used
- by Realms when the credential is stored using hex encoding. (markt)
- </fix>
- <fix>
- <bug>57038</bug>: Add a <code>WebResource.getCodeBase()</code> method,
- implement for all <code>WebResource</code> implementations and then use
- it in the web application class loader to set the correct code base for
- resources loaded from JARs and WARs. (markt)
- </fix>
- <fix>
- Correct a couple of NPEs in the JNDI Realm that could be triggered with
- when not specifying a roleBase and enabling roleSearchAsUser. (markt)
- </fix>
- <fix>
- Correctly handle relative values for the docBase attribute of a Context.
- (markt)
- </fix>
- <fix>
- Ensure that log messages generated by the web application class loader
- correctly identify the associated Context when multiple versions of a
- Context with the same path are present. (markt)
- </fix>
- <fix>
- Remove the unnecessary registration of context.xml as a redeploy
- resource. The context.xml having an external docBase has already been
- registered as a redeploy resource at first. (kfujino)
- </fix>
- <fix>
- <bug>57089</bug>: Ensure that configuration of a session ID generator is
- not lost when a web application is reloaded. (markt)
- </fix>
- <fix>
- <bug>57105</bug>: When parsing web.xml do not limit the buffer element
- of the jsp-property-group element to integer values as the allowed
- values are <code><number>kb</code> or <code>none</code>. (markt)
- </fix>
- <update>
- Update the minimum required version of the Tomcat Native library (if
- used) to 1.1.32. (markt)
- </update>
- <fix>
- Update storeconfig with newly introduced elements: SessionIdGenerator,
- CookieProcessor, JarScanner and JarScanFilter. (remm)
- </fix>
- <fix>
- Throw a <code>NullPointerException</code> if a null string is passed to
- the <code>write(String,int,int)</code> method of the
- <code>PrintWriter</code> obtained from the <code>ServletResponse</code>.
- (markt)
- </fix>
- <fix>
- Cookie rewrite flag abbreviation should be CO rather than C. (remm)
- </fix>
- <fix>
- <bug>57153</bug>: When the StandardJarScanner is configured to scan the
- full class path, ensure that class path entries added directly to the
- web application class loader are scanned. (markt)
- </fix>
- <fix>
- AsyncContext should remain usable until fireOnComplete is called. (remm)
- </fix>
- <fix>
- AsyncContext createListener should wrap any instantiation exception
- using a ServletException. (remm)
- </fix>
- <fix>
- <bug>57155</bug>: Allow a web application to be configured that does not
- have a docBase on the file system. This is primarily intended for use
- when embedding. (markt)
- </fix>
- <fix>
- Propagate header ordering from fileupload to the part implementation.
- (remm)
- </fix>
- </changelog>
- </subsection>
- <subsection name="Coyote">
- <changelog>
- <add>
- <bug>53952</bug>: Add support for TLSv1.1 and TLSv1.2 for APR connector.
- Based upon a patch by Marcel Šebek. This feature requires
- Tomcat Native library 1.1.32 or later. (schultz/jfclere)
- </add>
- <scode>
- Cache the <code>Encoder</code> instances used to convert Strings to byte
- arrays in the Connectors (e.g. when writing HTTP headers) to improve
- throughput. (markt)
- </scode>
- <add>
- Disable SSLv3 by default for JSSE based HTTPS connectors (BIO, NIO and
- NIO2). The change also ensures that SSLv2 is disabled for these
- connectors although SSLv2 should already be disabled by default by the
- JRE. (markt)
- </add>
- <add>
- Disable SSLv3 by default for the APR/native HTTPS connector. (markt)
- </add>
- <fix>
- Do not increase remaining counter at end of stream in
- IdentityInputFilter. (kkolinko)
- </fix>
- <fix>
- Trigger an error if an invalid attempt is made to use non-blocking IO.
- (markt)
- </fix>
- <fix>
- <bug>57157</bug>: Allow calls to
- <code>AsyncContext.start(Runnable)</code> during non-blocking IO reads
- and writes. (markt)
- </fix>
- <fix>
- Async state MUST_COMPLETE should still be started. (remm)
- </fix>
- </changelog>
- </subsection>
- <subsection name="Jasper">
- <changelog>
- <fix>
- <bug>57099</bug>: Ensure that semi-colons are not permitted in JSP
- import page directives. (markt)
- </fix>
- <fix>
- <bug>57113</bug>: Fix broken package imports in Expression Language when
- more than one package was imported and the desired class was not in the
- last package imported. (markt)
- </fix>
- <fix>
- <bug>57132</bug>: Fix import conflicts reporting in Expression Language.
- (kkolinko)
- </fix>
- <fix>
- When coercing an object to a given type, only attempt coercion to an
- array if both the object type and the target type are an array type.
- (violetagg/markt)
- </fix>
- <fix>
- Improve handling of invalid input to
- <code>javax.el.ImportHandler.resolveClass()</code>. (markt)
- </fix>
- <fix>
- Allow the same class to be added to an instance of
- <code>javax.el.ImportHandler</code> more than once without triggering
- an error. The second and subsequent calls for the same class will be
- ignored. (markt)
- </fix>
- <fix>
- <bug>57136</bug>: Ensure only <code>\${</code> and <code>\#{</code> are
- treated as escapes for <code>${</code> and <code>#{</code> rather than
- <code>\$</code> and <code>\#</code> being treated as escapes for
- <code>$</code> and <code>#</code> when processing literal expressions in
- expression language. (markt)
- </fix>
- <fix>
- When coercing an object to an array type in Expression Language, handle
- the case where the source object is an array of primitives.
- (markt/kkolinko)
- </fix>
- <fix>
- Do not throw an exception on missing JSP file servlet initialization.
- (remm)
- </fix>
- <fix>
- <bug>57148</bug>: When coercing an object to a given type and a
- <code>PropertyEditor</code> has been registered for the type correctly
- coerce the empty string to <code>null</code> if the
- <code>PropertyEditor</code> throws an exception. (kkolinko/markt)
- </fix>
- <fix>
- <bug>57153</bug>: Correctly scan for TLDs located in directories that
- represent expanded JARs files that have been added to the web application
- class loader's class path. (markt)
- </fix>
- <fix>
- <bug>57141</bug>: Enable EL in JSPs to refer to static fields of
- imported classes including the standard <code>java.lang.*</code>
- imports. (markt)
- </fix>
- </changelog>
- </subsection>
- <subsection name="Cluster">
- <changelog>
- <fix>
- Add support for the <code>SessionIdGenerator</code> to cluster manager
- template. (kfujino)
- </fix>
- <fix>
- Avoid possible integer overflows reported by Coverity Scan. (fschumacher)
- </fix>
- </changelog>
- </subsection>
- <subsection name="WebSocket">
- <changelog>
- <fix>
- <bug>57054</bug>: Correctly handle the case in the WebSocket client
- when the HTTP response to the upgrade request can not be read in a
- single pass; either because the buffer is too small or the server sent
- the response in multiple packets. (markt)
- </fix>
- <add>
- Extend support for the <code>permessage-deflate</code> extension to the
- client implementation. (markt)
- </add>
- <fix>
- Fix client subprotocol handling. (remm)
- </fix>
- <fix>
- Add null checks for arguments in remote endpoint. (remm/kkolinko)
- </fix>
- <fix>
- <bug>57091</bug>: Work around the behaviour of the Oracle JRE when
- creating new threads in an applet environment that breaks the WebSocket
- client implementation. Patch provided by Niklas Hallqvist. (markt)
- </fix>
- <fix>
- <bug>57118</bug>: Ensure that that an <code>EncodeException</code> is
- thrown by <code>RemoteEndpoint.Basic.sendObject(Object)</code> rather
- than an <code>IOException</code> when no suitable <code>Encoder</code>
- is configured for the given Object. (markt)
- </fix>
- </changelog>
- </subsection>
- <subsection name="Web applications">
- <changelog>
- <fix>
- Correct a couple of broken links in the Javadoc. (markt)
- </fix>
- <fix>
- Correct documentation for <code>ServerCookie.ALLOW_NAME_ONLY</code>
- system property. (kkolinko)
- </fix>
- <fix>
- <bug>57049</bug>: Clarified that <code>jvmRoute</code> can be set in
- <code><Engine></code>'s <code>jvmRoute</code> or in a system
- property. (schultz)
- </fix>
- <fix>
- Correct version of Java WebSocket mentioned in documentation
- (s/1.0/1.1/). (markt/kkolinko)
- </fix>
- <update>
- Suppress timestamp comments in Javadoc. (kkolinko)
- </update>
- <fix>
- <bug>57147</bug>: Various corrections to the JDBC Store section of the
- session manager configuration page of the documentation web application.
- (markt)
- </fix>
- </changelog>
- </subsection>
- <subsection name="Tribes">
- <changelog>
- <fix>
- <bug>45282</bug>: Improve shutdown of NIO receiver so that sockets are
- closed cleanly. (fhanik/markt)
- </fix>
- </changelog>
- </subsection>
- <subsection name="jdbc-pool">
- <changelog>
- <fix>
- <bug>57005</bug>: Fix javadoc errors when building with Java 8. Patch
- provided by Pierre Viret. (markt)
- </fix>
- <fix>
- <bug>57079</bug>: Use Tomcat version number for jdbc-pool module when
- building and shipping the module as part of Tomcat. (markt)
- </fix>
- <fix>
- Fix broken overview page in javadoc generated via "javadoc" task in
- jdbc-pool build.xml file. (kkolinko)
- </fix>
- </changelog>
- </subsection>
- <subsection name="Other">
- <changelog>
- <fix>
- <bug>56079</bug>: The uninstaller packaged with the Apache Tomcat
- Windows installer is now digitally signed. (markt)
- </fix>
- <fix>
- Fix timestamps in Tomcat build and jdbc-pool to use 24-hour format
- instead of 12-hour one and use UTC timezone. (markt/kkolinko)
- </fix>
- <fix>
- Update the package renamed copy of Apache Commons DBCP 2 to revision
- 1631450 to pick up additional fixes since the 2.0.1 release including
- Javadoc corrections to fix errors when compiling with Java 8. (markt)
- </fix>
- <update>
- <bug>56596</bug>: Update to Tomcat Native Library version 1.1.32 to
- pick up the Windows binaries that are based on OpenSSL 1.0.1j and APR
- 1.5.1. (markt)
- </update>
- <scode>
- In Tomcat tests: log name of the current test method at start time.
- (kkolinko)
- </scode>
- </changelog>
- </subsection>
-</section>
-<section name="Tomcat 8.0.14 (markt)" rtext="2014-09-29">
- <subsection name="Other">
- <changelog>
- <fix>
- <bug>56079</bug>: The Apache Tomcat Windows installer, the Apache Tomcat
- Windows service and the Apache Tomcat Windows service monitor
- application are now digitally signed. (markt)
- </fix>
- </changelog>
- </subsection>
-</section>
-<section name="Tomcat 8.0.13 (markt)" rtext="not released">
- <subsection name="Catalina">
- <changelog>
- <fix>
- <bug>55917</bug>: Allow bytes in the range 0x80 to 0xFF to appear in
- cookie values if the cookie is a V1 (RFC2109) cookie and the value is
- correctly quoted. The new RFC6265 based cookie parser must be enabled to
- correctly handle these cookies. (markt)
- </fix>
- <fix>
- <bug>55918</bug>: Do not permit control characters to appear in quoted
- V1 (RFC2109) cookie values. The new RFC6265 based cookie parser must be
- enabled to correctly handle these cookies. (markt)
- </fix>
- <fix>
- <bug>55921</bug>: Correctly handle (ignore the cookie) unescaped JSON in
- a cookie value. The new RFC6265 based cookie parser must be enabled to
- correctly handle these cookies. (markt)
- </fix>
- <add>
- <bug>56401</bug>: Log version information when Tomcat starts.
- (markt/kkolinko)
- </add>
- <add>
- <bug>56530</bug>: Add a web application class loader implementation that
- supports the parallel loading of web application classes. (markt)
- </add>
- <fix>
- <bug>56900</bug>: Fix some potential resource leaks when reading
- property files reported by Coverity Scan. Based on patches provided by
- Felix Schumacher. (markt)
- </fix>
- <fix>
- <bug>56902</bug>: Fix a potential resource leak in the Default Servlet
- reported by Coverity Scan. Based on a patch provided by Felix
- Schumacher. (markt)
- </fix>
- <fix>
- <bug>56903</bug>: Correct the return value for
- <code>StandardContext.getResourceOnlyServlets()</code> so that multiple
- names are separated by commas. Identified by Coverity Scan and fixed
- based on a patch by Felix Schumacher. (markt)
- </fix>
- <add>
- Add an additional implementation of a RFC6265 based cookie parser along
- with new Context options to select and configure it. This parser is
- currently considered experimental and is not used by default. (markt)
- </add>
- <fix>
- Fixed the multipart elements merge operation performed during web
- application deployment. Identified by Coverity Scan. (violetagg)
- </fix>
- <fix>
- Correct the information written by
- <code>ExtendedAccessLogValve</code> when a format token x-O(XXX) is
- used so that multiple values for a header XXX are separated by commas.
- Identified by Coverity Scan. (violetagg)
- </fix>
- <fix>
- Fix a potential resource leak when reading MANIFEST.MF file for
- extension dependencies reported by Coverity Scan. (violetagg)
- </fix>
- <fix>
- Fix some potential resource leaks when reading properties, files and
- other resources. Reported by Coverity Scan. (violetagg)
- </fix>
- <fix>
- Correct the previous fix for <bug>56825</bug> that enabled pre-emptive
- authentication to work with the SSL authenticator. (markt)
- </fix>
- <scode>
- Refactor to reduce code duplication identified by Simian. (markt)
- </scode>
- <fix>
- When using parallel deployment and <code>undeployOldVersions</code>
- feature is enabled on a Host, correctly undeploy context of old
- version. Make sure that Tomcat does not undeploy older Context if
- current context is not running. (kfujino)
- </fix>
- <fix>
- Fix a rare threading issue when locking resources via WebDAV.
- (markt)
- </fix>
- <fix>
- Fix a rare threading issue when using HTTP digest authentication.
- (markt)
- </fix>
- <fix>
- When deploying war, add XML file in the config base to the redeploy
- resources if war does not have META-INF/context.xml or
- <code>deployXML</code> is false. If XML file is created in the config
- base, redeploy will occur. (kfujino)
- </fix>
- <scode>
- Various changes to reduce unnecessary code in Tomcat's copy of
- Apache Commons BCEL to reduce the time taken for annotation scanning
- when web applications start. Includes contributions from kkolinko and
- hzhang9. (markt)
- </scode>
- <fix>
- <bug>56938</bug>: Ensure web applications that have mixed case context
- paths and are deployed as directories are correctly removed on undeploy
- when running on a case sensitive file system. (markt)
- </fix>
- <add>
- <bug>57004</bug>: Add <code>stuckThreadCount</code> property to
- <code>StuckThreadDetectionValve</code>'s JMX bean. Patch provided by
- Jiří Pejchal. (schultz)
- </add>
- <fix>
- <bug>57011</bug>: Ensure that the request and response are correctly
- recycled when processing errors during async processing. (markt)
- </fix>
- </changelog>
- </subsection>
- <subsection name="Coyote">
- <changelog>
- <fix>
- <bug>56910</bug>: Prevent the invalid value of <code>-1</code> being
- used for <code>maxConnections</code> with APR connectors. (markt)
- </fix>
- <fix>
- Ensure that AJP connectors enable the <code>KeepAliveTimeout</code>.
- (kfujino)
- </fix>
- <fix>
- Reduce duplicated code. All AJP connectors use common method to
- configuration of processor. (kfujino)
- </fix>
- </changelog>
- </subsection>
- <subsection name="Jasper">
- <changelog>
- <fix>
- <bug>43001</bug>: Enable the JspC Ant task to set the JspC option
- <code>mappedFile</code>. (markt)
- </fix>
- <fix>
- Ensure that the implementation of
- <code>javax.servlet.jsp.PageContext.include(String)</code>
- and
- <code>javax.servlet.jsp.PageContext.include(String, boolean)</code>
- will throw <code>IOException</code> when an I/O error occur during
- the operation. (violetagg)
- </fix>
- <fix>
- <bug>56908</bug>: Fix some potential resource leaks when reading
- jar files. Reported by Coverity Scan. Patch provided by Felix
- Schumacher. (violetagg)
- </fix>
- <fix>
- Fix a potential resource leak in JDTCompiler when checking whether
- a resource is a package. Reported by Coverity Scan. (fschumacher)
- </fix>
- <fix>
- <bug>56991</bug>: Deprecate the use of a request attribute to pass a
- <jsp-file> declaration to Jasper and prevent an infinite loop
- if this technique is used in conjunction with an include. (markt)
- </fix>
- </changelog>
- </subsection>
- <subsection name="WebSocket">
- <changelog>
- <fix>
- <bug>56905</bug>: Make destruction on web application stop of thread
- group used for WebSocket connections more robust. (kkolinko/markt)
- </fix>
- <fix>
- <bug>56907</bug>: Ensure that client IO threads are stopped if a secure
- WebSocket client connection fails. (markt)
- </fix>
- <fix>
- <bug>56982</bug>: Return the actual negotiated extensions rather than an
- empty list for <code>Session.getNegotiatedExtensions()</code>. (markt)
- </fix>
- <update>
- Update the WebSocket implementation to support the Java WebSocket
- specification version 1.1. (markt)
- </update>
- </changelog>
- </subsection>
- <subsection name="Web applications">
- <changelog>
- <add>
- Add <code>JarScanner</code> to the nested components listed for a
- Context. (markt)
- </add>
- <update>
- Update the Windows authentication documentation after some additional
- testing to answer the remaining questions. (markt)
- </update>
- </changelog>
- </subsection>
- <subsection name="Other">
- <changelog>
- <fix>
- <bug>56895</bug>: Correctly compose <code>JAVA_OPTS</code> in
- <code>catalina.bat</code> so that escape sequences are preserved. Patch
- by Lucas Theisen. (markt)
- </fix>
- <update>
- <bug>56988</bug>: Allow to use relative path in <code>base.path</code>
- setting when building Tomcat. (kkolinko)
- </update>
- <fix>
- <bug>56990</bug>: Ensure that the <code>ide-eclipse</code> build target
- downloads all the libraries required by the default Eclipse
- configuration files. (markt)
- </fix>
- <fix>
- Update the package renamed copy of Apache Commons DBCP 2 to revision
- 1626988 to pick up the fixes since the 2.0.1 release including support
- for custom eviction policies. (markt)
- </fix>
- <fix>
- Update the package renamed copy of Apache Commons Pool 2 to revision
- 1627271 to pick up the fixes since the 2.2 release including some memory
- leak fixes and support for application provided eviction policies.
- (markt)
- </fix>
- </changelog>
- </subsection>
-</section>
-<section name="Tomcat 8.0.12 (markt)" rtext="2014-09-03">
- <subsection name="Catalina">
- <changelog>
- <add>
- Make the session id generator extensible by adding a
- <code>SessionIdGenerator</code> interface, an abstract
- base class and a standard implementation. (rjung)
- </add>
- <fix>
- <bug>56882</bug>: Fix regression in processing of includes and forwards
- when Context have been reloaded. Tomcat was responding with HTTP Status
- 503 (Servlet xxx is currently unavailable). (kkolinko)
- </fix>
- </changelog>
- </subsection>
- <subsection name="Coyote">
- <changelog>
- <fix>
- When building a list of JSSE ciphers from an OpenSSL cipher definition,
- ignore unknown criteria rather than throwing a
- <code>NullPointerException</code>. (markt)
- </fix>
- <add>
- Add support for the EECDH alias when using the OpenSSL cipher syntax to
- define JSSE ciphers. (markt)
- </add>
- </changelog>
- </subsection>
- <subsection name="Jasper">
- <changelog>
- <fix>
- Correct a logic error in the <code>JasperElResolver</code>. There was no
- functional impact but the code was less efficient as a result of the
- error. Based on a patch by martinschaef. (markt)
- </fix>
- <fix>
- <bug>56568</bug>: Enable any HTTP method to be used to request a JSP
- page that has the <code>isErrorPage</code> page directive set to
- <code>true</code>. (markt)
- </fix>
- </changelog>
- </subsection>
- <subsection name="WebSocket">
- <changelog>
- <add>
- Extend support for the <code>permessage-deflate</code> extension to
- compression of outgoing messages on the server side. (markt)
- </add>
- </changelog>
- </subsection>
- <subsection name="Other">
- <changelog>
- <add>
- <bug>56323</bug>: Include the <code>*.bat</code> files when installing
- Tomcat via the Windows installer. (markt)
- </add>
- </changelog>
- </subsection>
-</section>
-<section name="Tomcat 8.0.11 (markt)" rtext="2014-08-22">
- <subsection name="Catalina">
- <changelog>
- <fix>
- <bug>56658</bug>: Fix regression that a context was inaccessible after
- reload. (kkolinko)
- </fix>
- <fix>
- <bug>56710</bug>: Do not map requests to servlets when context is
- being reloaded. (kkolinko)
- </fix>
- <fix>
- <bug>56712</bug>: Fix session idle time calculations in
- <code>PersistenceManager</code>. (kkolinko)
- </fix>
- <fix>
- <bug>56717</bug>: Fix duplicate registration of
- <code>MapperListener</code> during repeated starts of embedded Tomcat.
- (kkolinko)
- </fix>
- <add>
- <bug>56724</bug>: Write an error message to Tomcat logs if container
- background thread is aborted unexpectedly. (kkolinko)
- </add>
- <fix>
- When scanning class files (e.g. for annotations) and reading the number
- of parameters in a <code>MethodParameters</code> structure only read a
- single byte (rather than two bytes) as per the JVM specification. Patch
- provided by Francesco Komauli. (markt)
- </fix>
- <fix>
- Allow the JNDI Realm to start even if the directory is not available.
- The directory not being available is not fatal once the Realm is started
- and it need not be fatal when the Realm starts. Based on a patch by
- Cédric Couralet. (markt)
- </fix>
- <fix>
- <bug>56736</bug>: Avoid an incorrect <code>IllegalStateException</code>
- if the async timeout fires after a non-container thread has called
- <code>AsyncContext.dispatch()</code> but before a container thread
- starts processing the dispatch. (markt)
- </fix>
- <fix>
- <bug>56739</bug>: If an application handles an error on an application
- thread during asynchronous processing by calling
- <code>HttpServletResponse.sendError()</code>, then ensure that the
- application is given an opportunity to report that error via an
- appropriate application defined error page if one is configured. (markt)
- </fix>
- <fix>
- <bug>56784</bug>: Fix a couple of rare but theoretically possible
- atomicity bugs. (markt)
- </fix>
- <fix>
- <bug>56785</bug>: Avoid <code>NullPointerException</code> if directory
- exists on the class path that is not readable by the Tomcat user.
- (markt)
- </fix>
- <fix>
- <bug>56796</bug>: Remove unnecessary sleep when stopping a web
- application. (markt)
- </fix>
- <fix>
- <bug>56801</bug>: Improve performance of
- <code>org.apache.tomcat.util.file.Matcher</code> which is to filter JARs
- for scanning during web application start. Based on a patch by Sheldon
- Shao. (markt)
- </fix>
- <fix>
- <bug>56815</bug>: When the <code>gzip</code> option is enabled for the
- <code>DefaultServlet</code> ensure that a suitable <code>Vary</code>
- header is returned for resources that might be returned directly in
- compressed form. (markt)
- </fix>
- <fix>
- Do not mark threads from the container thread pool as container threads
- when being used to process <code>AsyncContext.start(Runnable)</code> so
- processing is correctly transferred back to a genuine container thread
- when necessary. (markt)
- </fix>
- <add>
- Add simple caching for calls to <code>StandardRoot.getResources()</code>
- in the new (for 8.0.x) resources implementation. (markt)
- </add>
- <fix>
- <bug>56825</bug>: Enable pre-emptive authentication to work with the
- SSL authenticator. Based on a patch by jlmonteiro. (markt)
- </fix>
- <fix>
- <bug>56840</bug>: Avoid NPE when the rewrite valve is mapped to
- a context. (remm)
- </fix>
- <fix>
- Correctly handle multiple <code>accept-language</code> headers rather
- than just using the first header to determine the user's preferred
- Locale. (markt)
- </fix>
- <fix>
- <bug>56848</bug>: Improve handling of <code>accept-language</code>
- headers. (markt)
- </fix>
- <fix>
- <bug>56857</bug>: Fix thread safety issue when calling ServletContext
- methods while running under a security manager. (markt)
- </fix>
- </changelog>
- </subsection>
- <subsection name="Coyote">
- <changelog>
- <fix>
- Fix NIO2 sendfile state tracking and error handling to fix
- various corruption issues. (remm)
- </fix>
- <fix>
- Missing timeout for NIO2 sendfile writes. (remm)
- </fix>
- <fix>
- Allow inline processing for NIO2 sendfile and optimize keepalive
- behavior. (remm)
- </fix>
- <fix>
- Fix excessive NIO2 sendfile direct memory use in some cases, sendfile
- will now instead use the regular socket write buffer as configured.
- (remm)
- </fix>
- <fix>
- <bug>56661</bug>: Fix <code>getLocalAddr()</code> for AJP connectors.
- The complete fix is only available with a recent AJP forwarder like
- the forthcoming mod_jk 1.2.41. (rjung)
- </fix>
- <fix>
- Use default ciphers defined as
- <code>HIGH:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5</code> so
- that no weak ciphers are enabled by default. (remm)
- </fix>
- <fix>
- <bug>56780</bug>: Enable Tomcat to start when using SSL with an IBM JRE
- in strict SP800-131a mode. (markt)
- </fix>
- <fix>
- <bug>56810</bug>: Remove use of Java 8 specific API calls in unit tests
- for OpenSSL to JSSE cipher conversion. (markt)
- </fix>
- </changelog>
- </subsection>
- <subsection name="Jasper">
- <changelog>
- <fix>
- <bug>56709</bug>: Fix system property name in a log message. Submitted
- by Robert Kish. (remm)
- </fix>
- <fix>
- <bug>56797</bug>: When matching a method in an EL expression, do not
- treat bridge methods as duplicates of the method they bridge to. In this
- case always call the target of the bridge method. (markt)
- </fix>
- </changelog>
- </subsection>
- <subsection name="WebSocket">
- <changelog>
- <fix>
- <bug>56746</bug>: Allow secure WebSocket client threads to use the
- current context class loader rather than explicitly setting it to the
- class loader that loaded the WebSocket implementation. This allows
- WebSocket client connections from within web applications to access,
- amongst other things, the JNDI resources associated with the web
- application. (markt)
- </fix>
- </changelog>
- </subsection>
- <subsection name="Web applications">
- <changelog>
- <fix>
- Correct the label in the list of sessions by idle time for the bin that
- represents the idle time immediately below the maximum permitted idle
- time when using the expire command of the Manager application. (markt)
- </fix>
- </changelog>
- </subsection>
- <subsection name="jdbc-pool">
- <changelog>
- <fix>
- <bug>53088</bug>: More identifiable thread name. (fhanik)
- </fix>
- <fix>
- <bug>53200</bug>: Selective logging for slow versus failed queries.
- (fhanik)
- </fix>
- <fix>
- <bug>53853</bug>: More flexible classloading. (fhanik)
- </fix>
- <fix>
- <bug>54225</bug>: Disallow empty init SQL. (fhanik)
- </fix>
- <fix>
- <bug>54227</bug>: Evaluate max age upon borrow. (fhanik)
- </fix>
- <fix>
- <bug>54235</bug>: Disallow nested pools exploitating using data source.
- (fhanik)
- </fix>
- <fix>
- <bug>54395</bug>: Fix JDBC interceptor parsing bug. (fhanik)
- </fix>
- <fix>
- <bug>54537</bug>: Performance improvement in
- <code>StatementFinalizer</code>. (fhanik)
- </fix>
- <fix>
- <bug>54978</bug>: Make sure proper connection validation always happens,
- regardless of config. (fhanik)
- </fix>
- <fix>
- <bug>56318</bug>: Ability to trace statement creation in
- <code>StatementFinalizer</code>. (fhanik)
- </fix>
- <fix>
- <bug>56789</bug>: getPool() returns the actual pool, always. (fhanik)
- </fix>
- </changelog>
- </subsection>
- <subsection name="Other">
- <changelog>
- <add>
- <bug>56788</bug>: Display the full version in the list of installed
- applications when installed via the Windows installer package. Patch
- provided by Alexandre Garnier. (markt)
- </add>
- <add>
- <bug>56829</bug>: Add the ability for users to define their own values
- for <code>_RUNJAVA</code> and <code>_RUNJDB</code> environment
- variables. Be more strict with executable filename on Windows
- (s/java/java.exe/). Based on a patch by Neeme Praks. (markt/kkolinko)
- </add>
- </changelog>
- </subsection>
-</section>
-<section name="Tomcat 8.0.10 (markt)" rtext="not released">
- <subsection name="Catalina">
- <changelog>
- <fix>
- <bug>44312</bug>: Log an error if there is a conflict between Host and
- Alias names. Improve host management methods in <code>Mapper</code>
- to avoid occasionally removing a wrong host. Check that host management
- operations are performed on the host and not on an alias. (kkolinko)
- </fix>
- <scode>
- <bug>56611</bug>: Refactor code to remove inefficient calls to
- <code>Method.isAnnotationPresent()</code>. Based on a patch by Jian Mou.
- (markt/kkolinko)
- </scode>
- <fix>
- Fix regression in
- <code>StandardContext.removeApplicationListener()</code>, introduced by
- the fix for bug <bug>56588</bug>. (kkolinko)
- </fix>
- <fix>
- <bug>56653</bug>: Fix concurrency issue with lists of contexts in
- <code>Mapper</code> when stopping Contexts. (kkolinko)
- </fix>
- <fix>
- <bug>56657</bug>: When using parallel deployment, if the same session id
- matches different versions of a web application, prefer the latest
- version. Ensure that remapping selects the version that we expect.
- (kkolinko)
- </fix>
- <fix>
- Assert that mapping result object is empty before performing mapping
- work in <code>Mapper</code>. (kkolinko)
- </fix>
- <scode>
- Remove <code>context</code> and <code>wrapper</code> fields in
- <code>Request</code> class and deprecate their setters. (kkolinko)
- </scode>
- <fix>
- <bug>56658</bug>: Avoid delay between registrations of mappings for
- context and for its servlets. (kkolinko)
- </fix>
- <fix>
- <bug>56665</bug>: Correct the generation of the effective web.xml when
- elements contain an empty string as value. (violetagg)
- </fix>
- <fix>
- Fix storeconfig exception routing issues, so that a major problem
- should avoid configuration overwrite. (remm)
- </fix>
- <fix>
- Add configuration fields for header names in SSLValve. (remm)
- </fix>
- <fix>
- <bug>56666</bug>: When clearing the SSO cookie use the same values for
- domain, path, httpOnly and secure as were used to set the SSO cookie.
- (markt)
- </fix>
- <fix>
- <bug>56677</bug>: Ensure that
- <code>HttpServletRequest.getServletContext()</code> returns the correct
- value during a cross-context dispatch. (markt)
- </fix>
- <fix>
- <bug>56684</bug>: Ensure that Tomcat does not shut down if the socket
- waiting for the shutdown command experiences a
- <code>SocketTimeoutException</code>. (markt)
- </fix>
- <fix>
- <bug>56693</bug>: Fix various issues in the static resource cache
- implementation where the cache retained a stale entry after the
- successful completion of an operation that always invalidates the cache
- entry such as a delete operation.
- (markt)
- </fix>
- <fix>
- When the current PathInfo is modified as a result of dispatching a
- request, ensure that a call to
- <code>HttpServletRequest.getPathTranslated()</code> returns a value that
- is based on the modified PathInfo. (markt)
- </fix>
- <fix>
- <bug>56698</bug>: When persisting idle sessions, only persist newly idle
- sessions. Patch provided by Felix Schumacher. (markt)
- </fix>
- </changelog>
- </subsection>
- <subsection name="Coyote">
- <changelog>
- <fix>
- <bug>56663</bug>: Fix edge cases demonstrated by ByteCounter relating
- to data available, remaining and extra write events, mostly occurring
- with non blocking Servlet 3.1. (remm)
- </fix>
- <fix>
- Avoid possible NPE stopping endpoints that are not started (stop
- shouldn't do anything in that case). (remm)
- </fix>
- <add>
- <bug>56704</bug>: Add support for OpenSSL syntax for ciphers when
- using JSSE SSL connectors. Submitted by Emmanuel Hugonnet. (remm)
- </add>
- <update>
- Allow to configure <code>maxSwallowSize</code> attribute of an HTTP
- connector via JMX. (kkolinko)
- </update>
- </changelog>
- </subsection>
- <subsection name="Jasper">
- <changelog>
- <fix>
- <bug>56543</bug>: Update to the Eclipse JDT Compiler 4.4. (violetagg)
- </fix>
- <fix>
- <bug>56652</bug>: Add support for method parameters that use arrays and
- varargs to <code>ELProcessor.defineFunction()</code>.(markt)
- </fix>
- </changelog>
- </subsection>
- <subsection name="WebSocket">
- <changelog>
- <add>
- Add support for the <code>permessage-deflate</code> extension. This is
- currently limited to decompressing incoming messages on the server side.
- It is expected that support will be extended to outgoing messages and to
- the client side shortly. (markt)
- </add>
- </changelog>
- </subsection>
- <subsection name="Web applications">
- <changelog>
- <fix>
- Attempt to obfuscate session cookie values associated with other web
- applications when viewing HTTP request headers with the Cookies example
- from the examples web application. This reduces the opportunity to use
- this example for malicious purposes should the advice to remove the
- examples web application from security sensitive systems be ignored.
- (markt)
- </fix>
- <fix>
- <bug>56694</bug>: Remove references to <code>Manager</code> attribute
- <code>checkInterval</code> from documentation and Javadoc since it no
- longer exists. Based on a patch by Felix Schumacher. Also remove other
- references to <code>checkInterval</code> that are no longer valid.
- (markt)
- </fix>
- </changelog>
- </subsection>
- <subsection name="Other">
- <changelog>
- <update>
- Update the API stability section of the release notes now that Tomcat 8
- has had its first stable release. (markt)
- </update>
- <update>
- Improve <code>build.xml</code> so that when Eclipse JDT Compiler is
- updated, it will delete the old JAR from <code>build/lib</code>
- directory. (kkolinko)
- </update>
- <scode>
- Simplify implementation of "setproxy" target in <code>build.xml</code>.
- (kkolinko)
- </scode>
- <update>
- Update optional Checkstyle library to 5.7. (kkolinko)
- </update>
- <update>
- <bug>56596</bug>: Update to Tomcat Native Library version 1.1.31 to
- pick up the Windows binaries that are based on OpenSSL 1.0.1h. (markt)
- </update>
- <fix>
- <bug>56685</bug>: Add quotes necessary for <code>daemon.sh</code> to
- work correctly on Solaris. Based on a suggestion by lfuka. (markt)
- </fix>
- <update>
- Update package renamed Apache Commons Pool2 to r1609323 to pick various
- bug fixes. (markt)
- </update>
- <update>
- Update package renamed Apache Commons DBCP2 to r1609329 to pick up a
- minor bug fix. (markt)
- </update>
- <update>
- Update package renamed Apache Commons FileUpload to r1596086 to pick
- various bug fixes. (markt)
- </update>
- </changelog>
- </subsection>
-</section>
-<section name="Tomcat 8.0.9 (markt)" rtext="2014-06-24">
- <subsection name="Catalina">
- <changelog>
- <fix>
- <bug>55282</bug>: Ensure that one and the same application listener is
- added only once when starting the web application. (violetagg)
- </fix>
- <fix>
- <bug>55975</bug>: Apply consistent escaping for double quote and
- backslash characters when escaping cookie values. (markt)
- </fix>
- <scode>
- <bug>56387</bug>: Improve the code that handles an attempt to load a
- class after a web application has been stopped. Use common code to handle
- this case regardless of the access path and don't throw an exception
- purely to log a stack trace. (markt)
- </scode>
- <scode>
- <bug>56399</bug>: Improve implementation of CoyoteAdapter.checkRecycled()
- to do not use an exception for flow control. (kkolinko)
- </scode>
- <add>
- <bug>56461</bug>: New <code>failCtxIfServletStartFails</code> attribute
- on Context and Host configuration to force the context startup to fail
- if a load-on-startup servlet fails its startup. (slaurent)
- </add>
- <add>
- <bug>56526</bug>: Improved the <code>StuckThreadDetectionValve</code> to
- optionally interrupt stuck threads to attempt to unblock them.
- (slaurent)
- </add>
- <fix>
- <bug>56545</bug>: Pre-load two additional classes, the loading of which
- may otherwise be triggered by a web application which in turn would
- trigger an exception when running under a security manager. (markt)
- </fix>
- <update>
- <bug>56546</bug>: Reduce logging level for stack traces of stuck web
- application threads printed by WebappClassLoader.clearReferencesThreads()
- from error to info. (kkolinko)
- </update>
- <scode>
- Refactor and simplify common code in object factories in
- <code>org.apache.catalina.naming</code> package, found thanks to Simian
- (Similarity Analyser) tool. Improve handling of Throwable.
- (markt/kkolinko)
- </scode>
- <fix>
- Relax cookie naming restrictions. Cookie attribute names used in the
- <code>Set-Cookie</code> header may be used unambiguously as cookie
- names. The restriction that prevented such usage has been removed.
- (jboynes/markt)
- </fix>
- <fix>
- Further relax cookie naming restrictions. Version 0 (a.k.a Netscape
- format) cookies may now use names that start with the <code>$</code>
- character. (jboynes/markt)
- </fix>
- <fix>
- Restrict cookie naming so that the <code>=</code> character is no longer
- permitted in a version 0 (a.k.a. Netscape format) cookie name. While
- Tomcat allowed this, browsers always truncated the name at the
- <code>=</code> character leading to a mis-match between the cookie the
- server set and the cookie returned by the browser. (jboynes/markt)
- </fix>
- <add>
- Add a simple <code>ServiceLoader</code> based discovery mechanism to the
- JULI <code>LogFactory</code> to make it easier to use JULI and Tomcat
- components that depend on JULI (such as Jasper) independently from
- Tomcat. Patch provided by Greg Wilkins. (markt)
- </add>
- <fix>
- <bug>56578</bug>: Correct regression in the fix for <bug>56339</bug>
- that prevented sessions from expiring when using clustering. (markt)
- </fix>
- <fix>
- <bug>56588</bug>: Remove code previously added to enforce the
- requirements of section 4.4 of the Servlet 3.1 specification. The code
- is no longer required now that Jasper initialization has been refactored
- and TLD defined listeners are added via a different code path that
- already enforces the specification requirements. (markt)
- </fix>
- <fix>
- <bug>56600</bug>: In WebdavServlet: Do not waste time generating
- response for broken PROPFIND request. (kkolinko)
- </fix>
- <fix>
- Provide a better error message when asynchronous operations are not
- supported by a filter or servlet. Patch provided by Romain Manni-Bucau.
- (violetagg)
- </fix>
- <fix>
- <bug>56606</bug>: User entries in <code>tomcat-users.xml</code> file
- are recommended to use "username" attribute rather than legacy "name"
- attribute. Fix inconsistencies in Windows installer, examples. Update
- digester rules and documentation for <code>MemoryRealm</code>.
- (markt/kkolinko)
- </fix>
- </changelog>
- </subsection>
- <subsection name="Coyote">
- <changelog>
- <fix>
- <bug>56518</bug>: When using NIO, do not attempt to write to the socket
- if the thread is marked interrupted as this will lead to a connection
- limit leak. This fix was based on analysis of the issue by hanyong.
- (markt)
- </fix>
- <fix>
- <bug>56521</bug>: Re-use the asynchronous write buffer between writes to
- reduce allocation and GC overhead. Based on a patch by leonzhx. Also
- make the buffer size configurable and remove copying of data within
- buffer when the buffer is only partially written on a subsequent write.
- (markt)
- </fix>
- <fix>
- Ensure that a request without a body is correctly handled during Comet
- processing. This fixes the Comet chat example. (markt)
- </fix>
- <fix>
- Fix input concurrency issue in NIO2 upgrade. (remm)
- </fix>
- <fix>
- Correct a copy/paste error and return a 500 response rather than a 400
- response when an internal server error occurs on early stages of
- request processing. (markt)
- </fix>
- <scode>
- <bug>56582</bug>: Use switch(actionCode) in processors instead of a
- chain of "elseif"s. (kkolinko)
- </scode>
- <fix>
- <bug>56582#c1</bug>: Implement DISPATCH_EXECUTE action for AJP
- connectors. (kkolinko)
- </fix>
- <fix>
- Fix CVE-2014-0227:
- Various improvements to ChunkedInputFilter including clean-up, i18n for
- error messages and adding an error flag to allow subsequent attempts at
- reading after an error to fail fast. (markt)
- </fix>
- <fix>
- If request contains an unrecognized Expect header, respond with error
- 417 (Expectation Failed), according to RFC2616 chapter 14.20. (markt)
- </fix>
- <fix>
- When an error occurs after the response has been committed close the
- connection immediately rather than attempting to finish the response to
- make it easier for the client to differentiate between a complete
- response and one that failed part way though. (markt)
- </fix>
- <scode>
- Remove the beta tag from the NIO2 connectors. (remm)
- </scode>
- <fix>
- <bug>56620</bug>: Avoid bogus access log entries when pausing the NIO
- HTTP connector and ensure that access log entries generated by error
- conditions use the correct request start time. (markt)
- </fix>
- <fix>
- Improve configuration of cache sizes in the endpoint. (markt)
- </fix>
- <add>
- Fix CVE-2014-0230:
- Add a new limit, defaulting to 2MB, for the amount of data Tomcat will
- swallow for an aborted upload. The limit is configurable by
- <code>maxSwallowSize</code> attribute of an HTTP connector. (markt)
- </add>
- </changelog>
- </subsection>
- <subsection name="Jasper">
- <changelog>
- <fix>
- <bug>56334#c15</bug>: Fix a regression in EL parsing when quoted string
- follows a whitespace. (kkolinko/markt)
- </fix>
- <update>
- <bug>56543</bug>: Update to the Eclipse JDT Compiler 4.4RC4 to pick up
- some fixes for Java 8 support. (markt/kkolinko)
- </update>
- <fix>
- <bug>56561</bug>: Avoid <code>NoSuchElementException</code> while
- handling attributes with empty string value. (violetagg)
- </fix>
- <scode>
- Do not configure a <code>JspFactory</code> in the
- <code>JasperInitializer</code> if one has already been set as might be
- the case in some embedding scenarios. (markt)
- </scode>
- <add>
- Add a simple implementation of <code>InstanceManager</code> and have
- Jasper use it if no other <code>InstanceManager</code> is provided. This
- makes it easier to use Jasper independently from Tomcat. Patch provided
- by Greg Wilkins. (markt)
- </add>
- <fix>
- <bug>56568</bug>: Allow any HTTP method when a JSP is being used as an
- error page. (markt)
- </fix>
- <update>
- <bug>56581</bug>: If an error on a JSP page occurs when response has
- already been committed, do not clear the buffer of JspWriter, but flush
- it. It will make more clear where the error occurred. (kkolinko)
- </update>
- <fix>
- <bug>56612</bug>: Correctly parse two consecutive escaped single quotes
- when used in UEL expression in a JSP. (markt)
- </fix>
- <update>
- Move code that parses EL expressions within JSP template text from
- <code>Parser</code> to <code>JspReader</code> class for better
- performance. (kkolinko)
- </update>
- <fix>
- <bug>56636</bug>: Correctly identify the required method when specified
- via <code>ELProcessor.defineFunction(String,String,String,String)</code>
- when using Expression Language. (markt)
- </fix>
- <fix>
- <bug>56638</bug>: When using
- <code>ELProcessor.defineFunction(String,String,String,String)</code> and
- no function name is specified, use the method name as the function name
- as required by the specification. (markt)
- </fix>
- </changelog>
- </subsection>
- <subsection name="WebSocket">
- <changelog>
- <scode>
- <bug>56446</bug>: Clearer handling of exceptions when calling a method
- on a POJO based WebSocket endpoint. Based on a suggestion by Eugene
- Chung. (markt)
- </scode>
- <fix>
- When a WebSocket client attempts to write to a closed connection, handle
- the resulting <code>IllegalStateException</code> in a manner consistent
- with the handling of an <code>IOException</code>. (markt)
- </fix>
- <fix>
- Add more varied endpoints for echo testing. (remm)
- </fix>
- <fix>
- <bug>56577</bug>: Improve the executor configuration used for the
- callbacks associated with asynchronous writes. (markt)
- </fix>
- </changelog>
- </subsection>
- <subsection name="Web applications">
- <changelog>
- <fix>
- Set the path for cookies created by the examples web application so they
- only returned to the examples application. This reduces the opportunity
- for using such cookies for malicious purposes should the advice to
- remove the examples web application from security sensitive systems be
- ignored. (markt/kkolinko)
- </fix>
- <fix>
- Attempt to obfuscate session cookie values associated with other web
- applications when viewing HTTP request headers with the Request Header
- example from the examples web application. This reduces the opportunity
- to use this example for malicious purposes should the advice to remove
- the examples web application from security sensitive systems be ignored.
- (markt)
- </fix>
- <add>
- Add options for all of the WebSocket echo endpoints to the WebSocket
- echo example in the examples web application. (markt)
- </add>
- <fix>
- Ensure that the asynchronous WebSocket echo endpoint in the examples
- web application always waits for the previous message to complete before
- it sends the next. (markt)
- </fix>
- </changelog>
- </subsection>
- <subsection name="Other">
- <changelog>
- <update>
- Update package renamed Apache Commons DBCP2 to r1596858. (markt)
- </update>
- </changelog>
- </subsection>
-</section>
-<section name="Tomcat 8.0.8 (markt)" rtext="beta, 2014-05-21">
- <subsection name="Catalina">
- <changelog>
- <fix>
- <bug>56536</bug>: Ensure that
- <code>HttpSessionBindingListener.valueUnbound()</code> uses the correct
- class loader when the <code>SingleSignOn</code> valve is used. (markt)
- </fix>
- </changelog>
- </subsection>
- <subsection name="Jasper">
- <changelog>
- <fix>
- <bug>56529</bug>: Avoid <code>NoSuchElementException</code> while handling
- attributes with empty string value in custom tags. Patch provided by
- Hariprasad Manchi. (violetagg)
- </fix>
- </changelog>
- </subsection>
-</section>
-<section name="Tomcat 8.0.7 (markt)" rtext="not released">
- <subsection name="Catalina">
- <changelog>
- <fix>
- <bug>56523</bug>: When using SPNEGO authentication, log the exceptions
- associated with failed user logins at debug level rather than error
- level. (markt)
- </fix>
- </changelog>
- </subsection>
- <subsection name="Coyote">
- <changelog>
- <add>
- <bug>56399</bug>: Assert that both Coyote and Catalina request objects
- have been properly recycled. (kkolinko)
- </add>
- </changelog>
- </subsection>
- <subsection name="Jasper">
- <changelog>
- <fix>
- <bug>56522</bug>: When setting a value for a
- <code>ValueExpression</code>, ensure that the expected coercions take
- place such as a <code>null</code> string being coerced to an empty
- string. (markt)
- </fix>
- </changelog>
- </subsection>
- <subsection name="Other">
- <changelog>
- <fix>
- Copy missing resources file from Apache Commons DBCP 2 to packaged
- renamed copy of DBCP 2. (markt)
- </fix>
- </changelog>
- </subsection>
-</section>
-<section name="Tomcat 8.0.6 (markt)" rtext="not released">
- <subsection name="Catalina">
- <changelog>
- <fix>
- Fix extension validation which was broken by refactoring for new
- resources implementation. (markt)
- </fix>
- <fix>
- Fix custom UTF-8 decoder so that a byte of value 0xC1 is always rejected
- immediately as it is never valid in a UTF-8 byte sequence. Update UTF-8
- decoder tests to account for UTF-8 decoding improvements in Java 8.
- The custom UTF-8 decoder is still required due to bugs in the UTF-8
- decoder provided by Java. Java 8's decoder is better than Java
- 7's but it is still buggy. (markt)
- </fix>
- <fix>
- <bug>56027</bug>: Add more options for managing FIPS mode in the
- AprLifecycleListener. (schultz/kkolinko)
- </fix>
- <fix>
- <bug>56320</bug>: Fix a file descriptor leak in the default servlet when
- sendfile is used. (markt)
- </fix>
- <fix>
- <bug>56321</bug>: When a WAR is modified, undeploy the web application
- before deleting any expanded directory as the undeploy process may
- refer to classes that need to be loaded from the expanded directory. If
- the expanded directory is deleted first, any attempt to load a new class
- during undeploy will fail. (markt)
- </fix>
- <fix>
- <bug>56327</bug>: Enable AJP as well as HTTP connectors to be created
- via JMX. Patch by kiran. (markt)
- </fix>
- <fix>
- <bug>56339</bug>: Avoid an infinite loop if an application calls
- <code>session.invalidate()</code> from the session destroyed event for
- that session. (markt)
- </fix>
- <scode>
- <bug>56365</bug>: Simplify file name pattern matching code in
- <code>StandardJarScanner</code>. Improve documentation. (kkolinko)
- </scode>
- <fix>
- Ensure that the static resource cache is able to detect when a cache
- entry is invalidated by being overridden by a new resource in a
- different <code>WebResourceSet</code>. (markt)
- </fix>
- <fix>
- <bug>56369</bug>: Ensure that removing an MBean notification listener
- reverts all the operations performed when adding an MBean notification
- listener. (markt)
- </fix>
- <scode>
- Improve implementation of <code>Lifecycle</code> for
- <code>WebappClassLoader</code>. State is now correctly reported rather
- than always reporting as <code>NEW</code>. (markt)
- </scode>
- <add>
- <bug>56382</bug>: Information about finished deployment and its execution
- time is added to the log files. Patch is provided by Danila Galimov.
- (violetagg)
- </add>
- <add>
- <bug>56383</bug>: Properties for disabling server information and error
- report are added to the <code>org.apache.catalina.valves.ErrorReportValve</code>.
- Based on the patch provided by Nick Bunn. (violetagg/kkolinko)
- </add>
- <fix>
- <bug>56390</bug>: Fix JAR locking issue with JARs containing TLDs and
- the TLD cache that prevented the undeployment of web applications when
- the WAR was deleted. (markt)
- </fix>
- <fix>
- Fix CVE-2014-0119:
- Only create XML parsing objects if required and fix associated potential
- memory leak in the default Servlet.
- Extend XML factory, parser etc. memory leak protection to cover some
- additional locations where, theoretically, a memory leak could occur.
- (markt)
- </fix>
- <fix>
- Modify generic exception handling so that
- <code>StackOverflowError</code> is not treated as a fatal error and can
- handled and/or logged as required. (markt)
- </fix>
- <fix>
- <bug>56409</bug>: Avoid <code>StackOverflowError</code> on non-Windows
- systems if a file named <code>\</code> is encountered when scanning for
- TLDs. (markt)
- </fix>
- <add>
- <bug>56430</bug>: Extend checks for suspicious URL patterns to include
- patterns of the form <code>*.a.b</code> which are not valid patterns for
- extension mappings. (markt)
- </add>
- <fix>
- <bug>56441</bug>: Raise the visibility of exceptions thrown when a
- problem is encountered calling a getter or setter on a component
- attribute. The logging level is raised from debug to warning. (markt)
- </fix>
- <add>
- <bug>56463</bug>: Property for disabling server information is added to
- the <code>DefaultServlet</code>. Server information is presented in the
- response sent to the client when directory listings is enabled.
- (violetagg)
- </add>
- <fix>
- <bug>56472</bug>: Allow NamingContextListener to clean up on stop if its
- start failed. (kkolinko)
- </fix>
- <fix>
- <bug>56481</bug>: Work around case insensitivity issue in
- <code>URLClassLoader</code> exposed by some recent refactoring. (markt)
- </fix>
- <add>
- <bug>56492</bug>: Avoid eclipse debugger pausing on uncaught exceptions
- when tomcat renews its threads. (slaurent)
- </add>
- <add>
- Add the <code>org.apache.naming</code> package to the packages requiring
- code to have the <code>defineClassInPackage</code> permission when
- running under a security manager. (markt)
- </add>
- <fix>
- Make the naming context tokens for containers more robust by using a
- separate object. Require RuntimePermission when introducing a new token.
- (markt/kkolinko)
- </fix>
- <fix>
- <bug>56501</bug>: <code>HttpServletRequest.getContextPath()</code>
- should return the undecoded context path used by the user agent. (markt)
- </fix>
- <fix>
- Minor fixes to <code>ThreadLocalLeakPreventionListener</code>. Do not
- trigger threads renewal for failed contexts. Do not ignore
- <code>threadRenewalDelay</code> setting. Improve documentation. (kkolinko)
- </fix>
- <fix>
- Correct regression introduced in <rev>1239520</rev> that broke loading
- of users from <code>tomcat-users.xml</code> when using the
- <code>JAASMemoryLoginModule</code>. (markt)
- </fix>
- <fix>
- Correct regression introduced in <rev>797162</rev> that broke
- authentication of users when using the
- <code>JAASMemoryLoginModule</code>. (markt)
- </fix>
- </changelog>
- </subsection>
- <subsection name="Coyote">
- <changelog>
- <fix>
- More cleanup of NIO2 endpoint shutdown. (remm)
- </fix>
- <fix>
- <bug>56336</bug>: AJP output corruption and errors. (remm)
- </fix>
- <fix>
- Handle various cases of incomplete writes in NIO2. (remm)
- </fix>
- <scode>
- Code cleanups and i18n in NIO2. (remm)
- </scode>
- <fix>
- Fix extra onDataAvailable calls in the NIO2 connector. (remm)
- </fix>
- <fix>
- Fix gather writes in NIO2 SSL. (remm)
- </fix>
- <scode>
- Upgrade the NIO2 connectors to beta, but still not ready for production. (remm)
- </scode>
- <scode>
- Fix code duplication between NIO and NIO2. (remm)
- </scode>
- <fix>
- <bug>56348</bug>: Fix slow asynchronous read when read was performed on
- a non-container thread. (markt)
- </fix>
- <fix>
- <bug>56416</bug>: Correct documentation for default value of socket
- linger for the AJP and HTTP connectors. (markt)
- </fix>
- <fix>
- Fix possible corruption if doing keepalive after a comet request. (remm)
- </fix>
- <fix>
- <bug>56518</bug>: Fix connection limit latch leak when a non-container
- thread is interrupted during asynchronous processing. (markt)
- </fix>
- </changelog>
- </subsection>
- <subsection name="Jasper">
- <changelog>
- <fix>
- <bug>56334</bug>: Fix a regression in the handling of back-slash
- escaping introduced by the fix for <bug>55735</bug>. (markt/kkolinko)
- </fix>
- <fix>
- <bug>56425</bug>: Improve method matching for EL expressions. When
- looking for matching methods, an exact match between parameter types is
- preferred followed by an assignable match followed by a coercible match.
- (markt)
- </fix>
- <fix>
- Correct the handling of back-slash escaping in the EL parser and no
- longer require that <code>\$</code> or <code>\#</code> must be followed
- by <code>{</code> in order for the back-slash escaping to take effect.
- (markt)
- </fix>
- </changelog>
- </subsection>
- <subsection name="Cluster">
- <changelog>
- <scode>
- Remove the implementation of
- <code>org.apache.catalina.LifecycleListener</code> from
- <code>org.apache.catalina.ha.tcp.SimpleTcpCluster</code>.
- <code>SimpleTcpCluster</code> does not work as
- <code>LifecycleListener</code>, it works as nested components of Host or
- Engine. (kfujino)
- </scode>
- <fix>
- Remove cluster and replicationValve from cluster manager template. These
- instance are not necessary to template. (kfujino)
- </fix>
- <fix>
- Add support for cross context session replication to
- <code>org.apache.catalina.ha.session.BackupManager</code>. (kfujino)
- </fix>
- <fix>
- Remove the unnecessary cross context check. It does not matter whether
- the context that is referenced by other context is set to
- <code>crossContext</code>=true. The context that refers to the different
- context must be set to <code>crossContext</code>=true. (kfujino)
- </fix>
- <scode>
- Move to <code>org.apache.catalina.ha.session.ClusterManagerBase</code>
- common logics of
- <code>org.apache.catalina.ha.session.BackupManager</code> and
- <code>org.apache.catalina.ha.session.DeltaManager</code>. (kfujino)
- </scode>
- <scode>
- Simplify the code of <code>o.a.c.ha.tcp.SimpleTcpCluster</code>. In
- order to add or remove cluster valve to Container, use pipeline instead
- of <code>IntrospectionUtils</code>. (kfujino)
- </scode>
- <fix>
- There is no need to set cluster instance when
- <code>SimpleTcpCluster.unregisterClusterValve</code> is called.
- Set null than cluster instance for cleanup. (kfujino)
- </fix>
- </changelog>
- </subsection>
- <subsection name="WebSocket">
- <changelog>
- <fix>
- <bug>56343</bug>: Avoid a NPE if Tomcat's Java WebSocket 1.0
- implementation is used with the Java WebSocket 1.0 API JAR from the
- reference implementation. (markt)
- </fix>
- <fix>
- Increase the default maximum size of the executor used by the WebSocket
- implementation for call backs associated with asynchronous writes from
- 10 to 200. (markt)
- </fix>
- <add>
- Add a warning if the thread group created for WebSocket asynchronous
- write call backs can not be destroyed when the web application is
- stopped. (markt)
- </add>
- <fix>
- Ensure that threads created to support WebSocket clients are stopped
- when no longer required. This will happen automatically for WebSocket
- client connections initiated by web applications but stand alone clients
- must call <code>WsWebSocketContainer.destroy()</code>. (markt)
- </fix>
- <fix>
- <bug>56449</bug>: When creating a new session, add the message handlers
- to the session before calling <code>Endpoint.onOpen()</code> so the
- message handlers are in place should the <code>onOpen()</code> method
- trigger the sending of any messages. (markt)
- </fix>
- <fix>
- <bug>56458</bug>: Report WebSocket sessions that are created over secure
- connections as secure rather than as not secure. (markt)
- </fix>
- <fix>
- Stop threads used for secure WebSocket client connections when they are
- no longer required and give them better names for easier debugging while
- they are running. (markt)
- </fix>
- </changelog>
- </subsection>
- <subsection name="Web applications">
- <changelog>
- <fix>
- Add Support for <code>copyXML</code> attribute of Host to Host Manager.
- (kfujino)
- </fix>
- <fix>
- Ensure that "name" request parameter is used as a application base of
- host if "webapps" request parameter is not set when adding host in
- HostManager Application. (kfujino)
- </fix>
- <fix>
- Correct documentation on Windows service options, aligning it with
- Apache Commons Daemon documentation. (kkolinko)
- </fix>
- <fix>
- <bug>56418</bug>: Ensure that the Manager web application does not
- report success for a web application deployment that fails. (slaurent)
- </fix>
- <update>
- Improve valves documentation. Split valves into groups. (kkolinko)
- </update>
- <fix>
- <bug>56513</bug>: Make the documentation crystal clear that using
- sendfile will disable any compression that Tomcat may otherwise have
- applied to the response. (markt)
- </fix>
- </changelog>
- </subsection>
- <subsection name="Other">
- <changelog>
- <scode>
- Review source code and take advantage of Java 7's
- try-with-resources syntax where possible. (markt)
- </scode>
- <fix>
- Align DisplayName of Tomcat installed by <code>service.bat</code> with
- one installed by the *.exe installer. Print a warning in case if neither
- server nor client jvm is found by <code>service.bat</code>. (kkolinko)
- </fix>
- <update>
- <bug>56363</bug>: Update to version 1.1.30 of Tomcat Native library.
- (schultz)
- </update>
- <update>
- Update package renamed Apache Commons BCEL to r1593495 to pick up some
- additional changes for Java 7 support and some code clean up. (markt)
- </update>
- <update>
- Update package renamed Apache Commons FileUpload to r1569132 to pick up
- some small improvements (e.g. better <code>null</code> protection) and
- some code clean up. (markt)
- </update>
- <update>
- Update package renamed Apache Commons Codec to r1586336 to pick up some
- Javadoc fixes and some code clean up. (markt)
- </update>
- <scode>
- Switch to including Apache Commons DBCP via a package renamed svn copy
- rather than building from a source release for consistency with other
- Commons packages and to allow faster releases to fix DBCP related
- issues. (markt)
- </scode>
- <update>
- Update package renamed Apache Commons Pool2 and DBCP2 to r1593563 to
- pick various bug fixes. (markt)
- </update>
- <add>
- In tests: allow to configure directory where JUnit reports and access
- log are written to. (kkolinko)
- </add>
- </changelog>
- </subsection>
-</section>
-<section name="Tomcat 8.0.5 (markt)" rtext="beta, 2014-03-27">
- <subsection name="Catalina">
- <changelog>
- <fix>
- Rework the fix for <bug>56190</bug> as the previous fix did not recycle
- the request in all cases leading to mis-routing of requests. (markt)
- </fix>
- <fix>
- Allow web applications to package tomcat-jdbc.jar and their JDBC driver
- of choice in the web application. (markt)
- </fix>
- <fix>
- <bug>56293</bug>: Cache resources loaded by the class loader from
- <code>/META-INF/services/</code> for better performance for repeated
- look ups. (markt)
- </fix>
- </changelog>
- </subsection>
- <subsection name="Coyote">
- <changelog>
- <fix>
- Fix possibly incomplete final flush with NIO2 when using non blocking
- mode. (remm)
- </fix>
- <fix>
- Cleanup NIO2 endpoint shutdown. (remm)
- </fix>
- <fix>
- Fix rare race condition notifying onWritePossible in the NIO2
- HTTP/1.1 connector. (remm)
- </fix>
- </changelog>
- </subsection>
- <subsection name="Jasper">
- <changelog>
- <fix>
- <bug>54475</bug>: Add Java 8 support to SMAP generation for JSPs. Patch
- by Robbie Gibson. (markt)
- </fix>
- </changelog>
- </subsection>
- <subsection name="Web applications">
- <changelog>
- <fix>
- <bug>56273</bug>: If the Manager web application does not perform an
- operation because the web application is already being serviced, report
- an error rather than reporting success. (markt)
- </fix>
- <fix>
- <bug>56304</bug>: Add a note to the documentation about not using
- WebSocket with BIO HTTP in production. (markt)
- </fix>
- </changelog>
- </subsection>
-</section>
-<section name="Tomcat 8.0.4 (markt)" rtext="not released">
- <subsection name="Catalina">
- <changelog>
- <fix>
- Restore the ability to use the <code>addURL()</code> method of the
- web application class loader to add external resources to the web
- application. (markt)
- </fix>
- <fix>
- Improve the robustness of web application undeployment based on some
- code analysis triggered by the report for <bug>54315</bug>. (markt)
- </fix>
- <fix>
- <bug>56125</bug>: Correctly construct the URL for a resource that
- represents the root of a JAR file. (markt)
- </fix>
- <fix>
- Generate a valid root element for the effective web.xml for a web
- application for all supported versions of web.xml. (markt)
- </fix>
- <add>
- Make it easier for applications embedding and/or extending Tomcat to
- modify the <code>javaseClassLoader</code> attribute of the
- <code>WebappClassLoader</code>. (markt)
- </add>
- <fix>
- Add missing support for <code><deny-uncovered-http-methods></code>
- element when merging web.xml files. (markt)
- </fix>
- <fix>
- Improve merging process for web.xml files to take account of the
- elements and attributes supported by the Servlet version of the merged
- file. (markt)
- </fix>
- <fix>
- Avoid <code>NullPointerException</code> in resource cache when making an
- invalid request for a resource outside of the web application. (markt)
- </fix>
- <fix>
- Remove an unnecessary null check identified by FindBugs. (markt)
- </fix>
- <add>
- In WebappClassLoader, when reporting threads that are still running
- while web application is being stopped, print their stack traces to
- the log. (kkolinko)
- </add>
- <fix>
- <bug>56190</bug>: The response should be closed (i.e. no further output
- is permitted) when a call to <code>AsyncContext.complete()</code> takes
- effect. (markt)
- </fix>
- <fix>
- <bug>56236</bug>: Enable Tomcat to work with alternative Servlet and
- JSP API JARs that package the XML schemas in such as way as to require
- a dependency on the JSP API before enabling validation for web.xml.
- Tomcat has no such dependency. (markt)
- </fix>
- <fix>
- <bug>56244</bug>: Fix MBeans descriptor for WebappClassLoader MBean.
- (kkolinko)
- </fix>
- <add>
- Add a work around for validating XML documents (often TLDs) that use
- just the file name to refer to the JavaEE schema on which they
- are based. (markt)
- </add>
- <add>
- Add methods of get the idle time from last client access time to
- <code>org.apache.catalina.Session</code>. (kfujino)
- </add>
- <fix>
- <bug>56246</bug>: Fix NullPointerException in MemoryRealm when
- authenticating an unknown user. (markt)
- </fix>
- <fix>
- <bug>56248</bug>: Allow the deployer to update an existing WAR file
- without undeploying the existing application if the update flag is set.
- This allows any existing custom context.xml for the application to be
- retained. To update an application and remove any existing context.xml
- simply undeploy the old version of the application before deploying the
- new version. (markt)
- </fix>
- <fix>
- <bug>56253</bug>: When listing resources that are provided by a JAR, fix
- possible <code>StringIndexOutOfBoundsException</code>s. Add some unit
- tests for this and similar scenarios and fix the additional issues those
- unit tests identified. Based on a patch by Larry Isaacs. (markt)
- </fix>
- <fix>
- Fix CVE-2014-0096:
- Redefine the <code>globalXsltFile</code> initialisation parameter of the
- DefaultServlet as relative to CATALINA_BASE/conf or CATALINA_HOME/conf.
- Prevent user supplied XSLTs used by the DefaultServlet from defining
- external entities. (markt)
- </fix>
- </changelog>
- </subsection>
- <subsection name="Coyote">
- <changelog>
- <fix>
- In some circumstances asynchronous requests could time out too soon.
- (markt)
- </fix>
- <fix>
- <bug>56172</bug>: Avoid possible request corruption when using the AJP
- NIO connector and a request is sent using more than one AJP message.
- Patch provided by Amund Elstad. (markt)
- </fix>
- <add>
- Add experimental NIO2 connector. Based on code developed by
- Nabil Benothman. (remm)
- </add>
- <fix>
- Fix CVE-2014-0075:
- Improve processing of chuck size from chunked headers. Avoid overflow
- and use a bit shift instead of a multiplication as it is marginally
- faster. (markt/kkolinko)
- </fix>
- <fix>
- Fix CVE-2014-0095:
- Correct regression introduced in 8.0.0-RC2 as part of the Servlet 3.1
- non-blocking IO support that broke handling of requests with an explicit
- content length of zero. (markt/kkolinko)
- </fix>
- <fix>
- Fix CVE-2014-0099:
- Fix possible overflow when parsing long values from a byte array.
- (markt)
- </fix>
- </changelog>
- </subsection>
- <subsection name="Jasper">
- <changelog>
- <fix>
- Change the default compiler source and compiler target versions to 1.7
- since Tomcat 8 requires a minimum of Java 7. (markt)
- </fix>
- <fix>
- <bug>56179</bug>: Fix parsing of EL expressions that contain unnecessary
- parentheses. (markt)
- </fix>
- <fix>
- <bug>56177</bug>: Handle dependency tracking for TLDs when using JspC
- with a tag library JAR that is located outside of the web application.
- (markt)
- </fix>
- <fix>
- Remove an unnecessary null check identified by FindBugs. (markt)
- </fix>
- <fix>
- <bug>56199</bug>: Restore validateXml option for JspC which determines
- if web.xml will be parsed with a validating parser. (markt)
- </fix>
- <fix>
- <bug>56223</bug>: Throw an <code>IllegalStateException</code> if a call
- is made to <code>ServletContext.setInitParameter()</code> after the
- ServletContext has been initialized. (markt)
- </fix>
- <fix>
- <bug>56265</bug>: Do not escape values of dynamic tag attributes
- containing EL expressions. (kkolinko)
- </fix>
- <fix>
- Make the default compiler source and target versions for JSPs Java 7
- since Tomcat 8 requires Java 7 as a minimum. (markt)
- </fix>
- <update>
- <bug>56283</bug>: Update to the Eclipse JDT Compiler P20140317-1600
- which adds support for Java 8 syntax to JSPs. Add support for value
- "1.8" for the <code>compilerSourceVM</code> and
- <code>compilerTargetVM</code> options. (markt)
- </update>
- </changelog>
- </subsection>
- <subsection name="WebSocket">
- <changelog>
- <fix>
- Avoid a possible deadlock when one thread is shutting down a connection
- while another thread is trying to write to it. (markt)
- </fix>
- <fix>
- Avoid NPE when flushing batched messages. (markt)
- </fix>
- </changelog>
- </subsection>
- <subsection name="Web Applications">
- <changelog>
- <add>
- <bug>56093</bug>: Add the SSL Valve to the documentation web
- application. (markt)
- </add>
- <fix>
- <bug>56217</bug>: Improve readability by using left alignment for the
- table cell containing the request information on the Manager application
- status page. (markt)
- </fix>
- <fix>
- Fixed <code>java.lang.NegativeArraySizeException</code> when using
- "Expire sessions" command in the manager web application on a
- context where the session timeout is disabled. (kfujino)
- </fix>
- <fix>
- Add support for <code>LAST_ACCESS_AT_START</code> system property to
- Manager web application. (kfujino)
- </fix>
- </changelog>
- </subsection>
- <subsection name="Other">
- <changelog>
- <fix>
- <bug>56115</bug>: Expose the <code>httpusecaches</code> property of
- Ant's <code>get</code> task as some users may need to change the
- default. Based on a suggestion by Anthony. (markt)
- </fix>
- <fix>
- <bug>56143</bug>: Improve <code>service.bat</code> so that it can be
- launched from a non-UAC console. This includes using a single call to
- <code>tomcat8.exe</code> to install the Windows service rather than
- three calls, and using command line arguments instead of environment
- variables to pass the settings. (markt/kkolinko)
- </fix>
- <scode>
- Simplify Windows *.bat files: remove %OS% checks, as current java does
- not run on ancient non-NT operating systems. (kkolinko)
- </scode>
- <fix>
- Align options between <code>service.bat</code> and <code>exe</code>
- Windows installer. For <code>service.bat</code> the changes are in
- --Classpath, --DisplayName, --StartPath, --StopPath. For
- <code>exe</code> installer the changes are in --JvmMs, --JvmMx options,
- which are now 128 Mb and 256 Mb respectively instead of being empty.
- Explicitly specify --LogPath path when uninstalling Windows service,
- avoiding default value for that option. (kkolinko)
- </fix>
- <fix>
- <bug>56137</bug>: Explicitly use NIO connector in SSL example in
- server.xml so it doesn't break if APR is enabled. (markt)
- </fix>
- <fix>
- <bug>56139</bug>: Avoid a web application class loader leak in some unit
- tests when running on Windows. (markt)
- </fix>
- <fix>
- Correct build script to avoid building JARs with empty packages. (markt)
- </fix>
- <add>
- Allow to limit JUnit test run to a number of selected test case
- methods. (kkolinko)
- </add>
- <update>
- Update Commons Pool 2 to 2.2. (markt)
- </update>
- <update>
- Update Commons DBCP 2 to the 2.0 release. (markt)
- </update>
- <fix>
- <bug>56189</bug>: Remove used file cpappend.bat from the distribution.
- (markt)
- </fix>
- <fix>
- <bug>56204</bug>: Remove unnecessary dependency between tasks in the
- build script. (markt)
- </fix>
- <fix>
- Add definition of <code>org.apache.catalina.ant.FindLeaksTask</code>.
- (kfujino)
- </fix>
- <fix>
- Implement <code>org.apache.catalina.ant.VminfoTask</code>,
- <code>org.apache.catalina.ant.ThreaddumpTask</code> and
- <code>org.apache.catalina.ant.SslConnectorCiphersTask</code>. (kfujino)
- </fix>
- <add>
- Add the option to the Apache Ant tasks to ignore the constraint of the
- first line of the response message that must be "OK -"
- (<code>ignoreResponseConstraint</code> in <code>AbstractCatalinaTask</code>).
- Default is false. (kfujino)
- </add>
- </changelog>
- </subsection>
-</section>
-<section name="Tomcat 8.0.3 (markt)" rtext="beta, 2014-02-11">
- <subsection name="Other">
- <changelog>
- <fix>
- Fix build of Apache Commons DBCP2 classes. (kkolinko)
- </fix>
- <update>
- Update Commons DBCP 2 to snapshot 170 dated 07 Feb 2014. This enables
- DBCP to work with a SecurityManager such that only DBCP needs to be
- granted the necessary permissions to communicate with the database.
- (markt)
- </update>
- </changelog>
- </subsection>
-</section>
-<section name="Tomcat 8.0.2 (markt)" rtext="not released">
- <subsection name="Catalina">
- <changelog>
- <fix>
- <bug>56082</bug>: Fix a concurrency bug in JULI's LogManager
- implementation. (markt)
- </fix>
- <fix>
- <bug>56085</bug>: <code>ServletContext.getRealPath(String)</code> should
- return <code>null</code> for invalid input rather than throwing an
- <code>IllegalArgumentException</code>. (markt)
- </fix>
- <fix>
- Fix WebDAV support that was broken by the refactoring for the new
- resources implementation. (markt)
- </fix>
- <scode>
- Simplify Catalina.initDirs(). (kkolinko)
- </scode>
- <fix>
- <bug>56096</bug>: When the attribute <code>rmiBindAddress</code> of the
- JMX Remote Lifecycle Listener is specified it's value will be used when
- constructing the address of a JMX API connector server. Patch is
- provided by Jim Talbut. (violetagg)
- </fix>
- <fix>
- When environment entry with one and the same name is defined in the web
- deployment descriptor and with annotation then the one specified in the
- web deployment descriptor is with priority. (violetagg)
- </fix>
- <fix>
- Fix passing the value of false for <code>xmlBlockExternal</code> option
- of Context to Jasper, as the default was changed in 8.0.1. (kkolinko)
- </fix>
- </changelog>
- </subsection>
- <subsection name="Coyote">
- <changelog>
- <fix>
- Enable non-blocking reads to take place on non-container threads.
- (markt)
- </fix>
- </changelog>
- </subsection>
- <subsection name="Cluster">
- <changelog>
- <scode>
- Simplify the code of
- <code>o.a.c.ha.tcp.SimpleTcpCluster.createManager(String)</code>.
- Remove unnecessary class cast. (kfujino)
- </scode>
- </changelog>
- </subsection>
- <subsection name="Web applications">
- <changelog>
- <fix>
- In Manager web application improve handling of file upload errors.
- Display a message instead of error 500 page. Simplify. (kkolinko)
- </fix>
- </changelog>
- </subsection>
- <subsection name="Other">
- <changelog>
- <fix>
- <bug>56104</bug>: Correct the version number on the welcome page of the
- Windows installer. (markt)
- </fix>
- <update>
- Update Commons DBCP 2 to snapshot 168 dated 05 Feb 2014. (markt)
- </update>
- <fix>
- Fix CVE-2014-0050, a denial of service with a malicious, malformed
- Content-Type header and multipart request processing. Fixed by merging
- latest code (r1565159) from Commons FileUpload. (markt)
- </fix>
- </changelog>
- </subsection>
-</section>
-<section name="Tomcat 8.0.1 (markt)" rtext="beta, 2014-02-02">
- <subsection name="Catalina">
- <changelog>
- <fix>
- Change default value of <code>xmlBlockExternal</code> attribute of
- Context. It is <code>true</code> now. (kkolinko)
- </fix>
- </changelog>
- </subsection>
- <subsection name="Coyote">
- <changelog>
- <fix>
- Correct regression in the fix for <bug>55996</bug> that meant that
- asynchronous requests might timeout too early. (markt)
- </fix>
- </changelog>
- </subsection>
- <subsection name="Jasper">
- <changelog>
- <fix>
- Change default value of the <code>blockExternal</code> attribute of
- JspC task. The default value is <code>true</code>. Add support for
- <code>-no-blockExternal</code> switch when JspC is run as a
- standalone application. (kkolinko)
- </fix>
- </changelog>
- </subsection>
- <subsection name="WebSocket">
- <changelog>
- <fix>
- Do not return an empty string for the
- <code>Sec-WebSocket-Protocol</code> HTTP header when no sub-protocol has
- been requested or no sub-protocol could be agreed as RFC6455 requires
- that no <code>Sec-WebSocket-Protocol</code> header is returned in this
- case. (markt)
- </fix>
- </changelog>
- </subsection>
-</section>
-<section name="Tomcat 8.0.0 (markt)" rtext="not released">
- <subsection name="Catalina">
- <changelog>
- <add>
- Implement JSR 340 - Servlet 3.1. The JSR 340 implementation includes
- contributions from Nick Williams and Jeremy Boynes. (markt)
- </add>
- <add>
- Implement JSR 245 MR2 - JSP 2.3. (markt)
- </add>
- <add>
- Implement JSR 341 - Unified Expression Language 3.0. (markt)
- </add>
- <add>
- Implement JSR 356 - WebSockets. The JSR 356 implementation includes
- contributions from Nick Williams, Rossen Stoyanchev and Niki Dokovski.
- (markt)
- </add>
- <update>
- <bug>46727</bug>: Refactor default servlet to make it easier to
- sub-class to implement finer grained control of the file encoding. Based
- on a patch by Fred Toth. (markt)
- </update>
- <add>
- <bug>45995</bug>: Align Tomcat with Apache httpd and perform MIME type
- mapping based on file extension in a case insensitive manner. (markt)
- </add>
- <scode>
- Remove duplicate code that converted a Host's appBase attribute to
- a canonical file. (markt)
- </scode>
- <scode>
- <bug>51408</bug>: Replace calls to <code>Charset.defaultCharset()</code>
- with an explicit reference to the ISO-8859-1 Charset. (markt)
- </scode>
- <scode>
- Refactor initialization code to use a single, consistent approach to
- determining the Catalina home (binary) and base (instance) directories.
- The search order for home is <code>catalina.home</code> system property,
- parent of current directory if boootstrap.jar is present and finally
- current working directory. The search order for Catalina base is
- <code>catalina.base</code> system property falling back to the value for
- Catalina home. (markt)
- </scode>
- <update>
- <bug>52092</bug>: JULI now uses the <code>OneLineFormatter</code> and
- <code>AsyncFileHandler</code> by default. (markt)
- </update>
- <fix>
- <bug>52558</bug>: Refactor <code>CometConnectionManagerValve</code> so
- that it does not prevent the session from being serialized in when
- running in a cluster. (markt)
- </fix>
- <fix>
- <bug>52767</bug>: Remove reference to MySQL specific autoReconnect
- property in <code>JDBCAccessLogValve</code>. (markt)
- </fix>
- <scode>
- Make the Mapper type-safe. Hosts, Contexts and Wrappers are no
- longer handled as plain objects, instead they keep their type.
- Code using the Mapper doesn't need to cast objects returned by
- the mapper. (rjung)
- </scode>
- <scode>
- Move Manager, Loader and Resources from Container to Context since
- Context is the only place they are used. The documentation already
- states (and has done for some time) that Context is the only valid
- location for these nested components. (markt)
- </scode>
- <scode>
- Move the Mapper from the Connector to the Service since the Mapper is
- identical for all Connectors of a given Service and it is common for
- there to be multiple Connectors for a Service (http, https and ajp).
- This means there is now only ever one Mapper per Service rather than
- possibly multiple identically configured Mapper objects. (markt)
- </scode>
- <scode>
- Remove the per Context Mapper objects and use the Mapper from the
- Service. This removes the need to maintain two copies of the mappings
- for Servlets and Filters. (markt)
- </scode>
- <add>
- Implement a new Resources implementation that merges Aliases,
- VirtualLoader, VirtualDirContext, JAR resources and external
- repositories into a single framework rather than a separate one for each
- feature. (markt)
- </add>
- <add>
- URL rewrite valve, similar in functionality to mod_rewrite. (remm)
- </add>
- <add>
- Port storeconfig functionality, which can persist to server.xml and
- context.xml runtime container configuration changes. (remm)
- </add>
- <add>
- <bug>54095</bug>: Add support to the Default Servlet for serving
- gzipped versions of static resources directly from disk as an
- alternative to Tomcat compressing them on each request. Patch by
- Philippe Marschall. (markt)
- </add>
- <fix>
- <bug>54708</bug>: Change the name of the working directory for the ROOT
- application (located under $CATALINA_BASE/work by default) from _ to
- ROOT. (markt)
- </fix>
- <add>
- Change default configuration so that a change to the global web.xml file
- will trigger a reload of all web applications. (markt)
- </add>
- <fix>
- <bug>55101</bug>: Make BASIC authentication more tolerant of whitespace.
- Patch provided by Brian Burch. (markt)
- </fix>
- <fix>
- <bug>55166</bug>: Move JSP descriptor and tag library descriptor schemas
- to servlet-api.jar to enable relative references between the schemas to
- be correctly resolved. (markt)
- </fix>
- <scode>
- Refactor the descriptor parsing code into a separate module that can be
- used by both Catalina and Jasper. Includes patches provided by Jeremy
- Boynes. (violetagg/markt)
- </scode>
- <scode>
- <bug>55246</bug>: Move TLD scanning to a ServletContainerInitializer
- provided by Jasper. Includes removal of TldConfig lifecycle listener and
- associated Context properties. (jboynes)
- </scode>
- <add>
- <bug>55317</bug>: Facilitate weaving by allowing ClassFileTransformer to
- be added to WebappClassLoader. Patch by Nick Williams. (markt)
- </add>
- <fix>
- <bug>55620</bug>: Enable Tomcat to start when either $CATALINA_HOME
- and/or $CATALINA_BASE contains a comma character. Prevent Tomcat from
- starting when $CATALINA_HOME and/or $CATALINA_BASE contains a semi-colon
- on Windows. Prevent Tomcat from starting when $CATALINA_HOME and/or
- $CATALINA_BASE contains a colon on Linux/FreeBSD/etc. (markt)
- </fix>
- <scode>
- Initialize the JSP runtime in Jasper's initializer to avoid need for a
- Jasper-specific lifecycle listener. <code>JasperListener</code> has been
- removed. (jboynes)
- </scode>
- <fix>
- Change ordering of elements of JMX objects names so components are
- grouped more logically in JConsole. Generally, components are now
- grouped by Host and then by Context. (markt)
- </fix>
- <add>
- Context listener to allow better EE and framework integration. (remm)
- </add>
- <fix>
- <bug>57896</bug>: Support defensive copying of "cookie" header so that
- unescaping double quotes in a cookie value does not corrupt original
- value of "cookie" header. This is an opt-in feature, enabled by
- <code>org.apache.tomcat.util.http.ServerCookie.PRESERVE_COOKIE_HEADER</code>
- system property. (remm/kkolinko)
- </fix>
- </changelog>
- </subsection>
- <subsection name="Coyote">
- <changelog>
- <add>
- Experimental support for SPDY. Includes contributions from Sheldon Shao.
- (costin)
- </add>
- <scode>
- The default connector is now the Java NIO connector even when specifying
- HTTP/1.1 as protocol (fhanik)
- </scode>
- <scode>
- Update default value of pollerThreadCount for the NIO connector. The new
- default value will never go above 2 regardless of available processors.
- (fhanik)
- </scode>
- <fix>
- <bug>54010</bug>: Remove some unnecessary code (duplicate calls to
- configure the scheme as https for AJP requests originally received over
- HTTPS). (markt)
- </fix>
- <scode>
- Refactor char encoding/decoding using NIO APIs. (remm)
- </scode>
- <update>
- Change the default URIEncoding for all connectors from ISO-8859-1 to
- UTF-8. (markt)
- </update>
- </changelog>
- </subsection>
- <subsection name="Jasper">
- <changelog>
- <scode>
- Simplify API of <code>ErrorDispatcher</code> class by using varargs.
- (kkolinko)
- </scode>
- <scode>
- Update Jasper to use the new common web.xml parsing code. Includes
- patches by Jeremy Boynes. (markt/violetagg)
- </scode>
- <add>
- Create test cases for JspC. Patch by Jeremy Boynes. (markt)
- </add>
- <scode>
- <bug>55246</bug>: TLD scanning is now performed by JasperInitializer
- (a ServletContainerInitializer) removing the need for support within the
- Servlet container itself. The scan is now performed only once rather than
- in two passes reducing startup time. (jboynes)
- </scode>
- <fix>
- <bug>55251</bug>: Do not allow JspC task to fail silently if the web.xml
- or web.xml fragment can not be generated. (markt)
- </fix>
- </changelog>
- </subsection>
- <subsection name="Cluster">
- <changelog>
- <scode>
- Remove unused JvmRouteSessionIDBinderListener and SessionIDMessage.
- (kfujino)
- </scode>
- <scode>
- Modify method signature in ReplicationValve. Cluster instance is not
- necessary to argument of method. (kfujino)
- </scode>
- <scode>
- Remove unused <code>expireSessionsOnShutdown</code> attribute in
- <code>org.apache.catalina.ha.session.BackupManager</code>. (kfujino)
- </scode>
- </changelog>
- </subsection>
- <subsection name="Web applications">
- <changelog>
- <add>
- Extend the diagnostic information provided by the Manager web
- application to include details of the configured SSL ciphers suites for
- each connector. (markt)
- </add>
<update>
- <bug>48550</bug>: Update examples web application to use UTF-8. (markt)
- </update>
- <update>
- <bug>55383</bug>: Improve the design and correct the HTML markup of
- the documentation web application. Patches provided by Konstantin
- Preißer. (markt)
+ Update the packaged version of the Tomcat Native Library to 1.2.5 to
+ pick up the Windows binaries that are based on OpenSSL 1.0.2g and APR
+ 1.5.1. (markt)
</update>
- </changelog>
- </subsection>
- <subsection name="Tribes">
- <changelog>
- <scode>
- Refactor <code>AbstractReplicatedMap</code> to use generics. A key
- side-effect of this is that the class now implements
- <code>Map<K,V></code> rather than extends
- <code>ConcurrentMap</code>. (markt)
- </scode>
- </changelog>
- </subsection>
- <subsection name="Other">
- <changelog>
- <scode>
- Remove unused, deprecated code. (markt)
- </scode>
- <scode>
- Remove static info String and associated getInfo() method where present.
- (markt)
- </scode>
<update>
- (<rev>1353242</rev>, <rev>1353410</rev>):
- Remove Ant tasks <code>jasper2</code> and <code>jkstatus</code>.
- The correct names are <code>jasper</code> and <code>jkupdate</code>.
- (kkolinko)
+ Modify the default <code>tomcat-users.xml</code> file to make it harder
+ for users to configure the entries intended for use with the examples
+ web application for the Manager application. (markt)
</update>
- <fix>
- <bug>53529</bug>: Clean-up the handling of
- <code>InterruptedException</code> throughout the code base. (markt)
- </fix>
- <add>
- <bug>54899</bug>: Provide an initial implementation of NetBeans support.
- Patch provided by Brian Burch. (markt)
- </add>
- <fix>
- <bug>55166</bug>: Move the JSP descriptor and tag library descriptor
- schema definition files from jsp-api.jar to servlet-api.jar so relative
- includes between the J2EE, Servlet and JSP schemas are correctly
- resolved. (markt)
- </fix>
- <fix>
- <bug>55372</bug>: When starting Tomcat with the <code>jpda</code> option
- to enable remote debugging, by default only listen on localhost for
- connections from a debugger. Prior to this change, Tomcat listened on
- all known addresses. (markt)
- </fix>
</changelog>
</subsection>
</section>
diff --git a/webapps/docs/class-loader-howto.xml b/webapps/docs/class-loader-howto.xml
index 779f299..a512fdf 100644
--- a/webapps/docs/class-loader-howto.xml
+++ b/webapps/docs/class-loader-howto.xml
@@ -176,9 +176,10 @@ When a request to load a
class from the web application's <em>WebappX</em> class loader is processed,
this class loader will look in the local repositories <strong>first</strong>,
instead of delegating before looking. There are exceptions. Classes which are
-part of the JRE base classes cannot be overridden. For some classes (such as
-the XML parser components in J2SE 1.4+), the J2SE 1.4 endorsed feature can be
-used.
+part of the JRE base classes cannot be overridden. There are some exceptions
+such as the XML parser components which can be overridden using the appropriate
+JVM feature which is the endorsed standards override feature for Java <= 8
+and the upgradeable modules feature for Java 9+.
Lastly, the web application class loader will always delegate first for JavaEE
API classes for the specifications implemented by Tomcat
(Servlet, JSP, EL, WebSocket). All other class loaders in Tomcat follow the
@@ -222,18 +223,13 @@ when you are running modern versions of Java, because the usual class loader
delegation process will always choose the implementation inside the JDK in
preference to this one.</p>
-<p>Java supports a mechanism called the "Endorsed Standards Override
+<p>Java <= 8 supports a mechanism called the "Endorsed Standards Override
Mechanism" to allow replacement of APIs created outside of the JCP
(i.e. DOM and SAX from W3C). It can also be used to update the XML parser
implementation. For more information, see:
<a href="http://docs.oracle.com/javase/1.5.0/docs/guide/standards/index.html">
-http://docs.oracle.com/javase/1.5.0/docs/guide/standards/index.html</a>.</p>
-
-<p>Tomcat utilizes this mechanism by including the system property setting
-<code>-Djava.endorsed.dirs=$JAVA_ENDORSED_DIRS</code> in the
-command line that starts the container. The default value of this option is
-<em>$CATALINA_HOME/endorsed</em>. This <em>endorsed</em> directory is not
-created by default.</p>
+http://docs.oracle.com/javase/1.5.0/docs/guide/standards/index.html</a>. For
+Java 9+, use the upgradeable modules feature.</p>
</section>
diff --git a/webapps/docs/cluster-howto.xml b/webapps/docs/cluster-howto.xml
index 1f6285c..2671210 100644
--- a/webapps/docs/cluster-howto.xml
+++ b/webapps/docs/cluster-howto.xml
@@ -64,7 +64,7 @@
<li>The IP broadcasted is <code>java.net.InetAddress.getLocalHost().getHostAddress()</code> (make sure you don't broadcast 127.0.0.1, this is a common error)</li>
<li>The TCP port listening for replication messages is the first available server socket in range <code>4000-4100</code></li>
<li>Listener is configured <code>ClusterSessionListener</code></li>
- <li>Two interceptors are configured <code>TcpFailureDetector</code> and <code>MessageDispatch15Interceptor</code></li>
+ <li>Two interceptors are configured <code>TcpFailureDetector</code> and <code>MessageDispatchInterceptor</code></li>
</ol>
<p>
The following is the default cluster configuration:
@@ -93,7 +93,7 @@
<Transport className="org.apache.catalina.tribes.transport.nio.PooledParallelSender"/>
</Sender>
<Interceptor className="org.apache.catalina.tribes.group.interceptors.TcpFailureDetector"/>
- <Interceptor className="org.apache.catalina.tribes.group.interceptors.MessageDispatch15Interceptor"/>
+ <Interceptor className="org.apache.catalina.tribes.group.interceptors.MessageDispatchInterceptor"/>
</Channel>
<Valve className="org.apache.catalina.ha.tcp.ReplicationValve"
@@ -263,7 +263,7 @@ should be completed:</p>
<Transport className="org.apache.catalina.tribes.transport.nio.PooledParallelSender"/>
</Sender>
<Interceptor className="org.apache.catalina.tribes.group.interceptors.TcpFailureDetector"/>
- <Interceptor className="org.apache.catalina.tribes.group.interceptors.MessageDispatch15Interceptor"/>
+ <Interceptor className="org.apache.catalina.tribes.group.interceptors.MessageDispatchInterceptor"/>
<Interceptor className="org.apache.catalina.tribes.group.interceptors.ThroughputInterceptor"/>
</Channel>
@@ -359,7 +359,7 @@ should be completed:</p>
<br/>For more info, Please visit the <a href="config/cluster-sender.html">reference documentation</a>
</p>
<source><![CDATA[ <Interceptor className="org.apache.catalina.tribes.group.interceptors.TcpFailureDetector"/>
- <Interceptor className="org.apache.catalina.tribes.group.interceptors.MessageDispatch15Interceptor"/>
+ <Interceptor className="org.apache.catalina.tribes.group.interceptors.MessageDispatchInterceptor"/>
<Interceptor className="org.apache.catalina.tribes.group.interceptors.ThroughputInterceptor"/>
</Channel>]]></source>
<p>
@@ -368,7 +368,7 @@ should be completed:</p>
Using interceptors, logic can be broken into more manageable pieces of code. The interceptors configured above are:<br/>
TcpFailureDetector - verifies crashed members through TCP, if multicast packets get dropped, this interceptor protects against false positives,
ie the node marked as crashed even though it still is alive and running.<br/>
- MessageDispatch15Interceptor - dispatches messages to a thread (thread pool) to send message asynchronously.<br/>
+ MessageDispatchInterceptor - dispatches messages to a thread (thread pool) to send message asynchronously.<br/>
ThroughputInterceptor - prints out simple stats on message traffic.<br/>
Please note that the order of interceptors is important. The way they are defined in server.xml is the way they are represented in the
channel stack. Think of it as a linked list, with the head being the first most interceptor and the tail the last.
diff --git a/webapps/docs/config/ajp.xml b/webapps/docs/config/ajp.xml
index 66e17bf..160acf3 100644
--- a/webapps/docs/config/ajp.xml
+++ b/webapps/docs/config/ajp.xml
@@ -183,8 +183,6 @@
found, the Java NIO based connector will be used.<br/>
To use an explicit protocol rather than rely on the auto-switching
mechanism described above, the following values may be used:<br/>
- <code>org.apache.coyote.ajp.AjpProtocol</code>
- - blocking Java connector<br/>
<code>org.apache.coyote.ajp.AjpNioProtocol</code>
- non blocking Java NIO connector.<br/>
<code>org.apache.coyote.ajp.AjpNio2Protocol</code>
@@ -278,9 +276,9 @@
<p>To use AJP, you must specify the protocol attribute (see above).</p>
- <p>The standard AJP connectors (BIO, NIO, NIO2 and APR/native) all support
- the following attributes in addition to the common Connector attributes
- listed above.</p>
+ <p>The standard AJP connectors (NIO, NIO2 and APR/native) all support the
+ following attributes in addition to the common Connector attributes listed
+ above.</p>
<attributes>
@@ -359,9 +357,8 @@
<attribute name="executorTerminationTimeoutMillis" required="false">
<p>The time that the private internal executor will wait for request
processing threads to terminate before continuing with the process of
- stopping the connector. If not set, the default is <code>0</code> (zero)
- for the BIO connector and <code>5000</code> (5 seconds) for the NIO, NIO2
- and APR/native connectors.</p>
+ stopping the connector. If not set, the default is <code>5000</code> (5
+ seconds).</p>
</attribute>
<attribute name="keepAliveTimeout" required="false">
@@ -380,10 +377,7 @@
start accepting and processing new connections again. Note that once the
limit has been reached, the operating system may still accept connections
based on the <code>acceptCount</code> setting. The default value varies by
- connector type. For BIO the default is the value of
- <strong>maxThreads</strong> unless an <a href="executor.html">Executor</a>
- is used in which case the default will be the value of maxThreads from the
- executor. For NIO and NIO2 the default is <code>10000</code>.
+ connector type. For NIO and NIO2 the default is <code>10000</code>.
For APR/native, the default is <code>8192</code>.</p>
<p>Note that for APR/native on Windows, the configured value will be
reduced to the highest multiple of 1024 that is less than or equal to
@@ -491,7 +485,7 @@
<subsection name="Java TCP socket attributes">
- <p>The BIO, NIO and NIO2 implementation support the following Java TCP socket
+ <p>The NIO and NIO2 implementation support the following Java TCP socket
attributes in addition to the common Connector and HTTP attributes listed
above.</p>
@@ -727,13 +721,6 @@
<code>-1</code> for unlimited cache and <code>0</code> for no cache.</p>
</attribute>
- <attribute name="socket.socketWrapperCache" required="false">
- <p>(int)Tomcat will cache SocketWrapper objects to reduce garbage
- collection. The integer value specifies how many objects to keep in the
- cache at most. The default is <code>500</code>. Other values are
- <code>-1</code> for unlimited cache and <code>0</code> for no cache.</p>
- </attribute>
-
</attributes>
</subsection>
@@ -751,13 +738,6 @@
</p>
</attribute>
- <attribute name="pollerSize" required="false">
- <p>Amount of sockets that the poller responsible for polling kept alive
- connections can hold at a given time. Extra connections will be closed
- right away. The default value is 8192, corresponding to 8192 keep-alive
- connections.</p>
- </attribute>
-
</attributes>
</subsection>
@@ -797,35 +777,30 @@
<table class="defaultTable" style="text-align: center;">
<tr>
<th />
- <th style="text-align: center;">Java Blocking Connector<br />BIO</th>
<th style="text-align: center;">Java Nio Connector<br />NIO</th>
<th style="text-align: center;">Java Nio2 Connector<br />NIO2</th>
<th style="text-align: center;">APR/native Connector<br />APR</th>
</tr>
<tr>
<th>Classname</th>
- <td><code class="noHighlight">AjpProtocol</code></td>
<td><code class="noHighlight">AjpNioProtocol</code></td>
<td><code class="noHighlight">AjpNio2Protocol</code></td>
<td><code class="noHighlight">AjpAprProtocol</code></td>
</tr>
<tr>
<th>Tomcat Version</th>
- <td>3.x onwards</td>
<td>7.x onwards</td>
<td>8.x onwards</td>
<td>5.5.x onwards</td>
</tr>
<tr>
<th>Support Polling</th>
- <td>NO</td>
<td>YES</td>
<td>YES</td>
<td>YES</td>
</tr>
<tr>
<th>Polling Size</th>
- <td>N/A</td>
<td><code class="noHighlight">maxConnections</code></td>
<td><code class="noHighlight">maxConnections</code></td>
<td><code class="noHighlight">maxConnections</code></td>
@@ -835,25 +810,21 @@
<td>Blocking</td>
<td>Blocking</td>
<td>Blocking</td>
- <td>Blocking</td>
</tr>
<tr>
<th>Read Request Body</th>
<td>Blocking</td>
<td>Blocking</td>
<td>Blocking</td>
- <td>Blocking</td>
</tr>
<tr>
<th>Write Response Headers and Body</th>
<td>Blocking</td>
<td>Blocking</td>
<td>Blocking</td>
- <td>Blocking</td>
</tr>
<tr>
<th>Wait for next Request</th>
- <td>Blocking</td>
<td>Non Blocking</td>
<td>Non Blocking</td>
<td>Non Blocking</td>
@@ -863,7 +834,6 @@
<td><code class="noHighlight">maxConnections</code></td>
<td><code class="noHighlight">maxConnections</code></td>
<td><code class="noHighlight">maxConnections</code></td>
- <td><code class="noHighlight">maxConnections</code></td>
</tr>
</table>
diff --git a/webapps/docs/config/cluster-interceptor.xml b/webapps/docs/config/cluster-interceptor.xml
index 48e7d97..fd43d2b 100644
--- a/webapps/docs/config/cluster-interceptor.xml
+++ b/webapps/docs/config/cluster-interceptor.xml
@@ -44,7 +44,6 @@
<ul>
<li><code>org.apache.catalina.tribes.group.interceptors.TcpFailureDetector</code></li>
<li><code>org.apache.catalina.tribes.group.interceptors.ThroughputInterceptor</code></li>
- <li><code>org.apache.catalina.tribes.group.interceptors.MessageDispatch15Interceptor</code></li>
<li><code>org.apache.catalina.tribes.group.interceptors.MessageDispatchInterceptor</code></li>
<li><code>org.apache.catalina.tribes.group.interceptors.NonBlockingCoordinator</code></li>
<li><code>org.apache.catalina.tribes.group.interceptors.OrderInterceptor</code></li>
@@ -105,32 +104,8 @@
</attribute>
</attributes>
</subsection>
- <subsection name="org.apache.catalina.tribes.group.interceptors.MessageDispatch15Interceptor Attributes">
- <attributes>
- <attribute name="className" required="true">
- Required, This dispatcher uses JDK 1.5 java.util.concurrent package
- </attribute>
- <attribute name="optionFlag" required="false">
- The default and hard coded value is <code>8 (org.apache.catalina.tribes.Channel.SEND_OPTIONS_ASYNCHRONOUS)</code>.
- The dispatcher will trigger on this value only, as it is predefined by Tribes.
- The other attributes are inherited from its base class <code>org.apache.catalina.tribes.group.interceptors.MessageDispatchInterceptor</code>.
- </attribute>
- <attribute name="maxThreads" required="false">
- The maximum number of threads in this pool, default is 10.
- </attribute>
- <attribute name="maxSpareThreads" required="false">
- The number of threads to keep in the pool, default is 2.
- </attribute>
- <attribute name="keepAliveTime" required="false">
- Maximum number of milliseconds of until Idle thread terminates. Default value is 5000(5 seconds).
- </attribute>
- </attributes>
- </subsection>
<subsection name="org.apache.catalina.tribes.group.interceptors.MessageDispatchInterceptor Attributes">
<attributes>
- <attribute name="className" required="true">
- Required, Same implementation as <code>MessageDispatch15Interceptor</code>, but with JDK 1.4 compliance.
- </attribute>
<attribute name="optionFlag" required="false">
The default and hard coded value is <code>8 (org.apache.catalina.tribes.Channel.SEND_OPTIONS_ASYNCHRONOUS)</code>.
The dispatcher will trigger on this value only, as it is predefined by Tribes.
@@ -144,6 +119,15 @@
if the queue fills up, one can trigger the behavior, if <code>alwaysSend</code> is set to true, the message will be sent synchronously
if the flag is false, an error is thrown
</attribute>
+ <attribute name="maxThreads" required="false">
+ The maximum number of threads in this pool, default is 10.
+ </attribute>
+ <attribute name="maxSpareThreads" required="false">
+ The number of threads to keep in the pool, default is 2.
+ </attribute>
+ <attribute name="keepAliveTime" required="false">
+ Maximum number of milliseconds of until Idle thread terminates. Default value is 5000(5 seconds).
+ </attribute>
</attributes>
</subsection>
<subsection name="org.apache.catalina.tribes.group.interceptors.TcpFailureDetector Attributes">
diff --git a/webapps/docs/config/cluster-manager.xml b/webapps/docs/config/cluster-manager.xml
index 56f303a..cbcc24a 100644
--- a/webapps/docs/config/cluster-manager.xml
+++ b/webapps/docs/config/cluster-manager.xml
@@ -77,26 +77,6 @@
when session attributes are being replicated or removed across Tomcat
nodes in the cluster.
</attribute>
- <attribute name="maxInactiveInterval" required="false">
- <p><strong>Deprecated</strong>: This should be configured via the
- Context.</p>
- <p>The initial maximum time interval, in seconds,
- between client requests before a session is invalidated. A negative value
- will result in sessions never timing out. If the attribute is not provided,
- a default of 1800 seconds (30 minutes) is used.</p>
-
- <p>This attribute provides the initial value whenever a
- new session is created, but the interval may be dynamically
- varied by a servlet via the
- <code>setMaxInactiveInterval</code> method of the <code>HttpSession</code> object.</p>
- </attribute>
- <attribute name="sessionIdLength" required="false">
- <p>The length of session ids created by this Manager, measured in bytes,
- excluding subsequent conversion to a hexadecimal string and
- excluding any JVM route information used for load balancing.
- This attribute is deprecated. Set the length on a nested
- <strong>SessionIdGenerator</strong> element instead.</p>
- </attribute>
<attribute name="processExpiresFrequency" required="false">
<p>Frequency of the session expiration, and related manager operations.
Manager operations will be done once for the specified amount of
diff --git a/webapps/docs/config/context.xml b/webapps/docs/config/context.xml
index 7aca689..7ec6444 100644
--- a/webapps/docs/config/context.xml
+++ b/webapps/docs/config/context.xml
@@ -534,7 +534,7 @@
<p>
Not reading the additional data will free the request processing thread
more quickly. Unfortunately most HTTP clients will not read the response
- if they can not write the full request.</p>
+ if they cannot write the full request.</p>
<p>The default is <code>true</code>, so additional data will be
read.</p>
<p>Note if an error occurs during the request processing that triggers
@@ -714,19 +714,6 @@
If not specified, the default value of <code>true</code> will be used.</p>
</attribute>
- <attribute name="clearReferencesStatic" required="false">
- <p>If <code>true</code>, Tomcat attempts to null out any static or final
- fields from loaded classes when a web application is stopped as a work
- around for apparent garbage collection bugs and application coding
- errors. There have been some issues reported with log4j when this
- is <code>true</code>. Applications without memory leaks using recent
- JVMs should operate correctly with this attribute set to
- <code>false</code>. If not specified, the default value of
- <code>false</code> will be used.</p>
- <p>This attribute has been deprecated and will be removed in Tomcat
- 8.5.</p>
- </attribute>
-
<attribute name="clearReferencesStopThreads" required="false">
<p>If <code>true</code>, Tomcat attempts to terminate threads that have
been started by the web application. Stopping threads is performed via
diff --git a/webapps/docs/config/cookie-processor.xml b/webapps/docs/config/cookie-processor.xml
index 1314e39..f671283 100644
--- a/webapps/docs/config/cookie-processor.xml
+++ b/webapps/docs/config/cookie-processor.xml
@@ -45,23 +45,6 @@
<a href="context.html">Context</a> component. If it is not included, a default
implementation will be created automatically.</p>
- <p><strong>Note:</strong> <strong>CookieProcessor</strong> is a new
- configuration element, introduced in Tomcat 8.0.15.</p>
- <ul>
- <li>The <strong>CookieProcessor</strong> element allows different cookie
- parsing configuration in each web application, or globally in the default
- <code>conf/context.xml</code> file. The legacy cookie parsing algorithm
- supported only limited global configuration via several
- <a href="systemprops.html#Specifications">system properties</a>. Those
- system properties are still supported, but are going to be deprecated in
- favor of this new configuration element.
- </li>
- <li>The new RFC6265-compliant implementation is a drop-in replacement for
- the original legacy one. The legacy implementation remains the default. You
- can select the implementation by setting <code>className</code> attribute
- on <strong>CookieProcessor</strong> element.</li>
- </ul>
-
</section>
@@ -89,10 +72,34 @@
<subsection name="Standard Implementation">
<p>The standard implementation of <strong>CookieProcessor</strong> is
- <code>org.apache.tomcat.util.http.LegacyCookieProcessor</code>. Note
- that it is anticipated that this will change to
- <code>org.apache.tomcat.util.http.Rfc6265CookieProcessor</code> in a future
- Tomcat 8 release.</p>
+ <code>org.apache.tomcat.util.http.Rfc6265CookieProcessor</code>.</p>
+
+ <p>This cookie processor is based on RFC6265 with the following changes to
+ support better interoperability:</p>
+
+ <ul>
+ <li>Values 0x80 to 0xFF are permitted in cookie-octet to support the use
+ of UTF-8 in cookie values as used by HTML 5.</li>
+ <li>For cookies without a value, the '=' is not required after the name as
+ some browsers do not sent it.</li>
+ </ul>
+
+ <p>The RFC 6265 cookie processor is generally more lenient than the legacy
+ cookie parser. In particular:</p>
+
+ <ul>
+ <li>The '<code>=</code>' and '<code>/</code>' characters are always
+ permitted in a cookie value.</li>
+ <li>Name only cookies are always permitted.</li>
+ <li>The cookie header is always preserved.</li>
+ </ul>
+
+ <p>No additional attributes are supported by the <strong>RFC 6265 Cookie
+ Processor</strong>.</p>
+
+ </subsection>
+
+ <subsection name="Legacy Cookie Processor - org.apache.tomcat.util.http.LegacyCookieProcessor">
<p>This is the legacy cookie parser based on RFC6265, RFC2109 and RFC2616.
It implements a strict interpretation of the cookie specifications. Due to
@@ -109,20 +116,14 @@
'<code>=</code>' is encountered and the remainder of the cookie value
will be dropped.</p>
<p>If not set the specification compliant default value of
- <code>false</code> will be used. This default may be changed by setting
- the
- <code>org.apache.tomcat.util.http.ServerCookie.ALLOW_EQUALS_IN_VALUE</code>
- <a href="systemprops.html">system property</a>.</p>
+ <code>false</code> will be used.</p>
</attribute>
<attribute name="allowHttpSepsInV0" required="false">
<p>If this is <code>true</code> Tomcat will allow HTTP separators in
cookie names and values.</p>
<p>If not specified, the default specification compliant value of
- <code>false</code> will be used. This default may be changed by setting
- the
- <code>org.apache.tomcat.util.http.ServerCookie.ALLOW_HTTP_SEPARATORS_IN_V0</code>
- <a href="systemprops.html">system property</a>.</p>
+ <code>false</code> will be used.</p>
</attribute>
<attribute name="allowNameOnly" required="false">
@@ -130,10 +131,7 @@
(with or without trailing '<code>=</code>') when parsing cookie headers.
If <code>false</code>, name only cookies will be dropped.</p>
<p>If not set the specification compliant default value of
- <code>false</code> will be used. This default may be changed by setting
- the
- <code>org.apache.tomcat.util.http.ServerCookie.ALLOW_NAME_ONLY</code>
- <a href="systemprops.html">system property</a>.</p>
+ <code>false</code> will be used.</p>
</attribute>
<attribute name="alwaysAddExpires" required="false">
@@ -153,48 +151,13 @@
headers. If <code>org.apache.catalina.STRICT_SERVLET_COMPLIANCE</code>
is set to <code>true</code>, the default of this setting will be
<code>true</code>, else the default value will be <code>false</code>.
- This default may be overridden by setting the
- <code>org.apache.tomcat.util.http.ServerCookie.FWD_SLASH_IS_SEPARATOR</code>
- system property.</p>
- </attribute>
-
- <attribute name="preserveCookieHeader" required="false">
- <p>This attribute is no longer used. From Tomcat 8.0.31, Tomcat will
- always preserve the cookie header returned by
- <code>HttpServletRequest.getHeader()</code>.</p>
+ </p>
</attribute>
</attributes>
</subsection>
- <subsection name="RFC 6265 Cookie Processor - org.apache.tomcat.util.http.Rfc6265CookieProcessor">
-
- <p>This cookie processor is based on RFC6265 with the following changes to
- support better interoperability:</p>
-
- <ul>
- <li>Values 0x80 to 0xFF are permitted in cookie-octet to support the use
- of UTF-8 in cookie values as used by HTML 5.</li>
- <li>For cookies without a value, the '=' is not required after the name as
- some browsers do not sent it.</li>
- </ul>
-
- <p>The RFC 6265 cookie processor is generally more lenient than the legacy
- cookie parser. In particular:</p>
-
- <ul>
- <li>The '<code>=</code>' and '<code>/</code>' characters are always
- permitted in a cookie value.</li>
- <li>Name only cookies are always permitted.</li>
- <li>The cookie header is always preserved.</li>
- </ul>
-
- <p>No additional attributes are supported by the <strong>RFC 6265 Cookie
- Processor</strong>.</p>
-
- </subsection>
-
</section>
diff --git a/webapps/docs/config/filter.xml b/webapps/docs/config/filter.xml
index 70f936d..4e23ee2 100644
--- a/webapps/docs/config/filter.xml
+++ b/webapps/docs/config/filter.xml
@@ -1419,7 +1419,7 @@ FINE: Request "/docs/config/manager.html" with response status "200"
<p>
Note : <code>x-forwarded-by</code> holds the trusted proxy <code>proxy1</code>.
<code>x-forwarded-by</code> holds <code>140.211.11.130</code> because
- <code>untrusted-proxy</code> is not trusted and thus, we can not trust that
+ <code>untrusted-proxy</code> is not trusted and thus, we cannot trust that
<code>untrusted-proxy</code> is the actual remote ip.
<code>request.remoteAddr</code> is <code>untrusted-proxy</code> that is an IP
verified by <code>proxy1</code>.
diff --git a/webapps/docs/config/host.xml b/webapps/docs/config/host.xml
index 63f9f1f..2823862 100644
--- a/webapps/docs/config/host.xml
+++ b/webapps/docs/config/host.xml
@@ -192,7 +192,10 @@
have a name that matches the <code>defaultHost</code> setting for that
Engine. See <a href="#Host_Name_Aliases">Host Name Aliases</a> for
information on how to assign more than one network name to the same
- virtual host.</p>
+ virtual host. If the name takes the form of <code>*.domainname</code>
+ (e.g. <code>*.apache.org</code>) then it will be treated as a match for
+ any host in that domain unless a host that has an exactly matching name
+ is found.</p>
</attribute>
<attribute name="startStopThreads" required="false">
@@ -476,6 +479,10 @@
involved must be registered in your DNS server to resolve to the
same computer that is running this instance of Catalina.</p>
+ <p>Aliases may also use the wildcard form (<code>*.domainname</code>)
+ permitted for the <strong>name</strong> attribute of a
+ <strong>Host</strong>.</p>
+
</subsection>
diff --git a/webapps/docs/config/http.xml b/webapps/docs/config/http.xml
index 2c9ef22..b173e6d 100644
--- a/webapps/docs/config/http.xml
+++ b/webapps/docs/config/http.xml
@@ -85,6 +85,15 @@
30000 (30 seconds).</p>
</attribute>
+ <attribute name="defaultSSLHostConfigName" required="false">
+ <p>The name of the default <strong>SSLHostConfig</strong> that will be
+ used for secure connections (if this connector is configured for secure
+ connections) if the client connection does not provide SNI or if the SNI
+ is provided but does not match any configured
+ <strong>SSLHostConfig</strong>. If not specified the default value of
+ <code>_default_</code> will be used.</p>
+ </attribute>
+
<attribute name="enableLookups" required="false">
<p>Set to <code>true</code> if you want calls to
<code>request.getRemoteHost()</code> to perform DNS lookups in
@@ -160,17 +169,17 @@
<attribute name="protocol" required="false">
<p>Sets the protocol to handle incoming traffic. The default value is
<code>HTTP/1.1</code> which uses an auto-switching mechanism to select
- either a non blocking Java NIO based connector or an APR/native based connector.
+ either a Java NIO based connector or an APR/native based connector.
If the <code>PATH</code> (Windows) or <code>LD_LIBRARY_PATH</code> (on
most unix systems) environment variables contain the Tomcat native
- library, the APR/native connector will be used. If the native library
- cannot be found, the non blocking Java based connector will be used. Note
- that the APR/native connector has different settings for HTTPS than the
- Java connectors.<br/>
+ library, and the <code>AprLifecycleListener</code> that is used to
+ initialize APR has its <code>useAprConnector</code> attribute set to
+ <code>true</code>, the APR/native connector will be used. If the native library
+ cannot be found or the attribute is not configured, the Java NIO based
+ connector will be used. Note that the APR/native connector has different
+ settings for HTTPS than the Java connectors.<br/>
To use an explicit protocol rather than rely on the auto-switching
mechanism described above, the following values may be used:<br/>
- <code>org.apache.coyote.http11.Http11Protocol</code> -
- blocking Java connector<br/>
<code>org.apache.coyote.http11.Http11NioProtocol</code> -
non blocking Java NIO connector<br/>
<code>org.apache.coyote.http11.Http11Nio2Protocol</code> -
@@ -272,9 +281,9 @@
<subsection name="Standard Implementation">
- <p>The standard HTTP connectors (BIO, NIO, NIO2 and APR/native) all support
- the following attributes in addition to the common Connector attributes
- listed above.</p>
+ <p>The standard HTTP connectors (NIO, NIO2 and APR/native) all support the
+ following attributes in addition to the common Connector attributes listed
+ above.</p>
<attributes>
@@ -321,6 +330,17 @@
connector is started and unbound when it is stopped.</p>
</attribute>
+ <attribute name="clientCertProvider" required="false">
+ <p>When client certificate information is presented in a form other than
+ instances of <code>java.security.cert.X509Certificate</code> it needs to
+ be converted before it can be used and this property controls which JSSE
+ provider is used to perform the conversion. For example it is used with
+ the <a href="ajp.html">AJP connectors</a>, the HTTP APR connector and
+ with the <a href="valve.html#SSL_Authenticator_Valve">
+ org.apache.catalina.valves.SSLValve</a>. If not specified, the default
+ provider will be used.</p>
+ </attribute>
+
<attribute name="compressableMimeType" required="false">
<p>The value is a comma separated list of MIME types for which HTTP
compression may be used.
@@ -403,9 +423,8 @@
<attribute name="executorTerminationTimeoutMillis" required="false">
<p>The time that the private internal executor will wait for request
processing threads to terminate before continuing with the process of
- stopping the connector. If not set, the default is <code>0</code> (zero)
- for the BIO connector and <code>5000</code> (5 seconds) for the NIO,
- NIO2 and APR/native connectors.</p>
+ stopping the connector. If not set, the default is <code>5000</code> (5
+ seconds).</p>
</attribute>
<attribute name="keepAliveTimeout" required="false">
@@ -425,10 +444,7 @@
start accepting and processing new connections again. Note that once the
limit has been reached, the operating system may still accept connections
based on the <code>acceptCount</code> setting. The default value varies by
- connector type. For BIO the default is the value of
- <strong>maxThreads</strong> unless an <a href="executor.html">Executor</a>
- is used in which case the default will be the value of maxThreads from the
- executor. For NIO and NIO2 the default is <code>10000</code>.
+ connector type. For NIO and NIO2 the default is <code>10000</code>.
For APR/native, the default is <code>8192</code>.</p>
<p>Note that for APR/native on Windows, the configured value will be
reduced to the highest multiple of 1024 that is less than or equal to
@@ -531,18 +547,16 @@
<attribute name="server" required="false">
<p>Overrides the Server header for the http response. If set, the value
- for this attribute overrides the Tomcat default and any Server header set
- by a web application. If not set, any value specified by the application
- is used. If the application does not specify a value then
- <code>Apache-Coyote/1.1</code> is used. Unless you are paranoid, you won't
- need this feature.
- </p>
+ for this attribute overrides any Server header set by a web application.
+ If not set, any value specified by the application is used. If the
+ application does not specify a value then no Server header is set.</p>
</attribute>
- <attribute name="socketBuffer" required="false">
- <p>The size (in bytes) of the buffer to be provided for socket
- output buffering. -1 can be specified to disable the use of a buffer.
- By default, a buffers of 9000 bytes will be used.</p>
+ <attribute name="serverRemoveAppProvidedValues" required="false">
+ <p>If <code>true</code>, any Server header set by a web
+ application will be removed. Note that if <strong>server</strong> is set,
+ this attribute is effectively ignored. If not set, the default value of
+ <code>false</code> will be used.</p>
</attribute>
<attribute name="SSLEnabled" required="false">
@@ -576,22 +590,13 @@
recorded correctly but it will be reported (e.g. via JMX) as
<code>-1</code> to make clear that it is not used.</p>
</attribute>
-
- <attribute name="upgradeAsyncWriteBufferSize" required="false">
- <p>The default size of the buffer to allocate to for asynchronous writes
- that can not be completed in a single operation, specified in bytes. Data that can't be
- written immediately will be stored in this buffer until it can be written.
- If more data needs to be stored than space is available in the buffer than
- the size of the buffer will be increased for the duration of the write. If
- not specified the default value of 8192 will be used.</p>
- </attribute>
</attributes>
</subsection>
<subsection name="Java TCP socket attributes">
- <p>The BIO, NIO and NIO2 implementation support the following Java TCP
+ <p>The NIO and NIO2 implementation support the following Java TCP
socket attributes in addition to the common Connector and HTTP attributes
listed above.</p>
@@ -665,24 +670,6 @@
</attributes>
</subsection>
- <subsection name="BIO specific configuration">
-
- <p>The following attributes are specific to the BIO connector.</p>
-
- <attributes>
-
- <attribute name="disableKeepAlivePercentage" required="false">
- <p>The percentage of processing threads that have to be in use before
- HTTP keep-alives are disabled to improve scalability. Values less than
- <code>0</code> will be changed to <code>0</code> and values greater than
- <code>100</code> will be changed to <code>100</code>. If not specified,
- the default value is <code>75</code>.</p>
- </attribute>
-
- </attributes>
-
- </subsection>
-
<subsection name="NIO specific configuration">
<p>The following attributes are specific to the NIO connector.</p>
@@ -716,11 +703,6 @@
default value is <code>1000</code> milliseconds.</p>
</attribute>
- <attribute name="useComet" required="false">
- <p>(bool)Whether to allow comet servlets or not. Default value is
- <code>true</code>.</p>
- </attribute>
-
<attribute name="useSendfile" required="false">
<p>(bool)Use this attribute to enable or disable sendfile capability.
The default value is <code>true</code>. Note that the use of sendfile
@@ -841,18 +823,6 @@
<strong>selectorPool.maxSelectors</strong> attribute.</p>
</attribute>
- <attribute name="oomParachute" required="false">
- <p>(int)The NIO connector implements an OutOfMemoryError strategy called
- parachute. It holds a chunk of data as a byte array. In case of an OOM,
- this chunk of data is released and the error is reported. This will give
- the VM enough room to clean up. The <code>oomParachute</code> represents
- the size in bytes of the parachute(the byte array). The default value is
- <code>1024*1024</code>(1MB). Please note, this only works for OOM errors
- regarding the Java Heap space, and there is absolutely no guarantee
- that you will be able to recover at all. If you have an OOM outside of
- the Java Heap, then this parachute trick will not help.
- </p>
- </attribute>
</attributes>
</subsection>
@@ -862,17 +832,6 @@
<attributes>
- <attribute name="useCaches" required="false">
- <p>(bool)Use this attribute to enable or disable object caching to
- reduce the amount of GC objects produced.
- The default value is <code>false</code>.</p>
- </attribute>
-
- <attribute name="useComet" required="false">
- <p>(bool)Whether to allow comet servlets or not. Default value is
- <code>true</code>.</p>
- </attribute>
-
<attribute name="useSendfile" required="false">
<p>(bool)Use this attribute to enable or disable sendfile capability.
The default value is <code>true</code>. Note that the use of sendfile
@@ -926,7 +885,7 @@
dealing with tens of thousands concurrent connections.</p>
</attribute>
- <attribute name="socket.bufferPoolSize" required="false">
+ <attribute name="socket.bufferPool" required="false">
<p>(int)The NIO2 connector uses a class called Nio2Channel that holds
elements linked to a socket. To reduce garbage collection, the NIO2
connector caches these channel objects. This value specifies the size of
@@ -942,25 +901,6 @@
<code>-1</code> for unlimited cache and <code>0</code> for no cache.</p>
</attribute>
- <attribute name="socket.socketWrapperCache" required="false">
- <p>(int)Tomcat will cache SocketWrapper objects to reduce garbage
- collection. The integer value specifies how many objects to keep in the
- cache at most. The default is <code>500</code>. Other values are
- <code>-1</code> for unlimited cache and <code>0</code> for no cache.</p>
- </attribute>
-
- <attribute name="oomParachute" required="false">
- <p>(int)The NIO2 connector implements an OutOfMemoryError strategy called
- parachute. It holds a chunk of data as a byte array. In case of an OOM,
- this chunk of data is released and the error is reported. This will give
- the VM enough room to clean up. The <code>oomParachute</code> represents
- the size in bytes of the parachute(the byte array). The default value is
- <code>1024*1024</code>(1MB). Please note, this only works for OOM errors
- regarding the Java Heap space, and there is absolutely no guarantee
- that you will be able to recover at all. If you have an OOM outside of
- the Java Heap, then this parachute trick will not help.
- </p>
- </attribute>
</attributes>
</subsection>
@@ -977,13 +917,6 @@
otherwise it is <code>false</code>.</p>
</attribute>
- <attribute name="pollerSize" required="false">
- <p>Amount of sockets that the poller responsible for polling kept alive
- connections can hold at a given time. Extra connections will be closed
- right away. The default value is 8192, corresponding to 8192 keep-alive
- connections. This is a synonym for maxConnections.</p>
- </attribute>
-
<attribute name="pollerThreadCount" required="false">
<p>Number of threads used to poll kept alive connections. On Windows the
default is chosen so that the sockets managed by each thread is
@@ -1009,13 +942,6 @@
specified amount. The default value is 1024.</p>
</attribute>
- <attribute name="sendfileThreadCount" required="false">
- <p>Number of threads used service sendfile sockets. On Windows the
- default is chosen so that the sockets managed by each thread is
- less than 1024. For Linux the default is 1. Changing the default on
- Windows is likely to have a negative performance impact.</p>
- </attribute>
-
<attribute name="threadPriority" required="false">
<p>(int)The priority of the acceptor and poller threads.
The default value is <code>5</code> (the value of the
@@ -1024,11 +950,6 @@
this priority means.</p>
</attribute>
- <attribute name="useComet" required="false">
- <p>(bool)Whether to allow comet servlets or not. Default value is
- <code>true</code>.</p>
- </attribute>
-
<attribute name="useSendfile" required="false">
<p>(bool)Use this attribute to enable or disable sendfile capability.
The default value is <code>true</code>. Note that the use of sendfile
@@ -1045,7 +966,18 @@
<section name="Nested Components">
- <p>None at this time.</p>
+ <p>First implemented in Tomcat 9 and back-ported to 8.5, Tomcat now supports
+ Server Name Indication (SNI). This allows multiple SSL configurations to be
+ associated with a single secure connector with the configuration used for any
+ given connection determined by the host name requested by the client. To
+ facilitate this, the <strong>SSLHostConfig</strong> element was added which
+ can be used to define one of these configurations. Any number of
+ <strong>SSLHostConfig</strong> may be nested in a <strong>Connector</strong>.
+ At the same time, support was added for multiple certificates to be associated
+ with a single <strong>SSLHostConfig</strong>. Each SSL certificate is
+ therefore configured in a <strong>Certificate</strong> element with in an
+ <strong>SSLHostConfig</strong>. For further information, see the SSL Support
+ section below.</p>
</section>
@@ -1056,21 +988,44 @@
<subsection name="HTTP/1.1 and HTTP/1.0 Support">
<p>This <strong>Connector</strong> supports all of the required features
- of the HTTP/1.1 protocol, as described in RFC 2616, including persistent
+ of the HTTP/1.1 protocol, as described in RFCs 7230-7235, including persistent
connections, pipelining, expectations and chunked encoding. If the client
- (typically a browser) supports only HTTP/1.0, the
+ supports only HTTP/1.0 or HTTP/0.9, the
<strong>Connector</strong> will gracefully fall back to supporting this
protocol as well. No special configuration is required to enable this
support. The <strong>Connector</strong> also supports HTTP/1.0
keep-alive.</p>
- <p>RFC 2616 requires that HTTP servers always begin their responses with
+ <p>RFC 7230 requires that HTTP servers always begin their responses with
the highest HTTP version that they claim to support. Therefore, this
<strong>Connector</strong> will always return <code>HTTP/1.1</code> at
the beginning of its responses.</p>
</subsection>
+ <subsection name="HTTP/2 Support">
+
+ <p>HTTP/2 is support is provided for TLS (h2), non-TLS via HTTP upgrade (h2c)
+ and direct HTTP/2 (h2c) connections. To enable HTTP/2 support for an HTTP
+ connector the following <strong>UpgradeProtocol</strong> element must be
+ nested within the <strong>Connector</strong> with a
+ <strong>className</strong> attribute of
+ <code>org.apache.coyote.http2.Http2Protocol</code>.</p>
+
+<source><![CDATA[<Connector ... >
+ <UpgradeProtocol className="org.apache.coyote.http2.Http2Protocol" />
+</Connector>]]></source>
+
+ <p>Because Java 8's TLS implementation does not support ALPN (which is
+ required for HTTP/2 over TLS), you must be using an OpenSSL based TLS
+ implementation to enable HTTP/2 support. See the
+ <code>sslImplementationName</code> attribute of the
+ <strong>Connector</strong>.</p>
+
+ <p>Additional configuration attributes are available. See the
+ <a href="http2.html">HTTP/2 Upgrade Protocol</a> documentaion for details.</p>
+
+ </subsection>
<subsection name="Proxy Support">
@@ -1090,7 +1045,6 @@
</subsection>
-
<subsection name="SSL Support">
<p>You can enable SSL support for a particular instance of this
@@ -1101,178 +1055,195 @@
attributes to the values <code>https</code> and <code>true</code>
respectively, to pass correct information to the servlets.</p>
- <p>The BIO, NIO and NIO2 connectors use the JSSE SSL whereas the APR/native
- connector uses OpenSSL. Therefore, in addition to using different attributes
- to configure SSL, the APR/native connector also requires keys and certificates
- to be provided in a different format.</p>
+ <p>The NIO and NIO2 connectors use either the JSSE Java SSL implementation or
+ an OpenSSL implementation, whereas the APR/native connector uses OpenSSL only.
+ Prior to Tomcat 8.5, different configuration attributes were used for JSSE and
+ OpenSSL. From Tomcat 8.5 onwards, and as far as possible, common configuration
+ attributes are used for both JSSE and OpenSSL. Also if using the JSSE OpenSSL
+ implementation, configuration can be set using either the JSSE or APR
+ attributes (note: but not both types within the same configuration). This is
+ to aid simpler switching between connector implementations for SSL
+ connectors.</p>
+
+ <p>Each secure connector must define at least one
+ <strong>SSLHostConfig</strong>. The names of the
+ <strong>SSLHostConfig</strong> elements must be unique and one of them must
+ match the <code>defaultSSLHostConfigName</code> attribute of the
+ <strong>Connector</strong>.</p>
+
+ <p>Each <strong>SSLHostConfig</strong> must in turn define at least one
+ <strong>Certificate</strong>. The types of the <strong>Certificate</strong>s
+ must be unique.</p>
+
+ <p>As of Tomcat 8.5, the majority of the SSL configuration attributes in the
+ <strong>Connector</strong> are deprecated. If specified, they will be used to
+ configure a <strong>SSLHostConfig</strong> and <strong>Certificate</strong>
+ for the <code>defaultSSLHostConfigName</code>. Note that if an explicit
+ <strong>SSLHostConfig</strong> element also exists for the
+ <code>defaultSSLHostConfigName</code> then that will be treated as a configuration
+ error. It is expected that Tomcat 10 will drop support for the SSL
+ configuration attributes in the <strong>Connector</strong>.</p>
<p>For more information, see the
<a href="../ssl-howto.html">SSL Configuration HOW-TO</a>.</p>
- <subsection name="SSL Support - BIO, NIO and NIO2">
+ </subsection>
- <p>The BIO, NIO and NIO2 connectors use the following attributes to configure SSL:
- </p>
+ <subsection name="SSL Support - SSLHostConfig">
+
+ <p></p>
<attributes>
- <attribute name="algorithm" required="false">
- <p>The certificate encoding algorithm to be used. This defaults to
- <code>KeyManagerFactory.getDefaultAlgorithm()</code> which returns
- <code>SunX509</code> for Sun JVMs. IBM JVMs return
- <code>IbmX509</code>. For other vendors, consult the JVM
- documentation for the default value.</p>
+ <attribute name="certificateRevocationFile" required="false">
+ <p>Name of the file that contains the concatenated certificate revocation
+ lists for the certificate authorities. The format is PEM-encoded. If not
+ defined, client certificates will not be checked against a certificate
+ revocation list (unless an OpenSSL based connector is used and
+ <strong>certificateRevocationPath</strong> is defined). Relative paths
+ will be resolved against <code>$CATALINA_BASE</code>. JSSE based
+ connectors may also specify a URL for this attribute.</p>
</attribute>
- <attribute name="allowUnsafeLegacyRenegotiation" required="false">
- <p>Is unsafe legacy TLS renegotiation allowed which is likely to expose
- users to CVE-2009-3555, a man-in-the-middle vulnerability in the TLS
- protocol that allows an attacker to inject arbitrary data into the user's
- request. If not specified, a default of <code>false</code> is used. This
- attribute only has an effect if the JVM does not support RFC 5746 as
- indicated by the presence of the pseudo-ciphersuite
- TLS_EMPTY_RENEGOTIATION_INFO_SCSV. This is available JRE/JDK 6 update 22
- onwards. Where RFC 5746 is supported the renegotiation - including support
- for unsafe legacy renegotiation - is controlled by the JVM configuration.
- </p>
+ <attribute name="certificateRevocationPath" required="false">
+ <p>OpenSSL only.</p>
+ <p>Name of the directory that contains the certificate revocation lists
+ for the certificate authorities. The format is PEM-encoded. Relative paths
+ will be resolved against <code>$CATALINA_BASE</code>.</p>
</attribute>
- <attribute name="useServerCipherSuitesOrder" required="false">
- <p>
- Set to <code>true</code> to enforce the server's cipher order
- (from the <code>ciphers</code> setting). Set to <code>false</code>
- to choose the first acceptable cipher suite presented by the client.
- <b>Use of this feature requires Java 8 or later.</b>
- Default is <i>undefined</i>, leaving the choice up to the JSSE
- implementation.
- </p>
+ <attribute name="certificateVerification" required="false">
+ <p>Set to <code>required</code> if you want the SSL stack to require a
+ valid certificate chain from the client before accepting a connection.
+ Set to <code>optional</code> if you want the SSL stack to request a client
+ Certificate, but not fail if one isn't presented. Set to
+ <code>optionalNoCA</code> if you want client certificates to be optional
+ and you don't want Tomcat to check them against the list of trusted CAs.
+ If the TLS provider doesn't support this option (OpenSSL does, JSSE does
+ not) it is treated as if <code>optional</code> was specified. A
+ <code>none</code> value (which is the default) will not require a
+ certificate chain unless the client requests a resource protected by a
+ security constraint that uses <code>CLIENT-CERT</code> authentication.</p>
+ </attribute>
+
+ <attribute name="certificateVerificationDepth" required="false">
+ <p>The maximum number of intermediate certificates that will be allowed
+ when validating client certificates. If not specified, the default value
+ of 10 will be used.</p>
</attribute>
- <attribute name="ciphers" required="false">
- <p>If specified and using ',' as a separator, only the ciphers that are
- listed and supported by the SSL implementation will be used.
- The ciphers are specified using the JSSE cipher naming convention. The
- special value of <code>ALL</code> will enable all supported ciphers. This
- will include many that are not secure. <code>ALL</code> is intended for
- testing purposes only.</p>
- <p>The list can also use ':' as a separator, in that case
- it will use the OpenSSL syntax (see OpenSSL documentation for the list
- of ciphers supported and the syntax). The behaviour of this filtering is
- kept aligned with the behaviour of the OpenSSL 1.0.2 stable branch.</p>
- <p>If not specified, a default (using the OpenSSL notation) of
- <code>HIGH:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!kRSA</code> will be used
- when running on Java 8 or later. On Java 7, <code>!DHE</code> will be
- added to this default when using a JSSE based connector.</p>
- <p>Note that Java does not treat the order in which ciphers are defined as
- an order of preference. See <code>useServerCipherSuitesOrder</code>.</p>
+ <attribute name="caCertificateFile" required="false">
+ <p>OpenSSL only.</p>
+ <p>Name of the file that contains the concatenated certificates for the
+ trusted certificate authorities. The format is PEM-encoded.</p>
</attribute>
- <attribute name="clientAuth" required="false">
- <p>Set to <code>true</code> if you want the SSL stack to require a
- valid certificate chain from the client before accepting a connection.
- Set to <code>want</code> if you want the SSL stack to request a client
- Certificate, but not fail if one isn't presented. A <code>false</code>
- value (which is the default) will not require a certificate chain
- unless the client requests a resource protected by a security
- constraint that uses <code>CLIENT-CERT</code> authentication.</p>
+ <attribute name="caCertificatePath" required="false">
+ <p>OpenSSL only.</p>
+ <p>Name of the directory that contains the certificates for the trusted
+ certificate authorities. The format is PEM-encoded.</p>
</attribute>
- <attribute name="clientCertProvider" required="false">
- <p>When client certificate information is presented in a form other than
- instances of <code>java.security.cert.X509Certificate</code> it needs to
- be converted before it can be used and this property controls which JSSE
- provider is used to perform the conversion. For example it is used with
- the <a href="ajp.html">AJP connectors</a>, the HTTP APR connector and
- with the <a href="valve.html#SSL_Authenticator_Valve">
- org.apache.catalina.valves.SSLValve</a>. If not specified, the default
- provider will be used.</p>
+ <attribute name="ciphers" required="false">
+ <p>The ciphers to enable using the OpenSSL syntax. (See the OpenSSL
+ documentation for the list of ciphers supported and the syntax).
+ Alternatively, a comma separated list of ciphers using the standard
+ OpenSSL cipher names or the standard JSSE cipher names may be used.</p>
+ <p>When converting from OpenSSL syntax to JSSE ciphers for JSSE based
+ connectors, the behaviour of the OpenSSL syntax parsing is kept aligned
+ with the behaviour of the OpenSSL 1.1.0 development branch.</p>
+ <p>Only the ciphers that are supported by the SSL implementation will be
+ used.</p>
+ <p>If not specified, a default (using the OpenSSL notation) of
+ <code>HIGH:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!kRSA</code> will be
+ used.</p>
+ <p>Note that, by default, the order in which ciphers are defined is
+ treated as an order of preference. See <code>honorCipherOrder</code>.</p>
</attribute>
- <attribute name="crlFile" required="false">
- <p>The certificate revocation list to be used to verify client
- certificates. If not defined, client certificates will not be checked
- against a certificate revocation list. The file may be specified using a
- URL, an absolute path or a relative (to CATALINA_BASE) path.</p>
+ <attribute name="disableCompression" required="false">
+ <p>OpenSSL only.</p>
+ <p>Configures if compression is disabled. The default is
+ <code>true</code>. If the OpenSSL version used does not support disabling
+ compression then the default for that OpenSSL version will be used.</p>
</attribute>
- <attribute name="keyAlias" required="false">
- <p>The alias used for the server key and certificate in the keystore. If
- not specified, the first key read from the keystore will be used. The
- order in which keys are read from the keystore is implementation
- dependent. It may not be the case that keys are read from the keystore in
- the same order as they were added. If more than one key is present in the
- keystore it is strongly recommended that a keyAlias is configured to
- ensure that the correct key is used.</p>
+ <attribute name="disableSessionTickets" required="false">
+ <p>OpenSSL only.</p>
+ <p>Disables use of TLS Session Tickets (RFC 4507) if set to
+ <code>true</code>. Default is <code>false</code>.</p>
</attribute>
- <attribute name="keyPass" required="false">
- <p>The password used to access the server certificate from the
- specified keystore file. The default value is "<code>changeit</code>".
- </p>
+ <attribute name="honorCipherOrder" required="false">
+ <p>Set to <code>true</code> to enforce the server's cipher order
+ (from the <code>ciphers</code> setting) instead of allowing
+ the client to choose the cipher. The default is <code>false</code>.
+ Use of this feature requires Java 8 or later.</p>
</attribute>
- <attribute name="keystoreFile" required="false">
- <p>The pathname of the keystore file where you have stored the
- server certificate to be loaded. By default, the pathname is
- the file "<code>.keystore</code>" in the operating system home
- directory of the user that is running Tomcat. If your
- <code>keystoreType</code> doesn't need a file use <code>""</code>
- (empty string) for this parameter. The file may be specified using a
- URL, an absolute path or a relative (to CATALINA_BASE) path.</p>
+ <attribute name="hostName" required="false">
+ <p>The name of the SSL Host. This should either be the fully qualified
+ domain name (e.g. <code>tomcat.apache.org</code>) or a wild card domain
+ name (e.g. <code>*.apache.org</code>). If not specified, the default value
+ of <code>_default_</code> will be used.</p>
</attribute>
- <attribute name="keystorePass" required="false">
- <p>The password used to access the specified keystore file. The default
- value is the value of the <code>keyPass</code> attribute.
- </p>
+ <attribute name="insecureRenegotiation" required="false">
+ <p>OpenSSL only.</p>
+ <p>Configures if insecure renegotiation is allowed. The default is
+ <code>false</code>. If the OpenSSL version used does not support
+ configuring if insecure renegotiation is allowed then the default for that
+ OpenSSL version will be used.</p>
</attribute>
- <attribute name="keystoreProvider" required="false">
- <p>The name of the keystore provider to be used for the server
- certificate. If not specified, the list of registered providers is
- traversed in preference order and the first provider that supports the
- <code>keystoreType</code> is used.
- </p>
+ <attribute name="keyManagerAlgorithm" required="false">
+ <p>JSSE only.</p>
+ <p>The <code>KeyManager</code> algorithm to be used. This defaults to
+ <code>KeyManagerFactory.getDefaultAlgorithm()</code> which returns
+ <code>SunX509</code> for Sun JVMs. IBM JVMs return
+ <code>IbmX509</code>. For other vendors, consult the JVM
+ documentation for the default value.</p>
</attribute>
- <attribute name="keystoreType" required="false">
- <p>The type of keystore file to be used for the server certificate.
- If not specified, the default value is "<code>JKS</code>".</p>
+ <attribute name="protocols" required="false">
+ <p>The names of the protocols to support when communicating with clients.
+ This should be a list of any combination of the following:
+ </p>
+ <ul><li>SSLv2Hello</li><li>SSLv2</li><li>SSLv3</li><li>TLSv1</li>
+ <li>TLSv1.1</li><li>TLSv1.2</li><li>all</li></ul>
+ <p>Each token in the list can be prefixed with a plus sign ("+")
+ or a minus sign ("-"). A plus sign adds the protocol, a minus sign
+ removes it form the current list. The list is built starting from
+ an empty list.</p>
+ <p>The token <code>all</code> is an alias for
+ <code>SSLv2Hello,TLSv1,TLSv1.1,TLSv1.2</code>.</p>
+ <p>Note that <code>SSLv2Hello</code> will be ignored for OpenSSL based
+ secure connectors. If more than one protocol is specified for an OpenSSL
+ based secure connector it will always support <code>SSLv2Hello</code>. If a
+ single protocol is specified it will not support
+ <code>SSLv2Hello</code>.</p>
+ <p>Note that <code>SSLv2</code> and <code>SSLv3</code> are inherently
+ unsafe.</p>
+ <p>If not specified, the default value of <code>all</code> will be
+ used.</p>
</attribute>
<attribute name="sessionCacheSize" required="false">
+ <p>JSSE only.</p>
<p>The number of SSL sessions to maintain in the session cache. Use 0 to
specify an unlimited cache size. If not specified, a default of 0 is
used.</p>
</attribute>
<attribute name="sessionTimeout" required="false">
+ <p>JSSE only.</p>
<p>The time, in seconds, after the creation of an SSL session that it will
timeout. Use 0 to specify an unlimited timeout. If not specified, a
default of 86400 (24 hours) is used.</p>
</attribute>
- <attribute name="sslEnabledProtocols" required="false">
- <p>The comma separated list of SSL protocols to support for HTTPS
- connections. If specified, only the protocols that are listed and
- supported by the SSL implementation will be enabled. If not specified,
- the JVM default (excluding SSLv2 and SSLv3 if the JVM enables either or
- both of them by default) is used. The permitted values may be obtained
- from the JVM documentation for the allowed values for
- <code>SSLSocket.setEnabledProtocols()</code> e.g.
- <a href="http://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#jssenames">
- Oracle Java 7</a>. Note: There is overlap between this attribute and
- <code>sslProtocol</code>.</p>
- </attribute>
-
- <attribute name="sslImplementationName" required="false">
- <p>The class name of the SSL implementation to use. If not specified, the
- default of <code>org.apache.tomcat.util.net.jsse.JSSEImplementation</code>
- will be used which wraps JVM's default JSSE provider. Note that the
- JVM can be configured to use a different JSSE provider as the default.</p>
- </attribute>
-
<attribute name="sslProtocol" required="false">
+ <p>JSSE only.</p>
<p>The SSL protocol(s) to use (a single value may enable multiple
protocols - see the JVM documentation for details). If not specified, the
default is <code>TLS</code>. The permitted values may be obtained from the
@@ -1280,24 +1251,19 @@
<code>SSLContext</code> instance e.g.
<a href="http://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#SSLContext">
Oracle Java 7</a>. Note: There is overlap between this attribute and
- <code>sslEnabledProtocols</code>.</p>
+ <code>protocols</code>.</p>
</attribute>
<attribute name="trustManagerClassName" required="false">
+ <p>JSSE only.</p>
<p>The name of a custom trust manager class to use to validate client
certificates. The class must have a zero argument constructor and must
also implement <code>javax.net.ssl.X509TrustManager</code>. If this
- attribute is set, the trust store attributes may be ignored.
- </p>
- </attribute>
-
- <attribute name="trustMaxCertLength" required="false">
- <p>The maximum number of intermediate certificates that will be allowed
- when validating client certificates. If not specified, the default value
- of 5 will be used.</p>
+ attribute is set, the trust store attributes may be ignored.</p>
</attribute>
<attribute name="truststoreAlgorithm" required="false">
+ <p>JSSE only.</p>
<p>The algorithm to use for truststore. If not specified, the default
value returned by
<code>javax.net.ssl.TrustManagerFactory.getDefaultAlgorithm()</code> is
@@ -1305,14 +1271,17 @@
</attribute>
<attribute name="truststoreFile" required="false">
+ <p>JSSE only.</p>
<p>The trust store file to use to validate client certificates. The
default is the value of the <code>javax.net.ssl.trustStore</code> system
property. If neither this attribute nor the default system property is
- set, no trust store will be configured. The file may be specified using a
- URL, an absolute path or a relative (to CATALINA_BASE) path.</p>
+ set, no trust store will be configured. Relative paths
+ will be resolved against <code>$CATALINA_BASE</code>. A URL may also be
+ used for this attribute.</p>
</attribute>
- <attribute name="truststorePass" required="false">
+ <attribute name="truststorePassword" required="false">
+ <p>JSSE only.</p>
<p>The password to access the trust store. The default is the value of the
<code>javax.net.ssl.trustStorePassword</code> system property. If that
property is null, no trust store password will be configured. If an
@@ -1322,6 +1291,7 @@
</attribute>
<attribute name="truststoreProvider" required="false">
+ <p>JSSE only.</p>
<p>The name of the truststore provider to be used for the server
certificate. The default is the value of the
<code>javax.net.ssl.trustStoreProvider</code> system property. If
@@ -1334,6 +1304,7 @@
</attribute>
<attribute name="truststoreType" required="false">
+ <p>JSSE only.</p>
<p>The type of key store used for the trust store. The default is the
value of the <code>javax.net.ssl.trustStoreType</code> system property. If
that property is null, the value of <code>keystoreType</code> is used as
@@ -1344,7 +1315,313 @@
</subsection>
- <subsection name="SSL Support - APR/Native">
+ <subsection name="SSL Support - Certificate">
+
+ <p></p>
+
+ <attributes>
+
+ <attribute name="certificateFile" required="true">
+ <p>Name of the file that contains the server certificate. The format is
+ PEM-encoded. Relative paths will be resolved against
+ <code>$CATALINA_BASE</code>.</p>
+ <p>In addition to the certificate, the file can also contain as optional
+ elements DH parameters and/or an EC curve name for ephemeral keys, as
+ generated by <code>openssl dhparam</code> and <code>openssl ecparam</code>,
+ respectively. The output of the respective OpenSSL command can simply
+ be concatenated to the certificate file.</p>
+ </attribute>
+
+ <attribute name="certificateChainFile" required="false">
+ <p>Name of the file that contains the certificate chain associated with
+ the server certificate used. The format is
+ PEM-encoded. Relative paths will be resolved against
+ <code>$CATALINA_BASE</code>.</p>
+ <p>The certificate chain used for Tomcat should not include the server
+ certificate as its first element.</p>
+ <p>Note that when using more than one certificate for different types,
+ they all must use the same certificate chain.</p>
+ </attribute>
+
+ <attribute name="certificateKeyAlias" required="false">
+ <p>JSSE only.</p>
+ <p>The alias used for the server key and certificate in the keystore. If
+ not specified, the first key read from the keystore will be used. The
+ order in which keys are read from the keystore is implementation
+ dependent. It may not be the case that keys are read from the keystore in
+ the same order as they were added. If more than one key is present in the
+ keystore it is strongly recommended that a keyAlias is configured to
+ ensure that the correct key is used.</p>
+ </attribute>
+
+ <attribute name="certificateKeyFile" required="false">
+ <p>Name of the file that contains the server private key. The format is
+ PEM-encoded. The default value is the value of
+ <strong>certificateFile</strong> and in this case both certificate and
+ private key have to be in this file (NOT RECOMMENDED). Relative paths will
+ be resolved against <code>$CATALINA_BASE</code>.</p>
+ </attribute>
+
+ <attribute name="certificateKeyPassword" required="false">
+ <p>The password used to access the private key associated with the server
+ certificate from the specified file.</p>
+ <p>If not specified, the default behaviour for JSSE is to use the
+ <strong>certificateKeystorePassword</strong>. For OpenSSL the default
+ behaviour is not to use a password.</p>
+ </attribute>
+
+ <attribute name="certificateKeystoreFile" required="false">
+ <p>JSSE only.</p>
+ <p>The pathname of the keystore file where you have stored the server
+ certificate and key to be loaded. By default, the pathname is the file
+ <code>.keystore</code> in the operating system home directory of the user
+ that is running Tomcat. If your <code>keystoreType</code> doesn't need a
+ file use <code>""</code> (empty string) or <code>NONE</code> for this
+ parameter. Relative paths will be resolved against
+ <code>$CATALINA_BASE</code>. A URL may also be used for this attribute.
+ </p>
+ </attribute>
+
+ <attribute name="certificateKeystorePassword" required="false">
+ <p>JSSE only.</p>
+ <p>The password to use to access the keystore containing the server's
+ private key and certificate. If not specified, a default of
+ <code>changeit</code> will be used.</p>
+ </attribute>
+
+ <attribute name="certificateKeystoreProvider" required="false">
+ <p>JSSE only.</p>
+ <p>The name of the keystore provider to be used for the server
+ certificate. If not specified, the value of the system property
+ <code>javax.net.ssl.keyStoreProvider</code> is used. If neither this
+ attribute nor the system property are set, the list of registered
+ providers is traversed in preference order and the first provider that
+ supports the <code>keystoreType</code> is used.
+ </p>
+ </attribute>
+
+ <attribute name="certificateKeystoreType" required="false">
+ <p>JSSE only.</p>
+ <p>The type of keystore file to be used for the server certificate.
+ If not specified, the value of the system property
+ <code>javax.net.ssl.keyStoreType</code> is used. If neither this attribute
+ nor the system property are set, a default value of "<code>JKS</code>". is
+ used.</p>
+ </attribute>
+
+ <attribute name="type" required="false">
+ <p>The type of certificate. This is used to identify the ciphers that are
+ compatible with the certificate. It must be one of <code>UNDEFINED</code>,
+ <code>RSA</code>, <code>DSS</code> or <code>EC</code>. If only one
+ <strong>Certificate</strong> is nested within a <code>SSLHostConfig</code>
+ then this attribute is not required and will default to
+ <code>UNDEFINED</code>. If multiple <strong>Certificate</strong>s are
+ nested within a <code>SSLHostConfig</code> then this attribute is required
+ and each <strong>Certificate</strong> must have a unique type.</p>
+ </attribute>
+
+ </attributes>
+
+ </subsection>
+
+ <subsection name="SSL Support - Connector - NIO and NIO2">
+
+ <p>When APR/native is enabled, the connectors will default to using OpenSSL through JSSE,
+ which may be more optimized than the JSSE Java implementation depending on the processor being used,
+ and can be complemented with many commercial accelerator components.</p>
+
+ <p>The following NIO and NIO2 SSL configuration attributes are not specific to
+ a virtual host and, therefore, must be configured on the connector.</p>
+
+ <attributes>
+
+ <attribute name="sniParseLimit" required="false">
+ <p>In order to implement SNI support, Tomcat has to parse the first TLS
+ message received on a new TLS connection (the client hello) to extract the
+ requested server name. The message needs to be buffered so it can then be
+ passed to the JSSE implementation for normal TLS processing. In theory,
+ this first message could be very large although in practice it is
+ typically a few hundred bytes. This attribute sets the maximum message
+ size that Tomcat will buffer. If a message exceeds this size, the
+ connection will be configured as if no server name was indicated by the
+ client. If not specified a default of <code>65536</code> (64k) will be
+ used.</p>
+ </attribute>
+
+ <attribute name="sslImplementationName" required="false">
+ <p>The class name of the SSL implementation to use. If not specified and
+ the tomcat-native library is not installed, the
+ default of <code>org.apache.tomcat.util.net.jsse.JSSEImplementation</code>
+ will be used which wraps JVM's default JSSE provider. Note that the
+ JVM can be configured to use a different JSSE provider as the default.
+ Tomcat also bundles a special SSL implementation for JSSE that is backed
+ by OpenSSL. To enable it, the native library should be enabled as if
+ intending to use the APR connector, and Tomcat will automatically enable it
+ and the default value of this attribute becomes
+ <code>org.apache.tomcat.util.net.openssl.OpenSSLImplementation</code>.
+ In that case, the attributes from either JSSE and OpenSSL
+ configuration styles can be used, as long as the two types are not mixed
+ (for example, it is not allowed to define use of a Java keystore and
+ specify a separate pem private key using the OpenSSL attribute).</p>
+ </attribute>
+
+ </attributes>
+
+ </subsection>
+
+ <subsection name="SSL Support - Connector - NIO and NIO2 (deprecated)">
+
+ <p>The following NIO and NIO2 SSL configuration attributes have been
+ deprecated in favor of the default
+ <a href="#SSL_Support_-_SSLHostConfig">SSLHostConfig</a> element.
+ </p>
+
+ <attributes>
+
+ <attribute name="algorithm" required="false">
+ <p>This is an alias for the <code>keyManagerAlgorithm</code> attribute of
+ the default <a href="#SSL_Support_-_SSLHostConfig">SSLHostConfig</a>
+ element.</p>
+ </attribute>
+
+ <attribute name="ciphers" required="false">
+ <p>This is an alias for the <code>ciphers</code> attribute of the default
+ <a href="#SSL_Support_-_SSLHostConfig">SSLHostConfig</a> element.</p>
+ </attribute>
+
+ <attribute name="clientAuth" required="false">
+ <p>This is an alias for the <code>certificateVerification</code> attribute
+ of the default <a href="#SSL_Support_-_SSLHostConfig">SSLHostConfig</a>
+ element.</p>
+ </attribute>
+
+ <attribute name="crlFile" required="false">
+ <p>This is an alias for the <code>certificateRevocationFile</code>
+ attribute of the default
+ <a href="#SSL_Support_-_SSLHostConfig">SSLHostConfig</a> element.</p>
+ </attribute>
+
+ <attribute name="keyAlias" required="false">
+ <p>This is an alias for the <code>certificateKeyAlias</code> attribute of
+ the first <a href="#SSL_Support_-_Certificate">Certificate</a> element
+ nested in the default
+ <a href="#SSL_Support_-_SSLHostConfig">SSLHostConfig</a> element.</p>
+ </attribute>
+
+ <attribute name="keyPass" required="false">
+ <p>This is an alias for the <code>certificateKeyPassword</code> attribute
+ of the first <a href="#SSL_Support_-_Certificate">Certificate</a> element
+ nested in the default
+ <a href="#SSL_Support_-_SSLHostConfig">SSLHostConfig</a> element.</p>
+ </attribute>
+
+ <attribute name="keystoreFile" required="false">
+ <p>This is an alias for the <code>certificateKeystoreFile</code> attribute
+ of the first <a href="#SSL_Support_-_Certificate">Certificate</a> element
+ nested in the default
+ <a href="#SSL_Support_-_SSLHostConfig">SSLHostConfig</a> element.</p>
+ </attribute>
+
+ <attribute name="keystorePass" required="false">
+ <p>This is an alias for the <code>certificateKeystorePassword</code>
+ attribute of the first
+ <a href="#SSL_Support_-_Certificate">Certificate</a> element nested in the
+ default <a href="#SSL_Support_-_SSLHostConfig">SSLHostConfig</a>
+ element.</p>
+ </attribute>
+
+ <attribute name="keystoreProvider" required="false">
+ <p>This is an alias for the <code>certificateKeystoreProvider</code>
+ attribute of the first
+ <a href="#SSL_Support_-_Certificate">Certificate</a> element nested in the
+ default <a href="#SSL_Support_-_SSLHostConfig">SSLHostConfig</a>
+ element.</p>
+ </attribute>
+
+ <attribute name="keystoreType" required="false">
+ <p>This is an alias for the <code>certificateKeystoreType</code> attribute
+ of the first <a href="#SSL_Support_-_Certificate">Certificate</a> element
+ nested in the default
+ <a href="#SSL_Support_-_SSLHostConfig">SSLHostConfig</a> element.</p>
+ </attribute>
+
+ <attribute name="sessionCacheSize" required="false">
+ <p>This is an alias for the <code>sessionCacheSize</code> attribute of the
+ default <a href="#SSL_Support_-_SSLHostConfig">SSLHostConfig</a>
+ element.</p>
+ </attribute>
+
+ <attribute name="sessionTimeout" required="false">
+ <p>This is an alias for the <code>sessionTimeout</code> attribute of the
+ default <a href="#SSL_Support_-_SSLHostConfig">SSLHostConfig</a>
+ element.</p>
+ </attribute>
+
+ <attribute name="sslEnabledProtocols" required="false">
+ <p>This is an alias for the <code>protocols</code> attribute of the
+ default <a href="#SSL_Support_-_SSLHostConfig">SSLHostConfig</a>
+ element.</p>
+ </attribute>
+
+ <attribute name="sslProtocol" required="false">
+ <p>This is an alias for the <code>sslProtocol</code> attribute of the
+ default <a href="#SSL_Support_-_SSLHostConfig">SSLHostConfig</a>
+ element.</p>
+ </attribute>
+
+ <attribute name="trustManagerClassName" required="false">
+ <p>This is an alias for the <code>trustManagerClassName</code> attribute
+ of the default <a href="#SSL_Support_-_SSLHostConfig">SSLHostConfig</a>
+ element.</p>
+ </attribute>
+
+ <attribute name="trustMaxCertLength" required="false">
+ <p>This is an alias for the <code>certificateVerificationDepth</code>
+ attribute of the default
+ <a href="#SSL_Support_-_SSLHostConfig">SSLHostConfig</a> element.</p>
+ </attribute>
+
+ <attribute name="truststoreAlgorithm" required="false">
+ <p>This is an alias for the <code>truststoreAlgorithm</code> attribute of
+ the default <a href="#SSL_Support_-_SSLHostConfig">SSLHostConfig</a>
+ element.</p>
+ </attribute>
+
+ <attribute name="truststoreFile" required="false">
+ <p>This is an alias for the <code>truststoreFile</code> attribute of
+ the default <a href="#SSL_Support_-_SSLHostConfig">SSLHostConfig</a>
+ element.</p>
+ </attribute>
+
+ <attribute name="truststorePass" required="false">
+ <p>This is an alias for the <code>truststorePassword</code> attribute of
+ the default <a href="#SSL_Support_-_SSLHostConfig">SSLHostConfig</a>
+ element.</p>
+ </attribute>
+
+ <attribute name="truststoreProvider" required="false">
+ <p>This is an alias for the <code>truststoreProvider</code> attribute of
+ the default <a href="#SSL_Support_-_SSLHostConfig">SSLHostConfig</a>
+ element.</p>
+ </attribute>
+
+ <attribute name="truststoreType" required="false">
+ <p>This is an alias for the <code>truststoreType</code> attribute of
+ the default <a href="#SSL_Support_-_SSLHostConfig">SSLHostConfig</a>
+ element.</p>
+ </attribute>
+
+ <attribute name="useServerCipherSuitesOrder" required="false">
+ <p>This is an alias for the <code>honorCipherOrder</code> attribute of the
+ default <a href="#SSL_Support_-_SSLHostConfig">SSLHostConfig</a>
+ element.</p>
+ </attribute>
+
+ </attributes>
+
+ </subsection>
+
+ <subsection name="SSL Support - Connector - APR/Native (deprecated)">
<p>When APR/native is enabled, the HTTPS connector will use a socket poller
for keep-alive, increasing scalability of the server. It also uses OpenSSL,
@@ -1363,140 +1640,95 @@
<attributes>
<attribute name="SSLCACertificateFile" required="false">
- <p>See <a href="http://httpd.apache.org/docs/2.2/mod/mod_ssl.html#sslcacertificatefile">
- the mod_ssl documentation</a>.</p>
- </attribute>
-
- <attribute name="SSLCACertificatePath" required="false">
- <p>See <a href="http://httpd.apache.org/docs/2.2/mod/mod_ssl.html#sslcacertificatepath">
- the mod_ssl documentation</a>.</p>
- </attribute>
-
- <attribute name="SSLCARevocationFile" required="false">
- <p>See <a href="http://httpd.apache.org/docs/2.2/mod/mod_ssl.html#sslcarevocationfile">
- the mod_ssl documentation</a>.</p>
- </attribute>
-
- <attribute name="SSLCARevocationPath" required="false">
- <p>See <a href="http://httpd.apache.org/docs/2.2/mod/mod_ssl.html#sslcarevocationpath">
- the mod_ssl documentation</a>.</p>
- </attribute>
-
- <attribute name="SSLCertificateChainFile" required="false">
- <p>See <a href="http://httpd.apache.org/docs/2.2/mod/mod_ssl.html#sslcertificatechainfile">
- the mod_ssl documentation</a>.</p>
- </attribute>
-
- <attribute name="SSLCACertificateFile" required="false">
- <p>Name of the file that contains the concatenated certificates for the
- trusted certificate authorities. The format is PEM-encoded.</p>
+ <p>This is an alias for the <code>caCertificateFile</code>
+ attribute of the default
+ <a href="#SSL_Support_-_SSLHostConfig">SSLHostConfig</a> element.</p>
</attribute>
<attribute name="SSLCACertificatePath" required="false">
- <p>Name of the directory that contains the certificates for the trusted
- certificate authorities. The format is PEM-encoded.</p>
+ <p>This is an alias for the <code>caCertificatePath</code>
+ attribute of the default
+ <a href="#SSL_Support_-_SSLHostConfig">SSLHostConfig</a> element.</p>
</attribute>
<attribute name="SSLCARevocationFile" required="false">
- <p>Name of the file that contains the concatenated certificate revocation
- lists for the certificate authorities. The format is PEM-encoded.</p>
+ <p>This is an alias for the <code>certificateRevocationFile</code>
+ attribute of the default
+ <a href="#SSL_Support_-_SSLHostConfig">SSLHostConfig</a> element.</p>
</attribute>
<attribute name="SSLCARevocationPath" required="false">
- <p>Name of the directory that contains the certificate revocation lists
- for the certificate authorities. The format is PEM-encoded.</p>
- </attribute>
-
- <attribute name="SSLCertificateChainFile" required="false">
- <p>Name of the file that contains concatenated certifcates for the
- certificate authorities which form the certifcate chain for the server
- certificate. The format is PEM-encoded.</p>
+ <p>This is an alias for the <code>certificateRevocationPath</code>
+ attribute of the default
+ <a href="#SSL_Support_-_SSLHostConfig">SSLHostConfig</a> element.</p>
</attribute>
<attribute name="SSLCertificateFile" required="true">
- <p>Name of the file that contains the server certificate. The format is
- PEM-encoded.</p>
- <p>In addition to the certificate, the file can also contain as optional
- elements DH parameters and/or an EC curve name for ephemeral keys, as
- generated by <code>openssl dhparam</code> and <code>openssl ecparam</code>,
- respectively. The output of the respective OpenSSL command can simply
- be concatenated to the certificate file. This feature needs APR/native
- version 1.1.34 or later.</p>
+ <p>This is an alias for the <code>certificateFile</code> attribute of the
+ first <a href="#SSL_Support_-_Certificate">Certificate</a> element nested
+ in the default <a href="#SSL_Support_-_SSLHostConfig">SSLHostConfig</a>
+ element.</p>
</attribute>
<attribute name="SSLCertificateKeyFile" required="false">
- <p>Name of the file that contains the server private key. The format is
- PEM-encoded. The default value is the value of "SSLCertificateFile" and in
- this case both certificate and private key have to be in this file (NOT
- RECOMMENDED).</p>
+ <p>This is an alias for the <code>certificateKeyFile</code> attribute of the
+ first <a href="#SSL_Support_-_Certificate">Certificate</a> element nested
+ in the default <a href="#SSL_Support_-_SSLHostConfig">SSLHostConfig</a>
+ element.</p>
</attribute>
<attribute name="SSLCipherSuite" required="false">
- <p>Ciphers which may be used for communicating with clients. The default
- is <code>HIGH:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5</code>. See the OpenSSL
- documentation for details of the syntax for this attribute.</p>
+ <p>This is an alias for the <code>ciphers</code> attribute of the default
+ <a href="#SSL_Support_-_SSLHostConfig">SSLHostConfig</a> element.</p>
</attribute>
<attribute name="SSLDisableCompression" required="false">
- <p>Disables compression if set to <code>true</code> and OpenSSL supports
- disabling compression. Default is <code>false</code> which inherits the
- default compression setting in OpenSSL.</p>
+ <p>This is an alias for the <code>disableCompression</code> attribute of
+ the default <a href="#SSL_Support_-_SSLHostConfig">SSLHostConfig</a>
+ element.</p>
</attribute>
<attribute name="SSLHonorCipherOrder" required="false">
- <p>Set to <code>true</code> to enforce the server's cipher order
- (from the <code>SSLCipherSuite</code> setting) instead of allowing
- the client to choose the cipher (which is the default).</p>
+ <p>This is an alias for the <code>honorCipherOrder</code> attribute of the
+ default <a href="#SSL_Support_-_SSLHostConfig">SSLHostConfig</a>
+ element.</p>
</attribute>
<attribute name="SSLPassword" required="false">
- <p>Pass phrase for the encrypted private key. If "SSLPassword" is not
- provided, the callback function should prompt for the pass phrase.</p>
+ <p>This is an alias for the <code>certificateKeyPassword</code> attribute
+ of the first <a href="#SSL_Support_-_Certificate">Certificate</a> element
+ nested in the default
+ <a href="#SSL_Support_-_SSLHostConfig">SSLHostConfig</a> element.</p>
</attribute>
<attribute name="SSLProtocol" required="false">
- <p>The names of the protocols to support when communicating with clients.
- This should be a list of any combination of the following:
- </p>
- <ul><li>SSLv2</li><li>SSLv3</li><li>TLSv1</li>
- <li>TLSv1.1</li><li>TLSv1.2</li><li>all</li></ul>
- <p>Each token in the list can be prefixed with a plus sign ("+")
- or a minus sign ("-"). A plus sign adds the protocol, a minus sign
- removes it form the current list. The list is built starting from
- an empty list.</p>
- <p>The token <code>all</code> is an alias for
- <code>TLSv1+TLSv1.1+TLSv1.2</code>.</p>
- <p>If more than one protocol is specified for an OpenSSL
- based secure connector it will always support <code>SSLv2Hello</code>. If a
- single protocol is specified it will not support
- <code>SSLv2Hello</code>.</p>
- <p>Note that <code>SSLv2</code> and <code>SSLv3</code> are inherently
- unsafe.</p>
- <p>If not specified, the default value of <code>all</code> will be
- used.</p>
+ <p>This is an alias for the <code>protocols</code> attribute of the
+ default <a href="#SSL_Support_-_SSLHostConfig">SSLHostConfig</a>
+ element.</p>
</attribute>
<attribute name="SSLVerifyClient" required="false">
- <p>Ask client for certificate. The default is "none", meaning the client
- will not have the opportunity to submit a certificate. Other acceptable
- values include "optional", "require" and "optionalNoCA".</p>
+ <p>This is an alias for the <code>certificateVerification</code> attribute
+ of the default <a href="#SSL_Support_-_SSLHostConfig">SSLHostConfig</a>
+ element.</p>
</attribute>
<attribute name="SSLVerifyDepth" required="false">
- <p>Maximum verification depth for client certificates. The default is
- "10".</p>
+ <p>This is an alias for the <code>certificateVerificationDepth</code>
+ attribute of the default
+ <a href="#SSL_Support_-_SSLHostConfig">SSLHostConfig</a> element.</p>
</attribute>
<attribute name="SSLDisableSessionTickets" required="false">
- <p>Disables use of TLS Session Tickets (RFC 4507) if set to
- <code>true</code>. Default is <code>false</code>.</p>
+ <p>This is an alias for the <code>disableSessionTickets</code> attribute
+ of the default <a href="#SSL_Support_-_SSLHostConfig">SSLHostConfig</a>
+ element.</p>
</attribute>
</attributes>
</subsection>
- </subsection>
<subsection name="Connector Comparison">
<p>Below is a small chart that shows how the connectors differ.</p>
@@ -1504,77 +1736,66 @@
<table class="defaultTable" style="text-align: center;">
<tr>
<th />
- <th style="text-align: center;">Java Blocking Connector<br />BIO</th>
<th style="text-align: center;">Java Nio Connector<br />NIO</th>
<th style="text-align: center;">Java Nio2 Connector<br />NIO2</th>
<th style="text-align: center;">APR/native Connector<br />APR</th>
</tr>
<tr>
<th>Classname</th>
- <td><code class="noHighlight">Http11Protocol</code></td>
<td><code class="noHighlight">Http11NioProtocol</code></td>
<td><code class="noHighlight">Http11Nio2Protocol</code></td>
<td><code class="noHighlight">Http11AprProtocol</code></td>
</tr>
<tr>
<th>Tomcat Version</th>
- <td>3.x onwards</td>
<td>6.x onwards</td>
<td>8.x onwards</td>
<td>5.5.x onwards</td>
</tr>
<tr>
<th>Support Polling</th>
- <td>NO</td>
<td>YES</td>
<td>YES</td>
<td>YES</td>
</tr>
<tr>
<th>Polling Size</th>
- <td>N/A</td>
<td><code class="noHighlight">maxConnections</code></td>
<td><code class="noHighlight">maxConnections</code></td>
<td><code class="noHighlight">maxConnections</code></td>
</tr>
<tr>
<th>Read Request Headers</th>
- <td>Blocking</td>
<td>Non Blocking</td>
<td>Non Blocking</td>
- <td>Blocking</td>
+ <td>Non Blocking</td>
</tr>
<tr>
<th>Read Request Body</th>
<td>Blocking</td>
<td>Blocking</td>
<td>Blocking</td>
- <td>Blocking</td>
</tr>
<tr>
<th>Write Response Headers and Body</th>
<td>Blocking</td>
<td>Blocking</td>
<td>Blocking</td>
- <td>Blocking</td>
</tr>
<tr>
<th>Wait for next Request</th>
- <td>Blocking</td>
<td>Non Blocking</td>
<td>Non Blocking</td>
<td>Non Blocking</td>
</tr>
<tr>
<th>SSL Support</th>
- <td>Java SSL</td>
- <td>Java SSL</td>
- <td>Java SSL</td>
+ <td>Java SSL or OpenSSL</td>
+ <td>Java SSL or OpenSSL</td>
<td>OpenSSL</td>
</tr>
<tr>
<th>SSL Handshake</th>
- <td>Blocking</td>
<td>Non blocking</td>
<td>Non blocking</td>
<td>Blocking</td>
@@ -1584,7 +1805,6 @@
<td><code class="noHighlight">maxConnections</code></td>
<td><code class="noHighlight">maxConnections</code></td>
<td><code class="noHighlight">maxConnections</code></td>
- <td><code class="noHighlight">maxConnections</code></td>
</tr>
</table>
diff --git a/webapps/docs/config/http2.xml b/webapps/docs/config/http2.xml
new file mode 100644
index 0000000..8e5b7e9
--- /dev/null
+++ b/webapps/docs/config/http2.xml
@@ -0,0 +1,187 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+-->
+<!DOCTYPE document [
+ <!ENTITY project SYSTEM "project.xml">
+]>
+<document url="http2.html">
+
+ &project;
+
+ <properties>
+ <title>The HTTP2 Upgrade Protocol</title>
+ </properties>
+
+<body>
+
+<section name="Table of Contents">
+<toc/>
+</section>
+
+<section name="Introduction">
+
+ <p>The <strong>HTTP Upgrade Protocol</strong> element represents an
+ <strong>Upgrade Protocol</strong> component that supports the HTTP/2 protocol.
+ An instance of this component must be associated with an existing
+ <a href="http.html">HTTP/1.1 Connector</a>.</p>
+
+ <p>HTTP/2 connectors use non-blocking I/O, only utilising a container thread
+ from the thread pool when there is data to read and write. However, because
+ the Servlet API is fundamentally blocking, each HTTP/2 stream requires a
+ dedicated container thread for the duration of that stream.</p>
+
+</section>
+
+
+<section name="Attributes">
+
+ <subsection name="Common Attributes">
+
+ <p>All implementations of <strong>Upgrade Protocol</strong> support the
+ following attributes:</p>
+
+ <attributes>
+
+ <attribute name="className" required="true">
+ <p>This must be <code>org.apache.coyote.http2.Http2Protocol</code>.</p>
+ </attribute>
+
+ </attributes>
+
+ </subsection>
+
+ <subsection name="Standard Implementation">
+
+ <p>The HTTP/2 <strong>Upgrade Protocol</strong> implementation supports the
+ following attributes in addition to the common attributes listed above.</p>
+
+ <attributes>
+
+ <attribute name="allowedTrailerHeaders" required="false">
+ <p>By default Tomcat will ignore all trailer headers when processing
+ HTTP/2 connections. For a header to be processed, it must be added to this
+ comma-separated list of header names.</p>
+ </attribute>
+
+ <attribute name="initialWindowSize" required="false">
+ <p>Controls the initial size of the flow control window for streams that
+ Tomcat advertises to clients. If not specified, the default value of
+ <code>65535</code> is used.</p>
+ </attribute>
+
+ <attribute name="keepAliveTimeout" required="false">
+ <p>The time, in milliseconds, that Tomcat will wait between HTTP/2 frames
+ before closing the connection. Negative values will be treated as an
+ infinite timeout. If not specified, a default value of <code>-1</code>
+ will be used.</p>
+ </attribute>
+
+ <attribute name="maxConccurentStreamExecution" required="false">
+ <p>The controls the maximum number of streams for any one connection that
+ can be allocated threads from the container thread pool. If more streams
+ are active than threads are avaialble, those streams will have to wait
+ for a stream to become available. If not specified, the default value of
+ <code>200</code> will be used.</p>
+ </attribute>
+
+ <attribute name="maxConccurentStreams" required="false">
+ <p>The controls the maximum number of active streams permitted for any one
+ connection. If a client attempts to open more active streams than this
+ limit, the stream will be reset with a <code>STREAM_REFUSED</code> error.
+ If not specified, the default value of <code>200</code> will be used.</p>
+ </attribute>
+
+ <attribute name="maxHeaderCount" required="false">
+ <p>The maximum number of headers in a request that is allowed by the
+ container. A request that contains more headers than the specified limit
+ will be rejected. A value of less than 0 means no limit.
+ If not specified, a default of 100 is used.</p>
+ </attribute>
+
+ <attribute name="maxHeaderSize" required="false">
+ <p>The maximum total size for all headers in a request that is allowed by
+ the container. Total size for a header is calculated as the uncompressed
+ size of the header name in bytes, plus the uncompressed size of the header
+ value in bytes plus an HTTP/2 overhead of 3 bytes per header. A request
+ that contains a set of headers that requires more than the specified limit
+ will be rejected. A value of less than 0 means no limit. If not specified,
+ a default of 8192 is used.</p>
+ </attribute>
+
+ <attribute name="maxTrailerCount" required="false">
+ <p>The maximum number of trailer headers in a request that is allowed by
+ the container. A request that contains more trailer headers than the
+ specified limit will be rejected. A value of less than 0 means no limit.
+ If not specified, a default of 100 is used.</p>
+ </attribute>
+
+ <attribute name="maxTrailerSize" required="false">
+ <p>The maximum total size for all trailer headers in a request that is
+ allowed by the container. Total size for a header is calculated as the
+ uncompressed size of the header name in bytes, plus the uncompressed size
+ of the header value in bytes plus an HTTP/2 overhead of 3 bytes per
+ header. A request that contains a set of trailer headers that requires
+ more than the specified limit will be rejected. A value of less than 0
+ means no limit. If not specified, a default of 8192 is used.</p>
+ </attribute>
+
+ <attribute name="readTimeout" required="false">
+ <p>The time, in milliseconds, that Tomcat will wait for additional data
+ when a partial HTTP/2 frame has been received. Negative values will be
+ treated as an infinite timeout. If not specified, a default value of
+ <code>10000</code> will be used.</p>
+ </attribute>
+
+ <attribute name="writeTimeout" required="false">
+ <p>The time, in milliseconds, that Tomcat will wait to write additional
+ data when an HTTP/2 frame has been partially written. Negative values will
+ be treated as an infinite timeout. If not specified, a default value of
+ <code>10000</code> will be used.</p>
+ </attribute>
+
+ </attributes>
+
+ <p>The HTTP/2 upgrade protocol will also inherit the following limits from the
+ <a href="http.html">HTTP Connector</a> it is nested with:</p>
+
+ <ul>
+ <li>maxCookieCount</li>
+ <li>maxParameterCount</li>
+ <li>maxPostSize</li>
+ <li>maxSavePostSize</li>
+ </ul>
+
+ </subsection>
+
+</section>
+
+<section name="Nested Components">
+
+ <p>This component does not support any nested components.</p>
+
+</section>
+
+
+<section name="Special Features">
+
+ <p>This component does not support any special features.</p>
+
+</section>
+
+</body>
+
+</document>
diff --git a/webapps/docs/config/jaspic.xml b/webapps/docs/config/jaspic.xml
new file mode 100644
index 0000000..aa24688
--- /dev/null
+++ b/webapps/docs/config/jaspic.xml
@@ -0,0 +1,186 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+-->
+<!DOCTYPE document [
+ <!ENTITY project SYSTEM "project.xml">
+]>
+<document url="jaspic.html">
+
+ &project;
+
+ <properties>
+ <title>JASPIC</title>
+ </properties>
+
+<body>
+
+<section name="Table of Contents">
+<toc />
+</section>
+
+<section name="Introduction">
+
+ <p>Tomcat implements JASPIC 1.1 Maintenance Release B
+ (<a href="https://www.jcp.org/en/jsr/detail?id=196">JSR 196</a>). The
+ implementation is primarily intended to enable the integration of 3rd party
+ JASPIC authentication implementations with Tomcat.</p>
+
+ <p>JASPIC may be configured dynamically by an application or statically via
+ the <code>$CATALINA_BASE/conf/jaspic-providers.xml</code> configuration file.
+ If present, a JASPIC configuration will over-ride any
+ <code><login-config></code> present in <code>web.xml</code>.</p>
+
+</section>
+
+<section name="Static configuration">
+
+ <subsection name="AuthConfigProvider">
+
+ <p>If the 3rd party implementation includes an
+ <code>AuthConfigProvider</code> then a web application can be configured to
+ use it by nesting the following inside the
+ <code><jaspic-providers></code> element in
+ <code>$CATALINA_BASE/conf/jaspic-providers.xml</code>.</p>
+<source><![CDATA[<provider name="any"
+ className="fully.qualified.implementation.class.Name"
+ layer="HttpServlet"
+ appContext="Catalina/localhost /contextPath"
+ description="any">
+ <property name="see-provider-documentation"
+ value="see-provider-documentation" />
+</provider>]]></source>
+
+ <p>The <code>name</code> and <code>description</code> attributes are not
+ used by Tomcat.</p>
+
+ <p>The <code>className</code> attribute must be the fully qualified class
+ name of the <code>AuthConfigProvider</code>. The implementation may be
+ packaged with the web application or in Tomcat's
+ <code>$CATALINA_BASE/lib</code> directory.</p>
+
+ <p>The <code>layer</code> attribute must be <code>HttpServlet</code>.</p>
+
+ <p>The <code>appContext</code> attribute must be exactly the concatenation
+ of:</p>
+ <ul>
+ <li>The engine name</li>
+ <li>The forward slash character</li>
+ <li>The host name</li>
+ <li>A single space</li>
+ <li>The context path</li>
+ </ul>
+
+ <p>If the <code>AuthConfigProvider</code> supports configuration via
+ properties these may be specified via <code><property></code> elements
+ nesting inside the <code><provide></code> element.</p>
+
+ </subsection>
+
+ <subsection name="ServerAuthModule">
+
+ <p>If the 3rd party implementation only provides an
+ <code>ServerAuthModule</code> then it will be necessary to provide a number
+ of supporting classes. These may be a custom implementation or,
+ alternatively, Tomcat provides a simple wrapper implementation for
+ <code>ServerAuthModule</code>s.
+ </p>
+
+ <p>Tomcat's wrapper for <code>ServerAuthModule</code> can be configured
+ by nesting the following inside the
+ <code><jaspic-providers></code> element in
+ <code>$CATALINA_BASE/conf/jaspic-providers.xml</code>.</p>
+<source><![CDATA[<provider name="any"
+ className="org.apache.catalina.authenticator.jaspic.SimpleAuthConfigProvider"
+ layer="HttpServlet"
+ appContext="Catalina/localhost /contextPath"
+ description="any">
+ <property name="org.apache.catalina.authenticator.jaspic.ServerAuthModule.1"
+ value="fully.qualified.implementation.class.Name" />
+ <property name="see-provider-documentation"
+ value="see-provider-documentation" />
+</provider>]]></source>
+
+ <p>The configuration is similar to the <code>AuthConfigProvider</code> in
+ the previous section but with some key differences.</p>
+
+ <p>The <code>className</code> attribute must be
+ <code>org.apache.catalina.authenticator.jaspic.SimpleAuthConfigProvider</code>.</p>
+
+ <p>The <code>ServerAuthModule</code>(s) are specified via properties. The
+ property name must be
+ <code>org.apache.catalina.authenticator.jaspic.ServerAuthModule.n</code>
+ where <code>n</code> is the index of the module. The index must start at 1
+ an increment in steps of 1 until all modules are defined. The value of the
+ property must be the fully qualified class name of the module.</p>
+ </subsection>
+
+</section>
+
+<section name="Dynamic configuration">
+
+ <p>JASPIC modules and configuration can be packaged within a WAR file with the
+ web application. The web application can then register the required JASPIC
+ configuration when it starts using the standard JASPIC APIs.</p>
+
+ <p>If parallel deployment is being used then dynamic configuration should not
+ be used. The JASPIC API assumes that a context path is unique for any given
+ host which is not the case when using parallel deployment. When using parallel
+ deployment, static JASPIC configuration should be used. This will require that
+ all versions of the application use the same JASPIC configuration.</p>
+
+</section>
+
+<section name="3rd party modules">
+
+ <p>This is not an exhaustive list. The Tomcat community welcomes contributions
+ that add to this section.</p>
+
+ <subsection name="Philip Green II's module for Google OAuth 2">
+
+ <p>The source code for this module along with the
+ <a href="https://github.com/phillipgreenii/google-oauth-2.0-serverauthmodule">documentation</a>
+ which includes details of the necessary Google API configuration is
+ available on GitHub.</p>
+
+ <p>A sample configuration for using this module with Tomcat would look like
+ this:</p>
+<source><![CDATA[<jaspic-providers xmlns="http://tomcat.apache.org/xml"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://tomcat.apache.org/xml jaspic-providers.xsd"
+ version="1.0">
+ <provider name="google-oauth"
+ className="org.apache.catalina.authenticator.jaspic.SimpleAuthConfigProvider"
+ layer="HttpServlet"
+ appContext="Catalina/localhost /contextPath"
+ description="Google OAuth test">
+ <property name="org.apache.catalina.authenticator.jaspic.ServerAuthModule.1"
+ value="com.idmworks.security.google.GoogleOAuthServerAuthModule" />
+ <property name="oauth.clientid"
+ value="obtained-from-Google-console" />
+ <property name="oauth.clientsecret"
+ value="obtained-from-Google-console" />
+ <property name="ignore_missing_login_context"
+ value="true" />
+ </provider>
+</jaspic-providers>]]></source>
+ </subsection>
+
+</section>
+
+</body>
+
+</document>
diff --git a/webapps/docs/config/listeners.xml b/webapps/docs/config/listeners.xml
index 0edd521..93e6509 100644
--- a/webapps/docs/config/listeners.xml
+++ b/webapps/docs/config/listeners.xml
@@ -126,6 +126,20 @@
<p>The default value is <code>off</code>.</p>
</attribute>
+ <attribute name="useAprConnector" required="false">
+ <p>This attribute controls the auto-selection of the connector
+ implementation. When the <strong>protocol</strong> is specified as
+ <code>HTTP/1.1</code> or <code>AJP/1.3</code> then if this attribute is
+ <code>true</code> the APR/native connector will be used but if this
+ attribute is false the NIO connector will be used.</p>
+ </attribute>
+
+ <attribute name="useOpenSSL" required="false">
+ <p>This attribute controls the auto-selection of the OpenSSL JSSE
+ implementation. The default is <code>true</code> which will use OpenSSL
+ if the native library is available and a NIO or NIO2 connector is used.</p>
+ </attribute>
+
</attributes>
</subsection>
@@ -242,12 +256,12 @@
<attribute name="securityPolicyProtection" required="false">
<p>Enables protection so that usage of the deprecated
- <code>javax.security.auth.Policy</code> class by a web application does not
- result in a memory leak. The first access of this class will trigger the
- static initializer that will retain a static reference to the context
- class loader. The protection calls the <code>getPolicy()</code> method
- of this class to ensure that the static initializer is not triggered by
- a web application. Defaults to <code>true</code>.</p>
+ <code>javax.security.auth.Policy</code> class by a web application does
+ not result in a memory leak. The first access of this class will trigger
+ the static initializer that will retain a static reference to the
+ context class loader. The protection calls the <code>getPolicy()</code>
+ method of this class to ensure that the static initializer is not
+ triggered by a web application. Defaults to <code>true</code>.</p>
<p>Note: The underlying leak has been fixed in Java 7 update 51 onwards
and Java 8 onwards. This protection is therefor disabled if running on
Java 8 onwards.</p>
diff --git a/webapps/docs/config/loader.xml b/webapps/docs/config/loader.xml
index e8f17a4..892fe28 100644
--- a/webapps/docs/config/loader.xml
+++ b/webapps/docs/config/loader.xml
@@ -127,12 +127,12 @@
</p>
<p>If not specified, the default value is
- <code>org.apache.catalina.loader.WebappClassLoader</code>. The
- default <strong>loaderClass</strong> is not parallel capable, which
- means that loading a class from this classloader is performed by one
- thread at a time. A parallel capable <strong>loaderClass</strong> is
- available and can be used by specifying
- <code>org.apache.catalina.loader.ParallelWebappClassLoader</code>.</p>
+ <code>org.apache.catalina.loader.ParallelWebappClassLoader</code>. The
+ default <strong>loaderClass</strong> is parallel capable, which
+ means that mutltiple threads may load difference classes in parallel.
+ A non-parallel capable <strong>loaderClass</strong> is available and can
+ be used by specifying
+ <code>org.apache.catalina.loader.WebappClassLoader</code>.</p>
</attribute>
<attribute name="searchExternalFirst" required="false">
diff --git a/webapps/docs/config/manager.xml b/webapps/docs/config/manager.xml
index cc7bb72..9fad3fb 100644
--- a/webapps/docs/config/manager.xml
+++ b/webapps/docs/config/manager.xml
@@ -65,22 +65,6 @@
If not specified, the standard value (defined below) will be used.</p>
</attribute>
- <attribute name="distributable" required="false">
- <p><strong>Deprecated</strong>: This should be configured via the
- Context.</p>
- <p>Set to <code>true</code> to ask the session manager to enforce
- the restrictions described in the Servlet Specification on
- distributable applications (primarily, this would mean that all
- session attributes must implement <code>java.io.Serializable</code>).
- Set to <code>false</code> (the default) to not enforce these
- restrictions.</p>
-
- <p><strong>NOTE</strong> - The value for this property is inherited
- automatically based on the presence or absence of the
- <code><distributable></code> element in the web application
- deployment descriptor (<code>/WEB-INF/web.xml</code>).</p>
- </attribute>
-
<attribute name="maxActiveSessions" required="false">
<p>The maximum number of active sessions that will be created by
this Manager, or <code>-1</code> (the default) for no limit.</p>
@@ -89,29 +73,6 @@
(e.g. with <code>HttpServletRequest.getSession()</code> call)
will fail with an <code>IllegalStateException</code>.</p>
</attribute>
-
- <attribute name="maxInactiveInterval" required="false">
- <p><strong>Deprecated</strong>: This should be configured via the
- Context.</p>
- <p>The initial maximum time interval, in seconds,
- between client requests before a session is invalidated. A negative value
- will result in sessions never timing out. If the attribute is not provided,
- a default of 1800 seconds (30 minutes) is used.</p>
-
- <p>This attribute provides the initial value whenever a
- new session is created, but the interval may be dynamically
- varied by a servlet via the
- <code>setMaxInactiveInterval</code> method of the <code>HttpSession</code> object.</p>
- </attribute>
-
- <attribute name="sessionIdLength" required="false">
- <p>The length of session ids created by this Manager, measured in bytes,
- excluding subsequent conversion to a hexadecimal string and
- excluding any JVM route information used for load balancing.
- This attribute is deprecated. Set the length on a nested
- <strong>SessionIdGenerator</strong> element instead.</p>
- </attribute>
-
</attributes>
</subsection>
diff --git a/webapps/docs/config/project.xml b/webapps/docs/config/project.xml
index 9001254..50fd996 100644
--- a/webapps/docs/config/project.xml
+++ b/webapps/docs/config/project.xml
@@ -44,7 +44,8 @@
</menu>
<menu name="Connectors">
- <item name="HTTP" href="http.html"/>
+ <item name="HTTP/1.1" href="http.html"/>
+ <item name="HTTP/2" href="http2.html"/>
<item name="AJP" href="ajp.html"/>
</menu>
@@ -89,6 +90,7 @@
<menu name="Other">
<item name="System properties" href="systemprops.html"/>
+ <item name="JASPIC" href="jaspic.html"/>
</menu>
</body>
diff --git a/webapps/docs/config/realm.xml b/webapps/docs/config/realm.xml
index f3390a9..7da9862 100644
--- a/webapps/docs/config/realm.xml
+++ b/webapps/docs/config/realm.xml
@@ -140,21 +140,6 @@
establishing a database connection.</p>
</attribute>
- <attribute name="digest" required="false">
- <p>The name of the <code>MessageDigest</code> algorithm used
- to encode user passwords stored in the database. If not specified,
- user passwords are assumed to be stored in clear-text.</p>
- <p>This attribute is deprecated. Set the algorithm on a nested
- <strong>CredentialHandler</strong> element instead.</p>
- </attribute>
-
- <attribute name="digestEncoding" required="false">
- <p>The charset for encoding digests. If not specified, the platform
- default will be used.</p>
- <p>This attribute is deprecated. Set the encoding on a nested
- <strong>CredentialHandler</strong> element instead.</p>
- </attribute>
-
<attribute name="driverName" required="true">
<p>Fully qualified Java class name of the JDBC driver to be
used to connect to the authentication database.</p>
@@ -183,8 +168,8 @@
<attribute name="userCredCol" required="true">
<p>Name of the column, in the "users" table, which contains
- the user's credentials (i.e. password). If a value for the
- <code>digest</code> attribute is specified, this component
+ the user's credentials (i.e. password). If a
+ <code>CredentialHandler</code> is specified, this component
will assume that the passwords have been encoded with the
specified algorithm. Otherwise, they will be assumed to be
in clear text.</p>
@@ -273,14 +258,6 @@
<p>The name of the JNDI JDBC DataSource for this Realm.</p>
</attribute>
- <attribute name="digest" required="false">
- <p>The name of the <code>MessageDigest</code> algorithm used
- to encode user passwords stored in the database. If not specified,
- user passwords are assumed to be stored in clear-text.</p>
- <p>This attribute is deprecated. Set the algorithm on a nested
- <strong>CredentialHandler</strong> element instead.</p>
- </attribute>
-
<attribute name="localDataSource" required="false">
<p>When the realm is nested inside a Context element, this allows the
realm to use a DataSource defined for the Context rather than a global
@@ -311,8 +288,8 @@
<attribute name="userCredCol" required="true">
<p>Name of the column, in the "users" table, which contains
- the user's credentials (i.e. password). If a value for the
- <code>digest</code> attribute is specified, this component
+ the user's credentials (i.e. password). If a
+ <code>CredentialHandler</code> is specified, this component
will assume that the passwords have been encoded with the
specified algorithm. Otherwise, they will be assumed to be
in clear text.</p>
@@ -416,7 +393,7 @@
</attribute>
<attribute name="alternateURL" required="false">
- <p>If a socket connection can not be made to the provider at
+ <p>If a socket connection cannot be made to the provider at
the <code>connectionURL</code> an attempt will be made to use the
<code>alternateURL</code>.</p>
</attribute>
@@ -479,17 +456,6 @@
"finding" and "searching". If not specified, "always" is used.</p>
</attribute>
- <attribute name="digest" required="false">
- <p>The digest algorithm to apply to the plaintext password offered
- by the user before comparing it with the value retrieved from the
- directory. Valid values are those accepted for the algorithm name
- by the <code>java.security.MessageDigest</code> class. If not
- specified the plaintext password is assumed to be retrieved. Not
- required unless <code>userPassword</code> is specified</p>
- <p>This attribute is deprecated. Set the algorithm on a nested
- <strong>CredentialHandler</strong> element instead.</p>
- </attribute>
-
<attribute name="hostnameVerifierClassName" required="false">
<p>The name of the class to use for hostname verification when
using StartTLS for securing the connection to the ldap server.
@@ -840,15 +806,6 @@
one of those roles.</p>
</attribute>
- <attribute name="digest" required="false">
- <p>The digest algorithm used to store passwords in non-plaintext
- formats. Valid values are those accepted for the algorithm name by the
- <code>java.security.MessageDigest</code> class. If not specified,
- passwords are stored in clear text.</p>
- <p>This attribute is deprecated. Set the algorithm on a nested
- <strong>CredentialHandler</strong> element instead.</p>
- </attribute>
-
<attribute name="pathname" required="false">
<p>URL, absolute path or relative path (to $CATALINA_BASE) for the XML
file containing our user information. See below for details on the
diff --git a/webapps/docs/config/systemprops.xml b/webapps/docs/config/systemprops.xml
index a607ccb..d4e5a2d 100644
--- a/webapps/docs/config/systemprops.xml
+++ b/webapps/docs/config/systemprops.xml
@@ -138,13 +138,6 @@
will be used.</p>
</property>
- <property name="org.apache.jasper.compiler. Parser.STRICT_QUOTE_ESCAPING">
- <p><strong>Deprecated</strong>. Configures the default setting for the
- <code>strictQuoteEscaping</code> JSP initialisation paramater.</p>
- <p>If not specified, the specification compliant default of
- <code>true</code> will be used.</p>
- </property>
-
<property name="org.apache.jasper.compiler. Parser.STRICT_WHITESPACE">
<p>If <code>false</code> the requirements for whitespace before an
attribute name will be relaxed so that the lack of whitespace will not
@@ -287,8 +280,6 @@
<li><code>org.apache.catalina.core.<br/>StandardHostValve.ACCESS_SESSION</code></li>
<li><code>org.apache.catalina.session.<br/>StandardSession.ACTIVITY_CHECK</code></li>
<li><code>org.apache.catalina.session.<br/>StandardSession.LAST_ACCESS_AT_START</code></li>
- <li><code>org.apache.tomcat.util.http.<br/>ServerCookie.ALWAYS_ADD_EXPIRES</code></li>
- <li><code>org.apache.tomcat.util.http.<br/>ServerCookie.FWD_SLASH_IS_SEPARATOR</code></li>
<li><code>org.apache.tomcat.util.http.<br/>ServerCookie.STRICT_NAMING</code></li>
<li>The <code>URIEncoding</code> attribute of any
<a href="http.html">HTTP connector</a> or
@@ -304,15 +295,6 @@
<li>The <code>xmlValidation</code> attribute of any
<a href="context.html">Context</a> element.</li>
</ul>
-
- <p>Note that changing a number of the above defaults is likely to break
- the majority of systems as some browsers are unable to correctly handle
- the cookie headers that result from a strict adherence to the
- specifications. Defaults, regardless of whether or not they have been
- changed by setting
- <code>org.apache.catalina.STRICT_SERVLET_COMPLIANCE</code> can always be
- overridden by explicitly setting the appropriate system property or element
- attribute.</p>
</property>
<property name="org.apache.catalina.connector. Response.ENFORCE_ENCODING_IN_GET_WRITER">
@@ -354,89 +336,13 @@
<li><code>org.apache.tomcat.websocket.server#isEnforceNoAddAfterHandshake</code>
(default changes from <code>false</code> to <code>true</code>)</li>
</ul>
-
- </property>
-
- <property
- name="org.apache.tomcat.util.http. ServerCookie.ALLOW_EQUALS_IN_VALUE">
- <p>Deprecated. This will be removed in Tomcat 9. Specify the
- <code>allowEqualsInValue</code> attribute on the
- <a href="cookie-processor.html">org.apache.tomcat.util.http.LegacyCookieProcessor</a>
- instead.</p>
- <p>This sets the default value for <code>allowEqualsInValue</code>
- attribute on the
- <code>org.apache.tomcat.util.http.LegacyCookieProcessor</code>.
- If this is <code>true</code> Tomcat will allow '<code>=</code>'
- characters when parsing unquoted cookie values. If <code>false</code>,
- cookie values containing '<code>=</code>' will be terminated when the
- '<code>=</code>' is encountered and the remainder of the cookie value will
- be dropped.</p>
- <p>If not specified, the default specification compliant value of
- <code>false</code> will be used.</p>
- </property>
-
- <property
- name="org.apache.tomcat.util.http. ServerCookie.ALLOW_HTTP_SEPARATORS_IN_V0">
- <p>Deprecated. This will be removed in Tomcat 9. Specify the
- <code>allowHttpSepsInV0</code> attribute on the
- <a href="cookie-processor.html">org.apache.tomcat.util.http.LegacyCookieProcessor</a>
- instead.</p>
- <p>This sets the default value for <code>allowHttpSepsInV0</code>
- attribute on the
- <code>org.apache.tomcat.util.http.LegacyCookieProcessor</code>.
- If this is <code>true</code> Tomcat will allow HTTP separators in
- cookie names and values.</p>
- <p>If not specified, the default specification compliant value of
- <code>false</code> will be used.</p>
- </property>
-
- <property name="org.apache.tomcat.util.http. ServerCookie.ALLOW_NAME_ONLY">
- <p>Deprecated. This will be removed in Tomcat 9. Specify the
- <code>allowNameOnly</code> attribute on the
- <a href="cookie-processor.html">org.apache.tomcat.util.http.LegacyCookieProcessor</a>
- instead.</p>
- <p>If this is <code>false</code> then the requirements of the cookie specifications
- that cookies must have values will be enforced and cookies consisting only
- of a name but no value will be ignored.</p>
- <p>If not specified, the default specification compliant value of
- <code>false</code> will be used.</p>
- </property>
-
- <property
- name="org.apache.tomcat.util.http. ServerCookie.ALWAYS_ADD_EXPIRES">
- <p>If this is <code>true</code> Tomcat will always add an expires
- parameter to a SetCookie header even for cookies with version greater than
- zero. This is to work around a known IE6 and IE7 bug that causes IE to
- ignore the Max-Age parameter in a SetCookie header.</p>
- <p>If <code>org.apache.catalina.STRICT_SERVLET_COMPLIANCE</code> is set to
- <code>true</code>, the default of this setting will be <code>false</code>,
- else the default value will be <code>true</code>.</p>
- </property>
-
- <property
- name="org.apache.tomcat.util.http. ServerCookie.FWD_SLASH_IS_SEPARATOR">
- <p> If this is true then the <code>/</code> (forward slash) character will
- be treated as a separator. Note that this character is frequently used in
- cookie path attributes and some browsers will fail to process a cookie if
- the path attribute is quoted as is required by a strict adherence to the
- specifications. This is highly likely to break session tracking using
- cookies.</p>
- <p>If <code>org.apache.catalina.STRICT_SERVLET_COMPLIANCE</code> is set to
- <code>true</code>, the default of this setting will be <code>true</code>,
- else the default value will be <code>false</code>.</p>
- </property>
-
- <property name="org.apache.tomcat.util.http. ServerCookie.PRESERVE_COOKIE_HEADER">
- <p>Deprecated. This attribute is no longer used. From Tomcat 8.0.31,
- Tomcat will always preserve the cookie header returned by
- <code>HttpServletRequest.getHeader()</code>. This will be removed in
- Tomcat 9.</p>
</property>
<property name="org.apache.tomcat.util.http. ServerCookie.STRICT_NAMING">
<p> If this is <code>true</code> then the requirements of the Servlet specification
- that Cookie names must adhere to RFC2109 (no use of separators) will be
- enforced.</p>
+ that Cookie names must adhere to RFC2109 will be enforced. If this is
+ <code>false</code> the naming rules specified in RFC6265 (allow the leading "$")
+ will be used.</p>
<p>If <code>org.apache.catalina.STRICT_SERVLET_COMPLIANCE</code> is set to
<code>true</code>, the default of this setting will be <code>true</code>,
else the default value will be <code>false</code>.</p>
@@ -505,29 +411,33 @@
<code>-Dorg.apache.juli.formatter=org.apache.juli.OneLineFormatter</code></p>
</property>
- <property name="org.apache.juli. AsyncOverflowDropType">
- <p>When the memory limit of records has been reached the system needs to determine what action to take.
- Currently there are three actions that can be taken:
- </p>
- <ul>
- <li><code>int OVERFLOW_DROP_LAST = 1</code> - the record that caused the overflow will be dropped and not logged</li>
- <li><code>int OVERFLOW_DROP_FIRST = 2</code> - the record that is next in line to be logged will be dropped to make room for the latest record on the queue</li>
- <li><code>int OVERFLOW_DROP_FLUSH = 3</code> - suspend the thread while the queue empties out and flushes the entries to the write buffer</li>
- <li><code>int OVERFLOW_DROP_CURRENT = 4</code> - drop the current log entry</li>
- </ul>
- <p>The default value is <code>1</code> (OVERFLOW_DROP_LAST).</p>
- </property>
-
<property name="org.apache.juli. AsyncMaxRecordCount">
- <p>The max number of log records that the async logger will keep in memory. When this limit is reached and a new record is being logged by the
- JULI framework the system will take an action based on the <code>org.apache.juli.AsyncOverflowDropType</code> setting.</p>
+ <p>The maximum number of log records that the JULI AsyncFileHandler will queue in memory.
+ New records are added to the queue and get asynchronously removed from the queue
+ and written to the files by a single writer thread.
+ When the queue is full and a new record is being logged
+ the log record will be handled based on the <code>org.apache.juli.AsyncOverflowDropType</code> setting.</p>
<p>The default value is <code>10000</code> records.
This number represents the global number of records, not on a per handler basis.
</p>
</property>
+ <property name="org.apache.juli. AsyncOverflowDropType">
+ <p>When the queue of log records of the JULI AsyncFileHandler is full,
+ new log records are handled according to the following setting:
+ </p>
+ <ul>
+ <li><code>1</code> - the newest record in the queue will be dropped and not logged</li>
+ <li><code>2</code> - the oldest record in the queue will be dropped and not logged</li>
+ <li><code>3</code> - suspend the logging thread until older records got written to the log file and the queue is no longer full.
+ This is the only setting that ensures that no messages get lost.</li>
+ <li><code>4</code> - drop the current log record</li>
+ </ul>
+ <p>The default value is <code>1</code> (drop the newest record in the queue).</p>
+ </property>
+
<property name="org.apache.juli. AsyncLoggerPollInterval">
- <p>The poll interval in milliseconds for the asynchronous logger thread in milliseconds.
+ <p>The poll interval in milliseconds for the asynchronous logger thread.
If the log queue is empty, the async thread will issue a poll(poll interval)
in order to not wake up too often.</p>
<p>The default value is <code>1000</code> milliseconds.</p>
@@ -579,7 +489,7 @@
value for <code>pluggabilitySkip</code> and <code>tldSkip</code>
attributes of the standard
<a href="jar-scan-filter.html">JarScanFilter</a> implementation.</p>
- <p>The coded default empty, however the system property is set in
+ <p>The coded default is empty, however the system property is set in
a default Tomcat installation via the
<code>$CATALINA_BASE/conf/catalina.properties</code> file.</p>
</property>
@@ -589,7 +499,7 @@
value for <code>pluggabilityScan</code> and <code>tldScan</code>
attributes of the standard
<a href="jar-scan-filter.html">JarScanFilter</a> implementation.</p>
- <p>The coded default empty, however the system property is set in
+ <p>The coded default is empty, however the system property is set in
a default Tomcat installation via the
<code>$CATALINA_BASE/conf/catalina.properties</code> file.</p>
</property>
@@ -602,7 +512,7 @@
<properties>
- <property name="org.apache.tomcat .websocket.ALLOW_UNSUPPORTED_EXTENSIONS">
+ <property name="org.apache.tomcat. websocket.ALLOW_UNSUPPORTED_EXTENSIONS">
<p>If <code>true</code>, allow unknown extensions to be declared by
the user.</p>
<p>The default value is <code>false</code>.</p>
@@ -643,15 +553,6 @@
<properties>
- <property
- name="org.apache.coyote. USE_CUSTOM_STATUS_MSG_IN_HEADER"><p>If this is
- <code>true</code>, custom HTTP status messages will be used within HTTP
- headers. If a custom message is specified that is not valid for use in an
- HTTP header (as defined by RFC2616) then the custom message will be
- ignored and the default message used.</p>
- <p>If not specified, the default value of <code>false</code> will be used.</p>
- </property>
-
<property name="catalina.useNaming">
<p>If this is <code>false</code> it will override the
<code>useNaming</code> attribute for all <a href="context.html">
diff --git a/webapps/docs/config/valve.xml b/webapps/docs/config/valve.xml
index 84b308d..3163593 100644
--- a/webapps/docs/config/valve.xml
+++ b/webapps/docs/config/valve.xml
@@ -321,7 +321,7 @@
<li><b><code>msec</code></b> - number of milliseconds since the epoch</li>
<li><b><code>msec_frac</code></b> - millisecond fraction</li>
</ul>
- <p>These formats can not be mixed with SimpleDateFormat formats in the same format
+ <p>These formats cannot be mixed with SimpleDateFormat formats in the same format
token.</p>
<p>Furthermore one can define whether to log the timestamp for the request start
@@ -469,15 +469,15 @@
<section name="Access Control">
-<subsection name="Remote Address Filter">
+<subsection name="Remote Address Valve">
<subsection name="Introduction">
- <p>The <strong>Remote Address Filter</strong> allows you to compare the
+ <p>The <strong>Remote Address Valve</strong> allows you to compare the
IP address of the client that submitted this request against one or more
<em>regular expressions</em>, and either allow the request to continue
or refuse to process the request from this client. A Remote Address
- Filter can be associated with any Catalina container
+ Valve can be associated with any Catalina container
(<a href="engine.html">Engine</a>, <a href="host.html">Host</a>, or
<a href="context.html">Context</a>), and must accept any request
presented to this container for processing before it will be passed on.</p>
@@ -503,13 +503,13 @@
will be <code>0:0:0:0:0:0:0:1</code> instead of the more widely used
<code>::1</code>. Consult your access logs for the actual value.</p>
- <p>See also: <a href="#Remote_Host_Filter">Remote Host Filter</a>,
+ <p>See also: <a href="#Remote_Host_Valve">Remote Host Valve</a>,
<a href="#Remote_IP_Valve">Remote IP Valve</a>.</p>
</subsection>
<subsection name="Attributes">
- <p>The <strong>Remote Address Filter</strong> supports the following
+ <p>The <strong>Remote Address Valve</strong> supports the following
configuration attributes:</p>
<attributes>
@@ -598,15 +598,15 @@
</subsection>
-<subsection name="Remote Host Filter">
+<subsection name="Remote Host Valve">
<subsection name="Introduction">
- <p>The <strong>Remote Host Filter</strong> allows you to compare the
+ <p>The <strong>Remote Host Valve</strong> allows you to compare the
hostname of the client that submitted this request against one or more
<em>regular expressions</em>, and either allow the request to continue
or refuse to process the request from this client. A Remote Host
- Filter can be associated with any Catalina container
+ Valve can be associated with any Catalina container
(<a href="engine.html">Engine</a>, <a href="host.html">Host</a>, or
<a href="context.html">Context</a>), and must accept any request
presented to this container for processing before it will be passed on.</p>
@@ -629,13 +629,13 @@
to return proper host names, you have to enable "DNS lookups" feature on
a <strong>Connector</strong>.</p>
- <p>See also: <a href="#Remote_Address_Filter">Remote Address Filter</a>,
+ <p>See also: <a href="#Remote_Address_Valve">Remote Address Valve</a>,
<a href="http.html">HTTP Connector</a> configuration.</p>
</subsection>
<subsection name="Attributes">
- <p>The <strong>Remote Host Filter</strong> supports the following
+ <p>The <strong>Remote Host Valve</strong> supports the following
configuration attributes:</p>
<attributes>
diff --git a/webapps/docs/default-servlet.xml b/webapps/docs/default-servlet.xml
index c90ab9f..cc9a1ae 100644
--- a/webapps/docs/default-servlet.xml
+++ b/webapps/docs/default-servlet.xml
@@ -94,15 +94,25 @@ directory listings are disabled and debugging is turned off.
expensive. Multiple requests for large directory listings can consume
significant proportions of server resources.
</property>
- <property name="gzip">
- If a gzipped version of a file exists (a file with <code>.gz</code>
- appended to the file name located alongside the original file), Tomcat
- will serve the gzipped file if the user agent supports gzip and this
+ <property name="precompressed">
+ If a precompressed version of a file exists (a file with <code>.br</code>
+ or <code>.gz</code> appended to the file name located alongside the
+ original file), Tomcat will serve the precompressed file if the user
+ agent supports the matching content encoding (br or gzip) and this
option is enabled. [false]
<br />
- The file with the <code>.gz</code> extension will be accessible if
- requested directly so if the original resource is protected with a
- security constraint, the gzipped version must be similarly protected.
+ The precompressed file with the with <code>.br</code> or <code>.gz</code>
+ extension will be accessible if requested directly so if the original
+ resource is protected with a security constraint, the precompressed
+ versions must be similarly protected.
+ <br />
+ It is also possible to configure the list of precompressed formats.
+ The syntax is comma separated list of
+ <code>[content-encoding]=[file-extension]</code> pairs. For example:
+ <code>br=.br,gzip=.gz,bzip2=.bz2</code>. If multiple formats are
+ specified, the client supports more than one and the client does not
+ express a preference, the order of the list of formats will be treated
+ as the server preference order and used to select the format returned.
</property>
<property name="readmeFile">
If a directory listing is presented, a readme file may also
diff --git a/webapps/docs/funcspecs/fs-admin-objects.xml b/webapps/docs/funcspecs/fs-admin-objects.xml
index a8094ef..99439a0 100644
--- a/webapps/docs/funcspecs/fs-admin-objects.xml
+++ b/webapps/docs/funcspecs/fs-admin-objects.xml
@@ -433,9 +433,6 @@ Operations</a> that can be performed when the administrative application is
establishing a JDBC connection.</li>
<li><code>connectionURL</code> - Connection URL to use when establishing
a JDBC connection.</li>
- <li><code>digest</code> - Name of the <code>MessageDigest</code> algorithm
- used to encode passwords in the database, or a zero-length string for
- no encoding. [Zero-length String]</li>
<li><code>driverName</code> - Fully qualified Java class name of the JDBC
driver to be utilized.</li>
<li><code>roleNameCol</code> - Name of the column, in the User Roles table,
diff --git a/webapps/docs/funcspecs/mbean-names.xml b/webapps/docs/funcspecs/mbean-names.xml
index 84f51ba..d042572 100644
--- a/webapps/docs/funcspecs/mbean-names.xml
+++ b/webapps/docs/funcspecs/mbean-names.xml
@@ -172,15 +172,6 @@ corresponding values:</p>
</tr>
<tr>
- <td>Server / Service / Engine / Host / Context / InstanceListener</td>
- <td style="text-align: center;">0..n</td>
- <td style="text-align: center;">(none)</td>
- <td><code class="noHighlight">type=${INSTANCE-LISTENER}, sequence=${###},
- context=${context.name}, host=${host.name},
- service=${service.name}</code></td>
- </tr>
-
- <tr>
<td>Server / Service / Engine / Host / Context / Listener</td>
<td style="text-align: center;">0..n</td>
<td style="text-align: center;">(none)</td>
diff --git a/webapps/docs/jasper-howto.xml b/webapps/docs/jasper-howto.xml
index 0752a23..b4e0025 100644
--- a/webapps/docs/jasper-howto.xml
+++ b/webapps/docs/jasper-howto.xml
@@ -201,9 +201,7 @@ default <code>false</code>.</li>
<li><strong>strictQuoteEscaping</strong> - When scriptlet expressions are used
for attribute values, should the rules in JSP.1.6 for the escaping of quote
characters be strictly applied? <code>true</code> or <code>false</code>, default
-<code>true</code> which can be changed with the
-<code>org.apache.jasper.compiler.Parser.STRICT_QUOTE_ESCAPING</code> system
-property.</li>
+<code>true</code>.</li>
<li><strong>quoteAttributeEL</strong> - When EL is used in an attribute value
on a JSP page, should the rules for quoting of attributes described in JSP.1.6
diff --git a/webapps/docs/jndi-resources-howto.xml b/webapps/docs/jndi-resources-howto.xml
index 1a045f7..00d679d 100644
--- a/webapps/docs/jndi-resources-howto.xml
+++ b/webapps/docs/jndi-resources-howto.xml
@@ -328,7 +328,7 @@ writer.println("foo = " + bean.getFoo() + ", bar = " +
<code>foo</code> property (although we could have), the bean will
contain whatever default value is set up by its constructor.</p>
- <p>Some beans have properties with types that can not automatically be
+ <p>Some beans have properties with types that cannot automatically be
converted from a string value. Setting such properties using the Tomcat
BeanFactory will fail with a NamingException. In cases were those beans
provide methods to set the properties from a string value, the Tomcat
@@ -448,7 +448,7 @@ public class MyBean2 {
files are placed in <code>$CATALINA_BASE/conf</code>. A typical XML would
look like:</p>
-<source><![CDATA[<?xml version='1.0' encoding='utf-8'?>
+<source><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
<tomcat-users>
<role rolename="tomcat"/>
<role rolename="role1"/>
diff --git a/webapps/docs/logging.xml b/webapps/docs/logging.xml
index 0fc491f..88ddbd0 100644
--- a/webapps/docs/logging.xml
+++ b/webapps/docs/logging.xml
@@ -38,20 +38,19 @@
<p>
The internal logging for Apache Tomcat uses JULI, a packaged renamed fork
of <a href="http://commons.apache.org/logging">Apache Commons Logging</a>
- that, by default, is hard-coded to use the <code>java.util.logging</code>
- framework. This ensures that Tomcat's internal logging and any web
- application logging will remain independent, even if a web application
- uses Apache Commons Logging.
+ that is hard-coded to use the <code>java.util.logging</code> framework.
+ This ensures that Tomcat's internal logging and any web application
+ logging will remain independent, even if a web application uses Apache
+ Commons Logging.
</p>
<p>
To configure Tomcat to use an alternative logging framework for its
- internal logging, one has to replace the JULI implementation that is
- hard-coded to use <code>java.util.logging</code> with a JULI
- implementation that retains the full Commons Logging discovery mechanism.
- Such an implementation is provided as an <a href="extras.html">extras</a>
- component. Instructions on how to configure Tomcat to use Log4j framework
- for its internal logging may be found <a href="#Using_Log4j">below</a>.
+ internal logging, follow the instructions provided by the alternative
+ logging framework for redirecting logging for applications that use
+ <code>java.util.logging</code>. 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.
</p>
<p>
@@ -407,205 +406,5 @@ java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter]]
</section>
- <section name="Using Log4j">
- <p>
- This section explains how to configure Tomcat to use
- <a href="http://logging.apache.org/log4j/">log4j</a> rather than
- java.util.logging for all Tomcat's internal logging.
- </p>
- <p><em>Note</em>: The steps described in this section are needed
- when you want to reconfigure Tomcat to use Apache log4j for its own
- logging. These steps are <strong>not</strong> needed if you just want
- to use log4j in your own web application. — In that case, just
- put <code>log4j.jar</code> and <code>log4j.properties</code> into
- <code>WEB-INF/lib</code> and <code>WEB-INF/classes</code>
- of your web application.
- </p>
- <p>
- The following steps describe configuring log4j to output Tomcat's
- internal logging.
- </p>
-
- <ol>
- <li>Create a file called <code>log4j.properties</code> with the
- following content and save it into <code>$CATALINA_BASE/lib</code></li>
- </ol>
- <source><![CDATA[
-log4j.rootLogger = INFO, CATALINA
-
-# Define all the appenders
-log4j.appender.CATALINA = org.apache.log4j.DailyRollingFileAppender
-log4j.appender.CATALINA.File = ${catalina.base}/logs/catalina
-log4j.appender.CATALINA.Append = true
-log4j.appender.CATALINA.Encoding = UTF-8
-# Roll-over the log once per day
-log4j.appender.CATALINA.DatePattern = '.'yyyy-MM-dd'.log'
-log4j.appender.CATALINA.layout = org.apache.log4j.PatternLayout
-log4j.appender.CATALINA.layout.ConversionPattern = %d [%t] %-5p %c- %m%n
-
-log4j.appender.LOCALHOST = org.apache.log4j.DailyRollingFileAppender
-log4j.appender.LOCALHOST.File = ${catalina.base}/logs/localhost
-log4j.appender.LOCALHOST.Append = true
-log4j.appender.LOCALHOST.Encoding = UTF-8
-log4j.appender.LOCALHOST.DatePattern = '.'yyyy-MM-dd'.log'
-log4j.appender.LOCALHOST.layout = org.apache.log4j.PatternLayout
-log4j.appender.LOCALHOST.layout.ConversionPattern = %d [%t] %-5p %c- %m%n
-
-log4j.appender.MANAGER = org.apache.log4j.DailyRollingFileAppender
-log4j.appender.MANAGER.File = ${catalina.base}/logs/manager
-log4j.appender.MANAGER.Append = true
-log4j.appender.MANAGER.Encoding = UTF-8
-log4j.appender.MANAGER.DatePattern = '.'yyyy-MM-dd'.log'
-log4j.appender.MANAGER.layout = org.apache.log4j.PatternLayout
-log4j.appender.MANAGER.layout.ConversionPattern = %d [%t] %-5p %c- %m%n
-
-log4j.appender.HOST-MANAGER = org.apache.log4j.DailyRollingFileAppender
-log4j.appender.HOST-MANAGER.File = ${catalina.base}/logs/host-manager
-log4j.appender.HOST-MANAGER.Append = true
-log4j.appender.HOST-MANAGER.Encoding = UTF-8
-log4j.appender.HOST-MANAGER.DatePattern = '.'yyyy-MM-dd'.log'
-log4j.appender.HOST-MANAGER.layout = org.apache.log4j.PatternLayout
-log4j.appender.HOST-MANAGER.layout.ConversionPattern = %d [%t] %-5p %c- %m%n
-
-log4j.appender.CONSOLE = org.apache.log4j.ConsoleAppender
-log4j.appender.CONSOLE.Encoding = UTF-8
-log4j.appender.CONSOLE.layout = org.apache.log4j.PatternLayout
-log4j.appender.CONSOLE.layout.ConversionPattern = %d [%t] %-5p %c- %m%n
-
-# Configure which loggers log to which appenders
-log4j.logger.org.apache.catalina.core.ContainerBase.[Catalina].[localhost] = INFO, LOCALHOST
-log4j.logger.org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/manager] =\
- INFO, MANAGER
-log4j.logger.org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/host-manager] =\
- INFO, HOST-MANAGER]]></source>
- <ol start="2">
- <li><a href="http://logging.apache.org/log4j">Download Log4J</a>
- (Tomcat requires v1.2.x).</li>
-
- <li><p>Download or build <code>tomcat-juli.jar</code> and
- <code>tomcat-juli-adapters.jar</code> that are available as an "extras"
- component for Tomcat. See <a href="extras.html">Additional Components
- documentation</a> for details.</p>
- <p>This <code>tomcat-juli.jar</code> differs from the default one. It
- contains the full Apache Commons Logging implementation and thus is
- able to discover the presence of log4j and configure itself.</p>
- </li>
-
- <li><p>If you want to configure Tomcat to use log4j globally:</p>
- <ul>
- <li>Put <code>log4j.jar</code> and
- <code>tomcat-juli-adapters.jar</code> from "extras" into
- <code>$CATALINA_HOME/lib</code>.</li>
- <li>Replace <code>$CATALINA_HOME/bin/tomcat-juli.jar</code> with
- <code>tomcat-juli.jar</code> from "extras".</li>
- </ul>
- </li>
-
- <li><p>If you are running Tomcat with separate
- <code>$CATALINA_HOME</code> and <code>$CATALINA_BASE</code> and want to
- configure to use log4j in a single <code>$CATALINA_BASE</code> only:</p>
-
- <ul>
- <li>Create <code>$CATALINA_BASE/bin</code> and
- <code>$CATALINA_BASE/lib</code> directories if they do not exist.
- </li>
- <li>Put <code>log4j.jar</code> and
- <code>tomcat-juli-adapters.jar</code> from "extras" into
- <code>$CATALINA_BASE/lib</code></li>
- <li>Put <code>tomcat-juli.jar</code> from "extras" as
- <code>$CATALINA_BASE/bin/tomcat-juli.jar</code></li>
- <li>If you are running with a
- <a href="security-manager-howto.html">security manager</a>, you
- would need to edit the
- <code>$CATALINA_BASE/conf/catalina.policy</code> file to adjust
- it to using a different copy of tomcat-juli.jar.</li>
- </ul>
-
- <p>Note: This works because libraries, if they exist in
- <code>$CATALINA_BASE</code>, are loaded in preference to the same
- library in <code>$CATALINA_HOME</code>.</p>
-
- <p>Note: tomcat-juli.jar is loaded from <code>$CATALINA_BASE</code>/bin
- not <code>$CATALINA_BASE</code>/lib as it is loaded as part of the
- bootstrap process and all the bootstrap classes are loaded from bin.</p>
- </li>
-
- <li><p>Delete <code>$CATALINA_BASE/conf/logging.properties</code> to
- prevent java.util.logging generating zero length log files.</p></li>
-
- <li><p>Start Tomcat</p></li>
- </ol>
-
- <p>
- This log4j configuration mirrors the default java.util.logging setup
- that ships with Tomcat: both the manager and host-manager apps get an
- individual log file, and everything else goes to the "catalina.log" log
- file. Each file is rolled-over once per day.
- </p>
-
- <p>
- You can (and should) be more picky about which packages to include
- in the logging. Tomcat defines loggers by Engine and Host names.
- For example, for a more detailed Catalina localhost log, add this to the
- end of the log4j.properties above. Note that there are known issues with
- using this naming convention (with square brackets) in log4j XML based
- configuration files, so we recommend you use a properties file as
- described until a future version of log4j allows this convention.
- </p>
- <source><![CDATA[log4j.logger.org.apache.catalina.core.ContainerBase.[Catalina].[localhost]=DEBUG
-log4j.logger.org.apache.catalina.core=DEBUG
-log4j.logger.org.apache.catalina.session=DEBUG]]></source>
-
- <p>
- Be warned: a level of DEBUG will produce megabytes of logging and slow
- startup of Tomcat. This level should be used sparingly when debugging of
- internal Tomcat operations is required.
- </p>
-
- <p>
- Your web applications should certainly use their own log4j configuration.
- This is valid <i>with</i> the above configuration. You would place a
- similar log4j.properties file in your web application's WEB-INF/classes
- directory, and log4jx.y.z.jar into WEB-INF/lib. Then specify your package
- level logging. This is a basic setup of log4j which does *not* require
- Commons-Logging, and you should consult the
- <a href="http://logging.apache.org/log4j/docs/documentation.html">log4j
- documentation</a> for more options. This page is intended only as a
- bootstrapping guide.
- </p>
-
- <p><em>Additional notes</em></p>
- <ul>
- <li><p>This exposes log4j libraries to the web applications through the
- Common classloader. See <a href="class-loader-howto.html">class loading</a>
- documentation for details.</p>
- <p>Because of that, the web applications and libraries using
- <a href="http://commons.apache.org/logging">Apache Commons Logging</a>
- library are likely to automatically choose log4j as the underlying
- logging implementation.</p></li>
-
- <li><p>The <code>java.util.logging</code> API is still available for
- those web applications that use it directly. The
- <code>${catalina.base}/conf/logging.properties</code> file is still
- referenced by Tomcat startup scripts. For more information, see the
- subsections of the <a href="#Introduction">Introduction</a> to
- this page.</p>
- <p> Removal of <code>${catalina.base}/conf/logging.properties</code>
- file, mentioned as one of the steps above, causes
- <code>java.util.logging</code> to fallback to the default
- configuration for the JRE, which is to use a ConsoleHandler
- and therefore not create any standard log files. You should
- confirm that all your log files are being created by log4j
- <i>before</i> disabling the standard mechanism.</p></li>
-
- <li><p>The <strong>Access Log Valve</strong> and
- <strong>ExtendedAccessLogValve</strong> use their own self-contained
- logging implementation, so they
- <strong><i>cannot be configured to use log4j</i></strong>.
- Refer to <a href="config/valve.html#Access_Logging">Valves</a>
- for specific configuration details.</p></li>
- </ul>
- </section>
-
</body>
</document>
diff --git a/webapps/docs/manager-howto.xml b/webapps/docs/manager-howto.xml
index 6139c73..7aad644 100644
--- a/webapps/docs/manager-howto.xml
+++ b/webapps/docs/manager-howto.xml
@@ -856,7 +856,7 @@ has been reloaded several times, it may be listed several times.</p>
<source>http://localhost:8080/manager/text/sslConnectorCiphers</source>
<p>The SSL Connector/Ciphers diagnostic lists the SSL/TLS ciphers that are currently
-configured for each connector. For BIO and NIO, the names of the individual
+configured for each connector. For NIO and NIO2, the names of the individual
cipher suites are listed. For APR, the value of SSLCipherSuite is returned.</p>
<p>The response will look something like this:</p>
diff --git a/webapps/docs/realm-howto.xml b/webapps/docs/realm-howto.xml
index 63e8ad1..d297702 100644
--- a/webapps/docs/realm-howto.xml
+++ b/webapps/docs/realm-howto.xml
@@ -168,13 +168,15 @@ authentication.</p>
<p>When a standard realm authenticates by retrieving the stored
password and comparing it with the value presented by the user, you
-can select digested passwords by specifying the <code>digest</code>
-attribute on your <code><Realm></code> element. The value for
-this attribute must be one of the digest algorithms supported by the
-<code>java.security.MessageDigest</code> class (SHA, MD2, or MD5).
-When you select this option, the contents of the password that is
-stored in the <code>Realm</code> must be the cleartext version of the
-password, as digested by the specified algorithm.</p>
+can select digested passwords by placing a <a href="config/credentialhandler.html">
+<code>CredentialHandler</code></a> element inside your <code><Realm></code>
+element. An easy choice to support one of the algorithms SSHA, SHA or MD5
+would be the usage of the <code>MessageDigestCredentialHandler</code>.
+This element must be configured to one of the digest algorithms supported
+by the <code>java.security.MessageDigest</code> class (SSHA, SHA or MD5).
+When you select this option, the contents of the password that is stored
+in the <code>Realm</code> must be the cleartext version of the password,
+as digested by the specified algorithm.</p>
<p>When the <code>authenticate()</code> method of the Realm is called, the
(cleartext) password specified by the user is itself digested by the same
@@ -538,7 +540,7 @@ to, and optionally the port number and distinguished name (DN) of the
required root naming context.</p>
<p>If you have more than one provider you can configure an
-<strong>alternateURL</strong>. If a socket connection can not be
+<strong>alternateURL</strong>. If a socket connection cannot be
made to the provider at the <strong>connectionURL</strong> an
attempt will be made to use the <strong>alternateURL</strong>.</p>
diff --git a/webapps/docs/security-howto.xml b/webapps/docs/security-howto.xml
index 6acab06..62f4566 100644
--- a/webapps/docs/security-howto.xml
+++ b/webapps/docs/security-howto.xml
@@ -55,17 +55,22 @@
the Tomcat process and provide that user with the minimum necessary
permissions for the operating system. For example, it should not be possible
to log on remotely using the Tomcat user.</p>
- <p>File permissions should also be suitably restricted. Taking the Tomcat
- instances at the ASF as an example (where auto-deployment is disabled and
- web applications are deployed as exploded directories), the standard
- configuration is to have all Tomcat files owned by root with group Tomcat
- and whilst owner has read/write privileges, group only has read and world
- has no permissions. The exceptions are the logs, temp and work directory
- that are owned by the Tomcat user rather than root. This means that even if
- an attacker compromises the Tomcat process, they can't change the
- Tomcat configuration, deploy new web applications or modify existing web
- applications. The Tomcat process runs with a umask of 007 to maintain these
- permissions.</p>
+ <p>File permissions should also be suitably restricted. In the
+ <code>.tar.gz</code> distribution, files and directories are not world
+ readable and the group does not have write access. On Unix like operating
+ systems, Tomcat runs with a default umask of <code>0027</code> to maintain
+ these permissions for files created while Tomcat is running (e.g. log files,
+ expanded WARs, etc.).</p>
+ <p>Taking the Tomcat instances at the ASF as an example (where
+ auto-deployment is disabled and web applications are deployed as exploded
+ directories), the standard configuration is to have all Tomcat files owned
+ by root with group Tomcat and whilst owner has read/write privileges, group
+ only has read and world has no permissions. The exceptions are the logs,
+ temp and work directory that are owned by the Tomcat user rather than root.
+ This means that even if an attacker compromises the Tomcat process, they
+ can't change the Tomcat configuration, deploy new web applications or
+ modify existing web applications. The Tomcat process runs with a umask of
+ 007 to maintain these permissions.</p>
<p>At the network level, consider using a firewall to limit both incoming
and outgoing connections to only those connections you expect to be
present.</p>
@@ -134,10 +139,11 @@
<li>Do not remove the use of the <a
href="config/realm.html#LockOut_Realm_-_org.apache.catalina.realm.LockOutRealm">LockOutRealm</a>
which prevents brute force attacks against user passwords.</li>
- <li>Uncomment the <a href="config/valve.html#Remote_Address_Filter">RemoteAddrValve</a>
- in <code>/META-INF/context.xml</code> which limits access to
- localhost. If remote access is required, limit it to specific IP
- addresses using this valve.</li>
+ <li>Configure the <a href="config/valve.html#Remote_Address_Valve">RemoteAddrValve</a>
+ in the <a href="config/context.html">context.xml</a> file for the
+ management application which limits access to localhost by default.
+ If remote access is required, limit it to specific IP addresses using
+ this valve.</li>
</ul>
</subsection>
</section>
@@ -294,15 +300,6 @@
proxy (the authenticated user name is passed to Tomcat as part of the AJP
protocol) with the option for Tomcat to still perform authorization.</p>
- <p>The <strong>allowUnsafeLegacyRenegotiation</strong> attribute provides
- a workaround for
- <a href="http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2009-3555">
- CVE-2009-3555</a>, a TLS man in the middle attack. This workaround applies
- to the BIO connector. It is only necessary if the underlying SSL
- implementation is vulnerable to CVE-2009-3555. For more information on the
- current state of this vulnerability and the work-arounds available see the
- <security>Tomcat <version-major/> security page</security>.</p>
-
<p>The <strong>requiredSecret</strong> 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.</p>
diff --git a/webapps/docs/ssl-howto.xml b/webapps/docs/ssl-howto.xml
index a0f4b8a..a0443d3 100644
--- a/webapps/docs/ssl-howto.xml
+++ b/webapps/docs/ssl-howto.xml
@@ -236,11 +236,12 @@ Certificate that can be used by your server.</p>
<subsection name="Edit the Tomcat Configuration File">
<p>
-Tomcat can use two different implementations of SSL:
+Tomcat can use three different implementations of SSL:
</p>
<ul>
-<li>the JSSE implementation provided as part of the Java runtime (since 1.4)</li>
-<li>the APR implementation, which uses the OpenSSL engine by default.</li>
+<li>JSSE implementation provided as part of the Java runtime</li>
+<li>JSSE implementation that uses OpenSSL</li>
+<li>APR implementation, which uses the OpenSSL engine by default</li>
</ul>
<p>
The exact configuration details depend on which implementation is being used.
@@ -248,44 +249,56 @@ If you configured Connector by specifying generic
<code>protocol="HTTP/1.1"</code> then the implementation used by Tomcat is
chosen automatically. If the installation uses <a href="apr.html">APR</a>
- i.e. you have installed the Tomcat native library -
-then it will use the APR SSL implementation, otherwise it will use the Java
+then it will use the JSSE OpenSSL implementation, otherwise it will use the Java
JSSE implementation.
</p>
<p>
-As configuration attributes for SSL support significantly differ between
-APR vs. JSSE implementations, it is <strong>recommended</strong> to
-avoid auto-selection of implementation. It is done by specifying a classname
+Auto-selection of implementation can be avoided if needed. It is done by specifying a classname
in the <b>protocol</b> attribute of the <a href="config/http.html">Connector</a>.</p>
<p>To define a Java (JSSE) connector, regardless of whether the APR library is
loaded or not, use one of the following:</p>
<source><![CDATA[<!-- Define a HTTP/1.1 Connector on port 8443, JSSE NIO implementation -->
<Connector protocol="org.apache.coyote.http11.Http11NioProtocol"
+ sslImplementationName="org.apache.tomcat.util.net.jsse.JSSEImplementation"
port="8443" .../>
<!-- Define a HTTP/1.1 Connector on port 8443, JSSE NIO2 implementation -->
<Connector protocol="org.apache.coyote.http11.Http11Nio2Protocol"
- port="8443" .../>
-
-<!-- Define a HTTP/1.1 Connector on port 8443, JSSE BIO implementation -->
-<Connector protocol="org.apache.coyote.http11.Http11Protocol"
+ sslImplementationName="org.apache.tomcat.util.net.jsse.JSSEImplementation"
port="8443" .../>]]></source>
+
+<p>The OpenSSL JSSE implementation can also be configured explicitly if needed. If the APR library
+is installed (as for using the APR connector), using the sslImplementationName attribute
+allows enabling it. When using the OpenSSL JSSE implementation, the configuration can use
+either the JSSE attributes or
+the OpenSSL attributes (as used for the APR connector), but must not mix attributes from
+both types in the same SSLHostConfig or Connector element.</p>
+<source><![CDATA[<!-- Define a HTTP/1.1 Connector on port 8443, JSSE NIO implementation and OpenSSL -->
+<Connector protocol="org.apache.coyote.http11.Http11NioProtocol" port="8443"
+ sslImplementationName="org.apache.tomcat.util.net.openssl.OpenSSLImplementation"
+ .../>]]></source>
+
<p>Alternatively, to specify an APR connector (the APR library must be available) use:</p>
<source><![CDATA[<!-- Define a HTTP/1.1 Connector on port 8443, APR implementation -->
<Connector protocol="org.apache.coyote.http11.Http11AprProtocol"
port="8443" .../>]]></source>
-
-<p>If you are using APR, you have the option of configuring an alternative engine to OpenSSL.</p>
+<p>If you are using APR or JSSE OpenSSL, you have the option of configuring an alternative engine to OpenSSL.</p>
<source><![CDATA[<Listener className="org.apache.catalina.core.AprLifecycleListener"
SSLEngine="someengine" SSLRandomSeed="somedevice" />]]></source>
<p>The default value is</p>
<source><![CDATA[<Listener className="org.apache.catalina.core.AprLifecycleListener"
SSLEngine="on" SSLRandomSeed="builtin" />]]></source>
+<p>Also the <code>useAprConnector</code> attribute may be used to have Tomcat default to
+using the APR connector rather than the NIO connector:</p>
+<source><![CDATA[<Listener className="org.apache.catalina.core.AprLifecycleListener"
+ useAprConnector="true" SSLEngine="on" SSLRandomSeed="builtin" />]]></source>
<p>
-So to use SSL under APR, make sure the SSLEngine attribute is set to something other than <code>off</code>.
-The default value is <code>on</code> and if you specify another value, it has to be a valid engine name.
+So to enable OpenSSL, make sure the SSLEngine attribute is set to something other than <code>off</code>.
+The default value is <code>on</code> and if you specify another value,
+it has to be a valid OpenSSL engine name.
</p>
<p>
@@ -299,7 +312,7 @@ sources like "/dev/urandom" that will allow quicker starts of Tomcat.
<code>$CATALINA_BASE</code> represents the base directory for the
Tomcat instance. An example <code><Connector></code> element
for an SSL connector is included in the default <code>server.xml</code>
-file installed with Tomcat. To configure an SSL connector that uses JSSE, you
+file installed with Tomcat. To configure an SSL connector that uses JSSE, you
will need to remove the comments and edit it so it looks something like
this:</p>
<source><![CDATA[<!-- Define a SSL Coyote HTTP/1.1 Connector on port 8443 -->
@@ -310,6 +323,10 @@ this:</p>
keystoreFile="${user.home}/.keystore" keystorePass="changeit"
clientAuth="false" sslProtocol="TLS"/>]]></source>
<p>
+ Note: If tomcat-native is installed, the configuration will use JSSE with
+ an OpenSSL implementation, which supports either this configuration or the APR
+ configuration example given below.</p>
+<p>
The APR connector uses different attributes for many SSL settings,
particularly keys and certificates. An example of an APR configuration is:</p>
<source><![CDATA[<!-- Define a SSL Coyote HTTP/1.1 Connector on port 8443 -->
@@ -326,8 +343,9 @@ this:</p>
are mandatory, are documented in the SSL Support section of the
<a href="config/http.html#SSL_Support">HTTP connector</a> configuration
reference. Make sure that you use the correct attributes for the connector you
-are using. The BIO, NIO and NIO2 connectors use JSSE whereas the APR/native connector
-uses APR.</p>
+are using. The NIO and NIO2 connectors use JSSE unless the JSSE OpenSSL implementation is
+installed (in which case it supports either the JSSE or OpenSSL configuration styles),
+whereas the APR/native connector uses APR.</p>
<p>The <code>port</code> attribute is the TCP/IP
port number on which Tomcat will listen for secure connections. You can
@@ -461,7 +479,7 @@ SSL communications, and what to do about them.</p>
"java.security.InvalidAlgorithmParameterException: Prime size must be multiple
of 64, and can only range from 512 to 1024 (inclusive)"
- <p>If you are using the APR/native connector, starting with version 1.1.34
+ <p>If you are using the APR/native connector or the JSSE OpenSSL implementation,
it will determine the strength of ephemeral DH keys from the key size of
your RSA certificate. For example a 2048 bit RSA key will result in
using a 2048 bit prime for the DH keys. Unfortunately Java 6 only supports
@@ -531,7 +549,7 @@ public class SessionTrackingModeListener implements ServletContextListener {
}]]></source>
- <p>Note: SSL session tracking is implemented for the BIO, NIO and NIO2 connectors.
+ <p>Note: SSL session tracking is implemented for the NIO and NIO2 connectors.
It is not yet implemented for the APR connector.</p>
</section>
@@ -561,8 +579,8 @@ mgr.invalidateSession();
response.setHeader("Connection", "close");]]></source>
<p>
Note that this code is Tomcat specific due to the use of the
- SSLSessionManager class. This is currently only available for the BIO, NIO
- and NIO2 connectors, not the APR/native connector.
+ SSLSessionManager class. This is currently only available for the NIO and
+ NIO2 connectors, not the APR/native connector.
</p>
</section>
diff --git a/webapps/docs/tomcat-docs.xsl b/webapps/docs/tomcat-docs.xsl
index e27d362..408795a 100644
--- a/webapps/docs/tomcat-docs.xsl
+++ b/webapps/docs/tomcat-docs.xsl
@@ -37,15 +37,15 @@
<xsl:param name="apache-logo" select="'/images/asf-feather.png'"/>
<xsl:param name="subdir" select="''"/>
<xsl:param name="relative-path" select="'.'"/>
- <xsl:param name="version" select="'8.0.x'"/>
+ <xsl:param name="version" select="'8.5.x'"/>
<xsl:param name="majorversion" select="'8'"/>
- <xsl:param name="majorminorversion" select="'8.0'"/>
+ <xsl:param name="majorminorversion" select="'8.5'"/>
<xsl:param name="build-date" select="'MMM d yyyy'"/>
<xsl:param name="build-date-iso-8601" select="'yyyy-dd-MM'"/>
<xsl:param name="year" select="'yyyy'"/>
<xsl:param name="buglink" select="'http://bz.apache.org/bugzilla/show_bug.cgi?id='"/>
<xsl:param name="revlink" select="'http://svn.apache.org/viewvc?view=rev&rev='"/>
- <xsl:param name="doclink" select="'http://tomcat.apache.org/tomcat-8.0-doc'"/>
+ <xsl:param name="doclink" select="'http://tomcat.apache.org/tomcat-8.5-doc'"/>
<xsl:param name="sylink" select="'http://tomcat.apache.org/security-8.html'"/>
<xsl:param name="dllink" select="'http://tomcat.apache.org/download-80.cgi'"/>
<xsl:param name="sitedir" select="''"/>
diff --git a/webapps/docs/web-socket-howto.xml b/webapps/docs/web-socket-howto.xml
index 29b6a2a..938d36c 100644
--- a/webapps/docs/web-socket-howto.xml
+++ b/webapps/docs/web-socket-howto.xml
@@ -49,24 +49,6 @@
code</a>.</p>
</section>
-<section name="Production usage">
-<p>Although the WebSocket implementation does work with any of the HTTP
-connectors, it is not recommended to the WebSocket with the BIO HTTP connector
-as the typical uses of WebSocket (large numbers of mostly idle connections) is
-not a good fit for the HTTP BIO connector which requires that one thread is
-allocated per connection regardless of whether or not the connection is idle.
-</p>
-
-<p>It has been reported (<bug>56304</bug>) that Linux can take large numbers of
-minutes to report dropped connections. When using WebSocket with the BIO HTTP
-connector this can result in threads blocking on writes for this period. This is
-likely to be undesirable. The time taken for the connection to be reported as
-dropped can be reduced by using the kernel network parameter
-<code>/proc/sys/net/ipv4/tcp_retries2</code>. Alternatively, one of the other
-HTTP connectors may be used as they utilise non-blocking IO enabling Tomcat to
-implement its own timeout mechanism to handle these cases.</p>
-</section>
-
<section name="Tomcat WebSocket specific configuration">
<p>Tomcat provides a number of Tomcat specific configuration options for
@@ -109,24 +91,6 @@ implement its own timeout mechanism to handle these cases.</p>
property to <code>true</code> but any explicit setting on the servlet context
will always take priority.</p>
-<p>The Java WebSocket 1.0 specification requires that callbacks for
- asynchronous writes are performed on a different thread to the thread that
- initiated the write. Since the container thread pool is not exposed via the
- Servlet API, the WebSocket implementation has to provide its own thread pool.
- This thread pool is controlled by the following servlet context
- initialization parameters:</p>
- <ul>
- <li><code>org.apache.tomcat.websocket.executorCoreSize</code>: The core
- size of the executor thread pool. If not set, the default of 0 (zero)
- is used. Note that the maximum permitted size of the executor thread
- pool is hard coded to <code>Integer.MAX_VALUE</code> which effectively
- means it is unlimited.</li>
- <li><code>org.apache.tomcat.websocket.executorKeepAliveTimeSeconds</code>:
- The maximum time an idle thread will remain in the executor thread pool
- until it is terminated. If not specified, the default of 60 seconds is
- used.</li>
- </ul>
-
<p>When using the WebSocket client to connect to server endpoints, the timeout
for IO operations while establishing the connection is controlled by the
<code>userProperties</code> of the provided
diff --git a/webapps/examples/WEB-INF/classes/async/Async0.java b/webapps/examples/WEB-INF/classes/async/Async0.java
index b9ecd25..5bc0ee4 100644
--- a/webapps/examples/WEB-INF/classes/async/Async0.java
+++ b/webapps/examples/WEB-INF/classes/async/Async0.java
@@ -17,6 +17,8 @@
package async;
import java.io.IOException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
import javax.servlet.AsyncContext;
import javax.servlet.ServletException;
@@ -38,7 +40,9 @@ public class Async0 extends HttpServlet {
if (Boolean.TRUE.equals(req.getAttribute("dispatch"))) {
log.info("Received dispatch, completing on the worker thread.");
log.info("After complete called started:"+req.isAsyncStarted());
- resp.getWriter().write("Async dispatch worked:+"+System.currentTimeMillis()+"\n");
+ Date date = new Date(System.currentTimeMillis());
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z");
+ resp.getWriter().write("Async dispatch worked: " + sdf.format(date) + "\n");
} else {
resp.setContentType("text/plain");
final AsyncContext actx = req.startAsync();
diff --git a/webapps/examples/WEB-INF/classes/async/Async2.java b/webapps/examples/WEB-INF/classes/async/Async2.java
index 736da93..0682d62 100644
--- a/webapps/examples/WEB-INF/classes/async/Async2.java
+++ b/webapps/examples/WEB-INF/classes/async/Async2.java
@@ -17,6 +17,8 @@
package async;
import java.io.IOException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
import javax.servlet.AsyncContext;
import javax.servlet.ServletException;
@@ -34,7 +36,8 @@ public class Async2 extends HttpServlet {
private static final Log log = LogFactory.getLog(Async2.class);
@Override
- protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+ protected void service(HttpServletRequest req, HttpServletResponse resp)
+ throws ServletException, IOException {
final AsyncContext actx = req.startAsync();
actx.setTimeout(30*1000);
Runnable run = new Runnable() {
@@ -45,7 +48,10 @@ public class Async2 extends HttpServlet {
log.info("Putting AsyncThread to sleep");
Thread.sleep(2*1000);
log.info("Writing data.");
- actx.getResponse().getWriter().write("Output from background thread. Time:"+System.currentTimeMillis()+"\n");
+ Date date = new Date(System.currentTimeMillis());
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z");
+ actx.getResponse().getWriter().write(
+ "Output from background thread. Time: " + sdf.format(date) + "\n");
actx.complete();
}catch (InterruptedException x) {
log.error("Async2",x);
diff --git a/webapps/examples/WEB-INF/classes/async/AsyncStockServlet.java b/webapps/examples/WEB-INF/classes/async/AsyncStockServlet.java
index 9fc3768..adaa72b 100644
--- a/webapps/examples/WEB-INF/classes/async/AsyncStockServlet.java
+++ b/webapps/examples/WEB-INF/classes/async/AsyncStockServlet.java
@@ -30,6 +30,9 @@ import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
+
import async.Stockticker.Stock;
import async.Stockticker.TickListener;
@@ -37,13 +40,15 @@ public class AsyncStockServlet extends HttpServlet implements TickListener, Asyn
private static final long serialVersionUID = 1L;
+ private static final Log log = LogFactory.getLog(AsyncStockServlet.class);
+
private static final ConcurrentLinkedQueue<AsyncContext> clients =
new ConcurrentLinkedQueue<>();
private static final AtomicInteger clientcount = new AtomicInteger(0);
private static final Stockticker ticker = new Stockticker();
public AsyncStockServlet() {
- System.out.println("AsyncStockServlet created");
+ log.info("AsyncStockServlet created");
}
diff --git a/webapps/examples/WEB-INF/classes/chat/ChatServlet.java b/webapps/examples/WEB-INF/classes/chat/ChatServlet.java
deleted file mode 100644
index a71aa19..0000000
--- a/webapps/examples/WEB-INF/classes/chat/ChatServlet.java
+++ /dev/null
@@ -1,292 +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 chat;
-
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServlet;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.apache.catalina.comet.CometEvent;
-import org.apache.catalina.comet.CometProcessor;
-
-
-/**
- * Helper class to implement Comet functionality.
- */
-public class ChatServlet
- extends HttpServlet implements CometProcessor {
-
- private static final long serialVersionUID = 1L;
-
- private static final String CHARSET = "UTF-8";
-
- protected final ArrayList<HttpServletResponse> connections =
- new ArrayList<>();
- protected transient MessageSender messageSender = null;
-
- @Override
- public void init() throws ServletException {
- messageSender = new MessageSender();
- Thread messageSenderThread =
- new Thread(messageSender, "MessageSender[" + getServletContext().getContextPath() + "]");
- messageSenderThread.setDaemon(true);
- messageSenderThread.start();
- }
-
- @Override
- public void destroy() {
- connections.clear();
- messageSender.stop();
- messageSender = null;
- }
-
- /**
- * Process the given Comet event.
- *
- * @param event The Comet event that will be processed
- * @throws IOException
- * @throws ServletException
- */
- @Override
- public void event(CometEvent event)
- throws IOException, ServletException {
-
- // Note: There should really be two servlets in this example, to avoid
- // mixing Comet stuff with regular connection processing
- HttpServletRequest request = event.getHttpServletRequest();
- HttpServletResponse response = event.getHttpServletResponse();
-
- if (event.getEventType() == CometEvent.EventType.BEGIN) {
- String action = request.getParameter("action");
- if (action != null) {
- if ("login".equals(action)) {
- String nickname = request.getParameter("nickname");
- request.getSession(true).setAttribute("nickname", nickname);
- response.sendRedirect("index.jsp");
- event.close();
- return;
- }
- String nickname = (String) request.getSession(true).getAttribute("nickname");
- String message = request.getParameter("message");
- messageSender.send(nickname, message);
- response.sendRedirect("post.jsp");
- event.close();
- return;
- }
- if (request.getSession(true).getAttribute("nickname") == null) {
- // Redirect to "login"
- log("Redirect to login for session: " + request.getSession(true).getId());
- response.sendRedirect("login.jsp");
- event.close();
- return;
- }
- begin(event, request, response);
- } else if (event.getEventType() == CometEvent.EventType.ERROR) {
- error(event, request, response);
- } else if (event.getEventType() == CometEvent.EventType.END) {
- end(event, request, response);
- } else if (event.getEventType() == CometEvent.EventType.READ) {
- read(event, request, response);
- }
- }
-
- protected void begin(@SuppressWarnings("unused") CometEvent event,
- HttpServletRequest request, HttpServletResponse response)
- throws IOException {
- log("Begin for session: " + request.getSession(true).getId());
-
- response.setContentType("text/html; charset=" + CHARSET);
-
- PrintWriter writer = response.getWriter();
- writer.println("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">");
- writer.println("<html><head><title>JSP Chat</title></head><body bgcolor=\"#FFFFFF\">");
- writer.println("<div>Welcome to the chat. <a href='chat'>Click here to reload this window</a></div>");
- writer.flush();
-
- synchronized(connections) {
- connections.add(response);
- }
-
- messageSender.send("Tomcat", request.getSession(true).getAttribute("nickname") + " joined the chat.");
- }
-
- protected void end(CometEvent event, HttpServletRequest request, HttpServletResponse response)
- throws IOException {
- log("End for session: " + request.getSession(true).getId());
- synchronized(connections) {
- connections.remove(response);
- }
-
- PrintWriter writer = response.getWriter();
- writer.println("</body></html>");
-
- event.close();
- }
-
- protected void error(CometEvent event, HttpServletRequest request, HttpServletResponse response)
- throws IOException {
- log("Error for session: " + request.getSession(true).getId());
- synchronized(connections) {
- connections.remove(response);
- }
- event.close();
- }
-
- protected void read(CometEvent event, HttpServletRequest request, HttpServletResponse response)
- throws IOException {
- InputStream is = request.getInputStream();
- byte[] buf = new byte[512];
- while (is.available() > 0) {
- log("Available: " + is.available());
- int n = is.read(buf);
- if (n > 0) {
- log("Read " + n + " bytes: " + new String(buf, 0, n)
- + " for session: " + request.getSession(true).getId());
- } else if (n < 0) {
- log("End of file: " + n);
- end(event, request, response);
- return;
- }
- }
- }
-
- @Override
- protected void service(HttpServletRequest request, HttpServletResponse response)
- throws IOException, ServletException {
- // Compatibility method: equivalent method using the regular connection model
- response.setContentType("text/html; charset=" + CHARSET);
- PrintWriter writer = response.getWriter();
- writer.println("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">");
- writer.println("<html><head><title>JSP Chat</title></head><body bgcolor=\"#FFFFFF\">");
- writer.println("Chat example only supports Comet processing. ");
- writer.println("Configure a connector that supports Comet and try again.");
- writer.println("</body></html>");
- }
-
-
- /**
- * Poller class.
- */
- public class MessageSender implements Runnable {
-
- protected boolean running = true;
- protected final ArrayList<String> messages = new ArrayList<>();
-
- public MessageSender() {
- // Default contructor
- }
-
- public void stop() {
- running = false;
- synchronized (messages) {
- messages.notify();
- }
- }
-
- public void send(String user, String message) {
- synchronized (messages) {
- messages.add("[" + user + "]: " + message);
- messages.notify();
- }
- }
-
- /**
- * The background thread that listens for incoming TCP/IP connections and
- * hands them off to an appropriate processor.
- */
- @Override
- public void run() {
-
- // Loop until we receive a shutdown command
- while (running) {
- String[] pendingMessages;
- synchronized (messages) {
- try {
- if (running && messages.size() == 0) {
- messages.wait();
- }
- } catch (InterruptedException e) {
- // Ignore
- }
- pendingMessages = messages.toArray(new String[0]);
- messages.clear();
- }
-
- synchronized (connections) {
- for (int i = 0; i < connections.size(); i++) {
- try {
- PrintWriter writer = connections.get(i).getWriter();
- for (int j = 0; j < pendingMessages.length; j++) {
- writer.println("<div>"+filter(pendingMessages[j]) + "</div>");
- }
- writer.flush();
- } catch (IOException e) {
- log("IOException sending message", e);
- }
- }
- }
-
- }
-
- }
-
- }
-
- /**
- * Filter the specified message string for characters that are sensitive
- * in HTML.
- *
- * @param message The message string to be filtered
- * @author Copied from org.apache.catalina.util.RequestUtil#filter(String)
- */
- protected static String filter(String message) {
- if (message == null)
- return (null);
-
- char content[] = new char[message.length()];
- message.getChars(0, message.length(), content, 0);
- StringBuilder result = new StringBuilder(content.length + 50);
- for (int i = 0; i < content.length; i++) {
- switch (content[i]) {
- case '<':
- result.append("<");
- break;
- case '>':
- result.append(">");
- break;
- case '&':
- result.append("&");
- break;
- case '"':
- result.append(""");
- break;
- default:
- result.append(content[i]);
- }
- }
- return (result.toString());
- }
-}
diff --git a/webapps/examples/WEB-INF/classes/compressionFilters/CompressionFilter.java b/webapps/examples/WEB-INF/classes/compressionFilters/CompressionFilter.java
index db9e4ef..3b851f1 100644
--- a/webapps/examples/WEB-INF/classes/compressionFilters/CompressionFilter.java
+++ b/webapps/examples/WEB-INF/classes/compressionFilters/CompressionFilter.java
@@ -42,12 +42,6 @@ import javax.servlet.http.HttpServletResponse;
public class CompressionFilter implements Filter {
/**
- * The filter configuration object we are associated with. If this value
- * is null, this filter instance is not currently configured.
- */
- private FilterConfig config = null;
-
- /**
* Minimal reasonable threshold.
*/
private final int minThreshold = 128;
@@ -85,62 +79,61 @@ public class CompressionFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) {
- config = filterConfig;
if (filterConfig != null) {
String value = filterConfig.getInitParameter("debug");
if (value!=null) {
debug = Integer.parseInt(value);
- }
+ }
String str = filterConfig.getInitParameter("compressionThreshold");
- if (str!=null) {
- compressionThreshold = Integer.parseInt(str);
- if (compressionThreshold != 0 && compressionThreshold < minThreshold) {
- if (debug > 0) {
- System.out.println("compressionThreshold should be either 0 - no compression or >= " + minThreshold);
- System.out.println("compressionThreshold set to " + minThreshold);
- }
- compressionThreshold = minThreshold;
+ if (str != null) {
+ compressionThreshold = Integer.parseInt(str);
+ if (compressionThreshold != 0 && compressionThreshold < minThreshold) {
+ if (debug > 0) {
+ System.out.println("compressionThreshold should be either 0 - no compression or >= " + minThreshold);
+ System.out.println("compressionThreshold set to " + minThreshold);
}
+ compressionThreshold = minThreshold;
}
+ }
str = filterConfig.getInitParameter("compressionBuffer");
- if (str!=null) {
- compressionBuffer = Integer.parseInt(str);
- if (compressionBuffer < minBuffer) {
- if (debug > 0) {
- System.out.println("compressionBuffer should be >= " + minBuffer);
- System.out.println("compressionBuffer set to " + minBuffer);
- }
- compressionBuffer = minBuffer;
+ if (str != null) {
+ compressionBuffer = Integer.parseInt(str);
+ if (compressionBuffer < minBuffer) {
+ if (debug > 0) {
+ System.out.println("compressionBuffer should be >= " + minBuffer);
+ System.out.println("compressionBuffer set to " + minBuffer);
}
+ compressionBuffer = minBuffer;
}
+ }
str = filterConfig.getInitParameter("compressionMimeTypes");
- if (str!=null) {
- List<String> values = new ArrayList<>();
- StringTokenizer st = new StringTokenizer(str, ",");
-
- while (st.hasMoreTokens()) {
- String token = st.nextToken().trim();
- if (token.length() > 0) {
- values.add(token);
- }
+ if (str != null) {
+ List<String> values = new ArrayList<>();
+ StringTokenizer st = new StringTokenizer(str, ",");
+
+ while (st.hasMoreTokens()) {
+ String token = st.nextToken().trim();
+ if (token.length() > 0) {
+ values.add(token);
}
+ }
- if (values.size() > 0) {
- compressionMimeTypes = values.toArray(
- new String[values.size()]);
- } else {
- compressionMimeTypes = null;
- }
+ if (values.size() > 0) {
+ compressionMimeTypes = values.toArray(
+ new String[values.size()]);
+ } else {
+ compressionMimeTypes = null;
+ }
- if (debug > 0) {
- System.out.println("compressionMimeTypes set to " +
- Arrays.toString(compressionMimeTypes));
- }
+ if (debug > 0) {
+ System.out.println("compressionMimeTypes set to " +
+ Arrays.toString(compressionMimeTypes));
}
}
+ }
}
@@ -149,9 +142,6 @@ public class CompressionFilter implements Filter {
*/
@Override
public void destroy() {
-
- this.config = null;
-
}
/**
@@ -171,8 +161,8 @@ public class CompressionFilter implements Filter {
* (<code>chain.doFilter()</code>), <br>
**/
@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 {
if (debug > 0) {
System.out.println("@doFilter");
@@ -245,24 +235,5 @@ public class CompressionFilter implements Filter {
return;
}
}
-
- /**
- * Set filter config
- * This function is equivalent to init. Required by Weblogic 6.1
- *
- * @param filterConfig The filter configuration object
- */
- public void setFilterConfig(FilterConfig filterConfig) {
- init(filterConfig);
- }
-
- /**
- * Required by Weblogic 6.1
- *
- * @return the FilterConfig that was used to initialise this filter.
- */
- public FilterConfig getFilterConfig() {
- return config;
- }
}
diff --git a/webapps/examples/WEB-INF/classes/compressionFilters/CompressionResponseStream.java b/webapps/examples/WEB-INF/classes/compressionFilters/CompressionResponseStream.java
index 00c4287..ccc6607 100644
--- a/webapps/examples/WEB-INF/classes/compressionFilters/CompressionResponseStream.java
+++ b/webapps/examples/WEB-INF/classes/compressionFilters/CompressionResponseStream.java
@@ -110,7 +110,10 @@ public class CompressionResponseStream extends ServletOutputStream {
// --------------------------------------------------------- Public Methods
/**
- * Set debug level
+ * Set debug level.
+ *
+ * @param debug The higher the number, the more detail shown. Currently the
+ * range 0 (none) to 3 (everything) is used.
*/
public void setDebugLevel(int debug) {
this.debug = debug;
@@ -139,7 +142,9 @@ public class CompressionResponseStream extends ServletOutputStream {
}
/**
- * Set supported mime types
+ * Set supported mime types.
+ *
+ * @param compressionMimeTypes The mimetypes that will be compressed.
*/
public void setCompressionMimeTypes(String[] compressionMimeTypes) {
this.compressionMimeTypes = compressionMimeTypes;
@@ -401,16 +406,16 @@ public class CompressionResponseStream extends ServletOutputStream {
// -------------------------------------------------------- Package Methods
-
/**
* Has this response stream been closed?
+ *
+ * @return <code>true</code> if the stream has been closed, otherwise false.
*/
public boolean closed() {
-
- return (this.closed);
-
+ return closed;
}
+
/**
* Checks if any entry in the string array starts with the specified value
*
diff --git a/webapps/examples/WEB-INF/classes/compressionFilters/CompressionServletResponseWrapper.java b/webapps/examples/WEB-INF/classes/compressionFilters/CompressionServletResponseWrapper.java
index 89e552e..60413c5 100644
--- a/webapps/examples/WEB-INF/classes/compressionFilters/CompressionServletResponseWrapper.java
+++ b/webapps/examples/WEB-INF/classes/compressionFilters/CompressionServletResponseWrapper.java
@@ -42,6 +42,8 @@ public class CompressionServletResponseWrapper
/**
* Calls the parent constructor which creates a ServletResponse adaptor
* wrapping the given response object.
+ *
+ * @param response The response object to be wrapped.
*/
public CompressionServletResponseWrapper(HttpServletResponse response) {
super(response);
@@ -102,7 +104,9 @@ public class CompressionServletResponseWrapper
/**
- * Set threshold number
+ * Set threshold number.
+ *
+ * @param threshold The new compression threshold
*/
public void setCompressionThreshold(int threshold) {
if (debug > 1) {
@@ -112,7 +116,9 @@ public class CompressionServletResponseWrapper
}
/**
- * Set compression buffer
+ * Set compression buffer.
+ *
+ * @param buffer New size of buffer to use for compressed output
*/
public void setCompressionBuffer(int buffer) {
if (debug > 1) {
@@ -122,7 +128,10 @@ public class CompressionServletResponseWrapper
}
/**
- * Set compressible mime types
+ * Set compressible mime types.
+ *
+ * @param mimeTypes The new list of mime types that will be considered for
+ * compression
*/
public void setCompressionMimeTypes(String[] mimeTypes) {
if (debug > 1) {
@@ -133,7 +142,9 @@ public class CompressionServletResponseWrapper
}
/**
- * Set debug level
+ * Set debug level.
+ *
+ * @param debug The new debug level
*/
public void setDebugLevel(int debug) {
this.debug = debug;
@@ -145,8 +156,11 @@ public class CompressionServletResponseWrapper
* associated with this Response.
*
* @exception IOException if an input/output error occurs
+ *
+ * @return A new servlet output stream that compressed any data written to
+ * it
*/
- public ServletOutputStream createOutputStream() throws IOException {
+ protected ServletOutputStream createOutputStream() throws IOException {
if (debug > 1) {
System.out.println("createOutputStream gets called");
}
diff --git a/webapps/examples/WEB-INF/classes/filters/ExampleFilter.java b/webapps/examples/WEB-INF/classes/filters/ExampleFilter.java
index 84bd52a..3809ed6 100644
--- a/webapps/examples/WEB-INF/classes/filters/ExampleFilter.java
+++ b/webapps/examples/WEB-INF/classes/filters/ExampleFilter.java
@@ -91,7 +91,7 @@ public final class ExampleFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain)
- throws IOException, ServletException {
+ throws IOException, ServletException {
// Store ourselves as a request attribute (if requested)
if (attribute != null)
@@ -104,7 +104,6 @@ public final class ExampleFilter implements Filter {
filterConfig.getServletContext().log
(this.toString() + ": " + (stopTime - startTime) +
" milliseconds");
-
}
@@ -118,7 +117,6 @@ public final class ExampleFilter implements Filter {
this.filterConfig = fConfig;
this.attribute = fConfig.getInitParameter("attribute");
-
}
@@ -134,9 +132,6 @@ public final class ExampleFilter implements Filter {
sb.append(filterConfig);
sb.append(")");
return (sb.toString());
-
}
-
-
}
diff --git a/webapps/examples/WEB-INF/classes/http2/SimpleImagePush.java b/webapps/examples/WEB-INF/classes/http2/SimpleImagePush.java
new file mode 100644
index 0000000..6bb2dae
--- /dev/null
+++ b/webapps/examples/WEB-INF/classes/http2/SimpleImagePush.java
@@ -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 http2;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.catalina.servlet4preview.http.PushBuilder;
+
+public class SimpleImagePush extends HttpServlet {
+
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ protected void doGet(HttpServletRequest req, HttpServletResponse resp)
+ throws ServletException, IOException {
+
+ PushBuilder pb = ((org.apache.catalina.servlet4preview.http.HttpServletRequest)
+ req).getPushBuilder().path("servlets/images/code.gif");
+ pb.push();
+
+ resp.setCharacterEncoding("UTF-8");
+ resp.setContentType("text/html");
+ PrintWriter pw = resp.getWriter();
+ pw.println("<html>");
+ pw.println("<body>");
+ pw.println("<p>The following image was provided via a push request.</p>");
+ pw.println("<img src=\"" + req.getContextPath() + "/servlets/images/code.gif\"/>");
+ pw.println("</body>");
+ pw.println("</html>");
+ pw.flush();
+ }
+}
diff --git a/webapps/examples/WEB-INF/classes/util/HTMLFilter.java b/webapps/examples/WEB-INF/classes/util/HTMLFilter.java
index a0f7dec..9bc8cff 100644
--- a/webapps/examples/WEB-INF/classes/util/HTMLFilter.java
+++ b/webapps/examples/WEB-INF/classes/util/HTMLFilter.java
@@ -31,6 +31,8 @@ public final class HTMLFilter {
* codes in the request URL that is often reported in error messages.
*
* @param message The message string to be filtered
+ *
+ * @return the filtered version of the message
*/
public static String filter(String message) {
diff --git a/webapps/examples/WEB-INF/classes/websocket/drawboard/Client.java b/webapps/examples/WEB-INF/classes/websocket/drawboard/Client.java
index 2ccbc68..8026bf2 100644
--- a/webapps/examples/WEB-INF/classes/websocket/drawboard/Client.java
+++ b/webapps/examples/WEB-INF/classes/websocket/drawboard/Client.java
@@ -80,7 +80,8 @@ public class Client {
* will be buffered and sent when possible.<br><br>
*
* This method can be called from multiple threads.
- * @param msg
+ *
+ * @param msg The message to send
*/
public void sendMessage(AbstractWebsocketMessage msg) {
synchronized (messagesToSend) {
diff --git a/webapps/examples/WEB-INF/classes/websocket/drawboard/DrawMessage.java b/webapps/examples/WEB-INF/classes/websocket/drawboard/DrawMessage.java
index 52d7971..8dceaba 100644
--- a/webapps/examples/WEB-INF/classes/websocket/drawboard/DrawMessage.java
+++ b/webapps/examples/WEB-INF/classes/websocket/drawboard/DrawMessage.java
@@ -37,14 +37,11 @@ public final class DrawMessage {
private byte colorR, colorG, colorB, colorA;
private double thickness;
private double x1, y1, x2, y2;
- private boolean lastInChain;
/**
- * The type.<br>
- * 1: Brush<br>
- * 2: Line<br>
- * 3: Rectangle<br>
- * 4: Ellipse
+ * The type.
+ *
+ * @return 1: Brush<br>2: Line<br>3: Rectangle<br>4: Ellipse
*/
public int getType() {
return type;
@@ -110,23 +107,10 @@ public final class DrawMessage {
this.y2 = y2;
}
- /**
- * Specifies if this DrawMessage is the last one in a chain
- * (e.g. a chain of brush paths).<br>
- * Currently it is unused.
- */
- public boolean isLastInChain() {
- return lastInChain;
- }
- public void setLastInChain(boolean lastInChain) {
- this.lastInChain = lastInChain;
- }
-
-
public DrawMessage(int type, byte colorR, byte colorG, byte colorB,
byte colorA, double thickness, double x1, double x2, double y1,
- double y2, boolean lastInChain) {
+ double y2) {
this.type = type;
this.colorR = colorR;
@@ -138,13 +122,13 @@ public final class DrawMessage {
this.x2 = x2;
this.y1 = y1;
this.y2 = y2;
- this.lastInChain = lastInChain;
}
/**
* Draws this DrawMessage onto the given Graphics2D.
- * @param g
+ *
+ * @param g The target for the DrawMessage
*/
public void draw(Graphics2D g) {
@@ -205,8 +189,7 @@ public final class DrawMessage {
return type + "," + (colorR & 0xFF) + "," + (colorG & 0xFF) + ","
+ (colorB & 0xFF) + "," + (colorA & 0xFF) + "," + thickness
- + "," + x1 + "," + y1 + "," + x2 + "," + y2 + ","
- + (lastInChain ? "1" : "0");
+ + "," + x1 + "," + y1 + "," + x2 + "," + y2;
}
public static DrawMessage parseFromString(String str)
@@ -216,7 +199,6 @@ public final class DrawMessage {
byte[] colors = new byte[4];
double thickness;
double[] coords = new double[4];
- boolean last;
try {
String[] elements = str.split(",");
@@ -240,15 +222,13 @@ public final class DrawMessage {
+ coords[i]);
}
- last = !"0".equals(elements[10]);
-
} catch (RuntimeException ex) {
throw new ParseException(ex);
}
DrawMessage m = new DrawMessage(type, colors[0], colors[1],
colors[2], colors[3], thickness, coords[0], coords[2],
- coords[1], coords[3], last);
+ coords[1], coords[3]);
return m;
}
diff --git a/webapps/examples/WEB-INF/classes/websocket/drawboard/Room.java b/webapps/examples/WEB-INF/classes/websocket/drawboard/Room.java
index 0aab7ec..67e086b 100644
--- a/webapps/examples/WEB-INF/classes/websocket/drawboard/Room.java
+++ b/webapps/examples/WEB-INF/classes/websocket/drawboard/Room.java
@@ -25,6 +25,7 @@ import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
+import java.util.Objects;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.locks.ReentrantLock;
@@ -166,7 +167,10 @@ public final class Room {
/**
* Creates a Player from the given Client and adds it to this room.
+ *
* @param client the client
+ *
+ * @return The newly created player
*/
public Player createAndAddPlayer(Client client) {
if (players.size() >= MAX_PLAYER_COUNT) {
@@ -338,7 +342,8 @@ public final class Room {
* runnable on this Room, it will not be executed recursively, but instead
* cached until the original runnable is finished, to keep the behavior of
* using a Executor.
- * @param task
+ *
+ * @param task The task to be executed
*/
public void invokeAndWait(Runnable task) {
@@ -465,8 +470,9 @@ public final class Room {
/**
* Handles the given DrawMessage by drawing it onto this Room's
* image and by broadcasting it to the connected players.
- * @param msg
- * @param msgId
+ *
+ * @param msg The draw message received
+ * @param msgId The ID for the draw message recieved
*/
public void handleDrawMessage(DrawMessage msg, long msgId) {
room.internalHandleDrawMessage(this, msg, msgId);
@@ -479,8 +485,8 @@ public final class Room {
* @param content
*/
private void sendRoomMessage(MessageType type, String content) {
- if (content == null || type == null)
- throw new NullPointerException();
+ Objects.requireNonNull(content);
+ Objects.requireNonNull(type);
String completeMsg = String.valueOf(type.flag) + content;
diff --git a/webapps/examples/WEB-INF/jsp2/jsp2-example-taglib.tld b/webapps/examples/WEB-INF/jsp2/jsp2-example-taglib.tld
index bb2234a..73173bd 100644
--- a/webapps/examples/WEB-INF/jsp2/jsp2-example-taglib.tld
+++ b/webapps/examples/WEB-INF/jsp2/jsp2-example-taglib.tld
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="UTF-8" ?>
+<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
diff --git a/webapps/examples/WEB-INF/web.xml b/webapps/examples/WEB-INF/web.xml
index 5ce3afa..9b45c00 100644
--- a/webapps/examples/WEB-INF/web.xml
+++ b/webapps/examples/WEB-INF/web.xml
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="ISO-8859-1"?>
+<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
@@ -122,10 +122,6 @@
<servlet-class>ServletToJsp</servlet-class>
</servlet>
<servlet>
- <servlet-name>ChatServlet</servlet-name>
- <servlet-class>chat.ChatServlet</servlet-class>
- </servlet>
- <servlet>
<servlet-name>CompressionFilterTestServlet</servlet-name>
<servlet-class>compressionFilters.CompressionFilterTestServlet</servlet-class>
</servlet>
@@ -155,10 +151,6 @@
</servlet>
<servlet-mapping>
- <servlet-name>ChatServlet</servlet-name>
- <url-pattern>/servlets/chat/chat</url-pattern>
- </servlet-mapping>
- <servlet-mapping>
<servlet-name>CompressionFilterTestServlet</servlet-name>
<url-pattern>/CompressionTest</url-pattern>
</servlet-mapping>
@@ -387,6 +379,16 @@
<url-pattern>/servlets/nonblocking/numberwriter</url-pattern>
</servlet-mapping>
+ <!-- Server Push examples -->
+ <servlet>
+ <servlet-name>simpleimagepush</servlet-name>
+ <servlet-class>http2.SimpleImagePush</servlet-class>
+ </servlet>
+ <servlet-mapping>
+ <servlet-name>simpleimagepush</servlet-name>
+ <url-pattern>/servlets/serverpush/simpleimage</url-pattern>
+ </servlet-mapping>
+
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.xhtml</welcome-file>
diff --git a/webapps/examples/jsp/async/async1.jsp b/webapps/examples/jsp/async/async1.jsp
index b7e3da6..af88869 100644
--- a/webapps/examples/jsp/async/async1.jsp
+++ b/webapps/examples/jsp/async/async1.jsp
@@ -14,13 +14,15 @@
See the License for the specific language governing permissions and
limitations under the License.
--%>
-<%@page session="false"%>
+<%@page session="false" import="java.util.Date,java.text.SimpleDateFormat"%>
Output from async1.jsp
Type is <%=request.getDispatcherType()%>
<%
-System.out.println("Inside Async 1");
+ System.out.println("Inside Async 1");
if (request.isAsyncStarted()) {
request.getAsyncContext().complete();
}
+ Date date = new Date(System.currentTimeMillis());
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z");
%>
-Completed async request at <%=new java.sql.Date(System.currentTimeMillis())%>
\ No newline at end of file
+Completed async request at <%=sdf.format(date)%>
\ No newline at end of file
diff --git a/webapps/examples/jsp/async/async3.jsp b/webapps/examples/jsp/async/async3.jsp
index fd54054..9d24e60 100644
--- a/webapps/examples/jsp/async/async3.jsp
+++ b/webapps/examples/jsp/async/async3.jsp
@@ -14,7 +14,12 @@
See the License for the specific language governing permissions and
limitations under the License.
--%>
-<%@page session="false"%>
+<%@page session="false" import="java.util.Date,java.text.SimpleDateFormat"%>
Output from async3.jsp
Type is <%=request.getDispatcherType()%>
-Completed async 3 request at <%=new java.sql.Date(System.currentTimeMillis())%>
\ No newline at end of file
+<%
+ Date date = new Date(System.currentTimeMillis());
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z");
+%>
+
+Completed async 3 request at <%=sdf.format(date)%>
\ No newline at end of file
diff --git a/webapps/examples/jsp/xml/xml.jsp b/webapps/examples/jsp/xml/xml.jsp
index fdd5f47..840b21f 100644
--- a/webapps/examples/jsp/xml/xml.jsp
+++ b/webapps/examples/jsp/xml/xml.jsp
@@ -1,4 +1,4 @@
-<?xml version="1.0"?>
+<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
diff --git a/webapps/examples/servlets/chat/index.jsp b/webapps/examples/servlets/chat/index.jsp
deleted file mode 100644
index a867e37..0000000
--- a/webapps/examples/servlets/chat/index.jsp
+++ /dev/null
@@ -1,32 +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.
---%>
-<%@page contentType="text/html; charset=UTF-8" %>
-<% if (session.getAttribute("nickname") == null) {
- response.sendRedirect("login.jsp");
- return;
-}
-%>
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">
-<html>
-<head>
- <title>JSP Chat</title>
-</head>
-<frameset rows="1*,4*">
- <frame name="post" src="post.jsp" scrolling="no" title="Post message">
- <frame name="chat" src="chat" scrolling="yes" title="Chat">
-</frameset>
-</html>
diff --git a/webapps/examples/servlets/chat/login.jsp b/webapps/examples/servlets/chat/login.jsp
deleted file mode 100644
index beb3b38..0000000
--- a/webapps/examples/servlets/chat/login.jsp
+++ /dev/null
@@ -1,33 +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.
---%>
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
-<%@page contentType="text/html; charset=UTF-8" %>
-<html>
-<head>
- <title>JSP Chat</title>
-</head>
-
-<body bgcolor="#FFFFFF">
-
-<form method="POST" action='chat' target="_top" name="loginForm">
-<input type="hidden" name="action" value="login">
-Nickname: <input type="text" name="nickname">
-<input type="submit">
-</form>
-
-</body>
-</html>
diff --git a/webapps/examples/servlets/chat/post.jsp b/webapps/examples/servlets/chat/post.jsp
deleted file mode 100644
index f6b38e5..0000000
--- a/webapps/examples/servlets/chat/post.jsp
+++ /dev/null
@@ -1,55 +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.
---%>
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
-<%@page contentType="text/html; charset=UTF-8" %>
-<html>
-<head>
- <title>JSP Chat</title>
-</head>
-
-<body bgcolor="#FFFFFF">
-
-<form method="POST" action='chat' name="postForm">
-<input type="hidden" name="action" value="post">
-Message: <input type="text" name="message">
-<input type="submit">
-</form>
-
-<br>
-<%
- String serverName = request.getServerName();
- if ("localhost".equals(serverName)) {
- serverName = "127.0.0.1";
- } else if ("127.0.0.1".equals(serverName)) {
- serverName = "localhost";
- }
-
- String chatUrl = request.getScheme() + "://" + serverName + ":"
- + request.getServerPort() + request.getContextPath()
- + request.getServletPath();
-
- // strip "post.jsp" from the address
- chatUrl = chatUrl.substring(0, chatUrl.lastIndexOf("/") + 1);
-%>
-<a target="_blank" href="<%=chatUrl %>">Click to open a new chat window</a>
-<em>Note</em>: To avoid hitting the limit on the count of simultaneous
-connections to the same host, imposed by the
-<a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec8.html#sec8.1.4">HTTP specification</a>,
-the second chat window should be opened using a different URL, e.g. with
-an IP address instead of the host name.
-</body>
-</html>
diff --git a/webapps/examples/servlets/index.html b/webapps/examples/servlets/index.html
index 625ffa2..1ac8ab4 100644
--- a/webapps/examples/servlets/index.html
+++ b/webapps/examples/servlets/index.html
@@ -149,20 +149,6 @@ for clarity.</p>
</tr>
<tr>
- <th colspan="3">Comet processing example:<br />
- <span style="font-weight: normal;">See the <strong>"Advanced IO"</strong> chapter in the User Guide for
- details. This example only works with the HTTP NIO or HTTP APR/native
- connectors as these are the only connectors that support Comet.</span></th>
-</tr>
-<tr>
- <td>Comet Chat</td>
- <td style="width: 30%;">
- <a href="chat/"><img SRC="images/execute.gif" alt=""> Execute</a>
- </td>
- <td style="width: 30%;"></td>
-</tr>
-
-<tr>
<th colspan="3">Servlet 3.1 Non-blocking IO examples</th>
</tr>
<tr>
@@ -180,6 +166,17 @@ for clarity.</p>
<td style="width: 30%;"></td>
</tr>
+<tr>
+ <th colspan="3">Servlet 4.0 Early Preview examples</th>
+</tr>
+<tr>
+ <td>HTTP/2 server push</td>
+ <td style="width: 30%;">
+ <a href="serverpush/simpleimage"><img src="images/execute.gif" alt=""> Execute</a>
+ </td>
+ <td style="width: 30%;"></td>
+</tr>
+
</table>
</body>
diff --git a/webapps/examples/websocket/drawboard.xhtml b/webapps/examples/websocket/drawboard.xhtml
index c08a218..2a3e74c 100644
--- a/webapps/examples/websocket/drawboard.xhtml
+++ b/webapps/examples/websocket/drawboard.xhtml
@@ -267,7 +267,7 @@
this.id = id;
}
- function Path(type, color, thickness, x1, y1, x2, y2, lastInChain) {
+ function Path(type, color, thickness, x1, y1, x2, y2) {
this.type = type;
this.color = color;
this.thickness = thickness;
@@ -275,7 +275,6 @@
this.y1 = y1;
this.x2 = x2;
this.y2 = y2;
- this.lastInChain = lastInChain;
function ellipse(ctx, x, y, w, h) {
/* Drawing a ellipse cannot be done directly in a
@@ -495,8 +494,7 @@
parseFloat(elements[7]),
parseFloat(elements[8]),
parseFloat(elements[9]),
- parseFloat(elements[10]),
- elements[11] != "0");
+ parseFloat(elements[10]));
// Draw the path onto the last canvas.
path.draw(canvasServerImageCtx);
@@ -613,7 +611,7 @@
availableColors[currentColorIndex],
availableThicknesses[currentThicknessIndex],
currentMouseX, currentMouseY, mouseX,
- mouseY, false);
+ mouseY);
// Draw it on the background canvas.
path.draw(canvasBackgroundCtx);
@@ -658,19 +656,24 @@
var mouseY = e.pageY - canvasDisplay.offsetTop;
var drawType = availableDrawTypes[currentDrawTypeIndex];
- var path = new Path(drawType.id, availableColors[currentColorIndex],
- availableThicknesses[currentThicknessIndex],
- currentMouseX, currentMouseY, mouseX,
- mouseY, true);
- // Draw it on the background canvas.
- path.draw(canvasBackgroundCtx);
-
- // Send it to the sever.
- pushPath(path);
-
- // Refresh old coordinates
- currentMouseX = mouseX;
- currentMouseY = mouseY;
+ // If we are drawing a continuous path and the previous mouse coordinates are the same as
+ // the new ones, there is no need to construct a new draw message as we don't need to
+ // "terminate" a path as every path element contains both the start and the end point.
+ if (!(drawType.continuous && mouseX == currentMouseX && mouseY == currentMouseY)) {
+ var path = new Path(drawType.id, availableColors[currentColorIndex],
+ availableThicknesses[currentThicknessIndex],
+ currentMouseX, currentMouseY, mouseX,
+ mouseY);
+ // Draw it on the background canvas.
+ path.draw(canvasBackgroundCtx);
+
+ // Send it to the sever.
+ pushPath(path);
+
+ // Refresh old coordinates
+ currentMouseX = mouseX;
+ currentMouseY = mouseY;
+ }
refreshDisplayCanvas();
}
@@ -807,8 +810,7 @@
+ path.color[2] + ","
+ Math.round(path.color[3] * 255.0) + ","
+ path.thickness + "," + path.x1 + ","
- + path.y1 + "," + path.x2 + "," + path.y2 + ","
- + (path.lastInChain ? "1" : "0");
+ + path.y1 + "," + path.x2 + "," + path.y2;
socket.send("1" + message);
}
@@ -875,7 +877,7 @@
</p>
<p>
It uses asynchronous sending of messages so that it doesn't need separate threads
- for each client to send messages (this needs NIO or APR connector to be used).<br/>
+ for each client to send messages.<br/>
Each "Room" (where the drawing happens) uses a ReentrantLock to synchronize access
(currently, only a single Room is implemented).
</p>
diff --git a/webapps/host-manager/META-INF/context.xml b/webapps/host-manager/META-INF/context.xml
index 3390e96..18c5623 100644
--- a/webapps/host-manager/META-INF/context.xml
+++ b/webapps/host-manager/META-INF/context.xml
@@ -16,12 +16,6 @@
limitations under the License.
-->
<Context antiResourceLocking="false" privileged="true" >
- <!--
- Remove the comment markers from around the Valve below to limit access to
- the host-manager application to clients connecting from localhost
- -->
- <!--
<Valve className="org.apache.catalina.valves.RemoteAddrValve"
allow="127\.\d+\.\d+\.\d+|::1|0:0:0:0:0:0:0:1" />
- -->
</Context>
\ No newline at end of file
diff --git a/webapps/host-manager/WEB-INF/jsp/403.jsp b/webapps/host-manager/WEB-INF/jsp/403.jsp
index 5eff6f0..949535e 100644
--- a/webapps/host-manager/WEB-INF/jsp/403.jsp
+++ b/webapps/host-manager/WEB-INF/jsp/403.jsp
@@ -34,6 +34,11 @@
You are not authorized to view this page.
</p>
<p>
+ By default the Host Manager is only accessible from a browser running on the
+ same machine as Tomcat. If you wish to modify this restriction, you'll need
+ to edit the Host Manager's <tt>context.xml</tt> file.
+ </p>
+ <p>
If you have already configured the Host Manager application to allow access
and you have used your browsers back button, used a saved book-mark or
similar then you may have triggered the cross-site request forgery (CSRF)
diff --git a/webapps/host-manager/WEB-INF/web.xml b/webapps/host-manager/WEB-INF/web.xml
index c2d6f0a..c315546 100644
--- a/webapps/host-manager/WEB-INF/web.xml
+++ b/webapps/host-manager/WEB-INF/web.xml
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="ISO-8859-1"?>
+<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
diff --git a/webapps/manager/META-INF/context.xml b/webapps/manager/META-INF/context.xml
index 21d9bac..743f25b 100644
--- a/webapps/manager/META-INF/context.xml
+++ b/webapps/manager/META-INF/context.xml
@@ -16,12 +16,6 @@
limitations under the License.
-->
<Context antiResourceLocking="false" privileged="true" >
- <!--
- Remove the comment markers from around the Valve below to limit access to
- the manager application to clients connecting from localhost
- -->
- <!--
<Valve className="org.apache.catalina.valves.RemoteAddrValve"
allow="127\.\d+\.\d+\.\d+|::1|0:0:0:0:0:0:0:1" />
- -->
</Context>
diff --git a/webapps/manager/WEB-INF/jsp/403.jsp b/webapps/manager/WEB-INF/jsp/403.jsp
index ad6aebe..6d95c93 100644
--- a/webapps/manager/WEB-INF/jsp/403.jsp
+++ b/webapps/manager/WEB-INF/jsp/403.jsp
@@ -34,6 +34,11 @@
You are not authorized to view this page.
</p>
<p>
+ By default the Manager is only accessible from a browser running on the
+ same machine as Tomcat. If you wish to modify this restriction, you'll need
+ to edit the Manager's <tt>context.xml</tt> file.
+ </p>
+ <p>
If you have already configured the Manager application to allow access and
you have used your browsers back button, used a saved book-mark or similar
then you may have triggered the cross-site request forgery (CSRF) protection
diff --git a/webapps/manager/WEB-INF/jsp/connectorCiphers.jsp b/webapps/manager/WEB-INF/jsp/connectorCiphers.jsp
index 9951d96..73c4590 100644
--- a/webapps/manager/WEB-INF/jsp/connectorCiphers.jsp
+++ b/webapps/manager/WEB-INF/jsp/connectorCiphers.jsp
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="ISO-8859-1"?>
+<?xml version="1.0" encoding="UTF-8"?>
<%--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
diff --git a/webapps/manager/WEB-INF/jsp/sessionDetail.jsp b/webapps/manager/WEB-INF/jsp/sessionDetail.jsp
index eeac8be..28235e9 100644
--- a/webapps/manager/WEB-INF/jsp/sessionDetail.jsp
+++ b/webapps/manager/WEB-INF/jsp/sessionDetail.jsp
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="ISO-8859-1"?>
+<?xml version="1.0" encoding="UTF-8"?>
<%--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
diff --git a/webapps/manager/WEB-INF/jsp/sessionsList.jsp b/webapps/manager/WEB-INF/jsp/sessionsList.jsp
index b977a96..80951bc 100644
--- a/webapps/manager/WEB-INF/jsp/sessionsList.jsp
+++ b/webapps/manager/WEB-INF/jsp/sessionsList.jsp
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="ISO-8859-1"?>
+<?xml version="1.0" encoding="UTF-8"?>
<%--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
diff --git a/webapps/manager/WEB-INF/web.xml b/webapps/manager/WEB-INF/web.xml
index ef917e6..d91728e 100644
--- a/webapps/manager/WEB-INF/web.xml
+++ b/webapps/manager/WEB-INF/web.xml
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="ISO-8859-1"?>
+<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
diff --git a/webapps/manager/xform.xsl b/webapps/manager/xform.xsl
index b07fcb9..06ced01 100644
--- a/webapps/manager/xform.xsl
+++ b/webapps/manager/xform.xsl
@@ -1,4 +1,4 @@
-<?xml version="1.0"?>
+<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-java/tomcat8.git
More information about the pkg-java-commits
mailing list