Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJan Bartel2015-12-03 00:57:51 -0500
committerJan Bartel2015-12-03 00:57:51 -0500
commit9ff55cb301fa9f55215d35fa462ece42f71925a0 (patch)
tree76623ca74b0bf813bfb080eab1faae7d6b0c50ed
parent82ffc4f3554ef9d6070e2d2d8bfaf1ec7a9a44a7 (diff)
parent4bbd060ca8468f3289ef049379a7cf4ec20ececa (diff)
downloadorg.eclipse.jetty.project-9ff55cb301fa9f55215d35fa462ece42f71925a0.tar.gz
org.eclipse.jetty.project-9ff55cb301fa9f55215d35fa462ece42f71925a0.tar.xz
org.eclipse.jetty.project-9ff55cb301fa9f55215d35fa462ece42f71925a0.zip
Merge branch 'master' into session-refactor
Conflicts: jetty-infinispan/src/main/java/org/eclipse/jetty/session/infinispan/InfinispanSessionManager.java jetty-nosql/src/main/java/org/eclipse/jetty/nosql/NoSqlSessionManager.java jetty-nosql/src/main/java/org/eclipse/jetty/nosql/mongodb/MongoSessionIdManager.java jetty-nosql/src/main/java/org/eclipse/jetty/nosql/mongodb/MongoSessionManager.java jetty-server/src/main/java/org/eclipse/jetty/server/session/JDBCSessionIdManager.java jetty-server/src/main/java/org/eclipse/jetty/server/session/JDBCSessionManager.java tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/JdbcTestServer.java tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/MongoTestServer.java tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractInvalidationSessionTest.java
-rw-r--r--VERSION.txt87
-rw-r--r--aggregates/jetty-all-compact3/pom.xml2
-rw-r--r--aggregates/jetty-all/pom.xml2
-rw-r--r--aggregates/jetty-websocket-all/pom.xml4
-rw-r--r--apache-jsp/pom.xml6
-rw-r--r--apache-jsp/src/main/config/modules/apache-jsp.mod5
-rw-r--r--apache-jstl/pom.xml2
-rw-r--r--apache-jstl/src/main/config/modules/apache-jstl.mod5
-rw-r--r--examples/async-rest/async-rest-jar/pom.xml2
-rw-r--r--examples/async-rest/async-rest-webapp/pom.xml2
-rw-r--r--examples/async-rest/pom.xml2
-rw-r--r--examples/embedded/pom.xml2
-rw-r--r--examples/embedded/src/main/java/org/eclipse/jetty/embedded/Http2Server.java4
-rw-r--r--examples/embedded/src/main/java/org/eclipse/jetty/embedded/LikeJettyXml.java5
-rw-r--r--examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneWebApp.java3
-rw-r--r--examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneWebAppWithJsp.java3
-rw-r--r--examples/embedded/src/main/resources/java-util-logging.properties9
-rw-r--r--examples/embedded/src/main/resources/jetty-logging.properties6
-rw-r--r--examples/pom.xml2
-rw-r--r--jetty-alpn/jetty-alpn-client/pom.xml2
-rw-r--r--jetty-alpn/jetty-alpn-server/pom.xml2
-rw-r--r--jetty-alpn/jetty-alpn-server/src/main/config/modules/alpn-impl/alpn-1.8.0_60.mod8
-rw-r--r--jetty-alpn/jetty-alpn-server/src/main/config/modules/alpn-impl/alpn-1.8.0_65.mod8
-rw-r--r--jetty-alpn/jetty-alpn-server/src/main/config/modules/alpn-impl/alpn-1.8.0_66.mod8
-rw-r--r--jetty-alpn/jetty-alpn-server/src/main/config/modules/alpn.mod15
-rw-r--r--jetty-alpn/pom.xml2
-rw-r--r--jetty-annotations/pom.xml3
-rw-r--r--jetty-annotations/src/main/config/modules/annotations.mod8
-rw-r--r--jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AnnotationConfiguration.java31
-rw-r--r--jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AnnotationParser.java35
-rw-r--r--jetty-annotations/src/main/java/org/eclipse/jetty/annotations/Util.java2
-rw-r--r--jetty-ant/pom.xml2
-rw-r--r--jetty-cdi/cdi-core/pom.xml2
-rw-r--r--jetty-cdi/cdi-full-servlet/pom.xml2
-rw-r--r--jetty-cdi/cdi-servlet/pom.xml2
-rw-r--r--jetty-cdi/cdi-servlet/src/main/config/modules/cdi.mod5
-rw-r--r--jetty-cdi/cdi-websocket/pom.xml2
-rw-r--r--jetty-cdi/cdi-websocket/src/test/java/org/eclipse/jetty/cdi/websocket/basicscope/ScopeBasicsTest.java1
-rw-r--r--jetty-cdi/cdi-websocket/src/test/java/org/eclipse/jetty/cdi/websocket/wsscope/WebSocketScopeBaselineTest.java1
-rw-r--r--jetty-cdi/pom.xml2
-rw-r--r--jetty-cdi/test-cdi-webapp/pom.xml2
-rw-r--r--jetty-client/pom.xml57
-rw-r--r--jetty-client/src/main/config/modules/client.mod5
-rw-r--r--jetty-client/src/main/java/org/eclipse/jetty/client/AbstractConnectionPool.java199
-rw-r--r--jetty-client/src/main/java/org/eclipse/jetty/client/AbstractHttpClientTransport.java12
-rw-r--r--jetty-client/src/main/java/org/eclipse/jetty/client/AuthenticationProtocolHandler.java2
-rw-r--r--jetty-client/src/main/java/org/eclipse/jetty/client/ConnectionPool.java417
-rw-r--r--jetty-client/src/main/java/org/eclipse/jetty/client/DuplexConnectionPool.java313
-rw-r--r--jetty-client/src/main/java/org/eclipse/jetty/client/HttpChannel.java5
-rw-r--r--jetty-client/src/main/java/org/eclipse/jetty/client/HttpClient.java52
-rw-r--r--jetty-client/src/main/java/org/eclipse/jetty/client/HttpContent.java47
-rw-r--r--jetty-client/src/main/java/org/eclipse/jetty/client/HttpDestination.java169
-rw-r--r--jetty-client/src/main/java/org/eclipse/jetty/client/HttpProxy.java42
-rw-r--r--jetty-client/src/main/java/org/eclipse/jetty/client/HttpReceiver.java1
-rw-r--r--jetty-client/src/main/java/org/eclipse/jetty/client/HttpRequestException.java2
-rw-r--r--jetty-client/src/main/java/org/eclipse/jetty/client/HttpSender.java11
-rw-r--r--jetty-client/src/main/java/org/eclipse/jetty/client/LeakTrackingConnectionPool.java2
-rw-r--r--jetty-client/src/main/java/org/eclipse/jetty/client/MultiplexConnectionPool.java302
-rw-r--r--jetty-client/src/main/java/org/eclipse/jetty/client/MultiplexHttpDestination.java128
-rw-r--r--jetty-client/src/main/java/org/eclipse/jetty/client/PoolingHttpDestination.java189
-rw-r--r--jetty-client/src/main/java/org/eclipse/jetty/client/ResponseNotifier.java4
-rw-r--r--jetty-client/src/main/java/org/eclipse/jetty/client/Socks4Proxy.java9
-rw-r--r--jetty-client/src/main/java/org/eclipse/jetty/client/ValidatingConnectionPool.java21
-rw-r--r--jetty-client/src/main/java/org/eclipse/jetty/client/api/Result.java8
-rw-r--r--jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpChannelOverHTTP.java54
-rw-r--r--jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpConnectionOverHTTP.java17
-rw-r--r--jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpConnectionUpgrader.java (renamed from jetty-start/src/main/java/org/eclipse/jetty/start/graph/Predicate.java)11
-rw-r--r--jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpDestinationOverHTTP.java7
-rw-r--r--jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpReceiverOverHTTP.java19
-rw-r--r--jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpSenderOverHTTP.java227
-rw-r--r--jetty-client/src/main/java/org/eclipse/jetty/client/util/MultiPartContentProvider.java404
-rw-r--r--jetty-client/src/test/java/org/eclipse/jetty/client/AbstractHttpClientServerTest.java11
-rw-r--r--jetty-client/src/test/java/org/eclipse/jetty/client/ClientConnectionCloseTest.java122
-rw-r--r--jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientCustomProxyTest.java37
-rw-r--r--jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientExplicitConnectionTest.java4
-rw-r--r--jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientFailureTest.java36
-rw-r--r--jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientTest.java181
-rw-r--r--jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientTimeoutTest.java2
-rw-r--r--jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientUploadDuringServerShutdown.java40
-rw-r--r--jetty-client/src/test/java/org/eclipse/jetty/client/HttpConnectionLifecycleTest.java105
-rw-r--r--jetty-client/src/test/java/org/eclipse/jetty/client/HttpRequestAbortTest.java58
-rw-r--r--jetty-client/src/test/java/org/eclipse/jetty/client/ServerConnectionCloseTest.java176
-rw-r--r--jetty-client/src/test/java/org/eclipse/jetty/client/TLSServerConnectionCloseTest.java213
-rw-r--r--jetty-client/src/test/java/org/eclipse/jetty/client/ValidatingConnectionPoolTest.java2
-rw-r--r--jetty-client/src/test/java/org/eclipse/jetty/client/http/HttpDestinationOverHTTPTest.java90
-rw-r--r--jetty-client/src/test/java/org/eclipse/jetty/client/http/HttpReceiverOverHTTPTest.java7
-rw-r--r--jetty-client/src/test/java/org/eclipse/jetty/client/http/HttpSenderOverHTTPTest.java7
-rw-r--r--jetty-client/src/test/java/org/eclipse/jetty/client/ssl/SslBytesServerTest.java14
-rw-r--r--jetty-client/src/test/java/org/eclipse/jetty/client/util/MultiPartContentProviderTest.java448
-rw-r--r--jetty-client/src/test/resources/jetty-logging.properties1
-rw-r--r--jetty-continuation/pom.xml2
-rw-r--r--jetty-deploy/pom.xml20
-rw-r--r--jetty-deploy/src/main/config/modules/deploy.mod5
-rw-r--r--jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings/DebugListenerBinding.java (renamed from jetty-start/src/main/java/org/eclipse/jetty/start/graph/NamePredicate.java)34
-rw-r--r--jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings/GlobalWebappConfigBinding.java2
-rw-r--r--jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/WebAppProvider.java2
-rw-r--r--jetty-distribution/pom.xml16
-rwxr-xr-xjetty-distribution/src/main/resources/bin/jetty.sh8
-rw-r--r--jetty-distribution/src/main/resources/modules/hawtio.mod5
-rw-r--r--jetty-distribution/src/main/resources/modules/jamon.mod5
-rw-r--r--jetty-distribution/src/main/resources/modules/jminix.mod5
-rw-r--r--jetty-distribution/src/main/resources/modules/jolokia.mod5
-rw-r--r--jetty-distribution/src/main/resources/modules/jsp.mod5
-rw-r--r--jetty-distribution/src/main/resources/modules/jstl.mod5
-rw-r--r--jetty-distribution/src/main/resources/modules/setuid.mod7
-rw-r--r--jetty-fcgi/fcgi-client/pom.xml2
-rw-r--r--jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpConnectionOverFCGI.java2
-rw-r--r--jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpDestinationOverFCGI.java7
-rw-r--r--jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/MultiplexHttpDestinationOverFCGI.java7
-rw-r--r--jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/parser/BeginRequestContentParser.java10
-rw-r--r--jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/parser/EndRequestContentParser.java14
-rw-r--r--jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/parser/HeaderParser.java21
-rw-r--r--jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/parser/ParamsContentParser.java35
-rw-r--r--jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/parser/Parser.java29
-rw-r--r--jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/parser/ResponseContentParser.java43
-rw-r--r--jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/parser/StreamContentParser.java4
-rw-r--r--jetty-fcgi/fcgi-server/pom.xml2
-rw-r--r--jetty-fcgi/fcgi-server/src/main/config/modules/fcgi.mod5
-rw-r--r--jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/proxy/FastCGIProxyServlet.java53
-rw-r--r--jetty-fcgi/fcgi-server/src/test/java/org/eclipse/jetty/fcgi/server/AbstractHttpClientServerTest.java30
-rw-r--r--jetty-fcgi/fcgi-server/src/test/java/org/eclipse/jetty/fcgi/server/HttpClientTest.java31
-rw-r--r--jetty-fcgi/fcgi-server/src/test/java/org/eclipse/jetty/fcgi/server/proxy/FastCGIProxyServletTest.java43
-rw-r--r--jetty-fcgi/pom.xml2
-rw-r--r--jetty-gcloud/gcloud-session-manager/pom.xml75
-rw-r--r--jetty-gcloud/gcloud-session-manager/src/main/config/etc/jetty-gcloud-sessions.xml35
-rw-r--r--jetty-gcloud/gcloud-session-manager/src/main/config/modules/gcloud-sessions.mod90
-rw-r--r--jetty-gcloud/gcloud-session-manager/src/main/java/org/eclipse/jetty/gcloud/session/GCloudConfiguration.java201
-rw-r--r--jetty-gcloud/gcloud-session-manager/src/main/java/org/eclipse/jetty/gcloud/session/GCloudSessionDataStore.java389
-rw-r--r--jetty-gcloud/gcloud-session-manager/src/main/java/org/eclipse/jetty/gcloud/session/GCloudSessionIdManager.java233
-rw-r--r--jetty-gcloud/gcloud-session-manager/src/main/java/org/eclipse/jetty/gcloud/session/GCloudSessionManager.java329
-rw-r--r--jetty-gcloud/gcloud-session-manager/src/test/java/org/eclipse/jetty/gcloud/session/GCloudSessionTester.java75
-rw-r--r--jetty-gcloud/pom.xml23
-rw-r--r--jetty-http-spi/pom.xml2
-rw-r--r--jetty-http/pom.xml2
-rw-r--r--jetty-http/src/main/java/org/eclipse/jetty/http/GzipHttpContent.java188
-rw-r--r--jetty-http/src/main/java/org/eclipse/jetty/http/HttpContent.java10
-rw-r--r--jetty-http/src/main/java/org/eclipse/jetty/http/HttpField.java16
-rw-r--r--jetty-http/src/main/java/org/eclipse/jetty/http/HttpGenerator.java325
-rw-r--r--jetty-http/src/main/java/org/eclipse/jetty/http/HttpHeader.java12
-rw-r--r--jetty-http/src/main/java/org/eclipse/jetty/http/HttpTokens.java2
-rw-r--r--jetty-http/src/main/java/org/eclipse/jetty/http/HttpURI.java5
-rw-r--r--jetty-http/src/main/java/org/eclipse/jetty/http/MetaData.java1
-rw-r--r--jetty-http/src/main/java/org/eclipse/jetty/http/ResourceHttpContent.java47
-rw-r--r--jetty-http/src/main/resources/org/eclipse/jetty/http/mime.properties2
-rw-r--r--jetty-http/src/test/java/org/eclipse/jetty/http/HttpFieldTest.java17
-rw-r--r--jetty-http/src/test/java/org/eclipse/jetty/http/HttpGeneratorClientTest.java38
-rw-r--r--jetty-http/src/test/java/org/eclipse/jetty/http/HttpGeneratorServerHTTPTest.java13
-rw-r--r--jetty-http/src/test/java/org/eclipse/jetty/http/HttpGeneratorServerTest.java2
-rw-r--r--jetty-http/src/test/java/org/eclipse/jetty/http/HttpURIParseTest.java7
-rw-r--r--jetty-http/src/test/java/org/eclipse/jetty/http/HttpURITest.java28
-rw-r--r--jetty-http2/http2-alpn-tests/pom.xml2
-rw-r--r--jetty-http2/http2-client/pom.xml2
-rw-r--r--jetty-http2/http2-client/src/main/java/org/eclipse/jetty/http2/client/HTTP2Client.java72
-rw-r--r--jetty-http2/http2-client/src/main/java/org/eclipse/jetty/http2/client/HTTP2ClientConnectionFactory.java13
-rw-r--r--jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/AbstractTest.java8
-rw-r--r--jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/AsyncIOTest.java7
-rw-r--r--jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/Client.java4
-rw-r--r--jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/FlowControlStrategyTest.java114
-rw-r--r--jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/HTTP2Test.java373
-rw-r--r--jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/IdleTimeoutTest.java46
-rw-r--r--jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/PrefaceTest.java327
-rw-r--r--jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/PriorityTest.java184
-rw-r--r--jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/ProxyProtocolTest.java69
-rw-r--r--jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/ProxyTest.java4
-rw-r--r--jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/PushCacheFilterTest.java50
-rw-r--r--jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/SessionFailureTest.java2
-rw-r--r--jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/StreamCloseTest.java50
-rw-r--r--jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/StreamCountTest.java12
-rw-r--r--jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/StreamResetTest.java42
-rw-r--r--jetty-http2/http2-common/pom.xml2
-rw-r--r--jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/AbstractFlowControlStrategy.java4
-rw-r--r--jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/BufferingFlowControlStrategy.java8
-rw-r--r--jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/FlowControlStrategy.java4
-rw-r--r--jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Connection.java9
-rw-r--r--jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Flusher.java2
-rw-r--r--jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Session.java86
-rw-r--r--jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Stream.java50
-rw-r--r--jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/ISession.java3
-rw-r--r--jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/IStream.java5
-rw-r--r--jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/api/Session.java25
-rw-r--r--jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/frames/HeadersFrame.java24
-rw-r--r--jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/frames/PingFrame.java63
-rw-r--r--jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/frames/PriorityFrame.java26
-rw-r--r--jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/generator/HeadersGenerator.java46
-rw-r--r--jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/generator/PingGenerator.java4
-rw-r--r--jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/generator/PriorityGenerator.java33
-rw-r--r--jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/parser/ContinuationBodyParser.java1
-rw-r--r--jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/parser/HeadersBodyParser.java38
-rw-r--r--jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/parser/Parser.java19
-rw-r--r--jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/parser/PrefaceParser.java2
-rw-r--r--jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/parser/PriorityBodyParser.java34
-rw-r--r--jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/parser/ServerParser.java17
-rw-r--r--jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/parser/SettingsBodyParser.java2
-rw-r--r--jetty-http2/http2-common/src/test/java/org/eclipse/jetty/http2/api/UsageTest.java2
-rw-r--r--jetty-http2/http2-common/src/test/java/org/eclipse/jetty/http2/frames/HeadersGenerateParseTest.java18
-rw-r--r--jetty-http2/http2-common/src/test/java/org/eclipse/jetty/http2/frames/PingGenerateParseTest.java33
-rw-r--r--jetty-http2/http2-common/src/test/java/org/eclipse/jetty/http2/frames/PriorityGenerateParseTest.java14
-rw-r--r--jetty-http2/http2-hpack/pom.xml2
-rw-r--r--jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackDecoder.java64
-rw-r--r--jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/Huffman.java5
-rw-r--r--jetty-http2/http2-hpack/src/test/java/org/eclipse/jetty/http2/hpack/HpackDecoderTest.java81
-rw-r--r--jetty-http2/http2-http-client-transport/pom.xml40
-rw-r--r--jetty-http2/http2-http-client-transport/src/main/java/org/eclipse/jetty/http2/client/http/HttpClientTransportOverHTTP2.java28
-rw-r--r--jetty-http2/http2-http-client-transport/src/main/java/org/eclipse/jetty/http2/client/http/HttpDestinationOverHTTP2.java7
-rw-r--r--jetty-http2/http2-http-client-transport/src/main/java/org/eclipse/jetty/http2/client/http/HttpSenderOverHTTP2.java9
-rw-r--r--jetty-http2/http2-http-client-transport/src/test/java/org/eclipse/jetty/http2/client/http/HttpClientTransportOverHTTP2Test.java78
-rw-r--r--jetty-http2/http2-http-client-transport/src/test/resources/jetty-logging.properties5
-rw-r--r--jetty-http2/http2-server/pom.xml2
-rw-r--r--jetty-http2/http2-server/src/main/config/modules/http2.mod6
-rw-r--r--jetty-http2/http2-server/src/main/config/modules/http2c.mod9
-rw-r--r--jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HTTP2ServerConnection.java18
-rw-r--r--jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HTTP2ServerSession.java6
-rw-r--r--jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HttpTransportOverHTTP2.java42
-rw-r--r--jetty-http2/http2-server/src/test/java/org/eclipse/jetty/http2/server/HTTP2CServerTest.java2
-rw-r--r--jetty-http2/http2-server/src/test/java/org/eclipse/jetty/http2/server/HTTP2ServerTest.java73
-rw-r--r--jetty-http2/pom.xml2
-rw-r--r--jetty-infinispan/pom.xml19
-rw-r--r--jetty-infinispan/src/main/config/modules/infinispan.mod6
-rw-r--r--jetty-infinispan/src/main/java/org/eclipse/jetty/session/infinispan/InfinispanSessionIdManager.java28
-rw-r--r--jetty-io/pom.xml2
-rw-r--r--jetty-io/src/main/java/org/eclipse/jetty/io/AbstractConnection.java56
-rw-r--r--jetty-io/src/main/java/org/eclipse/jetty/io/AbstractEndPoint.java266
-rw-r--r--jetty-io/src/main/java/org/eclipse/jetty/io/ByteArrayEndPoint.java161
-rw-r--r--jetty-io/src/main/java/org/eclipse/jetty/io/ChannelEndPoint.java310
-rw-r--r--jetty-io/src/main/java/org/eclipse/jetty/io/ClientConnectionFactory.java53
-rw-r--r--jetty-io/src/main/java/org/eclipse/jetty/io/Connection.java4
-rw-r--r--jetty-io/src/main/java/org/eclipse/jetty/io/EndPoint.java2
-rw-r--r--jetty-io/src/main/java/org/eclipse/jetty/io/FillInterest.java10
-rw-r--r--jetty-io/src/main/java/org/eclipse/jetty/io/ManagedSelector.java61
-rw-r--r--jetty-io/src/main/java/org/eclipse/jetty/io/NegotiatingClientConnection.java4
-rw-r--r--jetty-io/src/main/java/org/eclipse/jetty/io/SelectChannelEndPoint.java267
-rw-r--r--jetty-io/src/main/java/org/eclipse/jetty/io/SelectorManager.java72
-rw-r--r--jetty-io/src/main/java/org/eclipse/jetty/io/SocketChannelEndPoint.java81
-rw-r--r--jetty-io/src/main/java/org/eclipse/jetty/io/WriteFlusher.java19
-rw-r--r--jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java49
-rw-r--r--jetty-io/src/test/java/org/eclipse/jetty/io/IOTest.java10
-rw-r--r--jetty-io/src/test/java/org/eclipse/jetty/io/SelectChannelEndPointInterestsTest.java13
-rw-r--r--jetty-io/src/test/java/org/eclipse/jetty/io/SelectChannelEndPointSslTest.java3
-rw-r--r--jetty-io/src/test/java/org/eclipse/jetty/io/SelectChannelEndPointTest.java15
-rw-r--r--jetty-io/src/test/java/org/eclipse/jetty/io/SelectorManagerTest.java17
-rw-r--r--jetty-io/src/test/java/org/eclipse/jetty/io/SocketChannelEndPointTest.java (renamed from jetty-io/src/test/java/org/eclipse/jetty/io/ChannelEndPointTest.java)16
-rw-r--r--jetty-io/src/test/java/org/eclipse/jetty/io/SslConnectionTest.java22
-rw-r--r--jetty-io/src/test/java/org/eclipse/jetty/io/WriteFlusherTest.java48
-rw-r--r--jetty-jaas/pom.xml22
-rw-r--r--jetty-jaas/src/main/config/modules/jaas.mod5
-rw-r--r--jetty-jaas/src/main/java/org/eclipse/jetty/jaas/JAASLoginService.java2
-rw-r--r--jetty-jaas/src/main/java/org/eclipse/jetty/jaas/spi/AbstractDatabaseLoginModule.java42
-rw-r--r--jetty-jaas/src/main/java/org/eclipse/jetty/jaas/spi/AbstractLoginModule.java40
-rw-r--r--jetty-jaas/src/main/java/org/eclipse/jetty/jaas/spi/JDBCLoginModule.java2
-rw-r--r--jetty-jaas/src/main/java/org/eclipse/jetty/jaas/spi/LdapLoginModule.java53
-rw-r--r--jetty-jaas/src/main/java/org/eclipse/jetty/jaas/spi/PropertyFileLoginModule.java7
-rw-r--r--jetty-jaas/src/main/java/org/eclipse/jetty/jaas/spi/UserInfo.java59
-rw-r--r--jetty-jaspi/pom.xml2
-rw-r--r--jetty-jaspi/src/main/config/modules/jaspi.mod5
-rw-r--r--jetty-jaspi/src/test/java/org/eclipse/jetty/security/jaspi/JaspiTest.java44
-rw-r--r--jetty-jmx/pom.xml2
-rw-r--r--jetty-jmx/src/main/config/modules/jmx-remote.mod5
-rw-r--r--jetty-jmx/src/main/config/modules/jmx.mod6
-rw-r--r--jetty-jmx/src/main/java/org/eclipse/jetty/jmx/ObjectMBean.java17
-rw-r--r--jetty-jndi/pom.xml2
-rw-r--r--jetty-jndi/src/main/config/modules/jndi.mod5
-rw-r--r--jetty-jspc-maven-plugin/pom.xml2
-rw-r--r--jetty-jspc-maven-plugin/src/main/java/org/eclipse/jetty/jspc/plugin/JspcMojo.java23
-rw-r--r--jetty-maven-plugin/pom.xml2
-rw-r--r--jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/AbstractJettyMojo.java5
-rw-r--r--jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyRunForkedMojo.java2
-rw-r--r--jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyWebAppContext.java65
-rw-r--r--jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/ServerSupport.java8
-rw-r--r--jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/Starter.java5
-rw-r--r--jetty-monitor/pom.xml2
-rw-r--r--jetty-monitor/src/main/config/modules/monitor.mod6
-rw-r--r--jetty-nosql/pom.xml2
-rw-r--r--jetty-nosql/src/main/config/modules/nosql.mod5
-rw-r--r--jetty-nosql/src/main/java/org/eclipse/jetty/nosql/mongodb/MongoSessionManager.java2
-rw-r--r--jetty-osgi/jetty-osgi-alpn/pom.xml2
-rw-r--r--jetty-osgi/jetty-osgi-boot-jsp/pom.xml8
-rw-r--r--jetty-osgi/jetty-osgi-boot-warurl/pom.xml2
-rw-r--r--jetty-osgi/jetty-osgi-boot/pom.xml6
-rw-r--r--jetty-osgi/jetty-osgi-httpservice/pom.xml2
-rw-r--r--jetty-osgi/pom.xml2
-rw-r--r--jetty-osgi/test-jetty-osgi-context/pom.xml2
-rw-r--r--jetty-osgi/test-jetty-osgi-webapp/pom.xml5
-rw-r--r--jetty-osgi/test-jetty-osgi/pom.xml3
-rw-r--r--jetty-osgi/test-jetty-osgi/src/test/config/etc/jetty-testrealm.xml1
-rw-r--r--jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/test/TestJettyOSGiBootCore.java4
-rw-r--r--jetty-overlay-deployer/src/main/config/modules/overlay.mod6
-rw-r--r--jetty-plus/pom.xml29
-rw-r--r--jetty-plus/src/main/config/modules/plus.mod7
-rw-r--r--jetty-plus/src/main/java/org/eclipse/jetty/plus/annotation/ContainerInitializer.java2
-rw-r--r--jetty-plus/src/main/java/org/eclipse/jetty/plus/annotation/LifeCycleCallback.java2
-rw-r--r--jetty-plus/src/main/java/org/eclipse/jetty/plus/security/DataSourceLoginService.java133
-rw-r--r--jetty-plus/src/main/java/org/eclipse/jetty/plus/webapp/EnvConfiguration.java3
-rw-r--r--jetty-proxy/pom.xml2
-rw-r--r--jetty-proxy/src/main/config/modules/proxy.mod6
-rw-r--r--jetty-proxy/src/main/java/org/eclipse/jetty/proxy/AbstractProxyServlet.java28
-rw-r--r--jetty-proxy/src/main/java/org/eclipse/jetty/proxy/AsyncMiddleManServlet.java1
-rw-r--r--jetty-proxy/src/main/java/org/eclipse/jetty/proxy/ConnectHandler.java205
-rw-r--r--jetty-proxy/src/main/java/org/eclipse/jetty/proxy/ProxyServlet.java1
-rw-r--r--jetty-proxy/src/test/java/org/eclipse/jetty/proxy/AsyncMiddleManServletTest.java59
-rw-r--r--jetty-proxy/src/test/java/org/eclipse/jetty/proxy/ConnectHandlerTest.java26
-rw-r--r--jetty-proxy/src/test/java/org/eclipse/jetty/proxy/ProxyServletFailureTest.java10
-rw-r--r--jetty-proxy/src/test/java/org/eclipse/jetty/proxy/ProxyServletTest.java7
-rw-r--r--jetty-proxy/src/test/java/org/eclipse/jetty/proxy/ProxyTunnellingTest.java81
-rw-r--r--jetty-quickstart/pom.xml2
-rw-r--r--jetty-quickstart/src/main/config/modules/quickstart.mod6
-rw-r--r--jetty-rewrite/pom.xml2
-rw-r--r--jetty-rewrite/src/main/config/etc/jetty-rewrite.xml38
-rw-r--r--jetty-rewrite/src/main/config/modules/rewrite.mod6
-rw-r--r--jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/RewriteHandler.java2
-rw-r--r--jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/RuleContainer.java13
-rw-r--r--jetty-rewrite/src/test/resources/org.mortbay.jetty.rewrite.handler/jetty-rewrite.xml1
-rw-r--r--jetty-runner/pom.xml58
-rw-r--r--jetty-security/pom.xml4
-rw-r--r--jetty-security/src/main/config/modules/security.mod5
-rw-r--r--jetty-security/src/main/java/org/eclipse/jetty/security/AbstractLoginService.java248
-rw-r--r--jetty-security/src/main/java/org/eclipse/jetty/security/Authenticator.java27
-rw-r--r--jetty-security/src/main/java/org/eclipse/jetty/security/ConstraintAware.java10
-rw-r--r--jetty-security/src/main/java/org/eclipse/jetty/security/HashLoginService.java91
-rw-r--r--jetty-security/src/main/java/org/eclipse/jetty/security/JDBCLoginService.java140
-rw-r--r--jetty-security/src/main/java/org/eclipse/jetty/security/MappedLoginService.java344
-rw-r--r--jetty-security/src/main/java/org/eclipse/jetty/security/PropertyUserStore.java49
-rw-r--r--jetty-security/src/test/java/org/eclipse/jetty/security/AliasedConstraintTest.java6
-rw-r--r--jetty-security/src/test/java/org/eclipse/jetty/security/ConstraintTest.java3
-rw-r--r--jetty-security/src/test/java/org/eclipse/jetty/security/SpecExampleConstraintTest.java5
-rw-r--r--jetty-security/src/test/java/org/eclipse/jetty/security/TestLoginService.java69
-rw-r--r--jetty-server/pom.xml21
-rw-r--r--jetty-server/src/main/config/etc/jetty-debug.xml36
-rw-r--r--jetty-server/src/main/config/etc/jetty-http-forwarded.xml17
-rw-r--r--jetty-server/src/main/config/etc/jetty.xml6
-rw-r--r--jetty-server/src/main/config/modules/continuation.mod7
-rw-r--r--jetty-server/src/main/config/modules/debug.mod30
-rw-r--r--jetty-server/src/main/config/modules/debuglog.mod6
-rw-r--r--jetty-server/src/main/config/modules/ext.mod6
-rw-r--r--jetty-server/src/main/config/modules/gzip.mod7
-rw-r--r--jetty-server/src/main/config/modules/home-base-warning.mod6
-rw-r--r--jetty-server/src/main/config/modules/http-forwarded.mod20
-rw-r--r--jetty-server/src/main/config/modules/http.mod7
-rw-r--r--jetty-server/src/main/config/modules/https.mod6
-rw-r--r--jetty-server/src/main/config/modules/ipaccess.mod6
-rw-r--r--jetty-server/src/main/config/modules/jdbc-sessions.mod6
-rw-r--r--jetty-server/src/main/config/modules/jvm.mod3
-rw-r--r--jetty-server/src/main/config/modules/lowresources.mod7
-rw-r--r--jetty-server/src/main/config/modules/proxy-protocol-ssl.mod9
-rw-r--r--jetty-server/src/main/config/modules/proxy-protocol.mod10
-rw-r--r--jetty-server/src/main/config/modules/requestlog.mod5
-rw-r--r--jetty-server/src/main/config/modules/resources.mod7
-rw-r--r--jetty-server/src/main/config/modules/server.mod9
-rw-r--r--jetty-server/src/main/config/modules/ssl.mod7
-rw-r--r--jetty-server/src/main/config/modules/stats.mod6
-rw-r--r--jetty-server/src/main/java/org/eclipse/jetty/server/AbstractConnector.java6
-rw-r--r--jetty-server/src/main/java/org/eclipse/jetty/server/AbstractNCSARequestLog.java2
-rw-r--r--jetty-server/src/main/java/org/eclipse/jetty/server/AsyncContextState.java7
-rw-r--r--jetty-server/src/main/java/org/eclipse/jetty/server/DebugListener.java333
-rw-r--r--jetty-server/src/main/java/org/eclipse/jetty/server/Dispatcher.java43
-rw-r--r--jetty-server/src/main/java/org/eclipse/jetty/server/ForwardedRequestCustomizer.java1
-rw-r--r--jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannel.java244
-rw-r--r--jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannelOverHttp.java6
-rw-r--r--jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannelState.java496
-rw-r--r--jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java118
-rw-r--r--jetty-server/src/main/java/org/eclipse/jetty/server/HttpInput.java171
-rw-r--r--jetty-server/src/main/java/org/eclipse/jetty/server/HttpOutput.java29
-rw-r--r--jetty-server/src/main/java/org/eclipse/jetty/server/LocalConnector.java15
-rw-r--r--jetty-server/src/main/java/org/eclipse/jetty/server/NetworkTrafficServerConnector.java4
-rw-r--r--jetty-server/src/main/java/org/eclipse/jetty/server/ProxyConnectionFactory.java437
-rw-r--r--jetty-server/src/main/java/org/eclipse/jetty/server/PushBuilder.java198
-rw-r--r--jetty-server/src/main/java/org/eclipse/jetty/server/PushBuilderImpl.java122
-rw-r--r--jetty-server/src/main/java/org/eclipse/jetty/server/Request.java242
-rw-r--r--jetty-server/src/main/java/org/eclipse/jetty/server/RequestLogCollection.java4
-rw-r--r--jetty-server/src/main/java/org/eclipse/jetty/server/ResourceCache.java166
-rw-r--r--jetty-server/src/main/java/org/eclipse/jetty/server/ResourceContentFactory.java104
-rw-r--r--jetty-server/src/main/java/org/eclipse/jetty/server/Response.java209
-rw-r--r--jetty-server/src/main/java/org/eclipse/jetty/server/SecureRequestCustomizer.java21
-rw-r--r--jetty-server/src/main/java/org/eclipse/jetty/server/ServerConnector.java15
-rw-r--r--jetty-server/src/main/java/org/eclipse/jetty/server/ShutdownMonitor.java10
-rw-r--r--jetty-server/src/main/java/org/eclipse/jetty/server/SocketCustomizationListener.java8
-rw-r--r--jetty-server/src/main/java/org/eclipse/jetty/server/handler/AbstractHandler.java32
-rw-r--r--jetty-server/src/main/java/org/eclipse/jetty/server/handler/AllowSymLinkAliasChecker.java41
-rw-r--r--jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java129
-rw-r--r--jetty-server/src/main/java/org/eclipse/jetty/server/handler/DebugHandler.java6
-rw-r--r--jetty-server/src/main/java/org/eclipse/jetty/server/handler/ErrorHandler.java217
-rw-r--r--jetty-server/src/main/java/org/eclipse/jetty/server/handler/HandlerCollection.java1
-rw-r--r--jetty-server/src/main/java/org/eclipse/jetty/server/handler/HandlerWrapper.java2
-rw-r--r--jetty-server/src/main/java/org/eclipse/jetty/server/handler/ResourceHandler.java3
-rw-r--r--jetty-server/src/main/java/org/eclipse/jetty/server/handler/gzip/GzipHandler.java21
-rw-r--r--jetty-server/src/main/java/org/eclipse/jetty/server/handler/gzip/GzipHttpOutputInterceptor.java6
-rw-r--r--jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionDataStore.java3
-rw-r--r--jetty-server/src/test/java/org/eclipse/jetty/server/AbstractHttpTest.java49
-rw-r--r--jetty-server/src/test/java/org/eclipse/jetty/server/AsyncRequestReadTest.java10
-rw-r--r--jetty-server/src/test/java/org/eclipse/jetty/server/ConnectorStatisticsTest.java (renamed from jetty-server/src/test/java/org/eclipse/jetty/server/SelectChannelStatisticsTest.java)4
-rw-r--r--jetty-server/src/test/java/org/eclipse/jetty/server/ConnectorTimeoutTest.java1
-rw-r--r--jetty-server/src/test/java/org/eclipse/jetty/server/DumpHandler.java2
-rw-r--r--jetty-server/src/test/java/org/eclipse/jetty/server/ExtendedServerTest.java3
-rw-r--r--jetty-server/src/test/java/org/eclipse/jetty/server/HttpConnectionTest.java3
-rw-r--r--jetty-server/src/test/java/org/eclipse/jetty/server/HttpManyWaysToAsyncCommitBadBehaviourTest.java2
-rw-r--r--jetty-server/src/test/java/org/eclipse/jetty/server/HttpManyWaysToAsyncCommitTest.java44
-rw-r--r--jetty-server/src/test/java/org/eclipse/jetty/server/HttpManyWaysToCommitTest.java52
-rw-r--r--jetty-server/src/test/java/org/eclipse/jetty/server/HttpServerTestBase.java51
-rw-r--r--jetty-server/src/test/java/org/eclipse/jetty/server/RequestTest.java2
-rw-r--r--jetty-server/src/test/java/org/eclipse/jetty/server/ResourceCacheTest.java16
-rw-r--r--jetty-server/src/test/java/org/eclipse/jetty/server/ResponseTest.java3
-rw-r--r--jetty-server/src/test/java/org/eclipse/jetty/server/ServerConnectorAsyncContextTest.java (renamed from jetty-server/src/test/java/org/eclipse/jetty/server/SelectChannelAsyncContextTest.java)2
-rw-r--r--jetty-server/src/test/java/org/eclipse/jetty/server/ServerConnectorCloseTest.java (renamed from jetty-server/src/test/java/org/eclipse/jetty/server/SelectChannelConnectorCloseTest.java)2
-rw-r--r--jetty-server/src/test/java/org/eclipse/jetty/server/ServerConnectorHttpServerTest.java (renamed from jetty-server/src/test/java/org/eclipse/jetty/server/SelectChannelServerTest.java)2
-rw-r--r--jetty-server/src/test/java/org/eclipse/jetty/server/ServerConnectorTest.java24
-rw-r--r--jetty-server/src/test/java/org/eclipse/jetty/server/ServerConnectorTimeoutTest.java (renamed from jetty-server/src/test/java/org/eclipse/jetty/server/SelectChannelTimeoutTest.java)2
-rw-r--r--jetty-server/src/test/java/org/eclipse/jetty/server/ThreadStarvationTest.java10
-rw-r--r--jetty-server/src/test/java/org/eclipse/jetty/server/handler/ContextHandlerGetResourceTest.java35
-rw-r--r--jetty-server/src/test/java/org/eclipse/jetty/server/handler/DebugHandlerTest.java182
-rw-r--r--jetty-server/src/test/java/org/eclipse/jetty/server/handler/RequestLogTest.java25
-rw-r--r--jetty-server/src/test/java/org/eclipse/jetty/server/handler/ResourceHandlerTest.java21
-rw-r--r--jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SelectChannelServerSslTest.java1
-rw-r--r--jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SniSslConnectionFactoryTest.java10
-rw-r--r--jetty-server/src/test/resources/jetty-logging.properties2
-rw-r--r--jetty-servlet/pom.xml33
-rw-r--r--jetty-servlet/src/main/config/modules/servlet.mod5
-rw-r--r--jetty-servlet/src/main/java/org/eclipse/jetty/servlet/BaseHolder.java2
-rw-r--r--jetty-servlet/src/main/java/org/eclipse/jetty/servlet/DefaultServlet.java382
-rw-r--r--jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ErrorPageErrorHandler.java73
-rw-r--r--jetty-servlet/src/main/java/org/eclipse/jetty/servlet/FilterMapping.java17
-rw-r--r--jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletContextHandler.java2
-rw-r--r--jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHandler.java250
-rw-r--r--jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHolder.java145
-rw-r--r--jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletMapping.java19
-rw-r--r--jetty-servlet/src/main/java/org/eclipse/jetty/servlet/listener/ELContextCleaner.java2
-rw-r--r--jetty-servlet/src/test/java/org/eclipse/jetty/servlet/AsyncContextTest.java307
-rw-r--r--jetty-servlet/src/test/java/org/eclipse/jetty/servlet/AsyncIOServletTest.java2
-rw-r--r--jetty-servlet/src/test/java/org/eclipse/jetty/servlet/AsyncListenerTest.java813
-rw-r--r--jetty-servlet/src/test/java/org/eclipse/jetty/servlet/AsyncServletIOTest.java186
-rw-r--r--jetty-servlet/src/test/java/org/eclipse/jetty/servlet/AsyncServletTest.java91
-rw-r--r--jetty-servlet/src/test/java/org/eclipse/jetty/servlet/DefaultServletTest.java174
-rw-r--r--jetty-servlet/src/test/java/org/eclipse/jetty/servlet/DispatcherForwardTest.java141
-rw-r--r--jetty-servlet/src/test/java/org/eclipse/jetty/servlet/ErrorPageTest.java1
-rw-r--r--jetty-servlet/src/test/java/org/eclipse/jetty/servlet/GzipHandlerTest.java (renamed from examples/embedded/src/test/java/org/eclipse/jetty/embedded/GzipHandlerTest.java)117
-rw-r--r--jetty-servlet/src/test/resources/jetty-logging.properties3
-rw-r--r--jetty-servlets/pom.xml2
-rw-r--r--jetty-servlets/src/main/config/modules/servlets.mod9
-rw-r--r--jetty-servlets/src/main/java/org/eclipse/jetty/servlets/DoSFilter.java32
-rw-r--r--jetty-servlets/src/main/java/org/eclipse/jetty/servlets/PushCacheFilter.java22
-rw-r--r--jetty-servlets/src/test/java/org/eclipse/jetty/server/handler/gzip/GzipDefaultTest.java3
-rw-r--r--jetty-servlets/src/test/java/org/eclipse/jetty/servlets/DefaultServletStarvationTest.java216
-rw-r--r--jetty-servlets/src/test/java/org/eclipse/jetty/servlets/ThreadStarvationTest.java420
-rw-r--r--jetty-spring/pom.xml2
-rw-r--r--jetty-spring/src/main/config/modules/spring.mod6
-rw-r--r--jetty-start/dependency-reduced-pom.xml83
-rw-r--r--jetty-start/pom.xml34
-rw-r--r--jetty-start/src/main/java/org/eclipse/jetty/start/BaseBuilder.java198
-rw-r--r--jetty-start/src/main/java/org/eclipse/jetty/start/Licensing.java2
-rw-r--r--jetty-start/src/main/java/org/eclipse/jetty/start/Main.java94
-rw-r--r--jetty-start/src/main/java/org/eclipse/jetty/start/Module.java148
-rw-r--r--jetty-start/src/main/java/org/eclipse/jetty/start/ModuleGraphWriter.java34
-rw-r--r--jetty-start/src/main/java/org/eclipse/jetty/start/Modules.java281
-rw-r--r--jetty-start/src/main/java/org/eclipse/jetty/start/StartArgs.java20
-rw-r--r--jetty-start/src/main/java/org/eclipse/jetty/start/builders/StartDirBuilder.java6
-rw-r--r--jetty-start/src/main/java/org/eclipse/jetty/start/builders/StartIniBuilder.java6
-rw-r--r--jetty-start/src/main/java/org/eclipse/jetty/start/fileinits/MavenLocalRepoFileInitializer.java4
-rw-r--r--jetty-start/src/main/java/org/eclipse/jetty/start/fileinits/UriFileInitializer.java2
-rw-r--r--jetty-start/src/main/java/org/eclipse/jetty/start/graph/AllPredicate.java31
-rw-r--r--jetty-start/src/main/java/org/eclipse/jetty/start/graph/AndPredicate.java46
-rw-r--r--jetty-start/src/main/java/org/eclipse/jetty/start/graph/AnySelectionPredicate.java28
-rw-r--r--jetty-start/src/main/java/org/eclipse/jetty/start/graph/CriteriaPredicate.java45
-rw-r--r--jetty-start/src/main/java/org/eclipse/jetty/start/graph/CriteriaSetPredicate.java71
-rw-r--r--jetty-start/src/main/java/org/eclipse/jetty/start/graph/Graph.java503
-rw-r--r--jetty-start/src/main/java/org/eclipse/jetty/start/graph/GraphException.java36
-rw-r--r--jetty-start/src/main/java/org/eclipse/jetty/start/graph/Node.java179
-rw-r--r--jetty-start/src/main/java/org/eclipse/jetty/start/graph/NodeDepthComparator.java43
-rw-r--r--jetty-start/src/main/java/org/eclipse/jetty/start/graph/OnlyTransitivePredicate.java41
-rw-r--r--jetty-start/src/main/java/org/eclipse/jetty/start/graph/RegexNamePredicate.java40
-rw-r--r--jetty-start/src/main/java/org/eclipse/jetty/start/graph/Selection.java129
-rw-r--r--jetty-start/src/main/java/org/eclipse/jetty/start/graph/UniqueCriteriaPredicate.java63
-rw-r--r--jetty-start/src/main/resources/org/eclipse/jetty/start/usage.txt5
-rw-r--r--jetty-start/src/test/java/org/eclipse/jetty/start/ConfigurationAssert.java11
-rw-r--r--jetty-start/src/test/java/org/eclipse/jetty/start/ModuleGraphWriterTest.java2
-rw-r--r--jetty-start/src/test/java/org/eclipse/jetty/start/ModuleTest.java4
-rw-r--r--jetty-start/src/test/java/org/eclipse/jetty/start/ModulesTest.java103
-rw-r--r--jetty-start/src/test/java/org/eclipse/jetty/start/PropertyPassingTest.java2
-rw-r--r--jetty-start/src/test/java/org/eclipse/jetty/start/TestBadUseCases.java3
-rw-r--r--jetty-start/src/test/java/org/eclipse/jetty/start/graph/NodeTest.java75
-rw-r--r--jetty-start/src/test/resources/dist-home/modules/alpn-impl/alpn-1.8.0_60.mod8
-rw-r--r--jetty-start/src/test/resources/dist-home/modules/alpn-impl/alpn-1.8.0_65.mod8
-rw-r--r--jetty-start/src/test/resources/dist-home/modules/alpn-impl/alpn-1.8.0_66.mod8
-rw-r--r--jetty-unixsocket/.gitignore1
-rw-r--r--jetty-unixsocket/pom.xml43
-rw-r--r--jetty-unixsocket/src/main/config/etc/jetty-unixsocket-forwarded.xml17
-rw-r--r--jetty-unixsocket/src/main/config/etc/jetty-unixsocket-http.xml13
-rw-r--r--jetty-unixsocket/src/main/config/etc/jetty-unixsocket-http2c.xml18
-rw-r--r--jetty-unixsocket/src/main/config/etc/jetty-unixsocket-proxy-protocol.xml10
-rw-r--r--jetty-unixsocket/src/main/config/etc/jetty-unixsocket-secure.xml11
-rw-r--r--jetty-unixsocket/src/main/config/etc/jetty-unixsocket.xml25
-rw-r--r--jetty-unixsocket/src/main/config/modules/unixsocket-forwarded.mod24
-rw-r--r--jetty-unixsocket/src/main/config/modules/unixsocket-http.mod14
-rw-r--r--jetty-unixsocket/src/main/config/modules/unixsocket-http2c.mod21
-rw-r--r--jetty-unixsocket/src/main/config/modules/unixsocket-proxy-protocol.mod15
-rw-r--r--jetty-unixsocket/src/main/config/modules/unixsocket-secure.mod17
-rw-r--r--jetty-unixsocket/src/main/config/modules/unixsocket.mod54
-rw-r--r--jetty-unixsocket/src/main/java/org/eclipse/jetty/unixsocket/UnixSocketConnector.java436
-rw-r--r--jetty-unixsocket/src/main/java/org/eclipse/jetty/unixsocket/UnixSocketEndPoint.java74
-rw-r--r--jetty-unixsocket/src/test/java/org/eclipse/jetty/unixsocket/UnixSocketClient.java57
-rw-r--r--jetty-unixsocket/src/test/java/org/eclipse/jetty/unixsocket/UnixSocketServer.java63
-rwxr-xr-xjetty-unixsocket/src/test/resources/haproxybin0 -> 4937496 bytes
-rw-r--r--jetty-unixsocket/src/test/resources/jetty-logging.properties7
-rw-r--r--jetty-util-ajax/pom.xml2
-rw-r--r--jetty-util-ajax/src/main/java/org/eclipse/jetty/util/ajax/JSON.java2
-rw-r--r--jetty-util-ajax/src/main/java/org/eclipse/jetty/util/ajax/JSONCollectionConvertor.java2
-rw-r--r--jetty-util-ajax/src/main/java/org/eclipse/jetty/util/ajax/JSONEnumConvertor.java4
-rw-r--r--jetty-util-ajax/src/main/java/org/eclipse/jetty/util/ajax/JSONPojoConvertorFactory.java4
-rw-r--r--jetty-util/pom.xml19
-rw-r--r--jetty-util/src/main/config/modules/logging.mod6
-rw-r--r--jetty-util/src/main/java/org/eclipse/jetty/util/BufferUtil.java57
-rw-r--r--jetty-util/src/main/java/org/eclipse/jetty/util/Callback.java118
-rw-r--r--jetty-util/src/main/java/org/eclipse/jetty/util/IteratingCallback.java12
-rw-r--r--jetty-util/src/main/java/org/eclipse/jetty/util/Loader.java146
-rw-r--r--jetty-util/src/main/java/org/eclipse/jetty/util/MultiPartInputStreamParser.java116
-rw-r--r--jetty-util/src/main/java/org/eclipse/jetty/util/Promise.java69
-rw-r--r--jetty-util/src/main/java/org/eclipse/jetty/util/TopologicalSort.java185
-rw-r--r--jetty-util/src/main/java/org/eclipse/jetty/util/TypeUtil.java40
-rw-r--r--jetty-util/src/main/java/org/eclipse/jetty/util/UrlEncoded.java12
-rw-r--r--jetty-util/src/main/java/org/eclipse/jetty/util/log/AbstractLogger.java139
-rw-r--r--jetty-util/src/main/java/org/eclipse/jetty/util/log/JavaUtilLog.java152
-rw-r--r--jetty-util/src/main/java/org/eclipse/jetty/util/log/Log.java6
-rw-r--r--jetty-util/src/main/java/org/eclipse/jetty/util/log/StdErrLog.java179
-rw-r--r--jetty-util/src/main/java/org/eclipse/jetty/util/resource/JarResource.java8
-rw-r--r--jetty-util/src/main/java/org/eclipse/jetty/util/resource/Resource.java10
-rw-r--r--jetty-util/src/main/java/org/eclipse/jetty/util/security/Credential.java40
-rw-r--r--jetty-util/src/main/java/org/eclipse/jetty/util/ssl/SslContextFactory.java2
-rw-r--r--jetty-util/src/main/java/org/eclipse/jetty/util/statistic/CounterStatistic.java15
-rw-r--r--jetty-util/src/main/java/org/eclipse/jetty/util/thread/ExecutionStrategy.java2
-rwxr-xr-xjetty-util/src/main/java/org/eclipse/jetty/util/thread/QueuedThreadPool.java4
-rw-r--r--jetty-util/src/test/java/org/eclipse/jetty/util/TopologicalSortTest.java203
-rw-r--r--jetty-util/src/test/java/org/eclipse/jetty/util/log/StdErrLogTest.java2
-rw-r--r--jetty-util/src/test/java/org/eclipse/jetty/util/security/CredentialTest.java79
-rw-r--r--jetty-util/src/test/java/org/eclipse/jetty/util/statistic/CounterStatisticTest.java80
-rw-r--r--jetty-webapp/pom.xml2
-rw-r--r--jetty-webapp/src/main/config/modules/webapp.mod6
-rw-r--r--jetty-webapp/src/main/java/org/eclipse/jetty/webapp/AbsoluteOrdering.java95
-rw-r--r--jetty-webapp/src/main/java/org/eclipse/jetty/webapp/CachingWebAppClassLoader.java124
-rw-r--r--jetty-webapp/src/main/java/org/eclipse/jetty/webapp/DiscoveredAnnotation.java2
-rw-r--r--jetty-webapp/src/main/java/org/eclipse/jetty/webapp/JettyWebXmlConfiguration.java7
-rw-r--r--jetty-webapp/src/main/java/org/eclipse/jetty/webapp/MetaData.java20
-rw-r--r--jetty-webapp/src/main/java/org/eclipse/jetty/webapp/MetaInfConfiguration.java14
-rw-r--r--jetty-webapp/src/main/java/org/eclipse/jetty/webapp/Ordering.java460
-rw-r--r--jetty-webapp/src/main/java/org/eclipse/jetty/webapp/RelativeOrdering.java144
-rw-r--r--jetty-webapp/src/main/java/org/eclipse/jetty/webapp/StandardDescriptorProcessor.java58
-rw-r--r--jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebAppClassLoader.java106
-rw-r--r--jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebAppContext.java18
-rw-r--r--jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebDescriptor.java62
-rw-r--r--jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebInfConfiguration.java6
-rw-r--r--jetty-webapp/src/test/java/org/eclipse/jetty/webapp/OrderingTest.java37
-rw-r--r--jetty-websocket/javax-websocket-client-impl/pom.xml2
-rw-r--r--jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/DecoderReaderManySmallTest.java4
-rw-r--r--jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/DecoderReaderTest.java6
-rw-r--r--jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/EncoderTest.java4
-rw-r--r--jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/JsrSessionTest.java4
-rw-r--r--jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/samples/DummyConnection.java156
-rw-r--r--jetty-websocket/javax-websocket-server-impl/pom.xml2
-rw-r--r--jetty-websocket/javax-websocket-server-impl/src/main/config/modules/websocket.mod5
-rw-r--r--jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/ConfiguratorTest.java21
-rw-r--r--jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/OnPartialTest.java1
-rw-r--r--jetty-websocket/pom.xml20
-rw-r--r--jetty-websocket/websocket-api/pom.xml2
-rw-r--r--jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/util/QuoteUtil.java17
-rw-r--r--jetty-websocket/websocket-client/pom.xml2
-rw-r--r--jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/WebSocketClient.java1
-rw-r--r--jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/io/ConnectPromise.java15
-rw-r--r--jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/io/ConnectionManager.java44
-rw-r--r--jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/io/UpgradeConnection.java7
-rw-r--r--jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/io/WebSocketClientConnection.java16
-rw-r--r--jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/io/WebSocketClientSelectorManager.java29
-rw-r--r--jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/BadNetworkTest.java6
-rw-r--r--jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/ClientCloseTest.java47
-rw-r--r--jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/ClientConnectTest.java41
-rw-r--r--jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/CookieTest.java12
-rw-r--r--jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/JettyTrackingSocket.java6
-rw-r--r--jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/ServerReadThread.java10
-rw-r--r--jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/ServerWriteThread.java6
-rw-r--r--jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/SessionTest.java11
-rw-r--r--jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/SlowClientTest.java6
-rw-r--r--jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/SlowServerTest.java8
-rw-r--r--jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/TomcatServerQuirksTest.java8
-rw-r--r--jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/WebSocketClientTest.java29
-rw-r--r--jetty-websocket/websocket-common/pom.xml2
-rw-r--r--jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/LogicalConnection.java15
-rw-r--r--jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/WebSocketSession.java79
-rw-r--r--jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/events/AbstractEventDriver.java11
-rw-r--r--jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/events/annotated/CallableMethod.java3
-rw-r--r--jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/io/AbstractWebSocketConnection.java75
-rw-r--r--jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/io/IOState.java15
-rw-r--r--jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/io/LocalWebSocketConnection.java12
-rw-r--r--jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/message/MessageInputStreamTest.java1
-rw-r--r--jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/BlockheadClient.java73
-rw-r--r--jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/BlockheadServer.java615
-rw-r--r--jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/BlockheadServerConnection.java614
-rw-r--r--jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/DummyConnection.java (renamed from jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/DummyConnection.java)15
-rw-r--r--jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/IBlockheadClient.java79
-rw-r--r--jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/IBlockheadServerConnection.java68
-rw-r--r--jetty-websocket/websocket-server/pom.xml3
-rw-r--r--jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/WebSocketServerConnection.java14
-rw-r--r--jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/WebSocketServerFactory.java58
-rw-r--r--jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/FirefoxTest.java5
-rw-r--r--jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ManyConnectionsCleanupTest.java369
-rw-r--r--jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/WebSocketCloseTest.java20
-rw-r--r--jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/WebSocketServerSessionTest.java5
-rw-r--r--jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/browser/BrowserSocket.java2
-rw-r--r--jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/misbehaving/MisbehavingClassTest.java10
-rw-r--r--jetty-websocket/websocket-server/src/test/resources/jetty-logging.properties5
-rw-r--r--jetty-websocket/websocket-servlet/pom.xml3
-rw-r--r--jetty-xml/pom.xml2
-rw-r--r--jetty-xml/src/main/java/org/eclipse/jetty/xml/XmlConfiguration.java16
-rw-r--r--pom.xml62
-rw-r--r--tests/pom.xml2
-rw-r--r--tests/test-continuation/pom.xml2
-rw-r--r--tests/test-continuation/src/test/java/org/eclipse/jetty/continuation/ContinuationsTest.java2
-rw-r--r--tests/test-http-client-transport/pom.xml44
-rw-r--r--tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/AbstractTest.java141
-rw-r--r--tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/AsyncRequestContentTest.java191
-rw-r--r--tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/HttpClientIdleTimeoutTest.java24
-rw-r--r--tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/HttpClientLoadTest.java (renamed from jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientLoadTest.java)188
-rw-r--r--tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/HttpClientTest.java68
-rw-r--r--tests/test-http-client-transport/src/test/resources/keystore.jksbin0 -> 2206 bytes
-rw-r--r--tests/test-http-client-transport/src/test/resources/truststore.jksbin0 -> 916 bytes
-rw-r--r--tests/test-integration/pom.xml2
-rw-r--r--tests/test-integration/src/test/java/org/eclipse/jetty/test/DigestPostTest.java45
-rw-r--r--tests/test-jmx/jmx-webapp-it/pom.xml2
-rw-r--r--tests/test-jmx/jmx-webapp-it/src/test/java/org/eclipse/jetty/test/jmx/JmxIT.java2
-rw-r--r--tests/test-jmx/jmx-webapp/pom.xml2
-rw-r--r--tests/test-jmx/pom.xml2
-rw-r--r--tests/test-loginservice/pom.xml2
-rw-r--r--tests/test-loginservice/src/test/java/org/eclipse/jetty/DataSourceLoginServiceTest.java6
-rw-r--r--tests/test-loginservice/src/test/java/org/eclipse/jetty/DatabaseLoginServiceTestServer.java2
-rw-r--r--tests/test-quickstart/pom.xml2
-rw-r--r--tests/test-sessions/pom.xml3
-rw-r--r--tests/test-sessions/test-gcloud-sessions/pom.xml110
-rw-r--r--tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/ClientCrossContextSessionTest.java66
-rw-r--r--tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/ForwardedSessionTest.java58
-rw-r--r--tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/GCloudSessionTestSupport.java357
-rw-r--r--tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/GCloudTestServer.java101
-rw-r--r--tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/ImmortalSessionTest.java69
-rw-r--r--tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/InvalidationSessionTest.java78
-rw-r--r--tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/LastAccessTimeTest.java66
-rw-r--r--tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/LocalSessionScavengingTest.java67
-rw-r--r--tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/NewSessionTest.java70
-rw-r--r--tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/OrphanedSessionTest.java71
-rw-r--r--tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/ReentrantRequestSessionTest.java68
-rw-r--r--tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/RemoveSessionTest.java71
-rw-r--r--tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/SameNodeLoadTest.java67
-rw-r--r--tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/ServerCrossContextSessionTest.java67
-rw-r--r--tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/SessionExpiryTest.java98
-rw-r--r--tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/SessionInvalidateAndCreateTest.java69
-rw-r--r--tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/SessionMigrationTest.java68
-rw-r--r--tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/SessionRenewTest.java67
-rw-r--r--tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/SessionValueSavingTest.java68
-rw-r--r--tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/StopSessionManagerPreserveSessionTest.java95
-rw-r--r--tests/test-sessions/test-hash-sessions/pom.xml2
-rw-r--r--tests/test-sessions/test-infinispan-sessions/pom.xml2
-rw-r--r--tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/LastAccessTimeTest.java16
-rw-r--r--tests/test-sessions/test-jdbc-sessions/pom.xml6
-rw-r--r--tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/ClientCrossContextSessionTest.java13
-rw-r--r--tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/DirtyAttributeTest.java9
-rw-r--r--tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/ForwardedSessionTest.java7
-rw-r--r--tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/ImmortalSessionTest.java12
-rw-r--r--tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/InvalidationSessionTest.java11
-rw-r--r--tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/JdbcTestServer.java24
-rw-r--r--tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/LastAccessTimeTest.java12
-rw-r--r--tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/LocalSessionScavengingTest.java12
-rw-r--r--tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/MaxInactiveMigrationTest.java11
-rw-r--r--tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/ModifyMaxInactiveIntervalTest.java8
-rw-r--r--tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/NewSessionTest.java12
-rw-r--r--tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/OrphanedSessionTest.java11
-rw-r--r--tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/ProxySerializationTest.java8
-rw-r--r--tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/ReentrantRequestSessionTest.java13
-rw-r--r--tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/ReloadedSessionMissingClassTest.java8
-rw-r--r--tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/SaveIntervalTest.java7
-rw-r--r--tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/ServerCrossContextSessionTest.java11
-rw-r--r--tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/SessionExpiryTest.java15
-rw-r--r--tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/SessionInvalidateAndCreateTest.java9
-rw-r--r--tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/SessionMigrationTest.java11
-rw-r--r--tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/SessionRenewTest.java14
-rw-r--r--tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/SessionValueSavingTest.java30
-rw-r--r--tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/StopSessionManagerPreserveSessionTest.java14
-rw-r--r--tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/WebAppObjectInSessionTest.java13
-rw-r--r--tests/test-sessions/test-mongodb-sessions/pom.xml2
-rw-r--r--tests/test-sessions/test-sessions-common/pom.xml2
-rw-r--r--tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractImmortalSessionTest.java4
-rw-r--r--tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractInvalidationSessionTest.java1
-rw-r--r--tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractLastAccessTimeTest.java35
-rw-r--r--tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractRemoveSessionTest.java19
-rw-r--r--tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSameNodeLoadTest.java2
-rw-r--r--tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractServerCrossContextSessionTest.java6
-rw-r--r--tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSessionCookieTest.java4
-rw-r--r--tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSessionExpiryTest.java10
-rw-r--r--tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSessionRenewTest.java1
-rw-r--r--tests/test-webapps/pom.xml2
-rw-r--r--tests/test-webapps/test-jaas-webapp/pom.xml13
-rw-r--r--tests/test-webapps/test-jetty-webapp/pom.xml10
-rw-r--r--tests/test-webapps/test-jetty-webapp/src/main/assembly/embedded-jetty-web-for-webbundle.xml3
-rw-r--r--tests/test-webapps/test-jetty-webapp/src/main/config/demo-base/etc/test-realm.xml2
-rw-r--r--tests/test-webapps/test-jetty-webapp/src/main/config/demo-base/webapps/test.xml14
-rw-r--r--tests/test-webapps/test-jetty-webapp/src/main/java/com/acme/Dump.java2
-rw-r--r--tests/test-webapps/test-jndi-webapp/pom.xml2
-rw-r--r--tests/test-webapps/test-mock-resources/pom.xml2
-rw-r--r--tests/test-webapps/test-proxy-webapp/pom.xml2
-rw-r--r--tests/test-webapps/test-servlet-spec/pom.xml2
-rw-r--r--tests/test-webapps/test-servlet-spec/test-container-initializer/pom.xml2
-rw-r--r--tests/test-webapps/test-servlet-spec/test-spec-webapp/pom.xml18
-rw-r--r--tests/test-webapps/test-servlet-spec/test-web-fragment/pom.xml2
-rw-r--r--tests/test-webapps/test-webapp-rfc2616/pom.xml2
705 files changed, 22185 insertions, 11125 deletions
diff --git a/VERSION.txt b/VERSION.txt
index c41a182d65..d68345e6a0 100644
--- a/VERSION.txt
+++ b/VERSION.txt
@@ -1,4 +1,89 @@
-jetty-9.3.4-SNAPSHOT
+jetty-9.4.0-SNAPSHOT
+
+jetty-9.3.6.v20151106 - 06 November 2015
+ + 419966 Add ContentProvider that submits multipart/form-data.
+ + 472675 No main manifest attribute, in jetty-runner regression
+ + 476641 Proxy rewriteTarget() null return does not call error handler.
+ + 478757 DebugHandler thread name is mangled
+ + 479179 Fixed NPE from debug
+ + 479378 Incorrect REQUEST_URI.
+ + 479712 Documented --approve-all-licenses
+ + 479832 Use system properties for gcloud config for GCloudDatastore session
+ manager
+ + 479839 Regression when starting application with excessive scan times
+ + 479865 IllegalStateException: Multiple servlets map to path: *.jsp: jsp,jsp
+ + 480061 HTTP/2 server doesn't send GOAWAY frame when shutting down.
+ + 480162 Continuations behavior differences due to HttpURI behavior
+ + 480260 HPack decode error for buffers with offset.
+ + 480272 Update to newer jdt ecj version
+ + 480452 Large downloads via FastCGI proxy keep HttpClient connections active.
+ + 480764 Error parsing empty multipart.
+ + 481006 SSL requests intermittently fail with EOFException when SSL
+ renegotiation is disallowed.
+ + 481203 Add ability to set configurations to apply to WebAppContext for
+ jetty-maven-plugin
+ + 481225 Secondary resources with query parameters are not properly pushed.
+ + 481236 Make ShutdownMonitor java security manager friendly
+ + 481355 Nested Symlinks
+ + 481373 Corner cases where session may remain in JDBCSessionManager memory
+ + 481385 Incorrect parsing of END_REQUEST frames.
+ + 481418 ResourceHandler sets last modified
+ + 481437 Port ConnectHandler connect and context functionality from Jetty 8.
+ + 481554 DispatcherType reset race
+
+jetty-9.3.5.v20151012 - 12 October 2015
+ + 479343 calls to MetaData#orderFragments() with relative ordering adds
+ duplicate jars
+ + 479537 Server preface sent after client preface reply.
+ + 479584 WS Session does not contain UpgradeRequest information in
+ WebSocketAdapter.onWebSocketConnect callback
+
+jetty-9.3.4.v20151007 - 07 October 2015
+ + 428474 Expose batch mode in the Jetty WebSocket API
+ + 472082 isOpen returns true on CLOSING Connection
+ + 474936 WebSocketSessions are not always cleaned out from openSessions
+ + 475209 WebSocketServerFactory should not hand null object to
+ DecoratedObjectFactory
+ + 476023 Incorrect trimming of WebSocket close reason
+ + 476049 When using WebSocket Session.close() there should be no status code
+ or reason sent
+ + 476170 Support servers that close connections without sending Connection:
+ close header.
+ + 476720 getTrustStoreResource fixed
+ + 477087 Enforce that the preface contains a SETTINGS frame.
+ + 477123 AsyncListener callbacks need context scope
+ + 477270 Add ability to send a single PRIORITY frame.
+ + 477278 Refactored DefaultServlet for cached Gzip & Etags
+ + 477385 Make jetty osgi manifests only resolve jetty packages against a
+ single distro version
+ + 477641 ALPN classes exposed to webapps - fixed typo
+ + 477680 Encode merged query parameters
+ + 477737 Improve handling of etags with dynamic and static gzip
+ + 477757 Null args in TypeUtil .call & .construct result in confusing
+ exceptions
+ + 477817 Fixed memory leak in QueuedThreadPool
+ + 477878 HttpClient over HTTP/2 doesn't close upload stream.
+ + 477885 Jetty HTTP2 client fails to connect with Netty server - HTTP2 client
+ preface missing or corrupt.
+ + 477890 Overwhelmed HTTP/2 server discards data.
+ + 477895 Prevent leak of handles to deleted files after redeploy
+ + 477900 Increase client authentication default max content size
+ + 478008 Do not reset current value of CounterStatistics
+ + 478021 Client sending Connection: close does not shutdown output.
+ + 478105 prependFilterMapping check for null FilterHolder
+ + 478239 Remove pointless synchronize in infinispan scavenging
+ + 478247 WebappClassLoader pinned after redeploy
+ + 478275 Priority information in HEADERS frame is not sent.
+ + 478280 property file in temp directory
+ + 478372 JavaUtilLog setSourceClass and setSourceMethod
+ + 478434 Priority weights should be between 1 and 256 inclusive.
+ + 478752 Clarify support for HttpServletRequest.upgrade()
+ + 478757 DebugHandler thread name is mangled
+ + 478829 WebsocketSession not cleaned up / memory leak
+ + 478862 Update to jstl 1.2.5
+ + 478923 threads stuck at SharedBlockingCallback$Blocker.block
+ + 479026 Wrong CONNECT request idle timeout.
+ + 479277 HttpClient with HTTP/2 transport does not work for "https" URLs.
jetty-9.3.3.v20150827 - 27 August 2015
+ 470311 Introduce a proxy-protocol module.
diff --git a/aggregates/jetty-all-compact3/pom.xml b/aggregates/jetty-all-compact3/pom.xml
index 0e2c674eb9..c323c8bdcd 100644
--- a/aggregates/jetty-all-compact3/pom.xml
+++ b/aggregates/jetty-all-compact3/pom.xml
@@ -2,7 +2,7 @@
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId>
- <version>9.3.1-SNAPSHOT</version>
+ <version>9.4.0-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
diff --git a/aggregates/jetty-all/pom.xml b/aggregates/jetty-all/pom.xml
index 4ce3ad4bf8..3e0e91ef96 100644
--- a/aggregates/jetty-all/pom.xml
+++ b/aggregates/jetty-all/pom.xml
@@ -2,7 +2,7 @@
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId>
- <version>9.3.4-SNAPSHOT</version>
+ <version>9.4.0-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
diff --git a/aggregates/jetty-websocket-all/pom.xml b/aggregates/jetty-websocket-all/pom.xml
index 3c9c2689b8..ee65e2390c 100644
--- a/aggregates/jetty-websocket-all/pom.xml
+++ b/aggregates/jetty-websocket-all/pom.xml
@@ -2,7 +2,7 @@
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId>
- <version>9.1.0-SNAPSHOT</version>
+ <version>9.1.3-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -24,7 +24,7 @@
</goals>
<configuration>
<excludes>**/MANIFEST.MF</excludes>
- <excludeGroupIds>org.slf4j,org.eclipse.jetty.orbit,org.mortbay.jetty.alpn</excludeGroupIds>
+ <excludeGroupIds>javax.annotations,org.objectweb.asm,javax.servlet,org.slf4j,org.eclipse.jetty.orbit,org.mortbay.jetty.npn</excludeGroupIds>
<outputDirectory>${project.build.directory}/classes</outputDirectory>
<overWriteReleases>false</overWriteReleases>
<overWriteSnapshots>true</overWriteSnapshots>
diff --git a/apache-jsp/pom.xml b/apache-jsp/pom.xml
index 7267c80a59..66ecfd4fcf 100644
--- a/apache-jsp/pom.xml
+++ b/apache-jsp/pom.xml
@@ -2,7 +2,7 @@
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId>
- <version>9.3.4-SNAPSHOT</version>
+ <version>9.4.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>apache-jsp</artifactId>
@@ -88,8 +88,8 @@
<!-- Eclipse Java Compiler (for JSP Compilation) -->
<dependency>
- <groupId>org.eclipse.jetty.orbit</groupId>
- <artifactId>org.eclipse.jdt.core</artifactId>
+ <groupId>org.eclipse.jdt.core.compiler</groupId>
+ <artifactId>ecj</artifactId>
</dependency>
</dependencies>
</project>
diff --git a/apache-jsp/src/main/config/modules/apache-jsp.mod b/apache-jsp/src/main/config/modules/apache-jsp.mod
index 5123670cb0..c816f61c04 100644
--- a/apache-jsp/src/main/config/modules/apache-jsp.mod
+++ b/apache-jsp/src/main/config/modules/apache-jsp.mod
@@ -1,6 +1,5 @@
-#
-# Apache JSP Module
-#
+[description]
+Enables use of the apache implementation of JSP
[name]
apache-jsp
diff --git a/apache-jstl/pom.xml b/apache-jstl/pom.xml
index 4d21ada6b0..3396c849aa 100644
--- a/apache-jstl/pom.xml
+++ b/apache-jstl/pom.xml
@@ -2,7 +2,7 @@
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId>
- <version>9.3.4-SNAPSHOT</version>
+ <version>9.4.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>apache-jstl</artifactId>
diff --git a/apache-jstl/src/main/config/modules/apache-jstl.mod b/apache-jstl/src/main/config/modules/apache-jstl.mod
index e4a9001a2a..d7c703e7ea 100644
--- a/apache-jstl/src/main/config/modules/apache-jstl.mod
+++ b/apache-jstl/src/main/config/modules/apache-jstl.mod
@@ -1,6 +1,5 @@
-#
-# Apache JSTL
-#
+[description]
+Enables the apache version of JSTL
[name]
apache-jstl
diff --git a/examples/async-rest/async-rest-jar/pom.xml b/examples/async-rest/async-rest-jar/pom.xml
index 447d2c1d16..1d3dc19692 100644
--- a/examples/async-rest/async-rest-jar/pom.xml
+++ b/examples/async-rest/async-rest-jar/pom.xml
@@ -2,7 +2,7 @@
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>example-async-rest</artifactId>
- <version>9.3.4-SNAPSHOT</version>
+ <version>9.4.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>org.eclipse.jetty.example-async-rest</groupId>
diff --git a/examples/async-rest/async-rest-webapp/pom.xml b/examples/async-rest/async-rest-webapp/pom.xml
index a4b5301b36..52b08a7784 100644
--- a/examples/async-rest/async-rest-webapp/pom.xml
+++ b/examples/async-rest/async-rest-webapp/pom.xml
@@ -2,7 +2,7 @@
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>example-async-rest</artifactId>
- <version>9.3.4-SNAPSHOT</version>
+ <version>9.4.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>org.eclipse.jetty.example-async-rest</groupId>
diff --git a/examples/async-rest/pom.xml b/examples/async-rest/pom.xml
index 46ac32d248..3a3f023f0d 100644
--- a/examples/async-rest/pom.xml
+++ b/examples/async-rest/pom.xml
@@ -2,7 +2,7 @@
<parent>
<groupId>org.eclipse.jetty.examples</groupId>
<artifactId>examples-parent</artifactId>
- <version>9.3.4-SNAPSHOT</version>
+ <version>9.4.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
diff --git a/examples/embedded/pom.xml b/examples/embedded/pom.xml
index 0ec8639270..93806b92a4 100644
--- a/examples/embedded/pom.xml
+++ b/examples/embedded/pom.xml
@@ -2,7 +2,7 @@
<parent>
<groupId>org.eclipse.jetty.examples</groupId>
<artifactId>examples-parent</artifactId>
- <version>9.3.4-SNAPSHOT</version>
+ <version>9.4.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/Http2Server.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/Http2Server.java
index 8d984bf2d1..8e19c0a20e 100644
--- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/Http2Server.java
+++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/Http2Server.java
@@ -55,6 +55,7 @@ import org.eclipse.jetty.server.SslConnectionFactory;
import org.eclipse.jetty.servlet.DefaultServlet;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
+import org.eclipse.jetty.servlets.PushCacheFilter;
import org.eclipse.jetty.servlets.PushSessionCacheFilter;
import org.eclipse.jetty.util.ssl.SslContextFactory;
@@ -74,7 +75,8 @@ public class Http2Server
ServletContextHandler context = new ServletContextHandler(server, "/",ServletContextHandler.SESSIONS);
context.setResourceBase("src/main/resources/docroot");
- context.addFilter(PushSessionCacheFilter.class,"/*",EnumSet.of(DispatcherType.REQUEST));
+ context.addFilter(PushCacheFilter.class,"/*",EnumSet.of(DispatcherType.REQUEST));
+ // context.addFilter(PushSessionCacheFilter.class,"/*",EnumSet.of(DispatcherType.REQUEST));
context.addFilter(PushedTilesFilter.class,"/*",EnumSet.of(DispatcherType.REQUEST));
context.addServlet(new ServletHolder(servlet), "/test/*");
context.addServlet(DefaultServlet.class, "/").setInitParameter("maxCacheSize","81920");
diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/LikeJettyXml.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/LikeJettyXml.java
index 9cfc2ddcb7..38534445fb 100644
--- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/LikeJettyXml.java
+++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/LikeJettyXml.java
@@ -24,10 +24,12 @@ import java.lang.management.ManagementFactory;
import org.eclipse.jetty.deploy.DeploymentManager;
import org.eclipse.jetty.deploy.PropertiesConfigurationManager;
+import org.eclipse.jetty.deploy.bindings.DebugListenerBinding;
import org.eclipse.jetty.deploy.providers.WebAppProvider;
import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.jmx.MBeanContainer;
import org.eclipse.jetty.security.HashLoginService;
+import org.eclipse.jetty.server.DebugListener;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.HttpConfiguration;
import org.eclipse.jetty.server.HttpConnectionFactory;
@@ -155,6 +157,9 @@ public class LikeJettyXml
// === jetty-deploy.xml ===
DeploymentManager deployer = new DeploymentManager();
+ DebugListener debug = new DebugListener(System.out,true,true,true);
+ server.addBean(debug);
+ deployer.addLifeCycleBinding(new DebugListenerBinding(debug));
deployer.setContexts(contexts);
deployer.setContextAttribute(
"org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern",
diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneWebApp.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneWebApp.java
index e6882556ad..312d0adc7f 100644
--- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneWebApp.java
+++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneWebApp.java
@@ -52,9 +52,8 @@ public class OneWebApp
WebAppContext webapp = new WebAppContext();
webapp.setContextPath("/");
File warFile = new File(
- "../../jetty-distribution/target/distribution/test/webapps/test/");
+ "../../tests/test-jmx/jmx-webapp/target/jmx-webapp");
webapp.setWar(warFile.getAbsolutePath());
- webapp.addAliasCheck(new AllowSymLinkAliasChecker());
// A WebAppContext is a ContextHandler as well so it needs to be set to
// the server so it is aware of where to send the appropriate requests.
diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneWebAppWithJsp.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneWebAppWithJsp.java
index f391be7e02..58c177a59f 100644
--- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneWebAppWithJsp.java
+++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneWebAppWithJsp.java
@@ -62,6 +62,7 @@ public class OneWebAppWithJsp
+ warFile.getAbsolutePath() );
}
webapp.setWar( warFile.getAbsolutePath() );
+ webapp.setExtractWAR(true);
// This webapp will use jsps and jstl. We need to enable the
// AnnotationConfiguration in order to correctly
@@ -100,6 +101,8 @@ public class OneWebAppWithJsp
// Start things up!
server.start();
+
+ server.dumpStdErr();
// The use of server.join() the will make the current thread join and
// wait until the server is done executing.
diff --git a/examples/embedded/src/main/resources/java-util-logging.properties b/examples/embedded/src/main/resources/java-util-logging.properties
new file mode 100644
index 0000000000..4aaa236d96
--- /dev/null
+++ b/examples/embedded/src/main/resources/java-util-logging.properties
@@ -0,0 +1,9 @@
+
+# Logging
+handlers = java.util.logging.ConsoleHandler
+.level = INFO
+
+java.util.logging.SimpleFormatter.format=%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS %4$-6s %2$s %5$s%6$s%n
+
+# Console Logging
+java.util.logging.ConsoleHandler.level = ALL \ No newline at end of file
diff --git a/examples/embedded/src/main/resources/jetty-logging.properties b/examples/embedded/src/main/resources/jetty-logging.properties
index 04163c07af..62624e2d4a 100644
--- a/examples/embedded/src/main/resources/jetty-logging.properties
+++ b/examples/embedded/src/main/resources/jetty-logging.properties
@@ -1,9 +1,9 @@
-org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
+#org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.JavaUtilLog
+#org.eclipse.jetty.util.log.javautil.PROPERTIES=java-util-logging.properties
+#org.eclipse.jetty.util.log.SOURCE=true
org.eclipse.jetty.LEVEL=INFO
org.eclipse.jetty.STACKS=true
-org.eclipse.jetty.SOURCE=false
#org.eclipse.jetty.STACKS=false
-#org.eclipse.jetty.server.LEVEL=DEBUG
#org.eclipse.jetty.io.LEVEL=DEBUG
#org.eclipse.jetty.io.ssl.LEVEL=DEBUG
#org.eclipse.jetty.server.LEVEL=DEBUG
diff --git a/examples/pom.xml b/examples/pom.xml
index 69bc2c27f9..33883db3bc 100644
--- a/examples/pom.xml
+++ b/examples/pom.xml
@@ -21,7 +21,7 @@
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId>
- <version>9.3.4-SNAPSHOT</version>
+ <version>9.4.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<groupId>org.eclipse.jetty.examples</groupId>
diff --git a/jetty-alpn/jetty-alpn-client/pom.xml b/jetty-alpn/jetty-alpn-client/pom.xml
index e25c280081..5128921e97 100644
--- a/jetty-alpn/jetty-alpn-client/pom.xml
+++ b/jetty-alpn/jetty-alpn-client/pom.xml
@@ -2,7 +2,7 @@
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-alpn-parent</artifactId>
- <version>9.3.4-SNAPSHOT</version>
+ <version>9.4.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jetty-alpn-client</artifactId>
diff --git a/jetty-alpn/jetty-alpn-server/pom.xml b/jetty-alpn/jetty-alpn-server/pom.xml
index 3465055281..98fc5250fd 100644
--- a/jetty-alpn/jetty-alpn-server/pom.xml
+++ b/jetty-alpn/jetty-alpn-server/pom.xml
@@ -2,7 +2,7 @@
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-alpn-parent</artifactId>
- <version>9.3.4-SNAPSHOT</version>
+ <version>9.4.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jetty-alpn-server</artifactId>
diff --git a/jetty-alpn/jetty-alpn-server/src/main/config/modules/alpn-impl/alpn-1.8.0_60.mod b/jetty-alpn/jetty-alpn-server/src/main/config/modules/alpn-impl/alpn-1.8.0_60.mod
new file mode 100644
index 0000000000..9d207d9a65
--- /dev/null
+++ b/jetty-alpn/jetty-alpn-server/src/main/config/modules/alpn-impl/alpn-1.8.0_60.mod
@@ -0,0 +1,8 @@
+[name]
+protonego-boot
+
+[files]
+http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/8.1.5.v20150921/alpn-boot-8.1.5.v20150921.jar|lib/alpn/alpn-boot-8.1.5.v20150921.jar
+
+[exec]
+-Xbootclasspath/p:lib/alpn/alpn-boot-8.1.5.v20150921.jar
diff --git a/jetty-alpn/jetty-alpn-server/src/main/config/modules/alpn-impl/alpn-1.8.0_65.mod b/jetty-alpn/jetty-alpn-server/src/main/config/modules/alpn-impl/alpn-1.8.0_65.mod
new file mode 100644
index 0000000000..03b32d0774
--- /dev/null
+++ b/jetty-alpn/jetty-alpn-server/src/main/config/modules/alpn-impl/alpn-1.8.0_65.mod
@@ -0,0 +1,8 @@
+[name]
+protonego-boot
+
+[files]
+http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/8.1.6.v20151105/alpn-boot-8.1.6.v20151105.jar|lib/alpn/alpn-boot-8.1.6.v20151105.jar
+
+[exec]
+-Xbootclasspath/p:lib/alpn/alpn-boot-8.1.6.v20151105.jar
diff --git a/jetty-alpn/jetty-alpn-server/src/main/config/modules/alpn-impl/alpn-1.8.0_66.mod b/jetty-alpn/jetty-alpn-server/src/main/config/modules/alpn-impl/alpn-1.8.0_66.mod
new file mode 100644
index 0000000000..03b32d0774
--- /dev/null
+++ b/jetty-alpn/jetty-alpn-server/src/main/config/modules/alpn-impl/alpn-1.8.0_66.mod
@@ -0,0 +1,8 @@
+[name]
+protonego-boot
+
+[files]
+http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/8.1.6.v20151105/alpn-boot-8.1.6.v20151105.jar|lib/alpn/alpn-boot-8.1.6.v20151105.jar
+
+[exec]
+-Xbootclasspath/p:lib/alpn/alpn-boot-8.1.6.v20151105.jar
diff --git a/jetty-alpn/jetty-alpn-server/src/main/config/modules/alpn.mod b/jetty-alpn/jetty-alpn-server/src/main/config/modules/alpn.mod
index 7928e64928..10997501ff 100644
--- a/jetty-alpn/jetty-alpn-server/src/main/config/modules/alpn.mod
+++ b/jetty-alpn/jetty-alpn-server/src/main/config/modules/alpn.mod
@@ -1,11 +1,10 @@
-# ALPN is provided via a -Xbootclasspath that modifies the secure connections
-# in java to support the ALPN layer needed for HTTP/2.
-#
-# This modification has a tight dependency on specific recent updates of
-# Java 1.7 and Java 1.8 (Java versions prior to 1.7u40 are not supported).
-#
-# The alpn module will use an appropriate alpn-boot jar for your
-# specific version of Java.
+[description]
+Enables the ALPN extension to TLS(SSL) by adding modified classes to
+the JVM bootpath.
+This modification has a tight dependency on specific recent updates of
+Java 1.7 and Java 1.8 (Java versions prior to 1.7u40 are not supported).
+The alpn module will use an appropriate alpn-boot jar for your
+specific version of Java.
#
# IMPORTANT: Versions of Java that exist after this module was created are
# not guaranteed to work with existing alpn-boot jars, and might
diff --git a/jetty-alpn/pom.xml b/jetty-alpn/pom.xml
index 1c9efdddf6..4157d6c746 100644
--- a/jetty-alpn/pom.xml
+++ b/jetty-alpn/pom.xml
@@ -2,7 +2,7 @@
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId>
- <version>9.3.4-SNAPSHOT</version>
+ <version>9.4.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jetty-alpn-parent</artifactId>
diff --git a/jetty-annotations/pom.xml b/jetty-annotations/pom.xml
index 6690bce63e..8f432097d1 100644
--- a/jetty-annotations/pom.xml
+++ b/jetty-annotations/pom.xml
@@ -2,7 +2,7 @@
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId>
- <version>9.3.4-SNAPSHOT</version>
+ <version>9.4.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jetty-annotations</artifactId>
@@ -20,7 +20,6 @@
<extensions>true</extensions>
<configuration>
<instructions>
- <Import-Package>javax.servlet.*;version="[2.6.0,3.2)",org.objectweb.asm.*;version=5,*</Import-Package>
<Require-Capability>osgi.serviceloader; filter:="(osgi.serviceloader=javax.servlet.ServletContainerInitializer)";resolution:=optional;cardinality:=multiple, osgi.extender; filter:="(osgi.extender=osgi.serviceloader.processor)"</Require-Capability>
</instructions>
</configuration>
diff --git a/jetty-annotations/src/main/config/modules/annotations.mod b/jetty-annotations/src/main/config/modules/annotations.mod
index 65e4654127..4217be54fb 100644
--- a/jetty-annotations/src/main/config/modules/annotations.mod
+++ b/jetty-annotations/src/main/config/modules/annotations.mod
@@ -1,15 +1,11 @@
-#
-# Jetty Annotation Scanning Module
-#
+[description]
+Enables Annotation scanning for deployed webapplications.
[depend]
-# Annotations needs plus, and jndi features
plus
[lib]
-# Annotations needs jetty annotation jars
lib/jetty-annotations-${jetty.version}.jar
-# Need annotation processing jars too
lib/annotations/*.jar
[xml]
diff --git a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AnnotationConfiguration.java b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AnnotationConfiguration.java
index 8713d2bbc3..48d5b93f2e 100644
--- a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AnnotationConfiguration.java
+++ b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AnnotationConfiguration.java
@@ -88,7 +88,7 @@ public class AnnotationConfiguration extends AbstractConfiguration
protected CounterStatistic _webInfLibStats;
protected CounterStatistic _webInfClassesStats;
protected Pattern _sciExcludePattern;
-
+ protected ServiceLoader<ServletContainerInitializer> _loadedInitializers = null;
/**
* TimeStatistic
*
@@ -413,6 +413,9 @@ public class AnnotationConfiguration extends AbstractConfiguration
context.removeBean(starter);
context.removeAttribute(CONTAINER_INITIALIZER_STARTER);
}
+
+ if (_loadedInitializers != null)
+ _loadedInitializers.reload();
}
/**
@@ -823,22 +826,28 @@ public class AnnotationConfiguration extends AbstractConfiguration
return sci.getClass().getClassLoader()==context.getClassLoader().getParent();
}
+ /**
+ * Get SCIs that are not excluded from consideration
+ * @param context the web app context
+ * @return the list of non-excluded servlet container initializers
+ * @throws Exception if unable to get list
+ */
public List<ServletContainerInitializer> getNonExcludedInitializers (WebAppContext context)
throws Exception
{
ArrayList<ServletContainerInitializer> nonExcludedInitializers = new ArrayList<ServletContainerInitializer>();
-
+
//We use the ServiceLoader mechanism to find the ServletContainerInitializer classes to inspect
long start = 0;
ClassLoader old = Thread.currentThread().getContextClassLoader();
- ServiceLoader<ServletContainerInitializer> loadedInitializers = null;
+
try
{
if (LOG.isDebugEnabled())
start = System.nanoTime();
Thread.currentThread().setContextClassLoader(context.getClassLoader());
- loadedInitializers = ServiceLoader.load(ServletContainerInitializer.class);
+ _loadedInitializers = ServiceLoader.load(ServletContainerInitializer.class);
}
finally
{
@@ -847,21 +856,22 @@ public class AnnotationConfiguration extends AbstractConfiguration
if (LOG.isDebugEnabled())
LOG.debug("Service loaders found in {}ms", (TimeUnit.MILLISECONDS.convert((System.nanoTime()-start), TimeUnit.NANOSECONDS)));
-
+
Map<ServletContainerInitializer,Resource> sciResourceMap = new HashMap<ServletContainerInitializer,Resource>();
ServletContainerInitializerOrdering initializerOrdering = getInitializerOrdering(context);
//Get initial set of SCIs that aren't from excluded jars or excluded by the containerExclusionPattern, or excluded
//because containerInitializerOrdering omits it
- for (ServletContainerInitializer sci:loadedInitializers)
- {
+ for (ServletContainerInitializer sci:_loadedInitializers)
+ {
+
if (matchesExclusionPattern(sci))
{
if (LOG.isDebugEnabled()) LOG.debug("{} excluded by pattern", sci);
continue;
}
-
+
Resource sciResource = getJarFor(sci);
if (isFromExcludedJar(context, sci, sciResource))
{
@@ -921,7 +931,7 @@ public class AnnotationConfiguration extends AbstractConfiguration
{
for (Map.Entry<ServletContainerInitializer, Resource> entry:sciResourceMap.entrySet())
{
- if (webInfJar.equals(entry.getValue()))
+ if (webInfJar.equals(entry.getValue()))
nonExcludedInitializers.add(entry.getKey());
}
}
@@ -933,7 +943,8 @@ public class AnnotationConfiguration extends AbstractConfiguration
int i=0;
for (ServletContainerInitializer sci:nonExcludedInitializers)
LOG.debug("ServletContainerInitializer: {} {} from {}",(++i), sci.getClass().getName(), sciResourceMap.get(sci));
- }
+ }
+
return nonExcludedInitializers;
}
diff --git a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AnnotationParser.java b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AnnotationParser.java
index 587743cc34..60e42d8c8e 100644
--- a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AnnotationParser.java
+++ b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AnnotationParser.java
@@ -558,11 +558,14 @@ public class AnnotationParser
if (!isParsed(className) || resolver.shouldOverride(className))
{
className = className.replace('.', '/')+".class";
- URL resource = Loader.getResource(this.getClass(), className);
+ URL resource = Loader.getResource(className);
if (resource!= null)
{
Resource r = Resource.newResource(resource);
- scanClass(handlers, null, r.getInputStream());
+ try (InputStream is = r.getInputStream())
+ {
+ scanClass(handlers, null, is);
+ }
}
}
}
@@ -590,11 +593,14 @@ public class AnnotationParser
if (!isParsed(cz.getName()) || resolver.shouldOverride(cz.getName()))
{
String nameAsResource = cz.getName().replace('.', '/')+".class";
- URL resource = Loader.getResource(this.getClass(), nameAsResource);
+ URL resource = Loader.getResource(nameAsResource);
if (resource!= null)
{
Resource r = Resource.newResource(resource);
- scanClass(handlers, null, r.getInputStream());
+ try (InputStream is = r.getInputStream())
+ {
+ scanClass(handlers, null, is);
+ }
}
}
}
@@ -646,11 +652,14 @@ public class AnnotationParser
if ((resolver == null) || (!resolver.isExcluded(s) && (!isParsed(s) || resolver.shouldOverride(s))))
{
s = s.replace('.', '/')+".class";
- URL resource = Loader.getResource(this.getClass(), s);
+ URL resource = Loader.getResource(s);
if (resource!= null)
{
Resource r = Resource.newResource(resource);
- scanClass(handlers, null, r.getInputStream());
+ try (InputStream is = r.getInputStream())
+ {
+ scanClass(handlers, null, is);
+ }
}
}
}
@@ -845,8 +854,11 @@ public class AnnotationParser
if (fullname.endsWith(".class"))
{
- scanClass(handlers, null, r.getInputStream());
- return;
+ try (InputStream is=r.getInputStream())
+ {
+ scanClass(handlers, null, is);
+ return;
+ }
}
if (LOG.isDebugEnabled()) LOG.warn("Resource not scannable for classes: {}", r);
@@ -963,11 +975,14 @@ public class AnnotationParser
if ((resolver == null)
||
- (!resolver.isExcluded(shortName) && (!isParsed(shortName) || resolver.shouldOverride(shortName))))
+ (!resolver.isExcluded(shortName) && (!isParsed(shortName) || resolver.shouldOverride(shortName))))
{
Resource clazz = Resource.newResource("jar:"+jar.getURI()+"!/"+name);
if (LOG.isDebugEnabled()) {LOG.debug("Scanning class from jar {}", clazz);};
- scanClass(handlers, jar, clazz.getInputStream());
+ try (InputStream is = clazz.getInputStream())
+ {
+ scanClass(handlers, jar, is);
+ }
}
}
}
diff --git a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/Util.java b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/Util.java
index 6faa9590e3..7cb4dfd861 100644
--- a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/Util.java
+++ b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/Util.java
@@ -201,7 +201,7 @@ public class Util
}
case Type.OBJECT:
{
- return (Loader.loadClass(null, t.getClassName()));
+ return (Loader.loadClass(t.getClassName()));
}
case Type.SHORT:
{
diff --git a/jetty-ant/pom.xml b/jetty-ant/pom.xml
index f1c72ce1b9..3d97aac00f 100644
--- a/jetty-ant/pom.xml
+++ b/jetty-ant/pom.xml
@@ -2,7 +2,7 @@
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId>
- <version>9.3.4-SNAPSHOT</version>
+ <version>9.4.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jetty-ant</artifactId>
diff --git a/jetty-cdi/cdi-core/pom.xml b/jetty-cdi/cdi-core/pom.xml
index cfcd1baea9..ea31517183 100644
--- a/jetty-cdi/cdi-core/pom.xml
+++ b/jetty-cdi/cdi-core/pom.xml
@@ -2,7 +2,7 @@
<parent>
<groupId>org.eclipse.jetty.cdi</groupId>
<artifactId>jetty-cdi-parent</artifactId>
- <version>9.3.4-SNAPSHOT</version>
+ <version>9.4.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cdi-core</artifactId>
diff --git a/jetty-cdi/cdi-full-servlet/pom.xml b/jetty-cdi/cdi-full-servlet/pom.xml
index c02720ffba..f8509144da 100644
--- a/jetty-cdi/cdi-full-servlet/pom.xml
+++ b/jetty-cdi/cdi-full-servlet/pom.xml
@@ -2,7 +2,7 @@
<parent>
<groupId>org.eclipse.jetty.cdi</groupId>
<artifactId>jetty-cdi-parent</artifactId>
- <version>9.3.4-SNAPSHOT</version>
+ <version>9.4.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cdi-full-servlet</artifactId>
diff --git a/jetty-cdi/cdi-servlet/pom.xml b/jetty-cdi/cdi-servlet/pom.xml
index e1265a295a..e3b0593490 100644
--- a/jetty-cdi/cdi-servlet/pom.xml
+++ b/jetty-cdi/cdi-servlet/pom.xml
@@ -2,7 +2,7 @@
<parent>
<groupId>org.eclipse.jetty.cdi</groupId>
<artifactId>jetty-cdi-parent</artifactId>
- <version>9.3.4-SNAPSHOT</version>
+ <version>9.4.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cdi-servlet</artifactId>
diff --git a/jetty-cdi/cdi-servlet/src/main/config/modules/cdi.mod b/jetty-cdi/cdi-servlet/src/main/config/modules/cdi.mod
index ebffb55aed..68a926d62f 100644
--- a/jetty-cdi/cdi-servlet/src/main/config/modules/cdi.mod
+++ b/jetty-cdi/cdi-servlet/src/main/config/modules/cdi.mod
@@ -1,6 +1,5 @@
-#
-# [EXPERIMENTAL] CDI / Weld Jetty module
-#
+[description]
+Experimental CDI/Weld integration
[depend]
deploy
diff --git a/jetty-cdi/cdi-websocket/pom.xml b/jetty-cdi/cdi-websocket/pom.xml
index 27e5eff8c4..07c6a5a7c3 100644
--- a/jetty-cdi/cdi-websocket/pom.xml
+++ b/jetty-cdi/cdi-websocket/pom.xml
@@ -2,7 +2,7 @@
<parent>
<groupId>org.eclipse.jetty.cdi</groupId>
<artifactId>jetty-cdi-parent</artifactId>
- <version>9.3.4-SNAPSHOT</version>
+ <version>9.4.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cdi-websocket</artifactId>
diff --git a/jetty-cdi/cdi-websocket/src/test/java/org/eclipse/jetty/cdi/websocket/basicscope/ScopeBasicsTest.java b/jetty-cdi/cdi-websocket/src/test/java/org/eclipse/jetty/cdi/websocket/basicscope/ScopeBasicsTest.java
index 3561b2499b..1b5892086f 100644
--- a/jetty-cdi/cdi-websocket/src/test/java/org/eclipse/jetty/cdi/websocket/basicscope/ScopeBasicsTest.java
+++ b/jetty-cdi/cdi-websocket/src/test/java/org/eclipse/jetty/cdi/websocket/basicscope/ScopeBasicsTest.java
@@ -57,6 +57,7 @@ public class ScopeBasicsTest
/**
* Validation of Scope / Inject logic on non-websocket-scoped classes
+ * @throws Exception on test failure
*/
@Test
public void testBasicBehavior() throws Exception
diff --git a/jetty-cdi/cdi-websocket/src/test/java/org/eclipse/jetty/cdi/websocket/wsscope/WebSocketScopeBaselineTest.java b/jetty-cdi/cdi-websocket/src/test/java/org/eclipse/jetty/cdi/websocket/wsscope/WebSocketScopeBaselineTest.java
index 3c9e2e135d..ba1fd997e5 100644
--- a/jetty-cdi/cdi-websocket/src/test/java/org/eclipse/jetty/cdi/websocket/wsscope/WebSocketScopeBaselineTest.java
+++ b/jetty-cdi/cdi-websocket/src/test/java/org/eclipse/jetty/cdi/websocket/wsscope/WebSocketScopeBaselineTest.java
@@ -61,6 +61,7 @@ public class WebSocketScopeBaselineTest
* Test behavior of {@link WebSocketScope} in basic operation.
* <p>
* Food is declared as part of WebSocketScope, and as such, only 1 instance of it can exist.
+ * @throws Exception on test failure
*/
@Test
public void testScopeBehavior() throws Exception
diff --git a/jetty-cdi/pom.xml b/jetty-cdi/pom.xml
index 279de82909..b1cd311412 100644
--- a/jetty-cdi/pom.xml
+++ b/jetty-cdi/pom.xml
@@ -2,7 +2,7 @@
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId>
- <version>9.3.4-SNAPSHOT</version>
+ <version>9.4.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>org.eclipse.jetty.cdi</groupId>
diff --git a/jetty-cdi/test-cdi-webapp/pom.xml b/jetty-cdi/test-cdi-webapp/pom.xml
index 4410043056..931bfc9fc9 100644
--- a/jetty-cdi/test-cdi-webapp/pom.xml
+++ b/jetty-cdi/test-cdi-webapp/pom.xml
@@ -20,7 +20,7 @@
<parent>
<groupId>org.eclipse.jetty.cdi</groupId>
<artifactId>jetty-cdi-parent</artifactId>
- <version>9.3.4-SNAPSHOT</version>
+ <version>9.4.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>test-cdi-webapp</artifactId>
diff --git a/jetty-client/pom.xml b/jetty-client/pom.xml
index cc2168807f..9450cdcc99 100644
--- a/jetty-client/pom.xml
+++ b/jetty-client/pom.xml
@@ -2,7 +2,7 @@
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId>
- <version>9.3.4-SNAPSHOT</version>
+ <version>9.4.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -16,23 +16,6 @@
<build>
<plugins>
<plugin>
- <groupId>org.apache.felix</groupId>
- <artifactId>maven-bundle-plugin</artifactId>
- <extensions>true</extensions>
- <executions>
- <execution>
- <goals>
- <goal>manifest</goal>
- </goals>
- <configuration>
- <instructions>
- <Import-Package>javax.net.*,*</Import-Package>
- </instructions>
- </configuration>
- </execution>
- </executions>
- </plugin>
- <plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>findbugs-maven-plugin</artifactId>
<configuration>
@@ -65,6 +48,44 @@
</execution>
</executions>
</plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-shade-plugin</artifactId>
+ <version>2.4.2</version>
+ <executions>
+ <execution>
+ <phase>package</phase>
+ <goals>
+ <goal>shade</goal>
+ </goals>
+ <configuration>
+ <shadedArtifactAttached>true</shadedArtifactAttached>
+ <shadedClassifierName>hybrid</shadedClassifierName>
+ <artifactSet>
+ <includes>
+ <include>org.eclipse.jetty:jetty-http</include>
+ <include>org.eclipse.jetty:jetty-io</include>
+ <include>org.eclipse.jetty:jetty-util</include>
+ </includes>
+ </artifactSet>
+ <relocations>
+ <relocation>
+ <pattern>org.eclipse.jetty.http</pattern>
+ <shadedPattern>org.eclipse.jetty.client.shaded.http</shadedPattern>
+ </relocation>
+ <relocation>
+ <pattern>org.eclipse.jetty.io</pattern>
+ <shadedPattern>org.eclipse.jetty.client.shaded.io</shadedPattern>
+ </relocation>
+ <relocation>
+ <pattern>org.eclipse.jetty.util</pattern>
+ <shadedPattern>org.eclipse.jetty.client.shaded.util</shadedPattern>
+ </relocation>
+ </relocations>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
</plugins>
</build>
diff --git a/jetty-client/src/main/config/modules/client.mod b/jetty-client/src/main/config/modules/client.mod
index 39b58d4e69..e9d13c8c68 100644
--- a/jetty-client/src/main/config/modules/client.mod
+++ b/jetty-client/src/main/config/modules/client.mod
@@ -1,6 +1,5 @@
-#
-# Client Feature
-#
+[description]
+Adds the Jetty HTTP client to the server classpath.
[lib]
lib/jetty-client-${jetty.version}.jar
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/AbstractConnectionPool.java b/jetty-client/src/main/java/org/eclipse/jetty/client/AbstractConnectionPool.java
new file mode 100644
index 0000000000..d3b13b668d
--- /dev/null
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/AbstractConnectionPool.java
@@ -0,0 +1,199 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2015 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.client;
+
+import java.util.Collection;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.eclipse.jetty.client.api.Connection;
+import org.eclipse.jetty.client.api.Destination;
+import org.eclipse.jetty.util.Callback;
+import org.eclipse.jetty.util.Promise;
+import org.eclipse.jetty.util.annotation.ManagedAttribute;
+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;
+
+public abstract class AbstractConnectionPool implements ConnectionPool, Dumpable
+{
+ private static final Logger LOG = Log.getLogger(AbstractConnectionPool.class);
+
+ private final AtomicBoolean closed = new AtomicBoolean();
+ private final AtomicInteger connectionCount = new AtomicInteger();
+ private final Destination destination;
+ private final int maxConnections;
+ private final Callback requester;
+
+ protected AbstractConnectionPool(Destination destination, int maxConnections, Callback requester)
+ {
+ this.destination = destination;
+ this.maxConnections = maxConnections;
+ this.requester = requester;
+ }
+
+ @ManagedAttribute(value = "The max number of connections", readonly = true)
+ public int getMaxConnectionCount()
+ {
+ return maxConnections;
+ }
+
+ @ManagedAttribute(value = "The number of connections", readonly = true)
+ public int getConnectionCount()
+ {
+ return connectionCount.get();
+ }
+
+ @Override
+ public boolean isEmpty()
+ {
+ return connectionCount.get() == 0;
+ }
+
+ @Override
+ public boolean isClosed()
+ {
+ return closed.get();
+ }
+
+ @Override
+ public Connection acquire()
+ {
+ Connection connection = activate();
+ if (connection == null)
+ connection = tryCreate();
+ return connection;
+ }
+
+ private Connection tryCreate()
+ {
+ while (true)
+ {
+ int current = getConnectionCount();
+ final int next = current + 1;
+
+ if (next > maxConnections)
+ {
+ if (LOG.isDebugEnabled())
+ LOG.debug("Max connections {}/{} reached", current, maxConnections);
+ // Try again the idle connections
+ return activate();
+ }
+
+ if (connectionCount.compareAndSet(current, next))
+ {
+ if (LOG.isDebugEnabled())
+ LOG.debug("Connection {}/{} creation", next, maxConnections);
+
+ destination.newConnection(new Promise<Connection>()
+ {
+ @Override
+ public void succeeded(Connection connection)
+ {
+ if (LOG.isDebugEnabled())
+ LOG.debug("Connection {}/{} creation succeeded {}", next, maxConnections, connection);
+ onCreated(connection);
+ proceed();
+ }
+
+ @Override
+ public void failed(Throwable x)
+ {
+ if (LOG.isDebugEnabled())
+ LOG.debug("Connection " + next + "/" + maxConnections + " creation failed", x);
+ connectionCount.decrementAndGet();
+ requester.failed(x);
+ }
+ });
+
+ // Try again the idle connections
+ return activate();
+ }
+ }
+ }
+
+ protected abstract void onCreated(Connection connection);
+
+ protected void proceed()
+ {
+ requester.succeeded();
+ }
+
+ protected abstract Connection activate();
+
+ protected Connection active(Connection connection)
+ {
+ if (LOG.isDebugEnabled())
+ LOG.debug("Connection active {}", connection);
+ acquired(connection);
+ return connection;
+ }
+
+ protected void acquired(Connection connection)
+ {
+ }
+
+ protected boolean idle(Connection connection, boolean close)
+ {
+ if (close)
+ {
+ if (LOG.isDebugEnabled())
+ LOG.debug("Connection idle close {}", connection);
+ return false;
+ }
+ else
+ {
+ if (LOG.isDebugEnabled())
+ LOG.debug("Connection idle {}", connection);
+ return true;
+ }
+ }
+
+ protected void released(Connection connection)
+ {
+ }
+
+ protected void removed(Connection connection)
+ {
+ int pooled = connectionCount.decrementAndGet();
+ if (LOG.isDebugEnabled())
+ LOG.debug("Connection removed {} - pooled: {}", connection, pooled);
+ }
+
+ @Override
+ public void close()
+ {
+ if (closed.compareAndSet(false, true))
+ {
+ connectionCount.set(0);
+ }
+ }
+
+ protected void close(Collection<Connection> connections)
+ {
+ connections.forEach(Connection::close);
+ }
+
+ @Override
+ public String dump()
+ {
+ return ContainerLifeCycle.dump(this);
+ }
+}
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/AbstractHttpClientTransport.java b/jetty-client/src/main/java/org/eclipse/jetty/client/AbstractHttpClientTransport.java
index f4e9d90f7e..81959031f3 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/AbstractHttpClientTransport.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/AbstractHttpClientTransport.java
@@ -22,6 +22,7 @@ import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.net.SocketException;
+import java.nio.channels.SelectableChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.SocketChannel;
import java.util.Map;
@@ -31,6 +32,7 @@ import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.io.ManagedSelector;
import org.eclipse.jetty.io.SelectChannelEndPoint;
import org.eclipse.jetty.io.SelectorManager;
+import org.eclipse.jetty.io.SocketChannelEndPoint;
import org.eclipse.jetty.io.ssl.SslClientConnectionFactory;
import org.eclipse.jetty.util.Promise;
import org.eclipse.jetty.util.annotation.ManagedAttribute;
@@ -173,13 +175,15 @@ public abstract class AbstractHttpClientTransport extends ContainerLifeCycle imp
}
@Override
- protected EndPoint newEndPoint(SocketChannel channel, ManagedSelector selector, SelectionKey key)
+ protected EndPoint newEndPoint(SelectableChannel channel, ManagedSelector selector, SelectionKey key)
{
- return new SelectChannelEndPoint(channel, selector, key, getScheduler(), client.getIdleTimeout());
+ SocketChannelEndPoint endp = new SocketChannelEndPoint(channel, selector, key, getScheduler());
+ endp.setIdleTimeout(client.getIdleTimeout());
+ return endp;
}
@Override
- public org.eclipse.jetty.io.Connection newConnection(SocketChannel channel, EndPoint endPoint, Object attachment) throws IOException
+ public org.eclipse.jetty.io.Connection newConnection(SelectableChannel channel, EndPoint endPoint, Object attachment) throws IOException
{
@SuppressWarnings("unchecked")
Map<String, Object> context = (Map<String, Object>)attachment;
@@ -188,7 +192,7 @@ public abstract class AbstractHttpClientTransport extends ContainerLifeCycle imp
}
@Override
- protected void connectionFailed(SocketChannel channel, Throwable x, Object attachment)
+ protected void connectionFailed(SelectableChannel channel, Throwable x, Object attachment)
{
@SuppressWarnings("unchecked")
Map<String, Object> context = (Map<String, Object>)attachment;
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/AuthenticationProtocolHandler.java b/jetty-client/src/main/java/org/eclipse/jetty/client/AuthenticationProtocolHandler.java
index 9e7f684620..d25903e58a 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/AuthenticationProtocolHandler.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/AuthenticationProtocolHandler.java
@@ -37,7 +37,7 @@ import org.eclipse.jetty.util.log.Logger;
public abstract class AuthenticationProtocolHandler implements ProtocolHandler
{
- public static final int DEFAULT_MAX_CONTENT_LENGTH = 4096;
+ public static final int DEFAULT_MAX_CONTENT_LENGTH = 16*1024;
public static final Logger LOG = Log.getLogger(AuthenticationProtocolHandler.class);
private static final Pattern AUTHENTICATE_PATTERN = Pattern.compile("([^\\s]+)\\s+realm=\"([^\"]+)\"(.*)", Pattern.CASE_INSENSITIVE);
private static final String AUTHENTICATION_ATTRIBUTE = AuthenticationProtocolHandler.class.getName() + ".authentication";
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/ConnectionPool.java b/jetty-client/src/main/java/org/eclipse/jetty/client/ConnectionPool.java
index ecf45697e1..029a388d33 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/ConnectionPool.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/ConnectionPool.java
@@ -19,424 +19,23 @@
package org.eclipse.jetty.client;
import java.io.Closeable;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Deque;
-import java.util.List;
-import java.util.Queue;
-import java.util.concurrent.LinkedBlockingDeque;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.locks.ReentrantLock;
import org.eclipse.jetty.client.api.Connection;
-import org.eclipse.jetty.client.api.Destination;
-import org.eclipse.jetty.util.BlockingArrayQueue;
-import org.eclipse.jetty.util.Callback;
-import org.eclipse.jetty.util.Promise;
-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;
-import org.eclipse.jetty.util.thread.Sweeper;
-@ManagedObject("The connection pool")
-public class ConnectionPool implements Closeable, Dumpable, Sweeper.Sweepable
+public interface ConnectionPool extends Closeable
{
- private static final Logger LOG = Log.getLogger(ConnectionPool.class);
+ boolean isActive(Connection connection);
- private final AtomicInteger connectionCount = new AtomicInteger();
- private final ReentrantLock lock = new ReentrantLock();
- private final Destination destination;
- private final int maxConnections;
- private final Callback requester;
- private final Deque<Connection> idleConnections;
- private final Queue<Connection> activeConnections;
+ boolean isEmpty();
- public ConnectionPool(Destination destination, int maxConnections, Callback requester)
- {
- this.destination = destination;
- this.maxConnections = maxConnections;
- this.requester = requester;
- this.idleConnections = new LinkedBlockingDeque<>(maxConnections);
- this.activeConnections = new BlockingArrayQueue<>(maxConnections);
- }
+ boolean isClosed();
- @ManagedAttribute(value = "The number of connections", readonly = true)
- public int getConnectionCount()
- {
- return connectionCount.get();
- }
+ Connection acquire();
- @ManagedAttribute(value = "The number of idle connections", readonly = true)
- public int getIdleConnectionCount()
- {
- return idleConnections.size();
- }
+ boolean release(Connection connection);
- @ManagedAttribute(value = "The number of active connections", readonly = true)
- public int getActiveConnectionCount()
- {
- return activeConnections.size();
- }
-
- public Queue<Connection> getIdleConnections()
- {
- return idleConnections;
- }
-
- public Queue<Connection> getActiveConnections()
- {
- return activeConnections;
- }
-
- public Connection acquire()
- {
- Connection connection = activateIdle();
- if (connection == null)
- connection = tryCreate();
- return connection;
- }
-
- private Connection tryCreate()
- {
- while (true)
- {
- int current = getConnectionCount();
- final int next = current + 1;
-
- if (next > maxConnections)
- {
- if (LOG.isDebugEnabled())
- LOG.debug("Max connections {}/{} reached", current, maxConnections);
- // Try again the idle connections
- return activateIdle();
- }
-
- if (connectionCount.compareAndSet(current, next))
- {
- if (LOG.isDebugEnabled())
- LOG.debug("Connection {}/{} creation", next, maxConnections);
-
- destination.newConnection(new Promise<Connection>()
- {
- @Override
- public void succeeded(Connection connection)
- {
- if (LOG.isDebugEnabled())
- LOG.debug("Connection {}/{} creation succeeded {}", next, maxConnections, connection);
-
- idleCreated(connection);
-
- proceed();
- }
-
- @Override
- public void failed(Throwable x)
- {
- if (LOG.isDebugEnabled())
- LOG.debug("Connection " + next + "/" + maxConnections + " creation failed", x);
-
- connectionCount.decrementAndGet();
-
- requester.failed(x);
- }
- });
-
- // Try again the idle connections
- return activateIdle();
- }
- }
- }
-
- protected void proceed()
- {
- requester.succeeded();
- }
-
- protected void idleCreated(Connection connection)
- {
- boolean idle;
- lock();
- try
- {
- // Use "cold" new connections as last.
- idle = idleConnections.offerLast(connection);
- }
- finally
- {
- unlock();
- }
-
- idle(connection, idle);
- }
-
- private Connection activateIdle()
- {
- boolean acquired;
- Connection connection;
- lock();
- try
- {
- connection = idleConnections.pollFirst();
- if (connection == null)
- return null;
- acquired = activeConnections.offer(connection);
- }
- finally
- {
- unlock();
- }
-
- if (acquired)
- {
- if (LOG.isDebugEnabled())
- LOG.debug("Connection active {}", connection);
- acquired(connection);
- return connection;
- }
- else
- {
- if (LOG.isDebugEnabled())
- LOG.debug("Connection active overflow {}", connection);
- connection.close();
- return null;
- }
- }
-
- protected void acquired(Connection connection)
- {
- }
-
- public boolean release(Connection connection)
- {
- boolean idle;
- lock();
- try
- {
- if (!activeConnections.remove(connection))
- return false;
- // Make sure we use "hot" connections first.
- idle = offerIdle(connection);
- }
- finally
- {
- unlock();
- }
-
- released(connection);
- return idle(connection, idle);
- }
-
- protected boolean offerIdle(Connection connection)
- {
- return idleConnections.offerFirst(connection);
- }
-
- protected boolean idle(Connection connection, boolean idle)
- {
- if (idle)
- {
- if (LOG.isDebugEnabled())
- LOG.debug("Connection idle {}", connection);
- return true;
- }
- else
- {
- if (LOG.isDebugEnabled())
- LOG.debug("Connection idle overflow {}", connection);
- connection.close();
- return false;
- }
- }
-
- protected void released(Connection connection)
- {
- }
-
- public boolean remove(Connection connection)
- {
- return remove(connection, false);
- }
-
- protected boolean remove(Connection connection, boolean force)
- {
- boolean activeRemoved;
- boolean idleRemoved;
- lock();
- try
- {
- activeRemoved = activeConnections.remove(connection);
- idleRemoved = idleConnections.remove(connection);
- }
- finally
- {
- unlock();
- }
-
- if (activeRemoved)
- released(connection);
- boolean removed = activeRemoved || idleRemoved || force;
- if (removed)
- {
- int pooled = connectionCount.decrementAndGet();
- if (LOG.isDebugEnabled())
- LOG.debug("Connection removed {} - pooled: {}", connection, pooled);
- }
- return removed;
- }
-
- public boolean isActive(Connection connection)
- {
- lock();
- try
- {
- return activeConnections.contains(connection);
- }
- finally
- {
- unlock();
- }
- }
-
- public boolean isIdle(Connection connection)
- {
- lock();
- try
- {
- return idleConnections.contains(connection);
- }
- finally
- {
- unlock();
- }
- }
-
- public boolean isEmpty()
- {
- return connectionCount.get() == 0;
- }
-
- public void close()
- {
- List<Connection> idles = new ArrayList<>();
- List<Connection> actives = new ArrayList<>();
- lock();
- try
- {
- idles.addAll(idleConnections);
- idleConnections.clear();
- actives.addAll(activeConnections);
- activeConnections.clear();
- }
- finally
- {
- unlock();
- }
-
- connectionCount.set(0);
-
- for (Connection connection : idles)
- connection.close();
-
- // A bit drastic, but we cannot wait for all requests to complete
- for (Connection connection : actives)
- connection.close();
- }
+ boolean remove(Connection connection);
@Override
- public String dump()
- {
- return ContainerLifeCycle.dump(this);
- }
-
- @Override
- public void dump(Appendable out, String indent) throws IOException
- {
- List<Connection> actives = new ArrayList<>();
- List<Connection> idles = new ArrayList<>();
- lock();
- try
- {
- actives.addAll(activeConnections);
- idles.addAll(idleConnections);
- }
- finally
- {
- unlock();
- }
-
- ContainerLifeCycle.dumpObject(out, this);
- ContainerLifeCycle.dump(out, indent, actives, idles);
- }
-
- @Override
- public boolean sweep()
- {
- List<Sweeper.Sweepable> toSweep = new ArrayList<>();
- lock();
- try
- {
- for (Connection connection : getActiveConnections())
- {
- if (connection instanceof Sweeper.Sweepable)
- toSweep.add(((Sweeper.Sweepable)connection));
- }
- }
- finally
- {
- unlock();
- }
-
- for (Sweeper.Sweepable candidate : toSweep)
- {
- if (candidate.sweep())
- {
- boolean removed = getActiveConnections().remove(candidate);
- LOG.warn("Connection swept: {}{}{} from active connections{}{}",
- candidate,
- System.lineSeparator(),
- removed ? "Removed" : "Not removed",
- System.lineSeparator(),
- dump());
- }
- }
-
- return false;
- }
-
- protected void lock()
- {
- lock.lock();
- }
-
- protected void unlock()
- {
- lock.unlock();
- }
-
- @Override
- public String toString()
- {
- int activeSize;
- int idleSize;
- lock();
- try
- {
- activeSize = activeConnections.size();
- idleSize = idleConnections.size();
- }
- finally
- {
- unlock();
- }
-
- return String.format("%s[c=%d/%d,a=%d,i=%d]",
- getClass().getSimpleName(),
- connectionCount.get(),
- maxConnections,
- activeSize,
- idleSize);
- }
+ void close();
}
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/DuplexConnectionPool.java b/jetty-client/src/main/java/org/eclipse/jetty/client/DuplexConnectionPool.java
new file mode 100644
index 0000000000..c22966c372
--- /dev/null
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/DuplexConnectionPool.java
@@ -0,0 +1,313 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2015 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.client;
+
+import java.io.IOException;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Deque;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Queue;
+import java.util.Set;
+import java.util.concurrent.locks.ReentrantLock;
+
+import org.eclipse.jetty.client.api.Connection;
+import org.eclipse.jetty.client.api.Destination;
+import org.eclipse.jetty.util.Callback;
+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;
+import org.eclipse.jetty.util.thread.Sweeper;
+
+@ManagedObject("The connection pool")
+public class DuplexConnectionPool extends AbstractConnectionPool implements Dumpable, Sweeper.Sweepable
+{
+ private static final Logger LOG = Log.getLogger(DuplexConnectionPool.class);
+
+ private final ReentrantLock lock = new ReentrantLock();
+ private final Deque<Connection> idleConnections;
+ private final Set<Connection> activeConnections;
+
+ public DuplexConnectionPool(Destination destination, int maxConnections, Callback requester)
+ {
+ super(destination, maxConnections, requester);
+ this.idleConnections = new ArrayDeque<>(maxConnections);
+ this.activeConnections = new HashSet<>(maxConnections);
+ }
+
+ protected void lock()
+ {
+ lock.lock();
+ }
+
+ protected void unlock()
+ {
+ lock.unlock();
+ }
+
+ @ManagedAttribute(value = "The number of idle connections", readonly = true)
+ public int getIdleConnectionCount()
+ {
+ lock();
+ try
+ {
+ return idleConnections.size();
+ }
+ finally
+ {
+ unlock();
+ }
+ }
+
+ @ManagedAttribute(value = "The number of active connections", readonly = true)
+ public int getActiveConnectionCount()
+ {
+ lock();
+ try
+ {
+ return activeConnections.size();
+ }
+ finally
+ {
+ unlock();
+ }
+ }
+
+ public Queue<Connection> getIdleConnections()
+ {
+ return idleConnections;
+ }
+
+ public Collection<Connection> getActiveConnections()
+ {
+ return activeConnections;
+ }
+
+ @Override
+ public boolean isActive(Connection connection)
+ {
+ lock();
+ try
+ {
+ return activeConnections.contains(connection);
+ }
+ finally
+ {
+ unlock();
+ }
+ }
+
+ @Override
+ protected void onCreated(Connection connection)
+ {
+ lock();
+ try
+ {
+ // Use "cold" new connections as last.
+ idleConnections.offer(connection);
+ }
+ finally
+ {
+ unlock();
+ }
+
+ idle(connection, false);
+ }
+
+ @Override
+ protected Connection activate()
+ {
+ Connection connection;
+ lock();
+ try
+ {
+ connection = idleConnections.poll();
+ if (connection == null)
+ return null;
+ activeConnections.add(connection);
+ }
+ finally
+ {
+ unlock();
+ }
+
+ return active(connection);
+ }
+
+ public boolean release(Connection connection)
+ {
+ boolean closed = isClosed();
+ lock();
+ try
+ {
+ if (!activeConnections.remove(connection))
+ return false;
+
+ if (!closed)
+ {
+ // Make sure we use "hot" connections first.
+ deactivate(connection);
+ }
+ }
+ finally
+ {
+ unlock();
+ }
+
+ released(connection);
+ return idle(connection, closed);
+ }
+
+ protected boolean deactivate(Connection connection)
+ {
+ return idleConnections.offerFirst(connection);
+ }
+
+ public boolean remove(Connection connection)
+ {
+ return remove(connection, false);
+ }
+
+ protected boolean remove(Connection connection, boolean force)
+ {
+ boolean activeRemoved;
+ boolean idleRemoved;
+ lock();
+ try
+ {
+ activeRemoved = activeConnections.remove(connection);
+ idleRemoved = idleConnections.remove(connection);
+ }
+ finally
+ {
+ unlock();
+ }
+
+ if (activeRemoved || force)
+ released(connection);
+ boolean removed = activeRemoved || idleRemoved || force;
+ if (removed)
+ removed(connection);
+ return removed;
+ }
+
+ public void close()
+ {
+ super.close();
+
+ List<Connection> connections = new ArrayList<>();
+ lock();
+ try
+ {
+ connections.addAll(idleConnections);
+ idleConnections.clear();
+ connections.addAll(activeConnections);
+ activeConnections.clear();
+ }
+ finally
+ {
+ unlock();
+ }
+
+ close(connections);
+ }
+
+ @Override
+ public void dump(Appendable out, String indent) throws IOException
+ {
+ List<Connection> connections = new ArrayList<>();
+ lock();
+ try
+ {
+ connections.addAll(activeConnections);
+ connections.addAll(idleConnections);
+ }
+ finally
+ {
+ unlock();
+ }
+
+ ContainerLifeCycle.dumpObject(out, this);
+ ContainerLifeCycle.dump(out, indent, connections);
+ }
+
+ @Override
+ public boolean sweep()
+ {
+ List<Connection> toSweep = new ArrayList<>();
+ lock();
+ try
+ {
+ for (Connection connection : activeConnections)
+ {
+ if (connection instanceof Sweeper.Sweepable)
+ toSweep.add(connection);
+ }
+ }
+ finally
+ {
+ unlock();
+ }
+
+ for (Connection connection : toSweep)
+ {
+ if (((Sweeper.Sweepable)connection).sweep())
+ {
+ boolean removed = remove(connection, true);
+ LOG.warn("Connection swept: {}{}{} from active connections{}{}",
+ connection,
+ System.lineSeparator(),
+ removed ? "Removed" : "Not removed",
+ System.lineSeparator(),
+ dump());
+ }
+ }
+
+ return false;
+ }
+
+ @Override
+ public String toString()
+ {
+ int activeSize;
+ int idleSize;
+ lock();
+ try
+ {
+ activeSize = activeConnections.size();
+ idleSize = idleConnections.size();
+ }
+ finally
+ {
+ unlock();
+ }
+
+ return String.format("%s[c=%d/%d,a=%d,i=%d]",
+ getClass().getSimpleName(),
+ getConnectionCount(),
+ getMaxConnectionCount(),
+ activeSize,
+ idleSize);
+ }
+}
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpChannel.java b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpChannel.java
index d493a9e200..09dd2fb3ad 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpChannel.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpChannel.java
@@ -129,6 +129,11 @@ public abstract class HttpChannel
return getHttpReceiver().abort(exchange, failure);
}
+ public Result exchangeTerminating(HttpExchange exchange, Result result)
+ {
+ return result;
+ }
+
public void exchangeTerminated(HttpExchange exchange, Result result)
{
disassociate(exchange);
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClient.java b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClient.java
index 93337516af..cbf15b6fff 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClient.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClient.java
@@ -18,7 +18,6 @@
package org.eclipse.jetty.client;
-import java.io.IOException;
import java.net.CookieManager;
import java.net.CookiePolicy;
import java.net.CookieStore;
@@ -498,24 +497,18 @@ public class HttpClient extends ContainerLifeCycle
if (destination == null)
{
destination = transport.newHttpDestination(origin);
- if (isRunning())
+ addManaged(destination);
+ HttpDestination existing = destinations.putIfAbsent(origin, destination);
+ if (existing != null)
{
- HttpDestination existing = destinations.putIfAbsent(origin, destination);
- if (existing != null)
- {
- destination = existing;
- }
- else
- {
- addManaged(destination);
- if (LOG.isDebugEnabled())
- LOG.debug("Created {}", destination);
- }
-
- if (!isRunning())
- removeDestination(destination);
+ removeBean(destination);
+ destination = existing;
+ }
+ else
+ {
+ if (LOG.isDebugEnabled())
+ LOG.debug("Created {}", destination);
}
-
}
return destination;
}
@@ -531,15 +524,12 @@ public class HttpClient extends ContainerLifeCycle
*/
public List<Destination> getDestinations()
{
- return new ArrayList<Destination>(destinations.values());
+ return new ArrayList<>(destinations.values());
}
protected void send(final HttpRequest request, List<Response.ResponseListener> listeners)
{
String scheme = request.getScheme().toLowerCase(Locale.ENGLISH);
- if (!HttpScheme.HTTP.is(scheme) && !HttpScheme.HTTPS.is(scheme))
- throw new IllegalArgumentException("Invalid protocol " + scheme);
-
String host = request.getHost().toLowerCase(Locale.ENGLISH);
HttpDestination destination = destinationFor(scheme, host, request.getPort());
destination.send(request, listeners);
@@ -994,7 +984,7 @@ public class HttpClient extends ContainerLifeCycle
* anymore and leave space for new destinations.
*
* @param removeIdleDestinations whether destinations that have no connections should be removed
- * @see org.eclipse.jetty.client.ConnectionPool
+ * @see org.eclipse.jetty.client.DuplexConnectionPool
*/
public void setRemoveIdleDestinations(boolean removeIdleDestinations)
{
@@ -1047,19 +1037,25 @@ public class HttpClient extends ContainerLifeCycle
protected int normalizePort(String scheme, int port)
{
- return port > 0 ? port : HttpScheme.HTTPS.is(scheme) ? 443 : 80;
+ if (port > 0)
+ return port;
+ else if (isSchemeSecure(scheme))
+ return 443;
+ else
+ return 80;
}
public boolean isDefaultPort(String scheme, int port)
{
- return HttpScheme.HTTPS.is(scheme) ? port == 443 : port == 80;
+ if (isSchemeSecure(scheme))
+ return port == 443;
+ else
+ return port == 80;
}
- @Override
- public void dump(Appendable out, String indent) throws IOException
+ public boolean isSchemeSecure(String scheme)
{
- dumpThis(out);
- dump(out, indent, getBeans(), destinations.values());
+ return HttpScheme.HTTPS.is(scheme) || HttpScheme.WSS.is(scheme);
}
private class ContentDecoderFactorySet implements Set<ContentDecoder.Factory>
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpContent.java b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpContent.java
index 58b16bb4ad..1d01f0637c 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpContent.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpContent.java
@@ -67,11 +67,13 @@ public class HttpContent implements Callback, Closeable
{
private static final Logger LOG = Log.getLogger(HttpContent.class);
private static final ByteBuffer AFTER = ByteBuffer.allocate(0);
+ private static final ByteBuffer CLOSE = ByteBuffer.allocate(0);
private final ContentProvider provider;
private final Iterator<ByteBuffer> iterator;
private ByteBuffer buffer;
- private volatile ByteBuffer content;
+ private ByteBuffer content;
+ private boolean last;
public HttpContent(ContentProvider provider)
{
@@ -92,7 +94,7 @@ public class HttpContent implements Callback, Closeable
*/
public boolean isLast()
{
- return !iterator.hasNext();
+ return last;
}
/**
@@ -124,41 +126,50 @@ public class HttpContent implements Callback, Closeable
*/
public boolean advance()
{
- boolean advanced;
- boolean hasNext;
- ByteBuffer bytes;
if (iterator instanceof Synchronizable)
{
synchronized (((Synchronizable)iterator).getLock())
{
- advanced = iterator.hasNext();
- bytes = advanced ? iterator.next() : null;
- hasNext = advanced && iterator.hasNext();
+ return advance(iterator);
}
}
else
{
- advanced = iterator.hasNext();
- bytes = advanced ? iterator.next() : null;
- hasNext = advanced && iterator.hasNext();
+ return advance(iterator);
}
+ }
+
+ private boolean advance(Iterator<ByteBuffer> iterator)
+ {
+ boolean hasNext = iterator.hasNext();
+ ByteBuffer bytes = hasNext ? iterator.next() : null;
+ boolean hasMore = hasNext && iterator.hasNext();
+ boolean wasLast = last;
+ last = !hasMore;
- if (advanced)
+ if (hasNext)
{
buffer = bytes;
content = bytes == null ? null : bytes.slice();
if (LOG.isDebugEnabled())
- LOG.debug("Advanced content to {} chunk {}", hasNext ? "next" : "last", bytes);
+ LOG.debug("Advanced content to {} chunk {}", hasMore ? "next" : "last", String.valueOf(bytes));
return bytes != null;
}
else
{
- if (content != AFTER)
+ // No more content, but distinguish between last and consumed.
+ if (wasLast)
{
- content = buffer = AFTER;
+ buffer = content = AFTER;
if (LOG.isDebugEnabled())
LOG.debug("Advanced content past last chunk");
}
+ else
+ {
+ buffer = content = CLOSE;
+ if (LOG.isDebugEnabled())
+ LOG.debug("Advanced content to last chunk");
+ }
return false;
}
}
@@ -168,7 +179,7 @@ public class HttpContent implements Callback, Closeable
*/
public boolean isConsumed()
{
- return content == AFTER;
+ return buffer == AFTER;
}
@Override
@@ -176,6 +187,8 @@ public class HttpContent implements Callback, Closeable
{
if (isConsumed())
return;
+ if (buffer == CLOSE)
+ return;
if (iterator instanceof Callback)
((Callback)iterator).succeeded();
}
@@ -185,6 +198,8 @@ public class HttpContent implements Callback, Closeable
{
if (isConsumed())
return;
+ if (buffer == CLOSE)
+ return;
if (iterator instanceof Callback)
((Callback)iterator).failed(x);
}
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpDestination.java b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpDestination.java
index 9031562db2..58eaee5ef5 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpDestination.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpDestination.java
@@ -22,19 +22,21 @@ import java.io.Closeable;
import java.io.IOException;
import java.nio.channels.AsynchronousCloseException;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.RejectedExecutionException;
import org.eclipse.jetty.client.api.Connection;
import org.eclipse.jetty.client.api.Destination;
+import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.client.api.Response;
import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.http.HttpHeader;
-import org.eclipse.jetty.http.HttpScheme;
import org.eclipse.jetty.io.ClientConnectionFactory;
import org.eclipse.jetty.io.ssl.SslClientConnectionFactory;
import org.eclipse.jetty.util.BlockingArrayQueue;
+import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.Promise;
import org.eclipse.jetty.util.annotation.ManagedAttribute;
import org.eclipse.jetty.util.annotation.ManagedObject;
@@ -42,9 +44,10 @@ 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;
+import org.eclipse.jetty.util.thread.Sweeper;
@ManagedObject
-public abstract class HttpDestination extends ContainerLifeCycle implements Destination, Closeable, Dumpable
+public abstract class HttpDestination extends ContainerLifeCycle implements Destination, Closeable, Callback, Dumpable
{
protected static final Logger LOG = Log.getLogger(HttpDestination.class);
@@ -56,6 +59,7 @@ public abstract class HttpDestination extends ContainerLifeCycle implements Dest
private final ProxyConfiguration.Proxy proxy;
private final ClientConnectionFactory connectionFactory;
private final HttpField hostField;
+ private ConnectionPool connectionPool;
public HttpDestination(HttpClient client, Origin origin)
{
@@ -76,7 +80,7 @@ public abstract class HttpDestination extends ContainerLifeCycle implements Dest
}
else
{
- if (HttpScheme.HTTPS.is(getScheme()))
+ if (isSecure())
connectionFactory = newSslClientConnectionFactory(connectionFactory);
}
this.connectionFactory = connectionFactory;
@@ -87,6 +91,29 @@ public abstract class HttpDestination extends ContainerLifeCycle implements Dest
hostField = new HttpField(HttpHeader.HOST, host);
}
+ @Override
+ protected void doStart() throws Exception
+ {
+ this.connectionPool = newConnectionPool(client);
+ addBean(connectionPool);
+ super.doStart();
+ Sweeper sweeper = client.getBean(Sweeper.class);
+ if (sweeper != null && connectionPool instanceof Sweeper.Sweepable)
+ sweeper.offer((Sweeper.Sweepable)connectionPool);
+ }
+
+ @Override
+ protected void doStop() throws Exception
+ {
+ Sweeper sweeper = client.getBean(Sweeper.class);
+ if (sweeper != null && connectionPool instanceof Sweeper.Sweepable)
+ sweeper.remove((Sweeper.Sweepable)connectionPool);
+ super.doStop();
+ removeBean(connectionPool);
+ }
+
+ protected abstract ConnectionPool newConnectionPool(HttpClient client);
+
protected Queue<HttpExchange> newExchangeQueue(HttpClient client)
{
return new BlockingArrayQueue<>(client.getMaxRequestsQueuedPerDestination());
@@ -97,6 +124,11 @@ public abstract class HttpDestination extends ContainerLifeCycle implements Dest
return new SslClientConnectionFactory(client.getSslContextFactory(), client.getByteBufferPool(), client.getExecutor(), connectionFactory);
}
+ public boolean isSecure()
+ {
+ return client.isSchemeSecure(getScheme());
+ }
+
public HttpClient getHttpClient()
{
return client;
@@ -171,6 +203,24 @@ public abstract class HttpDestination extends ContainerLifeCycle implements Dest
return hostField;
}
+ @ManagedAttribute(value = "The connection pool", readonly = true)
+ public ConnectionPool getConnectionPool()
+ {
+ return connectionPool;
+ }
+
+ @Override
+ public void succeeded()
+ {
+ send();
+ }
+
+ @Override
+ public void failed(Throwable x)
+ {
+ abort(x);
+ }
+
protected void send(HttpRequest request, List<Response.ResponseListener> listeners)
{
if (!getScheme().equalsIgnoreCase(request.getScheme()))
@@ -217,7 +267,59 @@ public abstract class HttpDestination extends ContainerLifeCycle implements Dest
return queue.offer(exchange);
}
- public abstract void send();
+ public void send()
+ {
+ if (getHttpExchanges().isEmpty())
+ return;
+ process();
+ }
+
+ private void process()
+ {
+ Connection connection = connectionPool.acquire();
+ if (connection != null)
+ process(connection);
+ }
+
+ public void process(final Connection connection)
+ {
+ HttpClient client = getHttpClient();
+ final HttpExchange exchange = getHttpExchanges().poll();
+ if (LOG.isDebugEnabled())
+ LOG.debug("Processing exchange {} on {} of {}", exchange, connection, this);
+ if (exchange == null)
+ {
+ if (!connectionPool.release(connection))
+ connection.close();
+
+ if (!client.isRunning())
+ {
+ if (LOG.isDebugEnabled())
+ LOG.debug("{} is stopping", client);
+ connection.close();
+ }
+ }
+ else
+ {
+ final Request request = exchange.getRequest();
+ Throwable cause = request.getAbortCause();
+ if (cause != null)
+ {
+ if (LOG.isDebugEnabled())
+ LOG.debug("Aborted before processing {}: {}", exchange, cause);
+ // It may happen that the request is aborted before the exchange
+ // is created. Aborting the exchange a second time will result in
+ // a no-operation, so we just abort here to cover that edge case.
+ exchange.abort(cause);
+ }
+ else
+ {
+ send(connection, exchange);
+ }
+ }
+ }
+
+ protected abstract void send(Connection connection, HttpExchange exchange);
public void newConnection(Promise<Connection> promise)
{
@@ -239,14 +341,67 @@ public abstract class HttpDestination extends ContainerLifeCycle implements Dest
abort(new AsynchronousCloseException());
if (LOG.isDebugEnabled())
LOG.debug("Closed {}", this);
+ connectionPool.close();
}
public void release(Connection connection)
{
+ if (LOG.isDebugEnabled())
+ LOG.debug("Released {}", connection);
+ HttpClient client = getHttpClient();
+ if (client.isRunning())
+ {
+ if (connectionPool.isActive(connection))
+ {
+ if (connectionPool.release(connection))
+ send();
+ else
+ connection.close();
+ }
+ else
+ {
+ if (LOG.isDebugEnabled())
+ LOG.debug("Released explicit {}", connection);
+ }
+ }
+ else
+ {
+ if (LOG.isDebugEnabled())
+ LOG.debug("{} is stopped", client);
+ connection.close();
+ }
+ }
+
+ public boolean remove(Connection connection)
+ {
+ return connectionPool.remove(connection);
}
public void close(Connection connection)
{
+ boolean removed = remove(connection);
+
+ if (getHttpExchanges().isEmpty())
+ {
+ if (getHttpClient().isRemoveIdleDestinations() && connectionPool.isEmpty())
+ {
+ // There is a race condition between this thread removing the destination
+ // and another thread queueing a request to this same destination.
+ // If this destination is removed, but the request queued, a new connection
+ // will be opened, the exchange will be executed and eventually the connection
+ // will idle timeout and be closed. Meanwhile a new destination will be created
+ // in HttpClient and will be used for other requests.
+ getHttpClient().removeDestination(this);
+ }
+ }
+ else
+ {
+ // We need to execute queued requests even if this connection failed.
+ // We may create a connection that is not needed, but it will eventually
+ // idle timeout, so no worries.
+ if (removed)
+ process();
+ }
}
/**
@@ -274,6 +429,7 @@ public abstract class HttpDestination extends ContainerLifeCycle implements Dest
public void dump(Appendable out, String indent) throws IOException
{
ContainerLifeCycle.dumpObject(out, toString());
+ ContainerLifeCycle.dump(out, indent, Collections.singletonList(connectionPool));
}
public String asString()
@@ -284,11 +440,12 @@ public abstract class HttpDestination extends ContainerLifeCycle implements Dest
@Override
public String toString()
{
- return String.format("%s[%s]%x%s,queue=%d",
+ return String.format("%s[%s]%x%s,queue=%d,pool=%s",
HttpDestination.class.getSimpleName(),
asString(),
hashCode(),
proxy == null ? "" : "(via " + proxy + ")",
- exchanges.size());
+ exchanges.size(),
+ connectionPool);
}
}
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpProxy.java b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpProxy.java
index c46bc6cd35..2a92a5d42a 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpProxy.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpProxy.java
@@ -26,7 +26,6 @@ import java.util.concurrent.TimeUnit;
import org.eclipse.jetty.client.api.Connection;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.client.api.Response;
-import org.eclipse.jetty.client.api.Result;
import org.eclipse.jetty.client.http.HttpConnectionOverHTTP;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpMethod;
@@ -108,7 +107,7 @@ public class HttpProxy extends ProxyConfiguration.Proxy
public void succeeded(Connection connection)
{
HttpDestination destination = (HttpDestination)context.get(HttpClientTransport.HTTP_DESTINATION_CONTEXT_KEY);
- if (HttpScheme.HTTPS.is(destination.getScheme()))
+ if (destination.isSecure())
{
SslContextFactory sslContextFactory = destination.getHttpClient().getSslContextFactory();
if (sslContextFactory != null)
@@ -119,7 +118,7 @@ public class HttpProxy extends ProxyConfiguration.Proxy
{
String message = String.format("Cannot perform requests over SSL, no %s in %s",
SslContextFactory.class.getSimpleName(), HttpClient.class.getSimpleName());
- promise.failed(new IllegalStateException(message));
+ tunnelFailed(new IllegalStateException(message));
}
}
else
@@ -131,7 +130,7 @@ public class HttpProxy extends ProxyConfiguration.Proxy
@Override
public void failed(Throwable x)
{
- promise.failed(x);
+ tunnelFailed(x);
}
private void tunnel(HttpDestination destination, final Connection connection)
@@ -139,33 +138,31 @@ public class HttpProxy extends ProxyConfiguration.Proxy
String target = destination.getOrigin().getAddress().asString();
Origin.Address proxyAddress = destination.getConnectAddress();
HttpClient httpClient = destination.getHttpClient();
+ long connectTimeout = httpClient.getConnectTimeout();
Request connect = httpClient.newRequest(proxyAddress.getHost(), proxyAddress.getPort())
.scheme(HttpScheme.HTTP.asString())
.method(HttpMethod.CONNECT)
.path(target)
.header(HttpHeader.HOST, target)
- .timeout(httpClient.getConnectTimeout(), TimeUnit.MILLISECONDS);
+ .idleTimeout(2 * connectTimeout, TimeUnit.MILLISECONDS)
+ .timeout(connectTimeout, TimeUnit.MILLISECONDS);
- connection.send(connect, new Response.CompleteListener()
+ connection.send(connect, result ->
{
- @Override
- public void onComplete(Result result)
+ if (result.isFailed())
{
- if (result.isFailed())
+ tunnelFailed(result.getFailure());
+ }
+ else
+ {
+ Response response = result.getResponse();
+ if (response.getStatus() == 200)
{
- tunnelFailed(result.getFailure());
+ tunnelSucceeded();
}
else
{
- Response response = result.getResponse();
- if (response.getStatus() == 200)
- {
- tunnelSucceeded();
- }
- else
- {
- tunnelFailed(new HttpResponseException("Received " + response + " for " + result.getRequest(), response));
- }
+ tunnelFailed(new HttpResponseException("Received " + response + " for " + result.getRequest(), response));
}
}
});
@@ -182,10 +179,7 @@ public class HttpProxy extends ProxyConfiguration.Proxy
ClientConnectionFactory sslConnectionFactory = new SslClientConnectionFactory(client.getSslContextFactory(), client.getByteBufferPool(), client.getExecutor(), connectionFactory);
HttpConnectionOverHTTP oldConnection = (HttpConnectionOverHTTP)endPoint.getConnection();
org.eclipse.jetty.io.Connection newConnection = sslConnectionFactory.newConnection(endPoint, context);
- Helper.replaceConnection(oldConnection, newConnection);
- // Avoid setting fill interest in the old Connection,
- // without closing the underlying EndPoint.
- oldConnection.softClose();
+ endPoint.upgrade(newConnection);
if (LOG.isDebugEnabled())
LOG.debug("HTTP tunnel established: {} over {}", oldConnection, newConnection);
}
@@ -198,7 +192,7 @@ public class HttpProxy extends ProxyConfiguration.Proxy
private void tunnelFailed(Throwable failure)
{
endPoint.close();
- failed(failure);
+ promise.failed(failure);
}
}
}
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpReceiver.java b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpReceiver.java
index 2fb444130c..04bdf62731 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpReceiver.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpReceiver.java
@@ -437,6 +437,7 @@ public abstract class HttpReceiver
if (result != null)
{
+ result = channel.exchangeTerminating(exchange, result);
boolean ordered = getHttpDestination().getHttpClient().isStrictEventOrdering();
if (!ordered)
channel.exchangeTerminated(exchange, result);
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpRequestException.java b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpRequestException.java
index 37fcb97574..da1c95ae36 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpRequestException.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpRequestException.java
@@ -20,7 +20,7 @@ package org.eclipse.jetty.client;
import org.eclipse.jetty.client.api.Request;
-public class HttpRequestException extends Throwable
+public class HttpRequestException extends RuntimeException
{
private final Request request;
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpSender.java b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpSender.java
index 650587e48c..25fd7d9487 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpSender.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpSender.java
@@ -376,6 +376,7 @@ public abstract class HttpSender implements AsyncContentProvider.Listener
}
else
{
+ result = channel.exchangeTerminating(exchange, result);
HttpDestination destination = getHttpChannel().getHttpDestination();
boolean ordered = destination.getHttpClient().isStrictEventOrdering();
if (!ordered)
@@ -678,7 +679,7 @@ public abstract class HttpSender implements AsyncContentProvider.Listener
{
return content.isNonBlocking();
}
-
+
@Override
public void succeeded()
{
@@ -811,9 +812,9 @@ public abstract class HttpSender implements AsyncContentProvider.Listener
while (true)
{
boolean advanced = content.advance();
- boolean consumed = content.isConsumed();
+ boolean lastContent = content.isLast();
if (LOG.isDebugEnabled())
- LOG.debug("Content {} consumed {} for {}", advanced, consumed, exchange.getRequest());
+ LOG.debug("Content present {}, last {}, consumed {} for {}", advanced, lastContent, content.isConsumed(), exchange.getRequest());
if (advanced)
{
@@ -821,7 +822,7 @@ public abstract class HttpSender implements AsyncContentProvider.Listener
return Action.SCHEDULED;
}
- if (consumed)
+ if (lastContent)
{
sendContent(exchange, content, lastCallback);
return Action.IDLE;
@@ -894,7 +895,7 @@ public abstract class HttpSender implements AsyncContentProvider.Listener
{
return content.isNonBlocking();
}
-
+
@Override
public void succeeded()
{
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/LeakTrackingConnectionPool.java b/jetty-client/src/main/java/org/eclipse/jetty/client/LeakTrackingConnectionPool.java
index 7762af09f9..f5d3b98580 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/LeakTrackingConnectionPool.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/LeakTrackingConnectionPool.java
@@ -25,7 +25,7 @@ import org.eclipse.jetty.util.LeakDetector;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
-public class LeakTrackingConnectionPool extends ConnectionPool
+public class LeakTrackingConnectionPool extends DuplexConnectionPool
{
private static final Logger LOG = Log.getLogger(LeakTrackingConnectionPool.class);
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/MultiplexConnectionPool.java b/jetty-client/src/main/java/org/eclipse/jetty/client/MultiplexConnectionPool.java
new file mode 100644
index 0000000000..88561bb75b
--- /dev/null
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/MultiplexConnectionPool.java
@@ -0,0 +1,302 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2015 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.client;
+
+import java.io.IOException;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Deque;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.locks.ReentrantLock;
+import java.util.stream.Collectors;
+
+import org.eclipse.jetty.client.api.Connection;
+import org.eclipse.jetty.util.Callback;
+import org.eclipse.jetty.util.component.ContainerLifeCycle;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+public class MultiplexConnectionPool extends AbstractConnectionPool
+{
+ private static final Logger LOG = Log.getLogger(MultiplexConnectionPool.class);
+
+ private final ReentrantLock lock = new ReentrantLock();
+ private final int maxMultiplexed;
+ private final Deque<Holder> idleConnections;
+ private final Map<Connection, Holder> muxedConnections;
+ private final Map<Connection, Holder> busyConnections;
+
+ public MultiplexConnectionPool(HttpDestination destination, int maxConnections, Callback requester, int maxMultiplexed)
+ {
+ super(destination, maxConnections, requester);
+ this.maxMultiplexed = maxMultiplexed;
+ this.idleConnections = new ArrayDeque<>(maxConnections);
+ this.muxedConnections = new HashMap<>(maxConnections);
+ this.busyConnections = new HashMap<>(maxConnections);
+ }
+
+ protected void lock()
+ {
+ lock.lock();
+ }
+
+ protected void unlock()
+ {
+ lock.unlock();
+ }
+
+ @Override
+ public boolean isActive(Connection connection)
+ {
+ lock();
+ try
+ {
+ if (muxedConnections.containsKey(connection))
+ return true;
+ if (busyConnections.containsKey(connection))
+ return true;
+ return false;
+ }
+ finally
+ {
+ unlock();
+ }
+ }
+
+ @Override
+ protected void onCreated(Connection connection)
+ {
+ lock();
+ try
+ {
+ // Use "cold" connections as last.
+ idleConnections.offer(new Holder(connection));
+ }
+ finally
+ {
+ unlock();
+ }
+
+ idle(connection, false);
+ }
+
+ @Override
+ protected Connection activate()
+ {
+ Holder holder;
+ lock();
+ try
+ {
+ while (true)
+ {
+ if (muxedConnections.isEmpty())
+ {
+ holder = idleConnections.poll();
+ if (holder == null)
+ return null;
+ muxedConnections.put(holder.connection, holder);
+ }
+ else
+ {
+ holder = muxedConnections.values().iterator().next();
+ }
+
+ if (holder.count < maxMultiplexed)
+ {
+ ++holder.count;
+ break;
+ }
+ else
+ {
+ muxedConnections.remove(holder.connection);
+ busyConnections.put(holder.connection, holder);
+ }
+ }
+ }
+ finally
+ {
+ unlock();
+ }
+
+ return active(holder.connection);
+ }
+
+ @Override
+ public boolean release(Connection connection)
+ {
+ boolean closed = isClosed();
+ boolean idle = false;
+ Holder holder;
+ lock();
+ try
+ {
+ holder = muxedConnections.get(connection);
+ if (holder != null)
+ {
+ int count = --holder.count;
+ if (count == 0)
+ {
+ muxedConnections.remove(connection);
+ if (!closed)
+ {
+ idleConnections.offerFirst(holder);
+ idle = true;
+ }
+ }
+ }
+ else
+ {
+ holder = busyConnections.remove(connection);
+ if (holder != null)
+ {
+ int count = --holder.count;
+ if (!closed)
+ {
+ if (count == 0)
+ {
+ idleConnections.offerFirst(holder);
+ idle = true;
+ }
+ else
+ {
+ muxedConnections.put(connection, holder);
+ }
+ }
+ }
+ }
+ }
+ finally
+ {
+ unlock();
+ }
+
+ if (holder == null)
+ return false;
+
+ released(connection);
+ if (idle || closed)
+ return idle(connection, closed);
+ return true;
+ }
+
+ @Override
+ public boolean remove(Connection connection)
+ {
+ return remove(connection, false);
+ }
+
+ protected boolean remove(Connection connection, boolean force)
+ {
+ boolean activeRemoved = true;
+ boolean idleRemoved = false;
+ lock();
+ try
+ {
+ Holder holder = muxedConnections.remove(connection);
+ if (holder == null)
+ holder = busyConnections.remove(connection);
+ if (holder == null)
+ {
+ activeRemoved = false;
+ for (Iterator<Holder> iterator = idleConnections.iterator(); iterator.hasNext();)
+ {
+ holder = iterator.next();
+ if (holder.connection == connection)
+ {
+ idleRemoved = true;
+ iterator.remove();
+ break;
+ }
+ }
+ }
+ }
+ finally
+ {
+ unlock();
+ }
+
+ if (activeRemoved || force)
+ released(connection);
+ boolean removed = activeRemoved || idleRemoved || force;
+ if (removed)
+ removed(connection);
+ return removed;
+ }
+
+ @Override
+ public void close()
+ {
+ super.close();
+
+ List<Connection> connections;
+ lock();
+ try
+ {
+ connections = idleConnections.stream().map(holder -> holder.connection).collect(Collectors.toList());
+ connections.addAll(muxedConnections.keySet());
+ connections.addAll(busyConnections.keySet());
+ }
+ finally
+ {
+ unlock();
+ }
+
+ close(connections);
+ }
+
+ @Override
+ public void dump(Appendable out, String indent) throws IOException
+ {
+ List<Holder> connections = new ArrayList<>();
+ lock();
+ try
+ {
+ connections.addAll(busyConnections.values());
+ connections.addAll(muxedConnections.values());
+ connections.addAll(idleConnections);
+ }
+ finally
+ {
+ unlock();
+ }
+
+ ContainerLifeCycle.dumpObject(out, this);
+ ContainerLifeCycle.dump(out, indent, connections);
+ }
+
+ private static class Holder
+ {
+ private final Connection connection;
+ private int count;
+
+ private Holder(Connection connection)
+ {
+ this.connection = connection;
+ }
+
+ @Override
+ public String toString()
+ {
+ return String.format("%s[%d]", connection, count);
+ }
+ }
+}
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/MultiplexHttpDestination.java b/jetty-client/src/main/java/org/eclipse/jetty/client/MultiplexHttpDestination.java
index a50131f1ed..a23fb34a82 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/MultiplexHttpDestination.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/MultiplexHttpDestination.java
@@ -18,136 +18,16 @@
package org.eclipse.jetty.client;
-import java.util.concurrent.atomic.AtomicReference;
-
-import org.eclipse.jetty.client.api.Connection;
-import org.eclipse.jetty.client.api.Request;
-import org.eclipse.jetty.util.Promise;
-
-public abstract class MultiplexHttpDestination<C extends Connection> extends HttpDestination implements Promise<Connection>
+public abstract class MultiplexHttpDestination extends HttpDestination
{
- private final AtomicReference<ConnectState> connect = new AtomicReference<>(ConnectState.DISCONNECTED);
- private C connection;
-
protected MultiplexHttpDestination(HttpClient client, Origin origin)
{
super(client, origin);
}
- @Override
- public void send()
- {
- while (true)
- {
- ConnectState current = connect.get();
- switch (current)
- {
- case DISCONNECTED:
- {
- if (!connect.compareAndSet(current, ConnectState.CONNECTING))
- break;
- newConnection(this);
- return;
- }
- case CONNECTING:
- {
- // Waiting to connect, just return
- return;
- }
- case CONNECTED:
- {
- if (process(connection))
- break;
- return;
- }
- default:
- {
- abort(new IllegalStateException("Invalid connection state " + current));
- return;
- }
- }
- }
- }
-
- @Override
- @SuppressWarnings("unchecked")
- public void succeeded(Connection result)
- {
- C connection = this.connection = (C)result;
- if (connect.compareAndSet(ConnectState.CONNECTING, ConnectState.CONNECTED))
- {
- process(connection);
- }
- else
- {
- connection.close();
- failed(new IllegalStateException());
- }
- }
-
- @Override
- public void failed(Throwable x)
- {
- connect.set(ConnectState.DISCONNECTED);
- abort(x);
- }
-
- protected boolean process(final C connection)
- {
- HttpClient client = getHttpClient();
- final HttpExchange exchange = getHttpExchanges().poll();
- if (LOG.isDebugEnabled())
- LOG.debug("Processing {} on {}", exchange, connection);
- if (exchange == null)
- return false;
-
- final Request request = exchange.getRequest();
- Throwable cause = request.getAbortCause();
- if (cause != null)
- {
- if (LOG.isDebugEnabled())
- LOG.debug("Aborted before processing {}: {}", exchange, cause);
- // It may happen that the request is aborted before the exchange
- // is created. Aborting the exchange a second time will result in
- // a no-operation, so we just abort here to cover that edge case.
- exchange.abort(cause);
- }
- else
- {
- send(connection, exchange);
- }
- return true;
- }
-
- @Override
- public void close()
- {
- super.close();
- C connection = this.connection;
- if (connection != null)
- connection.close();
- }
-
- @Override
- public void close(Connection connection)
- {
- super.close(connection);
- while (true)
- {
- ConnectState current = connect.get();
- if (connect.compareAndSet(current, ConnectState.DISCONNECTED))
- {
- if (getHttpClient().isRemoveIdleDestinations())
- getHttpClient().removeDestination(this);
- break;
- }
- }
- }
-
- protected abstract void send(C connection, HttpExchange exchange);
-
- private enum ConnectState
+ protected ConnectionPool newConnectionPool(HttpClient client)
{
- DISCONNECTED, CONNECTING, CONNECTED
+ return new MultiplexConnectionPool(this, client.getMaxConnectionsPerDestination(), this,
+ client.getMaxRequestsQueuedPerDestination());
}
}
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/PoolingHttpDestination.java b/jetty-client/src/main/java/org/eclipse/jetty/client/PoolingHttpDestination.java
index 3030a97658..b77805aeae 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/PoolingHttpDestination.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/PoolingHttpDestination.java
@@ -18,200 +18,15 @@
package org.eclipse.jetty.client;
-import java.io.IOException;
-import java.util.Collections;
-
-import org.eclipse.jetty.client.api.Connection;
-import org.eclipse.jetty.client.api.Request;
-import org.eclipse.jetty.util.Callback;
-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.thread.Sweeper;
-
-@ManagedObject
-public abstract class PoolingHttpDestination<C extends Connection> extends HttpDestination implements Callback
+public abstract class PoolingHttpDestination extends HttpDestination
{
- private final ConnectionPool connectionPool;
-
public PoolingHttpDestination(HttpClient client, Origin origin)
{
super(client, origin);
- this.connectionPool = newConnectionPool(client);
- addBean(connectionPool);
- Sweeper sweeper = client.getBean(Sweeper.class);
- if (sweeper != null)
- sweeper.offer(connectionPool);
}
protected ConnectionPool newConnectionPool(HttpClient client)
{
- return new ConnectionPool(this, client.getMaxConnectionsPerDestination(), this);
- }
-
- @ManagedAttribute(value = "The connection pool", readonly = true)
- public ConnectionPool getConnectionPool()
- {
- return connectionPool;
- }
-
- @Override
- public void succeeded()
- {
- send();
- }
-
- @Override
- public void failed(final Throwable x)
- {
- abort(x);
- }
-
- public void send()
- {
- if (getHttpExchanges().isEmpty())
- return;
- process();
- }
-
- @SuppressWarnings("unchecked")
- public C acquire()
- {
- return (C)connectionPool.acquire();
- }
-
- private void process()
- {
- C connection = acquire();
- if (connection != null)
- process(connection);
- }
-
- /**
- * <p>Processes a new connection making it idle or active depending on whether requests are waiting to be sent.</p>
- * <p>A new connection is created when a request needs to be executed; it is possible that the request that
- * triggered the request creation is executed by another connection that was just released, so the new connection
- * may become idle.</p>
- * <p>If a request is waiting to be executed, it will be dequeued and executed by the new connection.</p>
- *
- * @param connection the new connection
- */
- public void process(final C connection)
- {
- HttpClient client = getHttpClient();
- final HttpExchange exchange = getHttpExchanges().poll();
- if (LOG.isDebugEnabled())
- LOG.debug("Processing exchange {} on {} of {}", exchange, connection, this);
- if (exchange == null)
- {
- if (!connectionPool.release(connection))
- connection.close();
-
- if (!client.isRunning())
- {
- if (LOG.isDebugEnabled())
- LOG.debug("{} is stopping", client);
- connection.close();
- }
- }
- else
- {
- final Request request = exchange.getRequest();
- Throwable cause = request.getAbortCause();
- if (cause != null)
- {
- if (LOG.isDebugEnabled())
- LOG.debug("Aborted before processing {}: {}", exchange, cause);
- // It may happen that the request is aborted before the exchange
- // is created. Aborting the exchange a second time will result in
- // a no-operation, so we just abort here to cover that edge case.
- exchange.abort(cause);
- }
- else
- {
- send(connection, exchange);
- }
- }
- }
-
- protected abstract void send(C connection, HttpExchange exchange);
-
- @Override
- public void release(Connection c)
- {
- @SuppressWarnings("unchecked")
- C connection = (C)c;
- if (LOG.isDebugEnabled())
- LOG.debug("Released {}", connection);
- HttpClient client = getHttpClient();
- if (client.isRunning())
- {
- if (connectionPool.isActive(connection))
- {
- if (connectionPool.release(connection))
- send();
- else
- connection.close();
- }
- else
- {
- if (LOG.isDebugEnabled())
- LOG.debug("Released explicit {}", connection);
- }
- }
- else
- {
- if (LOG.isDebugEnabled())
- LOG.debug("{} is stopped", client);
- connection.close();
- }
- }
-
- @Override
- public void close(Connection oldConnection)
- {
- super.close(oldConnection);
-
- connectionPool.remove(oldConnection);
-
- if (getHttpExchanges().isEmpty())
- {
- if (getHttpClient().isRemoveIdleDestinations() && connectionPool.isEmpty())
- {
- // There is a race condition between this thread removing the destination
- // and another thread queueing a request to this same destination.
- // If this destination is removed, but the request queued, a new connection
- // will be opened, the exchange will be executed and eventually the connection
- // will idle timeout and be closed. Meanwhile a new destination will be created
- // in HttpClient and will be used for other requests.
- getHttpClient().removeDestination(this);
- }
- }
- else
- {
- // We need to execute queued requests even if this connection failed.
- // We may create a connection that is not needed, but it will eventually
- // idle timeout, so no worries.
- process();
- }
- }
-
- public void close()
- {
- super.close();
- connectionPool.close();
- }
-
- @Override
- public void dump(Appendable out, String indent) throws IOException
- {
- super.dump(out, indent);
- ContainerLifeCycle.dump(out, indent, Collections.singletonList(connectionPool));
- }
-
- @Override
- public String toString()
- {
- return String.format("%s,pool=%s", super.toString(), connectionPool);
+ return new DuplexConnectionPool(this, client.getMaxConnectionsPerDestination(), this);
}
}
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/ResponseNotifier.java b/jetty-client/src/main/java/org/eclipse/jetty/client/ResponseNotifier.java
index 7d6d48a2e5..5c0969d3a8 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/ResponseNotifier.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/ResponseNotifier.java
@@ -210,7 +210,7 @@ public class ResponseNotifier
notifyHeaders(listeners, response);
if (response instanceof ContentResponse)
// TODO: handle callback
- notifyContent(listeners, response, ByteBuffer.wrap(((ContentResponse)response).getContent()), new Callback.Adapter());
+ notifyContent(listeners, response, ByteBuffer.wrap(((ContentResponse)response).getContent()), Callback.NOOP);
notifySuccess(listeners, response);
}
@@ -232,7 +232,7 @@ public class ResponseNotifier
notifyHeaders(listeners, response);
if (response instanceof ContentResponse)
// TODO: handle callback
- notifyContent(listeners, response, ByteBuffer.wrap(((ContentResponse)response).getContent()), new Callback.Adapter());
+ notifyContent(listeners, response, ByteBuffer.wrap(((ContentResponse)response).getContent()), Callback.NOOP);
notifyFailure(listeners, response, failure);
}
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/Socks4Proxy.java b/jetty-client/src/main/java/org/eclipse/jetty/client/Socks4Proxy.java
index 456e444e95..246681e646 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/Socks4Proxy.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/Socks4Proxy.java
@@ -27,7 +27,6 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.eclipse.jetty.client.api.Connection;
-import org.eclipse.jetty.http.HttpScheme;
import org.eclipse.jetty.io.AbstractConnection;
import org.eclipse.jetty.io.ClientConnectionFactory;
import org.eclipse.jetty.io.EndPoint;
@@ -196,12 +195,12 @@ public class Socks4Proxy extends ProxyConfiguration.Proxy
HttpDestination destination = (HttpDestination)context.get(HttpClientTransport.HTTP_DESTINATION_CONTEXT_KEY);
HttpClient client = destination.getHttpClient();
ClientConnectionFactory connectionFactory = this.connectionFactory;
- if (HttpScheme.HTTPS.is(destination.getScheme()))
+ if (destination.isSecure())
connectionFactory = new SslClientConnectionFactory(client.getSslContextFactory(), client.getByteBufferPool(), client.getExecutor(), connectionFactory);
- org.eclipse.jetty.io.Connection connection = connectionFactory.newConnection(getEndPoint(), context);
- ClientConnectionFactory.Helper.replaceConnection(this, connection);
+ org.eclipse.jetty.io.Connection newConnection = connectionFactory.newConnection(getEndPoint(), context);
+ getEndPoint().upgrade(newConnection);
if (LOG.isDebugEnabled())
- LOG.debug("SOCKS4 tunnel established: {} over {}", this, connection);
+ LOG.debug("SOCKS4 tunnel established: {} over {}", this, newConnection);
}
catch (Throwable x)
{
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/ValidatingConnectionPool.java b/jetty-client/src/main/java/org/eclipse/jetty/client/ValidatingConnectionPool.java
index 9ddc97a4f0..a218d14e87 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/ValidatingConnectionPool.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/ValidatingConnectionPool.java
@@ -34,7 +34,7 @@ import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.thread.Scheduler;
/**
- * <p>A {@link ConnectionPool} that validates connections before
+ * <p>A connection pool that validates connections before
* making them available for use.</p>
* <p>Connections that have just been opened are not validated.
* Connections that are {@link #release(Connection) released} will
@@ -56,7 +56,7 @@ import org.eclipse.jetty.util.thread.Scheduler;
* tuning the idle timeout of the servers to be larger than
* that of the client.</p>
*/
-public class ValidatingConnectionPool extends ConnectionPool
+public class ValidatingConnectionPool extends DuplexConnectionPool
{
private static final Logger LOG = Log.getLogger(ValidatingConnectionPool.class);
@@ -154,7 +154,7 @@ public class ValidatingConnectionPool extends ConnectionPool
private class Holder implements Runnable
{
private final long timestamp = System.nanoTime();
- private final AtomicBoolean latch = new AtomicBoolean();
+ private final AtomicBoolean done = new AtomicBoolean();
private final Connection connection;
public Scheduler.Task task;
@@ -166,30 +166,31 @@ public class ValidatingConnectionPool extends ConnectionPool
@Override
public void run()
{
- if (latch.compareAndSet(false, true))
+ if (done.compareAndSet(false, true))
{
- boolean idle;
+ boolean closed = isClosed();
lock();
try
{
- quarantine.remove(connection);
- idle = offerIdle(connection);
if (LOG.isDebugEnabled())
LOG.debug("Validated {}", connection);
+ quarantine.remove(connection);
+ if (!closed)
+ deactivate(connection);
}
finally
{
unlock();
}
- if (idle(connection, idle))
- proceed();
+ idle(connection, closed);
+ proceed();
}
}
public boolean cancel()
{
- if (latch.compareAndSet(false, true))
+ if (done.compareAndSet(false, true))
{
task.cancel();
return true;
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/api/Result.java b/jetty-client/src/main/java/org/eclipse/jetty/client/api/Result.java
index a782bd8d88..f934ba5f0e 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/api/Result.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/api/Result.java
@@ -52,6 +52,14 @@ public class Result
this.responseFailure = responseFailure;
}
+ public Result(Result result, Throwable responseFailure)
+ {
+ this.request = result.request;
+ this.requestFailure = result.requestFailure;
+ this.response = result.response;
+ this.responseFailure = responseFailure;
+ }
+
/**
* @return the request object
*/
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpChannelOverHTTP.java b/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpChannelOverHTTP.java
index a204cf3a2e..0ca65b109c 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpChannelOverHTTP.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpChannelOverHTTP.java
@@ -18,16 +18,20 @@
package org.eclipse.jetty.client.http;
+import java.util.Locale;
+
import org.eclipse.jetty.client.HttpChannel;
import org.eclipse.jetty.client.HttpExchange;
-import org.eclipse.jetty.client.HttpReceiver;
-import org.eclipse.jetty.client.HttpSender;
+import org.eclipse.jetty.client.HttpRequest;
+import org.eclipse.jetty.client.HttpResponse;
+import org.eclipse.jetty.client.HttpResponseException;
import org.eclipse.jetty.client.api.Response;
import org.eclipse.jetty.client.api.Result;
import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpHeaderValue;
import org.eclipse.jetty.http.HttpMethod;
+import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.http.HttpVersion;
public class HttpChannelOverHTTP extends HttpChannel
@@ -55,13 +59,13 @@ public class HttpChannelOverHTTP extends HttpChannel
}
@Override
- protected HttpSender getHttpSender()
+ protected HttpSenderOverHTTP getHttpSender()
{
return sender;
}
@Override
- protected HttpReceiver getHttpReceiver()
+ protected HttpReceiverOverHTTP getHttpReceiver()
{
return receiver;
}
@@ -85,6 +89,42 @@ public class HttpChannelOverHTTP extends HttpChannel
connection.release();
}
+ @Override
+ public Result exchangeTerminating(HttpExchange exchange, Result result)
+ {
+ if (result.isFailed())
+ return result;
+
+ HttpResponse response = exchange.getResponse();
+
+ if ((response.getVersion() == HttpVersion.HTTP_1_1) &&
+ (response.getStatus() == HttpStatus.SWITCHING_PROTOCOLS_101))
+ {
+ String connection = response.getHeaders().get(HttpHeader.CONNECTION);
+ if ((connection == null) || !connection.toLowerCase(Locale.US).contains("upgrade"))
+ {
+ return new Result(result,new HttpResponseException("101 Switching Protocols without Connection: Upgrade not supported",response));
+ }
+
+ // Upgrade Response
+ HttpRequest request = exchange.getRequest();
+ if (request instanceof HttpConnectionUpgrader)
+ {
+ HttpConnectionUpgrader listener = (HttpConnectionUpgrader)request;
+ try
+ {
+ listener.upgrade(response,getHttpConnection());
+ }
+ catch (Throwable x)
+ {
+ return new Result(result,x);
+ }
+ }
+ }
+
+ return result;
+ }
+
public void receive()
{
receiver.receive();
@@ -131,7 +171,10 @@ public class HttpChannelOverHTTP extends HttpChannel
}
else
{
- release();
+ if (response.getStatus() == HttpStatus.SWITCHING_PROTOCOLS_101)
+ connection.remove();
+ else
+ release();
}
}
@@ -143,4 +186,5 @@ public class HttpChannelOverHTTP extends HttpChannel
sender,
receiver);
}
+
}
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpConnectionOverHTTP.java b/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpConnectionOverHTTP.java
index da9fcd6e08..5c60de2c66 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpConnectionOverHTTP.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpConnectionOverHTTP.java
@@ -18,6 +18,7 @@
package org.eclipse.jetty.client.http;
+import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousCloseException;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -36,13 +37,13 @@ import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.thread.Sweeper;
-public class HttpConnectionOverHTTP extends AbstractConnection implements Connection, Sweeper.Sweepable
+public class HttpConnectionOverHTTP extends AbstractConnection implements Connection, org.eclipse.jetty.io.Connection.UpgradeFrom, Sweeper.Sweepable
{
private static final Logger LOG = Log.getLogger(HttpConnectionOverHTTP.class);
private final AtomicBoolean closed = new AtomicBoolean();
- private final Promise<Connection> promise;
private final AtomicInteger sweeps = new AtomicInteger();
+ private final Promise<Connection> promise;
private final Delegate delegate;
private final HttpChannelOverHTTP channel;
private long idleTimeout;
@@ -119,6 +120,13 @@ public class HttpConnectionOverHTTP extends AbstractConnection implements Connec
}
}
+ @Override
+ public ByteBuffer onUpgradeFrom()
+ {
+ HttpReceiverOverHTTP receiver = channel.getHttpReceiver();
+ return receiver.onUpgradeFrom();
+ }
+
public void release()
{
// Restore idle timeout
@@ -171,6 +179,11 @@ public class HttpConnectionOverHTTP extends AbstractConnection implements Connec
return true;
}
+ public void remove()
+ {
+ getHttpDestination().remove(this);
+ }
+
@Override
public String toString()
{
diff --git a/jetty-start/src/main/java/org/eclipse/jetty/start/graph/Predicate.java b/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpConnectionUpgrader.java
index b995932391..c2c43749be 100644
--- a/jetty-start/src/main/java/org/eclipse/jetty/start/graph/Predicate.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpConnectionUpgrader.java
@@ -16,12 +16,11 @@
// ========================================================================
//
-package org.eclipse.jetty.start.graph;
+package org.eclipse.jetty.client.http;
-/**
- * Matcher of Nodes
- */
-public interface Predicate
+import org.eclipse.jetty.client.HttpResponse;
+
+public interface HttpConnectionUpgrader
{
- public boolean match(Node<?> input);
+ public void upgrade(HttpResponse response, HttpConnectionOverHTTP connection);
}
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpDestinationOverHTTP.java b/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpDestinationOverHTTP.java
index 304ba96d35..284ce08fe5 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpDestinationOverHTTP.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpDestinationOverHTTP.java
@@ -22,8 +22,9 @@ import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.HttpExchange;
import org.eclipse.jetty.client.Origin;
import org.eclipse.jetty.client.PoolingHttpDestination;
+import org.eclipse.jetty.client.api.Connection;
-public class HttpDestinationOverHTTP extends PoolingHttpDestination<HttpConnectionOverHTTP>
+public class HttpDestinationOverHTTP extends PoolingHttpDestination
{
public HttpDestinationOverHTTP(HttpClient client, Origin origin)
{
@@ -31,8 +32,8 @@ public class HttpDestinationOverHTTP extends PoolingHttpDestination<HttpConnecti
}
@Override
- protected void send(HttpConnectionOverHTTP connection, HttpExchange exchange)
+ protected void send(Connection connection, HttpExchange exchange)
{
- connection.send(exchange);
+ ((HttpConnectionOverHTTP)connection).send(exchange);
}
}
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpReceiverOverHTTP.java b/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpReceiverOverHTTP.java
index b9b2709a92..bf6c039d46 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpReceiverOverHTTP.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpReceiverOverHTTP.java
@@ -88,6 +88,17 @@ public class HttpReceiverOverHTTP extends HttpReceiver implements HttpParser.Res
buffer = null;
}
+ protected ByteBuffer onUpgradeFrom()
+ {
+ if (BufferUtil.hasContent(buffer))
+ {
+ ByteBuffer upgradeBuffer = ByteBuffer.allocate(buffer.remaining());
+ upgradeBuffer.put(buffer);
+ return upgradeBuffer;
+ }
+ return null;
+ }
+
private void process()
{
try
@@ -96,11 +107,13 @@ public class HttpReceiverOverHTTP extends HttpReceiver implements HttpParser.Res
EndPoint endPoint = connection.getEndPoint();
while (true)
{
- // Connection may be closed in a parser callback.
- if (connection.isClosed())
+ boolean upgraded = connection != endPoint.getConnection();
+
+ // Connection may be closed or upgraded in a parser callback.
+ if (connection.isClosed() || upgraded)
{
if (LOG.isDebugEnabled())
- LOG.debug("{} closed", connection);
+ LOG.debug("{} {}", connection, upgraded ? "upgraded" : "closed");
releaseBuffer();
return;
}
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpSenderOverHTTP.java b/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpSenderOverHTTP.java
index 887315105b..80e762fb18 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpSenderOverHTTP.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpSenderOverHTTP.java
@@ -33,6 +33,7 @@ import org.eclipse.jetty.http.MetaData;
import org.eclipse.jetty.io.ByteBufferPool;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.util.Callback;
+import org.eclipse.jetty.util.IteratingCallback;
public class HttpSenderOverHTTP extends HttpSender
{
@@ -52,77 +53,9 @@ public class HttpSenderOverHTTP extends HttpSender
@Override
protected void sendHeaders(HttpExchange exchange, HttpContent content, Callback callback)
{
- Request request = exchange.getRequest();
- ContentProvider requestContent = request.getContent();
- long contentLength = requestContent == null ? -1 : requestContent.getLength();
- String path = request.getPath();
- String query = request.getQuery();
- if (query != null)
- path += "?" + query;
- MetaData.Request requestInfo = new MetaData.Request(request.getMethod(), new HttpURI(path), request.getVersion(), request.getHeaders(), contentLength);
-
try
{
- HttpClient client = getHttpChannel().getHttpDestination().getHttpClient();
- ByteBufferPool bufferPool = client.getByteBufferPool();
- ByteBuffer header = bufferPool.acquire(client.getRequestBufferSize(), false);
- ByteBuffer chunk = null;
-
- ByteBuffer contentBuffer = null;
- boolean lastContent = false;
- if (!expects100Continue(request))
- {
- content.advance();
- contentBuffer = content.getByteBuffer();
- lastContent = content.isLast();
- }
- while (true)
- {
- HttpGenerator.Result result = generator.generateRequest(requestInfo, header, chunk, contentBuffer, lastContent);
- switch (result)
- {
- case NEED_CHUNK:
- {
- chunk = bufferPool.acquire(HttpGenerator.CHUNK_SIZE, false);
- break;
- }
- case FLUSH:
- {
- int size = 1;
- boolean hasChunk = chunk != null;
- if (hasChunk)
- ++size;
- boolean hasContent = contentBuffer != null;
- if (hasContent)
- ++size;
- ByteBuffer[] toWrite = new ByteBuffer[size];
- ByteBuffer[] toRecycle = new ByteBuffer[hasChunk ? 2 : 1];
- toWrite[0] = header;
- toRecycle[0] = header;
- if (hasChunk)
- {
- toWrite[1] = chunk;
- toRecycle[1] = chunk;
- }
- if (hasContent)
- toWrite[toWrite.length - 1] = contentBuffer;
- EndPoint endPoint = getHttpChannel().getHttpConnection().getEndPoint();
- endPoint.write(new ByteBufferRecyclerCallback(callback, bufferPool, toRecycle), toWrite);
- return;
- }
- case DONE:
- {
- // The headers have already been generated, perhaps by a concurrent abort.
- callback.failed(new HttpRequestException("Could not generate headers", request));
- return;
- }
- default:
- {
- callback.failed(new IllegalStateException(result.toString()));
- return;
- }
- }
- }
+ new HeadersCallback(exchange, content, callback).iterate();
}
catch (Throwable x)
{
@@ -145,6 +78,10 @@ public class HttpSenderOverHTTP extends HttpSender
ByteBuffer contentBuffer = content.getByteBuffer();
boolean lastContent = content.isLast();
HttpGenerator.Result result = generator.generateRequest(null, null, chunk, contentBuffer, lastContent);
+ if (LOG.isDebugEnabled())
+ LOG.debug("Generated content ({} bytes) - {}/{}",
+ contentBuffer == null ? -1 : contentBuffer.remaining(),
+ result, generator);
switch (result)
{
case NEED_CHUNK:
@@ -168,17 +105,19 @@ public class HttpSenderOverHTTP extends HttpSender
}
case CONTINUE:
{
- break;
+ if (lastContent)
+ break;
+ callback.succeeded();
+ return;
}
case DONE:
{
- assert generator.isEnd();
callback.succeeded();
return;
}
default:
{
- throw new IllegalStateException();
+ throw new IllegalStateException(result.toString());
}
}
}
@@ -208,6 +147,8 @@ public class HttpSenderOverHTTP extends HttpSender
private void shutdownOutput()
{
+ if (LOG.isDebugEnabled())
+ LOG.debug("Request shutdown output {}", getHttpExchange().getRequest());
getHttpChannel().getHttpConnection().getEndPoint().shutdownOutput();
}
@@ -217,6 +158,148 @@ public class HttpSenderOverHTTP extends HttpSender
return String.format("%s[%s]", super.toString(), generator);
}
+ private class HeadersCallback extends IteratingCallback
+ {
+ private final HttpExchange exchange;
+ private final Callback callback;
+ private final MetaData.Request metaData;
+ private ByteBuffer headerBuffer;
+ private ByteBuffer chunkBuffer;
+ private ByteBuffer contentBuffer;
+ private boolean lastContent;
+ private boolean generated;
+
+ public HeadersCallback(HttpExchange exchange, HttpContent content, Callback callback)
+ {
+ super(false);
+ this.exchange = exchange;
+ this.callback = callback;
+
+ Request request = exchange.getRequest();
+ ContentProvider requestContent = request.getContent();
+ long contentLength = requestContent == null ? -1 : requestContent.getLength();
+ String path = request.getPath();
+ String query = request.getQuery();
+ if (query != null)
+ path += "?" + query;
+ metaData = new MetaData.Request(request.getMethod(), new HttpURI(path), request.getVersion(), request.getHeaders(), contentLength);
+
+ if (!expects100Continue(request))
+ {
+ content.advance();
+ contentBuffer = content.getByteBuffer();
+ lastContent = content.isLast();
+ }
+ }
+
+ @Override
+ protected Action process() throws Exception
+ {
+ HttpClient client = getHttpChannel().getHttpDestination().getHttpClient();
+ ByteBufferPool bufferPool = client.getByteBufferPool();
+
+ while (true)
+ {
+ HttpGenerator.Result result = generator.generateRequest(metaData, headerBuffer, chunkBuffer, contentBuffer, lastContent);
+ if (LOG.isDebugEnabled())
+ LOG.debug("Generated headers ({} bytes), chunk ({} bytes), content ({} bytes) - {}/{}",
+ headerBuffer == null ? -1 : headerBuffer.remaining(),
+ chunkBuffer == null ? -1 : chunkBuffer.remaining(),
+ contentBuffer == null ? -1 : contentBuffer.remaining(),
+ result, generator);
+ switch (result)
+ {
+ case NEED_HEADER:
+ {
+ headerBuffer = bufferPool.acquire(client.getRequestBufferSize(), false);
+ break;
+ }
+ case NEED_CHUNK:
+ {
+ chunkBuffer = bufferPool.acquire(HttpGenerator.CHUNK_SIZE, false);
+ break;
+ }
+ case FLUSH:
+ {
+ EndPoint endPoint = getHttpChannel().getHttpConnection().getEndPoint();
+ if (chunkBuffer == null)
+ {
+ if (contentBuffer == null)
+ endPoint.write(this, headerBuffer);
+ else
+ endPoint.write(this, headerBuffer, contentBuffer);
+ }
+ else
+ {
+ if (contentBuffer == null)
+ endPoint.write(this, headerBuffer, chunkBuffer);
+ else
+ endPoint.write(this, headerBuffer, chunkBuffer, contentBuffer);
+ }
+ generated = true;
+ return Action.SCHEDULED;
+ }
+ case SHUTDOWN_OUT:
+ {
+ shutdownOutput();
+ return Action.SUCCEEDED;
+ }
+ case CONTINUE:
+ {
+ if (generated)
+ return Action.SUCCEEDED;
+ break;
+ }
+ case DONE:
+ {
+ if (generated)
+ return Action.SUCCEEDED;
+ // The headers have already been generated by some
+ // other thread, perhaps by a concurrent abort().
+ throw new HttpRequestException("Could not generate headers", exchange.getRequest());
+ }
+ default:
+ {
+ throw new IllegalStateException(result.toString());
+ }
+ }
+ }
+ }
+
+ @Override
+ public void succeeded()
+ {
+ release();
+ super.succeeded();
+ }
+
+ @Override
+ public void failed(Throwable x)
+ {
+ release();
+ callback.failed(x);
+ super.failed(x);
+ }
+
+ @Override
+ protected void onCompleteSuccess()
+ {
+ super.onCompleteSuccess();
+ callback.succeeded();
+ }
+
+ private void release()
+ {
+ HttpClient client = getHttpChannel().getHttpDestination().getHttpClient();
+ ByteBufferPool bufferPool = client.getByteBufferPool();
+ bufferPool.release(headerBuffer);
+ headerBuffer = null;
+ if (chunkBuffer != null)
+ bufferPool.release(chunkBuffer);
+ chunkBuffer = null;
+ }
+ }
+
private class ByteBufferRecyclerCallback implements Callback
{
private final Callback callback;
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/util/MultiPartContentProvider.java b/jetty-client/src/main/java/org/eclipse/jetty/client/util/MultiPartContentProvider.java
new file mode 100644
index 0000000000..917f51a0fc
--- /dev/null
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/util/MultiPartContentProvider.java
@@ -0,0 +1,404 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2015 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.client.util;
+
+import java.io.ByteArrayOutputStream;
+import java.io.Closeable;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.NoSuchElementException;
+import java.util.Random;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.eclipse.jetty.client.AsyncContentProvider;
+import org.eclipse.jetty.client.Synchronizable;
+import org.eclipse.jetty.client.api.ContentProvider;
+import org.eclipse.jetty.http.HttpField;
+import org.eclipse.jetty.http.HttpFields;
+import org.eclipse.jetty.http.HttpHeader;
+import org.eclipse.jetty.io.RuntimeIOException;
+import org.eclipse.jetty.util.Callback;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+/**
+ * <p>A {@link ContentProvider} for form uploads with the {@code "multipart/form-data"}
+ * content type.</p>
+ * <p>Example usage:</p>
+ * <pre>
+ * MultiPartContentProvider multiPart = new MultiPartContentProvider();
+ * multiPart.addFieldPart("field", new StringContentProvider("foo"), null);
+ * multiPart.addFilePart("icon", "img.png", new PathContentProvider(Paths.get("/tmp/img.png")), null);
+ * multiPart.close();
+ * ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
+ * .method(HttpMethod.POST)
+ * .content(multiPart)
+ * .send();
+ * </pre>
+ * <p>The above example would be the equivalent of submitting this form:</p>
+ * <pre>
+ * &lt;form method="POST" enctype="multipart/form-data" accept-charset="UTF-8"&gt;
+ * &lt;input type="text" name="field" value="foo" /&gt;
+ * &lt;input type="file" name="icon" /&gt;
+ * &lt;/form&gt;
+ * </pre>
+ */
+public class MultiPartContentProvider extends AbstractTypedContentProvider implements AsyncContentProvider, Closeable
+{
+ private static final Logger LOG = Log.getLogger(MultiPartContentProvider.class);
+ private static final byte[] COLON_SPACE_BYTES = new byte[]{':', ' '};
+ private static final byte[] CR_LF_BYTES = new byte[]{'\r', '\n'};
+
+ private final List<Part> parts = new ArrayList<>();
+ private final ByteBuffer firstBoundary;
+ private final ByteBuffer middleBoundary;
+ private final ByteBuffer onlyBoundary;
+ private final ByteBuffer lastBoundary;
+ private final AtomicBoolean closed = new AtomicBoolean();
+ private Listener listener;
+ private long length = -1;
+
+ public MultiPartContentProvider()
+ {
+ this(makeBoundary());
+ }
+
+ public MultiPartContentProvider(String boundary)
+ {
+ super("multipart/form-data; boundary=" + boundary);
+ String firstBoundaryLine = "--" + boundary + "\r\n";
+ this.firstBoundary = ByteBuffer.wrap(firstBoundaryLine.getBytes(StandardCharsets.US_ASCII));
+ String middleBoundaryLine = "\r\n" + firstBoundaryLine;
+ this.middleBoundary = ByteBuffer.wrap(middleBoundaryLine.getBytes(StandardCharsets.US_ASCII));
+ String onlyBoundaryLine = "--" + boundary + "--\r\n";
+ this.onlyBoundary = ByteBuffer.wrap(onlyBoundaryLine.getBytes(StandardCharsets.US_ASCII));
+ String lastBoundaryLine = "\r\n" + onlyBoundaryLine;
+ this.lastBoundary = ByteBuffer.wrap(lastBoundaryLine.getBytes(StandardCharsets.US_ASCII));
+ }
+
+ private static String makeBoundary()
+ {
+ Random random = new Random();
+ StringBuilder builder = new StringBuilder("JettyHttpClientBoundary");
+ int length = builder.length();
+ while (builder.length() < length + 16)
+ {
+ long rnd = random.nextLong();
+ builder.append(Long.toString(rnd < 0 ? -rnd : rnd, 36));
+ }
+ builder.setLength(length + 16);
+ return builder.toString();
+ }
+
+ /**
+ * <p>Adds a field part with the given {@code name} as field name, and the given
+ * {@code content} as part content.</p>
+ * <p>The {@code Content-Type} of this part will be obtained from:</p>
+ * <ul>
+ * <li>the {@code Content-Type} header in the {@code fields} parameter; otherwise</li>
+ * <li>the {@link org.eclipse.jetty.client.api.ContentProvider.Typed#getContentType()} method if the {@code content} parameter
+ * implements {@link org.eclipse.jetty.client.api.ContentProvider.Typed}; otherwise</li>
+ * <li>"text/plain"</li>
+ * </ul>
+ *
+ * @param name the part name
+ * @param content the part content
+ * @param fields the headers associated with this part
+ */
+ public void addFieldPart(String name, ContentProvider content, HttpFields fields)
+ {
+ addPart(new Part(name, null, "text/plain", content, fields));
+ }
+
+ /**
+ * <p>Adds a file part with the given {@code name} as field name, the given
+ * {@code fileName} as file name, and the given {@code content} as part content.</p>
+ * <p>The {@code Content-Type} of this part will be obtained from:</p>
+ * <ul>
+ * <li>the {@code Content-Type} header in the {@code fields} parameter; otherwise</li>
+ * <li>the {@link org.eclipse.jetty.client.api.ContentProvider.Typed#getContentType()} method if the {@code content} parameter
+ * implements {@link org.eclipse.jetty.client.api.ContentProvider.Typed}; otherwise</li>
+ * <li>"application/octet-stream"</li>
+ * </ul>
+ *
+ * @param name the part name
+ * @param fileName the file name associated to this part
+ * @param content the part content
+ * @param fields the headers associated with this part
+ */
+ public void addFilePart(String name, String fileName, ContentProvider content, HttpFields fields)
+ {
+ addPart(new Part(name, fileName, "application/octet-stream", content, fields));
+ }
+
+ private void addPart(Part part)
+ {
+ parts.add(part);
+ if (LOG.isDebugEnabled())
+ LOG.debug("Added {}", part);
+ }
+
+ @Override
+ public void setListener(Listener listener)
+ {
+ this.listener = listener;
+ if (closed.get())
+ this.length = calculateLength();
+ }
+
+ private long calculateLength()
+ {
+ // Compute the length, if possible.
+ if (parts.isEmpty())
+ {
+ return onlyBoundary.remaining();
+ }
+ else
+ {
+ long result = 0;
+ for (int i = 0; i < parts.size(); ++i)
+ {
+ result += (i == 0) ? firstBoundary.remaining() : middleBoundary.remaining();
+ Part part = parts.get(i);
+ long partLength = part.length;
+ result += partLength;
+ if (partLength < 0)
+ {
+ result = -1;
+ break;
+ }
+ }
+ if (result > 0)
+ result += lastBoundary.remaining();
+ return result;
+ }
+ }
+
+ @Override
+ public long getLength()
+ {
+ return length;
+ }
+
+ @Override
+ public Iterator<ByteBuffer> iterator()
+ {
+ return new MultiPartIterator();
+ }
+
+ @Override
+ public void close()
+ {
+ closed.compareAndSet(false, true);
+ }
+
+ private static class Part
+ {
+ private final String name;
+ private final String fileName;
+ private final String contentType;
+ private final ContentProvider content;
+ private final HttpFields fields;
+ private final ByteBuffer headers;
+ private final long length;
+
+ private Part(String name, String fileName, String contentType, ContentProvider content, HttpFields fields)
+ {
+ this.name = name;
+ this.fileName = fileName;
+ this.contentType = contentType;
+ this.content = content;
+ this.fields = fields;
+ this.headers = headers();
+ this.length = content.getLength() < 0 ? -1 : headers.remaining() + content.getLength();
+ }
+
+ private ByteBuffer headers()
+ {
+ try
+ {
+ // Compute the Content-Disposition.
+ String contentDisposition = "Content-Disposition: form-data; name=\"" + name + "\"";
+ if (fileName != null)
+ contentDisposition += "; filename=\"" + fileName + "\"";
+ contentDisposition += "\r\n";
+
+ // Compute the Content-Type.
+ String contentType = fields == null ? null : fields.get(HttpHeader.CONTENT_TYPE);
+ if (contentType == null)
+ {
+ if (content instanceof Typed)
+ contentType = ((Typed)content).getContentType();
+ else
+ contentType = this.contentType;
+ }
+ contentType = "Content-Type: " + contentType + "\r\n";
+
+ if (fields == null || fields.size() == 0)
+ {
+ String headers = contentDisposition;
+ headers += contentType;
+ headers += "\r\n";
+ return ByteBuffer.wrap(headers.getBytes(StandardCharsets.UTF_8));
+ }
+
+ ByteArrayOutputStream buffer = new ByteArrayOutputStream((fields.size() + 1) * contentDisposition.length());
+ buffer.write(contentDisposition.getBytes(StandardCharsets.UTF_8));
+ buffer.write(contentType.getBytes(StandardCharsets.UTF_8));
+ for (HttpField field : fields)
+ {
+ if (HttpHeader.CONTENT_TYPE.equals(field.getHeader()))
+ continue;
+ buffer.write(field.getName().getBytes(StandardCharsets.US_ASCII));
+ buffer.write(COLON_SPACE_BYTES);
+ buffer.write(field.getValue().getBytes(StandardCharsets.UTF_8));
+ buffer.write(CR_LF_BYTES);
+ }
+ buffer.write(CR_LF_BYTES);
+ return ByteBuffer.wrap(buffer.toByteArray());
+ }
+ catch (IOException x)
+ {
+ throw new RuntimeIOException(x);
+ }
+ }
+
+ @Override
+ public String toString()
+ {
+ return String.format("%s@%x[name=%s,fileName=%s,length=%d,headers=%s]",
+ getClass().getSimpleName(),
+ hashCode(),
+ name,
+ fileName,
+ content.getLength(),
+ fields);
+ }
+ }
+
+ private class MultiPartIterator implements Iterator<ByteBuffer>, Synchronizable, Callback, Closeable
+ {
+ private Iterator<ByteBuffer> iterator;
+ private int index;
+ private State state = State.FIRST_BOUNDARY;
+
+ @Override
+ public boolean hasNext()
+ {
+ return state != State.COMPLETE;
+ }
+
+ @Override
+ public ByteBuffer next()
+ {
+ while (true)
+ {
+ switch (state)
+ {
+ case FIRST_BOUNDARY:
+ {
+ if (parts.isEmpty())
+ {
+ state = State.COMPLETE;
+ return onlyBoundary.slice();
+ }
+ else
+ {
+ state = State.HEADERS;
+ return firstBoundary.slice();
+ }
+ }
+ case HEADERS:
+ {
+ Part part = parts.get(index);
+ ContentProvider content = part.content;
+ if (content instanceof AsyncContentProvider)
+ ((AsyncContentProvider)content).setListener(listener);
+ iterator = content.iterator();
+ state = State.CONTENT;
+ return part.headers.slice();
+ }
+ case CONTENT:
+ {
+ if (iterator.hasNext())
+ return iterator.next();
+ ++index;
+ if (index == parts.size())
+ state = State.LAST_BOUNDARY;
+ else
+ state = State.MIDDLE_BOUNDARY;
+ break;
+ }
+ case MIDDLE_BOUNDARY:
+ {
+ state = State.HEADERS;
+ return middleBoundary.slice();
+ }
+ case LAST_BOUNDARY:
+ {
+ state = State.COMPLETE;
+ return lastBoundary.slice();
+ }
+ case COMPLETE:
+ {
+ throw new NoSuchElementException();
+ }
+ }
+ }
+ }
+
+ @Override
+ public Object getLock()
+ {
+ if (iterator instanceof Synchronizable)
+ return ((Synchronizable)iterator).getLock();
+ return this;
+ }
+
+ @Override
+ public void succeeded()
+ {
+ if (iterator instanceof Callback)
+ ((Callback)iterator).succeeded();
+ }
+
+ @Override
+ public void failed(Throwable x)
+ {
+ if (iterator instanceof Callback)
+ ((Callback)iterator).failed(x);
+ }
+
+ @Override
+ public void close() throws IOException
+ {
+ if (iterator instanceof Closeable)
+ ((Closeable)iterator).close();
+ }
+ }
+
+ private enum State
+ {
+ FIRST_BOUNDARY, HEADERS, CONTENT, MIDDLE_BOUNDARY, LAST_BOUNDARY, COMPLETE
+ }
+}
diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/AbstractHttpClientServerTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/AbstractHttpClientServerTest.java
index 034dfdaee6..9cea0fd213 100644
--- a/jetty-client/src/test/java/org/eclipse/jetty/client/AbstractHttpClientServerTest.java
+++ b/jetty-client/src/test/java/org/eclipse/jetty/client/AbstractHttpClientServerTest.java
@@ -24,7 +24,6 @@ import java.util.Collection;
import org.eclipse.jetty.client.http.HttpClientTransportOverHTTP;
import org.eclipse.jetty.http.HttpScheme;
import org.eclipse.jetty.server.Handler;
-import org.eclipse.jetty.server.NetworkConnector;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.toolchain.test.TestTracker;
@@ -51,7 +50,7 @@ public abstract class AbstractHttpClientServerTest
protected String scheme;
protected Server server;
protected HttpClient client;
- protected NetworkConnector connector;
+ protected ServerConnector connector;
public AbstractHttpClientServerTest(SslContextFactory sslContextFactory)
{
@@ -61,6 +60,12 @@ public abstract class AbstractHttpClientServerTest
public void start(Handler handler) throws Exception
{
+ startServer(handler);
+ startClient();
+ }
+
+ protected void startServer(Handler handler) throws Exception
+ {
if (sslContextFactory != null)
{
sslContextFactory.setEndpointIdentificationAlgorithm("");
@@ -80,8 +85,6 @@ public abstract class AbstractHttpClientServerTest
server.addConnector(connector);
server.setHandler(handler);
server.start();
-
- startClient();
}
protected void startClient() throws Exception
diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/ClientConnectionCloseTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/ClientConnectionCloseTest.java
new file mode 100644
index 0000000000..4564c5e22f
--- /dev/null
+++ b/jetty-client/src/test/java/org/eclipse/jetty/client/ClientConnectionCloseTest.java
@@ -0,0 +1,122 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2015 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.client;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.util.concurrent.atomic.AtomicReference;
+
+import javax.servlet.ServletException;
+import javax.servlet.ServletInputStream;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.client.api.ContentProvider;
+import org.eclipse.jetty.client.api.ContentResponse;
+import org.eclipse.jetty.client.util.DeferredContentProvider;
+import org.eclipse.jetty.client.util.StringContentProvider;
+import org.eclipse.jetty.http.HttpHeader;
+import org.eclipse.jetty.http.HttpHeaderValue;
+import org.eclipse.jetty.http.HttpStatus;
+import org.eclipse.jetty.io.EndPoint;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.handler.AbstractHandler;
+import org.eclipse.jetty.util.BufferUtil;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class ClientConnectionCloseTest extends AbstractHttpClientServerTest
+{
+ public ClientConnectionCloseTest(SslContextFactory sslContextFactory)
+ {
+ super(sslContextFactory);
+ }
+
+ @Test
+ public void testClientConnectionCloseShutdownOutputWithoutRequestContent() throws Exception
+ {
+ testClientConnectionCloseShutdownOutput(null);
+ }
+
+ @Test
+ public void testClientConnectionCloseShutdownOutputWithRequestContent() throws Exception
+ {
+ testClientConnectionCloseShutdownOutput(new StringContentProvider("data", StandardCharsets.UTF_8));
+ }
+
+ @Test
+ public void testClientConnectionCloseShutdownOutputWithChunkedRequestContent() throws Exception
+ {
+ DeferredContentProvider content = new DeferredContentProvider()
+ {
+ @Override
+ public long getLength()
+ {
+ return -1;
+ }
+ };
+ content.offer(ByteBuffer.wrap("data".getBytes(StandardCharsets.UTF_8)));
+ content.close();
+ testClientConnectionCloseShutdownOutput(content);
+ }
+
+ private void testClientConnectionCloseShutdownOutput(ContentProvider content) throws Exception
+ {
+ AtomicReference<EndPoint> ref = new AtomicReference<>();
+ start(new AbstractHandler()
+ {
+ @Override
+ public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+ {
+ baseRequest.setHandled(true);
+ ref.set(baseRequest.getHttpChannel().getEndPoint());
+ ServletInputStream input = request.getInputStream();
+ while (true)
+ {
+ int read = input.read();
+ if (read < 0)
+ break;
+ }
+ response.setStatus(HttpStatus.OK_200);
+ }
+ });
+
+ ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
+ .scheme(scheme)
+ .path("/ctx/path")
+ .header(HttpHeader.CONNECTION, HttpHeaderValue.CLOSE.asString())
+ .content(content)
+ .send();
+
+ Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
+
+ // Wait for the FIN to arrive to the server
+ Thread.sleep(1000);
+
+ // Do not read from the server because it will trigger
+ // the send of the TLS Close Message before the response.
+
+ EndPoint serverEndPoint = ref.get();
+ ByteBuffer buffer = BufferUtil.allocate(1);
+ int read = serverEndPoint.fill(buffer);
+ Assert.assertEquals(-1, read);
+ }
+}
diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientCustomProxyTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientCustomProxyTest.java
index 8910d64849..dba109cc0a 100644
--- a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientCustomProxyTest.java
+++ b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientCustomProxyTest.java
@@ -146,7 +146,7 @@ public class HttpClientCustomProxyTest
}
}
- private class CAFEBABEConnection extends AbstractConnection
+ private class CAFEBABEConnection extends AbstractConnection implements Callback
{
private final ClientConnectionFactory connectionFactory;
private final Map<String, Object> context;
@@ -162,8 +162,19 @@ public class HttpClientCustomProxyTest
public void onOpen()
{
super.onOpen();
+ getEndPoint().write(this, ByteBuffer.wrap(CAFE_BABE));
+ }
+
+ @Override
+ public void succeeded()
+ {
fillInterested();
- getEndPoint().write(new Callback.Adapter(), ByteBuffer.wrap(CAFE_BABE));
+ }
+
+ @Override
+ public void failed(Throwable x)
+ {
+ close();
}
@Override
@@ -177,7 +188,7 @@ public class HttpClientCustomProxyTest
Assert.assertArrayEquals(CAFE_BABE, buffer.array());
// We are good, upgrade the connection
- ClientConnectionFactory.Helper.replaceConnection(this, connectionFactory.newConnection(getEndPoint(), context));
+ getEndPoint().upgrade(connectionFactory.newConnection(getEndPoint(), context));
}
catch (Throwable x)
{
@@ -206,7 +217,7 @@ public class HttpClientCustomProxyTest
}
}
- private class CAFEBABEServerConnection extends AbstractConnection
+ private class CAFEBABEServerConnection extends AbstractConnection implements Callback
{
private final org.eclipse.jetty.server.ConnectionFactory connectionFactory;
@@ -232,15 +243,25 @@ public class HttpClientCustomProxyTest
int filled = getEndPoint().fill(buffer);
Assert.assertEquals(4, filled);
Assert.assertArrayEquals(CAFE_BABE, buffer.array());
- getEndPoint().write(new Callback.Adapter(), buffer);
-
- // We are good, upgrade the connection
- ClientConnectionFactory.Helper.replaceConnection(this, connectionFactory.newConnection(connector, getEndPoint()));
+ getEndPoint().write(this, buffer);
}
catch (Throwable x)
{
close();
}
}
+
+ @Override
+ public void succeeded()
+ {
+ // We are good, upgrade the connection
+ getEndPoint().upgrade(connectionFactory.newConnection(connector, getEndPoint()));
+ }
+
+ @Override
+ public void failed(Throwable x)
+ {
+ close();
+ }
}
}
diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientExplicitConnectionTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientExplicitConnectionTest.java
index 34c0b0ffa9..dd35154613 100644
--- a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientExplicitConnectionTest.java
+++ b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientExplicitConnectionTest.java
@@ -59,7 +59,7 @@ public class HttpClientExplicitConnectionTest extends AbstractHttpClientServerTe
Assert.assertEquals(200, response.getStatus());
HttpDestinationOverHTTP httpDestination = (HttpDestinationOverHTTP)destination;
- ConnectionPool connectionPool = httpDestination.getConnectionPool();
+ DuplexConnectionPool connectionPool = (DuplexConnectionPool)httpDestination.getConnectionPool();
Assert.assertTrue(connectionPool.getActiveConnections().isEmpty());
Assert.assertTrue(connectionPool.getIdleConnections().isEmpty());
}
@@ -94,7 +94,7 @@ public class HttpClientExplicitConnectionTest extends AbstractHttpClientServerTe
Assert.assertFalse(httpConnection.getEndPoint().isOpen());
HttpDestinationOverHTTP httpDestination = (HttpDestinationOverHTTP)destination;
- ConnectionPool connectionPool = httpDestination.getConnectionPool();
+ DuplexConnectionPool connectionPool = (DuplexConnectionPool)httpDestination.getConnectionPool();
Assert.assertTrue(connectionPool.getActiveConnections().isEmpty());
Assert.assertTrue(connectionPool.getIdleConnections().isEmpty());
}
diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientFailureTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientFailureTest.java
index 2584701e5a..e7ab277f20 100644
--- a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientFailureTest.java
+++ b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientFailureTest.java
@@ -25,9 +25,6 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import org.eclipse.jetty.client.api.Connection;
-import org.eclipse.jetty.client.api.Request;
-import org.eclipse.jetty.client.api.Response;
-import org.eclipse.jetty.client.api.Result;
import org.eclipse.jetty.client.http.HttpClientTransportOverHTTP;
import org.eclipse.jetty.client.http.HttpConnectionOverHTTP;
import org.eclipse.jetty.client.util.DeferredContentProvider;
@@ -89,14 +86,7 @@ public class HttpClientFailureTest
try
{
client.newRequest("localhost", connector.getLocalPort())
- .onRequestHeaders(new Request.HeadersListener()
- {
- @Override
- public void onHeaders(Request request)
- {
- connectionRef.get().getEndPoint().close();
- }
- })
+ .onRequestHeaders(request -> connectionRef.get().getEndPoint().close())
.timeout(5, TimeUnit.SECONDS)
.send();
Assert.fail();
@@ -106,7 +96,7 @@ public class HttpClientFailureTest
// Expected.
}
- ConnectionPool connectionPool = connectionRef.get().getHttpDestination().getConnectionPool();
+ DuplexConnectionPool connectionPool = (DuplexConnectionPool)connectionRef.get().getHttpDestination().getConnectionPool();
Assert.assertEquals(0, connectionPool.getConnectionCount());
Assert.assertEquals(0, connectionPool.getActiveConnections().size());
Assert.assertEquals(0, connectionPool.getIdleConnections().size());
@@ -134,25 +124,17 @@ public class HttpClientFailureTest
final CountDownLatch completeLatch = new CountDownLatch(1);
DeferredContentProvider content = new DeferredContentProvider();
client.newRequest("localhost", connector.getLocalPort())
- .onRequestCommit(new Request.CommitListener()
+ .onRequestCommit(request ->
{
- @Override
- public void onCommit(Request request)
- {
- connectionRef.get().getEndPoint().close();
- commitLatch.countDown();
- }
+ connectionRef.get().getEndPoint().close();
+ commitLatch.countDown();
})
.content(content)
.idleTimeout(2, TimeUnit.SECONDS)
- .send(new Response.CompleteListener()
+ .send(result ->
{
- @Override
- public void onComplete(Result result)
- {
- if (result.isFailed())
- completeLatch.countDown();
- }
+ if (result.isFailed())
+ completeLatch.countDown();
});
Assert.assertTrue(commitLatch.await(5, TimeUnit.SECONDS));
@@ -170,7 +152,7 @@ public class HttpClientFailureTest
Assert.assertTrue(contentLatch.await(5, TimeUnit.SECONDS));
Assert.assertTrue(completeLatch.await(5, TimeUnit.SECONDS));
- ConnectionPool connectionPool = connectionRef.get().getHttpDestination().getConnectionPool();
+ DuplexConnectionPool connectionPool = (DuplexConnectionPool)connectionRef.get().getHttpDestination().getConnectionPool();
Assert.assertEquals(0, connectionPool.getConnectionCount());
Assert.assertEquals(0, connectionPool.getActiveConnections().size());
Assert.assertEquals(0, connectionPool.getIdleConnections().size());
diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientTest.java
index 662b32a34e..404a3e8e3f 100644
--- a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientTest.java
+++ b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientTest.java
@@ -47,6 +47,7 @@ import java.util.concurrent.Exchanger;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
@@ -63,6 +64,7 @@ import org.eclipse.jetty.client.api.Destination;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.client.api.Response;
import org.eclipse.jetty.client.api.Result;
+import org.eclipse.jetty.client.http.HttpClientTransportOverHTTP;
import org.eclipse.jetty.client.http.HttpConnectionOverHTTP;
import org.eclipse.jetty.client.http.HttpDestinationOverHTTP;
import org.eclipse.jetty.client.util.BufferingResponseListener;
@@ -75,6 +77,7 @@ import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpHeaderValue;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.HttpVersion;
+import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.server.handler.AbstractHandler;
import org.eclipse.jetty.toolchain.test.TestingDir;
import org.eclipse.jetty.toolchain.test.annotation.Slow;
@@ -111,7 +114,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
Assert.assertEquals(200, response.getStatus());
HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, host, port);
- ConnectionPool connectionPool = destination.getConnectionPool();
+ DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool();
long start = System.nanoTime();
HttpConnectionOverHTTP connection = null;
@@ -367,16 +370,12 @@ public class HttpClientTest extends AbstractHttpClientServerTest
final byte[] content = {0, 1, 2, 3};
ContentResponse response = client.POST(scheme + "://localhost:" + connector.getLocalPort())
- .onRequestContent(new Request.ContentListener()
+ .onRequestContent((request, buffer) ->
{
- @Override
- public void onContent(Request request, ByteBuffer buffer)
- {
- byte[] bytes = new byte[buffer.remaining()];
- buffer.get(bytes);
- if (!Arrays.equals(content, bytes))
- request.abort(new Exception());
- }
+ byte[] bytes = new byte[buffer.remaining()];
+ buffer.get(bytes);
+ if (!Arrays.equals(content, bytes))
+ request.abort(new Exception());
})
.content(new BytesContentProvider(content))
.timeout(5, TimeUnit.SECONDS)
@@ -401,16 +400,12 @@ public class HttpClientTest extends AbstractHttpClientServerTest
final AtomicInteger progress = new AtomicInteger();
ContentResponse response = client.POST(scheme + "://localhost:" + connector.getLocalPort())
- .onRequestContent(new Request.ContentListener()
+ .onRequestContent((request, buffer) ->
{
- @Override
- public void onContent(Request request, ByteBuffer buffer)
- {
- byte[] bytes = new byte[buffer.remaining()];
- Assert.assertEquals(1, bytes.length);
- buffer.get(bytes);
- Assert.assertEquals(bytes[0], progress.getAndIncrement());
- }
+ byte[] bytes = new byte[buffer.remaining()];
+ Assert.assertEquals(1, bytes.length);
+ buffer.get(bytes);
+ Assert.assertEquals(bytes[0], progress.getAndIncrement());
})
.content(new BytesContentProvider(new byte[]{0}, new byte[]{1}, new byte[]{2}, new byte[]{3}, new byte[]{4}))
.timeout(5, TimeUnit.SECONDS)
@@ -432,19 +427,15 @@ public class HttpClientTest extends AbstractHttpClientServerTest
final CountDownLatch successLatch = new CountDownLatch(2);
client.newRequest("localhost", connector.getLocalPort())
.scheme(scheme)
- .onRequestBegin(new Request.BeginListener()
+ .onRequestBegin(request ->
{
- @Override
- public void onBegin(Request request)
+ try
{
- try
- {
- latch.await();
- }
- catch (InterruptedException x)
- {
- x.printStackTrace();
- }
+ latch.await();
+ }
+ catch (InterruptedException x)
+ {
+ x.printStackTrace();
}
})
.send(new Response.Listener.Adapter()
@@ -459,14 +450,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
client.newRequest("localhost", connector.getLocalPort())
.scheme(scheme)
- .onRequestQueued(new Request.QueuedListener()
- {
- @Override
- public void onQueued(Request request)
- {
- latch.countDown();
- }
- })
+ .onRequestQueued(request -> latch.countDown())
.send(new Response.Listener.Adapter()
{
@Override
@@ -514,27 +498,16 @@ public class HttpClientTest extends AbstractHttpClientServerTest
latch.countDown();
}
})
- .onResponseFailure(new Response.FailureListener()
- {
- @Override
- public void onFailure(Response response, Throwable failure)
- {
- latch.countDown();
- }
- })
+ .onResponseFailure((response, failure) -> latch.countDown())
.send(null);
client.newRequest("localhost", connector.getLocalPort())
.scheme(scheme)
.path("/two")
- .onResponseSuccess(new Response.SuccessListener()
+ .onResponseSuccess(response ->
{
- @Override
- public void onSuccess(Response response)
- {
- Assert.assertEquals(200, response.getStatus());
- latch.countDown();
- }
+ Assert.assertEquals(200, response.getStatus());
+ latch.countDown();
})
.send(null);
@@ -564,14 +537,10 @@ public class HttpClientTest extends AbstractHttpClientServerTest
client.newRequest("localhost", connector.getLocalPort())
.scheme(scheme)
.file(file)
- .onRequestSuccess(new Request.SuccessListener()
+ .onRequestSuccess(request ->
{
- @Override
- public void onSuccess(Request request)
- {
- requestTime.set(System.nanoTime());
- latch.countDown();
- }
+ requestTime.set(System.nanoTime());
+ latch.countDown();
})
.send(new Response.Listener.Adapter()
{
@@ -674,14 +643,11 @@ public class HttpClientTest extends AbstractHttpClientServerTest
final int port = connector.getLocalPort();
client.newRequest(host, port)
.scheme(scheme)
- .onRequestBegin(new Request.BeginListener()
+ .onRequestBegin(request ->
{
- @Override
- public void onBegin(Request request)
- {
- HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, host, port);
- destination.getConnectionPool().getActiveConnections().peek().close();
- }
+ HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, host, port);
+ DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool();
+ connectionPool.getActiveConnections().iterator().next().close();
})
.send(new Response.Listener.Adapter()
{
@@ -773,14 +739,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
.scheme(scheme)
- .onResponseHeader(new Response.HeaderListener()
- {
- @Override
- public boolean onHeader(Response response, HttpField field)
- {
- return !field.getName().equals(headerName);
- }
- })
+ .onResponseHeader((response1, field) -> !field.getName().equals(headerName))
.timeout(5, TimeUnit.SECONDS)
.send();
@@ -864,16 +823,12 @@ public class HttpClientTest extends AbstractHttpClientServerTest
final CountDownLatch latch = new CountDownLatch(1);
client.newRequest("idontexist", 80)
- .send(new Response.CompleteListener()
+ .send(result ->
{
- @Override
- public void onComplete(Result result)
- {
- Assert.assertTrue(result.isFailed());
- Throwable failure = result.getFailure();
- Assert.assertTrue(failure instanceof UnknownHostException);
- latch.countDown();
- }
+ Assert.assertTrue(result.isFailed());
+ Throwable failure = result.getFailure();
+ Assert.assertTrue(failure instanceof UnknownHostException);
+ latch.countDown();
});
Assert.assertTrue(latch.await(10, TimeUnit.SECONDS));
}
@@ -1323,14 +1278,10 @@ public class HttpClientTest extends AbstractHttpClientServerTest
final CountDownLatch completeLatch = new CountDownLatch(1);
client.newRequest("localhost", connector.getLocalPort())
.scheme(scheme)
- .send(new Response.CompleteListener()
+ .send(result ->
{
- @Override
- public void onComplete(Result result)
- {
- if (result.isFailed())
- completeLatch.countDown();
- }
+ if (result.isFailed())
+ completeLatch.countDown();
});
Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
@@ -1486,6 +1437,54 @@ public class HttpClientTest extends AbstractHttpClientServerTest
}
@Test
+ public void testRequestSentOnlyAfterConnectionOpen() throws Exception
+ {
+ startServer(new AbstractHandler()
+ {
+ @Override
+ public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+ {
+ baseRequest.setHandled(true);
+ }
+ });
+
+ final AtomicBoolean open = new AtomicBoolean();
+ client = new HttpClient(new HttpClientTransportOverHTTP()
+ {
+ @Override
+ protected HttpConnectionOverHTTP newHttpConnection(EndPoint endPoint, HttpDestination destination, Promise<Connection> promise)
+ {
+ return new HttpConnectionOverHTTP(endPoint, destination, promise)
+ {
+ @Override
+ public void onOpen()
+ {
+ open.set(true);
+ super.onOpen();
+ }
+ };
+ }
+ }, sslContextFactory);
+ client.start();
+
+ final CountDownLatch latch = new CountDownLatch(2);
+ client.newRequest("localhost", connector.getLocalPort())
+ .scheme(scheme)
+ .onRequestBegin(request ->
+ {
+ Assert.assertTrue(open.get());
+ latch.countDown();
+ })
+ .send(result ->
+ {
+ if (result.isSucceeded())
+ latch.countDown();
+ });
+
+ Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
+ }
+
+ @Test
public void testCONNECTWithHTTP10() throws Exception
{
try (ServerSocket server = new ServerSocket(0))
diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientTimeoutTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientTimeoutTest.java
index e41ee143d3..9ed138fa11 100644
--- a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientTimeoutTest.java
+++ b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientTimeoutTest.java
@@ -448,7 +448,7 @@ public class HttpClientTimeoutTest extends AbstractHttpClientServerTest
start(new EmptyServerHandler());
long timeout = 1000;
- Request request = client.newRequest("badscheme://localhost:" + connector.getLocalPort());
+ Request request = client.newRequest("badscheme://localhost:badport");
try
{
diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientUploadDuringServerShutdown.java b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientUploadDuringServerShutdown.java
index 61e8cf53d8..bc80ff7aae 100644
--- a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientUploadDuringServerShutdown.java
+++ b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientUploadDuringServerShutdown.java
@@ -31,8 +31,6 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.client.api.Connection;
-import org.eclipse.jetty.client.api.Response;
-import org.eclipse.jetty.client.api.Result;
import org.eclipse.jetty.client.http.HttpChannelOverHTTP;
import org.eclipse.jetty.client.http.HttpClientTransportOverHTTP;
import org.eclipse.jetty.client.http.HttpConnectionOverHTTP;
@@ -121,14 +119,7 @@ public class HttpClientUploadDuringServerShutdown
int length = 16 * 1024 * 1024 + random.nextInt(16 * 1024 * 1024);
client.newRequest("localhost", 8888)
.content(new BytesContentProvider(new byte[length]))
- .send(new Response.CompleteListener()
- {
- @Override
- public void onComplete(Result result)
- {
- latch.countDown();
- }
- });
+ .send(result -> latch.countDown());
long sleep = 1 + random.nextInt(10);
TimeUnit.MILLISECONDS.sleep(sleep);
}
@@ -244,35 +235,24 @@ public class HttpClientUploadDuringServerShutdown
final CountDownLatch completeLatch = new CountDownLatch(1);
client.newRequest("localhost", connector.getLocalPort())
.timeout(10, TimeUnit.SECONDS)
- .onRequestBegin(new org.eclipse.jetty.client.api.Request.BeginListener()
+ .onRequestBegin(request ->
{
- @Override
- public void onBegin(org.eclipse.jetty.client.api.Request request)
+ try
{
- try
- {
- beginLatch.countDown();
- completeLatch.await(5, TimeUnit.SECONDS);
- }
- catch (InterruptedException x)
- {
- x.printStackTrace();
- }
+ beginLatch.countDown();
+ completeLatch.await(5, TimeUnit.SECONDS);
}
- })
- .send(new Response.CompleteListener()
- {
- @Override
- public void onComplete(Result result)
+ catch (InterruptedException x)
{
- completeLatch.countDown();
+ x.printStackTrace();
}
- });
+ })
+ .send(result -> completeLatch.countDown());
Assert.assertTrue(completeLatch.await(5, TimeUnit.SECONDS));
HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination("http", "localhost", connector.getLocalPort());
- ConnectionPool pool = destination.getConnectionPool();
+ DuplexConnectionPool pool = (DuplexConnectionPool)destination.getConnectionPool();
Assert.assertEquals(0, pool.getConnectionCount());
Assert.assertEquals(0, pool.getIdleConnections().size());
Assert.assertEquals(0, pool.getActiveConnections().size());
diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpConnectionLifecycleTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpConnectionLifecycleTest.java
index 771f2368df..684ff02dce 100644
--- a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpConnectionLifecycleTest.java
+++ b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpConnectionLifecycleTest.java
@@ -21,6 +21,7 @@ package org.eclipse.jetty.client;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Arrays;
+import java.util.Collection;
import java.util.Queue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -69,35 +70,24 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
String host = "localhost";
int port = connector.getLocalPort();
HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, host, port);
- ConnectionPool connectionPool = destination.getConnectionPool();
+ DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool();
- final Queue<Connection> idleConnections = connectionPool.getIdleConnections();
+ final Collection<Connection> idleConnections = connectionPool.getIdleConnections();
Assert.assertEquals(0, idleConnections.size());
- final Queue<Connection> activeConnections = connectionPool.getActiveConnections();
+ final Collection<Connection> activeConnections = connectionPool.getActiveConnections();
Assert.assertEquals(0, activeConnections.size());
final CountDownLatch headersLatch = new CountDownLatch(1);
final CountDownLatch successLatch = new CountDownLatch(3);
client.newRequest(host, port)
.scheme(scheme)
- .onRequestSuccess(new Request.SuccessListener()
- {
- @Override
- public void onSuccess(Request request)
- {
- successLatch.countDown();
- }
- })
- .onResponseHeaders(new Response.HeadersListener()
+ .onRequestSuccess(request -> successLatch.countDown())
+ .onResponseHeaders(response ->
{
- @Override
- public void onHeaders(Response response)
- {
- Assert.assertEquals(0, idleConnections.size());
- Assert.assertEquals(1, activeConnections.size());
- headersLatch.countDown();
- }
+ Assert.assertEquals(0, idleConnections.size());
+ Assert.assertEquals(1, activeConnections.size());
+ headersLatch.countDown();
})
.send(new Response.Listener.Adapter()
{
@@ -130,12 +120,12 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
String host = "localhost";
int port = connector.getLocalPort();
HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, host, port);
- ConnectionPool connectionPool = destination.getConnectionPool();
+ DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool();
- final Queue<Connection> idleConnections = connectionPool.getIdleConnections();
+ final Collection<Connection> idleConnections = connectionPool.getIdleConnections();
Assert.assertEquals(0, idleConnections.size());
- final Queue<Connection> activeConnections = connectionPool.getActiveConnections();
+ final Collection<Connection> activeConnections = connectionPool.getActiveConnections();
Assert.assertEquals(0, activeConnections.size());
final CountDownLatch beginLatch = new CountDownLatch(1);
@@ -145,7 +135,7 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
@Override
public void onBegin(Request request)
{
- activeConnections.peek().close();
+ activeConnections.iterator().next().close();
beginLatch.countDown();
}
@@ -181,12 +171,12 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
String host = "localhost";
int port = connector.getLocalPort();
HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, host, port);
- ConnectionPool connectionPool = destination.getConnectionPool();
+ DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool();
final Queue<Connection> idleConnections = connectionPool.getIdleConnections();
Assert.assertEquals(0, idleConnections.size());
- final Queue<Connection> activeConnections = connectionPool.getActiveConnections();
+ final Collection<Connection> activeConnections = connectionPool.getActiveConnections();
Assert.assertEquals(0, activeConnections.size());
final CountDownLatch successLatch = new CountDownLatch(3);
@@ -241,12 +231,12 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
String host = "localhost";
int port = connector.getLocalPort();
HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, host, port);
- ConnectionPool connectionPool = destination.getConnectionPool();
+ DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool();
- final Queue<Connection> idleConnections = connectionPool.getIdleConnections();
+ final Collection<Connection> idleConnections = connectionPool.getIdleConnections();
Assert.assertEquals(0, idleConnections.size());
- final Queue<Connection> activeConnections = connectionPool.getActiveConnections();
+ final Collection<Connection> activeConnections = connectionPool.getActiveConnections();
Assert.assertEquals(0, activeConnections.size());
final long delay = 1000;
@@ -314,12 +304,12 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
String host = "localhost";
int port = connector.getLocalPort();
HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, host, port);
- ConnectionPool connectionPool = destination.getConnectionPool();
+ DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool();
- final Queue<Connection> idleConnections = connectionPool.getIdleConnections();
+ final Collection<Connection> idleConnections = connectionPool.getIdleConnections();
Assert.assertEquals(0, idleConnections.size());
- final Queue<Connection> activeConnections = connectionPool.getActiveConnections();
+ final Collection<Connection> activeConnections = connectionPool.getActiveConnections();
Assert.assertEquals(0, activeConnections.size());
server.stop();
@@ -327,22 +317,11 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
final CountDownLatch failureLatch = new CountDownLatch(2);
client.newRequest(host, port)
.scheme(scheme)
- .onRequestFailure(new Request.FailureListener()
+ .onRequestFailure((request, failure) -> failureLatch.countDown())
+ .send(result ->
{
- @Override
- public void onFailure(Request request, Throwable failure)
- {
- failureLatch.countDown();
- }
- })
- .send(new Response.Listener.Adapter()
- {
- @Override
- public void onComplete(Result result)
- {
- Assert.assertTrue(result.isFailed());
- failureLatch.countDown();
- }
+ Assert.assertTrue(result.isFailed());
+ failureLatch.countDown();
});
Assert.assertTrue(failureLatch.await(5, TimeUnit.SECONDS));
@@ -367,12 +346,12 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
String host = "localhost";
int port = connector.getLocalPort();
HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, host, port);
- ConnectionPool connectionPool = destination.getConnectionPool();
+ DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool();
- final Queue<Connection> idleConnections = connectionPool.getIdleConnections();
+ final Collection<Connection> idleConnections = connectionPool.getIdleConnections();
Assert.assertEquals(0, idleConnections.size());
- final Queue<Connection> activeConnections = connectionPool.getActiveConnections();
+ final Collection<Connection> activeConnections = connectionPool.getActiveConnections();
Assert.assertEquals(0, activeConnections.size());
final CountDownLatch latch = new CountDownLatch(1);
@@ -417,12 +396,12 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
String host = "localhost";
int port = connector.getLocalPort();
HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, host, port);
- ConnectionPool connectionPool = destination.getConnectionPool();
+ DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool();
- final Queue<Connection> idleConnections = connectionPool.getIdleConnections();
+ final Collection<Connection> idleConnections = connectionPool.getIdleConnections();
Assert.assertEquals(0, idleConnections.size());
- final Queue<Connection> activeConnections = connectionPool.getActiveConnections();
+ final Collection<Connection> activeConnections = connectionPool.getActiveConnections();
Assert.assertEquals(0, activeConnections.size());
Log.getLogger(HttpConnection.class).info("Expecting java.lang.IllegalStateException: HttpParser{s=CLOSED,...");
@@ -467,12 +446,12 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
String host = "localhost";
int port = connector.getLocalPort();
HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, host, port);
- ConnectionPool connectionPool = destination.getConnectionPool();
+ DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool();
- final Queue<Connection> idleConnections = connectionPool.getIdleConnections();
+ final Collection<Connection> idleConnections = connectionPool.getIdleConnections();
Assert.assertEquals(0, idleConnections.size());
- final Queue<Connection> activeConnections = connectionPool.getActiveConnections();
+ final Collection<Connection> activeConnections = connectionPool.getActiveConnections();
Assert.assertEquals(0, activeConnections.size());
ContentResponse response = client.newRequest(host, port)
@@ -499,25 +478,21 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
String host = "localhost";
int port = connector.getLocalPort();
HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, host, port);
- ConnectionPool connectionPool = destination.getConnectionPool();
+ DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool();
- final Queue<Connection> idleConnections = connectionPool.getIdleConnections();
+ final Collection<Connection> idleConnections = connectionPool.getIdleConnections();
Assert.assertEquals(0, idleConnections.size());
- final Queue<Connection> activeConnections = connectionPool.getActiveConnections();
+ final Collection<Connection> activeConnections = connectionPool.getActiveConnections();
Assert.assertEquals(0, activeConnections.size());
client.setStrictEventOrdering(false);
ContentResponse response = client.newRequest(host, port)
.scheme(scheme)
- .onResponseBegin(new Response.BeginListener()
+ .onResponseBegin(response1 ->
{
- @Override
- public void onBegin(Response response)
- {
- // Simulate a HTTP 1.0 response has been received.
- ((HttpResponse)response).version(HttpVersion.HTTP_1_0);
- }
+ // Simulate a HTTP 1.0 response has been received.
+ ((HttpResponse)response1).version(HttpVersion.HTTP_1_0);
})
.send();
diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpRequestAbortTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpRequestAbortTest.java
index c4d6010c51..4b33e574e1 100644
--- a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpRequestAbortTest.java
+++ b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpRequestAbortTest.java
@@ -25,12 +25,12 @@ import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
+
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.client.api.Request;
-import org.eclipse.jetty.client.api.Response;
import org.eclipse.jetty.client.api.Result;
import org.eclipse.jetty.client.http.HttpDestinationOverHTTP;
import org.eclipse.jetty.client.util.ByteBufferContentProvider;
@@ -88,7 +88,7 @@ public class HttpRequestAbortTest extends AbstractHttpClientServerTest
}
HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, "localhost", connector.getLocalPort());
- ConnectionPool connectionPool = destination.getConnectionPool();
+ DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool();
Assert.assertEquals(0, connectionPool.getConnectionCount());
Assert.assertEquals(0, connectionPool.getActiveConnections().size());
Assert.assertEquals(0, connectionPool.getIdleConnections().size());
@@ -135,7 +135,7 @@ public class HttpRequestAbortTest extends AbstractHttpClientServerTest
}
HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, "localhost", connector.getLocalPort());
- ConnectionPool connectionPool = destination.getConnectionPool();
+ DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool();
Assert.assertEquals(0, connectionPool.getConnectionCount());
Assert.assertEquals(0, connectionPool.getActiveConnections().size());
Assert.assertEquals(0, connectionPool.getIdleConnections().size());
@@ -182,7 +182,7 @@ public class HttpRequestAbortTest extends AbstractHttpClientServerTest
}
HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, "localhost", connector.getLocalPort());
- ConnectionPool connectionPool = destination.getConnectionPool();
+ DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool();
Assert.assertEquals(0, connectionPool.getConnectionCount());
Assert.assertEquals(0, connectionPool.getActiveConnections().size());
Assert.assertEquals(0, connectionPool.getIdleConnections().size());
@@ -204,14 +204,10 @@ public class HttpRequestAbortTest extends AbstractHttpClientServerTest
{
client.newRequest("localhost", connector.getLocalPort())
.scheme(scheme)
- .onRequestCommit(new Request.CommitListener()
+ .onRequestCommit(request ->
{
- @Override
- public void onCommit(Request request)
- {
- aborted.set(request.abort(cause));
- latch.countDown();
- }
+ aborted.set(request.abort(cause));
+ latch.countDown();
})
.timeout(5, TimeUnit.SECONDS)
.send();
@@ -225,7 +221,7 @@ public class HttpRequestAbortTest extends AbstractHttpClientServerTest
}
HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, "localhost", connector.getLocalPort());
- ConnectionPool connectionPool = destination.getConnectionPool();
+ DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool();
Assert.assertEquals(0, connectionPool.getConnectionCount());
Assert.assertEquals(0, connectionPool.getActiveConnections().size());
Assert.assertEquals(0, connectionPool.getIdleConnections().size());
@@ -260,14 +256,10 @@ public class HttpRequestAbortTest extends AbstractHttpClientServerTest
{
client.newRequest("localhost", connector.getLocalPort())
.scheme(scheme)
- .onRequestCommit(new Request.CommitListener()
+ .onRequestCommit(request ->
{
- @Override
- public void onCommit(Request request)
- {
- aborted.set(request.abort(cause));
- latch.countDown();
- }
+ aborted.set(request.abort(cause));
+ latch.countDown();
})
.content(new ByteBufferContentProvider(ByteBuffer.wrap(new byte[]{0}), ByteBuffer.wrap(new byte[]{1}))
{
@@ -289,7 +281,7 @@ public class HttpRequestAbortTest extends AbstractHttpClientServerTest
}
HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, "localhost", connector.getLocalPort());
- ConnectionPool connectionPool = destination.getConnectionPool();
+ DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool();
Assert.assertEquals(0, connectionPool.getConnectionCount());
Assert.assertEquals(0, connectionPool.getActiveConnections().size());
Assert.assertEquals(0, connectionPool.getIdleConnections().size());
@@ -315,14 +307,10 @@ public class HttpRequestAbortTest extends AbstractHttpClientServerTest
{
client.newRequest("localhost", connector.getLocalPort())
.scheme(scheme)
- .onRequestContent(new Request.ContentListener()
+ .onRequestContent((request, content) ->
{
- @Override
- public void onContent(Request request, ByteBuffer content)
- {
- aborted.set(request.abort(cause));
- latch.countDown();
- }
+ aborted.set(request.abort(cause));
+ latch.countDown();
})
.content(new ByteBufferContentProvider(ByteBuffer.wrap(new byte[]{0}), ByteBuffer.wrap(new byte[]{1}))
{
@@ -344,7 +332,7 @@ public class HttpRequestAbortTest extends AbstractHttpClientServerTest
}
HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, "localhost", connector.getLocalPort());
- ConnectionPool connectionPool = destination.getConnectionPool();
+ DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool();
Assert.assertEquals(0, connectionPool.getConnectionCount());
Assert.assertEquals(0, connectionPool.getActiveConnections().size());
Assert.assertEquals(0, connectionPool.getIdleConnections().size());
@@ -454,7 +442,7 @@ public class HttpRequestAbortTest extends AbstractHttpClientServerTest
}
HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, "localhost", connector.getLocalPort());
- ConnectionPool connectionPool = destination.getConnectionPool();
+ DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool();
Assert.assertEquals(0, connectionPool.getConnectionCount());
Assert.assertEquals(0, connectionPool.getActiveConnections().size());
Assert.assertEquals(0, connectionPool.getIdleConnections().size());
@@ -486,15 +474,11 @@ public class HttpRequestAbortTest extends AbstractHttpClientServerTest
Request request = client.newRequest("localhost", connector.getLocalPort())
.scheme(scheme)
.timeout(3 * delay, TimeUnit.MILLISECONDS);
- request.send(new Response.CompleteListener()
+ request.send(result ->
{
- @Override
- public void onComplete(Result result)
- {
- Assert.assertTrue(result.isFailed());
- Assert.assertSame(cause, result.getFailure());
- latch.countDown();
- }
+ Assert.assertTrue(result.isFailed());
+ Assert.assertSame(cause, result.getFailure());
+ latch.countDown();
});
TimeUnit.MILLISECONDS.sleep(delay);
diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/ServerConnectionCloseTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/ServerConnectionCloseTest.java
new file mode 100644
index 0000000000..47a7760e1a
--- /dev/null
+++ b/jetty-client/src/test/java/org/eclipse/jetty/client/ServerConnectionCloseTest.java
@@ -0,0 +1,176 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2015 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.client;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.util.concurrent.TimeUnit;
+
+import org.eclipse.jetty.client.api.ContentResponse;
+import org.eclipse.jetty.client.api.Request;
+import org.eclipse.jetty.client.http.HttpClientTransportOverHTTP;
+import org.eclipse.jetty.client.http.HttpDestinationOverHTTP;
+import org.eclipse.jetty.client.util.FutureResponseListener;
+import org.eclipse.jetty.http.HttpStatus;
+import org.eclipse.jetty.toolchain.test.TestTracker;
+import org.eclipse.jetty.util.thread.QueuedThreadPool;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+
+public class ServerConnectionCloseTest
+{
+ @Rule
+ public final TestTracker tracker = new TestTracker();
+ private HttpClient client;
+
+ private void startClient() throws Exception
+ {
+ QueuedThreadPool clientThreads = new QueuedThreadPool();
+ clientThreads.setName("client");
+ client = new HttpClient(new HttpClientTransportOverHTTP(1), null);
+ client.setExecutor(clientThreads);
+ client.start();
+ }
+
+ @After
+ public void dispose() throws Exception
+ {
+ if (client != null)
+ client.stop();
+ }
+
+ @Test
+ public void testServerSendsConnectionCloseWithoutContent() throws Exception
+ {
+ testServerSendsConnectionClose(true, false, "");
+ }
+
+ @Test
+ public void testServerSendsConnectionCloseWithContent() throws Exception
+ {
+ testServerSendsConnectionClose(true, false, "data");
+ }
+
+ @Test
+ public void testServerSendsConnectionCloseWithChunkedContent() throws Exception
+ {
+ testServerSendsConnectionClose(true, true, "data");
+ }
+
+ @Test
+ public void testServerSendsConnectionCloseWithoutContentButDoesNotClose() throws Exception
+ {
+ testServerSendsConnectionClose(false, false, "");
+ }
+
+ @Test
+ public void testServerSendsConnectionCloseWithContentButDoesNotClose() throws Exception
+ {
+ testServerSendsConnectionClose(false, false, "data");
+ }
+
+ @Test
+ public void testServerSendsConnectionCloseWithChunkedContentButDoesNotClose() throws Exception
+ {
+ testServerSendsConnectionClose(false, true, "data");
+ }
+
+ private void testServerSendsConnectionClose(boolean shutdownOutput, boolean chunked, String content) throws Exception
+ {
+ ServerSocket server = new ServerSocket(0);
+ int port = server.getLocalPort();
+
+ startClient();
+
+ Request request = client.newRequest("localhost", port).path("/ctx/path");
+ FutureResponseListener listener = new FutureResponseListener(request);
+ request.send(listener);
+
+ Socket socket = server.accept();
+
+ InputStream input = socket.getInputStream();
+ consumeRequest(input);
+
+ OutputStream output = socket.getOutputStream();
+ String serverResponse = "" +
+ "HTTP/1.1 200 OK\r\n" +
+ "Connection: close\r\n";
+ if (chunked)
+ {
+ serverResponse += "" +
+ "Transfer-Encoding: chunked\r\n" +
+ "\r\n";
+ for (int i = 0; i < 2; ++i)
+ {
+ serverResponse +=
+ Integer.toHexString(content.length()) + "\r\n" +
+ content + "\r\n";
+ }
+ serverResponse += "" +
+ "0\r\n" +
+ "\r\n";
+ }
+ else
+ {
+ serverResponse += "Content-Length: " + content.length() + "\r\n";
+ serverResponse += "\r\n";
+ serverResponse += content;
+ }
+
+ output.write(serverResponse.getBytes("UTF-8"));
+ output.flush();
+ if (shutdownOutput)
+ socket.shutdownOutput();
+
+ ContentResponse response = listener.get(5, TimeUnit.SECONDS);
+ Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
+
+ // Give some time to process the connection.
+ Thread.sleep(1000);
+
+ // Connection should have been removed from pool.
+ HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination("http", "localhost", port);
+ DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool();
+ Assert.assertEquals(0, connectionPool.getConnectionCount());
+ Assert.assertEquals(0, connectionPool.getIdleConnectionCount());
+ Assert.assertEquals(0, connectionPool.getActiveConnectionCount());
+ }
+
+ private boolean consumeRequest(InputStream input) throws IOException
+ {
+ int crlfs = 0;
+ while (true)
+ {
+ int read = input.read();
+ if (read < 0)
+ return true;
+ if (read == '\r' || read == '\n')
+ ++crlfs;
+ else
+ crlfs = 0;
+ if (crlfs == 4)
+ return false;
+ }
+ }
+}
diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/TLSServerConnectionCloseTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/TLSServerConnectionCloseTest.java
new file mode 100644
index 0000000000..d48ed22314
--- /dev/null
+++ b/jetty-client/src/test/java/org/eclipse/jetty/client/TLSServerConnectionCloseTest.java
@@ -0,0 +1,213 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2015 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.client;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.util.concurrent.TimeUnit;
+
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSocket;
+
+import org.eclipse.jetty.client.api.ContentResponse;
+import org.eclipse.jetty.client.api.Request;
+import org.eclipse.jetty.client.http.HttpClientTransportOverHTTP;
+import org.eclipse.jetty.client.http.HttpDestinationOverHTTP;
+import org.eclipse.jetty.client.util.FutureResponseListener;
+import org.eclipse.jetty.http.HttpStatus;
+import org.eclipse.jetty.toolchain.test.TestTracker;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
+import org.eclipse.jetty.util.thread.QueuedThreadPool;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class TLSServerConnectionCloseTest
+{
+ @Parameterized.Parameters(name = "CloseMode: {0}")
+ public static Object[] parameters()
+ {
+ return new Object[]{CloseMode.NONE, CloseMode.CLOSE, CloseMode.ABRUPT};
+ }
+
+ @Rule
+ public final TestTracker tracker = new TestTracker();
+ private HttpClient client;
+ private final CloseMode closeMode;
+
+ public TLSServerConnectionCloseTest(CloseMode closeMode)
+ {
+ this.closeMode = closeMode;
+ }
+
+ private void startClient() throws Exception
+ {
+ SslContextFactory sslContextFactory = new SslContextFactory();
+ sslContextFactory.setEndpointIdentificationAlgorithm("");
+ sslContextFactory.setKeyStorePath("src/test/resources/keystore.jks");
+ sslContextFactory.setKeyStorePassword("storepwd");
+ sslContextFactory.setTrustStorePath("src/test/resources/truststore.jks");
+ sslContextFactory.setTrustStorePassword("storepwd");
+
+ QueuedThreadPool clientThreads = new QueuedThreadPool();
+ clientThreads.setName("client");
+ client = new HttpClient(new HttpClientTransportOverHTTP(1), sslContextFactory);
+ client.setExecutor(clientThreads);
+ client.start();
+ }
+
+ @After
+ public void dispose() throws Exception
+ {
+ if (client != null)
+ client.stop();
+ }
+
+ @Test
+ public void testServerSendsConnectionCloseWithoutContent() throws Exception
+ {
+ testServerSendsConnectionClose(false, "");
+ }
+
+ @Test
+ public void testServerSendsConnectionCloseWithContent() throws Exception
+ {
+ testServerSendsConnectionClose(false, "data");
+ }
+
+ @Test
+ public void testServerSendsConnectionCloseWithChunkedContent() throws Exception
+ {
+ testServerSendsConnectionClose(true, "data");
+ }
+
+ private void testServerSendsConnectionClose(boolean chunked, String content) throws Exception
+ {
+ ServerSocket server = new ServerSocket(0);
+ int port = server.getLocalPort();
+
+ startClient();
+
+ Request request = client.newRequest("localhost", port).scheme("https").path("/ctx/path");
+ FutureResponseListener listener = new FutureResponseListener(request);
+ request.send(listener);
+
+ Socket socket = server.accept();
+ SSLContext sslContext = client.getSslContextFactory().getSslContext();
+ SSLSocket sslSocket = (SSLSocket)sslContext.getSocketFactory().createSocket(socket, "localhost", port, false);
+ sslSocket.setUseClientMode(false);
+ sslSocket.startHandshake();
+
+ InputStream input = sslSocket.getInputStream();
+ consumeRequest(input);
+
+ OutputStream output = sslSocket.getOutputStream();
+ String serverResponse = "" +
+ "HTTP/1.1 200 OK\r\n" +
+ "Connection: close\r\n";
+ if (chunked)
+ {
+ serverResponse += "" +
+ "Transfer-Encoding: chunked\r\n" +
+ "\r\n";
+ for (int i = 0; i < 2; ++i)
+ {
+ serverResponse +=
+ Integer.toHexString(content.length()) + "\r\n" +
+ content + "\r\n";
+ }
+ serverResponse += "" +
+ "0\r\n" +
+ "\r\n";
+ }
+ else
+ {
+ serverResponse += "Content-Length: " + content.length() + "\r\n";
+ serverResponse += "\r\n";
+ serverResponse += content;
+ }
+
+ output.write(serverResponse.getBytes("UTF-8"));
+ output.flush();
+
+ switch (closeMode)
+ {
+ case NONE:
+ {
+ break;
+ }
+ case CLOSE:
+ {
+ sslSocket.close();
+ break;
+ }
+ case ABRUPT:
+ {
+ socket.shutdownOutput();
+ break;
+ }
+ default:
+ {
+ throw new IllegalStateException();
+ }
+ }
+
+ ContentResponse response = listener.get(5, TimeUnit.SECONDS);
+ Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
+
+ // Give some time to process the connection.
+ Thread.sleep(1000);
+
+ // Connection should have been removed from pool.
+ HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination("http", "localhost", port);
+ DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool();
+ Assert.assertEquals(0, connectionPool.getConnectionCount());
+ Assert.assertEquals(0, connectionPool.getIdleConnectionCount());
+ Assert.assertEquals(0, connectionPool.getActiveConnectionCount());
+ }
+
+ private boolean consumeRequest(InputStream input) throws IOException
+ {
+ int crlfs = 0;
+ while (true)
+ {
+ int read = input.read();
+ if (read < 0)
+ return true;
+ if (read == '\r' || read == '\n')
+ ++crlfs;
+ else
+ crlfs = 0;
+ if (crlfs == 4)
+ return false;
+ }
+ }
+
+ private enum CloseMode
+ {
+ NONE, CLOSE, ABRUPT
+ }
+}
diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/ValidatingConnectionPoolTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/ValidatingConnectionPoolTest.java
index d6b707f0d5..a9ad8174fc 100644
--- a/jetty-client/src/test/java/org/eclipse/jetty/client/ValidatingConnectionPoolTest.java
+++ b/jetty-client/src/test/java/org/eclipse/jetty/client/ValidatingConnectionPoolTest.java
@@ -194,7 +194,7 @@ public class ValidatingConnectionPoolTest extends AbstractHttpClientServerTest
return new HttpDestinationOverHTTP(getHttpClient(), origin)
{
@Override
- protected ConnectionPool newConnectionPool(HttpClient client)
+ protected DuplexConnectionPool newConnectionPool(HttpClient client)
{
return new ValidatingConnectionPool(this, client.getMaxConnectionsPerDestination(), this, client.getScheduler(), timeout);
}
diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/http/HttpDestinationOverHTTPTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/http/HttpDestinationOverHTTPTest.java
index 4b6075b5aa..bf9af834d9 100644
--- a/jetty-client/src/test/java/org/eclipse/jetty/client/http/HttpDestinationOverHTTPTest.java
+++ b/jetty-client/src/test/java/org/eclipse/jetty/client/http/HttpDestinationOverHTTPTest.java
@@ -25,15 +25,13 @@ import java.util.concurrent.TimeUnit;
import org.eclipse.jetty.client.AbstractHttpClientServerTest;
import org.eclipse.jetty.client.ConnectionPool;
+import org.eclipse.jetty.client.DuplexConnectionPool;
import org.eclipse.jetty.client.EmptyServerHandler;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.Origin;
import org.eclipse.jetty.client.api.Connection;
import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.client.api.Destination;
-import org.eclipse.jetty.client.api.Request;
-import org.eclipse.jetty.client.api.Response;
-import org.eclipse.jetty.client.api.Result;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpHeaderValue;
import org.eclipse.jetty.util.ssl.SslContextFactory;
@@ -59,11 +57,13 @@ public class HttpDestinationOverHTTPTest extends AbstractHttpClientServerTest
public void test_FirstAcquire_WithEmptyQueue() throws Exception
{
HttpDestinationOverHTTP destination = new HttpDestinationOverHTTP(client, new Origin("http", "localhost", connector.getLocalPort()));
- Connection connection = destination.acquire();
+ destination.start();
+ DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool();
+ Connection connection = connectionPool.acquire();
if (connection == null)
{
// There are no queued requests, so the newly created connection will be idle
- connection = timedPoll(destination.getConnectionPool().getIdleConnections(), 5, TimeUnit.SECONDS);
+ connection = timedPoll(connectionPool.getIdleConnections(), 5, TimeUnit.SECONDS);
}
Assert.assertNotNull(connection);
}
@@ -72,7 +72,9 @@ public class HttpDestinationOverHTTPTest extends AbstractHttpClientServerTest
public void test_SecondAcquire_AfterFirstAcquire_WithEmptyQueue_ReturnsSameConnection() throws Exception
{
HttpDestinationOverHTTP destination = new HttpDestinationOverHTTP(client, new Origin("http", "localhost", connector.getLocalPort()));
- Connection connection1 = destination.acquire();
+ destination.start();
+ DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool();
+ Connection connection1 = connectionPool.acquire();
if (connection1 == null)
{
// There are no queued requests, so the newly created connection will be idle
@@ -80,11 +82,11 @@ public class HttpDestinationOverHTTPTest extends AbstractHttpClientServerTest
while (connection1 == null && TimeUnit.NANOSECONDS.toSeconds(System.nanoTime() - start) < 5)
{
TimeUnit.MILLISECONDS.sleep(50);
- connection1 = destination.getConnectionPool().getIdleConnections().peek();
+ connection1 = connectionPool.getIdleConnections().peek();
}
Assert.assertNotNull(connection1);
- Connection connection2 = destination.acquire();
+ Connection connection2 = connectionPool.acquire();
Assert.assertSame(connection1, connection2);
}
}
@@ -99,16 +101,16 @@ public class HttpDestinationOverHTTPTest extends AbstractHttpClientServerTest
@Override
protected ConnectionPool newConnectionPool(HttpClient client)
{
- return new ConnectionPool(this, client.getMaxConnectionsPerDestination(), this)
+ return new DuplexConnectionPool(this, client.getMaxConnectionsPerDestination(), this)
{
@Override
- protected void idleCreated(Connection connection)
+ protected void onCreated(Connection connection)
{
try
{
idleLatch.countDown();
latch.await(5, TimeUnit.SECONDS);
- super.idleCreated(connection);
+ super.onCreated(connection);
}
catch (InterruptedException x)
{
@@ -118,7 +120,9 @@ public class HttpDestinationOverHTTPTest extends AbstractHttpClientServerTest
};
}
};
- Connection connection1 = destination.acquire();
+ destination.start();
+ DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool();
+ Connection connection1 = connectionPool.acquire();
// Make sure we entered idleCreated().
Assert.assertTrue(idleLatch.await(5, TimeUnit.SECONDS));
@@ -128,13 +132,13 @@ public class HttpDestinationOverHTTPTest extends AbstractHttpClientServerTest
Assert.assertNull(connection1);
// Second attempt also returns null because we delayed idleCreated() above.
- Connection connection2 = destination.acquire();
+ Connection connection2 = connectionPool.acquire();
Assert.assertNull(connection2);
latch.countDown();
// There must be 2 idle connections.
- Queue<Connection> idleConnections = destination.getConnectionPool().getIdleConnections();
+ Queue<Connection> idleConnections = connectionPool.getIdleConnections();
Connection connection = timedPoll(idleConnections, 5, TimeUnit.SECONDS);
Assert.assertNotNull(connection);
connection = timedPoll(idleConnections, 5, TimeUnit.SECONDS);
@@ -145,23 +149,25 @@ public class HttpDestinationOverHTTPTest extends AbstractHttpClientServerTest
public void test_Acquire_Process_Release_Acquire_ReturnsSameConnection() throws Exception
{
HttpDestinationOverHTTP destination = new HttpDestinationOverHTTP(client, new Origin("http", "localhost", connector.getLocalPort()));
- HttpConnectionOverHTTP connection1 = destination.acquire();
+ destination.start();
+ DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool();
+ HttpConnectionOverHTTP connection1 = (HttpConnectionOverHTTP)connectionPool.acquire();
long start = System.nanoTime();
while (connection1 == null && TimeUnit.NANOSECONDS.toSeconds(System.nanoTime() - start) < 5)
{
TimeUnit.MILLISECONDS.sleep(50);
- connection1 = (HttpConnectionOverHTTP)destination.getConnectionPool().getIdleConnections().peek();
+ connection1 = (HttpConnectionOverHTTP)connectionPool.getIdleConnections().peek();
}
Assert.assertNotNull(connection1);
// Acquire the connection to make it active
- Assert.assertSame(connection1, destination.acquire());
+ Assert.assertSame(connection1, connectionPool.acquire());
destination.process(connection1);
destination.release(connection1);
- Connection connection2 = destination.acquire();
+ Connection connection2 = connectionPool.acquire();
Assert.assertSame(connection1, connection2);
}
@@ -172,7 +178,9 @@ public class HttpDestinationOverHTTPTest extends AbstractHttpClientServerTest
client.setIdleTimeout(idleTimeout);
HttpDestinationOverHTTP destination = new HttpDestinationOverHTTP(client, new Origin("http", "localhost", connector.getLocalPort()));
- Connection connection1 = destination.acquire();
+ destination.start();
+ DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool();
+ Connection connection1 = connectionPool.acquire();
if (connection1 == null)
{
// There are no queued requests, so the newly created connection will be idle
@@ -180,13 +188,13 @@ public class HttpDestinationOverHTTPTest extends AbstractHttpClientServerTest
while (connection1 == null && TimeUnit.NANOSECONDS.toSeconds(System.nanoTime() - start) < 5)
{
TimeUnit.MILLISECONDS.sleep(50);
- connection1 = destination.getConnectionPool().getIdleConnections().peek();
+ connection1 = connectionPool.getIdleConnections().peek();
}
Assert.assertNotNull(connection1);
TimeUnit.MILLISECONDS.sleep(2 * idleTimeout);
- connection1 = destination.getConnectionPool().getIdleConnections().poll();
+ connection1 = connectionPool.getIdleConnections().poll();
Assert.assertNull(connection1);
}
}
@@ -210,35 +218,23 @@ public class HttpDestinationOverHTTPTest extends AbstractHttpClientServerTest
client.newRequest("localhost", connector.getLocalPort())
.scheme(scheme)
.path("/one")
- .onRequestQueued(new Request.QueuedListener()
+ .onRequestQueued(request ->
{
- @Override
- public void onQueued(Request request)
- {
- // This request exceeds the maximum queued, should fail
- client.newRequest("localhost", connector.getLocalPort())
- .scheme(scheme)
- .path("/two")
- .send(new Response.CompleteListener()
- {
- @Override
- public void onComplete(Result result)
- {
- Assert.assertTrue(result.isFailed());
- Assert.assertThat(result.getRequestFailure(), Matchers.instanceOf(RejectedExecutionException.class));
- failureLatch.countDown();
- }
- });
- }
+ // This request exceeds the maximum queued, should fail
+ client.newRequest("localhost", connector.getLocalPort())
+ .scheme(scheme)
+ .path("/two")
+ .send(result ->
+ {
+ Assert.assertTrue(result.isFailed());
+ Assert.assertThat(result.getRequestFailure(), Matchers.instanceOf(RejectedExecutionException.class));
+ failureLatch.countDown();
+ });
})
- .send(new Response.CompleteListener()
+ .send(result ->
{
- @Override
- public void onComplete(Result result)
- {
- if (result.isSucceeded())
- successLatch.countDown();
- }
+ if (result.isSucceeded())
+ successLatch.countDown();
});
Assert.assertTrue(failureLatch.await(5, TimeUnit.SECONDS));
diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/http/HttpReceiverOverHTTPTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/http/HttpReceiverOverHTTPTest.java
index 55217609b5..feb5cf0c58 100644
--- a/jetty-client/src/test/java/org/eclipse/jetty/client/http/HttpReceiverOverHTTPTest.java
+++ b/jetty-client/src/test/java/org/eclipse/jetty/client/http/HttpReceiverOverHTTPTest.java
@@ -61,8 +61,10 @@ public class HttpReceiverOverHTTPTest
client = new HttpClient();
client.start();
destination = new HttpDestinationOverHTTP(client, new Origin("http", "localhost", 8080));
+ destination.start();
endPoint = new ByteArrayEndPoint();
- connection = new HttpConnectionOverHTTP(endPoint, destination, new Promise.Adapter<Connection>());
+ connection = new HttpConnectionOverHTTP(endPoint, destination, new Promise.Adapter<>());
+ endPoint.setConnection(connection);
}
@After
@@ -207,7 +209,7 @@ public class HttpReceiverOverHTTPTest
@Test
public void test_FillInterested_RacingWith_BufferRelease() throws Exception
{
- connection = new HttpConnectionOverHTTP(endPoint, destination, new Promise.Adapter<Connection>())
+ connection = new HttpConnectionOverHTTP(endPoint, destination, new Promise.Adapter<>())
{
@Override
protected HttpChannelOverHTTP newHttpChannel()
@@ -234,6 +236,7 @@ public class HttpReceiverOverHTTPTest
};
}
};
+ endPoint.setConnection(connection);
// Partial response to trigger the call to fillInterested().
endPoint.addInput("" +
diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/http/HttpSenderOverHTTPTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/http/HttpSenderOverHTTPTest.java
index b98aea13ab..e592c42ca8 100644
--- a/jetty-client/src/test/java/org/eclipse/jetty/client/http/HttpSenderOverHTTPTest.java
+++ b/jetty-client/src/test/java/org/eclipse/jetty/client/http/HttpSenderOverHTTPTest.java
@@ -67,6 +67,7 @@ public class HttpSenderOverHTTPTest
{
ByteArrayEndPoint endPoint = new ByteArrayEndPoint();
HttpDestinationOverHTTP destination = new HttpDestinationOverHTTP(client, new Origin("http", "localhost", 8080));
+ destination.start();
HttpConnectionOverHTTP connection = new HttpConnectionOverHTTP(endPoint, destination, new Promise.Adapter<Connection>());
Request request = client.newRequest(URI.create("http://localhost/"));
final CountDownLatch headersLatch = new CountDownLatch(1);
@@ -100,6 +101,7 @@ public class HttpSenderOverHTTPTest
{
ByteArrayEndPoint endPoint = new ByteArrayEndPoint("", 16);
HttpDestinationOverHTTP destination = new HttpDestinationOverHTTP(client, new Origin("http", "localhost", 8080));
+ destination.start();
HttpConnectionOverHTTP connection = new HttpConnectionOverHTTP(endPoint, destination, new Promise.Adapter<Connection>());
Request request = client.newRequest(URI.create("http://localhost/"));
connection.send(request, null);
@@ -129,6 +131,7 @@ public class HttpSenderOverHTTPTest
// Shutdown output to trigger the exception on write
endPoint.shutdownOutput();
HttpDestinationOverHTTP destination = new HttpDestinationOverHTTP(client, new Origin("http", "localhost", 8080));
+ destination.start();
HttpConnectionOverHTTP connection = new HttpConnectionOverHTTP(endPoint, destination, new Promise.Adapter<Connection>());
Request request = client.newRequest(URI.create("http://localhost/"));
final CountDownLatch failureLatch = new CountDownLatch(2);
@@ -158,6 +161,7 @@ public class HttpSenderOverHTTPTest
{
ByteArrayEndPoint endPoint = new ByteArrayEndPoint("", 16);
HttpDestinationOverHTTP destination = new HttpDestinationOverHTTP(client, new Origin("http", "localhost", 8080));
+ destination.start();
HttpConnectionOverHTTP connection = new HttpConnectionOverHTTP(endPoint, destination, new Promise.Adapter<Connection>());
Request request = client.newRequest(URI.create("http://localhost/"));
final CountDownLatch failureLatch = new CountDownLatch(2);
@@ -193,6 +197,7 @@ public class HttpSenderOverHTTPTest
{
ByteArrayEndPoint endPoint = new ByteArrayEndPoint();
HttpDestinationOverHTTP destination = new HttpDestinationOverHTTP(client, new Origin("http", "localhost", 8080));
+ destination.start();
HttpConnectionOverHTTP connection = new HttpConnectionOverHTTP(endPoint, destination, new Promise.Adapter<Connection>());
Request request = client.newRequest(URI.create("http://localhost/"));
String content = "abcdef";
@@ -227,6 +232,7 @@ public class HttpSenderOverHTTPTest
{
ByteArrayEndPoint endPoint = new ByteArrayEndPoint();
HttpDestinationOverHTTP destination = new HttpDestinationOverHTTP(client, new Origin("http", "localhost", 8080));
+ destination.start();
HttpConnectionOverHTTP connection = new HttpConnectionOverHTTP(endPoint, destination, new Promise.Adapter<Connection>());
Request request = client.newRequest(URI.create("http://localhost/"));
String content1 = "0123456789";
@@ -262,6 +268,7 @@ public class HttpSenderOverHTTPTest
{
ByteArrayEndPoint endPoint = new ByteArrayEndPoint();
HttpDestinationOverHTTP destination = new HttpDestinationOverHTTP(client, new Origin("http", "localhost", 8080));
+ destination.start();
HttpConnectionOverHTTP connection = new HttpConnectionOverHTTP(endPoint, destination, new Promise.Adapter<Connection>());
Request request = client.newRequest(URI.create("http://localhost/"));
String content1 = "0123456789";
diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/ssl/SslBytesServerTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/ssl/SslBytesServerTest.java
index 8dd287f492..535f7ecd89 100644
--- a/jetty-client/src/test/java/org/eclipse/jetty/client/ssl/SslBytesServerTest.java
+++ b/jetty-client/src/test/java/org/eclipse/jetty/client/ssl/SslBytesServerTest.java
@@ -54,10 +54,10 @@ import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.client.ssl.SslBytesTest.TLSRecord.Type;
import org.eclipse.jetty.http.HttpParser;
+import org.eclipse.jetty.io.ChannelEndPoint;
import org.eclipse.jetty.io.Connection;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.io.ManagedSelector;
-import org.eclipse.jetty.io.SelectChannelEndPoint;
import org.eclipse.jetty.io.ssl.SslConnection;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.HttpConnection;
@@ -173,9 +173,9 @@ public class SslBytesServerTest extends SslBytesTest
ServerConnector connector = new ServerConnector(server, null,null,null,1,1,sslFactory, httpFactory)
{
@Override
- protected SelectChannelEndPoint newEndPoint(SocketChannel channel, ManagedSelector selectSet, SelectionKey key) throws IOException
+ protected ChannelEndPoint newEndPoint(SocketChannel channel, ManagedSelector selectSet, SelectionKey key) throws IOException
{
- SelectChannelEndPoint endp = super.newEndPoint(channel,selectSet,key);
+ ChannelEndPoint endp = super.newEndPoint(channel,selectSet,key);
serverEndPoint.set(endp);
return endp;
}
@@ -367,11 +367,19 @@ public class SslBytesServerTest extends SslBytesTest
System.arraycopy(doneBytes, 0, chunk, recordBytes.length, doneBytes.length);
System.arraycopy(closeRecordBytes, 0, chunk, recordBytes.length + doneBytes.length, closeRecordBytes.length);
proxy.flushToServer(0, chunk);
+
// Close the raw socket
proxy.flushToServer(null);
// Expect the server to send a FIN as well
record = proxy.readFromServer();
+ if (record!=null)
+ {
+ // Close alert snuck out // TODO check if this is acceptable
+ Assert.assertEquals(Type.ALERT,record.getType());
+ record = proxy.readFromServer();
+ }
+
Assert.assertNull(record);
// Check that we did not spin
diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/util/MultiPartContentProviderTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/util/MultiPartContentProviderTest.java
new file mode 100644
index 0000000000..06c89e28e8
--- /dev/null
+++ b/jetty-client/src/test/java/org/eclipse/jetty/client/util/MultiPartContentProviderTest.java
@@ -0,0 +1,448 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2015 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.client.util;
+
+import java.io.BufferedWriter;
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardOpenOption;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Random;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import javax.servlet.MultipartConfigElement;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.Part;
+
+import org.eclipse.jetty.client.AbstractHttpClientServerTest;
+import org.eclipse.jetty.client.api.ContentProvider;
+import org.eclipse.jetty.client.api.ContentResponse;
+import org.eclipse.jetty.http.HttpFields;
+import org.eclipse.jetty.http.HttpHeader;
+import org.eclipse.jetty.http.HttpMethod;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.handler.AbstractHandler;
+import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
+import org.eclipse.jetty.util.IO;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class MultiPartContentProviderTest extends AbstractHttpClientServerTest
+{
+ public MultiPartContentProviderTest(SslContextFactory sslContextFactory)
+ {
+ super(sslContextFactory);
+ }
+
+ @Test
+ public void testEmptyMultiPart() throws Exception
+ {
+ start(new AbstractMultiPartHandler()
+ {
+ @Override
+ protected void handle(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+ {
+ Collection<Part> parts = request.getParts();
+ Assert.assertEquals(0, parts.size());
+ }
+ });
+
+ MultiPartContentProvider multiPart = new MultiPartContentProvider();
+ multiPart.close();
+ ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
+ .scheme(scheme)
+ .method(HttpMethod.POST)
+ .content(multiPart)
+ .send();
+
+ Assert.assertEquals(200, response.getStatus());
+ }
+
+ @Test
+ public void testSimpleField() throws Exception
+ {
+ String name = "field";
+ String value = "value";
+ start(new AbstractMultiPartHandler()
+ {
+ @Override
+ protected void handle(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+ {
+ Collection<Part> parts = request.getParts();
+ Assert.assertEquals(1, parts.size());
+ Part part = parts.iterator().next();
+ Assert.assertEquals(name, part.getName());
+ Assert.assertEquals(value, IO.toString(part.getInputStream()));
+ }
+ });
+
+ MultiPartContentProvider multiPart = new MultiPartContentProvider();
+ multiPart.addFieldPart(name, new StringContentProvider(value), null);
+ multiPart.close();
+ ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
+ .scheme(scheme)
+ .method(HttpMethod.POST)
+ .content(multiPart)
+ .send();
+
+ Assert.assertEquals(200, response.getStatus());
+ }
+
+ @Test
+ public void testFieldWithOverridenContentType() throws Exception
+ {
+ String name = "field";
+ String value = "\u00e8";
+ Charset encoding = StandardCharsets.ISO_8859_1;
+ start(new AbstractMultiPartHandler()
+ {
+ @Override
+ protected void handle(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+ {
+ Collection<Part> parts = request.getParts();
+ Assert.assertEquals(1, parts.size());
+ Part part = parts.iterator().next();
+ Assert.assertEquals(name, part.getName());
+ String contentType = part.getContentType();
+ Assert.assertNotNull(contentType);
+ int equal = contentType.lastIndexOf('=');
+ Charset charset = Charset.forName(contentType.substring(equal + 1));
+ Assert.assertEquals(encoding, charset);
+ Assert.assertEquals(value, IO.toString(part.getInputStream(), charset));
+ }
+ });
+
+ MultiPartContentProvider multiPart = new MultiPartContentProvider();
+ HttpFields fields = new HttpFields();
+ fields.put(HttpHeader.CONTENT_TYPE, "text/plain;charset=" + encoding.name());
+ BytesContentProvider content = new BytesContentProvider(value.getBytes(encoding));
+ multiPart.addFieldPart(name, content, fields);
+ multiPart.close();
+ ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
+ .scheme(scheme)
+ .method(HttpMethod.POST)
+ .content(multiPart)
+ .send();
+
+ Assert.assertEquals(200, response.getStatus());
+ }
+
+ @Test
+ public void testFieldDeferred() throws Exception
+ {
+ String name = "field";
+ byte[] data = "Hello, World".getBytes(StandardCharsets.US_ASCII);
+ start(new AbstractMultiPartHandler()
+ {
+ @Override
+ protected void handle(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+ {
+ Collection<Part> parts = request.getParts();
+ Assert.assertEquals(1, parts.size());
+ Part part = parts.iterator().next();
+ Assert.assertEquals(name, part.getName());
+ Assert.assertEquals("text/plain", part.getContentType());
+ Assert.assertArrayEquals(data, IO.readBytes(part.getInputStream()));
+ }
+ });
+
+ MultiPartContentProvider multiPart = new MultiPartContentProvider();
+ DeferredContentProvider content = new DeferredContentProvider();
+ multiPart.addFieldPart(name, content, null);
+ multiPart.close();
+ CountDownLatch responseLatch = new CountDownLatch(1);
+ client.newRequest("localhost", connector.getLocalPort())
+ .scheme(scheme)
+ .method(HttpMethod.POST)
+ .content(multiPart)
+ .send(result ->
+ {
+ if (result.isSucceeded())
+ {
+ Assert.assertEquals(200, result.getResponse().getStatus());
+ responseLatch.countDown();
+ }
+ });
+
+ // Wait until the request has been sent.
+ Thread.sleep(1000);
+
+ // Provide the content.
+ content.offer(ByteBuffer.wrap(data));
+ content.close();
+
+ Assert.assertTrue(responseLatch.await(5, TimeUnit.SECONDS));
+ }
+
+ @Test
+ public void testFileFromInputStream() throws Exception
+ {
+ String name = "file";
+ String fileName = "upload.png";
+ String contentType = "image/png";
+ byte[] data = new byte[512];
+ new Random().nextBytes(data);
+ start(new AbstractMultiPartHandler()
+ {
+ @Override
+ protected void handle(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+ {
+ Collection<Part> parts = request.getParts();
+ Assert.assertEquals(1, parts.size());
+ Part part = parts.iterator().next();
+ Assert.assertEquals(name, part.getName());
+ Assert.assertEquals(contentType, part.getContentType());
+ Assert.assertEquals(fileName, part.getSubmittedFileName());
+ Assert.assertEquals(data.length, part.getSize());
+ Assert.assertArrayEquals(data, IO.readBytes(part.getInputStream()));
+ }
+ });
+
+ CountDownLatch closeLatch = new CountDownLatch(1);
+ MultiPartContentProvider multiPart = new MultiPartContentProvider();
+ InputStreamContentProvider content = new InputStreamContentProvider(new ByteArrayInputStream(data)
+ {
+ @Override
+ public void close() throws IOException
+ {
+ super.close();
+ closeLatch.countDown();
+ }
+ });
+ HttpFields fields = new HttpFields();
+ fields.put(HttpHeader.CONTENT_TYPE, contentType);
+ multiPart.addFilePart(name, fileName, content, fields);
+ multiPart.close();
+ ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
+ .scheme(scheme)
+ .method(HttpMethod.POST)
+ .content(multiPart)
+ .send();
+
+ Assert.assertTrue(closeLatch.await(5, TimeUnit.SECONDS));
+ Assert.assertEquals(200, response.getStatus());
+ }
+
+ @Test
+ public void testFileFromPath() throws Exception
+ {
+ // Prepare a file to upload.
+ String data = "multipart_test_\u20ac";
+ Path tmpDir = MavenTestingUtils.getTargetTestingPath();
+ Path tmpPath = Files.createTempFile(tmpDir, "multipart_", ".txt");
+ Charset encoding = StandardCharsets.UTF_8;
+ try (BufferedWriter writer = Files.newBufferedWriter(tmpPath, encoding, StandardOpenOption.CREATE))
+ {
+ writer.write(data);
+ }
+
+ String name = "file";
+ String contentType = "text/plain; charset=" + encoding.name();
+ start(new AbstractMultiPartHandler()
+ {
+ @Override
+ protected void handle(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+ {
+ Collection<Part> parts = request.getParts();
+ Assert.assertEquals(1, parts.size());
+ Part part = parts.iterator().next();
+ Assert.assertEquals(name, part.getName());
+ Assert.assertEquals(contentType, part.getContentType());
+ Assert.assertEquals(tmpPath.getFileName().toString(), part.getSubmittedFileName());
+ Assert.assertEquals(Files.size(tmpPath), part.getSize());
+ Assert.assertEquals(data, IO.toString(part.getInputStream(), encoding));
+ }
+ });
+
+ MultiPartContentProvider multiPart = new MultiPartContentProvider();
+ ContentProvider content = new PathContentProvider(contentType, tmpPath);
+ multiPart.addFilePart(name, tmpPath.getFileName().toString(), content, null);
+ multiPart.close();
+ ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
+ .scheme(scheme)
+ .method(HttpMethod.POST)
+ .content(multiPart)
+ .send();
+
+ Assert.assertEquals(200, response.getStatus());
+
+ Files.delete(tmpPath);
+ }
+
+ @Test
+ public void testFieldWithFile() throws Exception
+ {
+ // Prepare a file to upload.
+ byte[] data = new byte[1024];
+ new Random().nextBytes(data);
+ Path tmpDir = MavenTestingUtils.getTargetTestingPath();
+ Path tmpPath = Files.createTempFile(tmpDir, "multipart_", ".txt");
+ try (OutputStream output = Files.newOutputStream(tmpPath, StandardOpenOption.CREATE))
+ {
+ output.write(data);
+ }
+
+ String field = "field";
+ String value = "\u20ac";
+ String fileField = "file";
+ Charset encoding = StandardCharsets.UTF_8;
+ String contentType = "text/plain;charset=" + encoding.name();
+ String headerName = "foo";
+ String headerValue = "bar";
+ start(new AbstractMultiPartHandler()
+ {
+ @Override
+ protected void handle(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+ {
+ List<Part> parts = new ArrayList<>(request.getParts());
+ Assert.assertEquals(2, parts.size());
+ Part fieldPart = parts.get(0);
+ Part filePart = parts.get(1);
+ if (!field.equals(fieldPart.getName()))
+ {
+ Part swap = filePart;
+ filePart = fieldPart;
+ fieldPart = swap;
+ }
+
+ Assert.assertEquals(field, fieldPart.getName());
+ Assert.assertEquals(contentType, fieldPart.getContentType());
+ Assert.assertEquals(value, IO.toString(fieldPart.getInputStream(), encoding));
+ Assert.assertEquals(headerValue, fieldPart.getHeader(headerName));
+
+ Assert.assertEquals(fileField, filePart.getName());
+ Assert.assertEquals("application/octet-stream", filePart.getContentType());
+ Assert.assertEquals(tmpPath.getFileName().toString(), filePart.getSubmittedFileName());
+ Assert.assertEquals(Files.size(tmpPath), filePart.getSize());
+ Assert.assertArrayEquals(data, IO.readBytes(filePart.getInputStream()));
+ }
+ });
+
+ MultiPartContentProvider multiPart = new MultiPartContentProvider();
+ HttpFields fields = new HttpFields();
+ fields.put(headerName, headerValue);
+ multiPart.addFieldPart(field, new StringContentProvider(value, encoding), fields);
+ multiPart.addFilePart(fileField, tmpPath.getFileName().toString(), new PathContentProvider(tmpPath), null);
+ multiPart.close();
+ ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
+ .scheme(scheme)
+ .method(HttpMethod.POST)
+ .content(multiPart)
+ .send();
+
+ Assert.assertEquals(200, response.getStatus());
+
+ Files.delete(tmpPath);
+ }
+
+ @Test
+ public void testFieldDeferredAndFileDeferred() throws Exception
+ {
+ String value = "text";
+ Charset encoding = StandardCharsets.US_ASCII;
+ byte[] fileData = new byte[1024];
+ new Random().nextBytes(fileData);
+ start(new AbstractMultiPartHandler()
+ {
+ @Override
+ protected void handle(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+ {
+ List<Part> parts = new ArrayList<>(request.getParts());
+ Assert.assertEquals(2, parts.size());
+ Part fieldPart = parts.get(0);
+ Part filePart = parts.get(1);
+ if (!"field".equals(fieldPart.getName()))
+ {
+ Part swap = filePart;
+ filePart = fieldPart;
+ fieldPart = swap;
+ }
+
+ Assert.assertEquals(value, IO.toString(fieldPart.getInputStream(), encoding));
+
+ Assert.assertEquals("file", filePart.getName());
+ Assert.assertEquals("application/octet-stream", filePart.getContentType());
+ Assert.assertEquals("fileName", filePart.getSubmittedFileName());
+ Assert.assertArrayEquals(fileData, IO.readBytes(filePart.getInputStream()));
+ }
+ });
+
+ MultiPartContentProvider multiPart = new MultiPartContentProvider();
+ DeferredContentProvider fieldContent = new DeferredContentProvider();
+ multiPart.addFieldPart("field", fieldContent, null);
+ DeferredContentProvider fileContent = new DeferredContentProvider();
+ multiPart.addFilePart("file", "fileName", fileContent, null);
+ CountDownLatch responseLatch = new CountDownLatch(1);
+ client.newRequest("localhost", connector.getLocalPort())
+ .scheme(scheme)
+ .method(HttpMethod.POST)
+ .content(multiPart)
+ .send(result ->
+ {
+ if (result.isSucceeded())
+ {
+ Assert.assertEquals(200, result.getResponse().getStatus());
+ responseLatch.countDown();
+ }
+ });
+
+ // Wait until the request has been sent.
+ Thread.sleep(1000);
+
+ // Provide the content, in reversed part order.
+ fileContent.offer(ByteBuffer.wrap(fileData));
+ fileContent.close();
+
+ Thread.sleep(1000);
+
+ fieldContent.offer(encoding.encode(value));
+ fieldContent.close();
+
+ multiPart.close();
+
+ Assert.assertTrue(responseLatch.await(5, TimeUnit.SECONDS));
+ }
+
+ private static abstract class AbstractMultiPartHandler extends AbstractHandler
+ {
+ @Override
+ public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+ {
+ baseRequest.setHandled(true);
+ File tmpDir = MavenTestingUtils.getTargetTestingDir();
+ request.setAttribute(Request.__MULTIPART_CONFIG_ELEMENT, new MultipartConfigElement(tmpDir.getAbsolutePath()));
+ handle(request, response);
+ }
+
+ protected abstract void handle(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException;
+ }
+}
diff --git a/jetty-client/src/test/resources/jetty-logging.properties b/jetty-client/src/test/resources/jetty-logging.properties
index 1c19e5331e..5f8794e83f 100644
--- a/jetty-client/src/test/resources/jetty-logging.properties
+++ b/jetty-client/src/test/resources/jetty-logging.properties
@@ -1,3 +1,4 @@
org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
#org.eclipse.jetty.LEVEL=DEBUG
#org.eclipse.jetty.client.LEVEL=DEBUG
+#org.eclipse.jetty.io.ChannelEndPoint.LEVEL=DEBUG \ No newline at end of file
diff --git a/jetty-continuation/pom.xml b/jetty-continuation/pom.xml
index ecb449e63a..edd16b1f70 100644
--- a/jetty-continuation/pom.xml
+++ b/jetty-continuation/pom.xml
@@ -2,7 +2,7 @@
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId>
- <version>9.3.4-SNAPSHOT</version>
+ <version>9.4.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jetty-continuation</artifactId>
diff --git a/jetty-deploy/pom.xml b/jetty-deploy/pom.xml
index 08169311d6..9f94d9e73b 100644
--- a/jetty-deploy/pom.xml
+++ b/jetty-deploy/pom.xml
@@ -2,7 +2,7 @@
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId>
- <version>9.3.4-SNAPSHOT</version>
+ <version>9.4.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jetty-deploy</artifactId>
@@ -15,24 +15,6 @@
<build>
<plugins>
<plugin>
- <groupId>org.apache.felix</groupId>
- <artifactId>maven-bundle-plugin</artifactId>
- <extensions>true</extensions>
- <executions>
- <execution>
- <goals>
- <goal>manifest</goal>
- </goals>
- <configuration>
- <instructions>
- <Import-Package>org.eclipse.jetty.jmx.*;resolution:=optional,*</Import-Package>
- <_nouses>true</_nouses>
- </instructions>
- </configuration>
- </execution>
- </executions>
- </plugin>
- <plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>findbugs-maven-plugin</artifactId>
<configuration>
diff --git a/jetty-deploy/src/main/config/modules/deploy.mod b/jetty-deploy/src/main/config/modules/deploy.mod
index f567a2090f..794868bfb4 100644
--- a/jetty-deploy/src/main/config/modules/deploy.mod
+++ b/jetty-deploy/src/main/config/modules/deploy.mod
@@ -1,6 +1,5 @@
-#
-# Deploy Feature
-#
+[description]
+Enables webapplication deployment from the webapps directory.
[depend]
webapp
diff --git a/jetty-start/src/main/java/org/eclipse/jetty/start/graph/NamePredicate.java b/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings/DebugListenerBinding.java
index db1755991d..cc8698cf50 100644
--- a/jetty-start/src/main/java/org/eclipse/jetty/start/graph/NamePredicate.java
+++ b/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings/DebugListenerBinding.java
@@ -16,20 +16,38 @@
// ========================================================================
//
-package org.eclipse.jetty.start.graph;
+package org.eclipse.jetty.deploy.bindings;
-public class NamePredicate implements Predicate
+import org.eclipse.jetty.deploy.App;
+import org.eclipse.jetty.deploy.graph.Node;
+import org.eclipse.jetty.server.DebugListener;
+
+
+/** A Deployment binding that installs a DebugListener in all deployed contexts
+ */
+public class DebugListenerBinding extends DebugBinding
{
- private final String name;
+ final DebugListener _debugListener;
+
+ public DebugListenerBinding()
+ {
+ this(new DebugListener());
+ }
- public NamePredicate(String name)
+ public DebugListenerBinding(DebugListener debugListener)
{
- this.name = name;
+ super(new String[]{"deploying"});
+ _debugListener=debugListener;
}
- @Override
- public boolean match(Node<?> input)
+ public DebugListener getDebugListener()
{
- return input.getName().equalsIgnoreCase(this.name);
+ return _debugListener;
}
+
+ public void processBinding(Node node, App app) throws Exception
+ {
+ app.getContextHandler().addEventListener(_debugListener);
+ }
+
}
diff --git a/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings/GlobalWebappConfigBinding.java b/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings/GlobalWebappConfigBinding.java
index 0c4121896f..15289d5016 100644
--- a/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings/GlobalWebappConfigBinding.java
+++ b/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings/GlobalWebappConfigBinding.java
@@ -97,6 +97,8 @@ public class GlobalWebappConfigBinding implements AppLifeCycle.Binding
Resource resource = Resource.newResource(app.getOriginId());
File file = resource.getFile();
jettyXmlConfig.getIdMap().put("Server",app.getDeploymentManager().getServer());
+ jettyXmlConfig.getProperties().put("jetty.home",System.getProperty("jetty.home","."));
+ jettyXmlConfig.getProperties().put("jetty.base",System.getProperty("jetty.base","."));
jettyXmlConfig.getProperties().put("jetty.webapp",file.getCanonicalPath());
jettyXmlConfig.getProperties().put("jetty.webapps",file.getParentFile().getCanonicalPath());
diff --git a/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/WebAppProvider.java b/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/WebAppProvider.java
index 5cda93f2db..91f70c9539 100644
--- a/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/WebAppProvider.java
+++ b/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/WebAppProvider.java
@@ -257,7 +257,7 @@ public class WebAppProvider extends ScanningAppProvider
if (resource.exists() && FileID.isXmlFile(file))
{
- XmlConfiguration xmlc = new XmlConfiguration(resource.getURL())
+ XmlConfiguration xmlc = new XmlConfiguration(resource.getURI().toURL())
{
@Override
public void initializeDefaults(Object context)
diff --git a/jetty-distribution/pom.xml b/jetty-distribution/pom.xml
index bdda3fcbc6..9878feb1ce 100644
--- a/jetty-distribution/pom.xml
+++ b/jetty-distribution/pom.xml
@@ -3,7 +3,7 @@
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId>
- <version>9.3.4-SNAPSHOT</version>
+ <version>9.4.0-SNAPSHOT</version>
</parent>
<artifactId>jetty-distribution</artifactId>
<name>Jetty :: Distribution Assemblies</name>
@@ -441,8 +441,8 @@
<goal>copy-dependencies</goal>
</goals>
<configuration>
- <includeGroupIds>org.eclipse.jetty,org.eclipse.jetty.toolchain,org.mortbay.jasper,org.eclipse.jetty.orbit</includeGroupIds>
- <includeArtifactIds>apache-jsp,apache-el,org.eclipse.jdt.core</includeArtifactIds>
+ <includeGroupIds>org.eclipse.jetty,org.eclipse.jetty.toolchain,org.mortbay.jasper,org.eclipse.jetty.orbit,org.eclipse.jdt.core.compiler</includeGroupIds>
+ <includeArtifactIds>apache-jsp,apache-el,ecj</includeArtifactIds>
<includeTypes>jar</includeTypes>
<prependGroupId>true</prependGroupId>
<outputDirectory>${assembly-directory}/lib/apache-jsp</outputDirectory>
@@ -711,6 +711,11 @@
<version>${project.version}</version>
</dependency>
<dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-unixsocket</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
<groupId>org.eclipse.jetty.fcgi</groupId>
<artifactId>fcgi-server</artifactId>
<version>${project.version}</version>
@@ -779,6 +784,11 @@
<version>${project.version}</version>
</dependency>
<dependency>
+ <groupId>org.eclipse.jetty.gcloud</groupId>
+ <artifactId>gcloud-session-manager</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-nosql</artifactId>
<version>${project.version}</version>
diff --git a/jetty-distribution/src/main/resources/bin/jetty.sh b/jetty-distribution/src/main/resources/bin/jetty.sh
index 8cbb41ce17..21c2c979f4 100755
--- a/jetty-distribution/src/main/resources/bin/jetty.sh
+++ b/jetty-distribution/src/main/resources/bin/jetty.sh
@@ -166,7 +166,7 @@ then
ETC=$HOME/etc
fi
-for CONFIG in $ETC/default/${NAME}{,9} $HOME/.${NAME}rc; do
+for CONFIG in {/etc,~/etc}/default/${NAME}{,9} $HOME/.${NAME}rc; do
if [ -f "$CONFIG" ] ; then
readConfig "$CONFIG"
fi
@@ -445,7 +445,7 @@ case "$ACTION" in
exit 1
fi
- if [ -n "$JETTY_USER" ]
+ if [ -n "$JETTY_USER" ] && [ `whoami` != "$JETTY_USER" ]
then
unset SU_SHELL
if [ "$JETTY_SHELL" ]
@@ -457,11 +457,11 @@ case "$ACTION" in
chown "$JETTY_USER" "$JETTY_PID"
# FIXME: Broken solution: wordsplitting, pathname expansion, arbitrary command execution, etc.
su - "$JETTY_USER" $SU_SHELL -c "
- exec ${RUN_CMD[*]} start-log-file="$JETTY_LOGS/start.log" &
+ exec ${RUN_CMD[*]} start-log-file="$JETTY_LOGS/start.log" > /dev/null &
disown \$!
echo \$! > '$JETTY_PID'"
else
- "${RUN_CMD[@]}" &
+ "${RUN_CMD[@]}" > /dev/null &
disown $!
echo $! > "$JETTY_PID"
fi
diff --git a/jetty-distribution/src/main/resources/modules/hawtio.mod b/jetty-distribution/src/main/resources/modules/hawtio.mod
index f6d0d9d511..fcc34d1504 100644
--- a/jetty-distribution/src/main/resources/modules/hawtio.mod
+++ b/jetty-distribution/src/main/resources/modules/hawtio.mod
@@ -1,6 +1,5 @@
-#
-# Hawtio x module
-#
+[description]
+Deploys the Hawtio console as a webapplication.
[depend]
stats
diff --git a/jetty-distribution/src/main/resources/modules/jamon.mod b/jetty-distribution/src/main/resources/modules/jamon.mod
index 2d1f144d1b..77cc3d1e9d 100644
--- a/jetty-distribution/src/main/resources/modules/jamon.mod
+++ b/jetty-distribution/src/main/resources/modules/jamon.mod
@@ -1,6 +1,5 @@
-#
-# JAMon Jetty module
-#
+[description]
+Deploys the JAMon webapplication
[depend]
stats
diff --git a/jetty-distribution/src/main/resources/modules/jminix.mod b/jetty-distribution/src/main/resources/modules/jminix.mod
index 05788f0915..81a75c7350 100644
--- a/jetty-distribution/src/main/resources/modules/jminix.mod
+++ b/jetty-distribution/src/main/resources/modules/jminix.mod
@@ -1,6 +1,5 @@
-#
-# JaMON Jetty module
-#
+[description]
+Deploys the Jminix JMX Console within the server
[depend]
stats
diff --git a/jetty-distribution/src/main/resources/modules/jolokia.mod b/jetty-distribution/src/main/resources/modules/jolokia.mod
index da8ac8f8c2..efe8a59185 100644
--- a/jetty-distribution/src/main/resources/modules/jolokia.mod
+++ b/jetty-distribution/src/main/resources/modules/jolokia.mod
@@ -1,6 +1,5 @@
-#
-# Jolokia Jetty module
-#
+[description]
+Deploys the Jolokia console as a web application.
[depend]
stats
diff --git a/jetty-distribution/src/main/resources/modules/jsp.mod b/jetty-distribution/src/main/resources/modules/jsp.mod
index a16cc93dc9..2bc7ba8522 100644
--- a/jetty-distribution/src/main/resources/modules/jsp.mod
+++ b/jetty-distribution/src/main/resources/modules/jsp.mod
@@ -1,6 +1,5 @@
-#
-# Jetty JSP Module
-#
+[description]
+Enables JSP for all webapplications deployed on the server.
[depend]
servlet
diff --git a/jetty-distribution/src/main/resources/modules/jstl.mod b/jetty-distribution/src/main/resources/modules/jstl.mod
index efc310af6e..dedb2c052c 100644
--- a/jetty-distribution/src/main/resources/modules/jstl.mod
+++ b/jetty-distribution/src/main/resources/modules/jstl.mod
@@ -1,6 +1,5 @@
-#
-# Jetty JSTL Module
-#
+[description]
+Enables JSTL for all webapplications deployed on the server
[depend]
jsp
diff --git a/jetty-distribution/src/main/resources/modules/setuid.mod b/jetty-distribution/src/main/resources/modules/setuid.mod
index 41ef757e82..c1174ccba8 100644
--- a/jetty-distribution/src/main/resources/modules/setuid.mod
+++ b/jetty-distribution/src/main/resources/modules/setuid.mod
@@ -1,6 +1,7 @@
-#
-# Set UID Feature
-#
+[description]
+Enables the unix setUID configuration so that the server
+may be started as root to open privileged ports/files before
+changing to a restricted user (eg jetty).
[depend]
server
diff --git a/jetty-fcgi/fcgi-client/pom.xml b/jetty-fcgi/fcgi-client/pom.xml
index ecce8fd865..8cf9e9a68b 100644
--- a/jetty-fcgi/fcgi-client/pom.xml
+++ b/jetty-fcgi/fcgi-client/pom.xml
@@ -3,7 +3,7 @@
<parent>
<groupId>org.eclipse.jetty.fcgi</groupId>
<artifactId>fcgi-parent</artifactId>
- <version>9.3.4-SNAPSHOT</version>
+ <version>9.4.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
diff --git a/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpConnectionOverFCGI.java b/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpConnectionOverFCGI.java
index 4be9cbf196..4d9691de50 100644
--- a/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpConnectionOverFCGI.java
+++ b/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpConnectionOverFCGI.java
@@ -181,7 +181,7 @@ public class HttpConnectionOverFCGI extends AbstractConnection implements Connec
if (channels.isEmpty())
close();
else
- failAndClose(new EOFException());
+ failAndClose(new EOFException(String.valueOf(getEndPoint())));
}
@Override
diff --git a/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpDestinationOverFCGI.java b/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpDestinationOverFCGI.java
index f6adf480c4..2f3447d384 100644
--- a/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpDestinationOverFCGI.java
+++ b/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpDestinationOverFCGI.java
@@ -22,8 +22,9 @@ import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.HttpExchange;
import org.eclipse.jetty.client.Origin;
import org.eclipse.jetty.client.PoolingHttpDestination;
+import org.eclipse.jetty.client.api.Connection;
-public class HttpDestinationOverFCGI extends PoolingHttpDestination<HttpConnectionOverFCGI>
+public class HttpDestinationOverFCGI extends PoolingHttpDestination
{
public HttpDestinationOverFCGI(HttpClient client, Origin origin)
{
@@ -31,8 +32,8 @@ public class HttpDestinationOverFCGI extends PoolingHttpDestination<HttpConnecti
}
@Override
- protected void send(HttpConnectionOverFCGI connection, HttpExchange exchange)
+ protected void send(Connection connection, HttpExchange exchange)
{
- connection.send(exchange);
+ ((HttpConnectionOverFCGI)connection).send(exchange);
}
}
diff --git a/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/MultiplexHttpDestinationOverFCGI.java b/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/MultiplexHttpDestinationOverFCGI.java
index 77f2259d80..80bb63cc47 100644
--- a/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/MultiplexHttpDestinationOverFCGI.java
+++ b/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/MultiplexHttpDestinationOverFCGI.java
@@ -22,8 +22,9 @@ import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.HttpExchange;
import org.eclipse.jetty.client.MultiplexHttpDestination;
import org.eclipse.jetty.client.Origin;
+import org.eclipse.jetty.client.api.Connection;
-public class MultiplexHttpDestinationOverFCGI extends MultiplexHttpDestination<HttpConnectionOverFCGI>
+public class MultiplexHttpDestinationOverFCGI extends MultiplexHttpDestination
{
public MultiplexHttpDestinationOverFCGI(HttpClient client, Origin origin)
{
@@ -31,8 +32,8 @@ public class MultiplexHttpDestinationOverFCGI extends MultiplexHttpDestination<H
}
@Override
- protected void send(HttpConnectionOverFCGI connection, HttpExchange exchange)
+ protected void send(Connection connection, HttpExchange exchange)
{
- connection.send(exchange);
+ ((HttpConnectionOverFCGI)connection).send(exchange);
}
}
diff --git a/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/parser/BeginRequestContentParser.java b/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/parser/BeginRequestContentParser.java
index 2367fb65fb..912f4f8140 100644
--- a/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/parser/BeginRequestContentParser.java
+++ b/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/parser/BeginRequestContentParser.java
@@ -22,6 +22,16 @@ import java.nio.ByteBuffer;
import org.eclipse.jetty.fcgi.FCGI;
+/**
+ * <p>Parser for the BEGIN_REQUEST frame body.</p>
+ * <pre>
+ * struct begin_request_body {
+ * ushort role;
+ * ubyte flags;
+ * ubyte[5] reserved;
+ * }
+ * </pre>
+ */
public class BeginRequestContentParser extends ContentParser
{
private final ServerParser.Listener listener;
diff --git a/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/parser/EndRequestContentParser.java b/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/parser/EndRequestContentParser.java
index b8173bf494..dc07bd544e 100644
--- a/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/parser/EndRequestContentParser.java
+++ b/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/parser/EndRequestContentParser.java
@@ -20,6 +20,16 @@ package org.eclipse.jetty.fcgi.parser;
import java.nio.ByteBuffer;
+/**
+ * <p>Parser for the END_REQUEST frame body.</p>
+ * <pre>
+ * struct end_request_body {
+ * uint applicationStatus;
+ * ubyte protocolStatus;
+ * ubyte[3] reserved;
+ * }
+ * </pre>
+ */
public class EndRequestContentParser extends ContentParser
{
private final Parser.Listener listener;
@@ -80,7 +90,7 @@ public class EndRequestContentParser extends ContentParser
}
else
{
- state = State.APPLICATION_BYTES;
+ state = State.RESERVED_BYTES;
cursor = 0;
break;
}
@@ -88,7 +98,7 @@ public class EndRequestContentParser extends ContentParser
case RESERVED_BYTES:
{
buffer.get();
- if (++cursor == 0)
+ if (++cursor == 3)
{
onEnd();
reset();
diff --git a/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/parser/HeaderParser.java b/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/parser/HeaderParser.java
index 078105a9f3..7d43112569 100644
--- a/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/parser/HeaderParser.java
+++ b/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/parser/HeaderParser.java
@@ -21,9 +21,28 @@ package org.eclipse.jetty.fcgi.parser;
import java.nio.ByteBuffer;
import org.eclipse.jetty.fcgi.FCGI;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+/**
+ * <p>Parser for FastCGI frame headers.</p>
+ * <pre>
+ * struct frame_header {
+ * ubyte version;
+ * ubyte type;
+ * ushort requestId;
+ * ushort contentLength;
+ * ubyte paddingLength;
+ * ubyte reserved;
+ * }
+ * </pre>
+ *
+ * @see Parser
+ */
public class HeaderParser
{
+ private static final Logger LOG = Log.getLogger(Parser.class);
+
private State state = State.VERSION;
private int cursor;
private int version;
@@ -109,6 +128,8 @@ public class HeaderParser
case RESERVED:
{
buffer.get();
+ if (LOG.isDebugEnabled())
+ LOG.debug("Parsed request {} header {} length={}", getRequest(), getFrameType(), getContentLength());
return true;
}
default:
diff --git a/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/parser/ParamsContentParser.java b/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/parser/ParamsContentParser.java
index 4678ad5ebe..dcf34fefc8 100644
--- a/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/parser/ParamsContentParser.java
+++ b/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/parser/ParamsContentParser.java
@@ -20,11 +20,44 @@ package org.eclipse.jetty.fcgi.parser;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
+/**
+ * <p>Parser for the PARAMS frame body.</p>
+ * <pre>
+ * struct small_name_small_value_params_body {
+ * ubyte nameLength;
+ * ubyte valueLength;
+ * ubyte[] nameBytes;
+ * ubyte[] valueBytes;
+ * }
+ *
+ * struct small_name_large_value_params_body {
+ * ubyte nameLength;
+ * uint valueLength;
+ * ubyte[] nameBytes;
+ * ubyte[] valueBytes;
+ * }
+ *
+ * struct large_name_small_value_params_body {
+ * uint nameLength;
+ * ubyte valueLength;
+ * ubyte[] nameBytes;
+ * ubyte[] valueBytes;
+ * }
+ *
+ * struct large_name_large_value_params_body {
+ * uint nameLength;
+ * uint valueLength;
+ * ubyte[] nameBytes;
+ * ubyte[] valueBytes;
+ * }
+ * </pre>
+ */
public class ParamsContentParser extends ContentParser
{
private static final Logger LOG = Log.getLogger(ParamsContentParser.class);
@@ -179,7 +212,7 @@ public class ParamsContentParser extends ContentParser
}
case PARAM:
{
- Charset utf8 = Charset.forName("UTF-8");
+ Charset utf8 = StandardCharsets.UTF_8;
onParam(new String(nameBytes, utf8), new String(valueBytes, utf8));
partialReset();
if (length == 0)
diff --git a/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/parser/Parser.java b/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/parser/Parser.java
index cb86d660a6..be2ac480c6 100644
--- a/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/parser/Parser.java
+++ b/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/parser/Parser.java
@@ -22,9 +22,33 @@ import java.nio.ByteBuffer;
import org.eclipse.jetty.fcgi.FCGI;
import org.eclipse.jetty.http.HttpField;
-
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+/**
+ * <p>The FastCGI protocol exchanges <em>frames</em>.</p>
+ * <pre>
+ * struct frame {
+ * ubyte version;
+ * ubyte type;
+ * ushort requestId;
+ * ushort contentLength;
+ * ubyte paddingLength;
+ * ubyte reserved;
+ * ubyte[] content;
+ * ubyte[] padding;
+ * }
+ * </pre>
+ * <p>Depending on the {@code type}, the content may have a different format,
+ * so there are specialized content parsers.</p>
+ *
+ * @see HeaderParser
+ * @see ContentParser
+ */
public abstract class Parser
{
+ private static final Logger LOG = Log.getLogger(Parser.class);
+
protected final HeaderParser headerParser = new HeaderParser();
private State state = State.HEADER;
private int padding;
@@ -56,6 +80,9 @@ public abstract class Parser
else
{
ContentParser.Result result = contentParser.parse(buffer);
+ if (LOG.isDebugEnabled())
+ LOG.debug("Parsed request {} content {} result={}", headerParser.getRequest(), headerParser.getFrameType(), result);
+
if (result == ContentParser.Result.PENDING)
{
// Not enough data, signal to read/parse more.
diff --git a/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/parser/ResponseContentParser.java b/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/parser/ResponseContentParser.java
index a31ad2d523..6d0fefae64 100644
--- a/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/parser/ResponseContentParser.java
+++ b/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/parser/ResponseContentParser.java
@@ -18,11 +18,13 @@
package org.eclipse.jetty.fcgi.parser;
+import java.io.EOFException;
import java.nio.ByteBuffer;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.eclipse.jetty.fcgi.FCGI;
+import org.eclipse.jetty.http.BadMessageException;
import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpHeader;
@@ -32,6 +34,14 @@ import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
+/**
+ * <p>The parser for STDOUT type frame bodies.</p>
+ * <p>STDOUT frame bodies contain both the HTTP headers (but not the response line)
+ * and the HTTP content (either Content-Length delimited or chunked).</p>
+ * <p>For this reason, a special HTTP parser is used to parse the frames body.
+ * This special HTTP parser is configured to skip the response line, and to
+ * parse HTTP headers and HTTP content.</p>
+ */
public class ResponseContentParser extends StreamContentParser
{
private static final Logger LOG = Log.getLogger(ResponseContentParser.class);
@@ -89,12 +99,12 @@ public class ResponseContentParser extends StreamContentParser
public boolean parse(ByteBuffer buffer)
{
- if (LOG.isDebugEnabled())
- LOG.debug("Response {} {} content {} {}", request, FCGI.StreamType.STD_OUT, state, buffer);
-
int remaining = buffer.remaining();
while (remaining > 0)
{
+ if (LOG.isDebugEnabled())
+ LOG.debug("Response {} {}, state {} {}", request, FCGI.StreamType.STD_OUT, state, buffer);
+
switch (state)
{
case HEADERS:
@@ -245,12 +255,12 @@ public class ResponseContentParser extends StreamContentParser
{
if (!seenResponseCode)
{
- // No Status header but we have other headers, assume 200 OK
+ // No Status header but we have other headers, assume 200 OK.
notifyBegin(200, "OK");
notifyHeaders(fields);
}
notifyHeaders();
- // Return from parsing so that we can parse the content
+ // Return from HTTP parsing so that we can parse the content.
return true;
}
@@ -277,21 +287,34 @@ public class ResponseContentParser extends StreamContentParser
@Override
public boolean messageComplete()
{
- // Return from parsing so that we can parse the next headers or the raw content.
- // No need to notify the listener because it will be done by FCGI_END_REQUEST.
- return true;
+ // No need to notify the end of the response to the
+ // listener because it will be done by FCGI_END_REQUEST.
+ return false;
}
@Override
public void earlyEOF()
{
- // TODO
+ fail(new EOFException());
}
@Override
public void badMessage(int status, String reason)
{
- // TODO
+ fail(new BadMessageException(status, reason));
+ }
+
+ protected void fail(Throwable failure)
+ {
+ try
+ {
+ listener.onFailure(request, failure);
+ }
+ catch (Throwable x)
+ {
+ if (LOG.isDebugEnabled())
+ LOG.debug("Exception while invoking listener " + listener, x);
+ }
}
}
diff --git a/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/parser/StreamContentParser.java b/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/parser/StreamContentParser.java
index a341013c2e..70602a626a 100644
--- a/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/parser/StreamContentParser.java
+++ b/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/parser/StreamContentParser.java
@@ -24,6 +24,10 @@ import org.eclipse.jetty.fcgi.FCGI;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
+/**
+ * <p>A stream content parser parses frame bodies of type STDIN, STDOUT and STDERR.</p>
+ * <p>STDOUT frame bodies are handled specially by {@link ResponseContentParser}.
+ */
public class StreamContentParser extends ContentParser
{
private static final Logger LOG = Log.getLogger(StreamContentParser.class);
diff --git a/jetty-fcgi/fcgi-server/pom.xml b/jetty-fcgi/fcgi-server/pom.xml
index cbad4c07f2..271ef70033 100644
--- a/jetty-fcgi/fcgi-server/pom.xml
+++ b/jetty-fcgi/fcgi-server/pom.xml
@@ -3,7 +3,7 @@
<parent>
<groupId>org.eclipse.jetty.fcgi</groupId>
<artifactId>fcgi-parent</artifactId>
- <version>9.3.4-SNAPSHOT</version>
+ <version>9.4.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
diff --git a/jetty-fcgi/fcgi-server/src/main/config/modules/fcgi.mod b/jetty-fcgi/fcgi-server/src/main/config/modules/fcgi.mod
index 14152d5f2b..6a4beaf1ab 100644
--- a/jetty-fcgi/fcgi-server/src/main/config/modules/fcgi.mod
+++ b/jetty-fcgi/fcgi-server/src/main/config/modules/fcgi.mod
@@ -1,6 +1,5 @@
-#
-# FastCGI Module
-#
+[description]
+Adds the FastCGI implementation to the classpath.
[depend]
servlet
diff --git a/jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/proxy/FastCGIProxyServlet.java b/jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/proxy/FastCGIProxyServlet.java
index d80124e6e5..8587042304 100644
--- a/jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/proxy/FastCGIProxyServlet.java
+++ b/jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/proxy/FastCGIProxyServlet.java
@@ -20,8 +20,11 @@ package org.eclipse.jetty.fcgi.server.proxy;
import java.net.URI;
import java.util.List;
+import java.util.TreeMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
@@ -32,6 +35,7 @@ import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.fcgi.FCGI;
import org.eclipse.jetty.fcgi.client.http.HttpClientTransportOverFCGI;
+import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpScheme;
@@ -66,6 +70,7 @@ public class FastCGIProxyServlet extends AsyncProxyServlet.Transparent
{
public static final String SCRIPT_ROOT_INIT_PARAM = "scriptRoot";
public static final String SCRIPT_PATTERN_INIT_PARAM = "scriptPattern";
+ public static final String ORIGINAL_URI_ATTRIBUTE_INIT_PARAM = "originalURIAttribute";
public static final String FASTCGI_HTTPS_INIT_PARAM = "fastCGI.HTTPS";
private static final String REMOTE_ADDR_ATTRIBUTE = FastCGIProxyServlet.class.getName() + ".remoteAddr";
@@ -77,6 +82,7 @@ public class FastCGIProxyServlet extends AsyncProxyServlet.Transparent
private static final String REQUEST_URI_ATTRIBUTE = FastCGIProxyServlet.class.getName() + ".requestURI";
private Pattern scriptPattern;
+ private String originalURIAttribute;
private boolean fcgiHTTPS;
@Override
@@ -89,6 +95,8 @@ public class FastCGIProxyServlet extends AsyncProxyServlet.Transparent
value = "(.+?\\.php)";
scriptPattern = Pattern.compile(value);
+ originalURIAttribute = getInitParameter(ORIGINAL_URI_ATTRIBUTE_INIT_PARAM);
+
fcgiHTTPS = Boolean.parseBoolean(getInitParameter(FASTCGI_HTTPS_INIT_PARAM));
}
@@ -110,24 +118,33 @@ public class FastCGIProxyServlet extends AsyncProxyServlet.Transparent
proxyRequest.attribute(SERVER_NAME_ATTRIBUTE, request.getServerName());
proxyRequest.attribute(SERVER_ADDR_ATTRIBUTE, request.getLocalAddr());
proxyRequest.attribute(SERVER_PORT_ATTRIBUTE, String.valueOf(request.getLocalPort()));
-
proxyRequest.attribute(SCHEME_ATTRIBUTE, request.getScheme());
- // If we are forwarded or included, retain the original request URI.
- String originalPath = (String)request.getAttribute(RequestDispatcher.FORWARD_REQUEST_URI);
- String originalQuery = (String)request.getAttribute(RequestDispatcher.FORWARD_QUERY_STRING);
- if (originalPath == null)
+ // Has the original URI been rewritten ?
+ String originalURI = null;
+ if (originalURIAttribute != null)
+ originalURI = (String)request.getAttribute(originalURIAttribute);
+
+ if (originalURI == null)
{
- originalPath = (String)request.getAttribute(RequestDispatcher.INCLUDE_REQUEST_URI);
- originalQuery = (String)request.getAttribute(RequestDispatcher.INCLUDE_QUERY_STRING);
+ // If we are forwarded or included, retain the original request URI.
+ String originalPath = (String)request.getAttribute(RequestDispatcher.FORWARD_REQUEST_URI);
+ String originalQuery = (String)request.getAttribute(RequestDispatcher.FORWARD_QUERY_STRING);
+ if (originalPath == null)
+ {
+ originalPath = (String)request.getAttribute(RequestDispatcher.INCLUDE_REQUEST_URI);
+ originalQuery = (String)request.getAttribute(RequestDispatcher.INCLUDE_QUERY_STRING);
+ }
+ if (originalPath != null)
+ {
+ originalURI = originalPath;
+ if (originalQuery != null)
+ originalURI += "?" + originalQuery;
+ }
}
- if (originalPath != null)
- {
- String originalURI = originalPath;
- if (originalQuery != null)
- originalURI += "?" + originalQuery;
+
+ if (originalURI != null)
proxyRequest.attribute(REQUEST_URI_ATTRIBUTE, originalURI);
- }
// If the Host header is missing, add it.
if (!proxyRequest.getHeaders().containsKey(HttpHeader.HOST.asString()))
@@ -212,6 +229,16 @@ public class FastCGIProxyServlet extends AsyncProxyServlet.Transparent
{
super.customize(request, fastCGIHeaders);
customizeFastCGIHeaders(request, fastCGIHeaders);
+ if (_log.isDebugEnabled())
+ {
+ TreeMap<String, String> fcgi = new TreeMap<>();
+ for (HttpField field : fastCGIHeaders)
+ fcgi.put(field.getName(), field.getValue());
+ String eol = System.lineSeparator();
+ _log.debug("FastCGI variables{}{}", eol, fcgi.entrySet().stream()
+ .map(entry -> String.format("%s: %s", entry.getKey(), entry.getValue()))
+ .collect(Collectors.joining(eol)));
+ }
}
}
}
diff --git a/jetty-fcgi/fcgi-server/src/test/java/org/eclipse/jetty/fcgi/server/AbstractHttpClientServerTest.java b/jetty-fcgi/fcgi-server/src/test/java/org/eclipse/jetty/fcgi/server/AbstractHttpClientServerTest.java
index 9c83445108..35b75f5dda 100644
--- a/jetty-fcgi/fcgi-server/src/test/java/org/eclipse/jetty/fcgi/server/AbstractHttpClientServerTest.java
+++ b/jetty-fcgi/fcgi-server/src/test/java/org/eclipse/jetty/fcgi/server/AbstractHttpClientServerTest.java
@@ -18,12 +18,9 @@
package org.eclipse.jetty.fcgi.server;
-import static org.hamcrest.Matchers.is;
-import static org.junit.Assert.assertThat;
-
import java.util.concurrent.atomic.AtomicLong;
-import org.eclipse.jetty.client.ConnectionPool;
+import org.eclipse.jetty.client.DuplexConnectionPool;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.HttpDestination;
import org.eclipse.jetty.client.LeakTrackingConnectionPool;
@@ -40,9 +37,12 @@ import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.toolchain.test.TestTracker;
import org.eclipse.jetty.util.LeakDetector;
import org.eclipse.jetty.util.thread.QueuedThreadPool;
+import org.hamcrest.Matchers;
import org.junit.After;
import org.junit.Rule;
+import static org.junit.Assert.assertThat;
+
public abstract class AbstractHttpClientServerTest
{
@Rule
@@ -71,7 +71,7 @@ public abstract class AbstractHttpClientServerTest
QueuedThreadPool executor = new QueuedThreadPool();
executor.setName(executor.getName() + "-client");
-
+
client = new HttpClient(new HttpClientTransportOverFCGI(1, false, "")
{
@Override
@@ -80,7 +80,7 @@ public abstract class AbstractHttpClientServerTest
return new HttpDestinationOverFCGI(client, origin)
{
@Override
- protected ConnectionPool newConnectionPool(HttpClient client)
+ protected DuplexConnectionPool newConnectionPool(HttpClient client)
{
return new LeakTrackingConnectionPool(this, client.getMaxConnectionsPerDestination(), this)
{
@@ -105,15 +105,15 @@ public abstract class AbstractHttpClientServerTest
{
System.gc();
- assertThat("Server BufferPool - leaked acquires", serverBufferPool.getLeakedAcquires(), is(0L));
- assertThat("Server BufferPool - leaked releases", serverBufferPool.getLeakedReleases(), is(0L));
- assertThat("Server BufferPool - unreleased", serverBufferPool.getLeakedResources(), is(0L));
-
- assertThat("Client BufferPool - leaked acquires", clientBufferPool.getLeakedAcquires(), is(0L));
- assertThat("Client BufferPool - leaked releases", clientBufferPool.getLeakedReleases(), is(0L));
- assertThat("Client BufferPool - unreleased", clientBufferPool.getLeakedResources(), is(0L));
-
- assertThat("Connection Leaks", connectionLeaks.get(), is(0L));
+ assertThat("Server BufferPool - leaked acquires", serverBufferPool.getLeakedAcquires(), Matchers.is(0L));
+ assertThat("Server BufferPool - leaked releases", serverBufferPool.getLeakedReleases(), Matchers.is(0L));
+ assertThat("Server BufferPool - unreleased", serverBufferPool.getLeakedResources(), Matchers.is(0L));
+
+ assertThat("Client BufferPool - leaked acquires", clientBufferPool.getLeakedAcquires(), Matchers.is(0L));
+ assertThat("Client BufferPool - leaked releases", clientBufferPool.getLeakedReleases(), Matchers.is(0L));
+ assertThat("Client BufferPool - unreleased", clientBufferPool.getLeakedResources(), Matchers.is(0L));
+
+ assertThat("Connection Leaks", connectionLeaks.get(), Matchers.is(0L));
if (client != null)
client.stop();
diff --git a/jetty-fcgi/fcgi-server/src/test/java/org/eclipse/jetty/fcgi/server/HttpClientTest.java b/jetty-fcgi/fcgi-server/src/test/java/org/eclipse/jetty/fcgi/server/HttpClientTest.java
index cac16c8519..135dd7a74e 100644
--- a/jetty-fcgi/fcgi-server/src/test/java/org/eclipse/jetty/fcgi/server/HttpClientTest.java
+++ b/jetty-fcgi/fcgi-server/src/test/java/org/eclipse/jetty/fcgi/server/HttpClientTest.java
@@ -96,6 +96,35 @@ public class HttpClientTest extends AbstractHttpClientServerTest
}
@Test
+ public void testGETResponseWithBigContent() throws Exception
+ {
+ final byte[] data = new byte[16 * 1024 * 1024];
+ new Random().nextBytes(data);
+ start(new AbstractHandler()
+ {
+ @Override
+ public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+ {
+ // Setting the Content-Length triggers the HTTP
+ // content mode for response content parsing,
+ // otherwise the RAW content mode is used.
+ response.setContentLength(data.length);
+ response.getOutputStream().write(data);
+ baseRequest.setHandled(true);
+ }
+ });
+
+ Request request = client.newRequest(scheme + "://localhost:" + connector.getLocalPort());
+ FutureResponseListener listener = new FutureResponseListener(request, data.length);
+ request.send(listener);
+ ContentResponse response = listener.get(15, TimeUnit.SECONDS);
+ Assert.assertNotNull(response);
+ Assert.assertEquals(200, response.getStatus());
+ byte[] content = response.getContent();
+ Assert.assertArrayEquals(data, content);
+ }
+
+ @Test
public void testGETWithParametersResponseWithContent() throws Exception
{
final String paramName1 = "a";
@@ -420,7 +449,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
Assert.assertNotNull(response);
Assert.assertEquals(200, response.getStatus());
}
-
+
@Test
public void testConnectionIdleTimeout() throws Exception
{
diff --git a/jetty-fcgi/fcgi-server/src/test/java/org/eclipse/jetty/fcgi/server/proxy/FastCGIProxyServletTest.java b/jetty-fcgi/fcgi-server/src/test/java/org/eclipse/jetty/fcgi/server/proxy/FastCGIProxyServletTest.java
index a5923c7470..c6dae98fd9 100644
--- a/jetty-fcgi/fcgi-server/src/test/java/org/eclipse/jetty/fcgi/server/proxy/FastCGIProxyServletTest.java
+++ b/jetty-fcgi/fcgi-server/src/test/java/org/eclipse/jetty/fcgi/server/proxy/FastCGIProxyServletTest.java
@@ -19,11 +19,9 @@
package org.eclipse.jetty.fcgi.server.proxy;
import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.util.Arrays;
-import java.util.Collection;
import java.util.Random;
import java.util.concurrent.TimeUnit;
+
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
@@ -32,7 +30,6 @@ import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.client.api.Request;
-import org.eclipse.jetty.client.api.Response;
import org.eclipse.jetty.client.util.FutureResponseListener;
import org.eclipse.jetty.fcgi.server.ServerFCGIConnectionFactory;
import org.eclipse.jetty.server.HttpConfiguration;
@@ -40,7 +37,7 @@ import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
-import org.eclipse.jetty.util.Callback;
+import org.eclipse.jetty.util.thread.QueuedThreadPool;
import org.junit.After;
import org.junit.Assert;
import org.junit.Test;
@@ -51,9 +48,9 @@ import org.junit.runners.Parameterized;
public class FastCGIProxyServletTest
{
@Parameterized.Parameters
- public static Collection<Object[]> parameters()
+ public static Object[] parameters()
{
- return Arrays.asList(new Object[]{true}, new Object[]{false});
+ return new Object[]{true, false};
}
private final boolean sendStatus200;
@@ -69,7 +66,9 @@ public class FastCGIProxyServletTest
public void prepare(HttpServlet servlet) throws Exception
{
- server = new Server();
+ QueuedThreadPool serverThreads = new QueuedThreadPool();
+ serverThreads.setName("server");
+ server = new Server(serverThreads);
httpConnector = new ServerConnector(server);
server.addConnector(httpConnector);
@@ -89,14 +88,18 @@ public class FastCGIProxyServletTest
}
};
ServletHolder fcgiServletHolder = new ServletHolder(fcgiServlet);
- context.addServlet(fcgiServletHolder, "*.php");
+ fcgiServletHolder.setName("fcgi");
fcgiServletHolder.setInitParameter(FastCGIProxyServlet.SCRIPT_ROOT_INIT_PARAM, "/scriptRoot");
fcgiServletHolder.setInitParameter("proxyTo", "http://localhost");
fcgiServletHolder.setInitParameter(FastCGIProxyServlet.SCRIPT_PATTERN_INIT_PARAM, "(.+?\\.php)");
+ context.addServlet(fcgiServletHolder, "*.php");
context.addServlet(new ServletHolder(servlet), servletPath + "/*");
+ QueuedThreadPool clientThreads = new QueuedThreadPool();
+ clientThreads.setName("client");
client = new HttpClient();
+ client.setExecutor(clientThreads);
server.addBean(client);
server.start();
@@ -144,21 +147,17 @@ public class FastCGIProxyServletTest
});
Request request = client.newRequest("localhost", httpConnector.getLocalPort())
- .onResponseContentAsync(new Response.AsyncContentListener()
+ .onResponseContentAsync((response, content, callback) ->
{
- @Override
- public void onContent(Response response, ByteBuffer content, Callback callback)
+ try
+ {
+ if (delay > 0)
+ TimeUnit.MILLISECONDS.sleep(delay);
+ callback.succeeded();
+ }
+ catch (InterruptedException x)
{
- try
- {
- if (delay > 0)
- TimeUnit.MILLISECONDS.sleep(delay);
- callback.succeeded();
- }
- catch (InterruptedException x)
- {
- callback.failed(x);
- }
+ callback.failed(x);
}
})
.path(path);
diff --git a/jetty-fcgi/pom.xml b/jetty-fcgi/pom.xml
index cb0d2a093a..d3f278cff6 100644
--- a/jetty-fcgi/pom.xml
+++ b/jetty-fcgi/pom.xml
@@ -3,7 +3,7 @@
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId>
- <version>9.3.4-SNAPSHOT</version>
+ <version>9.4.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
diff --git a/jetty-gcloud/gcloud-session-manager/pom.xml b/jetty-gcloud/gcloud-session-manager/pom.xml
new file mode 100644
index 0000000000..5110e61e4d
--- /dev/null
+++ b/jetty-gcloud/gcloud-session-manager/pom.xml
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <parent>
+ <groupId>org.eclipse.jetty.gcloud</groupId>
+ <artifactId>gcloud-parent</artifactId>
+ <version>9.4.0-SNAPSHOT</version>
+ </parent>
+
+ <modelVersion>4.0.0</modelVersion>
+ <artifactId>gcloud-session-manager</artifactId>
+ <name>Jetty :: GCloud :: Session Manager</name>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-server</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.google.gcloud</groupId>
+ <artifactId>gcloud-java-datastore</artifactId>
+ <version>${gcloud.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-webapp</artifactId>
+ <version>${project.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty.websocket</groupId>
+ <artifactId>websocket-servlet</artifactId>
+ <version>${project.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty.websocket</groupId>
+ <artifactId>websocket-server</artifactId>
+ <version>${project.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty.toolchain</groupId>
+ <artifactId>jetty-test-helper</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <properties>
+ <bundle-symbolic-name>${project.groupId}.session</bundle-symbolic-name>
+ </properties>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <extensions>true</extensions>
+ <executions>
+ <execution>
+ <goals>
+ <goal>manifest</goal>
+ </goals>
+ <configuration>
+ <instructions>
+ <Export-Package>org.eclipse.jetty.gcloud.session.*;version="${parsedVersion.majorVersion}.${parsedVersion.minorVersion}.${parsedVersion.incrementalVersion}";</Export-Package>
+ </instructions>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
diff --git a/jetty-gcloud/gcloud-session-manager/src/main/config/etc/jetty-gcloud-sessions.xml b/jetty-gcloud/gcloud-session-manager/src/main/config/etc/jetty-gcloud-sessions.xml
new file mode 100644
index 0000000000..b1b9a844db
--- /dev/null
+++ b/jetty-gcloud/gcloud-session-manager/src/main/config/etc/jetty-gcloud-sessions.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0"?>
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
+
+<Configure id="Server" class="org.eclipse.jetty.server.Server">
+
+ <!-- ============================================================================================== -->
+ <!-- GCloud configuration. -->
+ <!-- Note: passwords can use jetty obfuscation. See -->
+ <!-- https://www.eclipse.org/jetty/documentation/current/configuring-security-secure-passwords.html -->
+ <!-- See your start.ini or gcloud-sessions.ini file for more configuration information. -->
+ <!-- ============================================================================================== -->
+ <New id="gconf" class="org.eclipse.jetty.gcloud.session.GCloudConfiguration">
+ <!-- Either set jetty.gcloudSession.projectId or use system property/env var DATASTORE_DATASET-->
+ <Set name="projectId"><Property name="jetty.gcloudSession.projectId"/></Set>
+ <!-- To contact remote gclouddatastore set the following properties in start.ini -->
+ <Set name="p12File"><Property name="jetty.gcloudSession.p12File"/></Set>
+ <Set name="serviceAccount"><Property name="jetty.gcloudSession.serviceAccount"/></Set>
+ <Set name="password"><Property name="jetty.gcloudSession.password"/></Set>
+ </New>
+
+
+ <!-- ===================================================================== -->
+ <!-- Configure a GCloudSessionIdManager -->
+ <!-- ===================================================================== -->
+ <Set name="sessionIdManager">
+ <New id="idMgr" class="org.eclipse.jetty.gcloud.session.GCloudSessionIdManager">
+ <Arg>
+ <Ref id="Server"/>
+ </Arg>
+ <Set name="workerName"><Property name="jetty.gcloudSession.workerName" default="node1"/></Set>
+ <Set name="config"><Ref id="gconf"/></Set>
+ </New>
+ </Set>
+
+</Configure>
diff --git a/jetty-gcloud/gcloud-session-manager/src/main/config/modules/gcloud-sessions.mod b/jetty-gcloud/gcloud-session-manager/src/main/config/modules/gcloud-sessions.mod
new file mode 100644
index 0000000000..3a6aaef03a
--- /dev/null
+++ b/jetty-gcloud/gcloud-session-manager/src/main/config/modules/gcloud-sessions.mod
@@ -0,0 +1,90 @@
+[description]
+Enables the GCloudDatastore Session Mananger module.
+
+[depend]
+annotations
+webapp
+
+[files]
+
+maven://com.google.gcloud/gcloud-java-datastore/0.0.7|lib/gcloud/gcloud-java-datastore-0.0.7.jar
+maven://com.google.gcloud/gcloud-java-core/0.0.7|lib/gcloud/gcloud-java-core-0.0.7.jar
+maven://com.google.auth/google-auth-library-credentials/0.1.0|lib/gcloud/google-auth-library-credentials-0.1.0.jar
+maven://com.google.auth/google-auth-library-oauth2-http/0.1.0|lib/gcloud/google-auth-library-oauth2-http-0.1.0.jar
+maven://com.google.http-client/google-http-client-jackson2/1.19.0|lib/gcloud/google-http-client-jackson2-1.19.0.jar
+maven://com.fasterxml.jackson.core/jackson-core/2.1.3|lib/gcloud/jackson-core-2.1.3.jar
+maven://com.google.http-client/google-http-client/1.20.0|lib/gcloud/google-http-client-1.20.0.jar
+maven://com.google.code.findbugs/jsr305/1.3.9|lib/gcloud/jsr305-1.3.9.jar
+maven://org.apache.httpcomponents/httpclient/4.0.1|lib/gcloud/httpclient-4.0.1.jar
+maven://org.apache.httpcomponents/httpcore/4.0.1|lib/gcloud/httpcore-4.0.1.jar
+maven://commons-logging/commons-logging/1.1.1|lib/gcloud/commons-logging-1.1.1.jar
+maven://commons-codec/commons-codec/1.3|lib/gcloud/commons-codec-1.3.jar
+maven://com.google.oauth-client/google-oauth-client/1.20.0|lib/gcloud//google-oauth-client-1.20.0.jar
+maven://com.google.guava/guava/18.0|lib/gcloud/guava-18.0.jar
+maven://com.google.api-client/google-api-client-appengine/1.20.0|lib/gcloud/google-api-client-appengine-1.20.0.jar
+maven://com.google.oauth-client/google-oauth-client-appengine/1.20.0|lib/gcloud/google-oauth-client-appengine-1.20.0.jar
+maven://com.google.oauth-client/google-oauth-client-servlet/1.20.0|lib/gcloud/google-oauth-client-servlet-1.20.0.jar
+maven://com.google.http-client/google-http-client-jdo/1.20.0|lib/gcloud/google-http-client-jdo-1.20.0.jar
+maven://com.google.api-client/google-api-client-servlet/1.20.0|lib/gcloud/google-api-client-servlet-1.20.0.jar
+maven://javax.jdo/jdo2-api/2.3-eb|lib/gcloud/jdo2-api-2.3-eb.jar
+maven://javax.transaction/transaction-api/1.1|lib/gcloud/transaction-api-1.1.jar
+maven://com.google.http-client/google-http-client-appengine/1.20.0|lib/gcloud/google-http-client-appengine-1.20.0.jar
+maven://com.google.http-client/google-http-client-jackson/1.20.0|lib/gcloud/google-http-client-jackson-1.20.0.jar
+maven://org.codehaus.jackson/jackson-core-asl/1.9.11|lib/gcloud/jackson-core-asl-1.9.11.jar
+maven://joda-time/joda-time/2.8.2|lib/gcloud/joda-time-2.8.2.jar
+maven://org.json/json/20090211|lib/gcloud/json-20090211.jar
+maven://com.google.apis/google-api-services-datastore-protobuf/v1beta2-rev1-2.1.2|lib/gcloud/google-api-services-datastore-protobuf-v1beta2-rev1-2.1.2.jar
+maven://com.google.protobuf/protobuf-java/2.5.0|lib/gcloud/protobuf-java-2.5.0.jar
+maven://com.google.http-client/google-http-client-protobuf/1.15.0-rc|lib/gcloud/google-http-client-protobuf-1.15.0-rc.jar
+maven://com.google.api-client/google-api-client/1.15.0-rc|lib/gcloud/google-api-client-1.15.0-rc.jar
+maven://com.google.apis/google-api-services-datastore/v1beta2-rev23-1.19.0|lib/gcloud/google-api-services-datastore-v1beta2-rev23-1.19.0.jar
+
+[lib]
+lib/gcloud-session-manager-${jetty.version}.jar
+lib/gcloud/*.jar
+
+[xml]
+etc/jetty-gcloud-sessions.xml
+
+[license]
+GCloudDatastore is an open source project hosted on Github and released under the Apache 2.0 license.
+https://github.com/GoogleCloudPlatform/gcloud-java
+http://www.apache.org/licenses/LICENSE-2.0.html
+
+[ini-template]
+## Unique identifier for this node in the cluster
+# jetty.gcloudSession.workerName=node1
+
+
+## GCloudDatastore Session config
+## If running inside Google cloud all configuration is provided by
+## environment variables and you do not need to set anything in this file.
+##
+## If running externally to Google:
+## To contact the remote gcloud datastore:
+## 1. set the DATASTORE_DATASET System property/environment variable to the name of your project
+## or alternatively set the jetty.gcloudSession.projectId property below.
+## 2. set the jetty.gcloudSession.p12File, jetty.gcloudSession.serviceAccount and
+## jetty.gcloudSession.password (supports obfuscation) below.
+##
+## To contact a local dev gcloud datastore server:
+## 1. set the DATASTORE_DATASET System property/environment variable to the name of your project.
+## 2. set the DATASTORE_HOST System property/environment variable to the url of the dev server
+## as described at https://cloud.google.com/datastore/docs/tools/devserver#setting_environment_variables
+
+## The gcloud projectId
+## Set this property to connect to remote gcloud datastore.
+## Or, set the DATASTORE_DATASET System property/env variable instead.
+#jetty.gcloudSession.projectId=
+
+## The p12 file associated with the project.
+## Set this property to connect to remote gcloud datastore
+#jetty.gcloudSession.p12File=
+
+## The serviceAccount for the Datastore.
+## Set this property to connect to to remote gcloud datastore
+#jetty.gcloudSession.serviceAccount=
+
+## The password (can be obfuscated).
+## Set this property to connect to remote gcloud datastore
+#jetty.gcloudSession.password=
diff --git a/jetty-gcloud/gcloud-session-manager/src/main/java/org/eclipse/jetty/gcloud/session/GCloudConfiguration.java b/jetty-gcloud/gcloud-session-manager/src/main/java/org/eclipse/jetty/gcloud/session/GCloudConfiguration.java
new file mode 100644
index 0000000000..3ce4acb47d
--- /dev/null
+++ b/jetty-gcloud/gcloud-session-manager/src/main/java/org/eclipse/jetty/gcloud/session/GCloudConfiguration.java
@@ -0,0 +1,201 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2015 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.gcloud.session;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.security.KeyStore;
+import java.security.PrivateKey;
+import java.util.Properties;
+
+import org.eclipse.jetty.util.security.Password;
+
+import com.google.gcloud.AuthCredentials;
+import com.google.gcloud.datastore.DatastoreOptions;
+
+
+
+/**
+ * GCloudConfiguration
+ *
+ *
+ */
+public class GCloudConfiguration
+{
+ public static final String PROJECT_ID = "projectId";
+ public static final String P12 = "p12";
+ public static final String PASSWORD = "password";
+ public static final String SERVICE_ACCOUNT = "serviceAccount";
+
+ private String _projectId;
+ private String _p12Filename;
+ private File _p12File;
+ private String _serviceAccount;
+ private String _passwordSet;
+ private String _password;
+ private AuthCredentials _authCredentials;
+ private DatastoreOptions _options;
+
+ /**
+ * Generate a configuration from a properties file
+ *
+ * @param propsFile
+ * @return
+ * @throws IOException
+ */
+ public static GCloudConfiguration fromFile(String propsFile)
+ throws IOException
+ {
+ if (propsFile == null)
+ throw new IllegalArgumentException ("Null properties file");
+
+ File f = new File(propsFile);
+ if (!f.exists())
+ throw new IllegalArgumentException("No such file "+f.getAbsolutePath());
+ Properties props = new Properties();
+ try (FileInputStream is=new FileInputStream(f))
+ {
+ props.load(is);
+ }
+
+ GCloudConfiguration config = new GCloudConfiguration();
+ config.setProjectId(props.getProperty(PROJECT_ID));
+ config.setP12File(props.getProperty(P12));
+ config.setPassword(props.getProperty(PASSWORD));
+ config.setServiceAccount(props.getProperty(SERVICE_ACCOUNT));
+ return config;
+ }
+
+
+
+ public String getProjectId()
+ {
+ return _projectId;
+ }
+
+ public File getP12File()
+ {
+ return _p12File;
+ }
+
+ public String getServiceAccount()
+ {
+ return _serviceAccount;
+ }
+
+
+ public void setProjectId(String projectId)
+ {
+ checkForModification();
+ _projectId = projectId;
+ }
+
+ public void setP12File (String file)
+ {
+ checkForModification();
+ _p12Filename = file;
+
+ }
+
+
+ public void setServiceAccount (String serviceAccount)
+ {
+ checkForModification();
+ _serviceAccount = serviceAccount;
+ }
+
+
+ public void setPassword (String pwd)
+ {
+ checkForModification();
+ _passwordSet = pwd;
+
+ }
+
+
+ public DatastoreOptions getDatastoreOptions ()
+ throws Exception
+ {
+ if (_options == null)
+ {
+ if (_passwordSet == null && _p12Filename == null && _serviceAccount == null)
+ {
+ //When no values are explicitly presented for auth info, we are either running
+ //1. inside GCE environment, in which case all auth info is derived from the environment
+ //2. outside the GCE environment, but using a local gce dev server, in which case you
+ // need to set the following 2 environment/system properties
+ // DATASTORE_HOST: eg http://localhost:9999 - this is the host and port of a local development server
+ // DATASTORE_DATASET: eg myProj - this is the name of your project
+ _options = DatastoreOptions.defaultInstance();
+ }
+ else
+ {
+ //When running externally to GCE, you need to provide
+ //explicit auth info. You can either set the projectId explicitly, or you can set the
+ //DATASTORE_DATASET env/system property
+ _p12File = new File(_p12Filename);
+ Password p = new Password(_passwordSet);
+ _password = p.toString();
+ _options = DatastoreOptions.builder()
+ .projectId(_projectId)
+ .authCredentials(getAuthCredentials())
+ .build();
+ }
+ }
+ return _options;
+ }
+
+ /**
+ * @return
+ * @throws Exception
+ */
+ public AuthCredentials getAuthCredentials()
+ throws Exception
+ {
+ if (_authCredentials == null)
+ {
+ if (_password == null)
+ throw new IllegalStateException("No password");
+
+ if (_p12File == null || !_p12File.exists())
+ throw new IllegalStateException("No p12 file: "+(_p12File==null?"null":_p12File.getAbsolutePath()));
+
+ if (_serviceAccount == null)
+ throw new IllegalStateException("No service account");
+
+ char[] pwdChars = _password.toCharArray();
+ KeyStore keystore = KeyStore.getInstance("PKCS12");
+ keystore.load(new FileInputStream(getP12File()), pwdChars);
+ PrivateKey privateKey = (PrivateKey) keystore.getKey("privatekey", pwdChars);
+ _authCredentials = AuthCredentials.createFor(getServiceAccount(), privateKey);
+ }
+ return _authCredentials;
+ }
+
+ /**
+ * @throws IllegalStateException
+ */
+ protected void checkForModification () throws IllegalStateException
+ {
+ if (_authCredentials != null || _options != null)
+ throw new IllegalStateException("Cannot modify auth configuration after datastore initialized");
+ }
+}
diff --git a/jetty-gcloud/gcloud-session-manager/src/main/java/org/eclipse/jetty/gcloud/session/GCloudSessionDataStore.java b/jetty-gcloud/gcloud-session-manager/src/main/java/org/eclipse/jetty/gcloud/session/GCloudSessionDataStore.java
new file mode 100644
index 0000000000..14ed51a4d6
--- /dev/null
+++ b/jetty-gcloud/gcloud-session-manager/src/main/java/org/eclipse/jetty/gcloud/session/GCloudSessionDataStore.java
@@ -0,0 +1,389 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2015 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.gcloud.session;
+
+import java.io.ByteArrayOutputStream;
+import java.io.ObjectOutputStream;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.eclipse.jetty.server.session.AbstractSessionDataStore;
+import org.eclipse.jetty.server.session.ContextId;
+import org.eclipse.jetty.server.session.SessionData;
+import org.eclipse.jetty.util.ClassLoadingObjectInputStream;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+import com.google.gcloud.datastore.Blob;
+import com.google.gcloud.datastore.Datastore;
+import com.google.gcloud.datastore.DatastoreFactory;
+import com.google.gcloud.datastore.Entity;
+import com.google.gcloud.datastore.Key;
+import com.google.gcloud.datastore.KeyFactory;
+import com.google.gcloud.datastore.ProjectionEntity;
+import com.google.gcloud.datastore.Query;
+import com.google.gcloud.datastore.QueryResults;
+import com.google.gcloud.datastore.StructuredQuery;
+import com.google.gcloud.datastore.StructuredQuery.CompositeFilter;
+import com.google.gcloud.datastore.StructuredQuery.KeyQueryBuilder;
+import com.google.gcloud.datastore.StructuredQuery.Projection;
+import com.google.gcloud.datastore.StructuredQuery.ProjectionEntityQueryBuilder;
+import com.google.gcloud.datastore.StructuredQuery.PropertyFilter;
+
+/**
+ * GCloudSessionDataStore
+ *
+ *
+ */
+public class GCloudSessionDataStore extends AbstractSessionDataStore
+{
+ private final static Logger LOG = Log.getLogger("org.eclipse.jetty.server.session");
+
+ public static final String ID = "id";
+ public static final String CONTEXTPATH = "contextPath";
+ public static final String VHOST = "vhost";
+ public static final String ACCESSED = "accessed";
+ public static final String LASTACCESSED = "lastAccessed";
+ public static final String CREATETIME = "createTime";
+ public static final String COOKIESETTIME = "cookieSetTime";
+ public static final String LASTNODE = "lastNode";
+ public static final String EXPIRY = "expiry";
+ public static final String MAXINACTIVE = "maxInactive";
+ public static final String ATTRIBUTES = "attributes";
+
+ public static final String KIND = "GCloudSession";
+ public static final int DEFAULT_MAX_QUERY_RESULTS = 100;
+
+ private GCloudConfiguration _config;
+ private Datastore _datastore;
+ private KeyFactory _keyFactory;
+ private int _maxResults = DEFAULT_MAX_QUERY_RESULTS;
+
+
+
+
+
+
+
+
+
+
+
+ /**
+ * @see org.eclipse.jetty.server.session.AbstractSessionDataStore#doStart()
+ */
+ @Override
+ protected void doStart() throws Exception
+ {
+
+ if (_config == null)
+ throw new IllegalStateException("No DataStore configuration");
+
+ _datastore = DatastoreFactory.instance().get(_config.getDatastoreOptions());
+ _keyFactory = _datastore.newKeyFactory().kind(KIND);
+
+ super.doStart();
+ }
+
+ /**
+ * @see org.eclipse.jetty.util.component.AbstractLifeCycle#doStop()
+ */
+ @Override
+ protected void doStop() throws Exception
+ {
+ super.doStop();
+ }
+
+
+ /**
+ * @param cfg
+ */
+ public void setGCloudConfiguration (GCloudConfiguration cfg)
+ {
+ _config = cfg;
+ }
+
+ /**
+ * @return
+ */
+ public GCloudConfiguration getGCloudConfiguration ()
+ {
+ return _config;
+ }
+
+
+ /**
+ * @return
+ */
+ public int getMaxResults()
+ {
+ return _maxResults;
+ }
+
+
+ /**
+ * @param maxResults
+ */
+ public void setMaxResults(int maxResults)
+ {
+ if (_maxResults <= 0)
+ _maxResults = DEFAULT_MAX_QUERY_RESULTS;
+ else
+ _maxResults = maxResults;
+ }
+
+
+
+ /**
+ * @see org.eclipse.jetty.server.session.SessionDataStore#load(java.lang.String)
+ */
+ @Override
+ public SessionData load(String id) throws Exception
+ {
+ if (LOG.isDebugEnabled()) LOG.debug("Loading session {} from DataStore", id);
+
+ Entity entity = _datastore.get(makeKey(id, _contextId));
+ if (entity == null)
+ {
+ if (LOG.isDebugEnabled()) LOG.debug("No session {} in DataStore ", id);
+ return null;
+ }
+ else
+ {
+ SessionData data = sessionFromEntity(entity);
+ return data;
+ }
+ }
+
+ /**
+ * @see org.eclipse.jetty.server.session.SessionDataStore#delete(java.lang.String)
+ */
+ @Override
+ public boolean delete(String id) throws Exception
+ {
+ if (LOG.isDebugEnabled()) LOG.debug("Removing session {} from DataStore", id);
+ _datastore.delete(makeKey(id, _contextId));
+ return true;
+ }
+
+ /**
+ * @see org.eclipse.jetty.server.session.SessionDataStore#getExpired(java.util.Set)
+ */
+ @Override
+ public Set<String> getExpired(Set<String> candidates)
+ {
+ long now = System.currentTimeMillis();
+ Set<String> expired = new HashSet<String>();
+
+ //get up to maxResult number of sessions that have expired
+ ProjectionEntityQueryBuilder pbuilder = Query.projectionEntityQueryBuilder();
+ pbuilder.addProjection(Projection.property(ID));
+ pbuilder.filter(CompositeFilter.and(PropertyFilter.gt(EXPIRY, 0), PropertyFilter.le(EXPIRY, now)));
+ pbuilder.limit(_maxResults);
+ pbuilder.kind(KIND);
+ StructuredQuery<ProjectionEntity> pquery = pbuilder.build();
+ QueryResults<ProjectionEntity> presults = _datastore.run(pquery);
+
+ while (presults.hasNext())
+ {
+ ProjectionEntity pe = presults.next();
+ String id = pe.getString(ID);
+ expired.add(id);
+ }
+
+ //reconcile against ids that the SessionStore thinks are expired
+ Set<String> tmp = new HashSet<String>(candidates);
+ tmp.removeAll(expired);
+ if (!tmp.isEmpty())
+ {
+ //sessionstore thinks these are expired, but they are either no
+ //longer in the db or not expired in the db, or we exceeded the
+ //number of records retrieved by the expiry query, so check them
+ //individually
+ for (String s:tmp)
+ {
+ try
+ {
+ KeyQueryBuilder kbuilder = Query.keyQueryBuilder();
+ kbuilder.filter(PropertyFilter.eq(ID, s));
+ kbuilder.kind(KIND);
+ StructuredQuery<Key> kq = kbuilder.build();
+ QueryResults<Key> kresults = _datastore.run(kq);
+ if (!kresults.hasNext())
+ expired.add(s); //not in db, can be expired
+ }
+ catch (Exception e)
+ {
+ LOG.warn(e);
+ }
+ }
+ }
+
+ return expired;
+
+ }
+
+
+
+ /**
+ * @see org.eclipse.jetty.server.session.AbstractSessionDataStore#doStore(java.lang.String, org.eclipse.jetty.server.session.SessionData, boolean)
+ */
+ @Override
+ public void doStore(String id, SessionData data, boolean isNew) throws Exception
+ {
+ if (LOG.isDebugEnabled()) LOG.debug("Writing session {} to DataStore", data.getId());
+
+ Entity entity = entityFromSession(data, makeKey(id, _contextId));
+ _datastore.put(entity);
+ }
+
+ /**
+ * Make a unique key for this session.
+ * As the same session id can be used across multiple contexts, to
+ * make it unique, the key must be composed of:
+ * <ol>
+ * <li>the id</li>
+ * <li>the context path</li>
+ * <li>the virtual hosts</li>
+ * </ol>
+ *
+ *
+ * @param session
+ * @return
+ */
+ private Key makeKey (String id, ContextId context)
+ {
+ String key = context.getCanonicalContextPath()+"_"+context.getVhost()+"_"+id;
+ return _keyFactory.newKey(key);
+ }
+
+
+ /**
+ * Generate a gcloud datastore Entity from SessionData
+ * @param session
+ * @param key
+ * @return
+ * @throws Exception
+ */
+ private Entity entityFromSession (SessionData session, Key key) throws Exception
+ {
+ if (session == null)
+ return null;
+
+ Entity entity = null;
+
+ //serialize the attribute map
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ ObjectOutputStream oos = new ObjectOutputStream(baos);
+ oos.writeObject(session.getAllAttributes());
+ oos.flush();
+
+ //turn a session into an entity
+ entity = Entity.builder(key)
+ .set(ID, session.getId())
+ .set(CONTEXTPATH, session.getContextPath())
+ .set(VHOST, session.getVhost())
+ .set(ACCESSED, session.getAccessed())
+ .set(LASTACCESSED, session.getLastAccessed())
+ .set(CREATETIME, session.getCreated())
+ .set(COOKIESETTIME, session.getCookieSet())
+ .set(LASTNODE,session.getLastNode())
+ .set(EXPIRY, session.getExpiry())
+ .set(MAXINACTIVE, session.getMaxInactiveMs())
+ .set(ATTRIBUTES, Blob.copyFrom(baos.toByteArray())).build();
+
+ return entity;
+ }
+
+ /**
+ * Generate SessionData from an Entity retrieved from gcloud datastore.
+ * @param entity
+ * @return
+ * @throws Exception
+ */
+ private SessionData sessionFromEntity (Entity entity) throws Exception
+ {
+ if (entity == null)
+ return null;
+
+ final AtomicReference<SessionData> reference = new AtomicReference<SessionData>();
+ final AtomicReference<Exception> exception = new AtomicReference<Exception>();
+ Runnable load = new Runnable()
+ {
+ public void run ()
+ {
+ try
+ {
+ //turn an Entity into a Session
+ String id = entity.getString(ID);
+ String contextPath = entity.getString(CONTEXTPATH);
+ String vhost = entity.getString(VHOST);
+ long accessed = entity.getLong(ACCESSED);
+ long lastAccessed = entity.getLong(LASTACCESSED);
+ long createTime = entity.getLong(CREATETIME);
+ long cookieSet = entity.getLong(COOKIESETTIME);
+ String lastNode = entity.getString(LASTNODE);
+ long expiry = entity.getLong(EXPIRY);
+ long maxInactive = entity.getLong(MAXINACTIVE);
+ Blob blob = (Blob) entity.getBlob(ATTRIBUTES);
+
+ System.err.println("Session "+id+" from Entity, expiry="+expiry);
+
+ SessionData session = newSessionData (id, createTime, accessed, lastAccessed, maxInactive);
+ session.setLastNode(lastNode);
+ session.setContextPath(contextPath);
+ session.setVhost(vhost);
+ session.setCookieSet(cookieSet);
+ session.setLastNode(lastNode);
+ session.setExpiry(expiry);
+ try (ClassLoadingObjectInputStream ois = new ClassLoadingObjectInputStream(blob.asInputStream()))
+ {
+ Object o = ois.readObject();
+ session.putAllAttributes((Map<String,Object>)o);
+ }
+ reference.set(session);
+ }
+ catch (Exception e)
+ {
+ exception.set(e);
+ }
+ }
+ };
+
+ load.run();
+
+ /* if (_context==null)
+ load.run();
+ else
+ _context.getContextHandler().handle(null,load);*/
+
+
+ if (exception.get() != null)
+ {
+ throw exception.get();
+ }
+
+ return reference.get();
+ }
+
+
+}
diff --git a/jetty-gcloud/gcloud-session-manager/src/main/java/org/eclipse/jetty/gcloud/session/GCloudSessionIdManager.java b/jetty-gcloud/gcloud-session-manager/src/main/java/org/eclipse/jetty/gcloud/session/GCloudSessionIdManager.java
new file mode 100644
index 0000000000..b3157c9114
--- /dev/null
+++ b/jetty-gcloud/gcloud-session-manager/src/main/java/org/eclipse/jetty/gcloud/session/GCloudSessionIdManager.java
@@ -0,0 +1,233 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2015 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.gcloud.session;
+
+import java.util.Random;
+
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.session.AbstractSessionIdManager;
+import org.eclipse.jetty.server.session.Session;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+import com.google.gcloud.datastore.Datastore;
+import com.google.gcloud.datastore.DatastoreFactory;
+import com.google.gcloud.datastore.Entity;
+import com.google.gcloud.datastore.Key;
+import com.google.gcloud.datastore.KeyFactory;
+
+
+
+/**
+ * GCloudSessionIdManager
+ *
+ *
+ *
+ */
+public class GCloudSessionIdManager extends AbstractSessionIdManager
+{
+ private final static Logger LOG = Log.getLogger("org.eclipse.jetty.server.session");
+ public static final int DEFAULT_IDLE_EXPIRY_MULTIPLE = 2;
+ public static final String KIND = "GCloudSessionId";
+ private Server _server;
+ private Datastore _datastore;
+ private KeyFactory _keyFactory;
+ private GCloudConfiguration _config;
+
+
+
+
+ /**
+ * @param server
+ */
+ public GCloudSessionIdManager(Server server)
+ {
+ super();
+ _server = server;
+ }
+
+ /**
+ * @param server
+ * @param random
+ */
+ public GCloudSessionIdManager(Server server, Random random)
+ {
+ super(random);
+ _server = server;
+ }
+
+
+
+ /**
+ * Start the id manager.
+ * @see org.eclipse.jetty.server.session.AbstractSessionIdManager#doStart()
+ */
+ @Override
+ protected void doStart() throws Exception
+ {
+ if (_config == null)
+ throw new IllegalStateException("No gcloud configuration specified");
+
+
+ _datastore = DatastoreFactory.instance().get(_config.getDatastoreOptions());
+ _keyFactory = _datastore.newKeyFactory().kind(KIND);
+
+ super.doStart();
+ }
+
+
+
+ /**
+ * Stop the id manager
+ * @see org.eclipse.jetty.server.session.AbstractSessionIdManager#doStop()
+ */
+ @Override
+ protected void doStop() throws Exception
+ {
+ super.doStop();
+ }
+
+
+
+
+
+
+ public GCloudConfiguration getConfig()
+ {
+ return _config;
+ }
+
+ public void setConfig(GCloudConfiguration config)
+ {
+ _config = config;
+ }
+
+
+
+
+
+
+ /**
+ * Ask the datastore if a particular id exists.
+ *
+ * @param id
+ * @return
+ */
+ protected boolean exists (String id)
+ {
+ if (_datastore == null)
+ throw new IllegalStateException ("No DataStore");
+ Key key = _keyFactory.newKey(id);
+ return _datastore.get(key) != null;
+ }
+
+
+ /**
+ * Put a session id into the cluster.
+ *
+ * @param id
+ */
+ protected void insert (String id)
+ {
+ if (_datastore == null)
+ throw new IllegalStateException ("No DataStore");
+
+ Entity entity = Entity.builder(makeKey(id))
+ .set("id", id).build();
+ _datastore.put(entity);
+ }
+
+
+
+
+ /**
+ * Remove a session id from the cluster.
+ *
+ * @param id
+ */
+ protected void delete (String id)
+ {
+ if (_datastore == null)
+ throw new IllegalStateException ("No DataStore");
+
+ _datastore.delete(makeKey(id));
+ }
+
+
+
+ /**
+ * Generate a unique key from the session id.
+ *
+ * @param id
+ * @return
+ */
+ protected Key makeKey (String id)
+ {
+ return _keyFactory.newKey(id);
+ }
+
+ /**
+ * @see org.eclipse.jetty.server.SessionIdManager#isIdInUse(java.lang.String)
+ */
+ @Override
+ public boolean isIdInUse(String id)
+ {
+ if (id == null)
+ return false;
+
+
+ //ask the cluster - this should also tickle the idle expiration timer on the sessionid entry
+ //keeping it valid
+ try
+ {
+ return exists(id);
+ }
+ catch (Exception e)
+ {
+ LOG.warn("Problem checking inUse for id="+id, e);
+ return false;
+ }
+
+ }
+
+ /**
+ * @see org.eclipse.jetty.server.SessionIdManager#useId(org.eclipse.jetty.server.session.Session)
+ */
+ @Override
+ public void useId(Session session)
+ {
+ if (session == null)
+ return;
+
+ //insert into the store
+ insert (session.getId());
+ }
+
+ /**
+ * @see org.eclipse.jetty.server.SessionIdManager#removeId(java.lang.String)
+ */
+ @Override
+ public void removeId(String id)
+ {
+ if (id == null)
+ return;
+
+ delete(id);
+ }
+}
diff --git a/jetty-gcloud/gcloud-session-manager/src/main/java/org/eclipse/jetty/gcloud/session/GCloudSessionManager.java b/jetty-gcloud/gcloud-session-manager/src/main/java/org/eclipse/jetty/gcloud/session/GCloudSessionManager.java
new file mode 100644
index 0000000000..42ed7612dd
--- /dev/null
+++ b/jetty-gcloud/gcloud-session-manager/src/main/java/org/eclipse/jetty/gcloud/session/GCloudSessionManager.java
@@ -0,0 +1,329 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2015 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.gcloud.session;
+
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.locks.ReentrantLock;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.eclipse.jetty.server.session.AbstractSessionStore;
+import org.eclipse.jetty.server.session.MemorySessionStore;
+import org.eclipse.jetty.server.session.SessionManager;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+
+
+/**
+ * GCloudSessionManager
+ *
+ *
+ */
+public class GCloudSessionManager extends SessionManager
+{
+ private final static Logger LOG = Log.getLogger("org.eclipse.jetty.server.session");
+
+
+
+
+
+ private GCloudSessionDataStore _sessionDataStore = null;
+
+
+
+
+/*
+
+ *//**
+ * Session
+ *
+ * Representation of a session in local memory.
+ *//*
+ public class Session extends MemSession
+ {
+
+ private ReentrantLock _lock = new ReentrantLock();
+
+
+ private long _lastSyncTime;
+
+ private AtomicInteger _activeThreads = new AtomicInteger(0);
+
+
+
+ protected Session (HttpServletRequest request)
+ {
+ _activeThreads.incrementAndGet(); //access will not be called on a freshly created session so increment here
+ }
+
+
+
+ *//**
+ * Called on entry to the session.
+ *
+ * @see org.eclipse.jetty.server.session.AbstractSession#access(long)
+ *//*
+ @Override
+ protected boolean access(long time)
+ {
+ if (LOG.isDebugEnabled())
+ LOG.debug("Access session({}) for context {} on worker {}", getId(), getContextPath(), getSessionIdManager().getWorkerName());
+ try
+ {
+
+ long now = System.currentTimeMillis();
+ //lock so that no other thread can call access or complete until the first one has refreshed the session object if necessary
+ _lock.lock();
+ //a request thread is entering
+ if (_activeThreads.incrementAndGet() == 1)
+ {
+ //if the first thread, check that the session in memory is not stale, if we're checking for stale sessions
+ if (getStaleIntervalSec() > 0 && (now - getLastSyncTime()) >= (getStaleIntervalSec() * 1000L))
+ {
+ if (LOG.isDebugEnabled())
+ LOG.debug("Acess session({}) for context {} on worker {} stale session. Reloading.", getId(), getContextPath(), getSessionIdManager().getWorkerName());
+ refresh();
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ LOG.warn(e);
+ }
+ finally
+ {
+ _lock.unlock();
+ }
+
+ if (super.access(time))
+ {
+ int maxInterval=getMaxInactiveInterval();
+ _expiryTime = (maxInterval <= 0 ? 0 : (time + maxInterval*1000L));
+ return true;
+ }
+ return false;
+ }
+
+
+ *//**
+ * Exit from session
+ * @see org.eclipse.jetty.server.session.AbstractSession#complete()
+ *//*
+ @Override
+ protected void complete()
+ {
+ super.complete();
+
+ //lock so that no other thread that might be calling access can proceed until this complete is done
+ _lock.lock();
+
+ try
+ {
+ //if this is the last request thread to be in the session
+ if (_activeThreads.decrementAndGet() == 0)
+ {
+ try
+ {
+ //an invalid session will already have been removed from the
+ //local session map and deleted from the cluster. If its valid save
+ //it to the cluster.
+ //TODO consider doing only periodic saves if only the last access
+ //time to the session changes
+ if (isValid())
+ {
+ //if session still valid && its dirty or stale or never been synced, write it to the cluster
+ //otherwise, we just keep the updated last access time in memory
+ if (_dirty || getLastSyncTime() == 0 || isStale(System.currentTimeMillis()))
+ {
+ willPassivate();
+ save(this);
+ didActivate();
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ LOG.warn("Problem saving session({})",getId(), e);
+ }
+ finally
+ {
+ _dirty = false;
+ }
+ }
+ }
+ finally
+ {
+ _lock.unlock();
+ }
+ }
+
+ *//** Test if the session is stale
+ * @param atTime
+ * @return
+ *//*
+ protected boolean isStale (long atTime)
+ {
+ return (getStaleIntervalSec() > 0) && (atTime - getLastSyncTime() >= (getStaleIntervalSec()*1000L));
+ }
+
+
+ *//**
+ * Reload the session from the cluster. If the node that
+ * last managed the session from the cluster is ourself,
+ * then the session does not need refreshing.
+ * NOTE: this method MUST be called with sufficient locks
+ * in place to prevent 2 or more concurrent threads from
+ * simultaneously updating the session.
+ *//*
+ private void refresh ()
+ throws Exception
+ {
+ //get fresh copy from the cluster
+ Session fresh = load(makeKey(getClusterId(), _context));
+
+ //if the session no longer exists, invalidate
+ if (fresh == null)
+ {
+ invalidate();
+ return;
+ }
+
+ //cluster copy assumed to be the same as we were the last
+ //node to manage it
+ if (fresh.getLastNode().equals(getLastNode()))
+ return;
+
+ setLastNode(getSessionIdManager().getWorkerName());
+
+ //prepare for refresh
+ willPassivate();
+
+ //if fresh has no attributes, remove them
+ if (fresh.getAttributes() == 0)
+ this.clearAttributes();
+ else
+ {
+ //reconcile attributes
+ for (String key:fresh.getAttributeMap().keySet())
+ {
+ Object freshvalue = fresh.getAttribute(key);
+
+ //session does not already contain this attribute, so bind it
+ if (getAttribute(key) == null)
+ {
+ doPutOrRemove(key,freshvalue);
+ bindValue(key,freshvalue);
+ }
+ else //session already contains this attribute, update its value
+ {
+ doPutOrRemove(key,freshvalue);
+ }
+
+ }
+ // cleanup, remove values from session, that don't exist in data anymore:
+ for (String key : getNames())
+ {
+ if (fresh.getAttribute(key) == null)
+ {
+ Object oldvalue = getAttribute(key);
+ doPutOrRemove(key,null);
+ unbindValue(key,oldvalue);
+ }
+ }
+ }
+ //finish refresh
+ didActivate();
+ }
+
+
+ public void swapId (String newId, String newNodeId)
+ {
+ //TODO probably synchronize rather than use the access/complete lock?
+ _lock.lock();
+ setClusterId(newId);
+ setNodeId(newNodeId);
+ _lock.unlock();
+ }
+
+
+ }
+
+*/
+
+
+ /**
+ *
+ */
+ public GCloudSessionManager()
+ {
+ _sessionDataStore = new GCloudSessionDataStore();
+ _sessionStore = new MemorySessionStore();
+ }
+
+
+
+
+ /**
+ * @return
+ */
+ public GCloudSessionDataStore getSessionDataStore()
+ {
+ return _sessionDataStore;
+ }
+
+
+
+
+
+ /**
+ * Start the session manager.
+ *
+ * @see org.eclipse.jetty.server.session.AbstractSessionManager#doStart()
+ */
+ @Override
+ public void doStart() throws Exception
+ {
+ ((AbstractSessionStore)_sessionStore).setSessionDataStore(_sessionDataStore);
+ super.doStart();
+ }
+
+
+ /**
+ * Stop the session manager.
+ *
+ * @see org.eclipse.jetty.server.session.AbstractSessionManager#doStop()
+ */
+ @Override
+ public void doStop() throws Exception
+ {
+ super.doStop();
+ }
+
+
+
+
+
+ protected void scavengeGCloudDataStore()
+ throws Exception
+ {
+
+
+ }
+}
diff --git a/jetty-gcloud/gcloud-session-manager/src/test/java/org/eclipse/jetty/gcloud/session/GCloudSessionTester.java b/jetty-gcloud/gcloud-session-manager/src/test/java/org/eclipse/jetty/gcloud/session/GCloudSessionTester.java
new file mode 100644
index 0000000000..fe596e24ba
--- /dev/null
+++ b/jetty-gcloud/gcloud-session-manager/src/test/java/org/eclipse/jetty/gcloud/session/GCloudSessionTester.java
@@ -0,0 +1,75 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2015 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.gcloud.session;
+
+
+
+
+import org.eclipse.jetty.security.HashLoginService;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.handler.AllowSymLinkAliasChecker;
+import org.eclipse.jetty.server.session.SessionHandler;
+import org.eclipse.jetty.webapp.WebAppContext;
+
+public class GCloudSessionTester
+{
+ public static void main( String[] args ) throws Exception
+ {
+ if (args.length < 4)
+ System.err.println("Usage: GCloudSessionTester projectid p12file password serviceaccount");
+
+ System.setProperty("org.eclipse.jetty.server.session.LEVEL", "DEBUG");
+
+ Server server = new Server(8080);
+ HashLoginService loginService = new HashLoginService();
+ loginService.setName( "Test Realm" );
+ loginService.setConfig( "../../jetty-distribution/target/distribution/demo-base/resources/realm.properties" );
+ server.addBean( loginService );
+
+ GCloudConfiguration config = new GCloudConfiguration();
+ config.setProjectId(args[0]);
+ config.setP12File(args[1]);
+ config.setPassword(args[2]);
+ config.setServiceAccount(args[3]);
+
+ GCloudSessionIdManager idmgr = new GCloudSessionIdManager(server);
+ idmgr.setConfig(config);
+ idmgr.setWorkerName("w1");
+ server.setSessionIdManager(idmgr);
+
+
+ WebAppContext webapp = new WebAppContext();
+ webapp.setContextPath("/");
+ webapp.setWar("../../jetty-distribution/target/distribution/demo-base/webapps/test.war");
+ webapp.addAliasCheck(new AllowSymLinkAliasChecker());
+ GCloudSessionManager mgr = new GCloudSessionManager();
+ mgr.setSessionIdManager(idmgr);
+ webapp.setSessionHandler(new SessionHandler(mgr));
+
+ // A WebAppContext is a ContextHandler as well so it needs to be set to
+ // the server so it is aware of where to send the appropriate requests.
+ server.setHandler(webapp);
+
+ // Start things up!
+ server.start();
+
+
+ server.join();
+ }
+}
diff --git a/jetty-gcloud/pom.xml b/jetty-gcloud/pom.xml
new file mode 100644
index 0000000000..75d9bad29d
--- /dev/null
+++ b/jetty-gcloud/pom.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <parent>
+ <artifactId>jetty-project</artifactId>
+ <groupId>org.eclipse.jetty</groupId>
+ <version>9.4.0-SNAPSHOT</version>
+ </parent>
+
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>org.eclipse.jetty.gcloud</groupId>
+ <artifactId>gcloud-parent</artifactId>
+ <packaging>pom</packaging>
+ <name>Jetty :: GCloud</name>
+
+ <properties>
+ <gcloud.version>0.0.8</gcloud.version>
+ </properties>
+
+ <modules>
+ <module>gcloud-session-manager</module>
+ </modules>
+
+</project>
diff --git a/jetty-http-spi/pom.xml b/jetty-http-spi/pom.xml
index b88be85af6..03b9cd780c 100644
--- a/jetty-http-spi/pom.xml
+++ b/jetty-http-spi/pom.xml
@@ -2,7 +2,7 @@
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId>
- <version>9.3.4-SNAPSHOT</version>
+ <version>9.4.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jetty-http-spi</artifactId>
diff --git a/jetty-http/pom.xml b/jetty-http/pom.xml
index 150c4b9f8d..8455a9d324 100644
--- a/jetty-http/pom.xml
+++ b/jetty-http/pom.xml
@@ -3,7 +3,7 @@
<parent>
<artifactId>jetty-project</artifactId>
<groupId>org.eclipse.jetty</groupId>
- <version>9.3.4-SNAPSHOT</version>
+ <version>9.4.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jetty-http</artifactId>
diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/GzipHttpContent.java b/jetty-http/src/main/java/org/eclipse/jetty/http/GzipHttpContent.java
new file mode 100644
index 0000000000..1354358def
--- /dev/null
+++ b/jetty-http/src/main/java/org/eclipse/jetty/http/GzipHttpContent.java
@@ -0,0 +1,188 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2015 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;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.nio.channels.ReadableByteChannel;
+import org.eclipse.jetty.http.MimeTypes.Type;
+
+import org.eclipse.jetty.util.resource.Resource;
+
+/* ------------------------------------------------------------ */
+public class GzipHttpContent implements HttpContent
+{
+ private final HttpContent _content;
+ private final HttpContent _contentGz;
+ public final static String ETAG_GZIP="--gzip";
+ public final static String ETAG_GZIP_QUOTE="--gzip\"";
+ public final static PreEncodedHttpField CONTENT_ENCODING_GZIP=new PreEncodedHttpField(HttpHeader.CONTENT_ENCODING,"gzip");
+
+ public static String removeGzipFromETag(String etag)
+ {
+ if (etag==null)
+ return null;
+ int i = etag.indexOf(ETAG_GZIP_QUOTE);
+ if (i<0)
+ return etag;
+ return etag.substring(0,i)+'"';
+ }
+
+ public GzipHttpContent(HttpContent content, HttpContent contentGz)
+ {
+ _content=content;
+ _contentGz=contentGz;
+ }
+
+ @Override
+ public int hashCode()
+ {
+ return _content.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj)
+ {
+ return _content.equals(obj);
+ }
+
+ @Override
+ public Resource getResource()
+ {
+ return _content.getResource();
+ }
+
+ @Override
+ public HttpField getETag()
+ {
+ return new HttpField(HttpHeader.ETAG,getETagValue());
+ }
+
+ @Override
+ public String getETagValue()
+ {
+ return _content.getResource().getWeakETag(ETAG_GZIP);
+ }
+
+ @Override
+ public HttpField getLastModified()
+ {
+ return _content.getLastModified();
+ }
+
+ @Override
+ public String getLastModifiedValue()
+ {
+ return _content.getLastModifiedValue();
+ }
+
+ @Override
+ public HttpField getContentType()
+ {
+ return _content.getContentType();
+ }
+
+ @Override
+ public String getContentTypeValue()
+ {
+ return _content.getContentTypeValue();
+ }
+
+ @Override
+ public HttpField getContentEncoding()
+ {
+ return CONTENT_ENCODING_GZIP;
+ }
+
+ @Override
+ public String getContentEncodingValue()
+ {
+ return CONTENT_ENCODING_GZIP.getValue();
+ }
+
+ @Override
+ public String getCharacterEncoding()
+ {
+ return _content.getCharacterEncoding();
+ }
+
+ @Override
+ public Type getMimeType()
+ {
+ return _content.getMimeType();
+ }
+
+ @Override
+ public void release()
+ {
+ _content.release();
+ }
+
+ @Override
+ public ByteBuffer getIndirectBuffer()
+ {
+ return _contentGz.getIndirectBuffer();
+ }
+
+ @Override
+ public ByteBuffer getDirectBuffer()
+ {
+ return _contentGz.getDirectBuffer();
+ }
+
+ @Override
+ public HttpField getContentLength()
+ {
+ return _contentGz.getContentLength();
+ }
+
+ @Override
+ public long getContentLengthValue()
+ {
+ return _contentGz.getContentLengthValue();
+ }
+
+ @Override
+ public InputStream getInputStream() throws IOException
+ {
+ return _contentGz.getInputStream();
+ }
+
+ @Override
+ public ReadableByteChannel getReadableByteChannel() throws IOException
+ {
+ return _contentGz.getReadableByteChannel();
+ }
+
+ @Override
+ public String toString()
+ {
+ return String.format("GzipHttpContent@%x{r=%s|%s,lm=%s|%s,ct=%s}",hashCode(),
+ _content.getResource(),_contentGz.getResource(),
+ _content.getResource().lastModified(),_contentGz.getResource().lastModified(),
+ getContentType());
+ }
+
+ @Override
+ public HttpContent getGzipContent()
+ {
+ return null;
+ }
+} \ No newline at end of file
diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpContent.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpContent.java
index bf4f4cbc1e..7ae190aab2 100644
--- a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpContent.java
+++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpContent.java
@@ -44,6 +44,9 @@ public interface HttpContent
String getCharacterEncoding();
Type getMimeType();
+ HttpField getContentEncoding();
+ String getContentEncodingValue();
+
HttpField getContentLength();
long getContentLengthValue();
@@ -60,4 +63,11 @@ public interface HttpContent
ReadableByteChannel getReadableByteChannel() throws IOException;
void release();
+ HttpContent getGzipContent();
+
+
+ public interface Factory
+ {
+ HttpContent getContent(String path) throws IOException;
+ }
}
diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpField.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpField.java
index 8b6531f6f4..1b5b39f053 100644
--- a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpField.java
+++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpField.java
@@ -21,6 +21,8 @@ package org.eclipse.jetty.http;
import java.util.ArrayList;
import java.util.Objects;
+import org.eclipse.jetty.util.StringUtil;
+
/** A HTTP Field
*/
public class HttpField
@@ -191,7 +193,7 @@ public class HttpField
/* ------------------------------------------------------------ */
/** Look for a value in a possible multi valued field
- * @param search Values to search for
+ * @param search Values to search for (case insensitive)
* @return True iff the value is contained in the field value entirely or
* as an element of a quoted comma separated list. List element parameters (eg qualities) are ignored,
* except if they are q=0, in which case the item itself is ignored.
@@ -204,6 +206,8 @@ public class HttpField
return false;
if (_value==null)
return false;
+
+ search = StringUtil.asciiToLowerCase(search);
int state=0;
int match=0;
@@ -236,7 +240,7 @@ public class HttpField
break;
default: // character
- match = c==search.charAt(0)?1:-1;
+ match = Character.toLowerCase(c)==search.charAt(0)?1:-1;
state=1;
break;
}
@@ -261,7 +265,7 @@ public class HttpField
if (match>0)
{
if (match<search.length())
- match=c==search.charAt(match)?(match+1):-1;
+ match=Character.toLowerCase(c)==search.charAt(match)?(match+1):-1;
else if (c!=' ' && c!= '\t')
match=-1;
}
@@ -285,7 +289,7 @@ public class HttpField
if (match>=0)
{
if (match<search.length())
- match=c==search.charAt(match)?(match+1):-1;
+ match=Character.toLowerCase(c)==search.charAt(match)?(match+1):-1;
else
match=-1;
}
@@ -296,7 +300,7 @@ public class HttpField
if (match>=0)
{
if (match<search.length())
- match=c==search.charAt(match)?(match+1):-1;
+ match=Character.toLowerCase(c)==search.charAt(match)?(match+1):-1;
else
match=-1;
}
@@ -346,7 +350,7 @@ public class HttpField
if (param>=0)
{
if (param<__zeroquality.length())
- param=c==__zeroquality.charAt(param)?(param+1):-1;
+ param=Character.toLowerCase(c)==__zeroquality.charAt(param)?(param+1):-1;
else if (c!='0'&&c!='.')
param=-1;
}
diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpGenerator.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpGenerator.java
index becaf461de..0c1787b9f5 100644
--- a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpGenerator.java
+++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpGenerator.java
@@ -22,6 +22,8 @@ import java.io.IOException;
import java.nio.BufferOverflowException;
import java.nio.ByteBuffer;
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;
@@ -41,7 +43,7 @@ public class HttpGenerator
{
private final static Logger LOG = Log.getLogger(HttpGenerator.class);
- public final static boolean __STRICT=Boolean.getBoolean("org.eclipse.jetty.http.HttpGenerator.STRICT");
+ public final static boolean __STRICT=Boolean.getBoolean("org.eclipse.jetty.http.HttpGenerator.STRICT");
private final static byte[] __colon_space = new byte[] {':',' '};
private final static HttpHeaderValue[] CLOSE = {HttpHeaderValue.CLOSE};
@@ -67,8 +69,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)
{
@@ -87,7 +89,7 @@ public class HttpGenerator
{
this(false,false);
}
-
+
/* ------------------------------------------------------------------------------- */
public HttpGenerator(boolean sendServerVersion,boolean sendXPoweredBy)
{
@@ -160,7 +162,7 @@ public class HttpGenerator
{
return _noContent;
}
-
+
/* ------------------------------------------------------------ */
public void setPersistent(boolean persistent)
{
@@ -206,13 +208,16 @@ public class HttpGenerator
if (info==null)
return Result.NEED_INFO;
- // Do we need a request header
if (header==null)
return Result.NEED_HEADER;
// If we have not been told our persistence, set the default
if (_persistent==null)
- _persistent=(info.getVersion().ordinal() > HttpVersion.HTTP_1_0.ordinal());
+ {
+ _persistent=info.getVersion().ordinal() > HttpVersion.HTTP_1_0.ordinal();
+ if (!_persistent && HttpMethod.CONNECT.is(info.getMethod()))
+ _persistent=true;
+ }
// prepare the header
int pos=BufferUtil.flipToFill(header);
@@ -222,9 +227,9 @@ public class HttpGenerator
generateRequestLine(info,header);
if (info.getVersion()==HttpVersion.HTTP_0_9)
- _noContent=true;
- else
- generateHeaders(info,header,content,last);
+ throw new IllegalArgumentException("HTTP/0.9 not supported");
+
+ generateHeaders(info,header,content,last);
boolean expect100 = info.getFields().contains(HttpHeader.EXPECT, HttpHeaderValue.CONTINUE.asString());
@@ -278,12 +283,9 @@ public class HttpGenerator
}
if (last)
- {
_state=State.COMPLETING;
- return len>0?Result.FLUSH:Result.CONTINUE;
- }
- return Result.FLUSH;
+ return len>0?Result.FLUSH:Result.CONTINUE;
}