Merge "Update notes about password security"
authorFelix Dörre <felix@dogcraft.de>
Sat, 2 Jul 2016 13:57:16 +0000 (15:57 +0200)
committerGerrit Code Review <gigi-system@dogcraft.de>
Sat, 2 Jul 2016 13:57:16 +0000 (15:57 +0200)
431 files changed:
.gitattributes
.gitignore
.settings/org.eclipse.core.resources.prefs [new file with mode: 0644]
Gigi.MF [deleted file]
build.xml
config/.gitignore
doc/scripts/getJetty.sh
lib/jetty/org/eclipse/jetty/http/DateGenerator.java
lib/jetty/org/eclipse/jetty/http/DateParser.java
lib/jetty/org/eclipse/jetty/http/HttpContent.java
lib/jetty/org/eclipse/jetty/http/HttpCookie.java
lib/jetty/org/eclipse/jetty/http/HttpField.java
lib/jetty/org/eclipse/jetty/http/HttpFields.java
lib/jetty/org/eclipse/jetty/http/HttpGenerator.java
lib/jetty/org/eclipse/jetty/http/HttpHeader.java
lib/jetty/org/eclipse/jetty/http/HttpHeaderValue.java
lib/jetty/org/eclipse/jetty/http/HttpMethod.java
lib/jetty/org/eclipse/jetty/http/HttpParser.java
lib/jetty/org/eclipse/jetty/http/HttpScheme.java
lib/jetty/org/eclipse/jetty/http/HttpStatus.java
lib/jetty/org/eclipse/jetty/http/HttpTester.java
lib/jetty/org/eclipse/jetty/http/HttpTokens.java
lib/jetty/org/eclipse/jetty/http/HttpURI.java
lib/jetty/org/eclipse/jetty/http/HttpVersion.java
lib/jetty/org/eclipse/jetty/http/MimeTypes.java
lib/jetty/org/eclipse/jetty/http/PathMap.java
lib/jetty/org/eclipse/jetty/http/mime.properties
lib/jetty/org/eclipse/jetty/http/package-info.java
lib/jetty/org/eclipse/jetty/http/pathmap/MappedResource.java [new file with mode: 0644]
lib/jetty/org/eclipse/jetty/http/pathmap/PathMappings.java [new file with mode: 0644]
lib/jetty/org/eclipse/jetty/http/pathmap/PathSpec.java [new file with mode: 0644]
lib/jetty/org/eclipse/jetty/http/pathmap/PathSpecGroup.java [new file with mode: 0644]
lib/jetty/org/eclipse/jetty/http/pathmap/PathSpecSet.java [new file with mode: 0644]
lib/jetty/org/eclipse/jetty/http/pathmap/RegexPathSpec.java [new file with mode: 0644]
lib/jetty/org/eclipse/jetty/http/pathmap/ServletPathSpec.java [new file with mode: 0644]
lib/jetty/org/eclipse/jetty/http/pathmap/UriTemplatePathSpec.java [new file with mode: 0644]
lib/jetty/org/eclipse/jetty/io/AbstractConnection.java
lib/jetty/org/eclipse/jetty/io/AbstractEndPoint.java
lib/jetty/org/eclipse/jetty/io/ArrayByteBufferPool.java
lib/jetty/org/eclipse/jetty/io/ByteArrayEndPoint.java
lib/jetty/org/eclipse/jetty/io/ByteBufferPool.java
lib/jetty/org/eclipse/jetty/io/ChannelEndPoint.java
lib/jetty/org/eclipse/jetty/io/ClientConnectionFactory.java
lib/jetty/org/eclipse/jetty/io/Connection.java
lib/jetty/org/eclipse/jetty/io/EndPoint.java
lib/jetty/org/eclipse/jetty/io/EofException.java
lib/jetty/org/eclipse/jetty/io/FillInterest.java
lib/jetty/org/eclipse/jetty/io/IdleTimeout.java
lib/jetty/org/eclipse/jetty/io/LeakTrackingByteBufferPool.java
lib/jetty/org/eclipse/jetty/io/MappedByteBufferPool.java
lib/jetty/org/eclipse/jetty/io/NegotiatingClientConnection.java
lib/jetty/org/eclipse/jetty/io/NegotiatingClientConnectionFactory.java
lib/jetty/org/eclipse/jetty/io/NetworkTrafficListener.java
lib/jetty/org/eclipse/jetty/io/NetworkTrafficSelectChannelEndPoint.java
lib/jetty/org/eclipse/jetty/io/RuntimeIOException.java
lib/jetty/org/eclipse/jetty/io/SelectChannelEndPoint.java
lib/jetty/org/eclipse/jetty/io/SelectorManager.java
lib/jetty/org/eclipse/jetty/io/UncheckedPrintWriter.java
lib/jetty/org/eclipse/jetty/io/WriteFlusher.java
lib/jetty/org/eclipse/jetty/io/WriterOutputStream.java
lib/jetty/org/eclipse/jetty/io/package-info.java
lib/jetty/org/eclipse/jetty/io/ssl/SslClientConnectionFactory.java
lib/jetty/org/eclipse/jetty/io/ssl/SslConnection.java
lib/jetty/org/eclipse/jetty/io/ssl/package-info.java
lib/jetty/org/eclipse/jetty/security/AbstractUserAuthentication.java
lib/jetty/org/eclipse/jetty/security/Authenticator.java
lib/jetty/org/eclipse/jetty/security/ConstraintAware.java
lib/jetty/org/eclipse/jetty/security/ConstraintMapping.java
lib/jetty/org/eclipse/jetty/security/ConstraintSecurityHandler.java
lib/jetty/org/eclipse/jetty/security/CrossContextPsuedoSession.java
lib/jetty/org/eclipse/jetty/security/DefaultAuthenticatorFactory.java
lib/jetty/org/eclipse/jetty/security/DefaultIdentityService.java
lib/jetty/org/eclipse/jetty/security/DefaultUserIdentity.java
lib/jetty/org/eclipse/jetty/security/HashCrossContextPsuedoSession.java
lib/jetty/org/eclipse/jetty/security/HashLoginService.java
lib/jetty/org/eclipse/jetty/security/IdentityService.java
lib/jetty/org/eclipse/jetty/security/JDBCLoginService.java
lib/jetty/org/eclipse/jetty/security/LoginService.java
lib/jetty/org/eclipse/jetty/security/MappedLoginService.java
lib/jetty/org/eclipse/jetty/security/PropertyUserStore.java
lib/jetty/org/eclipse/jetty/security/RoleInfo.java
lib/jetty/org/eclipse/jetty/security/RoleRunAsToken.java
lib/jetty/org/eclipse/jetty/security/RunAsToken.java
lib/jetty/org/eclipse/jetty/security/SecurityHandler.java
lib/jetty/org/eclipse/jetty/security/ServerAuthException.java
lib/jetty/org/eclipse/jetty/security/SpnegoLoginService.java
lib/jetty/org/eclipse/jetty/security/SpnegoUserIdentity.java
lib/jetty/org/eclipse/jetty/security/SpnegoUserPrincipal.java
lib/jetty/org/eclipse/jetty/security/UserAuthentication.java
lib/jetty/org/eclipse/jetty/security/UserDataConstraint.java
lib/jetty/org/eclipse/jetty/security/authentication/BasicAuthenticator.java
lib/jetty/org/eclipse/jetty/security/authentication/ClientCertAuthenticator.java
lib/jetty/org/eclipse/jetty/security/authentication/DeferredAuthentication.java
lib/jetty/org/eclipse/jetty/security/authentication/DigestAuthenticator.java
lib/jetty/org/eclipse/jetty/security/authentication/FormAuthenticator.java
lib/jetty/org/eclipse/jetty/security/authentication/LoginAuthenticator.java
lib/jetty/org/eclipse/jetty/security/authentication/LoginCallback.java
lib/jetty/org/eclipse/jetty/security/authentication/LoginCallbackImpl.java
lib/jetty/org/eclipse/jetty/security/authentication/SessionAuthentication.java
lib/jetty/org/eclipse/jetty/security/authentication/SpnegoAuthenticator.java
lib/jetty/org/eclipse/jetty/security/authentication/package-info.java
lib/jetty/org/eclipse/jetty/security/package-info.java
lib/jetty/org/eclipse/jetty/server/AbstractConnectionFactory.java
lib/jetty/org/eclipse/jetty/server/AbstractConnector.java
lib/jetty/org/eclipse/jetty/server/AbstractNCSARequestLog.java
lib/jetty/org/eclipse/jetty/server/AbstractNetworkConnector.java
lib/jetty/org/eclipse/jetty/server/AsyncContextEvent.java
lib/jetty/org/eclipse/jetty/server/AsyncContextState.java
lib/jetty/org/eclipse/jetty/server/AsyncNCSARequestLog.java
lib/jetty/org/eclipse/jetty/server/Authentication.java
lib/jetty/org/eclipse/jetty/server/ByteBufferQueuedHttpInput.java
lib/jetty/org/eclipse/jetty/server/ClassLoaderDump.java
lib/jetty/org/eclipse/jetty/server/ConnectionFactory.java
lib/jetty/org/eclipse/jetty/server/Connector.java
lib/jetty/org/eclipse/jetty/server/ConnectorStatistics.java
lib/jetty/org/eclipse/jetty/server/CookieCutter.java
lib/jetty/org/eclipse/jetty/server/Dispatcher.java
lib/jetty/org/eclipse/jetty/server/EncodingHttpWriter.java
lib/jetty/org/eclipse/jetty/server/ForwardedRequestCustomizer.java
lib/jetty/org/eclipse/jetty/server/Handler.java
lib/jetty/org/eclipse/jetty/server/HandlerContainer.java
lib/jetty/org/eclipse/jetty/server/HomeBaseWarning.java [new file with mode: 0644]
lib/jetty/org/eclipse/jetty/server/HostHeaderCustomizer.java
lib/jetty/org/eclipse/jetty/server/HttpChannel.java
lib/jetty/org/eclipse/jetty/server/HttpChannelState.java
lib/jetty/org/eclipse/jetty/server/HttpConfiguration.java
lib/jetty/org/eclipse/jetty/server/HttpConnection.java
lib/jetty/org/eclipse/jetty/server/HttpConnectionFactory.java
lib/jetty/org/eclipse/jetty/server/HttpInput.java
lib/jetty/org/eclipse/jetty/server/HttpInputOverHTTP.java
lib/jetty/org/eclipse/jetty/server/HttpOutput.java
lib/jetty/org/eclipse/jetty/server/HttpTransport.java
lib/jetty/org/eclipse/jetty/server/HttpWriter.java
lib/jetty/org/eclipse/jetty/server/InclusiveByteRange.java
lib/jetty/org/eclipse/jetty/server/Iso88591HttpWriter.java
lib/jetty/org/eclipse/jetty/server/LocalConnector.java
lib/jetty/org/eclipse/jetty/server/LowResourceMonitor.java
lib/jetty/org/eclipse/jetty/server/NCSARequestLog.java
lib/jetty/org/eclipse/jetty/server/NegotiatingServerConnection.java
lib/jetty/org/eclipse/jetty/server/NegotiatingServerConnectionFactory.java
lib/jetty/org/eclipse/jetty/server/NetworkConnector.java
lib/jetty/org/eclipse/jetty/server/NetworkTrafficServerConnector.java
lib/jetty/org/eclipse/jetty/server/QueuedHttpInput.java
lib/jetty/org/eclipse/jetty/server/QuietServletException.java
lib/jetty/org/eclipse/jetty/server/Request.java
lib/jetty/org/eclipse/jetty/server/RequestLog.java
lib/jetty/org/eclipse/jetty/server/ResourceCache.java
lib/jetty/org/eclipse/jetty/server/Response.java
lib/jetty/org/eclipse/jetty/server/SecureRequestCustomizer.java
lib/jetty/org/eclipse/jetty/server/Server.java
lib/jetty/org/eclipse/jetty/server/ServerConnector.java
lib/jetty/org/eclipse/jetty/server/ServletRequestHttpWrapper.java
lib/jetty/org/eclipse/jetty/server/ServletResponseHttpWrapper.java
lib/jetty/org/eclipse/jetty/server/SessionIdManager.java
lib/jetty/org/eclipse/jetty/server/SessionManager.java
lib/jetty/org/eclipse/jetty/server/ShutdownMonitor.java
lib/jetty/org/eclipse/jetty/server/SslConnectionFactory.java
lib/jetty/org/eclipse/jetty/server/UserIdentity.java
lib/jetty/org/eclipse/jetty/server/Utf8HttpWriter.java
lib/jetty/org/eclipse/jetty/server/handler/AbstractHandler.java
lib/jetty/org/eclipse/jetty/server/handler/AbstractHandlerContainer.java
lib/jetty/org/eclipse/jetty/server/handler/AllowSymLinkAliasChecker.java
lib/jetty/org/eclipse/jetty/server/handler/AsyncDelayHandler.java [new file with mode: 0644]
lib/jetty/org/eclipse/jetty/server/handler/ContextHandler.java
lib/jetty/org/eclipse/jetty/server/handler/ContextHandlerCollection.java
lib/jetty/org/eclipse/jetty/server/handler/DebugHandler.java
lib/jetty/org/eclipse/jetty/server/handler/DefaultHandler.java
lib/jetty/org/eclipse/jetty/server/handler/ErrorHandler.java
lib/jetty/org/eclipse/jetty/server/handler/HandlerCollection.java
lib/jetty/org/eclipse/jetty/server/handler/HandlerList.java
lib/jetty/org/eclipse/jetty/server/handler/HandlerWrapper.java
lib/jetty/org/eclipse/jetty/server/handler/HotSwapHandler.java
lib/jetty/org/eclipse/jetty/server/handler/IPAccessHandler.java
lib/jetty/org/eclipse/jetty/server/handler/IdleTimeoutHandler.java
lib/jetty/org/eclipse/jetty/server/handler/MovedContextHandler.java
lib/jetty/org/eclipse/jetty/server/handler/RequestLogHandler.java
lib/jetty/org/eclipse/jetty/server/handler/ResourceHandler.java
lib/jetty/org/eclipse/jetty/server/handler/ScopedHandler.java
lib/jetty/org/eclipse/jetty/server/handler/SecuredRedirectHandler.java [new file with mode: 0644]
lib/jetty/org/eclipse/jetty/server/handler/ShutdownHandler.java
lib/jetty/org/eclipse/jetty/server/handler/StatisticsHandler.java
lib/jetty/org/eclipse/jetty/server/handler/package-info.java
lib/jetty/org/eclipse/jetty/server/nio/NetworkTrafficSelectChannelConnector.java
lib/jetty/org/eclipse/jetty/server/nio/package-info.java
lib/jetty/org/eclipse/jetty/server/package-info.java
lib/jetty/org/eclipse/jetty/server/session/AbstractSession.java
lib/jetty/org/eclipse/jetty/server/session/AbstractSessionIdManager.java
lib/jetty/org/eclipse/jetty/server/session/AbstractSessionManager.java
lib/jetty/org/eclipse/jetty/server/session/HashSessionIdManager.java
lib/jetty/org/eclipse/jetty/server/session/HashSessionManager.java
lib/jetty/org/eclipse/jetty/server/session/HashedSession.java
lib/jetty/org/eclipse/jetty/server/session/JDBCSessionIdManager.java
lib/jetty/org/eclipse/jetty/server/session/JDBCSessionManager.java
lib/jetty/org/eclipse/jetty/server/session/MemSession.java
lib/jetty/org/eclipse/jetty/server/session/SessionHandler.java
lib/jetty/org/eclipse/jetty/server/session/package-info.java
lib/jetty/org/eclipse/jetty/servlet/BaseHolder.java
lib/jetty/org/eclipse/jetty/servlet/DefaultServlet.java
lib/jetty/org/eclipse/jetty/servlet/ErrorPageErrorHandler.java
lib/jetty/org/eclipse/jetty/servlet/FilterHolder.java
lib/jetty/org/eclipse/jetty/servlet/FilterMapping.java
lib/jetty/org/eclipse/jetty/servlet/Holder.java
lib/jetty/org/eclipse/jetty/servlet/Invoker.java
lib/jetty/org/eclipse/jetty/servlet/JspPropertyGroupServlet.java
lib/jetty/org/eclipse/jetty/servlet/ListenerHolder.java
lib/jetty/org/eclipse/jetty/servlet/NoJspServlet.java
lib/jetty/org/eclipse/jetty/servlet/ServletContextHandler.java
lib/jetty/org/eclipse/jetty/servlet/ServletHandler.java
lib/jetty/org/eclipse/jetty/servlet/ServletHolder.java
lib/jetty/org/eclipse/jetty/servlet/ServletMapping.java
lib/jetty/org/eclipse/jetty/servlet/ServletTester.java
lib/jetty/org/eclipse/jetty/servlet/StatisticsServlet.java
lib/jetty/org/eclipse/jetty/servlet/listener/ELContextCleaner.java
lib/jetty/org/eclipse/jetty/servlet/listener/IntrospectorCleaner.java
lib/jetty/org/eclipse/jetty/servlet/listener/package-info.java
lib/jetty/org/eclipse/jetty/servlet/package-info.java
lib/jetty/org/eclipse/jetty/util/AbstractTrie.java
lib/jetty/org/eclipse/jetty/util/ArrayQueue.java
lib/jetty/org/eclipse/jetty/util/ArrayTernaryTrie.java
lib/jetty/org/eclipse/jetty/util/ArrayTrie.java
lib/jetty/org/eclipse/jetty/util/ArrayUtil.java
lib/jetty/org/eclipse/jetty/util/Atomics.java
lib/jetty/org/eclipse/jetty/util/Attributes.java
lib/jetty/org/eclipse/jetty/util/AttributesMap.java
lib/jetty/org/eclipse/jetty/util/B64Code.java
lib/jetty/org/eclipse/jetty/util/BlockingArrayQueue.java
lib/jetty/org/eclipse/jetty/util/BlockingCallback.java
lib/jetty/org/eclipse/jetty/util/BufferUtil.java
lib/jetty/org/eclipse/jetty/util/ByteArrayISO8859Writer.java
lib/jetty/org/eclipse/jetty/util/ByteArrayOutputStream2.java
lib/jetty/org/eclipse/jetty/util/Callback.java
lib/jetty/org/eclipse/jetty/util/ClassLoadingObjectInputStream.java
lib/jetty/org/eclipse/jetty/util/CompletableCallback.java
lib/jetty/org/eclipse/jetty/util/ConcurrentArrayQueue.java
lib/jetty/org/eclipse/jetty/util/ConcurrentHashSet.java
lib/jetty/org/eclipse/jetty/util/CountingCallback.java [new file with mode: 0644]
lib/jetty/org/eclipse/jetty/util/DateCache.java
lib/jetty/org/eclipse/jetty/util/Fields.java
lib/jetty/org/eclipse/jetty/util/ForkInvoker.java [deleted file]
lib/jetty/org/eclipse/jetty/util/FutureCallback.java
lib/jetty/org/eclipse/jetty/util/FuturePromise.java
lib/jetty/org/eclipse/jetty/util/HostMap.java
lib/jetty/org/eclipse/jetty/util/HttpCookieStore.java
lib/jetty/org/eclipse/jetty/util/IO.java
lib/jetty/org/eclipse/jetty/util/IPAddressMap.java
lib/jetty/org/eclipse/jetty/util/IncludeExclude.java [new file with mode: 0644]
lib/jetty/org/eclipse/jetty/util/IntrospectionUtil.java
lib/jetty/org/eclipse/jetty/util/IteratingCallback.java
lib/jetty/org/eclipse/jetty/util/IteratingNestedCallback.java
lib/jetty/org/eclipse/jetty/util/Jetty.java
lib/jetty/org/eclipse/jetty/util/LazyList.java
lib/jetty/org/eclipse/jetty/util/LeakDetector.java
lib/jetty/org/eclipse/jetty/util/Loader.java
lib/jetty/org/eclipse/jetty/util/MemoryUtils.java
lib/jetty/org/eclipse/jetty/util/MultiException.java
lib/jetty/org/eclipse/jetty/util/MultiMap.java
lib/jetty/org/eclipse/jetty/util/MultiPartInputStreamParser.java
lib/jetty/org/eclipse/jetty/util/MultiPartOutputStream.java
lib/jetty/org/eclipse/jetty/util/MultiPartWriter.java
lib/jetty/org/eclipse/jetty/util/PatternMatcher.java
lib/jetty/org/eclipse/jetty/util/Predicate.java [new file with mode: 0644]
lib/jetty/org/eclipse/jetty/util/Promise.java
lib/jetty/org/eclipse/jetty/util/QuotedStringTokenizer.java
lib/jetty/org/eclipse/jetty/util/ReadLineInputStream.java
lib/jetty/org/eclipse/jetty/util/RegexSet.java [new file with mode: 0644]
lib/jetty/org/eclipse/jetty/util/RolloverFileOutputStream.java
lib/jetty/org/eclipse/jetty/util/Scanner.java
lib/jetty/org/eclipse/jetty/util/SharedBlockingCallback.java
lib/jetty/org/eclipse/jetty/util/SocketAddressResolver.java
lib/jetty/org/eclipse/jetty/util/StringUtil.java
lib/jetty/org/eclipse/jetty/util/TreeTrie.java
lib/jetty/org/eclipse/jetty/util/Trie.java
lib/jetty/org/eclipse/jetty/util/TypeUtil.java
lib/jetty/org/eclipse/jetty/util/URIUtil.java
lib/jetty/org/eclipse/jetty/util/Uptime.java [new file with mode: 0644]
lib/jetty/org/eclipse/jetty/util/UrlEncoded.java
lib/jetty/org/eclipse/jetty/util/Utf8Appendable.java
lib/jetty/org/eclipse/jetty/util/Utf8LineParser.java
lib/jetty/org/eclipse/jetty/util/Utf8StringBuffer.java
lib/jetty/org/eclipse/jetty/util/Utf8StringBuilder.java
lib/jetty/org/eclipse/jetty/util/annotation/ManagedAttribute.java
lib/jetty/org/eclipse/jetty/util/annotation/ManagedObject.java
lib/jetty/org/eclipse/jetty/util/annotation/ManagedOperation.java
lib/jetty/org/eclipse/jetty/util/annotation/Name.java
lib/jetty/org/eclipse/jetty/util/annotation/package-info.java
lib/jetty/org/eclipse/jetty/util/component/AbstractLifeCycle.java
lib/jetty/org/eclipse/jetty/util/component/Container.java
lib/jetty/org/eclipse/jetty/util/component/ContainerLifeCycle.java
lib/jetty/org/eclipse/jetty/util/component/Destroyable.java
lib/jetty/org/eclipse/jetty/util/component/Dumpable.java
lib/jetty/org/eclipse/jetty/util/component/FileDestroyable.java
lib/jetty/org/eclipse/jetty/util/component/FileNoticeLifeCycleListener.java
lib/jetty/org/eclipse/jetty/util/component/Graceful.java
lib/jetty/org/eclipse/jetty/util/component/LifeCycle.java
lib/jetty/org/eclipse/jetty/util/component/package-info.java
lib/jetty/org/eclipse/jetty/util/log/AbstractLogger.java
lib/jetty/org/eclipse/jetty/util/log/JavaUtilLog.java
lib/jetty/org/eclipse/jetty/util/log/Log.java
lib/jetty/org/eclipse/jetty/util/log/Logger.java
lib/jetty/org/eclipse/jetty/util/log/LoggerLog.java
lib/jetty/org/eclipse/jetty/util/log/StacklessLogging.java
lib/jetty/org/eclipse/jetty/util/log/StdErrLog.java
lib/jetty/org/eclipse/jetty/util/log/package-info.java
lib/jetty/org/eclipse/jetty/util/package-info.java
lib/jetty/org/eclipse/jetty/util/preventers/AWTLeakPreventer.java
lib/jetty/org/eclipse/jetty/util/preventers/AbstractLeakPreventer.java
lib/jetty/org/eclipse/jetty/util/preventers/AppContextLeakPreventer.java
lib/jetty/org/eclipse/jetty/util/preventers/DOMLeakPreventer.java
lib/jetty/org/eclipse/jetty/util/preventers/DriverManagerLeakPreventer.java
lib/jetty/org/eclipse/jetty/util/preventers/GCThreadLeakPreventer.java
lib/jetty/org/eclipse/jetty/util/preventers/Java2DLeakPreventer.java
lib/jetty/org/eclipse/jetty/util/preventers/LDAPLeakPreventer.java
lib/jetty/org/eclipse/jetty/util/preventers/LoginConfigurationLeakPreventer.java
lib/jetty/org/eclipse/jetty/util/preventers/SecurityProviderLeakPreventer.java
lib/jetty/org/eclipse/jetty/util/preventers/package-info.java
lib/jetty/org/eclipse/jetty/util/resource/BadResource.java
lib/jetty/org/eclipse/jetty/util/resource/EmptyResource.java
lib/jetty/org/eclipse/jetty/util/resource/FileResource.java
lib/jetty/org/eclipse/jetty/util/resource/JarFileResource.java
lib/jetty/org/eclipse/jetty/util/resource/JarResource.java
lib/jetty/org/eclipse/jetty/util/resource/PathResource.java [new file with mode: 0644]
lib/jetty/org/eclipse/jetty/util/resource/Resource.java
lib/jetty/org/eclipse/jetty/util/resource/ResourceCollection.java
lib/jetty/org/eclipse/jetty/util/resource/ResourceFactory.java
lib/jetty/org/eclipse/jetty/util/resource/URLResource.java
lib/jetty/org/eclipse/jetty/util/resource/package-info.java
lib/jetty/org/eclipse/jetty/util/security/CertificateUtils.java
lib/jetty/org/eclipse/jetty/util/security/CertificateValidator.java
lib/jetty/org/eclipse/jetty/util/security/Constraint.java
lib/jetty/org/eclipse/jetty/util/security/Credential.java
lib/jetty/org/eclipse/jetty/util/security/Password.java
lib/jetty/org/eclipse/jetty/util/security/package-info.java
lib/jetty/org/eclipse/jetty/util/ssl/AliasedX509ExtendedKeyManager.java
lib/jetty/org/eclipse/jetty/util/ssl/AliasedX509KeyManager.java
lib/jetty/org/eclipse/jetty/util/ssl/SslContextFactory.java
lib/jetty/org/eclipse/jetty/util/ssl/package-info.java
lib/jetty/org/eclipse/jetty/util/statistic/CounterStatistic.java
lib/jetty/org/eclipse/jetty/util/statistic/SampleStatistic.java
lib/jetty/org/eclipse/jetty/util/statistic/package-info.java
lib/jetty/org/eclipse/jetty/util/thread/ExecutorThreadPool.java
lib/jetty/org/eclipse/jetty/util/thread/NonBlockingThread.java
lib/jetty/org/eclipse/jetty/util/thread/QueuedThreadPool.java
lib/jetty/org/eclipse/jetty/util/thread/ScheduledExecutorScheduler.java
lib/jetty/org/eclipse/jetty/util/thread/Scheduler.java
lib/jetty/org/eclipse/jetty/util/thread/ShutdownThread.java
lib/jetty/org/eclipse/jetty/util/thread/SpinLock.java [new file with mode: 0644]
lib/jetty/org/eclipse/jetty/util/thread/Sweeper.java [new file with mode: 0644]
lib/jetty/org/eclipse/jetty/util/thread/ThreadPool.java
lib/jetty/org/eclipse/jetty/util/thread/TimerScheduler.java
lib/jetty/org/eclipse/jetty/util/thread/package-info.java
src/org/cacert/gigi/Gigi.java
src/org/cacert/gigi/Gigi.templ
src/org/cacert/gigi/Launcher.java
src/org/cacert/gigi/api/CATSImport.java
src/org/cacert/gigi/api/GigiAPI.java
src/org/cacert/gigi/crypto/SMIME.java
src/org/cacert/gigi/database/DatabaseConnection.java
src/org/cacert/gigi/database/SQLFileManager.java
src/org/cacert/gigi/database/tableStructure.sql
src/org/cacert/gigi/database/upgrade/from_10.sql [new file with mode: 0644]
src/org/cacert/gigi/database/upgrade/from_11.sql [new file with mode: 0644]
src/org/cacert/gigi/database/upgrade/from_12.sql [new file with mode: 0644]
src/org/cacert/gigi/database/upgrade/from_13.sql [new file with mode: 0644]
src/org/cacert/gigi/database/upgrade/from_14.sql [new file with mode: 0644]
src/org/cacert/gigi/database/upgrade/from_9.sql [new file with mode: 0644]
src/org/cacert/gigi/dbObjects/CATS.java
src/org/cacert/gigi/dbObjects/Domain.java
src/org/cacert/gigi/dbObjects/DomainPingConfiguration.java
src/org/cacert/gigi/dbObjects/DomainPingType.java
src/org/cacert/gigi/dbObjects/EmailAddress.java
src/org/cacert/gigi/dbObjects/Organisation.java
src/org/cacert/gigi/dbObjects/User.java
src/org/cacert/gigi/email/EmailProvider.java
src/org/cacert/gigi/output/template/SprintfCommand.java
src/org/cacert/gigi/output/template/Template.java
src/org/cacert/gigi/pages/AboutPage.java [new file with mode: 0644]
src/org/cacert/gigi/pages/AboutPage.templ [new file with mode: 0644]
src/org/cacert/gigi/pages/LoginPage.java
src/org/cacert/gigi/pages/PasswordResetPage.java
src/org/cacert/gigi/pages/account/certs/CertificateAdd.java
src/org/cacert/gigi/pages/account/domain/DomainManagementForm.java
src/org/cacert/gigi/pages/account/domain/DomainManagementForm.templ
src/org/cacert/gigi/pages/account/domain/DomainOverview.java
src/org/cacert/gigi/pages/account/domain/DomainPinglogForm.templ
src/org/cacert/gigi/pages/account/domain/PingConfigForm.templ
src/org/cacert/gigi/pages/account/mail/MailManagementForm.java
src/org/cacert/gigi/pages/account/mail/MailManagementForm.templ
src/org/cacert/gigi/pages/account/mail/MailOverview.java
src/org/cacert/gigi/pages/admin/support/SupportUserDetailsForm.java
src/org/cacert/gigi/pages/main/RegisterPage.java
src/org/cacert/gigi/pages/main/Signup.java
src/org/cacert/gigi/pages/orga/CreateOrgForm.java
src/org/cacert/gigi/pages/orga/CreateOrgForm.templ
src/org/cacert/gigi/pages/orga/EditOrg.templ
src/org/cacert/gigi/pages/orga/OrgDomainAddForm.java
src/org/cacert/gigi/pages/orga/OrgDomainAddForm.templ
src/org/cacert/gigi/pages/orga/ViewOrgPage.java
src/org/cacert/gigi/pages/wot/AssuranceForm.java
src/org/cacert/gigi/ping/PingerDaemon.java
src/org/cacert/gigi/ping/SSLPinger.java
src/org/cacert/gigi/util/DNSUtil.java
src/org/cacert/gigi/util/PasswordHash.java
static/static/css/cacert.css
static/static/js/expert.js [new file with mode: 0644]
static/static/js/localDate.js [new file with mode: 0644]
static/static/menu.js [deleted file]
tests/com/lambdaworks/crypto/test/CryptoTestUtil.java
tests/com/lambdaworks/crypto/test/PBKDFTest.java
tests/com/lambdaworks/crypto/test/SCryptTest.java
tests/com/lambdaworks/crypto/test/SCryptUtilTest.java
tests/org/cacert/gigi/TestDomain.java
tests/org/cacert/gigi/TestLanguage.java
tests/org/cacert/gigi/TestOrga.java
tests/org/cacert/gigi/api/ImportCATSResult.java
tests/org/cacert/gigi/api/IssueCert.java
tests/org/cacert/gigi/email/TestEmailProviderClass.java
tests/org/cacert/gigi/pages/account/TestCertificateAdd.java
tests/org/cacert/gigi/pages/account/TestChangePassword.java
tests/org/cacert/gigi/pages/account/TestMailManagement.java
tests/org/cacert/gigi/pages/orga/TestOrgDomain.java [new file with mode: 0644]
tests/org/cacert/gigi/pages/orga/TestOrgManagement.java [moved from tests/org/cacert/gigi/pages/orga/TestOrgaManagement.java with 74% similarity]
tests/org/cacert/gigi/pages/wot/TestAssurance.java
tests/org/cacert/gigi/ping/TestSSL.java
tests/org/cacert/gigi/testUtils/ConfiguredTest.java
tests/org/cacert/gigi/testUtils/ManagedTest.java
tests/org/cacert/gigi/testUtils/OrgTest.java [new file with mode: 0644]
util-testing/org/cacert/gigi/DevelLauncher.java
util-testing/org/cacert/gigi/pages/Manager.java
util-testing/org/cacert/gigi/pages/Manager.templ
util-testing/org/cacert/gigi/util/SimpleSigner.java
util/org/cacert/gigi/util/FetchLocales.java

index 9535ab1..10c31b9 100644 (file)
@@ -1,6 +1,6 @@
 *      text=auto
 
-*.java text
+*.java text diff=java
 
 *.txt  text
 *.md   text
index 0c170e1..6746097 100644 (file)
 /bin
 /bintest
 /binutil
+/binutil-testing
+/lib/bin
 /work
 static.tar.gz
 
 /src/org/cacert/gigi/util/effective_tld_names.dat
+/Gigi.MF
+/testKeypair
+
+/signer
diff --git a/.settings/org.eclipse.core.resources.prefs b/.settings/org.eclipse.core.resources.prefs
new file mode 100644 (file)
index 0000000..99f26c0
--- /dev/null
@@ -0,0 +1,2 @@
+eclipse.preferences.version=1
+encoding/<project>=UTF-8
diff --git a/Gigi.MF b/Gigi.MF
deleted file mode 100644 (file)
index 614d595..0000000
--- a/Gigi.MF
+++ /dev/null
@@ -1,3 +0,0 @@
-Manifest-Version: 1.0
-Main-Class: org.cacert.gigi.Launcher
-
index be1b127..6816b7d 100644 (file)
--- a/build.xml
+++ b/build.xml
        <target depends="clean,clean-test" name="cleanall" />
        <target depends="build-project, build-testing, native" name="build" />
        <target depends="init" name="build-project">
+               <exec outputproperty="git-version" executable="git">
+                       <arg line="rev-parse"/>
+                       <arg line="HEAD"/>
+               </exec>
+               <manifest file="Gigi.MF">
+                       <attribute name="Main-Class" value="org.cacert.gigi.Launcher" />
+                       <attribute name="Implementation-Version" value="${git-version}" />
+               </manifest>
                <echo message="${ant.project.name}: ${ant.file}" />
                <javac encoding="UTF-8" debug="true" debuglevel="${debuglevel}" destdir="bin"
                        includeantruntime="false" source="${source}" target="${target}">
index 59f2d87..8bec870 100644 (file)
@@ -3,3 +3,6 @@ keystore.pkcs12
 cacerts.jks
 gigi.properties
 test.properties
+/profiles
+/ca
+/keys
index cbec366..784a6f1 100644 (file)
@@ -1,4 +1,5 @@
 #!/bin/sh
+set -e
 JETTY=C:/jars/jetty-distribution-9.1.0.RC0/org.eclipse.jetty.project
 
 pushd ../../lib/jetty/org/eclipse/jetty
@@ -6,7 +7,7 @@ rm -fR *
 
 pushd $JETTY
 
-git checkout refs/tags/jetty-9.2.1.v20140609
+git checkout refs/tags/jetty-9.2.16.v20160407
 popd
 
 
index 3ecaad8..e38d3c6 100644 (file)
@@ -1,6 +1,6 @@
 //
 //  ========================================================================
-//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+//  Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
 //  ------------------------------------------------------------------------
 //  All rights reserved. This program and the accompanying materials
 //  are made available under the terms of the Eclipse Public License v1.0
index 1ede4ce..f181ce1 100644 (file)
@@ -1,6 +1,6 @@
 //
 //  ========================================================================
-//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+//  Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
 //  ------------------------------------------------------------------------
 //  All rights reserved. This program and the accompanying materials
 //  are made available under the terms of the Eclipse Public License v1.0
index ebae56d..ba09880 100644 (file)
@@ -1,6 +1,6 @@
 //
 //  ========================================================================
-//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+//  Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
 //  ------------------------------------------------------------------------
 //  All rights reserved. This program and the accompanying materials
 //  are made available under the terms of the Eclipse Public License v1.0
@@ -168,5 +168,11 @@ public interface HttpContent
         {
             _resource.close();
         }
+        
+        @Override
+        public String toString()
+        {
+            return String.format("%s@%x{r=%s}",this.getClass().getSimpleName(),hashCode(),_resource);
+        }
     }
 }
index 1a2426b..2d769df 100644 (file)
@@ -1,6 +1,6 @@
 //
 //  ========================================================================
-//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+//  Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
 //  ------------------------------------------------------------------------
 //  All rights reserved. This program and the accompanying materials
 //  are made available under the terms of the Eclipse Public License v1.0
index 50f29b1..55ab65c 100644 (file)
@@ -1,6 +1,6 @@
 //
 //  ========================================================================
-//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+//  Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
 //  ------------------------------------------------------------------------
 //  All rights reserved. This program and the accompanying materials
 //  are made available under the terms of the Eclipse Public License v1.0
index d474227..6bec4d9 100644 (file)
@@ -1,6 +1,6 @@
 //
 //  ========================================================================
-//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+//  Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
 //  ------------------------------------------------------------------------
 //  All rights reserved. This program and the accompanying materials
 //  are made available under the terms of the Eclipse Public License v1.0
@@ -171,6 +171,17 @@ public class HttpFields implements Iterable<HttpField>
 
         return false;
     }
+
+    public boolean contains(HttpHeader header)
+    {
+        for (int i=0;i<_fields.size();i++)
+        {
+            HttpField f=_fields.get(i);
+            if (f.getHeader()==header)
+                return true;
+        }
+        return false;
+    }
     
     public boolean containsKey(String name)
     {
@@ -182,7 +193,8 @@ public class HttpFields implements Iterable<HttpField>
         }
         return false;
     }
-
+    
+    
     public String getStringField(HttpHeader header)
     {
         return getStringField(header.asString());
index a51e4ba..6f7ead3 100644 (file)
@@ -1,6 +1,6 @@
 //
 //  ========================================================================
-//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+//  Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
 //  ------------------------------------------------------------------------
 //  All rights reserved. This program and the accompanying materials
 //  are made available under the terms of the Eclipse Public License v1.0
@@ -23,6 +23,8 @@ import java.nio.BufferOverflowException;
 import java.nio.ByteBuffer;
 import java.nio.charset.StandardCharsets;
 import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
 
 import org.eclipse.jetty.http.HttpTokens.EndOfContent;
 import org.eclipse.jetty.util.BufferUtil;
@@ -70,8 +72,8 @@ public class HttpGenerator
     private final int _send;
     private final static int SEND_SERVER = 0x01;
     private final static int SEND_XPOWEREDBY = 0x02;
-
-
+    private final static Set<String> __assumedContentMethods = new HashSet<>(Arrays.asList(new String[]{HttpMethod.POST.asString(),HttpMethod.PUT.asString()}));
+  
     /* ------------------------------------------------------------------------------- */
     public static void setJettyVersion(String serverVersion)
     {
@@ -158,6 +160,12 @@ public class HttpGenerator
         return _endOfContent==EndOfContent.CHUNKED_CONTENT;
     }
 
+    /* ------------------------------------------------------------ */
+    public boolean isNoContent()
+    {
+        return _noContent;
+    }
+    
     /* ------------------------------------------------------------ */
     public void setPersistent(boolean persistent)
     {
@@ -287,7 +295,8 @@ public class HttpGenerator
             {
                 if (BufferUtil.hasContent(content))
                 {
-                    LOG.debug("discarding content in COMPLETING");
+                    if (LOG.isDebugEnabled())
+                        LOG.debug("discarding content in COMPLETING");
                     BufferUtil.clear(content);
                 }
 
@@ -310,7 +319,8 @@ public class HttpGenerator
             case END:
                 if (BufferUtil.hasContent(content))
                 {
-                    LOG.debug("discarding content in COMPLETING");
+                    if (LOG.isDebugEnabled())
+                        LOG.debug("discarding content in COMPLETING");
                     BufferUtil.clear(content);
                 }
                 return Result.DONE;
@@ -436,7 +446,8 @@ public class HttpGenerator
             {
                 if (BufferUtil.hasContent(content))
                 {
-                    LOG.debug("discarding content in COMPLETING");
+                    if (LOG.isDebugEnabled())
+                        LOG.debug("discarding content in COMPLETING");
                     BufferUtil.clear(content);
                 }
 
@@ -462,7 +473,8 @@ public class HttpGenerator
             case END:
                 if (BufferUtil.hasContent(content))
                 {
-                    LOG.debug("discarding content in COMPLETING");
+                    if (LOG.isDebugEnabled())
+                        LOG.debug("discarding content in COMPLETING");
                     BufferUtil.clear(content);
                 }
                 return Result.DONE;
@@ -621,7 +633,7 @@ public class HttpGenerator
 
                         if (values[0]==null)
                         {
-                            split = field.getValue().split("\\s*,\\s*");
+                            split = StringUtil.csvSplit(field.getValue());
                             if (split.length>0)
                             {
                                 values=new HttpHeaderValue[split.length];
@@ -735,12 +747,17 @@ public class HttpGenerator
                     long content_length = _contentPrepared+BufferUtil.length(content);
 
                     // Do we need to tell the headers about it
-                    if ((response!=null || content_length>0 || content_type ) && !_noContent)
+                    if (content_length>0)
                     {
                         header.put(HttpHeader.CONTENT_LENGTH.getBytesColonSpace());
                         BufferUtil.putDecLong(header, content_length);
                         header.put(HttpTokens.CRLF);
                     }
+                    else if (!_noContent)
+                    {
+                        if (content_type || response!=null || (request!=null && __assumedContentMethods.contains(request.getMethod())))
+                            header.put(CONTENT_LENGTH_0);
+                    }
                 }
                 else
                 {
@@ -757,19 +774,21 @@ public class HttpGenerator
 
             case CONTENT_LENGTH:
                 long content_length = _info.getContentLength();
-                if ((response!=null || content_length>0 || content_type ) && !_noContent)
+                if (content_length>0)
                 {
-                    // known length but not actually set.
                     header.put(HttpHeader.CONTENT_LENGTH.getBytesColonSpace());
                     BufferUtil.putDecLong(header, content_length);
                     header.put(HttpTokens.CRLF);
                 }
+                else if (!_noContent)
+                {
+                    if (content_type || response!=null || (request!=null && __assumedContentMethods.contains(request.getMethod())))
+                        header.put(CONTENT_LENGTH_0);
+                }
                 break;
 
             case NO_CONTENT:
-                if (response!=null && status >= 200 && status != 204 && status != 304)
-                    header.put(CONTENT_LENGTH_0);
-                break;
+                throw new IllegalStateException();
 
             case EOF_CONTENT:
                 _persistent = request!=null;
@@ -1042,7 +1061,7 @@ public class HttpGenerator
             char c=s.charAt(i);
             
             if (c<0 || c>0xff || c=='\r' || c=='\n')
-                buffer.put((byte)'?');
+                buffer.put((byte)' ');
             else
                 buffer.put((byte)(0xff&c));
         }
index ab0ddcf..a62f5f2 100644 (file)
@@ -1,6 +1,6 @@
 //
 //  ========================================================================
-//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+//  Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
 //  ------------------------------------------------------------------------
 //  All rights reserved. This program and the accompanying materials
 //  are made available under the terms of the Eclipse Public License v1.0
index 8338a32..7130100 100644 (file)
@@ -1,6 +1,6 @@
 //
 //  ========================================================================
-//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+//  Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
 //  ------------------------------------------------------------------------
 //  All rights reserved. This program and the accompanying materials
 //  are made available under the terms of the Eclipse Public License v1.0
index 8a26268..1d2eeda 100644 (file)
@@ -1,6 +1,6 @@
 //
 //  ========================================================================
-//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+//  Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
 //  ------------------------------------------------------------------------
 //  All rights reserved. This program and the accompanying materials
 //  are made available under the terms of the Eclipse Public License v1.0
@@ -39,11 +39,12 @@ public enum HttpMethod
     TRACE,
     CONNECT,
     MOVE,
-    PROXY;
+    PROXY,
+    PRI;
 
     /* ------------------------------------------------------------ */
     /**
-     * Optimised lookup to find a method name and trailing space in a byte array.
+     * Optimized lookup to find a method name and trailing space in a byte array.
      * @param bytes Array containing ISO-8859-1 characters
      * @param position The first valid index
      * @param limit The first non valid index
@@ -67,33 +68,35 @@ public enum HttpMethod
                     return PROXY;
                 if (bytes[position+1]=='U' && bytes[position+2]=='T' && bytes[position+3]==' ')
                     return PUT;
+                if (bytes[position+1]=='R' && bytes[position+2]=='I' && bytes[position+3]==' ')
+                    return PRI;
                 break;
             case 'H':
                 if (bytes[position+1]=='E' && bytes[position+2]=='A' && bytes[position+3]=='D' && length>=5 && bytes[position+4]==' ')
                     return HEAD;
                 break;
             case 'O':
-                if (bytes[position+1]=='O' && bytes[position+2]=='T' && bytes[position+3]=='I' && length>=8 &&
-                bytes[position+4]=='O' && bytes[position+5]=='N' && bytes[position+6]=='S' && bytes[position+7]==' ' )
+                if (bytes[position+1]=='P' && bytes[position+2]=='T' && bytes[position+3]=='I' && length>=8 &&
+                    bytes[position+4]=='O' && bytes[position+5]=='N' && bytes[position+6]=='S' && bytes[position+7]==' ' )
                     return OPTIONS;
                 break;
             case 'D':
                 if (bytes[position+1]=='E' && bytes[position+2]=='L' && bytes[position+3]=='E' && length>=7 &&
-                bytes[position+4]=='T' && bytes[position+5]=='E' && bytes[position+6]==' ' )
+                    bytes[position+4]=='T' && bytes[position+5]=='E' && bytes[position+6]==' ' )
                     return DELETE;
                 break;
             case 'T':
                 if (bytes[position+1]=='R' && bytes[position+2]=='A' && bytes[position+3]=='C' && length>=6 &&
-                bytes[position+4]=='E' && bytes[position+5]==' ' )
+                    bytes[position+4]=='E' && bytes[position+5]==' ' )
                     return TRACE;
                 break;
             case 'C':
                 if (bytes[position+1]=='O' && bytes[position+2]=='N' && bytes[position+3]=='N' && length>=8 &&
-                bytes[position+4]=='E' && bytes[position+5]=='C' && bytes[position+6]=='T' && bytes[position+7]==' ' )
+                    bytes[position+4]=='E' && bytes[position+5]=='C' && bytes[position+6]=='T' && bytes[position+7]==' ' )
                     return CONNECT;
                 break;
             case 'M':
-                if (bytes[position+1]=='O' && bytes[position+2]=='V' && bytes[position+3]=='E' &&  bytes[position+4]==' ')
+                if (bytes[position+1]=='O' && bytes[position+2]=='V' && bytes[position+3]=='E' && length>=5 && bytes[position+4]==' ')
                     return MOVE;
                 break;
 
@@ -105,17 +108,26 @@ public enum HttpMethod
 
     /* ------------------------------------------------------------ */
     /**
-     * Optimised lookup to find a method name and trailing space in a byte array.
-     * @param buffer buffer containing ISO-8859-1 characters
+     * Optimized lookup to find a method name and trailing space in a byte array.
+     * @param buffer buffer containing ISO-8859-1 characters, it is not modified.
      * @return A HttpMethod if a match or null if no easy match.
      */
     public static HttpMethod lookAheadGet(ByteBuffer buffer)
     {
         if (buffer.hasArray())
             return lookAheadGet(buffer.array(),buffer.arrayOffset()+buffer.position(),buffer.arrayOffset()+buffer.limit());
-
-        // TODO use cache and check for space
-        // return CACHE.getBest(buffer,0,buffer.remaining());
+        
+        int l = buffer.remaining();
+        if (l>=4)
+        {
+            HttpMethod m = CACHE.getBest(buffer,0,l);
+            if (m!=null)
+            {
+                int ml = m.asString().length();
+                if (l>ml && buffer.get(buffer.position()+ml)==' ')
+                    return m;
+            }
+        }
         return null;
     }
 
@@ -162,6 +174,7 @@ public enum HttpMethod
         return toString();
     }
 
+    /* ------------------------------------------------------------ */
     /**
      * Converts the given String parameter to an HttpMethod
      * @param method the String to get the equivalent HttpMethod from
index 79f1c28..e1ddee8 100644 (file)
@@ -1,6 +1,6 @@
 //
 //  ========================================================================
-//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+//  Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
 //  ------------------------------------------------------------------------
 //  All rights reserved. This program and the accompanying materials
 //  are made available under the terms of the Eclipse Public License v1.0
@@ -18,6 +18,8 @@
 
 package org.eclipse.jetty.http;
 
+import static org.eclipse.jetty.http.HttpTokens.*;
+
 import java.nio.ByteBuffer;
 import java.nio.charset.StandardCharsets;
 
@@ -35,7 +37,7 @@ import org.eclipse.jetty.util.log.Logger;
 /* ------------------------------------------------------------ */
 /** A Parser for HTTP 0.9, 1.0 and 1.1
  * <p>
- * This parser parses HTTP client and server messages from buffers
+ * This parser parses HTTP client and server messages from buffers
  * passed in the {@link #parseNext(ByteBuffer)} method.  The parsed
  * elements of the HTTP message are passed as event calls to the 
  * {@link HttpHandler} instance the parser is constructed with.
@@ -115,6 +117,7 @@ public class HttpParser
         CHUNK_SIZE,
         CHUNK_PARAMS,
         CHUNK,
+        CHUNK_END,
         END,
         CLOSED
     }
@@ -329,33 +332,31 @@ public class HttpParser
     }
 
     /* ------------------------------------------------------------------------------- */
-    private static class BadMessage extends Error
+    @SuppressWarnings("serial")
+    private static class BadMessageException extends RuntimeException
     {
-        private static final long serialVersionUID = 1L;
         private final int _code;
-        private final String _message;
 
-        BadMessage()
+        private BadMessageException()
         {
             this(400,null);
         }
         
-        BadMessage(int code)
+        private BadMessageException(int code)
         {
             this(code,null);
         }
         
-        BadMessage(String message)
+        private BadMessageException(String message)
         {
             this(400,message);
         }
         
-        BadMessage(int code,String message)
+        private BadMessageException(int code,String message)
         {
+            super(message);
             _code=code;
-            _message=message;
         }
-        
     }
     
     /* ------------------------------------------------------------------------------- */
@@ -365,23 +366,23 @@ public class HttpParser
         
         if (_cr)
         {
-            if (ch!=HttpTokens.LINE_FEED)
-                throw new BadMessage("Bad EOL");
+            if (ch!=LINE_FEED)
+                throw new BadMessageException("Bad EOL");
             _cr=false;
             return ch;
         }
 
-        if (ch>=0 && ch<HttpTokens.SPACE)
+        if (ch>=0 && ch<SPACE)
         {
-            if (ch==HttpTokens.CARRIAGE_RETURN)
+            if (ch==CARRIAGE_RETURN)
             {
                 if (buffer.hasRemaining())
                 {
                     if(_maxHeaderBytes>0 && _state.ordinal()<State.END.ordinal())
                         _headerBytes++;
                     ch=buffer.get();
-                    if (ch!=HttpTokens.LINE_FEED)
-                        throw new BadMessage("Bad EOL");
+                    if (ch!=LINE_FEED)
+                        throw new BadMessageException("Bad EOL");
                 }
                 else
                 {
@@ -392,8 +393,8 @@ public class HttpParser
                 }
             }
             // Only LF or TAB acceptable special characters
-            else if (!(ch==HttpTokens.LINE_FEED || ch==HttpTokens.TAB))
-                throw new BadMessage("Illegal character");
+            else if (!(ch==LINE_FEED || ch==TAB))
+                throw new IllegalCharacterException(_state,ch,buffer);
         }
         
         return ch;
@@ -433,7 +434,7 @@ public class HttpParser
         {
             int ch=next(buffer);
 
-            if (ch > HttpTokens.SPACE)
+            if (ch > SPACE)
             {
                 _string.setLength(0);
                 _string.append((char)ch);
@@ -443,13 +444,13 @@ public class HttpParser
             else if (ch==0)
                 break;
             else if (ch<0)
-                throw new BadMessage();
+                throw new BadMessageException();
             
             // count this white space as a header byte to avoid DOS
             if (_maxHeaderBytes>0 && ++_headerBytes>_maxHeaderBytes)
             {
                 LOG.warn("padding is too large >"+_maxHeaderBytes);
-                throw new BadMessage(HttpStatus.BAD_REQUEST_400);
+                throw new BadMessageException(HttpStatus.BAD_REQUEST_400);
             }
         }
         return false;
@@ -493,7 +494,7 @@ public class HttpParser
                 if (_state==State.URI)
                 {
                     LOG.warn("URI is too large >"+_maxHeaderBytes);
-                    throw new BadMessage(HttpStatus.REQUEST_URI_TOO_LONG_414);
+                    throw new BadMessageException(HttpStatus.REQUEST_URI_TOO_LONG_414);
                 }
                 else
                 {
@@ -501,14 +502,14 @@ public class HttpParser
                         LOG.warn("request is too large >"+_maxHeaderBytes);
                     else
                         LOG.warn("response is too large >"+_maxHeaderBytes);
-                    throw new BadMessage(HttpStatus.REQUEST_ENTITY_TOO_LARGE_413);
+                    throw new BadMessageException(HttpStatus.REQUEST_ENTITY_TOO_LARGE_413);
                 }
             }
 
             switch (_state)
             {
                 case METHOD:
-                    if (ch == HttpTokens.SPACE)
+                    if (ch == SPACE)
                     {
                         _length=_string.length();
                         _methodString=takeString();
@@ -517,8 +518,13 @@ public class HttpParser
                             _methodString=method.asString();
                         setState(State.SPACE1);
                     }
-                    else if (ch < HttpTokens.SPACE)
-                        throw new BadMessage(ch<0?"Illegal character":"No URI");
+                    else if (ch < SPACE)
+                    {
+                        if (ch==LINE_FEED)
+                            throw new BadMessageException("No URI");
+                        else
+                            throw new IllegalCharacterException(_state,ch,buffer);
+                    }
                     else
                         _string.append((char)ch);
                     break;
@@ -530,11 +536,11 @@ public class HttpParser
                         String version=takeString();
                         _version=HttpVersion.CACHE.get(version);
                         if (_version==null)
-                            throw new BadMessage(HttpStatus.BAD_REQUEST_400,"Unknown Version");
+                            throw new BadMessageException(HttpStatus.BAD_REQUEST_400,"Unknown Version");
                         setState(State.SPACE1);
                     }
                     else if (ch < HttpTokens.SPACE)
-                        throw new BadMessage(ch<0?"Illegal character":"No Status");
+                        throw new IllegalCharacterException(_state,ch,buffer);
                     else
                         _string.append((char)ch);
                     break;
@@ -567,7 +573,7 @@ public class HttpParser
                                 if (_maxHeaderBytes>0 && ++_headerBytes>_maxHeaderBytes)
                                 {
                                     LOG.warn("URI is too large >"+_maxHeaderBytes);
-                                    throw new BadMessage(HttpStatus.REQUEST_URI_TOO_LONG_414);
+                                    throw new BadMessageException(HttpStatus.REQUEST_URI_TOO_LONG_414);
                                 }
                                 if (_uri.remaining()<=len)
                                 {
@@ -585,7 +591,7 @@ public class HttpParser
                     }
                     else if (ch < HttpTokens.SPACE)
                     {
-                        throw new BadMessage(HttpStatus.BAD_REQUEST_400,_requestHandler!=null?"No URI":"No Status");
+                        throw new BadMessageException(HttpStatus.BAD_REQUEST_400,_requestHandler!=null?"No URI":"No Status");
                     }
                     break;
 
@@ -605,7 +611,7 @@ public class HttpParser
                     }
                     else
                     {
-                        throw new BadMessage();
+                        throw new BadMessageException();
                     }
                     break;
 
@@ -623,6 +629,7 @@ public class HttpParser
                         BufferUtil.clear(buffer);
                         handle=_handler.headerComplete()||handle;
                         handle=_handler.messageComplete()||handle;
+                        return handle;
                     }
                     else
                     {
@@ -662,7 +669,7 @@ public class HttpParser
                                 if (_method==HttpMethod.PROXY)
                                 {
                                     if (!(_requestHandler instanceof ProxyHandler))
-                                        throw new BadMessage();
+                                        throw new BadMessageException();
                                     
                                     _uri.flip();
                                     String protocol=BufferUtil.toString(_uri);
@@ -718,10 +725,11 @@ public class HttpParser
                             BufferUtil.clear(buffer);
                             handle=_handler.headerComplete()||handle;
                             handle=_handler.messageComplete()||handle;
+                            return handle;
                         }
                     }
                     else if (ch<0)
-                        throw new BadMessage();
+                        throw new BadMessageException();
                     break;
 
                 case REQUEST_VERSION:
@@ -733,7 +741,7 @@ public class HttpParser
                             _version=HttpVersion.CACHE.get(takeString());
                         }
                         if (_version==null)
-                            throw new BadMessage(HttpStatus.BAD_REQUEST_400,"Unknown Version");
+                            throw new BadMessageException(HttpStatus.BAD_REQUEST_400,"Unknown Version");
                         
                         // Should we try to cache header fields?
                         if (_connectionFields==null && _version.getVersion()>=HttpVersion.HTTP_1_1.getVersion())
@@ -750,7 +758,7 @@ public class HttpParser
                     else if (ch>=HttpTokens.SPACE)
                         _string.append((char)ch);
                     else
-                        throw new BadMessage();
+                        throw new BadMessageException();
 
                     break;
 
@@ -770,7 +778,7 @@ public class HttpParser
                             _length=_string.length();
                     } 
                     else
-                        throw new BadMessage();
+                        throw new BadMessageException();
                     break;
 
                 default:
@@ -797,7 +805,7 @@ public class HttpParser
                     catch(NumberFormatException e)
                     {
                         LOG.ignore(e);
-                        throw new BadMessage(HttpStatus.BAD_REQUEST_400,"Bad Content-Length");
+                        throw new BadMessageException(HttpStatus.BAD_REQUEST_400,"Bad Content-Length");
                     }
                     if (_contentLength <= 0)
                         _endOfContent=EndOfContent.NO_CONTENT;
@@ -815,7 +823,7 @@ public class HttpParser
                         _endOfContent=EndOfContent.CHUNKED_CONTENT;
                     else if (_valueString.contains(HttpHeaderValue.CHUNKED.toString()))
                     {
-                        throw new BadMessage(HttpStatus.BAD_REQUEST_400,"Bad chunking");
+                        throw new BadMessageException(HttpStatus.BAD_REQUEST_400,"Bad chunking");
                     }
                 }
                 break;
@@ -827,7 +835,7 @@ public class HttpParser
                 int port=0;
                 if (host==null || host.length()==0)
                 {
-                    throw new BadMessage(HttpStatus.BAD_REQUEST_400,"Bad Host header");
+                    throw new BadMessageException(HttpStatus.BAD_REQUEST_400,"Bad Host header");
                 }
 
                 int len=host.length();
@@ -849,7 +857,7 @@ public class HttpParser
                             {
                                 if (DEBUG)
                                     LOG.debug(e);
-                                throw new BadMessage(HttpStatus.BAD_REQUEST_400,"Bad Host header");
+                                throw new BadMessageException(HttpStatus.BAD_REQUEST_400,"Bad Host header");
                             }
                             break loop;
                     }
@@ -858,9 +866,9 @@ public class HttpParser
                 {
                     if (host.charAt(len-1)!=']') 
                     {
-                        throw new BadMessage(HttpStatus.BAD_REQUEST_400,"Bad IPv6 Host header");
+                        throw new BadMessageException(HttpStatus.BAD_REQUEST_400,"Bad IPv6 Host header");
                     }
-                    host = host.substring(1,len-1);
+                    host = host.substring(0,len);
                 }
                 else if (len!=host.length())
                     host = host.substring(0,len);
@@ -922,7 +930,7 @@ public class HttpParser
             if (_maxHeaderBytes>0 && ++_headerBytes>_maxHeaderBytes)
             {
                 LOG.warn("Header is too large >"+_maxHeaderBytes);
-                throw new BadMessage(HttpStatus.REQUEST_ENTITY_TOO_LARGE_413);
+                throw new BadMessageException(HttpStatus.REQUEST_ENTITY_TOO_LARGE_413);
             }
 
             switch (_state)
@@ -980,9 +988,9 @@ public class HttpParser
                                 // End of headers!
 
                                 // Was there a required host header?
-                                if (!_host && _version!=HttpVersion.HTTP_1_0 && _requestHandler!=null)
+                                if (!_host && _version==HttpVersion.HTTP_1_1 && _requestHandler!=null)
                                 {
-                                    throw new BadMessage(HttpStatus.BAD_REQUEST_400,"No Host");
+                                    throw new BadMessageException(HttpStatus.BAD_REQUEST_400,"No Host");
                                 }
 
                                 // is it a response that cannot have a body?
@@ -1010,27 +1018,27 @@ public class HttpParser
                                     case EOF_CONTENT:
                                         setState(State.EOF_CONTENT);
                                         handle=_handler.headerComplete()||handle;
-                                        break;
+                                        return handle;
 
                                     case CHUNKED_CONTENT:
                                         setState(State.CHUNKED_CONTENT);
                                         handle=_handler.headerComplete()||handle;
-                                        break;
+                                        return handle;
 
                                     case NO_CONTENT:
                                         handle=_handler.headerComplete()||handle;
                                         setState(State.END);
                                         handle=_handler.messageComplete()||handle;
-                                        break;
+                                        return handle;
 
                                     default:
                                         setState(State.CONTENT);
                                         handle=_handler.headerComplete()||handle;
-                                        break;
+                                        return handle;
                                 }
                             }
                             else if (ch<=HttpTokens.SPACE)
-                                throw new BadMessage();
+                                throw new BadMessageException();
                             else
                             {
                                 if (buffer.hasRemaining())
@@ -1147,8 +1155,8 @@ public class HttpParser
                             _length=_string.length();
                         break;
                     }
-                     
-                    throw new BadMessage("Illegal character");
+
+                    throw new IllegalCharacterException(_state,ch,buffer);
 
                 case HEADER_VALUE:
                     if (ch>HttpTokens.SPACE || ch<0)
@@ -1172,8 +1180,8 @@ public class HttpParser
                         setState(State.HEADER);
                         break; 
                     }
-
-                    throw new BadMessage("Illegal character");
+                    
+                    throw new IllegalCharacterException(_state,ch,buffer);
 
                 case HEADER_IN_VALUE:
                     if (ch>=HttpTokens.SPACE || ch<0 || ch==HttpTokens.TAB)
@@ -1201,7 +1209,8 @@ public class HttpParser
                         setState(State.HEADER);
                         break;
                     }
-                    throw new BadMessage("Illegal character");
+
+                    throw new IllegalCharacterException(_state,ch,buffer);
                     
                 default:
                     throw new IllegalStateException(_state.toString());
@@ -1256,8 +1265,7 @@ public class HttpParser
                 if (_responseStatus>0 && _headResponse)
                 {
                     setState(State.END);
-                    if (_handler.messageComplete())
-                        return true;
+                    return _handler.messageComplete();
                 }
                 else
                 {
@@ -1280,7 +1288,7 @@ public class HttpParser
                     // Just ignore data when closed
                     _headerBytes+=buffer.remaining();
                     BufferUtil.clear(buffer);
-                    if (_headerBytes>_maxHeaderBytes)
+                    if (_maxHeaderBytes>0 && _headerBytes>_maxHeaderBytes)
                     {
                         // Don't want to waste time reading data of a closed request
                         throw new IllegalStateException("too much data after closed");
@@ -1329,15 +1337,15 @@ public class HttpParser
             
             return false;
         }
-        catch(BadMessage e)
+        catch(BadMessageException e)
         {
             BufferUtil.clear(buffer);
 
-            LOG.warn("badMessage: "+e._code+(e._message!=null?" "+e._message:"")+" for "+_handler);
+            LOG.warn("badMessage: "+e._code+(e.getMessage()!=null?" "+e.getMessage():"")+" for "+_handler);
             if (DEBUG)
                 LOG.debug(e);
             setState(State.CLOSED);
-            _handler.badMessage(e._code, e._message);
+            _handler.badMessage(e._code, e.getMessage());
             return false;
         }
         catch(Exception e)
@@ -1372,8 +1380,7 @@ public class HttpParser
             if (content == 0)
             {
                 setState(State.END);
-                if (_handler.messageComplete())
-                    return true;
+                return _handler.messageComplete();
             }
         }
         
@@ -1397,8 +1404,7 @@ public class HttpParser
                     if (content == 0)
                     {
                         setState(State.END);
-                        if (_handler.messageComplete())
-                            return true;
+                        return _handler.messageComplete();
                     }
                     else
                     {
@@ -1421,8 +1427,7 @@ public class HttpParser
                         if(_contentPosition == _contentLength)
                         {
                             setState(State.END);
-                            if (_handler.messageComplete())
-                                return true;
+                            return _handler.messageComplete();
                         }
                     }
                     break;
@@ -1449,11 +1454,7 @@ public class HttpParser
                     if (ch == HttpTokens.LINE_FEED)
                     {
                         if (_chunkLength == 0)
-                        {
-                            setState(State.END);
-                            if (_handler.messageComplete())
-                                return true;
-                        }
+                            setState(State.CHUNK_END);
                         else
                             setState(State.CHUNK);
                     }
@@ -1470,11 +1471,7 @@ public class HttpParser
                     if (ch == HttpTokens.LINE_FEED)
                     {
                         if (_chunkLength == 0)
-                        {
-                            setState(State.END);
-                            if (_handler.messageComplete())
-                                return true;
-                        }
+                            setState(State.CHUNK_END);
                         else
                             setState(State.CHUNK);
                     }
@@ -1505,6 +1502,20 @@ public class HttpParser
                     break;
                 }
                 
+                case CHUNK_END:
+                {
+                    // TODO handle chunk trailer
+                    ch=next(buffer);
+                    if (ch==0)
+                        break;
+                    if (ch == HttpTokens.LINE_FEED)
+                    {
+                        setState(State.END);
+                        return _handler.messageComplete();
+                    }
+                    throw new IllegalCharacterException(_state,ch,buffer);
+                }
+                
                 case CLOSED:
                 {
                     BufferUtil.clear(buffer);
@@ -1577,6 +1588,29 @@ public class HttpParser
         _state=state;
     }
 
+    /* ------------------------------------------------------------------------------- */
+    public Trie<HttpField> getFieldCache()
+    {
+        return _connectionFields;
+    }
+
+    /* ------------------------------------------------------------------------------- */
+    private String getProxyField(ByteBuffer buffer)
+    {
+        _string.setLength(0);
+        _length=0;
+        
+        while (buffer.hasRemaining())
+        {
+            // process each character
+            byte ch=next(buffer);
+            if (ch<=' ')
+                return _string.toString();
+            _string.append((char)ch);    
+        }
+        throw new BadMessageException();
+    }
+    
     /* ------------------------------------------------------------------------------- */
     @Override
     public String toString()
@@ -1632,11 +1666,17 @@ public class HttpParser
         public int getHeaderCacheSize();
     }
 
+    /* ------------------------------------------------------------------------------- */
+    /* ------------------------------------------------------------------------------- */
+    /* ------------------------------------------------------------------------------- */
     public interface ProxyHandler 
     {
         void proxied(String protocol, String sAddr, String dAddr, int sPort, int dPort);
     }
-    
+
+    /* ------------------------------------------------------------------------------- */
+    /* ------------------------------------------------------------------------------- */
+    /* ------------------------------------------------------------------------------- */
     public interface RequestHandler<T> extends HttpHandler<T>
     {
         /**
@@ -1657,6 +1697,9 @@ public class HttpParser
         public abstract boolean parsedHostHeader(String host,int port);
     }
 
+    /* ------------------------------------------------------------------------------- */
+    /* ------------------------------------------------------------------------------- */
+    /* ------------------------------------------------------------------------------- */
     public interface ResponseHandler<T> extends HttpHandler<T>
     {
         /**
@@ -1665,24 +1708,15 @@ public class HttpParser
         public abstract boolean startResponse(HttpVersion version, int status, String reason);
     }
 
-    public Trie<HttpField> getFieldCache()
-    {
-        return _connectionFields;
-    }
-
-    private String getProxyField(ByteBuffer buffer)
+    /* ------------------------------------------------------------------------------- */
+    @SuppressWarnings("serial")
+    private static class IllegalCharacterException extends BadMessageException
     {
-        _string.setLength(0);
-        _length=0;
-        
-        while (buffer.hasRemaining())
+        private IllegalCharacterException(State state,byte ch,ByteBuffer buffer)
         {
-            // process each character
-            byte ch=next(buffer);
-            if (ch<=' ')
-                return _string.toString();
-            _string.append((char)ch);    
+            super(400,String.format("Illegal character 0x%X",ch));
+            // Bug #460642 - don't reveal buffers to end user
+            LOG.warn(String.format("Illegal character 0x%X in state=%s for buffer %s",ch,state,BufferUtil.toDetailString(buffer)));
         }
-        throw new BadMessage();
     }
 }
index 13f2a8d..f44bcdd 100644 (file)
@@ -1,6 +1,6 @@
 //
 //  ========================================================================
-//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+//  Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
 //  ------------------------------------------------------------------------
 //  All rights reserved. This program and the accompanying materials
 //  are made available under the terms of the Eclipse Public License v1.0
index e6ea1f7..8762644 100644 (file)
@@ -1,6 +1,6 @@
 //
 //  ========================================================================
-//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+//  Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
 //  ------------------------------------------------------------------------
 //  All rights reserved. This program and the accompanying materials
 //  are made available under the terms of the Eclipse Public License v1.0
@@ -612,7 +612,6 @@ package org.eclipse.jetty.http;
  */
 public class HttpStatus
 {
-    public final static int NOT_SET_000 = 0;
     public final static int CONTINUE_100 = 100;
     public final static int SWITCHING_PROTOCOLS_101 = 101;
     public final static int PROCESSING_102 = 102;
index 537c457..0283f9e 100644 (file)
@@ -1,6 +1,6 @@
 //
 //  ========================================================================
-//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+//  Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
 //  ------------------------------------------------------------------------
 //  All rights reserved. This program and the accompanying materials
 //  are made available under the terms of the Eclipse Public License v1.0
index 4138334..067a21e 100644 (file)
@@ -1,6 +1,6 @@
 //
 //  ========================================================================
-//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+//  Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
 //  ------------------------------------------------------------------------
 //  All rights reserved. This program and the accompanying materials
 //  are made available under the terms of the Eclipse Public License v1.0
index afe925e..46a8a5b 100644 (file)
@@ -1,6 +1,6 @@
 //
 //  ========================================================================
-//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+//  Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
 //  ------------------------------------------------------------------------
 //  All rights reserved. This program and the accompanying materials
 //  are made available under the terms of the Eclipse Public License v1.0
@@ -539,8 +539,6 @@ public class HttpURI
     {
         if (_host==_port)
             return null;
-        if (_raw[_host]=='[')
-            return new String(_raw,_host+1,_port-_host-2,_charset);
         return new String(_raw,_host,_port-_host,_charset);
     }
 
index eb889e5..ddedc27 100644 (file)
@@ -1,6 +1,6 @@
 //
 //  ========================================================================
-//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+//  Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
 //  ------------------------------------------------------------------------
 //  All rights reserved. This program and the accompanying materials
 //  are made available under the terms of the Eclipse Public License v1.0
@@ -31,7 +31,7 @@ public enum HttpVersion
     HTTP_0_9("HTTP/0.9",9),
     HTTP_1_0("HTTP/1.0",10),
     HTTP_1_1("HTTP/1.1",11),
-    HTTP_2_0("HTTP/2.0",20);
+    HTTP_2("HTTP/2.0",20);
 
     /* ------------------------------------------------------------ */
     public final static Trie<HttpVersion> CACHE= new ArrayTrie<HttpVersion>();
@@ -74,7 +74,7 @@ public enum HttpVersion
                     switch(bytes[position+7])
                     {
                         case '0':
-                            return HTTP_2_0;
+                            return HTTP_2;
                     }
                     break;
             }
@@ -166,6 +166,7 @@ public enum HttpVersion
             case 9: return HttpVersion.HTTP_0_9;
             case 10: return HttpVersion.HTTP_1_0;
             case 11: return HttpVersion.HTTP_1_1;
+            case 20: return HttpVersion.HTTP_2;
             default: throw new IllegalArgumentException();
         }
     }
index 9cac4ab..145f220 100644 (file)
@@ -1,6 +1,6 @@
 //
 //  ========================================================================
-//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+//  Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
 //  ------------------------------------------------------------------------
 //  All rights reserved. This program and the accompanying materials
 //  are made available under the terms of the Eclipse Public License v1.0
index b78f572..d219687 100644 (file)
@@ -1,6 +1,6 @@
 //
 //  ========================================================================
-//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+//  Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
 //  ------------------------------------------------------------------------
 //  All rights reserved. This program and the accompanying materials
 //  are made available under the terms of the Eclipse Public License v1.0
 
 package org.eclipse.jetty.http;
 
+import java.util.AbstractSet;
 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.StringTokenizer;
 
 import org.eclipse.jetty.util.ArrayTernaryTrie;
+import org.eclipse.jetty.util.IncludeExclude;
+import org.eclipse.jetty.util.Predicate;
 import org.eclipse.jetty.util.Trie;
 import org.eclipse.jetty.util.URIUtil;
 
@@ -569,4 +573,54 @@ public class PathMap<O> extends HashMap<String,O>
             this.mapped = mapped;
         }
     }
+    
+    public static class PathSet extends AbstractSet<String> implements Predicate<String>
+    {
+        private final PathMap<Boolean> _map = new PathMap<>();
+        
+        @Override
+        public Iterator<String> iterator()
+        {
+            return _map.keySet().iterator();
+        }
+
+        @Override
+        public int size()
+        {
+            return _map.size();
+        }
+        
+        @Override
+        public boolean add(String item)
+        {
+            return _map.put(item,Boolean.TRUE)==null;
+        }
+        
+        @Override
+        public boolean remove(Object item)
+        {
+            return _map.remove(item)!=null;
+        }
+
+        @Override
+        public boolean contains(Object o) 
+        { 
+            return _map.containsKey(o); 
+        }
+        
+        @Override
+        public boolean test(String s)
+        {
+            return _map.containsMatch(s);
+        }
+        
+        public boolean containsMatch(String s) 
+        { 
+            return _map.containsMatch(s); 
+        }
+        public boolean matches(String item)
+        {
+            return _map.containsMatch(item);
+        }
+    }
 }
index b270989..302cbd0 100644 (file)
@@ -12,6 +12,7 @@ bcpio=application/x-bcpio
 bin=application/octet-stream
 cab=application/x-cabinet
 cdf=application/x-netcdf
+chm=application/vnd.ms-htmlhelp
 class=application/java-vm
 cpio=application/x-cpio
 cpt=application/mac-compactpro
@@ -141,6 +142,7 @@ 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=application/x-troff
 tar=application/x-tar
index 825422a..aeca4a3 100644 (file)
@@ -1,6 +1,6 @@
 //
 //  ========================================================================
-//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+//  Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
 //  ------------------------------------------------------------------------
 //  All rights reserved. This program and the accompanying materials
 //  are made available under the terms of the Eclipse Public License v1.0
diff --git a/lib/jetty/org/eclipse/jetty/http/pathmap/MappedResource.java b/lib/jetty/org/eclipse/jetty/http/pathmap/MappedResource.java
new file mode 100644 (file)
index 0000000..155dce8
--- /dev/null
@@ -0,0 +1,101 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http.pathmap;
+
+import org.eclipse.jetty.util.annotation.ManagedAttribute;
+import org.eclipse.jetty.util.annotation.ManagedObject;
+
+@ManagedObject("Mapped Resource")
+public class MappedResource<E> implements Comparable<MappedResource<E>>
+{
+    private final PathSpec pathSpec;
+    private final E resource;
+
+    public MappedResource(PathSpec pathSpec, E resource)
+    {
+        this.pathSpec = pathSpec;
+        this.resource = resource;
+    }
+
+    /**
+     * Comparison is based solely on the pathSpec
+     */
+    @Override
+    public int compareTo(MappedResource<E> other)
+    {
+        return this.pathSpec.compareTo(other.pathSpec);
+    }
+
+    @Override
+    public boolean equals(Object obj)
+    {
+        if (this == obj)
+        {
+            return true;
+        }
+        if (obj == null)
+        {
+            return false;
+        }
+        if (getClass() != obj.getClass())
+        {
+            return false;
+        }
+        MappedResource<?> other = (MappedResource<?>)obj;
+        if (pathSpec == null)
+        {
+            if (other.pathSpec != null)
+            {
+                return false;
+            }
+        }
+        else if (!pathSpec.equals(other.pathSpec))
+        {
+            return false;
+        }
+        return true;
+    }
+
+    @ManagedAttribute(value = "path spec", readonly = true)
+    public PathSpec getPathSpec()
+    {
+        return pathSpec;
+    }
+
+    @ManagedAttribute(value = "resource", readonly = true)
+    public E getResource()
+    {
+        return resource;
+    }
+
+    @Override
+    public int hashCode()
+    {
+        final int prime = 31;
+        int result = 1;
+        result = (prime * result) + ((pathSpec == null) ? 0 : pathSpec.hashCode());
+        return result;
+    }
+
+    @Override
+    public String toString()
+    {
+        return String.format("MappedResource[pathSpec=%s,resource=%s]",pathSpec,resource);
+    }
+}
\ No newline at end of file
diff --git a/lib/jetty/org/eclipse/jetty/http/pathmap/PathMappings.java b/lib/jetty/org/eclipse/jetty/http/pathmap/PathMappings.java
new file mode 100644 (file)
index 0000000..d1c36cf
--- /dev/null
@@ -0,0 +1,159 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http.pathmap;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.jetty.util.annotation.ManagedAttribute;
+import org.eclipse.jetty.util.annotation.ManagedObject;
+import org.eclipse.jetty.util.component.ContainerLifeCycle;
+import org.eclipse.jetty.util.component.Dumpable;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+/**
+ * Path Mappings of PathSpec to Resource.
+ * <p>
+ * Sorted into search order upon entry into the Set
+ * 
+ * @param <E> the type of mapping endpoint
+ */
+@ManagedObject("Path Mappings")
+public class PathMappings<E> implements Iterable<MappedResource<E>>, Dumpable
+{
+    private static final Logger LOG = Log.getLogger(PathMappings.class);
+    private List<MappedResource<E>> mappings = new ArrayList<MappedResource<E>>();
+    private MappedResource<E> defaultResource = null;
+    private MappedResource<E> rootResource = null;
+
+    @Override
+    public String dump()
+    {
+        return ContainerLifeCycle.dump(this);
+    }
+
+    @Override
+    public void dump(Appendable out, String indent) throws IOException
+    {
+        ContainerLifeCycle.dump(out,indent,mappings);
+    }
+
+    @ManagedAttribute(value = "mappings", readonly = true)
+    public List<MappedResource<E>> getMappings()
+    {
+        return mappings;
+    }
+
+    public void reset()
+    {
+        mappings.clear();
+    }
+    
+    /**
+     * Return a list of MappedResource matches for the specified path.
+     * 
+     * @param path the path to return matches on
+     * @return the list of mapped resource the path matches on
+     */
+    public List<MappedResource<E>> getMatches(String path)
+    {
+        boolean matchRoot = "/".equals(path);
+        
+        List<MappedResource<E>> ret = new ArrayList<>();
+        int len = mappings.size();
+        for (int i = 0; i < len; i++)
+        {
+            MappedResource<E> mr = mappings.get(i);
+
+            switch (mr.getPathSpec().group)
+            {
+                case ROOT:
+                    if (matchRoot)
+                        ret.add(mr);
+                    break;
+                case DEFAULT:
+                    if (matchRoot || mr.getPathSpec().matches(path))
+                        ret.add(mr);
+                    break;
+                default:
+                    if (mr.getPathSpec().matches(path))
+                        ret.add(mr);
+                    break;
+            }
+        }
+        return ret;
+    }
+
+    public MappedResource<E> getMatch(String path)
+    {
+        if (path.equals("/") && rootResource != null)
+        {
+            return rootResource;
+        }
+        
+        int len = mappings.size();
+        for (int i = 0; i < len; i++)
+        {
+            MappedResource<E> mr = mappings.get(i);
+            if (mr.getPathSpec().matches(path))
+            {
+                return mr;
+            }
+        }
+        return defaultResource;
+    }
+
+    @Override
+    public Iterator<MappedResource<E>> iterator()
+    {
+        return mappings.iterator();
+    }
+
+    @SuppressWarnings("incomplete-switch")
+    public void put(PathSpec pathSpec, E resource)
+    {
+        MappedResource<E> entry = new MappedResource<>(pathSpec,resource);
+        switch (pathSpec.group)
+        {
+            case DEFAULT:
+                defaultResource = entry;
+                break;
+            case ROOT:
+                rootResource = entry;
+                break;
+        }
+        
+        // TODO: add warning when replacing an existing pathspec?
+        
+        mappings.add(entry);
+        if (LOG.isDebugEnabled())
+            LOG.debug("Added {} to {}",entry,this);
+        Collections.sort(mappings);
+    }
+
+    @Override
+    public String toString()
+    {
+        return String.format("%s[size=%d]",this.getClass().getSimpleName(),mappings.size());
+    }
+}
diff --git a/lib/jetty/org/eclipse/jetty/http/pathmap/PathSpec.java b/lib/jetty/org/eclipse/jetty/http/pathmap/PathSpec.java
new file mode 100644 (file)
index 0000000..a2b8ea5
--- /dev/null
@@ -0,0 +1,167 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http.pathmap;
+
+/**
+ * The base PathSpec, what all other path specs are based on
+ */
+public abstract class PathSpec implements Comparable<PathSpec>
+{
+    protected String pathSpec;
+    protected PathSpecGroup group;
+    protected int pathDepth;
+    protected int specLength;
+
+    @Override
+    public int compareTo(PathSpec other)
+    {
+        // Grouping (increasing)
+        int diff = this.group.ordinal() - other.group.ordinal();
+        if (diff != 0)
+        {
+            return diff;
+        }
+
+        // Spec Length (decreasing)
+        diff = other.specLength - this.specLength;
+        if (diff != 0)
+        {
+            return diff;
+        }
+
+        // Path Spec Name (alphabetical)
+        return this.pathSpec.compareTo(other.pathSpec);
+    }
+
+    @Override
+    public boolean equals(Object obj)
+    {
+        if (this == obj)
+        {
+            return true;
+        }
+        if (obj == null)
+        {
+            return false;
+        }
+        if (getClass() != obj.getClass())
+        {
+            return false;
+        }
+        PathSpec other = (PathSpec)obj;
+        if (pathSpec == null)
+        {
+            if (other.pathSpec != null)
+            {
+                return false;
+            }
+        }
+        else if (!pathSpec.equals(other.pathSpec))
+        {
+            return false;
+        }
+        return true;
+    }
+
+    public PathSpecGroup getGroup()
+    {
+        return group;
+    }
+
+    /**
+     * Get the number of path elements that this path spec declares.
+     * <p>
+     * This is used to determine longest match logic.
+     * 
+     * @return the depth of the path segments that this spec declares
+     */
+    public int getPathDepth()
+    {
+        return pathDepth;
+    }
+
+    /**
+     * Return the portion of the path that is after the path spec.
+     * 
+     * @param path
+     *            the path to match against
+     * @return the path info portion of the string
+     */
+    public abstract String getPathInfo(String path);
+
+    /**
+     * Return the portion of the path that matches a path spec.
+     * 
+     * @param path
+     *            the path to match against
+     * @return the match, or null if no match at all
+     */
+    public abstract String getPathMatch(String path);
+
+    /**
+     * The as-provided path spec.
+     * 
+     * @return the as-provided path spec
+     */
+    public String getDeclaration()
+    {
+        return pathSpec;
+    }
+
+    /**
+     * Get the relative path.
+     * 
+     * @param base
+     *            the base the path is relative to
+     * @param path
+     *            the additional path
+     * @return the base plus path with pathSpec portion removed
+     */
+    public abstract String getRelativePath(String base, String path);
+
+    @Override
+    public int hashCode()
+    {
+        final int prime = 31;
+        int result = 1;
+        result = (prime * result) + ((pathSpec == null)?0:pathSpec.hashCode());
+        return result;
+    }
+
+    /**
+     * Test to see if the provided path matches this path spec
+     * 
+     * @param path
+     *            the path to test
+     * @return true if the path matches this path spec, false otherwise
+     */
+    public abstract boolean matches(String path);
+
+    @Override
+    public String toString()
+    {
+        StringBuilder str = new StringBuilder();
+        str.append(this.getClass().getSimpleName()).append("[\"");
+        str.append(pathSpec);
+        str.append("\",pathDepth=").append(pathDepth);
+        str.append(",group=").append(group);
+        str.append("]");
+        return str.toString();
+    }
+}
diff --git a/lib/jetty/org/eclipse/jetty/http/pathmap/PathSpecGroup.java b/lib/jetty/org/eclipse/jetty/http/pathmap/PathSpecGroup.java
new file mode 100644 (file)
index 0000000..f9d96ce
--- /dev/null
@@ -0,0 +1,101 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http.pathmap;
+
+/**
+ * Types of path spec groups.
+ * <p>
+ * This is used to facilitate proper pathspec search order.
+ * <p>
+ * Search Order: 
+ * <ol>
+ * <li>{@link PathSpecGroup#ordinal()} [increasing]</li>
+ * <li>{@link PathSpec#specLength} [decreasing]</li>
+ * <li>{@link PathSpec#pathSpec} [natural sort order]</li>
+ * </ol>
+ */
+public enum PathSpecGroup
+{
+    // NOTE: Order of enums determines order of Groups.
+
+    /**
+     * For exactly defined path specs, no glob.
+     */
+    EXACT,
+    /**
+     * For path specs that have a hardcoded prefix and suffix with wildcard glob in the middle.
+     * 
+     * <pre>
+     *   "^/downloads/[^/]*.zip$"  - regex spec
+     *   "/a/{var}/c"              - uri-template spec
+     * </pre>
+     * 
+     * Note: there is no known servlet spec variant of this kind of path spec
+     */
+    MIDDLE_GLOB,
+    /**
+     * For path specs that have a hardcoded prefix and a trailing wildcard glob.
+     * <p>
+     * 
+     * <pre>
+     *   "/downloads/*"          - servlet spec
+     *   "/api/*"                - servlet spec
+     *   "^/rest/.*$"            - regex spec
+     *   "/bookings/{guest-id}"  - uri-template spec
+     *   "/rewards/{vip-level}"  - uri-template spec
+     * </pre>
+     */
+    PREFIX_GLOB,
+    /**
+     * For path specs that have a wildcard glob with a hardcoded suffix
+     * 
+     * <pre>
+     *   "*.do"        - servlet spec
+     *   "*.css"       - servlet spec
+     *   "^.*\.zip$"   - regex spec
+     * </pre>
+     * 
+     * Note: there is no known uri-template spec variant of this kind of path spec
+     */
+    SUFFIX_GLOB,
+    /**
+     * The root spec for accessing the Root behavior.
+     * 
+     * <pre>
+     *   ""           - servlet spec       (Root Servlet)
+     *   null         - servlet spec       (Root Servlet)
+     * </pre>
+     * 
+     * Note: there is no known uri-template spec variant of this kind of path spec
+     */
+    ROOT,
+    /**
+     * The default spec for accessing the Default path behavior.
+     * 
+     * <pre>
+     *   "/"           - servlet spec      (Default Servlet)
+     *   "/"           - uri-template spec (Root Context)
+     *   "^/$"         - regex spec        (Root Context)
+     * </pre>
+     * 
+     * Per Servlet Spec, pathInfo is always null for these specs.
+     * If nothing above matches, then default will match.
+     */
+    DEFAULT,
+}
diff --git a/lib/jetty/org/eclipse/jetty/http/pathmap/PathSpecSet.java b/lib/jetty/org/eclipse/jetty/http/pathmap/PathSpecSet.java
new file mode 100644 (file)
index 0000000..1ffb783
--- /dev/null
@@ -0,0 +1,223 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http.pathmap;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import java.util.TreeSet;
+
+import org.eclipse.jetty.util.Predicate;
+
+/**
+ * A Set of PathSpec strings.
+ * <p>
+ * Used by {@link org.eclipse.jetty.util.IncludeExclude} logic
+ */
+public class PathSpecSet implements Set<String>, Predicate<String>
+{
+    private final Set<PathSpec> specs = new TreeSet<>();
+
+    @Override
+    public boolean test(String s)
+    {
+        for (PathSpec spec : specs)
+        {
+            if (spec.matches(s))
+            {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public boolean isEmpty()
+    {
+        return specs.isEmpty();
+    }
+
+    @Override
+    public Iterator<String> iterator()
+    {
+        return new Iterator<String>()
+        {
+            private Iterator<PathSpec> iter = specs.iterator();
+
+            @Override
+            public boolean hasNext()
+            {
+                return iter.hasNext();
+            }
+
+            @Override
+            public String next()
+            {
+                PathSpec spec = iter.next();
+                if (spec == null)
+                {
+                    return null;
+                }
+                return spec.getDeclaration();
+            }
+
+            @Override
+            public void remove()
+            {
+                throw new UnsupportedOperationException("Remove not supported by this Iterator");
+            }
+        };
+    }
+
+    @Override
+    public int size()
+    {
+        return specs.size();
+    }
+
+    @Override
+    public boolean contains(Object o)
+    {
+        if (o instanceof PathSpec)
+        {
+            return specs.contains(o);
+        }
+        if (o instanceof String)
+        {
+            return specs.contains(toPathSpec((String)o));
+        }
+        return false;
+    }
+
+    private PathSpec asPathSpec(Object o)
+    {
+        if (o == null)
+        {
+            return null;
+        }
+        if (o instanceof PathSpec)
+        {
+            return (PathSpec)o;
+        }
+        if (o instanceof String)
+        {
+            return toPathSpec((String)o);
+        }
+        return toPathSpec(o.toString());
+    }
+
+    private PathSpec toPathSpec(String rawSpec)
+    {
+        if ((rawSpec == null) || (rawSpec.length() < 1))
+        {
+            throw new RuntimeException("Path Spec String must start with '^', '/', or '*.': got [" + rawSpec + "]");
+        }
+        if (rawSpec.charAt(0) == '^')
+        {
+            return new RegexPathSpec(rawSpec);
+        }
+        else
+        {
+            return new ServletPathSpec(rawSpec);
+        }
+    }
+
+    @Override
+    public Object[] toArray()
+    {
+        return toArray(new String[specs.size()]);
+    }
+
+    @Override
+    public <T> T[] toArray(T[] a)
+    {
+        int i = 0;
+        for (PathSpec spec : specs)
+        {
+            a[i++] = (T)spec.getDeclaration();
+        }
+        return a;
+    }
+
+    @Override
+    public boolean add(String e)
+    {
+        return specs.add(toPathSpec(e));
+    }
+
+    @Override
+    public boolean remove(Object o)
+    {
+        return specs.remove(asPathSpec(o));
+    }
+
+    @Override
+    public boolean containsAll(Collection<?> coll)
+    {
+        for (Object o : coll)
+        {
+            if (!specs.contains(asPathSpec(o)))
+                return false;
+        }
+        return true;
+    }
+
+    @Override
+    public boolean addAll(Collection<? extends String> coll)
+    {
+        boolean ret = false;
+
+        for (String s : coll)
+        {
+            ret |= add(s);
+        }
+
+        return ret;
+    }
+
+    @Override
+    public boolean retainAll(Collection<?> coll)
+    {
+        List<PathSpec> collSpecs = new ArrayList<>();
+        for (Object o : coll)
+        {
+            collSpecs.add(asPathSpec(o));
+        }
+        return specs.retainAll(collSpecs);
+    }
+
+    @Override
+    public boolean removeAll(Collection<?> coll)
+    {
+        List<PathSpec> collSpecs = new ArrayList<>();
+        for (Object o : coll)
+        {
+            collSpecs.add(asPathSpec(o));
+        }
+        return specs.removeAll(collSpecs);
+    }
+
+    @Override
+    public void clear()
+    {
+        specs.clear();
+    }
+}
diff --git a/lib/jetty/org/eclipse/jetty/http/pathmap/RegexPathSpec.java b/lib/jetty/org/eclipse/jetty/http/pathmap/RegexPathSpec.java
new file mode 100644 (file)
index 0000000..0d83f3b
--- /dev/null
@@ -0,0 +1,176 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http.pathmap;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class RegexPathSpec extends PathSpec
+{
+    protected Pattern pattern;
+
+    protected RegexPathSpec()
+    {
+        super();
+    }
+
+    public RegexPathSpec(String regex)
+    {
+        super.pathSpec = regex;
+        boolean inGrouping = false;
+        this.pathDepth = 0;
+        this.specLength = pathSpec.length();
+        // build up a simple signature we can use to identify the grouping
+        StringBuilder signature = new StringBuilder();
+        for (char c : pathSpec.toCharArray())
+        {
+            switch (c)
+            {
+                case '[':
+                    inGrouping = true;
+                    break;
+                case ']':
+                    inGrouping = false;
+                    signature.append('g'); // glob
+                    break;
+                case '*':
+                    signature.append('g'); // glob
+                    break;
+                case '/':
+                    if (!inGrouping)
+                    {
+                        this.pathDepth++;
+                    }
+                    break;
+                default:
+                    if (!inGrouping)
+                    {
+                        if (Character.isLetterOrDigit(c))
+                        {
+                            signature.append('l'); // literal (exact)
+                        }
+                    }
+                    break;
+            }
+        }
+        this.pattern = Pattern.compile(pathSpec);
+
+        // Figure out the grouping based on the signature
+        String sig = signature.toString();
+
+        if (Pattern.matches("^l*$",sig))
+        {
+            this.group = PathSpecGroup.EXACT;
+        }
+        else if (Pattern.matches("^l*g+",sig))
+        {
+            this.group = PathSpecGroup.PREFIX_GLOB;
+        }
+        else if (Pattern.matches("^g+l+$",sig))
+        {
+            this.group = PathSpecGroup.SUFFIX_GLOB;
+        }
+        else
+        {
+            this.group = PathSpecGroup.MIDDLE_GLOB;
+        }
+    }
+
+    public Matcher getMatcher(String path)
+    {
+        return this.pattern.matcher(path);
+    }
+
+    @Override
+    public String getPathInfo(String path)
+    {
+        // Path Info only valid for PREFIX_GLOB types
+        if (group == PathSpecGroup.PREFIX_GLOB)
+        {
+            Matcher matcher = getMatcher(path);
+            if (matcher.matches())
+            {
+                if (matcher.groupCount() >= 1)
+                {
+                    String pathInfo = matcher.group(1);
+                    if ("".equals(pathInfo))
+                    {
+                        return "/";
+                    }
+                    else
+                    {
+                        return pathInfo;
+                    }
+                }
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public String getPathMatch(String path)
+    {
+        Matcher matcher = getMatcher(path);
+        if (matcher.matches())
+        {
+            if (matcher.groupCount() >= 1)
+            {
+                int idx = matcher.start(1);
+                if (idx > 0)
+                {
+                    if (path.charAt(idx - 1) == '/')
+                    {
+                        idx--;
+                    }
+                    return path.substring(0,idx);
+                }
+            }
+            return path;
+        }
+        return null;
+    }
+
+    public Pattern getPattern()
+    {
+        return this.pattern;
+    }
+
+    @Override
+    public String getRelativePath(String base, String path)
+    {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public boolean matches(final String path)
+    {
+        int idx = path.indexOf('?');
+        if (idx >= 0)
+        {
+            // match only non-query part
+            return getMatcher(path.substring(0,idx)).matches();
+        }
+        else
+        {
+            // match entire path
+            return getMatcher(path).matches();
+        }
+    }
+}
diff --git a/lib/jetty/org/eclipse/jetty/http/pathmap/ServletPathSpec.java b/lib/jetty/org/eclipse/jetty/http/pathmap/ServletPathSpec.java
new file mode 100644 (file)
index 0000000..4563305
--- /dev/null
@@ -0,0 +1,261 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http.pathmap;
+
+import org.eclipse.jetty.util.URIUtil;
+
+public class ServletPathSpec extends PathSpec
+{
+    public ServletPathSpec(String servletPathSpec)
+    {
+        super();
+        assertValidServletPathSpec(servletPathSpec);
+
+        // The Root Path Spec
+        if ((servletPathSpec == null) || (servletPathSpec.length() == 0))
+        {
+            super.pathSpec = "";
+            super.pathDepth = -1; // force this to be at the end of the sort order
+            this.specLength = 1;
+            this.group = PathSpecGroup.ROOT;
+            return;
+        }
+
+        // The Default Path Spec
+        if("/".equals(servletPathSpec))
+        {
+            super.pathSpec = "/";
+            super.pathDepth = -1; // force this to be at the end of the sort order
+            this.specLength = 1;
+            this.group = PathSpecGroup.DEFAULT;
+            return;
+        }
+        
+        this.specLength = servletPathSpec.length();
+        super.pathDepth = 0;
+        char lastChar = servletPathSpec.charAt(specLength - 1);
+        // prefix based
+        if ((servletPathSpec.charAt(0) == '/') && (specLength > 1) && (lastChar == '*'))
+        {
+            this.group = PathSpecGroup.PREFIX_GLOB;
+        }
+        // suffix based
+        else if (servletPathSpec.charAt(0) == '*')
+        {
+            this.group = PathSpecGroup.SUFFIX_GLOB;
+        }
+        else
+        {
+            this.group = PathSpecGroup.EXACT;
+        }
+
+        for (int i = 0; i < specLength; i++)
+        {
+            int cp = servletPathSpec.codePointAt(i);
+            if (cp < 128)
+            {
+                char c = (char)cp;
+                switch (c)
+                {
+                    case '/':
+                        super.pathDepth++;
+                        break;
+                }
+            }
+        }
+
+        super.pathSpec = servletPathSpec;
+    }
+
+    private void assertValidServletPathSpec(String servletPathSpec)
+    {
+        if ((servletPathSpec == null) || servletPathSpec.equals(""))
+        {
+            return; // empty path spec
+        }
+
+        int len = servletPathSpec.length();
+        // path spec must either start with '/' or '*.'
+        if (servletPathSpec.charAt(0) == '/')
+        {
+            // Prefix Based
+            if (len == 1)
+            {
+                return; // simple '/' path spec
+            }
+            int idx = servletPathSpec.indexOf('*');
+            if (idx < 0)
+            {
+                return; // no hit on glob '*'
+            }
+            // only allowed to have '*' at the end of the path spec
+            if (idx != (len - 1))
+            {
+                throw new IllegalArgumentException("Servlet Spec 12.2 violation: glob '*' can only exist at end of prefix based matches: bad spec \""+ servletPathSpec +"\"");
+            }
+        }
+        else if (servletPathSpec.startsWith("*."))
+        {
+            // Suffix Based
+            int idx = servletPathSpec.indexOf('/');
+            // cannot have path separator
+            if (idx >= 0)
+            {
+                throw new IllegalArgumentException("Servlet Spec 12.2 violation: suffix based path spec cannot have path separators: bad spec \""+ servletPathSpec +"\"");
+            }
+
+            idx = servletPathSpec.indexOf('*',2);
+            // only allowed to have 1 glob '*', at the start of the path spec
+            if (idx >= 1)
+            {
+                throw new IllegalArgumentException("Servlet Spec 12.2 violation: suffix based path spec cannot have multiple glob '*': bad spec \""+ servletPathSpec +"\"");
+            }
+        }
+        else
+        {
+            throw new IllegalArgumentException("Servlet Spec 12.2 violation: path spec must start with \"/\" or \"*.\": bad spec \""+ servletPathSpec +"\"");
+        }
+    }
+
+    @Override
+    public String getPathInfo(String path)
+    {
+        // Path Info only valid for PREFIX_GLOB types
+        if (group == PathSpecGroup.PREFIX_GLOB)
+        {
+            if (path.length() == (specLength - 2))
+            {
+                return null;
+            }
+            return path.substring(specLength - 2);
+        }
+
+        return null;
+    }
+
+    @Override
+    public String getPathMatch(String path)
+    {
+        switch (group)
+        {
+            case EXACT:
+                if (pathSpec.equals(path))
+                {
+                    return path;
+                }
+                else
+                {
+                    return null;
+                }
+            case PREFIX_GLOB:
+                if (isWildcardMatch(path))
+                {
+                    return path.substring(0,specLength - 2);
+                }
+                else
+                {
+                    return null;
+                }
+            case SUFFIX_GLOB:
+                if (path.regionMatches(path.length() - (specLength - 1),pathSpec,1,specLength - 1))
+                {
+                    return path;
+                }
+                else
+                {
+                    return null;
+                }
+            case DEFAULT:
+                return path;
+            default:
+                return null;
+        }
+    }
+
+    @Override
+    public String getRelativePath(String base, String path)
+    {
+        String info = getPathInfo(path);
+        if (info == null)
+        {
+            info = path;
+        }
+
+        if (info.startsWith("./"))
+        {
+            info = info.substring(2);
+        }
+        if (base.endsWith(URIUtil.SLASH))
+        {
+            if (info.startsWith(URIUtil.SLASH))
+            {
+                path = base + info.substring(1);
+            }
+            else
+            {
+                path = base + info;
+            }
+        }
+        else if (info.startsWith(URIUtil.SLASH))
+        {
+            path = base + info;
+        }
+        else
+        {
+            path = base + URIUtil.SLASH + info;
+        }
+        return path;
+    }
+
+    private boolean isWildcardMatch(String path)
+    {
+        // For a spec of "/foo/*" match "/foo" , "/foo/..." but not "/foobar"
+        int cpl = specLength - 2;
+        if ((group == PathSpecGroup.PREFIX_GLOB) && (path.regionMatches(0,pathSpec,0,cpl)))
+        {
+            if ((path.length() == cpl) || ('/' == path.charAt(cpl)))
+            {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public boolean matches(String path)
+    {
+        switch (group)
+        {
+            case EXACT:
+                return pathSpec.equals(path);
+            case PREFIX_GLOB:
+                return isWildcardMatch(path);
+            case SUFFIX_GLOB:
+                return path.regionMatches((path.length() - specLength) + 1,pathSpec,1,specLength - 1);
+            case ROOT:
+                // Only "/" matches
+                return ("/".equals(path));
+            case DEFAULT:
+                // If we reached this point, then everything matches
+                return true;
+            default:
+                return false;
+        }
+    }
+}
diff --git a/lib/jetty/org/eclipse/jetty/http/pathmap/UriTemplatePathSpec.java b/lib/jetty/org/eclipse/jetty/http/pathmap/UriTemplatePathSpec.java
new file mode 100644 (file)
index 0000000..208820b
--- /dev/null
@@ -0,0 +1,341 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http.pathmap;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.eclipse.jetty.util.TypeUtil;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+/**
+ * PathSpec for URI Template based declarations
+ * 
+ * @see <a href="https://tools.ietf.org/html/rfc6570">URI Templates (Level 1)</a>
+ */
+public class UriTemplatePathSpec extends RegexPathSpec
+{
+    private static final Logger LOG = Log.getLogger(UriTemplatePathSpec.class);
+    
+    private static final Pattern VARIABLE_PATTERN = Pattern.compile("\\{(.*)\\}");
+    /** Reserved Symbols in URI Template variable */
+    private static final String VARIABLE_RESERVED = ":/?#[]@" + // gen-delims
+                                                    "!$&'()*+,;="; // sub-delims
+    /** Allowed Symbols in a URI Template variable */
+    private static final String VARIABLE_SYMBOLS="-._";
+    private static final Set<String> FORBIDDEN_SEGMENTS;
+
+    static
+    {
+        FORBIDDEN_SEGMENTS = new HashSet<>();
+        FORBIDDEN_SEGMENTS.add("/./");
+        FORBIDDEN_SEGMENTS.add("/../");
+        FORBIDDEN_SEGMENTS.add("//");
+    }
+
+    private String variables[];
+
+    public UriTemplatePathSpec(String rawSpec)
+    {
+        super();
+        Objects.requireNonNull(rawSpec,"Path Param Spec cannot be null");
+
+        if ("".equals(rawSpec) || "/".equals(rawSpec))
+        {
+            super.pathSpec = "/";
+            super.pattern = Pattern.compile("^/$");
+            super.pathDepth = 1;
+            this.specLength = 1;
+            this.variables = new String[0];
+            this.group = PathSpecGroup.EXACT;
+            return;
+        }
+
+        if (rawSpec.charAt(0) != '/')
+        {
+            // path specs must start with '/'
+            StringBuilder err = new StringBuilder();
+            err.append("Syntax Error: path spec \"");
+            err.append(rawSpec);
+            err.append("\" must start with '/'");
+            throw new IllegalArgumentException(err.toString());
+        }
+
+        for (String forbidden : FORBIDDEN_SEGMENTS)
+        {
+            if (rawSpec.contains(forbidden))
+            {
+                StringBuilder err = new StringBuilder();
+                err.append("Syntax Error: segment ");
+                err.append(forbidden);
+                err.append(" is forbidden in path spec: ");
+                err.append(rawSpec);
+                throw new IllegalArgumentException(err.toString());
+            }
+        }
+
+        this.pathSpec = rawSpec;
+
+        StringBuilder regex = new StringBuilder();
+        regex.append('^');
+
+        List<String> varNames = new ArrayList<>();
+        // split up into path segments (ignoring the first slash that will always be empty)
+        String segments[] = rawSpec.substring(1).split("/");
+        char segmentSignature[] = new char[segments.length];
+        this.pathDepth = segments.length;
+        for (int i = 0; i < segments.length; i++)
+        {
+            String segment = segments[i];
+            Matcher mat = VARIABLE_PATTERN.matcher(segment);
+
+            if (mat.matches())
+            {
+                // entire path segment is a variable.
+                String variable = mat.group(1);
+                if (varNames.contains(variable))
+                {
+                    // duplicate variable names
+                    StringBuilder err = new StringBuilder();
+                    err.append("Syntax Error: variable ");
+                    err.append(variable);
+                    err.append(" is duplicated in path spec: ");
+                    err.append(rawSpec);
+                    throw new IllegalArgumentException(err.toString());
+                }
+
+                assertIsValidVariableLiteral(variable);
+
+                segmentSignature[i] = 'v'; // variable
+                // valid variable name
+                varNames.add(variable);
+                // build regex
+                regex.append("/([^/]+)");
+            }
+            else if (mat.find(0))
+            {
+                // variable exists as partial segment
+                StringBuilder err = new StringBuilder();
+                err.append("Syntax Error: variable ");
+                err.append(mat.group());
+                err.append(" must exist as entire path segment: ");
+                err.append(rawSpec);
+                throw new IllegalArgumentException(err.toString());
+            }
+            else if ((segment.indexOf('{') >= 0) || (segment.indexOf('}') >= 0))
+            {
+                // variable is split with a path separator
+                StringBuilder err = new StringBuilder();
+                err.append("Syntax Error: invalid path segment /");
+                err.append(segment);
+                err.append("/ variable declaration incomplete: ");
+                err.append(rawSpec);
+                throw new IllegalArgumentException(err.toString());
+            }
+            else if (segment.indexOf('*') >= 0)
+            {
+                // glob segment
+                StringBuilder err = new StringBuilder();
+                err.append("Syntax Error: path segment /");
+                err.append(segment);
+                err.append("/ contains a wildcard symbol (not supported by this uri-template implementation): ");
+                err.append(rawSpec);
+                throw new IllegalArgumentException(err.toString());
+            }
+            else
+            {
+                // valid path segment
+                segmentSignature[i] = 'e'; // exact
+                // build regex
+                regex.append('/');
+                // escape regex special characters
+                for (char c : segment.toCharArray())
+                {
+                    if ((c == '.') || (c == '[') || (c == ']') || (c == '\\'))
+                    {
+                        regex.append('\\');
+                    }
+                    regex.append(c);
+                }
+            }
+        }
+        
+        // Handle trailing slash (which is not picked up during split)
+        if(rawSpec.charAt(rawSpec.length()-1) == '/')
+        {
+            regex.append('/');
+        }
+
+        regex.append('$');
+
+        this.pattern = Pattern.compile(regex.toString());
+
+        int varcount = varNames.size();
+        this.variables = varNames.toArray(new String[varcount]);
+
+        // Convert signature to group
+        String sig = String.valueOf(segmentSignature);
+
+        if (Pattern.matches("^e*$",sig))
+        {
+            this.group = PathSpecGroup.EXACT;
+        }
+        else if (Pattern.matches("^e*v+",sig))
+        {
+            this.group = PathSpecGroup.PREFIX_GLOB;
+        }
+        else if (Pattern.matches("^v+e+",sig))
+        {
+            this.group = PathSpecGroup.SUFFIX_GLOB;
+        }
+        else
+        {
+            this.group = PathSpecGroup.MIDDLE_GLOB;
+        }
+    }
+
+    /**
+     * Validate variable literal name, per RFC6570, Section 2.1 Literals
+     * @param variable
+     * @param pathParamSpec
+     */
+    private void assertIsValidVariableLiteral(String variable)
+    {
+        int len = variable.length();
+        
+        int i = 0;
+        int codepoint;
+        boolean valid = (len > 0); // must not be zero length
+        
+        while (valid && i < len)
+        {
+            codepoint = variable.codePointAt(i);
+            i += Character.charCount(codepoint);
+
+            // basic letters, digits, or symbols
+            if (isValidBasicLiteralCodepoint(codepoint))
+            {
+                continue;
+            }
+
+            // The ucschar and iprivate pieces
+            if (Character.isSupplementaryCodePoint(codepoint))
+            {
+                continue;
+            }
+
+            // pct-encoded
+            if (codepoint == '%')
+            {
+                if (i + 2 > len)
+                {
+                    // invalid percent encoding, missing extra 2 chars
+                    valid = false;
+                    continue;
+                }
+                codepoint = TypeUtil.convertHexDigit(variable.codePointAt(i++)) << 4;
+                codepoint |= TypeUtil.convertHexDigit(variable.codePointAt(i++));
+
+                // validate basic literal
+                if (isValidBasicLiteralCodepoint(codepoint))
+                {
+                    continue;
+                }
+            }
+            
+            valid = false;
+        }
+
+        if (!valid)
+        {
+            // invalid variable name
+            StringBuilder err = new StringBuilder();
+            err.append("Syntax Error: variable {");
+            err.append(variable);
+            err.append("} an invalid variable name: ");
+            err.append(pathSpec);
+            throw new IllegalArgumentException(err.toString());
+        }
+    }
+    
+    private boolean isValidBasicLiteralCodepoint(int codepoint)
+    {
+        // basic letters or digits
+        if((codepoint >= 'a' && codepoint <= 'z') ||
+           (codepoint >= 'A' && codepoint <= 'Z') ||
+           (codepoint >= '0' && codepoint <= '9'))
+        {
+            return true;
+        }
+        
+        // basic allowed symbols
+        if(VARIABLE_SYMBOLS.indexOf(codepoint) >= 0)
+        {
+            return true; // valid simple value
+        }
+        
+        // basic reserved symbols
+        if(VARIABLE_RESERVED.indexOf(codepoint) >= 0)
+        {
+            LOG.warn("Detected URI Template reserved symbol [{}] in path spec \"{}\"",(char)codepoint,pathSpec);
+            return false; // valid simple value
+        }
+
+        return false;
+    }
+
+    public Map<String, String> getPathParams(String path)
+    {
+        Matcher matcher = getMatcher(path);
+        if (matcher.matches())
+        {
+            if (group == PathSpecGroup.EXACT)
+            {
+                return Collections.emptyMap();
+            }
+            Map<String, String> ret = new HashMap<>();
+            int groupCount = matcher.groupCount();
+            for (int i = 1; i <= groupCount; i++)
+            {
+                ret.put(this.variables[i - 1],matcher.group(i));
+            }
+            return ret;
+        }
+        return null;
+    }
+
+    public int getVariableCount()
+    {
+        return variables.length;
+    }
+
+    public String[] getVariables()
+    {
+        return this.variables;
+    }
+}
index 7f8e9f8..28c7082 100644 (file)
@@ -1,6 +1,6 @@
 //
 //  ========================================================================
-//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+//  Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
 //  ------------------------------------------------------------------------
 //  All rights reserved. This program and the accompanying materials
 //  are made available under the terms of the Eclipse Public License v1.0
@@ -40,7 +40,7 @@ import org.eclipse.jetty.util.thread.NonBlockingThread;
 public abstract class AbstractConnection implements Connection
 {
     private static final Logger LOG = Log.getLogger(AbstractConnection.class);
-    
+
     public static final boolean EXECUTE_ONFILLABLE=true;
 
     private final List<Listener> listeners = new CopyOnWriteArrayList<>();
@@ -56,7 +56,7 @@ public abstract class AbstractConnection implements Connection
     {
         this(endp,executor,EXECUTE_ONFILLABLE);
     }
-    
+
     protected AbstractConnection(EndPoint endp, Executor executor, final boolean executeOnfillable)
     {
         if (executor == null)
@@ -88,7 +88,7 @@ public abstract class AbstractConnection implements Connection
     {
         return _executor;
     }
-    
+
     protected void failedCallback(final Callback callback, final Throwable x)
     {
         if (NonBlockingThread.isNonBlockingThread())
@@ -115,7 +115,7 @@ public abstract class AbstractConnection implements Connection
             callback.failed(x);
         }
     }
-    
+
     /**
      * <p>Utility method to be called to register read interest.</p>
      * <p>After a call to this method, {@link #onFillable()} or {@link #onFillInterestedFailed(Throwable)}
@@ -124,8 +124,9 @@ public abstract class AbstractConnection implements Connection
      */
     public void fillInterested()
     {
-        LOG.debug("fillInterested {}",this);           
-        
+        if (LOG.isDebugEnabled())
+            LOG.debug("fillInterested {}",this);
+
         while(true)
         {
             State state=_state.get();
@@ -133,10 +134,11 @@ public abstract class AbstractConnection implements Connection
                 break;
         }
     }
-    
+
     public void fillInterested(Callback callback)
     {
-        LOG.debug("fillInterested {}",this);
+        if (LOG.isDebugEnabled())
+            LOG.debug("fillInterested {}",this);
 
         while(true)
         {
@@ -149,7 +151,7 @@ public abstract class AbstractConnection implements Connection
                 break;
         }
     }
-    
+
     /**
      * <p>Callback method invoked when the endpoint is ready to be read.</p>
      * @see #fillInterested()
@@ -162,7 +164,8 @@ public abstract class AbstractConnection implements Connection
      */
     protected void onFillInterestedFailed(Throwable cause)
     {
-        LOG.debug("{} onFillInterestedFailed {}", this, cause);
+        if (LOG.isDebugEnabled())
+            LOG.debug("{} onFillInterestedFailed {}", this, cause);
         if (_endPoint.isOpen())
         {
             boolean close = true;
@@ -178,7 +181,7 @@ public abstract class AbstractConnection implements Connection
         }
 
         if (_endPoint.isOpen())
-            fillInterested();        
+            fillInterested();
     }
 
     /**
@@ -193,7 +196,8 @@ public abstract class AbstractConnection implements Connection
     @Override
     public void onOpen()
     {
-        LOG.debug("onOpen {}", this);
+        if (LOG.isDebugEnabled())
+            LOG.debug("onOpen {}", this);
 
         for (Listener listener : listeners)
             listener.onOpened(this);
@@ -202,7 +206,8 @@ public abstract class AbstractConnection implements Connection
     @Override
     public void onClose()
     {
-        LOG.debug("onClose {}",this);
+        if (LOG.isDebugEnabled())
+            LOG.debug("onClose {}",this);
 
         for (Listener listener : listeners)
             listener.onClosed(this);
@@ -253,23 +258,28 @@ public abstract class AbstractConnection implements Connection
     @Override
     public String toString()
     {
-        return String.format("%s@%x{%s}", getClass().getSimpleName(), hashCode(), _state.get());
+        return String.format("%s@%x[%s,%s]",
+                getClass().getSimpleName(),
+                hashCode(),
+                _state.get(),
+                _endPoint);
     }
-    
+
     public boolean next(State state, State next)
     {
         if (next==null)
             return true;
         if(_state.compareAndSet(state,next))
         {
-            LOG.debug("{}-->{} {}",state,next,this);
+            if (LOG.isDebugEnabled())
+                LOG.debug("{}-->{} {}",state,next,this);
             if (next!=state)
                 next.onEnter(AbstractConnection.this);
             return true;
         }
         return false;
     }
-    
+
     private static final class IdleState extends State
     {
         private IdleState()
@@ -402,11 +412,11 @@ public abstract class AbstractConnection implements Connection
         {
             return _name;
         }
-        
+
         void onEnter(AbstractConnection connection)
         {
         }
-        
+
         State fillInterested()
         {
             throw new IllegalStateException(this.toString());
@@ -421,28 +431,28 @@ public abstract class AbstractConnection implements Connection
         {
             throw new IllegalStateException(this.toString());
         }
-        
+
         State onFailed()
         {
             throw new IllegalStateException(this.toString());
         }
     }
-    
+
 
     public static final State IDLE=new IdleState();
-    
+
     public static final State FILL_INTERESTED=new FillInterestedState();
-    
+
     public static final State FILLING=new FillingState();
-    
+
     public static final State REFILLING=new RefillingState();
 
     public static final State FILLING_FILL_INTERESTED=new FillingFillInterestedState("FILLING_FILL_INTERESTED");
-    
+
     public class NestedState extends State
     {
         private final State _nested;
-        
+
         NestedState(State nested)
         {
             super("NESTED("+nested+")");
@@ -465,19 +475,19 @@ public abstract class AbstractConnection implements Connection
         {
             return new NestedState(_nested.onFillable());
         }
-        
+
         @Override
         State onFilled()
         {
             return new NestedState(_nested.onFilled());
         }
     }
-    
-    
+
+
     public class FillingInterestedCallback extends NestedState
     {
         private final Callback _callback;
-        
+
         FillingInterestedCallback(Callback callback,State nested)
         {
             super("FILLING_INTERESTED_CALLBACK",nested==FILLING?REFILLING:nested);
@@ -517,13 +527,13 @@ public abstract class AbstractConnection implements Connection
                             break;
                     }
                     _callback.failed(x);
-                }  
+                }
             };
-            
+
             connection.getEndPoint().fillInterested(callback);
         }
     }
-    
+
     private final Runnable _runOnFillable = new Runnable()
     {
         @Override
@@ -544,10 +554,10 @@ public abstract class AbstractConnection implements Connection
             }
         }
     };
-    
-    
+
+
     private class ReadCallback implements Callback
-    {   
+    {
         @Override
         public void succeeded()
         {
@@ -577,7 +587,7 @@ public abstract class AbstractConnection implements Connection
                 }
             });
         }
-        
+
         @Override
         public String toString()
         {
index 8fa2cc8..44a38ec 100644 (file)
@@ -1,6 +1,6 @@
 //
 //  ========================================================================
-//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+//  Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
 //  ------------------------------------------------------------------------
 //  All rights reserved. This program and the accompanying materials
 //  are made available under the terms of the Eclipse Public License v1.0
@@ -23,6 +23,7 @@ import java.net.InetSocketAddress;
 import java.nio.ByteBuffer;
 import java.util.concurrent.TimeoutException;
 
+import org.eclipse.jetty.util.BufferUtil;
 import org.eclipse.jetty.util.Callback;
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
@@ -44,7 +45,7 @@ public abstract class AbstractEndPoint extends IdleTimeout implements EndPoint
             return AbstractEndPoint.this.needsFill();
         }
     };
-    
+
     private final WriteFlusher _writeFlusher = new WriteFlusher(this)
     {
         @Override
@@ -78,7 +79,7 @@ public abstract class AbstractEndPoint extends IdleTimeout implements EndPoint
     {
         return _remote;
     }
-    
+
     @Override
     public Connection getConnection()
     {
@@ -94,7 +95,8 @@ public abstract class AbstractEndPoint extends IdleTimeout implements EndPoint
     @Override
     public void onOpen()
     {
-        LOG.debug("onOpen {}",this);
+        if (LOG.isDebugEnabled())
+            LOG.debug("onOpen {}",this);
         super.onOpen();
     }
 
@@ -102,11 +104,12 @@ public abstract class AbstractEndPoint extends IdleTimeout implements EndPoint
     public void onClose()
     {
         super.onClose();
-        LOG.debug("onClose {}",this);
+        if (LOG.isDebugEnabled())
+            LOG.debug("onClose {}",this);
         _writeFlusher.onClose();
         _fillInterest.onClose();
     }
-    
+
     @Override
     public void close()
     {
@@ -147,24 +150,45 @@ public abstract class AbstractEndPoint extends IdleTimeout implements EndPoint
         boolean input_shutdown=isInputShutdown();
         boolean fillFailed = _fillInterest.onFail(timeout);
         boolean writeFailed = _writeFlusher.onFail(timeout);
-        
-        // If the endpoint is half closed and there was no onFail handling, the close here
-        // This handles the situation where the connection has completed its close handling 
+
+        // If the endpoint is half closed and there was no fill/write handling, then close here.
+        // This handles the situation where the connection has completed its close handling
         // and the endpoint is half closed, but the other party does not complete the close.
         // This perhaps should not check for half closed, however the servlet spec case allows
-        // for a dispatched servlet or suspended request to extend beyond the connections idle 
-        // time.  So if this test would always close an idle endpoint that is not handled, then 
+        // for a dispatched servlet or suspended request to extend beyond the connections idle
+        // time.  So if this test would always close an idle endpoint that is not handled, then
         // we would need a mode to ignore timeouts for some HTTP states
         if (isOpen() && (output_shutdown || input_shutdown) && !(fillFailed || writeFailed))
             close();
-        else 
+        else
             LOG.debug("Ignored idle endpoint {}",this);
     }
 
+    @Override
+    public void upgrade(Connection newConnection)
+    {
+        Connection old_connection = getConnection();
+
+        if (LOG.isDebugEnabled())
+            LOG.debug("{} upgrading from {} to {}", this, old_connection, newConnection);
+
+        ByteBuffer prefilled = (old_connection instanceof Connection.UpgradeFrom)
+                ?((Connection.UpgradeFrom)old_connection).onUpgradeFrom():null;
+        old_connection.onClose();
+        old_connection.getEndPoint().setConnection(newConnection);
+
+        if (newConnection instanceof Connection.UpgradeTo)
+            ((Connection.UpgradeTo)newConnection).onUpgradeTo(prefilled);
+        else if (BufferUtil.hasContent(prefilled))
+            throw new IllegalStateException();
+
+        newConnection.onOpen();
+    }
+
     @Override
     public String toString()
     {
-        return String.format("%s@%x{%s<->%d,%s,%s,%s,%s,%s,%d,%s}",
+        return String.format("%s@%x{%s<->%d,%s,%s,%s,%s,%s,%d/%d,%s}",
                 getClass().getSimpleName(),
                 hashCode(),
                 getRemoteAddress(),
@@ -174,6 +198,7 @@ public abstract class AbstractEndPoint extends IdleTimeout implements EndPoint
                 isOutputShutdown()?"OSHUT":"out",
                 _fillInterest.isInterested()?"R":"-",
                 _writeFlusher.isInProgress()?"W":"-",
+                getIdleFor(),
                 getIdleTimeout(),
                 getConnection()==null?null:getConnection().getClass().getSimpleName());
     }
index fa3a01d..86b4972 100644 (file)
@@ -1,6 +1,6 @@
 //
 //  ========================================================================
-//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+//  Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
 //  ------------------------------------------------------------------------
 //  All rights reserved. This program and the accompanying materials
 //  are made available under the terms of the Eclipse Public License v1.0
index 4b8c527..73cea8c 100644 (file)
@@ -1,6 +1,6 @@
 //
 //  ========================================================================
-//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+//  Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
 //  ------------------------------------------------------------------------
 //  All rights reserved. This program and the accompanying materials
 //  are made available under the terms of the Eclipse Public License v1.0
index 302adde..4b5bada 100644 (file)
@@ -1,6 +1,6 @@
 //
 //  ========================================================================
-//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+//  Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
 //  ------------------------------------------------------------------------
 //  All rights reserved. This program and the accompanying materials
 //  are made available under the terms of the Eclipse Public License v1.0
index c65ca0c..5ce60e7 100644 (file)
@@ -1,6 +1,6 @@
 //
 //  ========================================================================
-//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+//  Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
 //  ------------------------------------------------------------------------
 //  All rights reserved. This program and the accompanying materials
 //  are made available under the terms of the Eclipse Public License v1.0
@@ -61,7 +61,8 @@ public class ChannelEndPoint extends AbstractEndPoint
 
     protected void shutdownInput()
     {
-        LOG.debug("ishut {}", this);
+        if (LOG.isDebugEnabled())
+            LOG.debug("ishut {}", this);
         _ishut=true;
         if (_oshut)
             close();
@@ -70,7 +71,8 @@ public class ChannelEndPoint extends AbstractEndPoint
     @Override
     public void shutdownOutput()
     {
-        LOG.debug("oshut {}", this);
+        if (LOG.isDebugEnabled())
+            LOG.debug("oshut {}", this);
         _oshut = true;
         if (_channel.isOpen())
         {
@@ -109,7 +111,8 @@ public class ChannelEndPoint extends AbstractEndPoint
     public void close()
     {
         super.close();
-        LOG.debug("close {}", this);
+        if (LOG.isDebugEnabled())
+            LOG.debug("close {}", this);
         try
         {
             _channel.close();
index 7eb5931..a11fe8e 100644 (file)
@@ -1,6 +1,6 @@
 //
 //  ========================================================================
-//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+//  Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
 //  ------------------------------------------------------------------------
 //  All rights reserved. This program and the accompanying materials
 //  are made available under the terms of the Eclipse Public License v1.0
@@ -19,6 +19,7 @@
 package org.eclipse.jetty.io;
 
 import java.io.IOException;
+import java.nio.ByteBuffer;
 import java.util.Map;
 
 import org.eclipse.jetty.util.log.Log;
@@ -51,7 +52,7 @@ public interface ClientConnectionFactory
          * {@link EndPoint} associated with {@code oldConnection}, performing connection lifecycle management.
          * <p />
          * The {@code oldConnection} will be closed by invoking {@link org.eclipse.jetty.io.Connection#onClose()}
-         * and the {@code newConnection} will be opened by invoking {@link org.eclipse.jetty.io.Connection#onOpen()}.
+         * and the {@code newConnection} will be opened by invoking {@link org.eclipse.jetty.io.Connection#onOpen(ByteBuffer)}.
          * @param oldConnection the old connection to replace
          * @param newConnection the new connection replacement
          */
index 96baa01..5c7d564 100644 (file)
@@ -1,6 +1,6 @@
 //
 //  ========================================================================
-//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+//  Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
 //  ------------------------------------------------------------------------
 //  All rights reserved. This program and the accompanying materials
 //  are made available under the terms of the Eclipse Public License v1.0
 package org.eclipse.jetty.io;
 
 import java.io.Closeable;
-
-import org.eclipse.jetty.util.Callback;
+import java.nio.ByteBuffer;
 
 /**
  * <p>A {@link Connection} is associated to an {@link EndPoint} so that I/O events
  * happening on the {@link EndPoint} can be processed by the {@link Connection}.</p>
- * <p>A typical implementation of {@link Connection} overrides {@link #onOpen()} to
+ * <p>A typical implementation of {@link Connection} overrides {@link #onOpen(ByteBuffer)} to
  * {@link EndPoint#fillInterested(Callback) set read interest} on the {@link EndPoint},
  * and when the {@link EndPoint} signals read readyness, this {@link Connection} can
  * read bytes from the network and interpret them.</p>
@@ -34,10 +33,6 @@ public interface Connection extends Closeable
 {
     public void addListener(Listener listener);
 
-    /**
-     * <p>Callback method invoked when this {@link Connection} is opened.</p>
-     * <p>Creators of the connection implementation are responsible for calling this method.</p>
-     */
     public void onOpen();
 
     /**
@@ -50,7 +45,7 @@ public interface Connection extends Closeable
      * @return the {@link EndPoint} associated with this {@link Connection}
      */
     public EndPoint getEndPoint();
-
+    
     /**
      * <p>Performs a logical close of this connection.</p>
      * <p>For simple connections, this may just mean to delegate the close to the associated
@@ -66,6 +61,30 @@ public interface Connection extends Closeable
     public long getBytesOut();
     public long getCreatedTimeStamp();
     
+    public interface UpgradeFrom extends Connection
+    {
+        /* ------------------------------------------------------------ */
+        /** Take the input buffer from the connection on upgrade.
+         * <p>This method is used to take any unconsumed input from
+         * a connection during an upgrade.
+         * @return A buffer of unconsumed input. The caller must return the buffer
+         * to the bufferpool when consumed and this connection must not.
+         */
+        ByteBuffer onUpgradeFrom();
+    }
+    
+    public interface UpgradeTo extends Connection
+    {
+        /**
+         * <p>Callback method invoked when this {@link Connection} is upgraded.</p>
+         * <p>This must be called before {@link #onOpen()}.</p>
+         * @param prefilledBuffer An optional buffer that can contain prefilled data. Typically this
+         * results from an upgrade of one protocol to the other where the old connection has buffered
+         * data destined for the new connection.  The new connection must take ownership of the buffer
+         * and is responsible for returning it to the buffer pool
+         */
+        void onUpgradeTo(ByteBuffer prefilled);
+    }
     
     public interface Listener
     {
index 87adb40..c96dcb8 100644 (file)
@@ -1,6 +1,6 @@
 //
 //  ========================================================================
-//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+//  Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
 //  ------------------------------------------------------------------------
 //  All rights reserved. This program and the accompanying materials
 //  are made available under the terms of the Eclipse Public License v1.0
@@ -26,8 +26,6 @@ import java.nio.channels.ReadPendingException;
 import java.nio.channels.WritePendingException;
 
 import org.eclipse.jetty.util.Callback;
-import org.eclipse.jetty.util.FutureCallback;
-
 
 /**
  *
@@ -224,6 +222,7 @@ public interface EndPoint extends Closeable
     /**
      * @param connection the {@link Connection} associated with this {@link EndPoint}
      * @see #getConnection()
+     * @see #upgrade(Connection)
      */
     void setConnection(Connection connection);
 
@@ -239,5 +238,13 @@ public interface EndPoint extends Closeable
      */
     void onClose();
 
-    
+
+    /** Upgrade connections.
+     * Close the old connection, update the endpoint and open the new connection.
+     * If the oldConnection is an instance of {@link Connection.UpgradeFrom} then
+     * a prefilled buffer is requested and passed to the newConnection if it is an instance
+     * of {@link Connection.UpgradeTo}
+     * @param newConnection The connection to upgrade to
+     */
+    public void upgrade(Connection newConnection);
 }
index 72042f4..29aa5e0 100644 (file)
@@ -1,6 +1,6 @@
 //
 //  ========================================================================
-//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+//  Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
 //  ------------------------------------------------------------------------
 //  All rights reserved. This program and the accompanying materials
 //  are made available under the terms of the Eclipse Public License v1.0
index b2c3f68..a234397 100644 (file)
@@ -1,6 +1,6 @@
 //
 //  ========================================================================
-//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+//  Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
 //  ------------------------------------------------------------------------
 //  All rights reserved. This program and the accompanying materials
 //  are made available under the terms of the Eclipse Public License v1.0
@@ -58,7 +58,7 @@ public abstract class FillInterest
         
         if (!_interested.compareAndSet(null,callback))
         {
-            LOG.warn("Read pending for "+_interested.get()+" pervented "+callback);
+            LOG.warn("Read pending for "+_interested.get()+" prevented "+callback);
             throw new ReadPendingException();
         }
         try
index 8b251ac..d8a6c27 100644 (file)
@@ -1,6 +1,6 @@
 //
 //  ========================================================================
-//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+//  Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
 //  ------------------------------------------------------------------------
 //  All rights reserved. This program and the accompanying materials
 //  are made available under the terms of the Eclipse Public License v1.0
@@ -66,6 +66,11 @@ public abstract class IdleTimeout
         return _idleTimestamp;
     }
 
+    public long getIdleFor()
+    {
+        return System.currentTimeMillis() - getIdleTimestamp();
+    }
+
     public long getIdleTimeout()
     {
         return _idleTimeout;
@@ -142,13 +147,15 @@ public abstract class IdleTimeout
             long idleElapsed = System.currentTimeMillis() - idleTimestamp;
             long idleLeft = idleTimeout - idleElapsed;
 
-            LOG.debug("{} idle timeout check, elapsed: {} ms, remaining: {} ms", this, idleElapsed, idleLeft);
+            if (LOG.isDebugEnabled())
+                LOG.debug("{} idle timeout check, elapsed: {} ms, remaining: {} ms", this, idleElapsed, idleLeft);
 
             if (idleTimestamp != 0 && idleTimeout > 0)
             {
                 if (idleLeft <= 0)
                 {
-                    LOG.debug("{} idle timeout expired", this);
+                    if (LOG.isDebugEnabled())
+                        LOG.debug("{} idle timeout expired", this);
                     try
                     {
                         onIdleExpired(new TimeoutException("Idle timeout expired: " + idleElapsed + "/" + idleTimeout + " ms"));
index a6fc756..f9ac66e 100644 (file)
@@ -1,6 +1,6 @@
 //
 //  ========================================================================
-//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+//  Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
 //  ------------------------------------------------------------------------
 //  All rights reserved. This program and the accompanying materials
 //  are made available under the terms of the Eclipse Public License v1.0
@@ -19,7 +19,9 @@
 package org.eclipse.jetty.io;
 
 import java.nio.ByteBuffer;
+import java.util.concurrent.atomic.AtomicLong;
 
+import org.eclipse.jetty.util.BufferUtil;
 import org.eclipse.jetty.util.LeakDetector;
 import org.eclipse.jetty.util.component.ContainerLifeCycle;
 import org.eclipse.jetty.util.log.Log;
@@ -31,14 +33,24 @@ public class LeakTrackingByteBufferPool extends ContainerLifeCycle implements By
 
     private final LeakDetector<ByteBuffer> leakDetector = new LeakDetector<ByteBuffer>()
     {
+        public String id(ByteBuffer resource)
+        {
+            return BufferUtil.toIDString(resource);
+        }
+
         @Override
         protected void leaked(LeakInfo leakInfo)
         {
+            leaked.incrementAndGet();
             LeakTrackingByteBufferPool.this.leaked(leakInfo);
         }
     };
-    
+
+    private final static boolean NOISY = Boolean.getBoolean(LeakTrackingByteBufferPool.class.getName() + ".NOISY");
     private final ByteBufferPool delegate;
+    private final AtomicLong leakedReleases = new AtomicLong(0);
+    private final AtomicLong leakedAcquires = new AtomicLong(0);
+    private final AtomicLong leaked = new AtomicLong(0);
 
     public LeakTrackingByteBufferPool(ByteBufferPool delegate)
     {
@@ -51,8 +63,13 @@ public class LeakTrackingByteBufferPool extends ContainerLifeCycle implements By
     public ByteBuffer acquire(int size, boolean direct)
     {
         ByteBuffer buffer = delegate.acquire(size, direct);
-        if (!leakDetector.acquired(buffer))
-            LOG.warn("ByteBuffer {}@{} not tracked", buffer, System.identityHashCode(buffer));
+        boolean leaked = leakDetector.acquired(buffer);
+        if (NOISY || !leaked)
+        {
+            leakedAcquires.incrementAndGet();
+            LOG.info(String.format("ByteBuffer acquire %s leaked.acquired=%s", leakDetector.id(buffer), leaked ? "normal" : "LEAK"),
+                    new Throwable("LeakStack.Acquire"));
+        }
         return buffer;
     }
 
@@ -61,11 +78,46 @@ public class LeakTrackingByteBufferPool extends ContainerLifeCycle implements By
     {
         if (buffer == null)
             return;
-        if (!leakDetector.released(buffer))
-            LOG.warn("ByteBuffer {}@{} released but not acquired", buffer, System.identityHashCode(buffer));
+        boolean leaked = leakDetector.released(buffer);
+        if (NOISY || !leaked)
+        {
+            leakedReleases.incrementAndGet();
+            LOG.info(String.format("ByteBuffer release %s leaked.released=%s", leakDetector.id(buffer), leaked ? "normal" : "LEAK"), new Throwable(
+                    "LeakStack.Release"));
+        }
         delegate.release(buffer);
     }
 
+    public void clearTracking()
+    {
+        leakedAcquires.set(0);
+        leakedReleases.set(0);
+    }
+
+    /**
+     * @return count of BufferPool.acquire() calls that detected a leak
+     */
+    public long getLeakedAcquires()
+    {
+        return leakedAcquires.get();
+    }
+
+    /**
+     * @return count of BufferPool.release() calls that detected a leak
+     */
+    public long getLeakedReleases()
+    {
+        return leakedReleases.get();
+    }
+
+    /**
+     * @return count of resources that were acquired but not released
+     */
+    public long getLeakedResources()
+    {
+        return leaked.get();
+    }
+
     protected void leaked(LeakDetector<ByteBuffer>.LeakInfo leakInfo)
     {
         LOG.warn("ByteBuffer " + leakInfo.getResourceDescription() + " leaked at:", leakInfo.getStackFrames());
index b331904..bc9e814 100644 (file)
@@ -1,6 +1,6 @@
 //
 //  ========================================================================
-//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+//  Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
 //  ------------------------------------------------------------------------
 //  All rights reserved. This program and the accompanying materials
 //  are made available under the terms of the Eclipse Public License v1.0
@@ -23,6 +23,7 @@ import java.util.Queue;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentLinkedQueue;
 import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.atomic.AtomicInteger;
 
 import org.eclipse.jetty.util.BufferUtil;
 
@@ -56,13 +57,19 @@ public class MappedByteBufferPool implements ByteBufferPool
         if (result == null)
         {
             int capacity = bucket * factor;
-            result = direct ? BufferUtil.allocateDirect(capacity) : BufferUtil.allocate(capacity);
+            result = newByteBuffer(capacity, direct);
         }
 
         BufferUtil.clear(result);
         return result;
     }
 
+    protected ByteBuffer newByteBuffer(int capacity, boolean direct)
+    {
+        return direct ? BufferUtil.allocateDirect(capacity)
+                      : BufferUtil.allocate(capacity);
+    }
+
     @Override
     public void release(ByteBuffer buffer)
     {
@@ -108,4 +115,20 @@ public class MappedByteBufferPool implements ByteBufferPool
     {
         return direct ? directBuffers : heapBuffers;
     }
+
+    public static class Tagged extends MappedByteBufferPool
+    {
+        private final AtomicInteger tag = new AtomicInteger();
+
+        @Override
+        protected ByteBuffer newByteBuffer(int capacity, boolean direct)
+        {
+            ByteBuffer buffer = super.newByteBuffer(capacity + 4, direct);
+            buffer.limit(buffer.capacity());
+            buffer.putInt(tag.incrementAndGet());
+            ByteBuffer slice = buffer.slice();
+            BufferUtil.clear(slice);
+            return slice;
+        }
+    }
 }
index cd05630..f0a8b1f 100644 (file)
@@ -1,6 +1,6 @@
 //
 //  ========================================================================
-//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+//  Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
 //  ------------------------------------------------------------------------
 //  All rights reserved. This program and the accompanying materials
 //  are made available under the terms of the Eclipse Public License v1.0
 package org.eclipse.jetty.io;
 
 import java.io.IOException;
+import java.nio.ByteBuffer;
 import java.util.Map;
 import java.util.concurrent.Executor;
+
 import javax.net.ssl.SSLEngine;
 
 import org.eclipse.jetty.util.BufferUtil;
index ff38600..5f20837 100644 (file)
@@ -1,6 +1,6 @@
 //
 //  ========================================================================
-//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+//  Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
 //  ------------------------------------------------------------------------
 //  All rights reserved. This program and the accompanying materials
 //  are made available under the terms of the Eclipse Public License v1.0
index 6a4239f..3a04207 100644 (file)
@@ -1,6 +1,6 @@
 //
 //  ========================================================================
-//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+//  Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
 //  ------------------------------------------------------------------------
 //  All rights reserved. This program and the accompanying materials
 //  are made available under the terms of the Eclipse Public License v1.0
index a4b6f7d..bf7606b 100644 (file)
@@ -1,6 +1,6 @@
 //
 //  ========================================================================
-//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+//  Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
 //  ------------------------------------------------------------------------
 //  All rights reserved. This program and the accompanying materials
 //  are made available under the terms of the Eclipse Public License v1.0
@@ -25,7 +25,6 @@ import java.nio.channels.SelectionKey;
 import java.nio.channels.SocketChannel;
 import java.util.List;
 
-import org.eclipse.jetty.util.BufferUtil;
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
 import org.eclipse.jetty.util.thread.Scheduler;
index 88c33f7..5f0e95b 100644 (file)
@@ -1,6 +1,6 @@
 //
 //  ========================================================================
-//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+//  Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
 //  ------------------------------------------------------------------------
 //  All rights reserved. This program and the accompanying materials
 //  are made available under the terms of the Eclipse Public License v1.0
index 3050402..6eec73d 100644 (file)
@@ -1,6 +1,6 @@
 //
 //  ========================================================================
-//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+//  Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
 //  ------------------------------------------------------------------------
 //  All rights reserved. This program and the accompanying materials
 //  are made available under the terms of the Eclipse Public License v1.0
@@ -132,18 +132,21 @@ public class SelectChannelEndPoint extends ChannelEndPoint implements SelectorMa
             {
                 if (_interestOps.compareAndSet(oldInterestOps, newInterestOps))
                 {
-                    LOG.debug("Local interests updated {} -> {} for {}", oldInterestOps, newInterestOps, this);
+                    if (LOG.isDebugEnabled())
+                        LOG.debug("Local interests updating {} -> {} for {}", oldInterestOps, newInterestOps, this);
                     _selector.updateKey(_updateTask);
                 }
                 else
                 {
-                    LOG.debug("Local interests update conflict: now {}, was {}, attempted {} for {}", _interestOps.get(), oldInterestOps, newInterestOps, this);
+                    if (LOG.isDebugEnabled())
+                        LOG.debug("Local interests update conflict: now {}, was {}, attempted {} for {}", _interestOps.get(), oldInterestOps, newInterestOps, this);
                     continue;
                 }
             }
             else
             {
-                LOG.debug("Ignoring local interests update {} -> {} for {}", oldInterestOps, newInterestOps, this);
+                if (LOG.isDebugEnabled())
+                    LOG.debug("Ignoring local interests update {} -> {} for {}", oldInterestOps, newInterestOps, this);
             }
             break;
         }
@@ -152,8 +155,9 @@ public class SelectChannelEndPoint extends ChannelEndPoint implements SelectorMa
 
     private void setKeyInterests(int oldInterestOps, int newInterestOps)
     {
-        LOG.debug("Key interests updated {} -> {}", oldInterestOps, newInterestOps);
         _key.interestOps(newInterestOps);
+        if (LOG.isDebugEnabled())
+            LOG.debug("Key interests updated {} -> {} on {}", oldInterestOps, newInterestOps, this);
     }
 
     @Override
index fd3c6c6..26a18c3 100644 (file)
@@ -1,6 +1,6 @@
 //
 //  ========================================================================
-//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+//  Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
 //  ------------------------------------------------------------------------
 //  All rights reserved. This program and the accompanying materials
 //  are made available under the terms of the Eclipse Public License v1.0
@@ -41,6 +41,7 @@ import java.util.concurrent.atomic.AtomicReference;
 
 import org.eclipse.jetty.util.ConcurrentArrayQueue;
 import org.eclipse.jetty.util.TypeUtil;
+import org.eclipse.jetty.util.annotation.ManagedAttribute;
 import org.eclipse.jetty.util.component.AbstractLifeCycle;
 import org.eclipse.jetty.util.component.ContainerLifeCycle;
 import org.eclipse.jetty.util.component.Dumpable;
@@ -60,14 +61,15 @@ public abstract class SelectorManager extends AbstractLifeCycle implements Dumpa
     public static final String SUBMIT_KEY_UPDATES = "org.eclipse.jetty.io.SelectorManager.submitKeyUpdates";
     public static final int DEFAULT_CONNECT_TIMEOUT = 15000;
     protected static final Logger LOG = Log.getLogger(SelectorManager.class);
-    private final static boolean __submitKeyUpdates = Boolean.valueOf(System.getProperty(SUBMIT_KEY_UPDATES, "false"));
-    
+    private final static boolean __submitKeyUpdates = Boolean.valueOf(System.getProperty(SUBMIT_KEY_UPDATES, "true"));
+
     private final Executor executor;
     private final Scheduler scheduler;
     private final ManagedSelector[] _selectors;
     private long _connectTimeout = DEFAULT_CONNECT_TIMEOUT;
     private long _selectorIndex;
-    
+    private int _priorityDelta;
+
     protected SelectorManager(Executor executor, Scheduler scheduler)
     {
         this(executor, scheduler, (Runtime.getRuntime().availableProcessors() + 1) / 2);
@@ -112,6 +114,42 @@ public abstract class SelectorManager extends AbstractLifeCycle implements Dumpa
         _connectTimeout = milliseconds;
     }
 
+
+    @ManagedAttribute("The priority delta to apply to selector threads")
+    public int getSelectorPriorityDelta()
+    {
+        return _priorityDelta;
+    }
+
+    /**
+     * Sets the selector thread priority delta to the given amount.
+     * <p>This allows the selector threads to run at a different priority.
+     * Typically this would be used to lower the priority to give preference
+     * to handling previously accepted connections rather than accepting
+     * new connections.</p>
+     *
+     * @param selectorPriorityDelta the amount to change the thread priority
+     *                              delta to (may be negative)
+     * @see Thread#getPriority()
+     */
+    public void setSelectorPriorityDelta(int selectorPriorityDelta)
+    {
+        int oldDelta = _priorityDelta;
+        _priorityDelta = selectorPriorityDelta;
+        if (oldDelta != selectorPriorityDelta && isStarted())
+        {
+            for (ManagedSelector selector : _selectors)
+            {
+                Thread thread = selector._thread;
+                if (thread != null)
+                {
+                    int deltaDiff = selectorPriorityDelta - oldDelta;
+                    thread.setPriority(Math.max(Thread.MIN_PRIORITY, Math.min(Thread.MAX_PRIORITY, thread.getPriority() - deltaDiff)));
+                }
+            }
+        }
+    }
+
     /**
      * Executes the given task in a different thread.
      *
@@ -142,11 +180,13 @@ public abstract class SelectorManager extends AbstractLifeCycle implements Dumpa
 
     /**
      * <p>Registers a channel to perform a non-blocking connect.</p>
-     * <p>The channel must be set in non-blocking mode, and {@link SocketChannel#connect(SocketAddress)}
-     * must be called prior to calling this method.</p>
+     * <p>The channel must be set in non-blocking mode, {@link SocketChannel#connect(SocketAddress)}
+     * must be called prior to calling this method, and the connect operation must not be completed
+     * (the return value of {@link SocketChannel#connect(SocketAddress)} must be false).</p>
      *
      * @param channel    the channel to register
      * @param attachment the attachment object
+     * @see #accept(SocketChannel, Object)
      */
     public void connect(SocketChannel channel, Object attachment)
     {
@@ -154,33 +194,44 @@ public abstract class SelectorManager extends AbstractLifeCycle implements Dumpa
         set.submit(set.new Connect(channel, attachment));
     }
 
+    /**
+     * @see #accept(SocketChannel, Object)
+     */
+    public void accept(SocketChannel channel)
+    {
+        accept(channel, null);
+    }
+
     /**
      * <p>Registers a channel to perform non-blocking read/write operations.</p>
      * <p>This method is called just after a channel has been accepted by {@link ServerSocketChannel#accept()},
-     * or just after having performed a blocking connect via {@link Socket#connect(SocketAddress, int)}.</p>
+     * or just after having performed a blocking connect via {@link Socket#connect(SocketAddress, int)}, or
+     * just after a non-blocking connect via {@link SocketChannel#connect(SocketAddress)} that completed
+     * successfully.</p>
      *
      * @param channel the channel to register
+     * @param attachment the attachment object
      */
-    public void accept(final SocketChannel channel)
+    public void accept(SocketChannel channel, Object attachment)
     {
         final ManagedSelector selector = chooseSelector();
-        selector.submit(selector.new Accept(channel));
+        selector.submit(selector.new Accept(channel, attachment));
     }
-    
+
     /**
      * <p>Registers a server channel for accept operations.
      * When a {@link SocketChannel} is accepted from the given {@link ServerSocketChannel}
      * then the {@link #accepted(SocketChannel)} method is called, which must be
      * overridden by a derivation of this class to handle the accepted channel
-     * 
+     *
      * @param server the server channel to register
      */
-    public void acceptor(final ServerSocketChannel server)
+    public void acceptor(ServerSocketChannel server)
     {
         final ManagedSelector selector = chooseSelector();
         selector.submit(selector.new Acceptor(server));
     }
-    
+
     /**
      * Callback method when a channel is accepted from the {@link ServerSocketChannel}
      * passed to {@link #acceptor(ServerSocketChannel)}.
@@ -263,7 +314,8 @@ public abstract class SelectorManager extends AbstractLifeCycle implements Dumpa
             if (isRunning())
                 LOG.warn("Exception while notifying connection " + connection, x);
             else
-                LOG.debug("Exception while notifying connection {}",connection, x);
+                LOG.debug("Exception while notifying connection " + connection, x);
+            throw x;
         }
     }
 
@@ -377,17 +429,19 @@ public abstract class SelectorManager extends AbstractLifeCycle implements Dumpa
         @Override
         protected void doStop() throws Exception
         {
-            LOG.debug("Stopping {}", this);
+            if (LOG.isDebugEnabled())
+                LOG.debug("Stopping {}", this);
             Stop stop = new Stop();
             submit(stop);
             stop.await(getStopTimeout());
-            LOG.debug("Stopped {}", this);
+            if (LOG.isDebugEnabled())
+                LOG.debug("Stopped {}", this);
         }
 
         /**
          * Submit a task to update a selector key.  If the System property {@link SelectorManager#SUBMIT_KEY_UPDATES}
-         * is set true (default is false), the task is passed to {@link #submit(Runnable)}.   Otherwise it is run immediately and the selector 
-         * woken up if need be.   
+         * is set true (default is false), the task is passed to {@link #submit(Runnable)}.   Otherwise it is run immediately and the selector
+         * woken up if need be.
          * @param update the update to a key
          */
         public void updateKey(Runnable update)
@@ -398,12 +452,16 @@ public abstract class SelectorManager extends AbstractLifeCycle implements Dumpa
             }
             else
             {
-                runChange(update);
+                // Run only 1 change at once
+                synchronized (this)
+                {
+                    runChange(update);
+                }
                 if (_state.compareAndSet(State.SELECT, State.WAKEUP))
                    wakeup();
             }
         }
-        
+
         /**
          * <p>Submits a change to be executed in the selector thread.</p>
          * <p>Changes may be submitted from any thread, and the selector thread woken up
@@ -419,7 +477,8 @@ public abstract class SelectorManager extends AbstractLifeCycle implements Dumpa
             // change to the queue and process the state.
 
             _changes.offer(change);
-            LOG.debug("Queued change {}", change);
+            if (LOG.isDebugEnabled())
+                LOG.debug("Queued change {}", change);
 
             out: while (true)
             {
@@ -463,7 +522,8 @@ public abstract class SelectorManager extends AbstractLifeCycle implements Dumpa
         {
             try
             {
-                LOG.debug("Running change {}", change);
+                if (LOG.isDebugEnabled())
+                    LOG.debug("Running change {}", change);
                 change.run();
             }
             catch (Throwable x)
@@ -477,18 +537,27 @@ public abstract class SelectorManager extends AbstractLifeCycle implements Dumpa
         {
             _thread = Thread.currentThread();
             String name = _thread.getName();
+            int priority = _thread.getPriority();
             try
             {
-                _thread.setName(name + "-selector-" + SelectorManager.this.getClass().getSimpleName()+"@"+Integer.toHexString(SelectorManager.this.hashCode())+"/"+_id);
-                LOG.debug("Starting {} on {}", _thread, this);
+                if (_priorityDelta != 0)
+                    _thread.setPriority(Math.max(Thread.MIN_PRIORITY, Math.min(Thread.MAX_PRIORITY, priority + _priorityDelta)));
+
+                _thread.setName(String.format("%s-selector-%s@%h/%d", name, SelectorManager.this.getClass().getSimpleName(), SelectorManager.this.hashCode(), _id));
+                if (LOG.isDebugEnabled())
+                    LOG.debug("Starting {} on {}", _thread, this);
                 while (isRunning())
                     select();
-                runChanges();
+                while(isStopping())
+                    runChanges();
             }
             finally
             {
-                LOG.debug("Stopped {} on {}", _thread, this);
+                if (LOG.isDebugEnabled())
+                    LOG.debug("Stopped {} on {}", _thread, this);
                 _thread.setName(name);
+                if (_priorityDelta != 0)
+                    _thread.setPriority(priority);
             }
         }
 
@@ -519,7 +588,7 @@ public abstract class SelectorManager extends AbstractLifeCycle implements Dumpa
                             _state.set(State.CHANGES);
                             continue;
                         default:
-                            throw new IllegalStateException();    
+                            throw new IllegalStateException();
                     }
                 }
                 // Must check first for SELECT and *then* for WAKEUP
@@ -607,10 +676,16 @@ public abstract class SelectorManager extends AbstractLifeCycle implements Dumpa
                 boolean connected = finishConnect(channel);
                 if (connected)
                 {
-                    connect.timeout.cancel();
-                    key.interestOps(0);
-                    EndPoint endpoint = createEndPoint(channel, key);
-                    key.attach(endpoint);
+                    if (connect.timeout.cancel())
+                    {
+                        key.interestOps(0);
+                        EndPoint endpoint = createEndPoint(channel, key);
+                        key.attach(endpoint);
+                    }
+                    else
+                    {
+                        throw new SocketTimeoutException("Concurrent Connect Timeout");
+                    }
                 }
                 else
                 {
@@ -622,7 +697,7 @@ public abstract class SelectorManager extends AbstractLifeCycle implements Dumpa
                 connect.failed(x);
             }
         }
-        
+
         private void processAccept(SelectionKey key)
         {
             ServerSocketChannel server = (ServerSocketChannel)key.channel();
@@ -671,13 +746,15 @@ public abstract class SelectorManager extends AbstractLifeCycle implements Dumpa
             Connection connection = newConnection(channel, endPoint, selectionKey.attachment());
             endPoint.setConnection(connection);
             connectionOpened(connection);
-            LOG.debug("Created {}", endPoint);
+            if (LOG.isDebugEnabled())
+                LOG.debug("Created {}", endPoint);
             return endPoint;
         }
 
         public void destroyEndPoint(EndPoint endPoint)
         {
-            LOG.debug("Destroyed {}", endPoint);
+            if (LOG.isDebugEnabled())
+                LOG.debug("Destroyed {}", endPoint);
             Connection connection = endPoint.getConnection();
             if (connection != null)
                 connectionClosed(connection);
@@ -792,7 +869,8 @@ public abstract class SelectorManager extends AbstractLifeCycle implements Dumpa
                 try
                 {
                     SelectionKey key = _channel.register(_selector, SelectionKey.OP_ACCEPT, null);
-                    LOG.debug("{} acceptor={}", this, key);
+                    if (LOG.isDebugEnabled())
+                        LOG.debug("{} acceptor={}", this, key);
                 }
                 catch (Throwable x)
                 {
@@ -804,11 +882,13 @@ public abstract class SelectorManager extends AbstractLifeCycle implements Dumpa
 
         private class Accept implements Runnable
         {
-            private final SocketChannel _channel;
+            private final SocketChannel channel;
+            private final Object attachment;
 
-            public Accept(SocketChannel channel)
+            private Accept(SocketChannel channel, Object attachment)
             {
-                this._channel = channel;
+                this.channel = channel;
+                this.attachment = attachment;
             }
 
             @Override
@@ -816,13 +896,13 @@ public abstract class SelectorManager extends AbstractLifeCycle implements Dumpa
             {
                 try
                 {
-                    SelectionKey key = _channel.register(_selector, 0, null);
-                    EndPoint endpoint = createEndPoint(_channel, key);
+                    SelectionKey key = channel.register(_selector, 0, attachment);
+                    EndPoint endpoint = createEndPoint(channel, key);
                     key.attach(endpoint);
                 }
                 catch (Throwable x)
                 {
-                    closeNoExceptions(_channel);
+                    closeNoExceptions(channel);
                     LOG.debug(x);
                 }
             }
@@ -835,7 +915,7 @@ public abstract class SelectorManager extends AbstractLifeCycle implements Dumpa
             private final Object attachment;
             private final Scheduler.Task timeout;
 
-            public Connect(SocketChannel channel, Object attachment)
+            private Connect(SocketChannel channel, Object attachment)
             {
                 this.channel = channel;
                 this.attachment = attachment;
@@ -855,7 +935,7 @@ public abstract class SelectorManager extends AbstractLifeCycle implements Dumpa
                 }
             }
 
-            protected void failed(Throwable failure)
+            private void failed(Throwable failure)
             {
                 if (failed.compareAndSet(false, true))
                 {
@@ -881,8 +961,9 @@ public abstract class SelectorManager extends AbstractLifeCycle implements Dumpa
                 SocketChannel channel = connect.channel;
                 if (channel.isConnectionPending())
                 {
-                    LOG.debug("Channel {} timed out while connecting, closing it", channel);
-                    connect.failed(new SocketTimeoutException());
+                    if (LOG.isDebugEnabled())
+                        LOG.debug("Channel {} timed out while connecting, closing it", channel);
+                    connect.failed(new SocketTimeoutException("Connect Timeout"));
                 }
             }
         }
index 6898ddf..928605e 100644 (file)
@@ -1,6 +1,6 @@
 //
 //  ========================================================================
-//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+//  Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
 //  ------------------------------------------------------------------------
 //  All rights reserved. This program and the accompanying materials
 //  are made available under the terms of the Eclipse Public License v1.0
@@ -129,7 +129,8 @@ public class UncheckedPrintWriter extends PrintWriter
             _ioException.initCause(th);
         }
 
-        LOG.debug(th);
+        if (LOG.isDebugEnabled())
+            LOG.debug(th);
     }
 
 
index fccc622..ed594c1 100644 (file)
@@ -1,6 +1,6 @@
 //
 //  ========================================================================
-//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+//  Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
 //  ------------------------------------------------------------------------
 //  All rights reserved. This program and the accompanying materials
 //  are made available under the terms of the Eclipse Public License v1.0
@@ -46,7 +46,7 @@ abstract public class WriteFlusher
 {
     private static final Logger LOG = Log.getLogger(WriteFlusher.class);
     private static final boolean DEBUG = LOG.isDebugEnabled(); // Easy for the compiler to remove the code if DEBUG==false
-    private static final ByteBuffer[] EMPTY_BUFFERS = new ByteBuffer[0];
+    private static final ByteBuffer[] EMPTY_BUFFERS = new ByteBuffer[]{BufferUtil.EMPTY_BUFFER};
     private static final EnumMap<StateType, Set<StateType>> __stateTransitions = new EnumMap<>(StateType.class);
     private static final State __IDLE = new IdleState();
     private static final State __WRITING = new WritingState();
@@ -245,7 +245,7 @@ abstract public class WriteFlusher
         private PendingState(ByteBuffer[] buffers, Callback callback)
         {
             super(StateType.PENDING);
-            _buffers = compact(buffers);
+            _buffers = buffers;
             _callback = callback;
         }
 
@@ -269,41 +269,6 @@ abstract public class WriteFlusher
             if (_callback!=null)
                 _callback.succeeded();
         }
-
-        /**
-         * Compacting the buffers is needed because the semantic of WriteFlusher is
-         * to write the buffers and if the caller sees that the buffer is consumed,
-         * then it can recycle it.
-         * If we do not compact, then it is possible that we store a consumed buffer,
-         * which is then recycled and refilled; when the WriteFlusher is invoked to
-         * complete the write, it will write the refilled bytes, garbling the content.
-         *
-         * @param buffers the buffers to compact
-         * @return the compacted buffers
-         */
-        private ByteBuffer[] compact(ByteBuffer[] buffers)
-        {
-            int length = buffers.length;
-
-            // Just one element, no need to compact
-            if (length < 2)
-                return buffers;
-
-            // How many still have content ?
-            int consumed = 0;
-            while (consumed < length && BufferUtil.isEmpty(buffers[consumed]))
-                ++consumed;
-
-            // All of them still have content, no need to compact
-            if (consumed == 0)
-                return buffers;
-
-            // None has content, return empty
-            if (consumed == length)
-                return EMPTY_BUFFERS;
-
-            return Arrays.copyOfRange(buffers,consumed,length);
-        }
     }
 
     /**
@@ -334,22 +299,19 @@ abstract public class WriteFlusher
 
         try
         {
-            boolean flushed=_endPoint.flush(buffers);
-            if (DEBUG)
-                LOG.debug("flushed {}", flushed);
+            buffers=flush(buffers);
 
-            // Are we complete?
-            for (ByteBuffer b : buffers)
+            // if we are incomplete?
+            if (buffers!=null)
             {
-                if (!flushed||BufferUtil.hasContent(b))
-                {
-                    PendingState pending=new PendingState(buffers, callback);
-                    if (updateState(__WRITING,pending))
-                        onIncompleteFlushed();
-                    else
-                        fail(pending);
-                    return;
-                }
+                if (DEBUG)
+                    LOG.debug("flushed incomplete");
+                PendingState pending=new PendingState(buffers, callback);
+                if (updateState(__WRITING,pending))
+                    onIncompleteFlushed();
+                else
+                    fail(pending);
+                return;
             }
 
             // If updateState didn't succeed, we don't care as our buffers have been written
@@ -382,7 +344,7 @@ abstract public class WriteFlusher
      * {@link #onFail(Throwable)} or {@link #onClose()}
      */
     public void completeWrite()
-    {
+    {         
         if (DEBUG)
             LOG.debug("completeWrite: {}", this);
 
@@ -399,21 +361,20 @@ abstract public class WriteFlusher
         {
             ByteBuffer[] buffers = pending.getBuffers();
 
-            boolean flushed=_endPoint.flush(buffers);
-            if (DEBUG)
-                LOG.debug("flushed {}", flushed);
+            buffers=flush(buffers);
 
-            // Are we complete?
-            for (ByteBuffer b : buffers)
+            // if we are incomplete?
+            if (buffers!=null)
             {
-                if (!flushed || BufferUtil.hasContent(b))
-                {
-                    if (updateState(__COMPLETING,pending))
-                        onIncompleteFlushed();
-                    else
-                        fail(pending);
-                    return;
-                }
+                if (DEBUG)
+                    LOG.debug("flushed incomplete {}",BufferUtil.toDetailString(buffers));
+                if (buffers!=pending.getBuffers())
+                    pending=new PendingState(buffers, pending._callback);
+                if (updateState(__COMPLETING,pending))
+                    onIncompleteFlushed();
+                else
+                    fail(pending);
+                return;
             }
 
             // If updateState didn't succeed, we don't care as our buffers have been written
@@ -432,6 +393,49 @@ abstract public class WriteFlusher
         }
     }
 
+    /* ------------------------------------------------------------ */
+    /** Flush the buffers iteratively until no progress is made
+     * @param buffers The buffers to flush
+     * @return The unflushed buffers, or null if all flushed
+     * @throws IOException
+     */
+    protected ByteBuffer[] flush(ByteBuffer[] buffers) throws IOException
+    {
+        boolean progress=true;
+        while(progress && buffers!=null)
+        {
+            int before=buffers.length==0?0:buffers[0].remaining();
+            boolean flushed=_endPoint.flush(buffers);
+            int r=buffers.length==0?0:buffers[0].remaining();
+            
+            if (flushed)
+                return null;
+            
+            progress=before!=r;
+            
+            int not_empty=0;
+            while(r==0)
+            {
+                if (++not_empty==buffers.length)
+                {
+                    buffers=null;
+                    not_empty=0;
+                    break;
+                }
+                progress=true;
+                r=buffers[not_empty].remaining();
+            }
+
+            if (not_empty>0)
+                buffers=Arrays.copyOfRange(buffers,not_empty,buffers.length);
+        }        
+        
+        // If buffers is null, then flush has returned false but has consumed all the data!
+        // This is probably SSL being unable to flush the encrypted buffer, so return EMPTY_BUFFERS
+        // and that will keep this WriteFlusher pending.
+        return buffers==null?EMPTY_BUFFERS:buffers;
+    }
+    
     /* ------------------------------------------------------------ */
     /** Notify the flusher of a failure
      * @param cause The cause of the failure
index d08b473..0ee7214 100644 (file)
@@ -1,6 +1,6 @@
 //
 //  ========================================================================
-//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+//  Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
 //  ------------------------------------------------------------------------
 //  All rights reserved. This program and the accompanying materials
 //  are made available under the terms of the Eclipse Public License v1.0
index 6316823..9db1092 100644 (file)
@@ -1,6 +1,6 @@
 //
 //  ========================================================================
-//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+//  Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
 //  ------------------------------------------------------------------------
 //  All rights reserved. This program and the accompanying materials
&