Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJoakim Erdfelt2015-11-04 19:35:39 +0000
committerJoakim Erdfelt2015-11-04 19:35:39 +0000
commita69180e34470d860c875114bba0a123217f1aeba (patch)
treeebd3285694c6105709131d9744ebe08356bdc116
parent5fa750973ab92e41de8b2dfc204f495c78002087 (diff)
parent3e2658a41b07ec2b455ea806f2ba6f8a3245f581 (diff)
downloadorg.eclipse.jetty.project-a69180e34470d860c875114bba0a123217f1aeba.tar.gz
org.eclipse.jetty.project-a69180e34470d860c875114bba0a123217f1aeba.tar.xz
org.eclipse.jetty.project-a69180e34470d860c875114bba0a123217f1aeba.zip
Merge branch 'master' into feature/wsclient-httpclient
-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/ConnectionPool.java31
-rw-r--r--jetty-client/src/main/java/org/eclipse/jetty/client/DuplexConnectionPool.java295
-rw-r--r--jetty-client/src/main/java/org/eclipse/jetty/client/HttpClient.java11
-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.java2
-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.java218
-rw-r--r--jetty-client/src/main/java/org/eclipse/jetty/client/Socks4Proxy.java3
-rw-r--r--jetty-client/src/main/java/org/eclipse/jetty/client/ValidatingConnectionPool.java19
-rw-r--r--jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpConnectionOverHTTP.java11
-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.java4
-rw-r--r--jetty-client/src/main/java/org/eclipse/jetty/client/util/MultiPartContentProvider.java388
-rw-r--r--jetty-client/src/test/java/org/eclipse/jetty/client/AbstractHttpClientServerTest.java8
-rw-r--r--jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientCustomProxyTest.java35
-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/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.java2
-rw-r--r--jetty-client/src/test/java/org/eclipse/jetty/client/TLSServerConnectionCloseTest.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.java4
-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/util/MultiPartContentProviderTest.java439
-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.java10
-rw-r--r--jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/parser/ResponseContentParser.java10
-rw-r--r--jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/parser/StreamContentParser.java4
-rw-r--r--jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/proxy/FastCGIProxyServlet.java53
-rw-r--r--jetty-gcloud/gcloud-session-manager/src/main/java/org/eclipse/jetty/gcloud/session/GCloudSessionManager.java2
-rw-r--r--jetty-http/src/main/java/org/eclipse/jetty/http/HttpURI.java1
-rw-r--r--jetty-http/src/main/java/org/eclipse/jetty/http/MetaData.java1
-rw-r--r--jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/ProxyProtocolTest.java67
-rw-r--r--jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/PushCacheFilterTest.java4
-rw-r--r--jetty-http2/http2-http-client-transport/src/main/java/org/eclipse/jetty/http2/client/http/HttpDestinationOverHTTP2.java7
-rw-r--r--jetty-infinispan/src/main/java/org/eclipse/jetty/session/infinispan/InfinispanSessionManager.java4
-rw-r--r--jetty-io/src/main/java/org/eclipse/jetty/io/AbstractEndPoint.java2
-rw-r--r--jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/AbstractJettyMojo.java3
-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-nosql/src/main/java/org/eclipse/jetty/nosql/NoSqlSessionManager.java3
-rw-r--r--jetty-nosql/src/main/java/org/eclipse/jetty/nosql/mongodb/MongoSessionIdManager.java4
-rw-r--r--jetty-nosql/src/main/java/org/eclipse/jetty/nosql/mongodb/MongoSessionManager.java2
-rw-r--r--jetty-proxy/src/main/java/org/eclipse/jetty/proxy/ConnectHandler.java191
-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/ProxyServletTest.java7
-rw-r--r--jetty-proxy/src/test/java/org/eclipse/jetty/proxy/ProxyTunnellingTest.java26
-rw-r--r--jetty-rewrite/src/main/config/etc/jetty-rewrite.xml48
-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/Dispatcher.java25
-rw-r--r--jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannel.java42
-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.java14
-rw-r--r--jetty-server/src/main/java/org/eclipse/jetty/server/SecureRequestCustomizer.java9
-rw-r--r--jetty-server/src/main/java/org/eclipse/jetty/server/session/JDBCSessionIdManager.java91
-rw-r--r--jetty-server/src/main/java/org/eclipse/jetty/server/session/JDBCSessionManager.java56
-rw-r--r--jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletMapping.java19
-rw-r--r--jetty-servlets/src/main/java/org/eclipse/jetty/servlets/PushCacheFilter.java22
-rw-r--r--jetty-util/src/main/java/org/eclipse/jetty/util/MultiPartInputStreamParser.java107
-rw-r--r--jetty-webapp/src/main/java/org/eclipse/jetty/webapp/CachingWebAppClassLoader.java121
-rw-r--r--jetty-webapp/src/main/java/org/eclipse/jetty/webapp/StandardDescriptorProcessor.java56
-rw-r--r--pom.xml13
-rw-r--r--tests/test-http-client-transport/pom.xml6
-rw-r--r--tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/AbstractTest.java45
-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)180
-rw-r--r--tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/HttpClientTest.java1
-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.xml4
-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/src/test/java/org/eclipse/jetty/nosql/mongodb/MongoTestServer.java6
-rw-r--r--tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractInvalidationSessionTest.java30
-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-webapps/test-jetty-webapp/src/main/config/demo-base/webapps/test.xml9
117 files changed, 3749 insertions, 1704 deletions
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/ConnectionPool.java b/jetty-client/src/main/java/org/eclipse/jetty/client/ConnectionPool.java
index fc95421a0b..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
@@ -18,17 +18,24 @@
package org.eclipse.jetty.client;
-import org.eclipse.jetty.client.api.Destination;
-import org.eclipse.jetty.util.Callback;
-
-/**
- * @deprecated use {@link DuplexConnectionPool} instead
- */
-@Deprecated
-public class ConnectionPool extends DuplexConnectionPool
+import java.io.Closeable;
+
+import org.eclipse.jetty.client.api.Connection;
+
+public interface ConnectionPool extends Closeable
{
- public ConnectionPool(Destination destination, int maxConnections, Callback requester)
- {
- super(destination, maxConnections, requester);
- }
+ boolean isActive(Connection connection);
+
+ boolean isEmpty();
+
+ boolean isClosed();
+
+ Connection acquire();
+
+ boolean release(Connection connection);
+
+ boolean remove(Connection connection);
+
+ @Override
+ 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
index d887923e1c..c22966c372 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/DuplexConnectionPool.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/DuplexConnectionPool.java
@@ -18,21 +18,20 @@
package org.eclipse.jetty.client;
-import java.io.Closeable;
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.concurrent.LinkedBlockingDeque;
-import java.util.concurrent.atomic.AtomicInteger;
+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.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;
@@ -42,43 +41,57 @@ import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.thread.Sweeper;
@ManagedObject("The connection pool")
-public class DuplexConnectionPool implements Closeable, Dumpable, Sweeper.Sweepable
+public class DuplexConnectionPool extends AbstractConnectionPool implements Dumpable, Sweeper.Sweepable
{
private static final Logger LOG = Log.getLogger(DuplexConnectionPool.class);
- 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;
+ private final Set<Connection> activeConnections;
public DuplexConnectionPool(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);
+ super(destination, maxConnections, requester);
+ this.idleConnections = new ArrayDeque<>(maxConnections);
+ this.activeConnections = new HashSet<>(maxConnections);
}
- @ManagedAttribute(value = "The number of connections", readonly = true)
- public int getConnectionCount()
+ protected void lock()
+ {
+ lock.lock();
+ }
+
+ protected void unlock()
{
- return connectionCount.get();
+ lock.unlock();
}
@ManagedAttribute(value = "The number of idle connections", readonly = true)
public int getIdleConnectionCount()
{
- return idleConnections.size();
+ lock();
+ try
+ {
+ return idleConnections.size();
+ }
+ finally
+ {
+ unlock();
+ }
}
@ManagedAttribute(value = "The number of active connections", readonly = true)
public int getActiveConnectionCount()
{
- return activeConnections.size();
+ lock();
+ try
+ {
+ return activeConnections.size();
+ }
+ finally
+ {
+ unlock();
+ }
}
public Queue<Connection> getIdleConnections()
@@ -86,139 +99,76 @@ public class DuplexConnectionPool implements Closeable, Dumpable, Sweeper.Sweepa
return idleConnections;
}
- public Queue<Connection> getActiveConnections()
+ public Collection<Connection> getActiveConnections()
{
return activeConnections;
}
- public Connection acquire()
- {
- Connection connection = activateIdle();
- if (connection == null)
- connection = tryCreate();
- return connection;
- }
-
- private Connection tryCreate()
+ @Override
+ public boolean isActive(Connection connection)
{
- while (true)
+ lock();
+ try
{
- 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();
- }
+ return activeConnections.contains(connection);
+ }
+ finally
+ {
+ unlock();
}
}
- protected void proceed()
- {
- requester.succeeded();
- }
-
- protected void idleCreated(Connection connection)
+ @Override
+ protected void onCreated(Connection connection)
{
- boolean idle;
lock();
try
{
// Use "cold" new connections as last.
- idle = idleConnections.offerLast(connection);
+ idleConnections.offer(connection);
}
finally
{
unlock();
}
- idle(connection, idle);
+ idle(connection, false);
}
- private Connection activateIdle()
+ @Override
+ protected Connection activate()
{
- boolean acquired;
Connection connection;
lock();
try
{
- connection = idleConnections.pollFirst();
+ connection = idleConnections.poll();
if (connection == null)
return null;
- acquired = activeConnections.offer(connection);
+ activeConnections.add(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)
- {
+ return active(connection);
}
public boolean release(Connection connection)
{
- boolean idle;
+ boolean closed = isClosed();
lock();
try
{
if (!activeConnections.remove(connection))
return false;
- // Make sure we use "hot" connections first.
- idle = offerIdle(connection);
+
+ if (!closed)
+ {
+ // Make sure we use "hot" connections first.
+ deactivate(connection);
+ }
}
finally
{
@@ -226,35 +176,14 @@ public class DuplexConnectionPool implements Closeable, Dumpable, Sweeper.Sweepa
}
released(connection);
- return idle(connection, idle);
+ return idle(connection, closed);
}
- protected boolean offerIdle(Connection connection)
+ protected boolean deactivate(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);
@@ -275,59 +204,25 @@ public class DuplexConnectionPool implements Closeable, Dumpable, Sweeper.Sweepa
unlock();
}
- if (activeRemoved)
+ if (activeRemoved || force)
released(connection);
boolean removed = activeRemoved || idleRemoved || force;
if (removed)
- {
- int pooled = connectionCount.decrementAndGet();
- if (LOG.isDebugEnabled())
- LOG.debug("Connection removed {} - pooled: {}", connection, pooled);
- }
+ removed(connection);
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<>();
+ super.close();
+
+ List<Connection> connections = new ArrayList<>();
lock();
try
{
- idles.addAll(idleConnections);
+ connections.addAll(idleConnections);
idleConnections.clear();
- actives.addAll(activeConnections);
+ connections.addAll(activeConnections);
activeConnections.clear();
}
finally
@@ -335,32 +230,18 @@ public class DuplexConnectionPool implements Closeable, Dumpable, Sweeper.Sweepa
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();
- }
-
- @Override
- public String dump()
- {
- return ContainerLifeCycle.dump(this);
+ close(connections);
}
@Override
public void dump(Appendable out, String indent) throws IOException
{
- List<Connection> actives = new ArrayList<>();
- List<Connection> idles = new ArrayList<>();
+ List<Connection> connections = new ArrayList<>();
lock();
try
{
- actives.addAll(activeConnections);
- idles.addAll(idleConnections);
+ connections.addAll(activeConnections);
+ connections.addAll(idleConnections);
}
finally
{
@@ -368,20 +249,20 @@ public class DuplexConnectionPool implements Closeable, Dumpable, Sweeper.Sweepa
}
ContainerLifeCycle.dumpObject(out, this);
- ContainerLifeCycle.dump(out, indent, actives, idles);
+ ContainerLifeCycle.dump(out, indent, connections);
}
@Override
public boolean sweep()
{
- List<Sweeper.Sweepable> toSweep = new ArrayList<>();
+ List<Connection> toSweep = new ArrayList<>();
lock();
try
{
- for (Connection connection : getActiveConnections())
+ for (Connection connection : activeConnections)
{
if (connection instanceof Sweeper.Sweepable)
- toSweep.add(((Sweeper.Sweepable)connection));
+ toSweep.add(connection);
}
}
finally
@@ -389,13 +270,13 @@ public class DuplexConnectionPool implements Closeable, Dumpable, Sweeper.Sweepa
unlock();
}
- for (Sweeper.Sweepable candidate : toSweep)
+ for (Connection connection : toSweep)
{
- if (candidate.sweep())
+ if (((Sweeper.Sweepable)connection).sweep())
{
- boolean removed = getActiveConnections().remove(candidate);
+ boolean removed = remove(connection, true);
LOG.warn("Connection swept: {}{}{} from active connections{}{}",
- candidate,
+ connection,
System.lineSeparator(),
removed ? "Removed" : "Not removed",
System.lineSeparator(),
@@ -406,16 +287,6 @@ public class DuplexConnectionPool implements Closeable, Dumpable, Sweeper.Sweepa
return false;
}
- protected void lock()
- {
- lock.lock();
- }
-
- protected void unlock()
- {
- lock.unlock();
- }
-
@Override
public String toString()
{
@@ -434,8 +305,8 @@ public class DuplexConnectionPool implements Closeable, Dumpable, Sweeper.Sweepa
return String.format("%s[c=%d/%d,a=%d,i=%d]",
getClass().getSimpleName(),
- connectionCount.get(),
- maxConnections,
+ getConnectionCount(),
+ getMaxConnectionCount(),
activeSize,
idleSize);
}
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 0720751663..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
@@ -1039,7 +1039,7 @@ public class HttpClient extends ContainerLifeCycle
{
if (port > 0)
return port;
- else if (HttpScheme.HTTPS.is(scheme) || HttpScheme.WSS.is(scheme))
+ else if (isSchemeSecure(scheme))
return 443;
else
return 80;
@@ -1047,12 +1047,17 @@ public class HttpClient extends ContainerLifeCycle
public boolean isDefaultPort(String scheme, int port)
{
- if (HttpScheme.HTTPS.is(scheme) || HttpScheme.WSS.is(scheme))
+ if (isSchemeSecure(scheme))
return port == 443;
- else
+ else
return port == 80;
}
+ public boolean isSchemeSecure(String scheme)
+ {
+ return HttpScheme.HTTPS.is(scheme) || HttpScheme.WSS.is(scheme);
+ }
+
private class ContentDecoderFactorySet implements Set<ContentDecoder.Factory>
{
private final Set<ContentDecoder.Factory> set = new HashSet<>();
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 ff24d7b3b1..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()) || HttpScheme.WSS.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 7199f17eb7..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
@@ -107,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()) || HttpScheme.WSS.is(destination.getScheme()))
+ if (destination.isSecure())
{
SslContextFactory sslContextFactory = destination.getHttpClient().getSslContextFactory();
if (sslContextFactory != null)
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 7005e16fd6..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,229 +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 DuplexConnectionPool 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);
}
- @Override
- protected void doStart() throws Exception
- {
- HttpClient client = getHttpClient();
- this.connectionPool = newConnectionPool(client);
- addBean(connectionPool);
- super.doStart();
- Sweeper sweeper = client.getBean(Sweeper.class);
- if (sweeper != null)
- sweeper.offer(connectionPool);
- }
-
- @Override
- protected void doStop() throws Exception
- {
- HttpClient client = getHttpClient();
- Sweeper sweeper = client.getBean(Sweeper.class);
- if (sweeper != null)
- sweeper.remove(connectionPool);
- super.doStop();
- removeBean(connectionPool);
- }
-
- protected DuplexConnectionPool newConnectionPool(HttpClient client)
+ protected ConnectionPool newConnectionPool(HttpClient client)
{
return new DuplexConnectionPool(this, client.getMaxConnectionsPerDestination(), this);
}
-
- @ManagedAttribute(value = "The connection pool", readonly = true)
- public DuplexConnectionPool 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();
- }
- }
-
- public boolean remove(Connection connection)
- {
- return connectionPool.remove(connection);
- }
-
- @Override
- public void close(Connection connection)
- {
- super.close(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();
- }
- }
-
- 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);
- }
}
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 f73932c111..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,7 +195,7 @@ 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()) || HttpScheme.WSS.is(destination.getScheme()))
+ if (destination.isSecure())
connectionFactory = new SslClientConnectionFactory(client.getSslContextFactory(), client.getByteBufferPool(), client.getExecutor(), connectionFactory);
org.eclipse.jetty.io.Connection newConnection = connectionFactory.newConnection(getEndPoint(), context);
getEndPoint().upgrade(newConnection);
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 2235f75dab..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
@@ -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/http/HttpConnectionOverHTTP.java b/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpConnectionOverHTTP.java
index 5b592f6ce1..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
@@ -42,8 +42,8 @@ public class HttpConnectionOverHTTP extends AbstractConnection implements Connec
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;
@@ -89,14 +89,7 @@ public class HttpConnectionOverHTTP extends AbstractConnection implements Connec
fillInterested();
promise.succeeded(this);
}
-
- @Override
- public void onClose()
- {
- softClose();
- super.onClose();
- }
-
+
public boolean isClosed()
{
return closed.get();
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 22d8c7e030..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
@@ -92,8 +92,8 @@ public class HttpReceiverOverHTTP extends HttpReceiver implements HttpParser.Res
{
if (BufferUtil.hasContent(buffer))
{
- ByteBuffer upgradeBuffer = buffer;
- releaseBuffer(); // TODO: right place to do this?
+ ByteBuffer upgradeBuffer = ByteBuffer.allocate(buffer.remaining());
+ upgradeBuffer.put(buffer);
return upgradeBuffer;
}
return null;
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..dddff6a814
--- /dev/null
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/util/MultiPartContentProvider.java
@@ -0,0 +1,388 @@
+//
+// ========================================================================
+// 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 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);
+ * 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
+{
+ 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 Listener listener;
+ private long length;
+
+ 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;
+
+ // Compute the length, if possible.
+ if (parts.isEmpty())
+ {
+ length = onlyBoundary.remaining();
+ }
+ else
+ {
+ for (int i = 0; i < parts.size(); ++i)
+ {
+ length += (i == 0) ? firstBoundary.remaining() : middleBoundary.remaining();
+ Part part = parts.get(i);
+ long partLength = part.length;
+ length += partLength;
+ if (partLength < 0)
+ {
+ length = -1;
+ break;
+ }
+ }
+ if (length > 0)
+ length += lastBoundary.remaining();
+ }
+ }
+
+ @Override
+ public long getLength()
+ {
+ return length;
+ }
+
+ @Override
+ public Iterator<ByteBuffer> iterator()
+ {
+ return new MultiPartIterator();
+ }
+
+ 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 edb6c9ce71..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
@@ -60,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("");
@@ -79,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/HttpClientCustomProxyTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientCustomProxyTest.java
index 55911a8ddc..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(Callback.NOOP, ByteBuffer.wrap(CAFE_BABE));
+ }
+
+ @Override
+ public void failed(Throwable x)
+ {
+ close();
}
@Override
@@ -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(Callback.NOOP, buffer);
-
- // We are good, upgrade the connection
- getEndPoint().upgrade(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 c54f88981e..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;
- DuplexConnectionPool 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;
- DuplexConnectionPool 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 0db34443c2..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.
}
- DuplexConnectionPool 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));
- DuplexConnectionPool 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 bff369f79d..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);
- DuplexConnectionPool 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/HttpClientUploadDuringServerShutdown.java b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientUploadDuringServerShutdown.java
index b899a1f211..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());
- DuplexConnectionPool 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 f8cbb04c5a..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);
- DuplexConnectionPool 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);
- DuplexConnectionPool 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);
- DuplexConnectionPool 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);
- DuplexConnectionPool 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);
- DuplexConnectionPool 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);
- DuplexConnectionPool 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);
- DuplexConnectionPool 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);
- DuplexConnectionPool 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);
- DuplexConnectionPool 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 45b78e7d18..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());
- DuplexConnectionPool 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());
- DuplexConnectionPool 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());
- DuplexConnectionPool 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());
- DuplexConnectionPool 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());
- DuplexConnectionPool 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());
- DuplexConnectionPool 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());
- DuplexConnectionPool 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
index 23d72de601..47a7760e1a 100644
--- a/jetty-client/src/test/java/org/eclipse/jetty/client/ServerConnectionCloseTest.java
+++ b/jetty-client/src/test/java/org/eclipse/jetty/client/ServerConnectionCloseTest.java
@@ -151,7 +151,7 @@ public class ServerConnectionCloseTest
// Connection should have been removed from pool.
HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination("http", "localhost", port);
- DuplexConnectionPool connectionPool = destination.getConnectionPool();
+ DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool();
Assert.assertEquals(0, connectionPool.getConnectionCount());
Assert.assertEquals(0, connectionPool.getIdleConnectionCount());
Assert.assertEquals(0, connectionPool.getActiveConnectionCount());
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
index 79a9cd1879..d48ed22314 100644
--- a/jetty-client/src/test/java/org/eclipse/jetty/client/TLSServerConnectionCloseTest.java
+++ b/jetty-client/src/test/java/org/eclipse/jetty/client/TLSServerConnectionCloseTest.java
@@ -183,7 +183,7 @@ public class TLSServerConnectionCloseTest
// Connection should have been removed from pool.
HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination("http", "localhost", port);
- DuplexConnectionPool connectionPool = destination.getConnectionPool();
+ DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool();
Assert.assertEquals(0, connectionPool.getConnectionCount());
Assert.assertEquals(0, connectionPool.getIdleConnectionCount());
Assert.assertEquals(0, connectionPool.getActiveConnectionCount());
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 aa3b4f5e74..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
@@ -24,6 +24,7 @@ import java.util.concurrent.RejectedExecutionException;
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;
@@ -31,9 +32,6 @@ 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);
}
}
@@ -97,18 +99,18 @@ public class HttpDestinationOverHTTPTest extends AbstractHttpClientServerTest
HttpDestinationOverHTTP destination = new HttpDestinationOverHTTP(client, new Origin("http", "localhost", connector.getLocalPort()))
{
@Override
- protected DuplexConnectionPool newConnectionPool(HttpClient client)
+ protected ConnectionPool newConnectionPool(HttpClient client)
{
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 e057cf34f9..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
@@ -30,6 +30,7 @@ import org.eclipse.jetty.client.HttpExchange;
import org.eclipse.jetty.client.HttpRequest;
import org.eclipse.jetty.client.HttpResponseException;
import org.eclipse.jetty.client.Origin;
+import org.eclipse.jetty.client.api.Connection;
import org.eclipse.jetty.client.api.Response;
import org.eclipse.jetty.client.util.FutureResponseListener;
import org.eclipse.jetty.http.HttpFields;
@@ -60,6 +61,7 @@ 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<>());
endPoint.setConnection(connection);
@@ -235,7 +237,7 @@ public class HttpReceiverOverHTTPTest
}
};
endPoint.setConnection(connection);
-
+
// Partial response to trigger the call to fillInterested().
endPoint.addInput("" +
"HTTP/1.1 200 OK\r\n" +
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/util/MultiPartContentProviderTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/util/MultiPartContentProviderTest.java
new file mode 100644
index 0000000000..4ae83b23b8
--- /dev/null
+++ b/jetty-client/src/test/java/org/eclipse/jetty/client/util/MultiPartContentProviderTest.java
@@ -0,0 +1,439 @@
+//
+// ========================================================================
+// 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();
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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();
+
+ 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-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 402f3a4897..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,6 +22,8 @@ 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>
@@ -39,9 +41,14 @@ import org.eclipse.jetty.http.HttpField;
* </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;
@@ -73,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 def4394cc1..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
@@ -35,8 +35,8 @@ import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
/**
- * <p>The parser for STDOUT type frames.</p>
- * <p>STDOUT frames contain both the HTTP headers (but not the response line)
+ * <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
@@ -99,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:
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 ae7f7bc041..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
@@ -25,8 +25,8 @@ import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
/**
- * <p>A stream content parser parses frames of type STDIN, STDOUT and STDERR.</p>
- * <p>STDOUT frames are handled specially by {@link ResponseContentParser}.
+ * <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
{
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-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
index b6b39a9bba..a6137e47ea 100644
--- 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
@@ -829,6 +829,7 @@ public class GCloudSessionManager extends AbstractSessionManager
if (memSession == null)
{
memSession = session;
+ _sessionsStats.increment();
}
//final check
@@ -1008,6 +1009,7 @@ public class GCloudSessionManager extends AbstractSessionManager
{
//indicate that the session was reinflated
session.didActivate();
+ _sessionsStats.increment();
LOG.debug("getSession({}): loaded session from cluster", idInCluster);
}
return session;
diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpURI.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpURI.java
index 5da5dd4512..d5cc83519c 100644
--- a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpURI.java
+++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpURI.java
@@ -119,6 +119,7 @@ public class HttpURI
public HttpURI(HttpURI uri)
{
this(uri._scheme,uri._host,uri._port,uri._path,uri._param,uri._query,uri._fragment);
+ _uri=uri._uri;
}
/* ------------------------------------------------------------ */
diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/MetaData.java b/jetty-http/src/main/java/org/eclipse/jetty/http/MetaData.java
index b4038cbdb9..43bc07d794 100644
--- a/jetty-http/src/main/java/org/eclipse/jetty/http/MetaData.java
+++ b/jetty-http/src/main/java/org/eclipse/jetty/http/MetaData.java
@@ -158,7 +158,6 @@ public class MetaData implements Iterable<HttpField>
this(request.getMethod(),new HttpURI(request.getURI()), request.getVersion(), new HttpFields(request.getFields()), request.getContentLength());
}
- // TODO MetaData should be immuttable!!!
public void recycle()
{
super.recycle();
diff --git a/jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/ProxyProtocolTest.java b/jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/ProxyProtocolTest.java
index 650cc26746..f72199e465 100644
--- a/jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/ProxyProtocolTest.java
+++ b/jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/ProxyProtocolTest.java
@@ -48,6 +48,7 @@ import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.server.handler.AbstractHandler;
import org.eclipse.jetty.util.FuturePromise;
import org.eclipse.jetty.util.Promise;
+import org.eclipse.jetty.util.TypeUtil;
import org.junit.After;
import org.junit.Assert;
import org.junit.Test;
@@ -80,13 +81,25 @@ public class ProxyProtocolTest
}
@Test
- public void test_PROXY_GET() throws Exception
+ public void test_PROXY_GET_v1() throws Exception
{
startServer(new AbstractHandler()
{
@Override
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
{
+ try
+ {
+ Assert.assertEquals("1.2.3.4",request.getRemoteAddr());
+ Assert.assertEquals(1111,request.getRemotePort());
+ Assert.assertEquals("5.6.7.8",request.getLocalAddr());
+ Assert.assertEquals(2222,request.getLocalPort());
+ }
+ catch(Throwable th)
+ {
+ th.printStackTrace();
+ response.setStatus(500);
+ }
baseRequest.setHandled(true);
}
});
@@ -118,4 +131,56 @@ public class ProxyProtocolTest
});
Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
}
+
+ @Test
+ public void test_PROXY_GET_v2() throws Exception
+ {
+ startServer(new AbstractHandler()
+ {
+ @Override
+ public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+ {
+ try
+ {
+ Assert.assertEquals("10.0.0.4",request.getRemoteAddr());
+ Assert.assertEquals(33824,request.getRemotePort());
+ Assert.assertEquals("10.0.0.4",request.getLocalAddr());
+ Assert.assertEquals(8888,request.getLocalPort());
+ }
+ catch(Throwable th)
+ {
+ th.printStackTrace();
+ response.setStatus(500);
+ }
+ baseRequest.setHandled(true);
+ }
+ });
+
+ String request1 = "0D0A0D0A000D0A515549540A211100140A0000040A000004842022B82000050000000000";
+ SocketChannel channel = SocketChannel.open();
+ channel.connect(new InetSocketAddress("localhost", connector.getLocalPort()));
+ channel.write(ByteBuffer.wrap(TypeUtil.fromHexString(request1)));
+
+ FuturePromise<Session> promise = new FuturePromise<>();
+ client.accept(null, channel, new Session.Listener.Adapter(), promise);
+ Session session = promise.get(5, TimeUnit.SECONDS);
+
+ HttpFields fields = new HttpFields();
+ String uri = "http://localhost:" + connector.getLocalPort() + "/";
+ MetaData.Request metaData = new MetaData.Request("GET", new HttpURI(uri), HttpVersion.HTTP_2, fields);
+ HeadersFrame frame = new HeadersFrame(metaData, null, true);
+ CountDownLatch latch = new CountDownLatch(1);
+ session.newStream(frame, new Promise.Adapter<>(), new Stream.Listener.Adapter()
+ {
+ @Override
+ public void onHeaders(Stream stream, HeadersFrame frame)
+ {
+ MetaData.Response response = (MetaData.Response)frame.getMetaData();
+ Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
+ if (frame.isEndStream())
+ latch.countDown();
+ }
+ });
+ Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
+ }
}
diff --git a/jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/PushCacheFilterTest.java b/jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/PushCacheFilterTest.java
index 4088d95d39..1037b3bd65 100644
--- a/jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/PushCacheFilterTest.java
+++ b/jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/PushCacheFilterTest.java
@@ -695,6 +695,10 @@ public class PushCacheFilterTest extends AbstractTest
@Override
public Stream.Listener onPush(Stream stream, PushPromiseFrame frame)
{
+ MetaData metaData = frame.getMetaData();
+ Assert.assertTrue(metaData instanceof MetaData.Request);
+ MetaData.Request pushedRequest = (MetaData.Request)metaData;
+ Assert.assertEquals(servletPath + secondaryResource, pushedRequest.getURI().getPathQuery());
return new Adapter()
{
@Override
diff --git a/jetty-http2/http2-http-client-transport/src/main/java/org/eclipse/jetty/http2/client/http/HttpDestinationOverHTTP2.java b/jetty-http2/http2-http-client-transport/src/main/java/org/eclipse/jetty/http2/client/http/HttpDestinationOverHTTP2.java
index b83d52371a..114df6f3f6 100644
--- a/jetty-http2/http2-http-client-transport/src/main/java/org/eclipse/jetty/http2/client/http/HttpDestinationOverHTTP2.java
+++ b/jetty-http2/http2-http-client-transport/src/main/java/org/eclipse/jetty/http2/client/http/HttpDestinationOverHTTP2.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 HttpDestinationOverHTTP2 extends MultiplexHttpDestination<HttpConnectionOverHTTP2>
+public class HttpDestinationOverHTTP2 extends MultiplexHttpDestination
{
public HttpDestinationOverHTTP2(HttpClient client, Origin origin)
{
@@ -31,8 +32,8 @@ public class HttpDestinationOverHTTP2 extends MultiplexHttpDestination<HttpConne
}
@Override
- protected void send(HttpConnectionOverHTTP2 connection, HttpExchange exchange)
+ protected void send(Connection connection, HttpExchange exchange)
{
- connection.send(exchange);
+ ((HttpConnectionOverHTTP2)connection).send(exchange);
}
}
diff --git a/jetty-infinispan/src/main/java/org/eclipse/jetty/session/infinispan/InfinispanSessionManager.java b/jetty-infinispan/src/main/java/org/eclipse/jetty/session/infinispan/InfinispanSessionManager.java
index 039263a46c..072d0f1a77 100644
--- a/jetty-infinispan/src/main/java/org/eclipse/jetty/session/infinispan/InfinispanSessionManager.java
+++ b/jetty-infinispan/src/main/java/org/eclipse/jetty/session/infinispan/InfinispanSessionManager.java
@@ -664,7 +664,7 @@ public class InfinispanSessionManager extends AbstractSessionManager
for (String candidateId:candidateIds)
{
if (LOG.isDebugEnabled())
- LOG.debug("Session {} expired ", candidateId);
+ LOG.debug("Session {} candidate for expiry", candidateId);
Session candidateSession = _sessions.get(candidateId);
if (candidateSession != null)
@@ -691,6 +691,7 @@ public class InfinispanSessionManager extends AbstractSessionManager
if (LOG.isDebugEnabled()) LOG.debug("Session({}) not local to this session manager, removing from local memory", candidateId);
candidateSession.willPassivate();
_sessions.remove(candidateSession.getClusterId());
+ _sessionsStats.decrement();
}
}
@@ -870,6 +871,7 @@ public class InfinispanSessionManager extends AbstractSessionManager
{
//indicate that the session was reinflated
session.didActivate();
+ _sessionsStats.increment();
LOG.debug("getSession({}): loaded session from cluster", idInCluster);
}
return session;
diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/AbstractEndPoint.java b/jetty-io/src/main/java/org/eclipse/jetty/io/AbstractEndPoint.java
index 0444f788ab..5b49c8e44c 100644
--- a/jetty-io/src/main/java/org/eclipse/jetty/io/AbstractEndPoint.java
+++ b/jetty-io/src/main/java/org/eclipse/jetty/io/AbstractEndPoint.java
@@ -182,7 +182,7 @@ public abstract class AbstractEndPoint extends IdleTimeout implements EndPoint
Connection old_connection = getConnection();
if (LOG.isDebugEnabled())
- LOG.debug("{} upgradeing from {} to {}", this, old_connection, newConnection);
+ LOG.debug("{} upgrading from {} to {}", this, old_connection, newConnection);
ByteBuffer prefilled = (old_connection instanceof Connection.UpgradeFrom)
?((Connection.UpgradeFrom)old_connection).onUpgradeFrom():null;
diff --git a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/AbstractJettyMojo.java b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/AbstractJettyMojo.java
index 402ad48243..caa79c57a9 100644
--- a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/AbstractJettyMojo.java
+++ b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/AbstractJettyMojo.java
@@ -443,6 +443,9 @@ public abstract class AbstractJettyMojo extends AbstractMojo
//set up a RequestLog if one is provided and the handle structure
ServerSupport.configureHandlers(server, this.requestLog);
+
+ //Set up list of default Configurations to apply to a webapp
+ ServerSupport.configureDefaultConfigurationClasses(server);
configureWebApplication();
ServerSupport.addWebApplication(server, webApp);
diff --git a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyRunForkedMojo.java b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyRunForkedMojo.java
index d4ac65e2ac..22cf2f21dc 100644
--- a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyRunForkedMojo.java
+++ b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyRunForkedMojo.java
@@ -244,6 +244,8 @@ public class JettyRunForkedMojo extends JettyRunMojo
//ensure handler structure enabled
ServerSupport.configureHandlers(server, null);
+
+ ServerSupport.configureDefaultConfigurationClasses(server);
//ensure config of the webapp based on settings in plugin
configureWebApplication();
diff --git a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyWebAppContext.java b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyWebAppContext.java
index 8f1f343584..0a2dd8cc3b 100644
--- a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyWebAppContext.java
+++ b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyWebAppContext.java
@@ -72,23 +72,25 @@ public class JettyWebAppContext extends WebAppContext
private static final String DEFAULT_CONTAINER_INCLUDE_JAR_PATTERN = ".*/javax.servlet-[^/]*\\.jar$|.*/servlet-api-[^/]*\\.jar$|.*javax.servlet.jsp.jstl-[^/]*\\.jar|.*taglibs-standard-impl-.*\\.jar";
private static final String WEB_INF_CLASSES_PREFIX = "/WEB-INF/classes";
private static final String WEB_INF_LIB_PREFIX = "/WEB-INF/lib";
+
+
+ public static final String[] DEFAULT_CONFIGURATION_CLASSES = {
+ "org.eclipse.jetty.maven.plugin.MavenWebInfConfiguration",
+ "org.eclipse.jetty.webapp.WebXmlConfiguration",
+ "org.eclipse.jetty.webapp.MetaInfConfiguration",
+ "org.eclipse.jetty.webapp.FragmentConfiguration",
+ "org.eclipse.jetty.plus.webapp.EnvConfiguration",
+ "org.eclipse.jetty.plus.webapp.PlusConfiguration",
+ "org.eclipse.jetty.annotations.AnnotationConfiguration",
+ "org.eclipse.jetty.webapp.JettyWebXmlConfiguration"
+ };
- private final Configuration[] _defaultConfigurations = {
- new MavenWebInfConfiguration(),
- new WebXmlConfiguration(),
- new MetaInfConfiguration(),
- new FragmentConfiguration(),
- new EnvConfiguration(),
- new PlusConfiguration(),
- new AnnotationConfiguration(),
- new JettyWebXmlConfiguration()
- };
- private final Configuration[] _quickStartConfigurations = {
- new MavenQuickStartConfiguration(),
- new EnvConfiguration(),
- new PlusConfiguration(),
- new JettyWebXmlConfiguration()
+ private final String[] QUICKSTART_CONFIGURATION_CLASSES = {
+ "org.eclipse.jetty.maven.plugin.MavenQuickStartConfiguration",
+ "org.eclipse.jetty.plus.webapp.EnvConfiguration",
+ "org.eclipse.jetty.plus.webapp.PlusConfiguration",
+ "org.eclipse.jetty.webapp.JettyWebXmlConfiguration"
};
private File _classes = null;
@@ -100,6 +102,7 @@ public class JettyWebAppContext extends WebAppContext
private String _jettyEnvXml;
private List<Overlay> _overlays;
private Resource _quickStartWebXml;
+
@@ -338,25 +341,17 @@ public class JettyWebAppContext extends WebAppContext
{
//choose if this will be a quickstart or normal start
if (!isGenerateQuickStart() && getQuickStartWebDescriptor() != null)
- setConfigurations(_quickStartConfigurations);
- else
{
- setConfigurations(_defaultConfigurations);
+ setConfigurationClasses(QUICKSTART_CONFIGURATION_CLASSES);
+ }
+ else
+ {
if (isGenerateQuickStart())
{
_preconfigProcessor = new PreconfigureDescriptorProcessor();
getMetaData().addDescriptorProcessor(_preconfigProcessor);
}
}
-
- //inject configurations with config from maven plugin
- for (Configuration c:getConfigurations())
- {
- if (c instanceof EnvConfiguration && getJettyEnvXml() != null)
- ((EnvConfiguration)c).setJettyEnvXml(Resource.toURL(new File(getJettyEnvXml())));
- else if (c instanceof MavenQuickStartConfiguration && getQuickStartWebDescriptor() != null)
- ((MavenQuickStartConfiguration)c).setQuickStartWebXml(getQuickStartWebDescriptor());
- }
//Set up the pattern that tells us where the jars are that need scanning
@@ -404,6 +399,22 @@ public class JettyWebAppContext extends WebAppContext
}
+ @Override
+ protected void loadConfigurations() throws Exception
+ {
+ super.loadConfigurations();
+
+ //inject configurations with config from maven plugin
+ for (Configuration c:getConfigurations())
+ {
+ if (c instanceof EnvConfiguration && getJettyEnvXml() != null)
+ ((EnvConfiguration)c).setJettyEnvXml(Resource.toURL(new File(getJettyEnvXml())));
+ else if (c instanceof MavenQuickStartConfiguration && getQuickStartWebDescriptor() != null)
+ ((MavenQuickStartConfiguration)c).setQuickStartWebXml(getQuickStartWebDescriptor());
+ }
+ }
+
+
/* ------------------------------------------------------------ */
public void doStop () throws Exception
{
diff --git a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/ServerSupport.java b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/ServerSupport.java
index babfe6830a..407a17f2a9 100644
--- a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/ServerSupport.java
+++ b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/ServerSupport.java
@@ -35,6 +35,7 @@ import org.eclipse.jetty.server.handler.DefaultHandler;
import org.eclipse.jetty.server.handler.HandlerCollection;
import org.eclipse.jetty.server.handler.RequestLogHandler;
import org.eclipse.jetty.util.resource.Resource;
+import org.eclipse.jetty.webapp.Configuration;
import org.eclipse.jetty.webapp.WebAppContext;
import org.eclipse.jetty.xml.XmlConfiguration;
@@ -46,6 +47,13 @@ import org.eclipse.jetty.xml.XmlConfiguration;
*/
public class ServerSupport
{
+
+ public static void configureDefaultConfigurationClasses (Server server)
+ {
+ server.setAttribute(Configuration.ATTR, JettyWebAppContext.DEFAULT_CONFIGURATION_CLASSES);
+ }
+
+
/**
* Set up the handler structure to receive a webapp.
* Also put in a DefaultHandler so we get a nice page
diff --git a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/Starter.java b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/Starter.java
index 0f9e361cbd..a5b94536ad 100644
--- a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/Starter.java
+++ b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/Starter.java
@@ -125,7 +125,10 @@ public class Starter
//check if contexts already configured, create if not
ServerSupport.configureHandlers(server, null);
-
+
+ //Set up list of default Configurations to apply to a webapp
+ ServerSupport.configureDefaultConfigurationClasses(server);
+
webApp = new JettyWebAppContext();
//configure webapp from properties file describing unassembled webapp
diff --git a/jetty-nosql/src/main/java/org/eclipse/jetty/nosql/NoSqlSessionManager.java b/jetty-nosql/src/main/java/org/eclipse/jetty/nosql/NoSqlSessionManager.java
index 7d5e9dca58..51b8d60912 100644
--- a/jetty-nosql/src/main/java/org/eclipse/jetty/nosql/NoSqlSessionManager.java
+++ b/jetty-nosql/src/main/java/org/eclipse/jetty/nosql/NoSqlSessionManager.java
@@ -96,7 +96,10 @@ public abstract class NoSqlSessionManager extends AbstractSessionManager impleme
session=race;
}
else
+ {
__log.debug("session loaded ", idInCluster);
+ _sessionsStats.increment();
+ }
//check if the session we just loaded has actually expired, maybe while we weren't running
if (getMaxInactiveInterval() > 0 && session.getAccessed() > 0 && ((getMaxInactiveInterval()*1000L)+session.getAccessed()) < System.currentTimeMillis())
diff --git a/jetty-nosql/src/main/java/org/eclipse/jetty/nosql/mongodb/MongoSessionIdManager.java b/jetty-nosql/src/main/java/org/eclipse/jetty/nosql/mongodb/MongoSessionIdManager.java
index d822ea7926..894a3fda10 100644
--- a/jetty-nosql/src/main/java/org/eclipse/jetty/nosql/mongodb/MongoSessionIdManager.java
+++ b/jetty-nosql/src/main/java/org/eclipse/jetty/nosql/mongodb/MongoSessionIdManager.java
@@ -206,7 +206,7 @@ public class MongoSessionIdManager extends AbstractSessionIdManager
protected void scavenge()
{
long now = System.currentTimeMillis();
- __log.debug("SessionIdManager:scavenge:at {}", now);
+ __log.debug(getWorkerName()+":SessionIdManager:scavenge:at {}", now);
/*
* run a query returning results that:
* - are in the known list of sessionIds
@@ -258,7 +258,7 @@ public class MongoSessionIdManager extends AbstractSessionIdManager
for ( DBObject session : checkSessions )
{
- __log.debug("SessionIdManager:scavenge: expiring session {}", (String)session.get(MongoSessionManager.__ID));
+ __log.debug(getWorkerName()+":SessionIdManager:scavenge: {} expiring session {}", atTime,(String)session.get(MongoSessionManager.__ID));
expireAll((String)session.get(MongoSessionManager.__ID));
}
}
diff --git a/jetty-nosql/src/main/java/org/eclipse/jetty/nosql/mongodb/MongoSessionManager.java b/jetty-nosql/src/main/java/org/eclipse/jetty/nosql/mongodb/MongoSessionManager.java
index df8f917188..8c44e9fa47 100644
--- a/jetty-nosql/src/main/java/org/eclipse/jetty/nosql/mongodb/MongoSessionManager.java
+++ b/jetty-nosql/src/main/java/org/eclipse/jetty/nosql/mongodb/MongoSessionManager.java
@@ -268,7 +268,9 @@ public class MongoSessionManager extends NoSqlSessionManager
if (currentMaxIdle != null && getMaxInactiveInterval() > 0 && getMaxInactiveInterval() < currentMaxIdle)
sets.put(__MAX_IDLE, getMaxInactiveInterval());
if (currentExpiry != null && expiry > 0 && expiry != currentExpiry)
+ {
sets.put(__EXPIRY, expiry);
+ }
}
}
diff --git a/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/ConnectHandler.java b/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/ConnectHandler.java
index 71a75bc899..7fd0b777fc 100644
--- a/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/ConnectHandler.java
+++ b/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/ConnectHandler.java
@@ -18,6 +18,7 @@
package org.eclipse.jetty.proxy;
+import java.io.Closeable;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
@@ -51,6 +52,7 @@ import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.handler.HandlerWrapper;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.Callback;
+import org.eclipse.jetty.util.Promise;
import org.eclipse.jetty.util.TypeUtil;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
@@ -160,21 +162,17 @@ public class ConnectHandler extends HandlerWrapper
protected void doStart() throws Exception
{
if (executor == null)
- {
- setExecutor(getServer().getThreadPool());
- }
+ executor = getServer().getThreadPool();
+
if (scheduler == null)
- {
- setScheduler(new ScheduledExecutorScheduler());
- addBean(getScheduler());
- }
+ addBean(scheduler = new ScheduledExecutorScheduler());
+
if (bufferPool == null)
- {
- setByteBufferPool(new MappedByteBufferPool());
- addBean(getByteBufferPool());
- }
+ addBean(bufferPool = new MappedByteBufferPool());
+
addBean(selector = newSelectorManager());
selector.setConnectTimeout(getConnectTimeout());
+
super.doStart();
}
@@ -191,16 +189,8 @@ public class ConnectHandler extends HandlerWrapper
String serverAddress = request.getRequestURI();
if (LOG.isDebugEnabled())
LOG.debug("CONNECT request for {}", serverAddress);
- try
- {
- handleConnect(baseRequest, request, response, serverAddress);
- }
- catch (Exception x)
- {
- // TODO
- LOG.warn("ConnectHandler " + baseRequest.getHttpURI() + " " + x);
- LOG.debug(x);
- }
+
+ handleConnect(baseRequest, request, response, serverAddress);
}
else
{
@@ -249,32 +239,40 @@ public class ConnectHandler extends HandlerWrapper
return;
}
- SocketChannel channel = SocketChannel.open();
- channel.socket().setTcpNoDelay(true);
- channel.configureBlocking(false);
-
- AsyncContext asyncContext = request.startAsync();
- asyncContext.setTimeout(0);
-
HttpTransport transport = baseRequest.getHttpChannel().getHttpTransport();
-
// TODO Handle CONNECT over HTTP2!
if (!(transport instanceof HttpConnection))
{
if (LOG.isDebugEnabled())
- LOG.debug("CONNECT forbidden for {}", transport);
+ LOG.debug("CONNECT not supported for {}", transport);
sendConnectResponse(request, response, HttpServletResponse.SC_FORBIDDEN);
return;
}
- InetSocketAddress address = newConnectAddress(host, port);
+ AsyncContext asyncContext = request.startAsync();
+ asyncContext.setTimeout(0);
+
if (LOG.isDebugEnabled())
- LOG.debug("Connecting to {}", address);
- ConnectContext connectContext = new ConnectContext(request, response, asyncContext, (HttpConnection)transport);
- if (channel.connect(address))
- selector.accept(channel, connectContext);
- else
- selector.connect(channel, connectContext);
+ LOG.debug("Connecting to {}:{}", host, port);
+
+ connectToServer(request, host, port, new Promise<SocketChannel>()
+ {
+ @Override
+ public void succeeded(SocketChannel channel)
+ {
+ ConnectContext connectContext = new ConnectContext(request, response, asyncContext, (HttpConnection)transport);
+ if (channel.isConnected())
+ selector.accept(channel, connectContext);
+ else
+ selector.connect(channel, connectContext);
+ }
+
+ @Override
+ public void failed(Throwable x)
+ {
+ onConnectFailure(request, response, asyncContext, x);
+ }
+ });
}
catch (Exception x)
{
@@ -282,37 +280,59 @@ public class ConnectHandler extends HandlerWrapper
}
}
- /* ------------------------------------------------------------ */
- /** Create the address the connect channel will connect to.
- * @param host The host from the connect request
- * @param port The port from the connect request
+ protected void connectToServer(HttpServletRequest request, String host, int port, Promise<SocketChannel> promise)
+ {
+ SocketChannel channel = null;
+ try
+ {
+ channel = SocketChannel.open();
+ channel.socket().setTcpNoDelay(true);
+ channel.configureBlocking(false);
+ InetSocketAddress address = newConnectAddress(host, port);
+ channel.connect(address);
+ promise.succeeded(channel);
+ }
+ catch (Throwable x)
+ {
+ close(channel);
+ promise.failed(x);
+ }
+ }
+
+ private void close(Closeable closeable)
+ {
+ try
+ {
+ if (closeable != null)
+ closeable.close();
+ }
+ catch (Throwable x)
+ {
+ LOG.ignore(x);
+ }
+ }
+
+ /**
+ * Creates the server address to connect to.
+ *
+ * @param host The host from the CONNECT request
+ * @param port The port from the CONNECT request
* @return The InetSocketAddress to connect to.
*/
protected InetSocketAddress newConnectAddress(String host, int port)
{
return new InetSocketAddress(host, port);
}
-
+
protected void onConnectSuccess(ConnectContext connectContext, UpstreamConnection upstreamConnection)
{
- HttpConnection httpConnection = connectContext.getHttpConnection();
- ByteBuffer requestBuffer = httpConnection.getRequestBuffer();
- ByteBuffer buffer = BufferUtil.EMPTY_BUFFER;
- int remaining = requestBuffer.remaining();
- if (remaining > 0)
- {
- buffer = bufferPool.acquire(remaining, requestBuffer.isDirect());
- BufferUtil.flipToFill(buffer);
- buffer.put(requestBuffer);
- buffer.flip();
- }
-
ConcurrentMap<String, Object> context = connectContext.getContext();
HttpServletRequest request = connectContext.getRequest();
prepareContext(request, context);
+ HttpConnection httpConnection = connectContext.getHttpConnection();
EndPoint downstreamEndPoint = httpConnection.getEndPoint();
- DownstreamConnection downstreamConnection = newDownstreamConnection(downstreamEndPoint, context, buffer);
+ DownstreamConnection downstreamConnection = newDownstreamConnection(downstreamEndPoint, context);
downstreamConnection.setInputBufferSize(getBufferSize());
upstreamConnection.setConnection(downstreamConnection);
@@ -324,6 +344,7 @@ public class ConnectHandler extends HandlerWrapper
sendConnectResponse(request, response, HttpServletResponse.SC_OK);
upgradeConnection(request, response, downstreamConnection);
+
connectContext.getAsyncContext().complete();
}
@@ -349,7 +370,8 @@ public class ConnectHandler extends HandlerWrapper
}
catch (IOException x)
{
- // TODO: nothing we can do, close the connection
+ if (LOG.isDebugEnabled())
+ LOG.debug("Could not send CONNECT response", x);
}
}
@@ -367,9 +389,9 @@ public class ConnectHandler extends HandlerWrapper
return true;
}
- protected DownstreamConnection newDownstreamConnection(EndPoint endPoint, ConcurrentMap<String, Object> context, ByteBuffer buffer)
+ protected DownstreamConnection newDownstreamConnection(EndPoint endPoint, ConcurrentMap<String, Object> context)
{
- return new DownstreamConnection(endPoint, getExecutor(), getByteBufferPool(), context, buffer);
+ return new DownstreamConnection(endPoint, getExecutor(), getByteBufferPool(), context);
}
protected UpstreamConnection newUpstreamConnection(EndPoint endPoint, ConnectContext connectContext)
@@ -396,13 +418,17 @@ public class ConnectHandler extends HandlerWrapper
*
* @param endPoint the endPoint to read from
* @param buffer the buffer to read data into
+ * @param context the context information related to the connection
* @return the number of bytes read (possibly 0 since the read is non-blocking)
* or -1 if the channel has been closed remotely
* @throws IOException if the endPoint cannot be read
*/
- protected int read(EndPoint endPoint, ByteBuffer buffer) throws IOException
+ protected int read(EndPoint endPoint, ByteBuffer buffer, ConcurrentMap<String, Object> context) throws IOException
{
- return endPoint.fill(buffer);
+ int read = endPoint.fill(buffer);
+ if (LOG.isDebugEnabled())
+ LOG.debug("{} read {} bytes", this, read);
+ return read;
}
/**
@@ -411,8 +437,9 @@ public class ConnectHandler extends HandlerWrapper
* @param endPoint the endPoint to write to
* @param buffer the buffer to write
* @param callback the completion callback to invoke
+ * @param context the context information related to the connection
*/
- protected void write(EndPoint endPoint, ByteBuffer buffer, Callback callback)
+ protected void write(EndPoint endPoint, ByteBuffer buffer, Callback callback, ConcurrentMap<String, Object> context)
{
if (LOG.isDebugEnabled())
LOG.debug("{} writing {} bytes", this, buffer.remaining());
@@ -494,14 +521,9 @@ public class ConnectHandler extends HandlerWrapper
@Override
protected void connectionFailed(SocketChannel channel, final Throwable ex, final Object attachment)
{
- getExecutor().execute(new Runnable()
- {
- public void run()
- {
- ConnectContext connectContext = (ConnectContext)attachment;
- onConnectFailure(connectContext.request, connectContext.response, connectContext.asyncContext, ex);
- }
- });
+ close(channel);
+ ConnectContext connectContext = (ConnectContext)attachment;
+ onConnectFailure(connectContext.request, connectContext.response, connectContext.asyncContext, ex);
}
}
@@ -561,37 +583,36 @@ public class ConnectHandler extends HandlerWrapper
public void onOpen()
{
super.onOpen();
- getExecutor().execute(new Runnable()
- {
- public void run()
- {
- onConnectSuccess(connectContext, UpstreamConnection.this);
- fillInterested();
- }
- });
+ onConnectSuccess(connectContext, UpstreamConnection.this);
+ fillInterested();
}
@Override
protected int read(EndPoint endPoint, ByteBuffer buffer) throws IOException
{
- return ConnectHandler.this.read(endPoint, buffer);
+ return ConnectHandler.this.read(endPoint, buffer, getContext());
}
@Override
protected void write(EndPoint endPoint, ByteBuffer buffer,Callback callback)
{
- ConnectHandler.this.write(endPoint, buffer, callback);
+ ConnectHandler.this.write(endPoint, buffer, callback, getContext());
}
}
- public class DownstreamConnection extends ProxyConnection
+ public class DownstreamConnection extends ProxyConnection implements Connection.UpgradeTo
{
- private final ByteBuffer buffer;
+ private ByteBuffer buffer;
- public DownstreamConnection(EndPoint endPoint, Executor executor, ByteBufferPool bufferPool, ConcurrentMap<String, Object> context, ByteBuffer buffer)
+ public DownstreamConnection(EndPoint endPoint, Executor executor, ByteBufferPool bufferPool, ConcurrentMap<String, Object> context)
{
super(endPoint, executor, bufferPool, context);
- this.buffer = buffer;
+ }
+
+ @Override
+ public void onUpgradeTo(ByteBuffer buffer)
+ {
+ this.buffer = buffer == null ? BufferUtil.EMPTY_BUFFER : buffer;
}
@Override
@@ -623,13 +644,13 @@ public class ConnectHandler extends HandlerWrapper
@Override
protected int read(EndPoint endPoint, ByteBuffer buffer) throws IOException
{
- return ConnectHandler.this.read(endPoint, buffer);
+ return ConnectHandler.this.read(endPoint, buffer, getContext());
}
@Override
protected void write(EndPoint endPoint, ByteBuffer buffer, Callback callback)
{
- ConnectHandler.this.write(endPoint, buffer, callback);
+ ConnectHandler.this.write(endPoint, buffer, callback, getContext());
}
}
}
diff --git a/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/ConnectHandlerTest.java b/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/ConnectHandlerTest.java
index f601975408..780e70686a 100644
--- a/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/ConnectHandlerTest.java
+++ b/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/ConnectHandlerTest.java
@@ -27,6 +27,8 @@ import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
+import java.nio.ByteBuffer;
+import java.nio.channels.SocketChannel;
import java.nio.charset.StandardCharsets;
import java.util.Locale;
import java.util.concurrent.ConcurrentMap;
@@ -36,12 +38,15 @@ import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
+import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.server.handler.AbstractHandler;
import org.eclipse.jetty.toolchain.test.http.SimpleHttpResponse;
import org.eclipse.jetty.util.B64Code;
+import org.eclipse.jetty.util.Callback;
+import org.eclipse.jetty.util.Promise;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
@@ -631,12 +636,33 @@ public class ConnectHandlerTest extends AbstractConnectHandlerTest
}
@Override
+ protected void connectToServer(HttpServletRequest request, String host, int port, Promise<SocketChannel> promise)
+ {
+ Assert.assertEquals(contextValue, request.getAttribute(contextKey));
+ super.connectToServer(request, host, port, promise);
+ }
+
+ @Override
protected void prepareContext(HttpServletRequest request, ConcurrentMap<String, Object> context)
{
// Transfer data from the HTTP request to the connection context
Assert.assertEquals(contextValue, request.getAttribute(contextKey));
context.put(contextKey, request.getAttribute(contextKey));
}
+
+ @Override
+ protected int read(EndPoint endPoint, ByteBuffer buffer, ConcurrentMap<String, Object> context) throws IOException
+ {
+ Assert.assertEquals(contextValue, context.get(contextKey));
+ return super.read(endPoint, buffer, context);
+ }
+
+ @Override
+ protected void write(EndPoint endPoint, ByteBuffer buffer, Callback callback, ConcurrentMap<String, Object> context)
+ {
+ Assert.assertEquals(contextValue, context.get(contextKey));
+ super.write(endPoint, buffer, callback, context);
+ }
});
proxy.start();
diff --git a/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/ProxyServletTest.java b/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/ProxyServletTest.java
index 1f96b48a51..4d2dcc9233 100644
--- a/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/ProxyServletTest.java
+++ b/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/ProxyServletTest.java
@@ -61,6 +61,7 @@ import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
+import org.eclipse.jetty.client.DuplexConnectionPool;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.HttpContentResponse;
import org.eclipse.jetty.client.HttpProxy;
@@ -1081,7 +1082,8 @@ public class ProxyServletTest
Assert.assertEquals(-1, input.read());
HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination("http", "localhost", port);
- Assert.assertEquals(0, destination.getConnectionPool().getIdleConnections().size());
+ DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool();
+ Assert.assertEquals(0, connectionPool.getIdleConnections().size());
}
@Test
@@ -1154,7 +1156,8 @@ public class ProxyServletTest
}
HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination("http", "localhost", port);
- Assert.assertEquals(0, destination.getConnectionPool().getIdleConnections().size());
+ DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool();
+ Assert.assertEquals(0, connectionPool.getIdleConnections().size());
}
@Test
diff --git a/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/ProxyTunnellingTest.java b/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/ProxyTunnellingTest.java
index e563962b6d..fd333cd88e 100644
--- a/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/ProxyTunnellingTest.java
+++ b/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/ProxyTunnellingTest.java
@@ -54,6 +54,7 @@ import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
import org.eclipse.jetty.toolchain.test.TestTracker;
import org.eclipse.jetty.util.Promise;
import org.eclipse.jetty.util.ssl.SslContextFactory;
+import org.eclipse.jetty.util.thread.QueuedThreadPool;
import org.hamcrest.Matchers;
import org.junit.After;
import org.junit.Assert;
@@ -87,7 +88,10 @@ public class ProxyTunnellingTest
sslContextFactory.setKeyStorePath(keyStorePath);
sslContextFactory.setKeyStorePassword("storepwd");
sslContextFactory.setKeyManagerPassword("keypwd");
- server = new Server();
+
+ QueuedThreadPool serverThreads = new QueuedThreadPool();
+ serverThreads.setName("server");
+ server = new Server(serverThreads);
serverConnector = new ServerConnector(server, sslContextFactory);
server.addConnector(serverConnector);
server.setHandler(handler);
@@ -101,7 +105,9 @@ public class ProxyTunnellingTest
protected void startProxy(ConnectHandler connectHandler) throws Exception
{
- proxy = new Server();
+ QueuedThreadPool proxyThreads = new QueuedThreadPool();
+ proxyThreads.setName("proxy");
+ proxy = new Server(proxyThreads);
proxyConnector = new ServerConnector(proxy);
proxy.addConnector(proxyConnector);
// Under Windows, it takes a while to detect that a connection
@@ -136,7 +142,7 @@ public class ProxyTunnellingTest
}
}
- @Test
+ @Test(timeout=60000)
public void testOneExchangeViaSSL() throws Exception
{
startSSLServer(new ServerHandler());
@@ -167,7 +173,7 @@ public class ProxyTunnellingTest
}
}
- @Test
+ @Test(timeout=60000)
public void testTwoExchangesViaSSL() throws Exception
{
startSSLServer(new ServerHandler());
@@ -210,7 +216,7 @@ public class ProxyTunnellingTest
}
}
- @Test
+ @Test(timeout=60000)
public void testTwoConcurrentExchangesViaSSL() throws Exception
{
startSSLServer(new ServerHandler());
@@ -278,7 +284,7 @@ public class ProxyTunnellingTest
}
}
- @Test
+ @Test(timeout=60000)
public void testShortIdleTimeoutOverriddenByRequest() throws Exception
{
// Short idle timeout for HttpClient.
@@ -331,7 +337,7 @@ public class ProxyTunnellingTest
}
}
- @Test
+ @Test(timeout=60000)
public void testProxyDown() throws Exception
{
startSSLServer(new ServerHandler());
@@ -363,7 +369,7 @@ public class ProxyTunnellingTest
}
}
- @Test
+ @Test(timeout=60000)
public void testServerDown() throws Exception
{
startSSLServer(new ServerHandler());
@@ -395,7 +401,7 @@ public class ProxyTunnellingTest
}
}
- @Test
+ @Test(timeout=60000)
public void testProxyClosesConnection() throws Exception
{
startSSLServer(new ServerHandler());
@@ -429,7 +435,7 @@ public class ProxyTunnellingTest
}
}
- @Test
+ @Test(timeout=60000)
@Ignore("External Proxy Server no longer stable enough for testing")
public void testExternalProxy() throws Exception
{
diff --git a/jetty-rewrite/src/main/config/etc/jetty-rewrite.xml b/jetty-rewrite/src/main/config/etc/jetty-rewrite.xml
index 63af3c21d7..65a5dea44a 100644
--- a/jetty-rewrite/src/main/config/etc/jetty-rewrite.xml
+++ b/jetty-rewrite/src/main/config/etc/jetty-rewrite.xml
@@ -19,31 +19,29 @@
<Set name="rewriteRequestURI"><Property name="jetty.rewrite.rewriteRequestURI" deprecated="rewrite.rewriteRequestURI" default="true"/></Set>
<Set name="rewritePathInfo"><Property name="jetty.rewrite.rewritePathInfo" deprecated="rewrite.rewritePathInfo" default="false"/></Set>
<Set name="originalPathAttribute"><Property name="jetty.rewrite.originalPathAttribute" deprecated="rewrite.originalPathAttribute" default="requestedPath"/></Set>
- </New>
- <!-- Set DispatcherTypes -->
- <Set name="dispatcherTypes">
- <Array type="javax.servlet.DispatcherType">
- <Item><Call class="javax.servlet.DispatcherType" name="valueOf"><Arg>REQUEST</Arg></Call></Item>
- <Item><Call class="javax.servlet.DispatcherType" name="valueOf"><Arg>ASYNC</Arg></Call></Item>
- </Array>
- </Set>
- </Set>
-
-
-
- <!-- example rule -->
- <!--
- <Call name="addRule">
- <Arg>
- <New class="org.eclipse.jetty.rewrite.handler.HeaderPatternRule">
- <Set name="pattern">/favicon.ico</Set>
- <Set name="name">Cache-Control</Set>
- <Set name="value">Max-Age=3600,public</Set>
- <Set name="terminating">true</Set>
- </New>
- </Arg>
- </Call>
- -->
+ <!-- Set DispatcherTypes -->
+ <Set name="dispatcherTypes">
+ <Array type="javax.servlet.DispatcherType">
+ <Item><Call class="javax.servlet.DispatcherType" name="valueOf"><Arg>REQUEST</Arg></Call></Item>
+ <Item><Call class="javax.servlet.DispatcherType" name="valueOf"><Arg>ASYNC</Arg></Call></Item>
+ </Array>
+ </Set>
+
+ <!-- example rule -->
+ <!--
+ <Call name="addRule">
+ <Arg>
+ <New class="org.eclipse.jetty.rewrite.handler.HeaderPatternRule">
+ <Set name="pattern">/favicon.ico</Set>
+ <Set name="name">Cache-Control</Set>
+ <Set name="value">Max-Age=3600,public</Set>
+ <Set name="terminating">true</Set>
+ </New>
+ </Arg>
+ </Call>
+ -->
+ </New>
+ </Set>
</Configure>
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractNCSARequestLog.java b/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractNCSARequestLog.java
index ab46bd5217..036b5142ad 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractNCSARequestLog.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractNCSARequestLog.java
@@ -142,7 +142,7 @@ public abstract class AbstractNCSARequestLog extends AbstractLifeCycle implement
buf.append("] \"");
append(buf,request.getMethod());
buf.append(' ');
- append(buf,request.getHttpURI().toString());
+ append(buf,request.getOriginalURI());
buf.append(' ');
append(buf,request.getProtocol());
buf.append("\" ");
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/Dispatcher.java b/jetty-server/src/main/java/org/eclipse/jetty/server/Dispatcher.java
index 2bb8739591..1a04bd749a 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/Dispatcher.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/Dispatcher.java
@@ -31,10 +31,7 @@ import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
-import org.eclipse.jetty.http.HttpFields;
-import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.HttpURI;
-import org.eclipse.jetty.http.MetaData;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.util.Attributes;
import org.eclipse.jetty.util.MultiMap;
@@ -222,28 +219,6 @@ public class Dispatcher implements RequestDispatcher
}
}
- @Deprecated
- public void push(ServletRequest request)
- {
- Request baseRequest = Request.getBaseRequest(request);
- HttpFields fields = new HttpFields(baseRequest.getHttpFields());
-
- String query=baseRequest.getQueryString();
- if (_uri.hasQuery())
- {
- if (query==null)
- query=_uri.getQuery();
- else
- query=query+"&"+_uri.getQuery(); // TODO is this correct semantic?
- }
-
- HttpURI uri = HttpURI.createHttpURI(request.getScheme(),request.getServerName(),request.getServerPort(),_uri.getPath(),baseRequest.getHttpURI().getParam(),query,null);
-
- MetaData.Request push = new MetaData.Request(HttpMethod.GET.asString(),uri,baseRequest.getHttpVersion(),fields);
-
- baseRequest.getHttpChannel().getHttpTransport().push(push);
- }
-
@Override
public String toString()
{
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannel.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannel.java
index 8f423e463f..95b4f59a9e 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannel.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannel.java
@@ -29,6 +29,7 @@ import java.nio.channels.ClosedChannelException;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
import javax.servlet.DispatcherType;
import javax.servlet.RequestDispatcher;
@@ -264,6 +265,8 @@ public class HttpChannel implements Runnable, HttpOutput.Interceptor
handle();
}
+ AtomicReference<Action> caller = new AtomicReference<>();
+
/**
* @return True if the channel is ready to continue handling (ie it is not suspended)
*/
@@ -297,7 +300,6 @@ public class HttpChannel implements Runnable, HttpOutput.Interceptor
throw new IllegalStateException("state=" + _state);
_request.setHandled(false);
_response.getHttpOutput().reopen();
- _request.setDispatcherType(DispatcherType.REQUEST);
List<HttpConfiguration.Customizer> customizers = _configuration.getCustomizers();
if (!customizers.isEmpty())
@@ -305,7 +307,15 @@ public class HttpChannel implements Runnable, HttpOutput.Interceptor
for (HttpConfiguration.Customizer customizer : customizers)
customizer.customize(getConnector(), _configuration, _request);
}
- getServer().handle(this);
+ try
+ {
+ _request.setDispatcherType(DispatcherType.REQUEST);
+ getServer().handle(this);
+ }
+ finally
+ {
+ _request.setDispatcherType(null);
+ }
break;
}
@@ -313,8 +323,16 @@ public class HttpChannel implements Runnable, HttpOutput.Interceptor
{
_request.setHandled(false);
_response.getHttpOutput().reopen();
- _request.setDispatcherType(DispatcherType.ASYNC);
- getServer().handleAsync(this);
+
+ try
+ {
+ _request.setDispatcherType(DispatcherType.ASYNC);
+ getServer().handleAsync(this);
+ }
+ finally
+ {
+ _request.setDispatcherType(null);
+ }
break;
}
@@ -336,8 +354,16 @@ public class HttpChannel implements Runnable, HttpOutput.Interceptor
_request.setAttribute(ERROR_STATUS_CODE,code);
_request.setHandled(false);
_response.getHttpOutput().reopen();
- _request.setDispatcherType(DispatcherType.ERROR);
- getServer().handle(this);
+
+ try
+ {
+ _request.setDispatcherType(DispatcherType.ERROR);
+ getServer().handle(this);
+ }
+ finally
+ {
+ _request.setDispatcherType(null);
+ }
}
break;
}
@@ -390,10 +416,6 @@ public class HttpChannel implements Runnable, HttpOutput.Interceptor
else
handleException(failure);
}
- finally
- {
- _request.setDispatcherType(null);
- }
action = _state.unhandle();
}
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/ProxyConnectionFactory.java b/jetty-server/src/main/java/org/eclipse/jetty/server/ProxyConnectionFactory.java
index b72cf5718d..7a5e51cca1 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/ProxyConnectionFactory.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/ProxyConnectionFactory.java
@@ -19,17 +19,23 @@
package org.eclipse.jetty.server;
import java.io.IOException;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.ReadPendingException;
import java.nio.channels.WritePendingException;
+import java.nio.charset.StandardCharsets;
import java.util.Iterator;
import org.eclipse.jetty.io.AbstractConnection;
import org.eclipse.jetty.io.Connection;
import org.eclipse.jetty.io.EndPoint;
+import org.eclipse.jetty.util.AttributesMap;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.Callback;
+import org.eclipse.jetty.util.TypeUtil;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
@@ -38,14 +44,17 @@ import org.eclipse.jetty.util.log.Logger;
/**
* ConnectionFactory for the PROXY Protocol.
* <p>This factory can be placed in front of any other connection factory
- * to process the proxy line before the normal protocol handling</p>
+ * to process the proxy v1 or v2 line before the normal protocol handling</p>
*
* @see <a href="http://www.haproxy.org/download/1.5/doc/proxy-protocol.txt">http://www.haproxy.org/download/1.5/doc/proxy-protocol.txt</a>
*/
public class ProxyConnectionFactory extends AbstractConnectionFactory
{
+ public static final String TLS_VERSION = "TLS_VERSION";
+
private static final Logger LOG = Log.getLogger(ProxyConnectionFactory.class);
private final String _next;
+ private int _maxProxyHeader=1024;
/* ------------------------------------------------------------ */
/** Proxy Connection Factory that uses the next ConnectionFactory
@@ -63,6 +72,16 @@ public class ProxyConnectionFactory extends AbstractConnectionFactory
_next=nextProtocol;
}
+ public int getMaxProxyHeader()
+ {
+ return _maxProxyHeader;
+ }
+
+ public void setMaxProxyHeader(int maxProxyHeader)
+ {
+ _maxProxyHeader = maxProxyHeader;
+ }
+
@Override
public Connection newConnection(Connector connector, EndPoint endp)
{
@@ -80,10 +99,79 @@ public class ProxyConnectionFactory extends AbstractConnectionFactory
}
}
- return new ProxyConnection(endp,connector,next);
+ return new ProxyProtocolV1orV2Connection(endp,connector,next);
+ }
+
+ public class ProxyProtocolV1orV2Connection extends AbstractConnection
+ {
+ private final Connector _connector;
+ private final String _next;
+ private ByteBuffer _buffer = BufferUtil.allocate(16);
+
+ protected ProxyProtocolV1orV2Connection(EndPoint endp, Connector connector, String next)
+ {
+ super(endp,connector.getExecutor());
+ _connector=connector;
+ _next=next;
+ }
+
+ @Override
+ public void onOpen()
+ {
+ super.onOpen();
+ fillInterested();
+ }
+
+ @Override
+ public void onFillable()
+ {
+ try
+ {
+ while(BufferUtil.space(_buffer)>0)
+ {
+ // Read data
+ int fill=getEndPoint().fill(_buffer);
+ if (fill<0)
+ {
+ getEndPoint().shutdownOutput();
+ return;
+ }
+ if (fill==0)
+ {
+ fillInterested();
+ return;
+ }
+ }
+
+ // Is it a V1?
+ switch(_buffer.get(0))
+ {
+ case 'P':
+ {
+ ProxyProtocolV1Connection v1 = new ProxyProtocolV1Connection(getEndPoint(),_connector,_next,_buffer);
+ getEndPoint().upgrade(v1);
+ return;
+ }
+ case 0x0D:
+ {
+ ProxyProtocolV2Connection v2 = new ProxyProtocolV2Connection(getEndPoint(),_connector,_next,_buffer);
+ getEndPoint().upgrade(v2);
+ return;
+ }
+ default:
+ LOG.warn("Not PROXY protocol for {}",getEndPoint());
+ close();
+ }
+ }
+ catch (Throwable x)
+ {
+ LOG.warn("PROXY error for "+getEndPoint(),x);
+ close();
+ }
+ }
}
- public static class ProxyConnection extends AbstractConnection
+ public static class ProxyProtocolV1Connection extends AbstractConnection
{
// 0 1 2 3 4 5 6
// 98765432109876543210987654321
@@ -97,11 +185,13 @@ public class ProxyConnectionFactory extends AbstractConnectionFactory
private int _fields;
private int _length;
- protected ProxyConnection(EndPoint endp, Connector connector, String next)
+ protected ProxyProtocolV1Connection(EndPoint endp, Connector connector, String next,ByteBuffer buffer)
{
super(endp,connector.getExecutor());
_connector=connector;
_next=next;
+ _length=buffer.remaining();
+ parse(buffer);
}
@Override
@@ -110,16 +200,60 @@ public class ProxyConnectionFactory extends AbstractConnectionFactory
super.onOpen();
fillInterested();
}
+
+
+ private boolean parse(ByteBuffer buffer)
+ {
+ // parse fields
+ while (buffer.hasRemaining())
+ {
+ byte b = buffer.get();
+ if (_fields<6)
+ {
+ if (b==' ' || b=='\r' && _fields==5)
+ {
+ _field[_fields++]=_builder.toString();
+ _builder.setLength(0);
+ }
+ else if (b<' ')
+ {
+ LOG.warn("Bad character {} for {}",b&0xFF,getEndPoint());
+ close();
+ return false;
+ }
+ else
+ {
+ _builder.append((char)b);
+ }
+ }
+ else
+ {
+ if (b=='\n')
+ {
+ _fields=7;
+ return true;
+ }
+ LOG.warn("Bad CRLF for {}",getEndPoint());
+ close();
+ return false;
+ }
+ }
+
+ return true;
+ }
+
@Override
public void onFillable()
{
try
{
ByteBuffer buffer=null;
- loop: while(true)
+ while(_fields<7)
{
// Create a buffer that will not read too much data
+ // since once read it is impossible to push back for the
+ // real connection to read it.
int size=Math.max(1,__size[_fields]-_builder.length());
if (buffer==null || buffer.capacity()!=size)
buffer=BufferUtil.allocate(size);
@@ -147,38 +281,8 @@ public class ProxyConnectionFactory extends AbstractConnectionFactory
return;
}
- // parse fields
- while (buffer.hasRemaining())
- {
- byte b = buffer.get();
- if (_fields<6)
- {
- if (b==' ' || b=='\r' && _fields==5)
- {
- _field[_fields++]=_builder.toString();
- _builder.setLength(0);
- }
- else if (b<' ')
- {
- LOG.warn("Bad character {} for {}",b&0xFF,getEndPoint());
- close();
- return;
- }
- else
- {
- _builder.append((char)b);
- }
- }
- else
- {
- if (b=='\n')
- break loop;
-
- LOG.warn("Bad CRLF for {}",getEndPoint());
- close();
- return;
- }
- }
+ if (!parse(buffer))
+ return;
}
// Check proxy
@@ -197,10 +301,13 @@ public class ProxyConnectionFactory extends AbstractConnectionFactory
ConnectionFactory connectionFactory = _connector.getConnectionFactory(_next);
if (connectionFactory == null)
{
- LOG.info("Next protocol '{}' for {}",_next,getEndPoint());
+ LOG.warn("No Next protocol '{}' for {}",_next,getEndPoint());
close();
return;
}
+
+ if (LOG.isDebugEnabled())
+ LOG.warn("Next protocol '{}' for {} r={} l={}",_next,getEndPoint(),remote,local);
EndPoint endPoint = new ProxyEndPoint(getEndPoint(),remote,local);
Connection newConnection = connectionFactory.newConnection(_connector, endPoint);
@@ -213,8 +320,260 @@ public class ProxyConnectionFactory extends AbstractConnectionFactory
}
}
}
+
+
+ enum Family { UNSPEC, INET, INET6, UNIX };
+ enum Transport { UNSPEC, STREAM, DGRAM };
+ private static final byte[] MAGIC = new byte[]{0x0D,0x0A,0x0D,0x0A,0x00,0x0D,0x0A,0x51,0x55,0x49,0x54,0x0A};
+
+ public class ProxyProtocolV2Connection extends AbstractConnection
+ {
+ private final Connector _connector;
+ private final String _next;
+ private final boolean _local;
+ private final Family _family;
+ private final Transport _transport;
+ private final int _length;
+ private final ByteBuffer _buffer;
+
+ protected ProxyProtocolV2Connection(EndPoint endp, Connector connector, String next,ByteBuffer buffer)
+ throws IOException
+ {
+ super(endp,connector.getExecutor());
+ _connector=connector;
+ _next=next;
+
+ if (buffer.remaining()!=16)
+ throw new IllegalStateException();
+
+ if (LOG.isDebugEnabled())
+ LOG.debug("PROXYv2 header {} for {}",BufferUtil.toHexSummary(buffer),this);
+
+ // struct proxy_hdr_v2 {
+ // uint8_t sig[12]; /* hex 0D 0A 0D 0A 00 0D 0A 51 55 49 54 0A */
+ // uint8_t ver_cmd; /* protocol version and command */
+ // uint8_t fam; /* protocol family and address */
+ // uint16_t len; /* number of following bytes part of the header */
+ // };
+ for (int i=0;i<MAGIC.length;i++)
+ if (buffer.get()!=MAGIC[i])
+ throw new IOException("Bad PROXY protocol v2 signature");
+
+ int versionAndCommand = 0xff & buffer.get();
+ if ((versionAndCommand&0xf0) != 0x20)
+ throw new IOException("Bad PROXY protocol v2 version");
+ _local=(versionAndCommand&0xf)==0x00;
+
+ int transportAndFamily = 0xff & buffer.get();
+ switch(transportAndFamily>>4)
+ {
+ case 0: _family=Family.UNSPEC; break;
+ case 1: _family=Family.INET; break;
+ case 2: _family=Family.INET6; break;
+ case 3: _family=Family.UNIX; break;
+ default:
+ throw new IOException("Bad PROXY protocol v2 family");
+ }
+
+ switch(0xf&transportAndFamily)
+ {
+ case 0: _transport=Transport.UNSPEC; break;
+ case 1: _transport=Transport.STREAM; break;
+ case 2: _transport=Transport.DGRAM; break;
+ default:
+ throw new IOException("Bad PROXY protocol v2 family");
+ }
+
+ _length = buffer.getChar();
+
+ if (!_local && (_family==Family.UNSPEC || _family==Family.UNIX || _transport!=Transport.STREAM))
+ throw new IOException(String.format("Unsupported PROXY protocol v2 mode 0x%x,0x%x",versionAndCommand,transportAndFamily));
+
+ if (_length>_maxProxyHeader)
+ throw new IOException(String.format("Unsupported PROXY protocol v2 mode 0x%x,0x%x,0x%x",versionAndCommand,transportAndFamily,_length));
+
+ _buffer = _length>0?BufferUtil.allocate(_length):BufferUtil.EMPTY_BUFFER;
+ }
+
+ @Override
+ public void onOpen()
+ {
+ super.onOpen();
+ if (_buffer.remaining()==_length)
+ next();
+ else
+ fillInterested();
+ }
+
+ @Override
+ public void onFillable()
+ {
+ try
+ {
+ while(_buffer.remaining()<_length)
+ {
+ // Read data
+ int fill=getEndPoint().fill(_buffer);
+ if (fill<0)
+ {
+ getEndPoint().shutdownOutput();
+ return;
+ }
+ if (fill==0)
+ {
+ fillInterested();
+ return;
+ }
+ }
+ }
+ catch (Throwable x)
+ {
+ LOG.warn("PROXY error for "+getEndPoint(),x);
+ close();
+ return;
+ }
+
+ next();
+ }
+
+ private void next()
+ {
+ if (LOG.isDebugEnabled())
+ LOG.debug("PROXYv2 next {} from {} for {}",_next,BufferUtil.toHexSummary(_buffer),this);
+
+ // Create the next protocol
+ ConnectionFactory connectionFactory = _connector.getConnectionFactory(_next);
+ if (connectionFactory == null)
+ {
+ LOG.info("Next protocol '{}' for {}",_next,getEndPoint());
+ close();
+ return;
+ }
+
+ // Do we need to wrap the endpoint?
+ EndPoint endPoint=getEndPoint();
+ if (!_local)
+ {
+ try
+ {
+ InetAddress src;
+ InetAddress dst;
+ int sp;
+ int dp;
+
+ switch(_family)
+ {
+ case INET:
+ {
+ byte[] addr=new byte[4];
+ _buffer.get(addr);
+ src = Inet4Address.getByAddress(addr);
+ _buffer.get(addr);
+ dst = Inet4Address.getByAddress(addr);
+ sp = _buffer.getChar();
+ dp = _buffer.getChar();
+
+ break;
+ }
+
+ case INET6:
+ {
+ byte[] addr=new byte[16];
+ _buffer.get(addr);
+ src = Inet6Address.getByAddress(addr);
+ _buffer.get(addr);
+ dst = Inet6Address.getByAddress(addr);
+ sp = _buffer.getChar();
+ dp = _buffer.getChar();
+ break;
+ }
+
+ default:
+ throw new IllegalStateException();
+ }
+
+
+ // Extract Addresses
+ InetSocketAddress remote=new InetSocketAddress(src,sp);
+ InetSocketAddress local =new InetSocketAddress(dst,dp);
+ ProxyEndPoint proxyEndPoint = new ProxyEndPoint(endPoint,remote,local);
+ endPoint = proxyEndPoint;
+
+
+ // Any additional info?
+ while(_buffer.hasRemaining())
+ {
+ int type = 0xff & _buffer.get();
+ int length = _buffer.getShort();
+ byte[] value = new byte[length];
+ _buffer.get(value);
+
+ if (LOG.isDebugEnabled())
+ LOG.debug(String.format("T=%x L=%d V=%s for %s",type,length,TypeUtil.toHexString(value),this));
+
+ // TODO interpret these values
+ switch(type)
+ {
+ case 0x01: // PP2_TYPE_ALPN
+ break;
+ case 0x02: // PP2_TYPE_AUTHORITY
+ break;
+ case 0x20: // PP2_TYPE_SSL
+ {
+ int i=0;
+ int client = 0xff & value[i++];
+ int verify = (0xff & value[i++])<<24 + (0xff & value[i++])<<16 + (0xff & value[i++])<<8 + (0xff&value[i++]);
+ while(i<value.length)
+ {
+ int ssl_type = 0xff & value[i++];
+ int ssl_length = (0xff & value[i++])*0x100 + (0xff&value[i++]);
+ byte[] ssl_val = new byte[ssl_length];
+ System.arraycopy(value,i,ssl_val,0,ssl_length);
+ i+=ssl_length;
+
+ switch(ssl_type)
+ {
+ case 0x21: // PP2_TYPE_SSL_VERSION
+ String version=new String(ssl_val,0,ssl_length,StandardCharsets.ISO_8859_1);
+ if (client==1)
+ proxyEndPoint.setAttribute(TLS_VERSION,version);
+ break;
+
+ default:
+ break;
+ }
+ }
+ break;
+ }
+ case 0x21: // PP2_TYPE_SSL_VERSION
+ break;
+ case 0x22: // PP2_TYPE_SSL_CN
+ break;
+ case 0x30: // PP2_TYPE_NETNS
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (LOG.isDebugEnabled())
+ LOG.debug("{} {}",getEndPoint(),proxyEndPoint.toString());
+
+
+ }
+ catch(Exception e)
+ {
+ LOG.warn(e);
+ }
+ }
+
+ Connection newConnection = connectionFactory.newConnection(_connector, endPoint);
+ endPoint.upgrade(newConnection);
+ }
+ }
+
- public static class ProxyEndPoint implements EndPoint
+ public static class ProxyEndPoint extends AttributesMap implements EndPoint
{
private final EndPoint _endp;
private final InetSocketAddress _remote;
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/PushBuilder.java b/jetty-server/src/main/java/org/eclipse/jetty/server/PushBuilder.java
index 5e807b45dc..a16c9960e6 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/PushBuilder.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/PushBuilder.java
@@ -25,63 +25,99 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
+
/** Build a request to be pushed.
- * <p>
- * A PushBuilder is obtained by calling {@link Request#getPushBuilder()}
- * which creates an initializes the builder as follows:
+ *
+ * <p>A PushBuilder is obtained by calling {@link
+ * Request#getPushBuilder()} (<code>Eventually HttpServletRequest.getPushBuilder()</code>).
+ * Each call to this method will
+ * return a new instance of a PushBuilder based off the current {@code
+ * HttpServletRequest}. Any mutations to the returned PushBuilder are
+ * not reflected on future returns.</p>
+ *
+ * <p>The instance is initialized as follows:</p>
+ *
* <ul>
- * <li> Each call to getPushBuilder() will return a new instance of a
- * PushBuilder based off the Request. Any mutations to the
- * returned PushBuilder are not reflected on future returns.</li>
+ *
* <li>The method is initialized to "GET"</li>
- * <li>The requests headers are added to the Builder, except for:<ul>
+ *
+ * <li>The existing headers of the current {@link HttpServletRequest}
+ * are added to the builder, except for:
+ *
+ * <ul>
* <li>Conditional headers (eg. If-Modified-Since)
* <li>Range headers
* <li>Expect headers
* <li>Authorization headers
* <li>Referrer headers
- * </ul></li>
- * <li>If the request was Authenticated, an Authorization header will
+ * </ul>
+ *
+ * </li>
+ *
+ * <li>If the request was authenticated, an Authorization header will
* be set with a container generated token that will result in equivalent
- * Authorization for the pushed request</li>
- * <li>The query string from {@link HttpServletRequest#getQueryString()}
- * <li>The {@link HttpServletRequest#getRequestedSessionId()} value, unless at the time
- * of the call {@link HttpServletRequest#getSession(boolean)}
- * has previously been called to create a new {@link HttpSession}, in
- * which case the new session ID will be used as the PushBuilders
- * requested session ID. The source of the requested session id will be the
- * same as for the request</li>
- * <li>The Referer header will be set to {@link HttpServletRequest#getRequestURL()}
- * plus any {@link HttpServletRequest#getQueryString()} </li>
+ * Authorization for the pushed request.</li>
+ *
+ * <li>The {@link HttpServletRequest#getRequestedSessionId()} value,
+ * unless at the time of the call {@link
+ * HttpServletRequest#getSession(boolean)} has previously been called to
+ * create a new {@link HttpSession}, in which case the new session ID
+ * will be used as the PushBuilder's requested session ID. The source of
+ * the requested session id will be the same as for the request</li>
+ *
+ * <li>The Referer(sic) header will be set to {@link
+ * HttpServletRequest#getRequestURL()} plus any {@link
+ * HttpServletRequest#getQueryString()} </li>
+ *
* <li>If {@link HttpServletResponse#addCookie(Cookie)} has been called
* on the associated response, then a corresponding Cookie header will be added
* to the PushBuilder, unless the {@link Cookie#getMaxAge()} is &lt;=0, in which
* case the Cookie will be removed from the builder.</li>
- * <li>If this request has has the conditional headers If-Modified-Since or
- * If-None-Match then the {@link #isConditional()} header is set to true.</li>
- * </ul>
- * <p>A PushBuilder can be customized by chained calls to mutator methods before the
- * {@link #push()} method is called to initiate a push request with the current state
- * of the builder. After the call to {@link #push()}, the builder may be reused for
- * another push, however the {@link #path(String)}, {@link #etag(String)} and
- * {@link #lastModified(String)} values will have been nulled. All other
- * values are retained over calls to {@link #push()}.
+ *
+ * <li>If this request has has the conditional headers If-Modified-Since
+ * or If-None-Match, then the {@link #isConditional()} header is set to
+ * true.</li>
+ *
+ * </ul>
+ *
+ * <p>The {@link #path} method must be called on the {@code PushBuilder}
+ * instance before the call to {@link #push}. Failure to do so must
+ * cause an exception to be thrown from {@link
+ * #push}, as specified in that method.</p>
+ *
+ * <p>A PushBuilder can be customized by chained calls to mutator
+ * methods before the {@link #push()} method is called to initiate an
+ * asynchronous push request with the current state of the builder.
+ * After the call to {@link #push()}, the builder may be reused for
+ * another push, however the implementation must make it so the {@link
+ * #path(String)}, {@link #etag(String)} and {@link
+ * #lastModified(String)} values are cleared before returning from
+ * {@link #push}. All other values are retained over calls to {@link
+ * #push()}.
+ *
+ * @since 4.0
*/
public interface PushBuilder
{
- /** Set the method to be used for the push.
- * Defaults to GET.
+ /**
+ * <p>Set the method to be used for the push.</p>
+ *
+ * <p>Any non-empty String may be used for the method.</p>
+ *
* @param method the method to be used for the push.
* @return this builder.
+ * @throws NullPointerException if the argument is {@code null}
+ * @throws IllegalArgumentException if the argument is the empty String
*/
public abstract PushBuilder method(String method);
/** Set the query string to be used for the push.
- * Defaults to the requests query string.
- * Will be appended to any query String included in a call to {@link #path(String)}. This
- * method should be used instead of a query in {@link #path(String)} when multiple
- * {@link #push()} calls are to be made with the same query string, or to remove a
- * query string obtained from the associated request.
+ *
+ * Will be appended to any query String included in a call to {@link
+ * #path(String)}. Any duplicate parameters must be preserved. This
+ * method should be used instead of a query in {@link #path(String)}
+ * when multiple {@link #push()} calls are to be made with the same
+ * query string.
* @param queryString the query string to be used for the push.
* @return this builder.
*/
@@ -108,33 +144,55 @@ public interface PushBuilder
*/
public abstract PushBuilder conditional(boolean conditional);
- /** Set a header to be used for the push.
+ /**
+ * <p>Set a header to be used for the push. If the builder has an
+ * existing header with the same name, its value is overwritten.</p>
+ *
* @param name The header name to set
* @param value The header value to set
* @return this builder.
*/
public abstract PushBuilder setHeader(String name, String value);
+
- /** Add a header to be used for the push.
+ /**
+ * <p>Add a header to be used for the push.</p>
* @param name The header name to add
* @param value The header value to add
* @return this builder.
*/
public abstract PushBuilder addHeader(String name, String value);
+
+
+ /**
+ * <p>Remove the named header. If the header does not exist, take
+ * no action.</p>
+ *
+ * @param name The name of the header to remove
+ * @return this builder.
+ */
+ public abstract PushBuilder removeHeader(String name);
+
- /** Set the URI path to be used for the push.
- * The path may start with "/" in which case it is treated as an
- * absolute path, otherwise it is relative to the context path of
- * the associated request.
- * There is no path default and {@link #path(String)} must be called
- * before every call to {@link #push()}
+
+ /**
+ * Set the URI path to be used for the push. The path may start
+ * with "/" in which case it is treated as an absolute path,
+ * otherwise it is relative to the context path of the associated
+ * request. There is no path default and {@link #path(String)} must
+ * be called before every call to {@link #push()}. If a query
+ * string is present in the argument {@code path}, its contents must
+ * be merged with the contents previously passed to {@link
+ * #queryString}, preserving duplicates.
+ *
* @param path the URI path to be used for the push, which may include a
* query string.
* @return this builder.
*/
public abstract PushBuilder path(String path);
- /** Set the etag to be used for conditional pushes.
+ /**
+ * Set the etag to be used for conditional pushes.
* The etag will be used only if {@link #isConditional()} is true.
* Defaults to no etag. The value is nulled after every call to
* {@link #push()}
@@ -143,33 +201,44 @@ public interface PushBuilder
*/
public abstract PushBuilder etag(String etag);
- /** Set the last modified date to be used for conditional pushes.
- * The last modified date will be used only if {@link #isConditional()} is true.
- * Defaults to no date. The value is nulled after every call to
- * {@link #push()}
+ /**
+ * Set the last modified date to be used for conditional pushes.
+ * The last modified date will be used only if {@link
+ * #isConditional()} is true. Defaults to no date. The value is
+ * nulled after every call to {@link #push()}
* @param lastModified the last modified date to be used for the push.
* @return this builder.
- * */
+ */
public abstract PushBuilder lastModified(String lastModified);
- /** Push a resource.
- * Push a resource based on the current state of the PushBuilder. If {@link #isConditional()}
- * is true and an etag or lastModified value is provided, then an appropriate conditional header
- * will be generated. If both an etag and lastModified value are provided only an If-None-Match header
- * will be generated. If the builder has a session ID, then the pushed request
- * will include the session ID either as a Cookie or as a URI parameter as appropriate. The builders
- * query string is merged with any passed query string.
- * After initiating the push, the builder has its path, etag and lastModified fields nulled. All
- * other fields are left as is for possible reuse in another push.
- * @throws IllegalArgumentException if the method set expects a request body (eg POST)
+ /** Push a resource given the current state of the builder,
+ * returning immediately without blocking.
+ *
+ * <p>Push a resource based on the current state of the PushBuilder.
+ * If {@link #isConditional()} is true and an etag or lastModified
+ * value is provided, then an appropriate conditional header will be
+ * generated. If both an etag and lastModified value are provided
+ * only an If-None-Match header will be generated. If the builder
+ * has a session ID, then the pushed request will include the
+ * session ID either as a Cookie or as a URI parameter as
+ * appropriate. The builders query string is merged with any passed
+ * query string.</p>
+ *
+ * <p>Before returning from this method, the builder has its path,
+ * etag and lastModified fields nulled. All other fields are left as
+ * is for possible reuse in another push.</p>
+ *
+ * @throws IllegalArgumentException if the method set expects a
+ * request body (eg POST)
+ *
+ * @throws IllegalStateException if there was no call to {@link
+ * #path} on this instance either between its instantiation or the
+ * last call to {@code push()} that did not throw an
+ * IllegalStateException.
*/
public abstract void push();
-
-
-
-
public abstract String getMethod();
public abstract String getQueryString();
public abstract String getSessionId();
@@ -179,7 +248,4 @@ public interface PushBuilder
public abstract String getPath();
public abstract String getEtag();
public abstract String getLastModified();
-
-
-
} \ No newline at end of file
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/PushBuilderImpl.java b/jetty-server/src/main/java/org/eclipse/jetty/server/PushBuilderImpl.java
index f39cffa878..def6bed888 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/PushBuilderImpl.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/PushBuilderImpl.java
@@ -32,14 +32,14 @@ import org.eclipse.jetty.util.log.Logger;
/* ------------------------------------------------------------ */
-/**
+/**
*/
public class PushBuilderImpl implements PushBuilder
-{
+{
private static final Logger LOG = Log.getLogger(PushBuilderImpl.class);
private final static HttpField JettyPush = new HttpField("x-http2-push","PushBuilder");
-
+
private final Request _request;
private final HttpFields _fields;
private String _method;
@@ -49,7 +49,7 @@ public class PushBuilderImpl implements PushBuilder
private String _path;
private String _etag;
private String _lastModified;
-
+
public PushBuilderImpl(Request request, HttpFields fields, String method, String queryString, String sessionId, boolean conditional)
{
super();
@@ -65,124 +65,88 @@ public class PushBuilderImpl implements PushBuilder
}
/* ------------------------------------------------------------ */
- /**
- * @see org.eclipse.jetty.server.PushBuilder#getMethod()
- */
@Override
public String getMethod()
{
return _method;
}
-
+
/* ------------------------------------------------------------ */
- /**
- * @see org.eclipse.jetty.server.PushBuilder#method(java.lang.String)
- */
@Override
public PushBuilder method(String method)
{
_method = method;
return this;
}
-
+
/* ------------------------------------------------------------ */
- /**
- * @see org.eclipse.jetty.server.PushBuilder#getQueryString()
- */
@Override
public String getQueryString()
{
return _queryString;
}
-
+
/* ------------------------------------------------------------ */
- /**
- * @see org.eclipse.jetty.server.PushBuilder#queryString(java.lang.String)
- */
@Override
public PushBuilder queryString(String queryString)
{
_queryString = queryString;
return this;
}
-
+
/* ------------------------------------------------------------ */
- /**
- * @see org.eclipse.jetty.server.PushBuilder#getSessionId()
- */
@Override
public String getSessionId()
{
return _sessionId;
}
-
+
/* ------------------------------------------------------------ */
- /**
- * @see org.eclipse.jetty.server.PushBuilder#sessionId(java.lang.String)
- */
@Override
public PushBuilder sessionId(String sessionId)
{
_sessionId = sessionId;
return this;
}
-
+
/* ------------------------------------------------------------ */
- /**
- * @see org.eclipse.jetty.server.PushBuilder#isConditional()
- */
@Override
public boolean isConditional()
{
return _conditional;
}
-
+
/* ------------------------------------------------------------ */
- /**
- * @see org.eclipse.jetty.server.PushBuilder#conditional(boolean)
- */
@Override
public PushBuilder conditional(boolean conditional)
{
_conditional = conditional;
return this;
}
-
+
/* ------------------------------------------------------------ */
- /**
- * @see org.eclipse.jetty.server.PushBuilder#getHeaderNames()
- */
@Override
public Set<String> getHeaderNames()
{
return _fields.getFieldNamesCollection();
}
-
+
/* ------------------------------------------------------------ */
- /**
- * @see org.eclipse.jetty.server.PushBuilder#getHeader(java.lang.String)
- */
@Override
public String getHeader(String name)
{
return _fields.get(name);
}
-
+
/* ------------------------------------------------------------ */
- /**
- * @see org.eclipse.jetty.server.PushBuilder#setHeader(java.lang.String, java.lang.String)
- */
@Override
public PushBuilder setHeader(String name,String value)
{
_fields.put(name,value);
return this;
}
-
+
/* ------------------------------------------------------------ */
- /**
- * @see org.eclipse.jetty.server.PushBuilder#addHeader(java.lang.String, java.lang.String)
- */
@Override
public PushBuilder addHeader(String name,String value)
{
@@ -190,11 +154,15 @@ public class PushBuilderImpl implements PushBuilder
return this;
}
-
/* ------------------------------------------------------------ */
- /**
- * @see org.eclipse.jetty.server.PushBuilder#getPath()
- */
+ @Override
+ public PushBuilder removeHeader(String name)
+ {
+ _fields.remove(name);
+ return this;
+ }
+
+ /* ------------------------------------------------------------ */
@Override
public String getPath()
{
@@ -202,9 +170,6 @@ public class PushBuilderImpl implements PushBuilder
}
/* ------------------------------------------------------------ */
- /**
- * @see org.eclipse.jetty.server.PushBuilder#path(java.lang.String)
- */
@Override
public PushBuilder path(String path)
{
@@ -213,9 +178,6 @@ public class PushBuilderImpl implements PushBuilder
}
/* ------------------------------------------------------------ */
- /**
- * @see org.eclipse.jetty.server.PushBuilder#getEtag()
- */
@Override
public String getEtag()
{
@@ -223,9 +185,6 @@ public class PushBuilderImpl implements PushBuilder
}
/* ------------------------------------------------------------ */
- /**
- * @see org.eclipse.jetty.server.PushBuilder#etag(java.lang.String)
- */
@Override
public PushBuilder etag(String etag)
{
@@ -234,9 +193,6 @@ public class PushBuilderImpl implements PushBuilder
}
/* ------------------------------------------------------------ */
- /**
- * @see org.eclipse.jetty.server.PushBuilder#getLastModified()
- */
@Override
public String getLastModified()
{
@@ -244,9 +200,6 @@ public class PushBuilderImpl implements PushBuilder
}
/* ------------------------------------------------------------ */
- /**
- * @see org.eclipse.jetty.server.PushBuilder#lastModified(java.lang.String)
- */
@Override
public PushBuilder lastModified(String lastModified)
{
@@ -255,40 +208,36 @@ public class PushBuilderImpl implements PushBuilder
}
/* ------------------------------------------------------------ */
- /* ------------------------------------------------------------ */
- /**
- * @see org.eclipse.jetty.server.PushBuilder#push()
- */
@Override
public void push()
{
if (HttpMethod.POST.is(_method) || HttpMethod.PUT.is(_method))
throw new IllegalStateException("Bad Method "+_method);
-
+
if (_path==null || _path.length()==0)
throw new IllegalStateException("Bad Path "+_path);
-
+
String path=_path;
String query=_queryString;
int q=path.indexOf('?');
if (q>=0)
{
- query=(query!=null && query.length()>0)?(_path.substring(q+1)+'&'+query):_path.substring(q+1);
- path=_path.substring(0,q);
+ query=(query!=null && query.length()>0)?(path.substring(q+1)+'&'+query):path.substring(q+1);
+ path=path.substring(0,q);
}
-
+
if (!path.startsWith("/"))
path=URIUtil.addPaths(_request.getContextPath(),path);
-
+
String param=null;
if (_sessionId!=null)
{
if (_request.isRequestedSessionIdFromURL())
param="jsessionid="+_sessionId;
- // TODO else
+ // TODO else
// _fields.add("Cookie","JSESSIONID="+_sessionId);
}
-
+
if (_conditional)
{
if (_etag!=null)
@@ -296,16 +245,17 @@ public class PushBuilderImpl implements PushBuilder
else if (_lastModified!=null)
_fields.add(HttpHeader.IF_MODIFIED_SINCE,_lastModified);
}
-
- HttpURI uri = HttpURI.createHttpURI(_request.getScheme(),_request.getServerName(),_request.getServerPort(),_path,param,query,null);
+
+ HttpURI uri = HttpURI.createHttpURI(_request.getScheme(),_request.getServerName(),_request.getServerPort(),path,param,query,null);
MetaData.Request push = new MetaData.Request(_method,uri,_request.getHttpVersion(),_fields);
-
+
if (LOG.isDebugEnabled())
LOG.debug("Push {} {} inm={} ims={}",_method,uri,_fields.get(HttpHeader.IF_NONE_MATCH),_fields.get(HttpHeader.IF_MODIFIED_SINCE));
-
+
_request.getHttpChannel().getHttpTransport().push(push);
_path=null;
_etag=null;
_lastModified=null;
}
+
}
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java b/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java
index fbc42e0708..8cf22eb9fb 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java
@@ -40,6 +40,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
+import java.util.concurrent.ConcurrentLinkedQueue;
import javax.servlet.AsyncContext;
import javax.servlet.AsyncListener;
@@ -161,6 +162,7 @@ public class Request implements HttpServletRequest
private final HttpInput _input;
private MetaData.Request _metadata;
+ private String _originalURI;
private String _contextPath;
private String _servletPath;
@@ -1580,6 +1582,14 @@ public class Request implements HttpServletRequest
/* ------------------------------------------------------------ */
/**
+ * @return Returns the original uri passed in metadata before customization/rewrite
+ */
+ public String getOriginalURI()
+ {
+ return _originalURI;
+ }
+ /* ------------------------------------------------------------ */
+ /**
* @param uri the URI to set
*/
public void setHttpURI(HttpURI uri)
@@ -1745,8 +1755,9 @@ public class Request implements HttpServletRequest
* @param request the Request metadata
*/
public void setMetaData(org.eclipse.jetty.http.MetaData.Request request)
- {
+ {
_metadata=request;
+ _originalURI=_metadata.getURIString();
setMethod(request.getMethod());
HttpURI uri = request.getURI();
@@ -1803,6 +1814,7 @@ public class Request implements HttpServletRequest
protected void recycle()
{
_metadata=null;
+ _originalURI=null;
if (_context != null)
throw new IllegalStateException("Request in context!");
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/SecureRequestCustomizer.java b/jetty-server/src/main/java/org/eclipse/jetty/server/SecureRequestCustomizer.java
index 7dc74588d8..1ede4423c6 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/SecureRequestCustomizer.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/SecureRequestCustomizer.java
@@ -71,13 +71,19 @@ public class SecureRequestCustomizer implements HttpConfiguration.Customizer
request.setSecure(true);
if (request.getHttpURI().getScheme()==null)
- request.setScheme(HttpScheme.HTTPS.asString());
+ request.getHttpURI().setScheme(HttpScheme.HTTPS.asString());
SslConnection.DecryptedEndPoint ssl_endp = (DecryptedEndPoint)request.getHttpChannel().getEndPoint();
SslConnection sslConnection = ssl_endp.getSslConnection();
SSLEngine sslEngine=sslConnection.getSSLEngine();
customize(sslEngine,request);
+
+ if (request.getHttpURI().getScheme()==null)
+ request.setScheme(HttpScheme.HTTPS.asString());
}
+
+ if (HttpScheme.HTTPS.is(request.getScheme()))
+ request.setSecure(true);
}
/**
@@ -104,7 +110,6 @@ public class SecureRequestCustomizer implements HttpConfiguration.Customizer
*/
public void customize(SSLEngine sslEngine, Request request)
{
- request.setScheme(HttpScheme.HTTPS.asString());
SSLSession sslSession = sslEngine.getSession();
if (_sniHostCheck)
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/JDBCSessionIdManager.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/JDBCSessionIdManager.java
index 99108d86d7..3bfb8ba242 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/JDBCSessionIdManager.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/JDBCSessionIdManager.java
@@ -1250,6 +1250,8 @@ public class JDBCSessionIdManager extends AbstractSessionIdManager
*/
private void scavenge ()
{
+ Set<String> candidateIds = getAllCandidateExpiredSessionIds();
+
Connection connection = null;
try
{
@@ -1283,7 +1285,7 @@ public class JDBCSessionIdManager extends AbstractSessionIdManager
}
}
}
- scavengeSessions(expiredSessionIds, false);
+ scavengeSessions(candidateIds, expiredSessionIds, false);
//Pass 2: find sessions that have expired a while ago for which this node was their last manager
@@ -1306,7 +1308,7 @@ public class JDBCSessionIdManager extends AbstractSessionIdManager
if (LOG.isDebugEnabled()) LOG.debug ("Found expired sessionId="+sessionId+" last managed by "+getWorkerName());
}
}
- scavengeSessions(expiredSessionIds, false);
+ scavengeSessions(candidateIds, expiredSessionIds, false);
}
@@ -1329,9 +1331,13 @@ public class JDBCSessionIdManager extends AbstractSessionIdManager
if (LOG.isDebugEnabled()) LOG.debug ("Found expired sessionId="+sessionId);
}
}
- scavengeSessions(expiredSessionIds, true);
+ scavengeSessions(candidateIds, expiredSessionIds, true);
}
}
+
+ //Tell session managers to check remaining sessions in memory that may have expired
+ //but are no longer in the database
+ scavengeSessions(candidateIds);
}
}
catch (Exception e)
@@ -1363,24 +1369,20 @@ public class JDBCSessionIdManager extends AbstractSessionIdManager
/**
* @param expiredSessionIds
*/
- private void scavengeSessions (Set<String> expiredSessionIds, boolean forceDelete)
+ private void scavengeSessions (Set<String> candidateIds, Set<String> expiredSessionIds, boolean forceDelete)
{
Set<String> remainingIds = new HashSet<String>(expiredSessionIds);
- Handler[] contexts = _server.getChildHandlersByClass(ContextHandler.class);
- for (int i=0; contexts!=null && i<contexts.length; i++)
+ Set<SessionManager> managers = getAllSessionManagers();
+ for (SessionManager m:managers)
{
- SessionHandler sessionHandler = ((ContextHandler)contexts[i]).getChildHandlerByClass(SessionHandler.class);
- if (sessionHandler != null)
+ Set<String> successfullyExpiredIds = ((JDBCSessionManager)m).expire(expiredSessionIds);
+ if (successfullyExpiredIds != null)
{
- SessionManager manager = sessionHandler.getSessionManager();
- if (manager != null && manager instanceof JDBCSessionManager)
- {
- Set<String> successfullyExpiredIds = ((JDBCSessionManager)manager).expire(expiredSessionIds);
- if (successfullyExpiredIds != null)
- remainingIds.removeAll(successfullyExpiredIds);
- }
+ remainingIds.removeAll(successfullyExpiredIds);
+ candidateIds.removeAll(successfullyExpiredIds);
}
}
+
//Any remaining ids are of those sessions that no context removed
if (!remainingIds.isEmpty() && forceDelete)
@@ -1402,6 +1404,63 @@ public class JDBCSessionIdManager extends AbstractSessionIdManager
}
}
}
+
+ /**
+ * These are the session ids that the session managers thought had
+ * expired, but were not expired in the database. This could be
+ * because the session is live on another node, or that the
+ * session no longer exists in the database because some other
+ * node removed it.
+ * @param candidateIds
+ */
+ private void scavengeSessions (Set<String> candidateIds)
+ {
+ if (candidateIds.isEmpty())
+ return;
+
+
+ Set<SessionManager> managers = getAllSessionManagers();
+
+ for (SessionManager m:managers)
+ {
+ //tell the session managers to check the sessions that have expired in memory
+ //if they are no longer in the database, they should be removed
+ ((JDBCSessionManager)m).expireCandidates(candidateIds);
+ }
+ }
+
+ private Set<String> getAllCandidateExpiredSessionIds()
+ {
+ HashSet<String> candidateIds = new HashSet<>();
+
+ Set<SessionManager> managers = getAllSessionManagers();
+
+ for (SessionManager m:managers)
+ {
+ candidateIds.addAll(((JDBCSessionManager)m).getCandidateExpiredIds());
+ }
+
+ return candidateIds;
+ }
+
+
+ private Set<SessionManager> getAllSessionManagers()
+ {
+ HashSet<SessionManager> managers = new HashSet<>();
+
+ Handler[] contexts = _server.getChildHandlersByClass(ContextHandler.class);
+ for (int i=0; contexts!=null && i<contexts.length; i++)
+ {
+ SessionHandler sessionHandler = ((ContextHandler)contexts[i]).getChildHandlerByClass(SessionHandler.class);
+ if (sessionHandler != null)
+ {
+ SessionManager manager = sessionHandler.getSessionManager();
+ if (manager != null && manager instanceof JDBCSessionManager)
+ managers.add(manager);
+ }
+ }
+ return managers;
+ }
@@ -1411,7 +1470,7 @@ public class JDBCSessionIdManager extends AbstractSessionIdManager
{
if (expiredIds == null || expiredIds.isEmpty())
return;
-
+
String[] ids = expiredIds.toArray(new String[expiredIds.size()]);
try (Connection con = getConnection())
{
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/JDBCSessionManager.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/JDBCSessionManager.java
index bae26477b3..6962694507 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/JDBCSessionManager.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/JDBCSessionManager.java
@@ -535,6 +535,7 @@ public class JDBCSessionManager extends AbstractSessionManager
session.setLastNode(getSessionIdManager().getWorkerName());
_sessions.put(idInCluster, session);
+ _sessionsStats.increment();
//update in db
try
@@ -569,6 +570,11 @@ public class JDBCSessionManager extends AbstractSessionManager
}
else
{
+ if (memSession != null)
+ {
+ //Session must have been removed from db by another node
+ removeSession(memSession, true);
+ }
//No session in db with matching id and context path.
LOG.debug("getSession({}): No session in database matching id={}",idInCluster,idInCluster);
}
@@ -838,6 +844,7 @@ public class JDBCSessionManager extends AbstractSessionManager
//loaded an expired session last managed on this node for this context, add it to the list so we can
//treat it like a normal expired session
_sessions.put(session.getClusterId(), session);
+ _sessionsStats.increment();
}
else
{
@@ -866,7 +873,54 @@ public class JDBCSessionManager extends AbstractSessionManager
}
}
-
+ protected void expireCandidates (Set<String> candidateIds)
+ {
+ Iterator<String> itor = candidateIds.iterator();
+ long now = System.currentTimeMillis();
+ while (itor.hasNext())
+ {
+ String id = itor.next();
+
+ //check if expired in db
+ try
+ {
+ Session memSession = _sessions.get(id);
+ if (memSession == null)
+ {
+ continue; //no longer in memory
+ }
+
+ Session s = loadSession(id, canonicalize(_context.getContextPath()), getVirtualHost(_context));
+ if (s == null)
+ {
+ //session no longer exists, can be safely expired
+ memSession.timeout();
+ }
+ }
+ catch (Exception e)
+ {
+ LOG.warn("Error checking db for expiry for session {}", id);
+ }
+ }
+ }
+
+ protected Set<String> getCandidateExpiredIds ()
+ {
+ HashSet<String> expiredIds = new HashSet<>();
+
+ Iterator<String> itor = _sessions.keySet().iterator();
+ while (itor.hasNext())
+ {
+ String id = itor.next();
+ //check to see if session should have expired
+ Session session = _sessions.get(id);
+ if (session._expiryTime > 0 && System.currentTimeMillis() > session._expiryTime)
+ expiredIds.add(id);
+ }
+ return expiredIds;
+ }
+
+
/**
* Load a session from the database
* @param id the id
diff --git a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletMapping.java b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletMapping.java
index ab181862f4..ae10b7d872 100644
--- a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletMapping.java
+++ b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletMapping.java
@@ -65,6 +65,25 @@ public class ServletMapping
{
_pathSpecs = pathSpecs;
}
+
+
+ /* ------------------------------------------------------------ */
+ /** Test if the list of path specs contains a particular one.
+ * @param pathSpec the path spec
+ * @return true if path spec matches something in mappings
+ */
+ public boolean containsPathSpec (String pathSpec)
+ {
+ if (_pathSpecs == null || _pathSpecs.length == 0)
+ return false;
+
+ for (String p:_pathSpecs)
+ {
+ if (p.equals(pathSpec))
+ return true;
+ }
+ return false;
+ }
/* ------------------------------------------------------------ */
/**
diff --git a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/PushCacheFilter.java b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/PushCacheFilter.java
index d6710d74db..544c392a9c 100644
--- a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/PushCacheFilter.java
+++ b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/PushCacheFilter.java
@@ -34,7 +34,6 @@ import java.util.concurrent.atomic.AtomicLong;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
-import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
@@ -45,7 +44,7 @@ import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpURI;
import org.eclipse.jetty.http.HttpVersion;
-import org.eclipse.jetty.server.Dispatcher;
+import org.eclipse.jetty.server.PushBuilder;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.URIUtil;
@@ -157,6 +156,9 @@ public class PushCacheFilter implements Filter
LOG.debug("{} {} referrer={} conditional={}", request.getMethod(), request.getRequestURI(), referrer, conditional);
String path = URIUtil.addPaths(request.getServletPath(), request.getPathInfo());
+ String query = request.getQueryString();
+ if (query != null)
+ path += "?" + query;
if (referrer != null)
{
HttpURI referrerURI = new HttpURI(referrer);
@@ -186,14 +188,13 @@ public class PushCacheFilter implements Filter
long primaryTimestamp = primaryResource._timestamp.get();
if (primaryTimestamp != 0)
{
- RequestDispatcher dispatcher = request.getServletContext().getRequestDispatcher(path);
if (now - primaryTimestamp < TimeUnit.MILLISECONDS.toNanos(_associatePeriod))
{
- ConcurrentMap<String, RequestDispatcher> associated = primaryResource._associated;
+ ConcurrentMap<String, String> associated = primaryResource._associated;
// Not strictly concurrent-safe, just best effort to limit associations.
if (associated.size() <= _maxAssociations)
{
- if (associated.putIfAbsent(path, dispatcher) == null)
+ if (associated.putIfAbsent(path, path) == null)
{
if (LOG.isDebugEnabled())
LOG.debug("Associated {} to {}", path, referrerPathNoContext);
@@ -253,11 +254,14 @@ public class PushCacheFilter implements Filter
// Push associated for non conditional
if (!conditional && !primaryResource._associated.isEmpty())
{
- for (RequestDispatcher dispatcher : primaryResource._associated.values())
+ PushBuilder builder = Request.getBaseRequest(request).getPushBuilder();
+
+ for (String associated : primaryResource._associated.values())
{
if (LOG.isDebugEnabled())
- LOG.debug("Pushing {} for {}", dispatcher, path);
- ((Dispatcher)dispatcher).push(request);
+ LOG.debug("Pushing {} for {}", associated, path);
+
+ builder.path(associated).push();
}
}
@@ -297,7 +301,7 @@ public class PushCacheFilter implements Filter
private static class PrimaryResource
{
- private final ConcurrentMap<String, RequestDispatcher> _associated = new ConcurrentHashMap<>();
+ private final ConcurrentMap<String, String> _associated = new ConcurrentHashMap<>();
private final AtomicLong _timestamp = new AtomicLong();
}
}
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/MultiPartInputStreamParser.java b/jetty-util/src/main/java/org/eclipse/jetty/util/MultiPartInputStreamParser.java
index 179b6f318d..755e6bd830 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/MultiPartInputStreamParser.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/MultiPartInputStreamParser.java
@@ -127,7 +127,7 @@ public class MultiPartInputStreamParser
if (MultiPartInputStreamParser.this._config.getFileSizeThreshold() > 0 && _size + length > MultiPartInputStreamParser.this._config.getFileSizeThreshold() && _file==null)
createFile();
-
+
_out.write(bytes, offset, length);
_size += length;
}
@@ -136,7 +136,7 @@ public class MultiPartInputStreamParser
throws IOException
{
_file = File.createTempFile("MultiPart", "", MultiPartInputStreamParser.this._tmpDir);
-
+
if (_deleteOnExit)
_file.deleteOnExit();
FileOutputStream fos = new FileOutputStream(_file);
@@ -175,7 +175,7 @@ public class MultiPartInputStreamParser
{
if (name == null)
return null;
- return (String)_headers.getValue(name.toLowerCase(Locale.ENGLISH), 0);
+ return _headers.getValue(name.toLowerCase(Locale.ENGLISH), 0);
}
/**
@@ -211,8 +211,8 @@ public class MultiPartInputStreamParser
}
}
-
- /**
+
+ /**
* @see javax.servlet.http.Part#getSubmittedFileName()
*/
@Override
@@ -241,7 +241,7 @@ public class MultiPartInputStreamParser
*/
public long getSize()
{
- return _size;
+ return _size;
}
/**
@@ -252,7 +252,7 @@ public class MultiPartInputStreamParser
if (_file == null)
{
_temporary = false;
-
+
//part data is only in the ByteArrayOutputStream and never been written to disk
_file = new File (_tmpDir, fileName);
@@ -290,12 +290,12 @@ public class MultiPartInputStreamParser
public void delete() throws IOException
{
if (_file != null && _file.exists())
- _file.delete();
+ _file.delete();
}
-
+
/**
* Only remove tmp files.
- *
+ *
* @throws IOException if unable to delete the file
*/
public void cleanUp() throws IOException
@@ -342,7 +342,7 @@ public class MultiPartInputStreamParser
_contextTmpDir = contextTmpDir;
if (_contextTmpDir == null)
_contextTmpDir = new File (System.getProperty("java.io.tmpdir"));
-
+
if (_config == null)
_config = new MultipartConfigElement(_contextTmpDir.getAbsolutePath());
}
@@ -357,7 +357,7 @@ public class MultiPartInputStreamParser
return Collections.emptyList();
Collection<List<Part>> values = _parts.values();
- List<Part> parts = new ArrayList<Part>();
+ List<Part> parts = new ArrayList<>();
for (List<Part> o: values)
{
List<Part> asList = LazyList.getList(o, false);
@@ -368,7 +368,7 @@ public class MultiPartInputStreamParser
/**
* Delete any tmp storage for parts, and clear out the parts list.
- *
+ *
* @throws MultiException if unable to delete the parts
*/
public void deleteParts ()
@@ -381,22 +381,22 @@ public class MultiPartInputStreamParser
try
{
((MultiPartInputStreamParser.MultiPart)p).cleanUp();
- }
+ }
catch(Exception e)
- {
- err.add(e);
+ {
+ err.add(e);
}
}
_parts.clear();
-
+
err.ifExceptionThrowMulti();
}
-
+
/**
* Parse, if necessary, the multipart data and return the list of Parts.
- *
- * @return the parts
+ *
+ * @return the parts
* @throws IOException if unable to get the parts
*/
public Collection<Part> getParts()
@@ -404,7 +404,7 @@ public class MultiPartInputStreamParser
{
parse();
Collection<List<Part>> values = _parts.values();
- List<Part> parts = new ArrayList<Part>();
+ List<Part> parts = new ArrayList<>();
for (List<Part> o: values)
{
List<Part> asList = LazyList.getList(o, false);
@@ -416,7 +416,7 @@ public class MultiPartInputStreamParser
/**
* Get the named Part.
- *
+ *
* @param name the part name
* @return the parts
* @throws IOException if unable to get the part
@@ -425,13 +425,13 @@ public class MultiPartInputStreamParser
throws IOException
{
parse();
- return (Part)_parts.getValue(name, 0);
+ return _parts.getValue(name, 0);
}
/**
* Parse, if necessary, the multipart stream.
- *
+ *
* @throws IOException if unable to parse
*/
protected void parse ()
@@ -443,7 +443,7 @@ public class MultiPartInputStreamParser
//initialize
long total = 0; //keep running total of size of bytes read from input and throw an exception if exceeds MultipartConfigElement._maxRequestSize
- _parts = new MultiMap<Part>();
+ _parts = new MultiMap<>();
//if its not a multipart request, don't parse it
if (_contentType == null || !_contentType.startsWith("multipart/form-data"))
@@ -475,28 +475,29 @@ public class MultiPartInputStreamParser
bend = (bend < 0? _contentType.length(): bend);
contentTypeBoundary = QuotedStringTokenizer.unquote(value(_contentType.substring(bstart,bend)).trim());
}
-
+
String boundary="--"+contentTypeBoundary;
- byte[] byteBoundary=(boundary+"--").getBytes(StandardCharsets.ISO_8859_1);
+ String lastBoundary=boundary+"--";
+ byte[] byteBoundary=lastBoundary.getBytes(StandardCharsets.ISO_8859_1);
// Get first boundary
String line = null;
try
{
- line=((ReadLineInputStream)_in).readLine();
+ line=((ReadLineInputStream)_in).readLine();
}
catch (IOException e)
{
LOG.warn("Badly formatted multipart request");
throw e;
}
-
+
if (line == null)
throw new IOException("Missing content for multipart request");
-
+
boolean badFormatLogged = false;
line=line.trim();
- while (line != null && !line.equals(boundary))
+ while (line != null && !line.equals(boundary) && !line.equals(lastBoundary))
{
if (!badFormatLogged)
{
@@ -510,6 +511,10 @@ public class MultiPartInputStreamParser
if (line == null)
throw new IOException("Missing initial multi part boundary");
+ // Empty multipart.
+ if (line.equals(lastBoundary))
+ return;
+
// Read each part
boolean lastPart=false;
@@ -518,20 +523,20 @@ public class MultiPartInputStreamParser
String contentDisposition=null;
String contentType=null;
String contentTransferEncoding=null;
-
- MultiMap<String> headers = new MultiMap<String>();
+
+ MultiMap<String> headers = new MultiMap<>();
while(true)
{
line=((ReadLineInputStream)_in).readLine();
-
+
//No more input
if(line==null)
break outer;
-
+
//end of headers:
if("".equals(line))
break;
-
+
total += line.length();
if (_config.getMaxRequestSize() > 0 && total > _config.getMaxRequestSize())
throw new IllegalStateException ("Request exceeds maxRequestSize ("+_config.getMaxRequestSize()+")");
@@ -595,7 +600,7 @@ public class MultiPartInputStreamParser
part.setContentType(contentType);
_parts.add(name, part);
part.open();
-
+
InputStream partInput = null;
if ("base64".equalsIgnoreCase(contentTransferEncoding))
{
@@ -627,7 +632,7 @@ public class MultiPartInputStreamParser
else
partInput = _in;
-
+
try
{
int state=-2;
@@ -646,7 +651,7 @@ public class MultiPartInputStreamParser
throw new IllegalStateException("Request exceeds maxRequestSize ("+_config.getMaxRequestSize()+")");
state=-2;
-
+
// look for CR and/or LF
if(c==13||c==10)
{
@@ -661,7 +666,7 @@ public class MultiPartInputStreamParser
}
break;
}
-
+
// Look for boundary
if(b>=0&&b<byteBoundary.length&&c==byteBoundary[b])
{
@@ -685,7 +690,7 @@ public class MultiPartInputStreamParser
part.write(c);
}
}
-
+
// Check for incomplete boundary match, writing out the chars we matched along the way
if((b>0&&b<byteBoundary.length-2)||(b==byteBoundary.length-1))
{
@@ -699,18 +704,18 @@ public class MultiPartInputStreamParser
part.write(byteBoundary,0,b);
b=-1;
}
-
+
// Boundary match. If we've run out of input or we matched the entire final boundary marker, then this is the last part.
if(b>0||c==-1)
{
-
+
if(b==byteBoundary.length)
lastPart=true;
if(state==10)
state=-2;
break;
}
-
+
// handle CR LF
if(cr)
part.write(13);
@@ -733,7 +738,7 @@ public class MultiPartInputStreamParser
if (!lastPart)
throw new IOException("Incomplete parts");
}
-
+
public void setDeleteOnExit(boolean deleteOnExit)
{
_deleteOnExit = deleteOnExit;
@@ -753,8 +758,8 @@ public class MultiPartInputStreamParser
String value = nameEqualsValue.substring(idx+1).trim();
return QuotedStringTokenizer.unquoteOnly(value);
}
-
-
+
+
/* ------------------------------------------------------------ */
private String filenameValue(String nameEqualsValue)
{
@@ -782,7 +787,7 @@ public class MultiPartInputStreamParser
return QuotedStringTokenizer.unquoteOnly(value, true);
}
-
+
private static class Base64InputStream extends InputStream
{
@@ -791,7 +796,7 @@ public class MultiPartInputStreamParser
byte[] _buffer;
int _pos;
-
+
public Base64InputStream(ReadLineInputStream rlis)
{
_in = rlis;
@@ -806,7 +811,7 @@ public class MultiPartInputStreamParser
//We need to put them back into the bytes returned from this
//method because the parsing of the multipart content uses them
//as markers to determine when we've reached the end of a part.
- _line = _in.readLine();
+ _line = _in.readLine();
if (_line==null)
return -1; //nothing left
if (_line.startsWith("--"))
@@ -824,7 +829,7 @@ public class MultiPartInputStreamParser
_pos=0;
}
-
+
return _buffer[_pos++];
}
}
diff --git a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/CachingWebAppClassLoader.java b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/CachingWebAppClassLoader.java
new file mode 100644
index 0000000000..b099a34810
--- /dev/null
+++ b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/CachingWebAppClassLoader.java
@@ -0,0 +1,121 @@
+//
+// ========================================================================
+// 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.webapp;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.eclipse.jetty.util.ConcurrentHashSet;
+import org.eclipse.jetty.util.annotation.ManagedObject;
+import org.eclipse.jetty.util.annotation.ManagedOperation;
+
+
+/**
+ * A WebAppClassLoader that caches {@link #getResource(String)} results.
+ * Specifically this ClassLoader caches not found classes and resources,
+ * which can greatly increase performance for applications that search
+ * for resources.
+ */
+@ManagedObject
+public class CachingWebAppClassLoader extends WebAppClassLoader
+{
+ private final ConcurrentHashSet<String> _notFound = new ConcurrentHashSet<>();
+ private final ConcurrentHashMap<String,URL> _cache = new ConcurrentHashMap<>();
+
+ public CachingWebAppClassLoader(ClassLoader parent, Context context) throws IOException
+ {
+ super(parent,context);
+ }
+
+ public CachingWebAppClassLoader(Context context) throws IOException
+ {
+ super(context);
+ }
+
+ @Override
+ public URL getResource(String name)
+ {
+ if (_notFound.contains(name))
+ return null;
+
+ URL url = _cache.get(name);
+
+ if (name==null)
+ {
+ url = super.getResource(name);
+
+ if (url==null)
+ {
+ _notFound.add(name);
+ }
+ else
+ {
+ _cache.putIfAbsent(name,url);
+ }
+ }
+
+ return url;
+ }
+
+ @Override
+ protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException
+ {
+ if (_notFound.contains(name))
+ throw new ClassNotFoundException(name+": in notfound cache");
+ try
+ {
+ return super.loadClass(name,resolve);
+ }
+ catch (ClassNotFoundException nfe)
+ {
+ _notFound.add(name);
+ throw nfe;
+ }
+ }
+
+ @Override
+ protected Class<?> findClass(String name) throws ClassNotFoundException
+ {
+ if (_notFound.contains(name))
+ throw new ClassNotFoundException(name+": in notfound cache");
+ try
+ {
+ return super.findClass(name);
+ }
+ catch (ClassNotFoundException nfe)
+ {
+ _notFound.add(name);
+ throw nfe;
+ }
+ }
+
+ @ManagedOperation
+ public void clearCache()
+ {
+ _cache.clear();
+ _notFound.clear();
+ }
+
+ @Override
+ public String toString()
+ {
+ return "Caching["+super.toString()+"]";
+ }
+}
diff --git a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/StandardDescriptorProcessor.java b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/StandardDescriptorProcessor.java
index 75ee94edc9..b2dc458675 100644
--- a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/StandardDescriptorProcessor.java
+++ b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/StandardDescriptorProcessor.java
@@ -51,6 +51,7 @@ import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jetty.servlet.ServletMapping;
import org.eclipse.jetty.util.ArrayUtil;
import org.eclipse.jetty.util.Loader;
+import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.security.Constraint;
@@ -1210,8 +1211,9 @@ public class StandardDescriptorProcessor extends IterativeDescriptorProcessor
//remove ps from the path specs on the existing mapping
//if the mapping now has no pathspecs, remove it
String[] updatedPaths = ArrayUtil.removeFromArray(sm.getPathSpecs(), ps);
+
if (updatedPaths == null || updatedPaths.length == 0)
- {
+ {
if (LOG.isDebugEnabled()) LOG.debug("Removed empty mapping {}",sm);
listItor.remove();
}
@@ -1230,9 +1232,9 @@ public class StandardDescriptorProcessor extends IterativeDescriptorProcessor
paths.add(p);
context.getMetaData().setOrigin(servletName+".servlet.mapping."+p, descriptor);
}
+
mapping.setPathSpecs((String[]) paths.toArray(new String[paths.size()]));
if (LOG.isDebugEnabled()) LOG.debug("Added mapping {} ",mapping);
-
_servletMappings.add(mapping);
return mapping;
}
@@ -1367,10 +1369,52 @@ public class StandardDescriptorProcessor extends IterativeDescriptorProcessor
//add mappings to the jsp servlet from the property-group mappings
if (paths.size() > 0)
{
- ServletMapping mapping = new ServletMapping();
- mapping.setServletName("jsp");
- mapping.setPathSpecs(paths.toArray(new String[paths.size()]));
- _servletMappings.add(mapping);
+ ServletMapping jspMapping = null;
+ for (ServletMapping m: _servletMappings)
+ {
+ if (m.getServletName().equals("jsp"))
+ {
+ jspMapping = m;
+ break;
+ }
+ }
+ if (jspMapping != null)
+ {
+ if (jspMapping.getPathSpecs() == null)
+ {
+ //no paths in jsp servlet mapping, we will add all of ours
+ if (LOG.isDebugEnabled()) LOG.debug("Adding all paths from jsp-config to jsp servlet mapping");
+ jspMapping.setPathSpecs(paths.toArray(new String[paths.size()]));
+ }
+ else
+ {
+ //check if each of our paths is already present in existing mapping
+ ListIterator<String> piterator = paths.listIterator();
+ while (piterator.hasNext())
+ {
+ String p = piterator.next();
+ if (jspMapping.containsPathSpec(p))
+ piterator.remove();
+ }
+
+ //any remaining paths, add to the jspMapping
+ if (paths.size() > 0)
+ {
+ for (String p:jspMapping.getPathSpecs())
+ paths.add(p);
+ if (LOG.isDebugEnabled()) LOG.debug("Adding extra paths from jsp-config to jsp servlet mapping");
+ jspMapping.setPathSpecs((String[])paths.toArray(new String[paths.size()]));
+ }
+ }
+ }
+ else
+ {
+ //no mapping for jsp yet, make one
+ ServletMapping mapping = new ServletMapping();
+ mapping.setServletName("jsp");
+ mapping.setPathSpecs(paths.toArray(new String[paths.size()]));
+ _servletMappings.add(mapping);
+ }
}
}
diff --git a/pom.xml b/pom.xml
index a1f5ebf3b9..6eb2857e0c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -975,5 +975,18 @@
<alpn.version>8.1.5.v20150921</alpn.version>
</properties>
</profile>
+
+ <profile>
+ <id>8u66</id>
+ <activation>
+ <property>
+ <name>java.version</name>
+ <value>1.8.0_66</value>
+ </property>
+ </activation>
+ <properties>
+ <alpn.version>8.1.5.v20150921</alpn.version>
+ </properties>
+ </profile>
</profiles>
</project>
diff --git a/tests/test-http-client-transport/pom.xml b/tests/test-http-client-transport/pom.xml
index 321d387c0c..ec608ac8bc 100644
--- a/tests/test-http-client-transport/pom.xml
+++ b/tests/test-http-client-transport/pom.xml
@@ -90,6 +90,12 @@
<scope>test</scope>
</dependency>
<dependency>
+ <groupId>org.eclipse.jetty.fcgi</groupId>
+ <artifactId>fcgi-server</artifactId>
+ <version>${project.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
diff --git a/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/AbstractTest.java b/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/AbstractTest.java
index ca377c311c..19c237c7da 100644
--- a/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/AbstractTest.java
+++ b/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/AbstractTest.java
@@ -25,6 +25,8 @@ import org.eclipse.jetty.alpn.server.ALPNServerConnectionFactory;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.HttpClientTransport;
import org.eclipse.jetty.client.http.HttpClientTransportOverHTTP;
+import org.eclipse.jetty.fcgi.client.http.HttpClientTransportOverFCGI;
+import org.eclipse.jetty.fcgi.server.ServerFCGIConnectionFactory;
import org.eclipse.jetty.http2.HTTP2Cipher;
import org.eclipse.jetty.http2.client.HTTP2Client;
import org.eclipse.jetty.http2.client.http.HttpClientTransportOverHTTP2;
@@ -39,6 +41,7 @@ import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.server.SslConnectionFactory;
import org.eclipse.jetty.toolchain.test.TestTracker;
+import org.eclipse.jetty.util.SocketAddressResolver;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.eclipse.jetty.util.thread.QueuedThreadPool;
import org.junit.After;
@@ -52,7 +55,7 @@ public abstract class AbstractTest
@Parameterized.Parameters(name = "transport: {0}")
public static Object[] parameters() throws Exception
{
- return new Object[]{Transport.HTTP, Transport.HTTPS, Transport.H2C, Transport.H2};
+ return Transport.values();
}
@Rule
@@ -87,22 +90,28 @@ public abstract class AbstractTest
QueuedThreadPool serverThreads = new QueuedThreadPool();
serverThreads.setName("server");
server = new Server(serverThreads);
- connector = new ServerConnector(server, provideServerConnectionFactory(transport));
+ connector = newServerConnector(server);
server.addConnector(connector);
server.setHandler(handler);
server.start();
}
+ protected ServerConnector newServerConnector(Server server)
+ {
+ return new ServerConnector(server, provideServerConnectionFactory(transport));
+ }
+
private void startClient() throws Exception
{
QueuedThreadPool clientThreads = new QueuedThreadPool();
clientThreads.setName("client");
client = new HttpClient(provideClientTransport(transport), sslContextFactory);
client.setExecutor(clientThreads);
+ client.setSocketAddressResolver(new SocketAddressResolver.Sync());
client.start();
}
- private ConnectionFactory[] provideServerConnectionFactory(Transport transport)
+ protected ConnectionFactory[] provideServerConnectionFactory(Transport transport)
{
List<ConnectionFactory> result = new ArrayList<>();
switch (transport)
@@ -139,6 +148,11 @@ public abstract class AbstractTest
result.add(h2);
break;
}
+ case FCGI:
+ {
+ result.add(new ServerFCGIConnectionFactory(new HttpConfiguration()));
+ break;
+ }
default:
{
throw new IllegalArgumentException();
@@ -147,7 +161,7 @@ public abstract class AbstractTest
return result.toArray(new ConnectionFactory[result.size()]);
}
- private HttpClientTransport provideClientTransport(Transport transport)
+ protected HttpClientTransport provideClientTransport(Transport transport)
{
switch (transport)
{
@@ -163,6 +177,10 @@ public abstract class AbstractTest
http2Client.setSelectors(1);
return new HttpClientTransportOverHTTP2(http2Client);
}
+ case FCGI:
+ {
+ return new HttpClientTransportOverFCGI(1, false, "");
+ }
default:
{
throw new IllegalArgumentException();
@@ -176,6 +194,7 @@ public abstract class AbstractTest
{
case HTTP:
case H2C:
+ case FCGI:
return "http://localhost:" + connector.getLocalPort();
case HTTPS:
case H2:
@@ -185,6 +204,22 @@ public abstract class AbstractTest
}
}
+ protected boolean isTransportSecure()
+ {
+ switch (transport)
+ {
+ case HTTP:
+ case H2C:
+ case FCGI:
+ return false;
+ case HTTPS:
+ case H2:
+ return true;
+ default:
+ throw new IllegalArgumentException();
+ }
+ }
+
@After
public void stop() throws Exception
{
@@ -196,6 +231,6 @@ public abstract class AbstractTest
protected enum Transport
{
- HTTP, HTTPS, H2C, H2
+ HTTP, HTTPS, H2C, H2, FCGI
}
}
diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientLoadTest.java b/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/HttpClientLoadTest.java
index 7ddeea8b90..0fab406c2f 100644
--- a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientLoadTest.java
+++ b/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/HttpClientLoadTest.java
@@ -16,12 +16,11 @@
// ========================================================================
//
-package org.eclipse.jetty.client;
+package org.eclipse.jetty.http.client;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import java.util.Random;
@@ -34,29 +33,33 @@ import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
-import org.eclipse.jetty.client.api.Connection;
+import org.eclipse.jetty.client.ConnectionPool;
+import org.eclipse.jetty.client.HttpClient;
+import org.eclipse.jetty.client.HttpClientTransport;
+import org.eclipse.jetty.client.HttpDestination;
+import org.eclipse.jetty.client.LeakTrackingConnectionPool;
+import org.eclipse.jetty.client.Origin;
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.BytesContentProvider;
+import org.eclipse.jetty.fcgi.client.http.HttpClientTransportOverFCGI;
+import org.eclipse.jetty.fcgi.client.http.HttpDestinationOverFCGI;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpMethod;
-import org.eclipse.jetty.http.HttpScheme;
+import org.eclipse.jetty.io.ArrayByteBufferPool;
+import org.eclipse.jetty.io.ByteBufferPool;
import org.eclipse.jetty.io.LeakTrackingByteBufferPool;
import org.eclipse.jetty.io.MappedByteBufferPool;
-import org.eclipse.jetty.server.AbstractConnectionFactory;
-import org.eclipse.jetty.server.HttpConnectionFactory;
+import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.server.handler.AbstractHandler;
import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.LeakDetector;
-import org.eclipse.jetty.util.SocketAddressResolver;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
-import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.eclipse.jetty.util.thread.Scheduler;
import org.hamcrest.Matchers;
import org.junit.Assert;
@@ -64,66 +67,99 @@ import org.junit.Test;
import static org.junit.Assert.assertThat;
-public class HttpClientLoadTest extends AbstractHttpClientServerTest
+public class HttpClientLoadTest extends AbstractTest
{
private final Logger logger = Log.getLogger(HttpClientLoadTest.class);
+ private final AtomicLong connectionLeaks = new AtomicLong();
- public HttpClientLoadTest(SslContextFactory sslContextFactory)
+ public HttpClientLoadTest(Transport transport)
{
- super(sslContextFactory);
+ super(transport);
}
- @Test
- public void testIterative() throws Exception
+ @Override
+ protected ServerConnector newServerConnector(Server server)
{
int cores = Runtime.getRuntime().availableProcessors();
+ ByteBufferPool byteBufferPool = new ArrayByteBufferPool();
+ byteBufferPool = new LeakTrackingByteBufferPool(byteBufferPool);
+ return new ServerConnector(server, null, null, byteBufferPool,
+ 1, Math.min(1, cores / 2), provideServerConnectionFactory(transport));
+ }
- final AtomicLong connectionLeaks = new AtomicLong();
-
- start(new LoadHandler());
- server.stop();
- server.removeConnector(connector);
- LeakTrackingByteBufferPool serverBufferPool = new LeakTrackingByteBufferPool(new MappedByteBufferPool.Tagged());
- connector = new ServerConnector(server, connector.getExecutor(), connector.getScheduler(),
- serverBufferPool , 1, Math.min(1, cores / 2),
- AbstractConnectionFactory.getFactories(sslContextFactory, new HttpConnectionFactory()));
- server.addConnector(connector);
- server.start();
-
- client.stop();
-
- HttpClient newClient = new HttpClient(new HttpClientTransportOverHTTP()
+ @Override
+ protected HttpClientTransport provideClientTransport(Transport transport)
+ {
+ switch (transport)
{
- @Override
- public HttpDestination newHttpDestination(Origin origin)
+ case HTTP:
+ case HTTPS:
+ {
+ return new HttpClientTransportOverHTTP(1)
+ {
+ @Override
+ public HttpDestination newHttpDestination(Origin origin)
+ {
+ return new HttpDestinationOverHTTP(getHttpClient(), origin)
+ {
+ @Override
+ protected ConnectionPool newConnectionPool(HttpClient client)
+ {
+ return new LeakTrackingConnectionPool(this, client.getMaxConnectionsPerDestination(), this)
+ {
+ @Override
+ protected void leaked(LeakDetector.LeakInfo leakInfo)
+ {
+ super.leaked(leakInfo);
+ connectionLeaks.incrementAndGet();
+ }
+ };
+ }
+ };
+ }
+ };
+ }
+ case FCGI:
{
- return new HttpDestinationOverHTTP(getHttpClient(), origin)
+ return new HttpClientTransportOverFCGI(1, false, "")
{
@Override
- protected DuplexConnectionPool newConnectionPool(HttpClient client)
+ public HttpDestination newHttpDestination(Origin origin)
{
- return new LeakTrackingConnectionPool(this, client.getMaxConnectionsPerDestination(), this)
+ return new HttpDestinationOverFCGI(getHttpClient(), origin)
{
@Override
- protected void leaked(LeakDetector.LeakInfo resource)
+ protected ConnectionPool newConnectionPool(HttpClient client)
{
- connectionLeaks.incrementAndGet();
+ return new LeakTrackingConnectionPool(this, client.getMaxConnectionsPerDestination(), this)
+ {
+ @Override
+ protected void leaked(LeakDetector.LeakInfo leakInfo)
+ {
+ super.leaked(leakInfo);
+ connectionLeaks.incrementAndGet();
+ }
+ };
}
};
}
};
}
- }, sslContextFactory);
- newClient.setExecutor(client.getExecutor());
- newClient.setSocketAddressResolver(new SocketAddressResolver.Sync());
- client = newClient;
- LeakTrackingByteBufferPool clientBufferPool = new LeakTrackingByteBufferPool(new MappedByteBufferPool.Tagged());
- client.setByteBufferPool(clientBufferPool);
+ default:
+ {
+ return super.provideClientTransport(transport);
+ }
+ }
+ }
+
+ @Test
+ public void testIterative() throws Exception
+ {
+ start(new LoadHandler());
+
+ client.setByteBufferPool(new LeakTrackingByteBufferPool(new MappedByteBufferPool.Tagged()));
client.setMaxConnectionsPerDestination(32768);
client.setMaxRequestsQueuedPerDestination(1024 * 1024);
- client.setDispatchIO(false);
- client.setStrictEventOrdering(false);
- client.start();
Random random = new Random();
// At least 25k requests to warmup properly (use -XX:+PrintCompilation to verify JIT activity)
@@ -143,13 +179,23 @@ public class HttpClientLoadTest extends AbstractHttpClientServerTest
System.gc();
- 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));
+ ByteBufferPool byteBufferPool = connector.getByteBufferPool();
+ if (byteBufferPool instanceof LeakTrackingByteBufferPool)
+ {
+ LeakTrackingByteBufferPool serverBufferPool = (LeakTrackingByteBufferPool)byteBufferPool;
+ 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));
+ byteBufferPool = client.getByteBufferPool();
+ if (byteBufferPool instanceof LeakTrackingByteBufferPool)
+ {
+ LeakTrackingByteBufferPool clientBufferPool = (LeakTrackingByteBufferPool)byteBufferPool;
+ 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));
}
@@ -159,29 +205,15 @@ public class HttpClientLoadTest extends AbstractHttpClientServerTest
CountDownLatch latch = new CountDownLatch(iterations);
List<String> failures = new ArrayList<>();
- int factor = logger.isDebugEnabled() ? 25 : 1;
- factor *= "http".equalsIgnoreCase(scheme) ? 10 : 1000;
+ int factor = (logger.isDebugEnabled() ? 25 : 1) * 100;
// Dumps the state of the client if the test takes too long
final Thread testThread = Thread.currentThread();
- Scheduler.Task task = client.getScheduler().schedule(new Runnable()
+ Scheduler.Task task = client.getScheduler().schedule(() ->
{
- @Override
- public void run()
- {
- logger.warn("Interrupting test, it is taking too long");
- for (String host : Arrays.asList("localhost", "127.0.0.1"))
- {
- HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, host, connector.getLocalPort());
- DuplexConnectionPool connectionPool = destination.getConnectionPool();
- for (Connection connection : new ArrayList<>(connectionPool.getActiveConnections()))
- {
- HttpConnectionOverHTTP active = (HttpConnectionOverHTTP)connection;
- logger.warn(active.getEndPoint() + " exchange " + active.getHttpChannel().getHttpExchange());
- }
- }
- testThread.interrupt();
- }
+ logger.warn("Interrupting test, it is taking too long");
+ logger.warn(client.dump());
+ testThread.interrupt();
}, iterations * factor, TimeUnit.MILLISECONDS);
long begin = System.nanoTime();
@@ -209,7 +241,7 @@ public class HttpClientLoadTest extends AbstractHttpClientServerTest
// Choose a random method
HttpMethod method = random.nextBoolean() ? HttpMethod.GET : HttpMethod.POST;
- boolean ssl = HttpScheme.HTTPS.is(scheme);
+ boolean ssl = isTransportSecure();
// Choose randomly whether to close the connection on the client or on the server
boolean clientClose = false;
@@ -222,7 +254,7 @@ public class HttpClientLoadTest extends AbstractHttpClientServerTest
int maxContentLength = 64 * 1024;
int contentLength = random.nextInt(maxContentLength) + 1;
- test(scheme, host, method.asString(), clientClose, serverClose, contentLength, true, latch, failures);
+ test(ssl ? "https" : "http", host, method.asString(), clientClose, serverClose, contentLength, true, latch, failures);
}
private void test(String scheme, String host, String method, boolean clientClose, boolean serverClose, int contentLength, final boolean checkContentLength, final CountDownLatch latch, final List<String> failures) throws InterruptedException
@@ -298,6 +330,7 @@ public class HttpClientLoadTest extends AbstractHttpClientServerTest
switch (method)
{
case "GET":
+ {
int contentLength = request.getIntHeader("X-Download");
if (contentLength > 0)
{
@@ -305,10 +338,13 @@ public class HttpClientLoadTest extends AbstractHttpClientServerTest
response.getOutputStream().write(new byte[contentLength]);
}
break;
+ }
case "POST":
+ {
response.setHeader("X-Content", request.getHeader("X-Upload"));
IO.copy(request.getInputStream(), response.getOutputStream());
break;
+ }
}
if (Boolean.parseBoolean(request.getHeader("X-Close")))
diff --git a/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/HttpClientTest.java b/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/HttpClientTest.java
index 56ffdd156d..5a17404c55 100644
--- a/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/HttpClientTest.java
+++ b/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/HttpClientTest.java
@@ -91,6 +91,7 @@ public class HttpClientTest extends AbstractTest
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
{
baseRequest.setHandled(true);
+ response.setContentLength(length);
response.getOutputStream().write(bytes);
}
});
diff --git a/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/LastAccessTimeTest.java b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/LastAccessTimeTest.java
index 25c0ad2308..bb09b64333 100644
--- a/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/LastAccessTimeTest.java
+++ b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/LastAccessTimeTest.java
@@ -18,14 +18,6 @@
package org.eclipse.jetty.server.session;
-import java.io.File;
-
-import org.eclipse.jetty.util.IO;
-import org.infinispan.Cache;
-import org.infinispan.configuration.cache.Configuration;
-import org.infinispan.configuration.cache.ConfigurationBuilder;
-import org.infinispan.manager.DefaultCacheManager;
-import org.infinispan.manager.EmbeddedCacheManager;
import org.junit.AfterClass;
import org.junit.BeforeClass;
@@ -61,5 +53,13 @@ public class LastAccessTimeTest extends AbstractLastAccessTimeTest
super.testLastAccessTime();
}
+ @Override
+ public void assertAfterScavenge(AbstractSessionManager manager)
+ {
+ //The infinispan session manager will remove a session from its local memory that was a candidate to be scavenged if
+ //it checks with the cluster and discovers that another node is managing it, so the count is 0
+ assertSessionCounts(0, 1, 1, manager);
+ }
+
}
diff --git a/tests/test-sessions/test-jdbc-sessions/pom.xml b/tests/test-sessions/test-jdbc-sessions/pom.xml
index 9d4a1b8a15..b4a75a545a 100644
--- a/tests/test-sessions/test-jdbc-sessions/pom.xml
+++ b/tests/test-sessions/test-jdbc-sessions/pom.xml
@@ -65,13 +65,13 @@
<dependency>
<groupId>org.apache.derby</groupId>
<artifactId>derby</artifactId>
- <version>10.4.1.3</version>
+ <version>10.12.1.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.derby</groupId>
<artifactId>derbytools</artifactId>
- <version>10.4.1.3</version>
+ <version>10.12.1.1</version>
<scope>test</scope>
</dependency>
<dependency>
diff --git a/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/ClientCrossContextSessionTest.java b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/ClientCrossContextSessionTest.java
index eb4109b1f8..a5348ef81f 100644
--- a/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/ClientCrossContextSessionTest.java
+++ b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/ClientCrossContextSessionTest.java
@@ -18,9 +18,6 @@
package org.eclipse.jetty.server.session;
-import java.sql.DriverManager;
-import java.sql.SQLException;
-
import org.junit.After;
import org.junit.Test;
@@ -46,12 +43,8 @@ public class ClientCrossContextSessionTest extends AbstractClientCrossContextSes
@After
public void tearDown() throws Exception
{
- try
- {
- DriverManager.getConnection( "jdbc:derby:sessions;shutdown=true" );
- }
- catch( SQLException expected )
- {
- }
+ JdbcTestServer.shutdown(null);
}
+
+
}
diff --git a/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/DirtyAttributeTest.java b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/DirtyAttributeTest.java
index 5d2379ab6c..e123a80b2d 100644
--- a/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/DirtyAttributeTest.java
+++ b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/DirtyAttributeTest.java
@@ -38,6 +38,7 @@ import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.servlet.ServletContextHandler;
+import org.junit.After;
import org.junit.Test;
@@ -127,6 +128,14 @@ public class DirtyAttributeTest
}
}
+
+ @After
+ public void tearDown() throws Exception
+ {
+ JdbcTestServer.shutdown(null);
+ }
+
+
public static class TestValue implements HttpSessionActivationListener, HttpSessionBindingListener, Serializable
{
int passivates = 0;
diff --git a/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/ForwardedSessionTest.java b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/ForwardedSessionTest.java
index 7d2f42b976..de843b8608 100644
--- a/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/ForwardedSessionTest.java
+++ b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/ForwardedSessionTest.java
@@ -19,6 +19,7 @@
package org.eclipse.jetty.server.session;
+import org.junit.After;
import org.junit.Test;
/**
@@ -45,6 +46,12 @@ public class ForwardedSessionTest extends AbstractForwardedSessionTest
}
+ @After
+ public void tearDown() throws Exception
+ {
+ JdbcTestServer.shutdown(null);
+ }
+
}
diff --git a/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/ImmortalSessionTest.java b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/ImmortalSessionTest.java
index cabd7248e0..fd4b53e796 100644
--- a/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/ImmortalSessionTest.java
+++ b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/ImmortalSessionTest.java
@@ -18,9 +18,6 @@
package org.eclipse.jetty.server.session;
-import java.sql.DriverManager;
-import java.sql.SQLException;
-
import org.junit.After;
import org.junit.Test;
@@ -46,12 +43,7 @@ public class ImmortalSessionTest extends AbstractImmortalSessionTest
@After
public void tearDown() throws Exception
{
- try
- {
- DriverManager.getConnection( "jdbc:derby:sessions;shutdown=true" );
- }
- catch( SQLException expected )
- {
- }
+ JdbcTestServer.shutdown(null);
}
+
}
diff --git a/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/InvalidationSessionTest.java b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/InvalidationSessionTest.java
index 8075dbdfec..dd68fff5b5 100644
--- a/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/InvalidationSessionTest.java
+++ b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/InvalidationSessionTest.java
@@ -18,9 +18,6 @@
package org.eclipse.jetty.server.session;
-import java.sql.DriverManager;
-import java.sql.SQLException;
-
import org.junit.After;
import org.junit.Test;
@@ -60,12 +57,6 @@ public class InvalidationSessionTest extends AbstractInvalidationSessionTest
@After
public void tearDown() throws Exception
{
- try
- {
- DriverManager.getConnection( "jdbc:derby:sessions;shutdown=true" );
- }
- catch( SQLException expected )
- {
- }
+ JdbcTestServer.shutdown(null);
}
}
diff --git a/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/JdbcTestServer.java b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/JdbcTestServer.java
index 5b55861707..03e9ff1d06 100644
--- a/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/JdbcTestServer.java
+++ b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/JdbcTestServer.java
@@ -22,6 +22,7 @@ import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
+import java.sql.SQLException;
import java.util.HashSet;
import java.util.Set;
@@ -35,7 +36,8 @@ import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
public class JdbcTestServer extends AbstractTestServer
{
public static final String DRIVER_CLASS = "org.apache.derby.jdbc.EmbeddedDriver";
- public static final String DEFAULT_CONNECTION_URL = "jdbc:derby:sessions;create=true";
+ public static final String DEFAULT_CONNECTION_URL = "jdbc:derby:memory:sessions;create=true";
+ public static final String DEFAULT_SHUTDOWN_URL = "jdbc:derby:memory:sessions;drop=true";
public static final int SAVE_INTERVAL = 1;
@@ -43,6 +45,26 @@ public class JdbcTestServer extends AbstractTestServer
{
System.setProperty("derby.system.home", MavenTestingUtils.getTargetFile("test-derby").getAbsolutePath());
}
+
+
+ public static void shutdown (String connectionUrl)
+ throws Exception
+ {
+ if (connectionUrl == null)
+ connectionUrl = DEFAULT_SHUTDOWN_URL;
+
+ try
+ {
+ DriverManager.getConnection(connectionUrl);
+ }
+ catch( SQLException expected )
+ {
+ if (!"08006".equals(expected.getSQLState()))
+ {
+ throw expected;
+ }
+ }
+ }
public JdbcTestServer(int port)
diff --git a/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/LastAccessTimeTest.java b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/LastAccessTimeTest.java
index 541c1ef41c..6939fb4683 100644
--- a/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/LastAccessTimeTest.java
+++ b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/LastAccessTimeTest.java
@@ -18,9 +18,6 @@
package org.eclipse.jetty.server.session;
-import java.sql.DriverManager;
-import java.sql.SQLException;
-
import org.junit.After;
import org.junit.Test;
@@ -37,20 +34,13 @@ public class LastAccessTimeTest extends AbstractLastAccessTimeTest
@Test
public void testLastAccessTime() throws Exception
{
- // Log.getLog().setDebugEnabled(true);
super.testLastAccessTime();
}
@After
public void tearDown() throws Exception
{
- try
- {
- DriverManager.getConnection( "jdbc:derby:sessions;shutdown=true" );
- }
- catch( SQLException expected )
- {
- }
+ JdbcTestServer.shutdown(null);
}
}
diff --git a/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/LocalSessionScavengingTest.java b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/LocalSessionScavengingTest.java
index 87eec2c6dd..ac89427445 100644
--- a/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/LocalSessionScavengingTest.java
+++ b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/LocalSessionScavengingTest.java
@@ -18,9 +18,6 @@
package org.eclipse.jetty.server.session;
-import java.sql.DriverManager;
-import java.sql.SQLException;
-
import org.junit.After;
import org.junit.Test;
@@ -56,16 +53,11 @@ public class LocalSessionScavengingTest extends AbstractLocalSessionScavengingTe
{
super.testLocalSessionsScavenging();
}
+
@After
public void tearDown() throws Exception
{
- try
- {
- DriverManager.getConnection( "jdbc:derby:sessions;shutdown=true" );
- }
- catch( SQLException expected )
- {
- }
+ JdbcTestServer.shutdown(null);
}
}
diff --git a/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/MaxInactiveMigrationTest.java b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/MaxInactiveMigrationTest.java
index 6256945aed..b32500aa77 100644
--- a/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/MaxInactiveMigrationTest.java
+++ b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/MaxInactiveMigrationTest.java
@@ -23,8 +23,6 @@ import static org.junit.Assert.assertTrue;
import java.io.IOException;
import java.io.PrintWriter;
-import java.sql.DriverManager;
-import java.sql.SQLException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
@@ -81,13 +79,8 @@ public class MaxInactiveMigrationTest
testServer1.stop();
testServer2.stop();
client.stop();
- try
- {
- DriverManager.getConnection( "jdbc:derby:sessions;shutdown=true" );
- }
- catch( SQLException expected )
- {
- }
+
+ JdbcTestServer.shutdown(null);
}
diff --git a/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/ModifyMaxInactiveIntervalTest.java b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/ModifyMaxInactiveIntervalTest.java
index a39b655f73..d777a61ee3 100644
--- a/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/ModifyMaxInactiveIntervalTest.java
+++ b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/ModifyMaxInactiveIntervalTest.java
@@ -33,6 +33,7 @@ import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.servlet.ServletContextHandler;
+import org.junit.After;
import org.junit.Test;
@@ -103,6 +104,13 @@ public class ModifyMaxInactiveIntervalTest
}
}
+
+ @After
+ public void tearDown() throws Exception
+ {
+ JdbcTestServer.shutdown(null);
+ }
+
public static class TestModServlet extends HttpServlet
{
@Override
diff --git a/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/NewSessionTest.java b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/NewSessionTest.java
index fc05235225..5a6006cdd1 100644
--- a/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/NewSessionTest.java
+++ b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/NewSessionTest.java
@@ -18,9 +18,6 @@
package org.eclipse.jetty.server.session;
-import java.sql.DriverManager;
-import java.sql.SQLException;
-
import org.junit.After;
import org.junit.Test;
@@ -46,12 +43,7 @@ public class NewSessionTest extends AbstractNewSessionTest
@After
public void tearDown() throws Exception
{
- try
- {
- DriverManager.getConnection( "jdbc:derby:sessions;shutdown=true" );
- }
- catch( SQLException expected )
- {
- }
+ JdbcTestServer.shutdown(null);
}
+
}
diff --git a/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/OrphanedSessionTest.java b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/OrphanedSessionTest.java
index 391f3e0e49..4042f373fe 100644
--- a/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/OrphanedSessionTest.java
+++ b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/OrphanedSessionTest.java
@@ -18,9 +18,6 @@
package org.eclipse.jetty.server.session;
-import java.sql.DriverManager;
-import java.sql.SQLException;
-
import org.junit.After;
import org.junit.Test;
@@ -43,12 +40,6 @@ public class OrphanedSessionTest extends AbstractOrphanedSessionTest
@After
public void tearDown() throws Exception
{
- try
- {
- DriverManager.getConnection( "jdbc:derby:sessions;shutdown=true" );
- }
- catch( SQLException expected )
- {
- }
+ JdbcTestServer.shutdown(null);
}
}
diff --git a/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/ProxySerializationTest.java b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/ProxySerializationTest.java
index 38db019ce5..6efffa406a 100644
--- a/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/ProxySerializationTest.java
+++ b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/ProxySerializationTest.java
@@ -20,6 +20,7 @@
package org.eclipse.jetty.server.session;
import org.eclipse.jetty.servlet.ServletContextHandler;
+import org.junit.After;
import org.junit.Test;
/**
@@ -55,4 +56,11 @@ public class ProxySerializationTest extends AbstractProxySerializationTest
super.testProxySerialization();
}
+
+
+ @After
+ public void tearDown() throws Exception
+ {
+ JdbcTestServer.shutdown(null);
+ }
}
diff --git a/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/ReentrantRequestSessionTest.java b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/ReentrantRequestSessionTest.java
index dced30d8d2..82b3d5f6eb 100644
--- a/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/ReentrantRequestSessionTest.java
+++ b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/ReentrantRequestSessionTest.java
@@ -18,9 +18,6 @@
package org.eclipse.jetty.server.session;
-import java.sql.DriverManager;
-import java.sql.SQLException;
-
import org.junit.After;
import org.junit.Test;
@@ -42,15 +39,11 @@ public class ReentrantRequestSessionTest extends AbstractReentrantRequestSession
super.testReentrantRequestSession();
}
+
@After
public void tearDown() throws Exception
{
- try
- {
- DriverManager.getConnection( "jdbc:derby:sessions;shutdown=true" );
- }
- catch( SQLException expected )
- {
- }
+ JdbcTestServer.shutdown(null);
}
+
}
diff --git a/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/ReloadedSessionMissingClassTest.java b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/ReloadedSessionMissingClassTest.java
index 4dbec1c397..44acb9d263 100644
--- a/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/ReloadedSessionMissingClassTest.java
+++ b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/ReloadedSessionMissingClassTest.java
@@ -38,6 +38,7 @@ import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.StdErrLog;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.webapp.WebAppContext;
+import org.junit.After;
import org.junit.Rule;
import org.junit.Test;
@@ -139,4 +140,11 @@ public class ReloadedSessionMissingClassTest
server1.stop();
}
}
+
+ @After
+ public void tearDown() throws Exception
+ {
+ JdbcTestServer.shutdown(null);
+ }
+
}
diff --git a/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/SaveIntervalTest.java b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/SaveIntervalTest.java
index 3b02825a9a..e031b29543 100644
--- a/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/SaveIntervalTest.java
+++ b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/SaveIntervalTest.java
@@ -35,6 +35,7 @@ import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
+import org.junit.After;
import org.junit.Ignore;
import org.junit.Test;
@@ -133,6 +134,12 @@ public class SaveIntervalTest
}
}
+ @After
+ public void tearDown() throws Exception
+ {
+ JdbcTestServer.shutdown(null);
+ }
+
public static class TestSaveIntervalServlet extends HttpServlet
{
public HttpSession _session;
diff --git a/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/ServerCrossContextSessionTest.java b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/ServerCrossContextSessionTest.java
index 83c69448d7..497881fc72 100644
--- a/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/ServerCrossContextSessionTest.java
+++ b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/ServerCrossContextSessionTest.java
@@ -18,9 +18,6 @@
package org.eclipse.jetty.server.session;
-import java.sql.DriverManager;
-import java.sql.SQLException;
-
import org.junit.After;
import org.junit.Test;
@@ -44,12 +41,6 @@ public class ServerCrossContextSessionTest extends AbstractServerCrossContextSes
@After
public void tearDown() throws Exception
{
- try
- {
- DriverManager.getConnection( "jdbc:derby:sessions;shutdown=true" );
- }
- catch( SQLException expected )
- {
- }
+ JdbcTestServer.shutdown(null);
}
}
diff --git a/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/SessionExpiryTest.java b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/SessionExpiryTest.java
index 5177b93987..581113df63 100644
--- a/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/SessionExpiryTest.java
+++ b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/SessionExpiryTest.java
@@ -18,9 +18,6 @@
package org.eclipse.jetty.server.session;
-import java.sql.DriverManager;
-import java.sql.SQLException;
-
import org.junit.After;
import org.junit.Test;
@@ -58,18 +55,10 @@ public class SessionExpiryTest extends AbstractSessionExpiryTest
super.testSessionNotExpired();
}
-
-
-
@After
public void tearDown() throws Exception
{
- try
- {
- DriverManager.getConnection( "jdbc:derby:sessions;shutdown=true" );
- }
- catch( SQLException expected )
- {
- }
+ JdbcTestServer.shutdown(null);
}
+
}
diff --git a/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/SessionInvalidateAndCreateTest.java b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/SessionInvalidateAndCreateTest.java
index 746e0bd522..de88616878 100644
--- a/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/SessionInvalidateAndCreateTest.java
+++ b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/SessionInvalidateAndCreateTest.java
@@ -19,6 +19,7 @@
package org.eclipse.jetty.server.session;
+import org.junit.After;
import org.junit.Test;
public class SessionInvalidateAndCreateTest extends AbstractSessionInvalidateAndCreateTest
@@ -35,4 +36,12 @@ public class SessionInvalidateAndCreateTest extends AbstractSessionInvalidateAnd
{
super.testSessionScavenge();
}
+
+
+ @After
+ public void tearDown() throws Exception
+ {
+ JdbcTestServer.shutdown(null);
+ }
+
}
diff --git a/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/SessionMigrationTest.java b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/SessionMigrationTest.java
index 2ff6fa42bc..e8bbbf7509 100644
--- a/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/SessionMigrationTest.java
+++ b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/SessionMigrationTest.java
@@ -18,9 +18,6 @@
package org.eclipse.jetty.server.session;
-import java.sql.DriverManager;
-import java.sql.SQLException;
-
import org.junit.After;
import org.junit.Test;
@@ -44,12 +41,6 @@ public class SessionMigrationTest extends AbstractSessionMigrationTest
@After
public void tearDown() throws Exception
{
- try
- {
- DriverManager.getConnection( "jdbc:derby:sessions;shutdown=true" );
- }
- catch( SQLException expected )
- {
- }
+ JdbcTestServer.shutdown(null);
}
}
diff --git a/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/SessionRenewTest.java b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/SessionRenewTest.java
index 60416b6e8f..cfd4f15b1a 100644
--- a/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/SessionRenewTest.java
+++ b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/SessionRenewTest.java
@@ -18,9 +18,6 @@
package org.eclipse.jetty.server.session;
-import java.sql.DriverManager;
-import java.sql.SQLException;
-
import org.junit.After;
import org.junit.Test;
@@ -39,16 +36,11 @@ public class SessionRenewTest extends AbstractSessionRenewTest
super.testSessionRenewal();
}
+
@After
public void tearDown() throws Exception
{
- try
- {
- DriverManager.getConnection( "jdbc:derby:sessions;shutdown=true" );
- }
- catch( SQLException expected )
- {
- }
+ JdbcTestServer.shutdown(null);
}
-
+
}
diff --git a/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/SessionValueSavingTest.java b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/SessionValueSavingTest.java
index 41c25609f7..d647534943 100644
--- a/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/SessionValueSavingTest.java
+++ b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/SessionValueSavingTest.java
@@ -18,9 +18,6 @@
package org.eclipse.jetty.server.session;
-import java.sql.DriverManager;
-import java.sql.SQLException;
-
import org.junit.After;
import org.junit.Test;
@@ -34,21 +31,16 @@ public class SessionValueSavingTest extends AbstractSessionValueSavingTest
return new JdbcTestServer(port,max,scavenge);
}
- @Test
- public void testSessionValueSaving() throws Exception
- {
- super.testSessionValueSaving();
- }
+ @Test
+ public void testSessionValueSaving() throws Exception
+ {
+ super.testSessionValueSaving();
+ }
+
- @After
- public void tearDown() throws Exception
- {
- try
- {
- DriverManager.getConnection( "jdbc:derby:sessions;shutdown=true" );
- }
- catch( SQLException expected )
- {
- }
- }
+ @After
+ public void tearDown() throws Exception
+ {
+ JdbcTestServer.shutdown(null);
+ }
}
diff --git a/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/StopSessionManagerPreserveSessionTest.java b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/StopSessionManagerPreserveSessionTest.java
index 84b2ac32ad..1c43a75024 100644
--- a/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/StopSessionManagerPreserveSessionTest.java
+++ b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/StopSessionManagerPreserveSessionTest.java
@@ -21,9 +21,6 @@ package org.eclipse.jetty.server.session;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
-import java.sql.DriverManager;
-import java.sql.SQLException;
-
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.junit.After;
import org.junit.Test;
@@ -31,17 +28,12 @@ import org.junit.Test;
public class StopSessionManagerPreserveSessionTest extends AbstractStopSessionManagerPreserveSessionTest
{
JdbcTestServer _server;
-
+
+
@After
public void tearDown() throws Exception
{
- try
- {
- DriverManager.getConnection( "jdbc:derby:sessions;shutdown=true" );
- }
- catch( SQLException expected )
- {
- }
+ JdbcTestServer.shutdown(null);
}
@Override
diff --git a/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/WebAppObjectInSessionTest.java b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/WebAppObjectInSessionTest.java
index 8a480903b2..88d7df119d 100644
--- a/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/WebAppObjectInSessionTest.java
+++ b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/WebAppObjectInSessionTest.java
@@ -18,9 +18,6 @@
package org.eclipse.jetty.server.session;
-import java.sql.DriverManager;
-import java.sql.SQLException;
-
import org.eclipse.jetty.util.resource.Resource;
import org.junit.After;
import org.junit.Test;
@@ -45,17 +42,11 @@ public class WebAppObjectInSessionTest extends AbstractWebAppObjectInSessionTest
super.testWebappObjectInSession();
}
-
@After
public void tearDown() throws Exception
{
- try
- {
- DriverManager.getConnection( "jdbc:derby:sessions;shutdown=true" );
- }
- catch( SQLException expected )
- {
- }
+ JdbcTestServer.shutdown(null);
}
+
}
diff --git a/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/MongoTestServer.java b/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/MongoTestServer.java
index e3c78c2222..0ada08a41d 100644
--- a/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/MongoTestServer.java
+++ b/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/MongoTestServer.java
@@ -39,6 +39,7 @@ public class MongoTestServer extends AbstractTestServer
{
static int __workers=0;
private boolean _saveAllAttributes = false; // false save dirty, true save all
+ private int _saveInterval = 0;
public static class TestMongoSessionIdManager extends MongoSessionIdManager
@@ -70,13 +71,13 @@ public class MongoTestServer extends AbstractTestServer
public MongoTestServer(int port, int maxInactivePeriod, int scavengePeriod)
{
super(port, maxInactivePeriod, scavengePeriod);
+ _saveInterval = 0;
}
public MongoTestServer(int port, int maxInactivePeriod, int scavengePeriod, boolean saveAllAttributes)
{
super(port, maxInactivePeriod, scavengePeriod);
-
_saveAllAttributes = saveAllAttributes;
}
@@ -109,10 +110,9 @@ public class MongoTestServer extends AbstractTestServer
throw new RuntimeException(e);
}
- manager.setSavePeriod(1);
+ manager.setSavePeriod(_saveInterval);
manager.setStalePeriod(0);
manager.setSaveAllAttributes(_saveAllAttributes);
- //manager.setScavengePeriod((int)TimeUnit.SECONDS.toMillis(_scavengePeriod));
return manager;
}
diff --git a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractInvalidationSessionTest.java b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractInvalidationSessionTest.java
index 8d2d660404..de345efba3 100644
--- a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractInvalidationSessionTest.java
+++ b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractInvalidationSessionTest.java
@@ -32,6 +32,7 @@ import javax.servlet.http.HttpSession;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.client.api.Request;
+import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.util.thread.QueuedThreadPool;
import org.junit.Test;
@@ -51,16 +52,20 @@ public abstract class AbstractInvalidationSessionTest
String contextPath = "";
String servletMapping = "/server";
AbstractTestServer server1 = createServer(0);
- server1.addContext(contextPath).addServlet(TestServlet.class, servletMapping);
+ ServletContextHandler context1 = server1.addContext(contextPath);
+ context1.addServlet(TestServlet.class, servletMapping);
+ AbstractSessionManager m1 = (AbstractSessionManager) context1.getSessionHandler().getSessionManager();
try
{
server1.start();
int port1 = server1.getPort();
AbstractTestServer server2 = createServer(0);
- server2.addContext(contextPath).addServlet(TestServlet.class, servletMapping);
-
+ ServletContextHandler context2 = server2.addContext(contextPath);
+ context2.addServlet(TestServlet.class, servletMapping);
+ AbstractSessionManager m2 = (AbstractSessionManager) context2.getSessionHandler().getSessionManager();
+
try
{
server2.start();
@@ -81,25 +86,33 @@ public abstract class AbstractInvalidationSessionTest
assertEquals(HttpServletResponse.SC_OK,response1.getStatus());
String sessionCookie = response1.getHeaders().get("Set-Cookie");
assertTrue(sessionCookie != null);
+ assertEquals(1, m1.getSessions());
+ assertEquals(1, m1.getSessionsMax());
+ assertEquals(1, m1.getSessionsTotal());
+
// Mangle the cookie, replacing Path with $Path, etc.
sessionCookie = sessionCookie.replaceFirst("(\\W)(P|p)ath=", "$1\\$Path=");
// Be sure the session is also present in node2
-
Request request2 = client.newRequest(urls[1] + "?action=increment");
request2.header("Cookie", sessionCookie);
ContentResponse response2 = request2.send();
assertEquals(HttpServletResponse.SC_OK,response2.getStatus());
-
+ assertEquals(1, m2.getSessions());
+ assertEquals(1, m2.getSessionsMax());
+ assertEquals(1, m2.getSessionsTotal());
+
// Invalidate on node1
Request request1 = client.newRequest(urls[0] + "?action=invalidate");
request1.header("Cookie", sessionCookie);
response1 = request1.send();
assertEquals(HttpServletResponse.SC_OK, response1.getStatus());
-
-
+ assertEquals(0, m1.getSessions());
+ assertEquals(1, m1.getSessionsMax());
+ assertEquals(1, m1.getSessionsTotal());
+
pause();
// Be sure on node2 we don't see the session anymore
@@ -107,6 +120,9 @@ public abstract class AbstractInvalidationSessionTest
request2.header("Cookie", sessionCookie);
response2 = request2.send();
assertEquals(HttpServletResponse.SC_OK,response2.getStatus());
+ assertEquals(0, m2.getSessions());
+ assertEquals(1, m2.getSessionsMax());
+ assertEquals(1, m2.getSessionsTotal());
}
finally
{
diff --git a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractLastAccessTimeTest.java b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractLastAccessTimeTest.java
index 787eb739f3..847e800dc8 100644
--- a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractLastAccessTimeTest.java
+++ b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractLastAccessTimeTest.java
@@ -65,15 +65,19 @@ public abstract class AbstractLastAccessTimeTest
ServletHolder holder1 = new ServletHolder(servlet1);
ServletContextHandler context = server1.addContext(contextPath);
TestSessionListener listener1 = new TestSessionListener();
- context.addEventListener(listener1);
+ context.getSessionHandler().addEventListener(listener1);
context.addServlet(holder1, servletMapping);
+ AbstractSessionManager m1 = (AbstractSessionManager)context.getSessionHandler().getSessionManager();
+
try
{
server1.start();
int port1=server1.getPort();
AbstractTestServer server2 = createServer(0, maxInactivePeriod, scavengePeriod);
- server2.addContext(contextPath).addServlet(TestServlet.class, servletMapping);
+ ServletContextHandler context2 = server2.addContext(contextPath);
+ context2.addServlet(TestServlet.class, servletMapping);
+ AbstractSessionManager m2 = (AbstractSessionManager)context2.getSessionHandler().getSessionManager();
try
{
@@ -89,9 +93,12 @@ public abstract class AbstractLastAccessTimeTest
assertEquals("test", response1.getContentAsString());
String sessionCookie = response1.getHeaders().get("Set-Cookie");
assertTrue( sessionCookie != null );
+ assertEquals(1, m1.getSessions());
+ assertEquals(1, m1.getSessionsMax());
+ assertEquals(1, m1.getSessionsTotal());
// Mangle the cookie, replacing Path with $Path, etc.
- sessionCookie = sessionCookie.replaceFirst("(\\W)(P|p)ath=", "$1\\$Path=");
-
+ sessionCookie = sessionCookie.replaceFirst("(\\W)(P|p)ath=", "$1\\$Path=");
+
// Perform some request to server2 using the session cookie from the previous request
// This should migrate the session from server1 to server2, and leave server1's
// session in a very stale state, while server2 has a very fresh session.
@@ -111,14 +118,15 @@ public abstract class AbstractLastAccessTimeTest
sessionCookie = setCookie.replaceFirst("(\\W)(P|p)ath=", "$1\\$Path=");
Thread.sleep(requestInterval);
+ assertSessionCounts(1,1,1, m2);
}
-
// At this point, session1 should be eligible for expiration.
// Let's wait for the scavenger to run, waiting 2.5 times the scavenger period
Thread.sleep(scavengePeriod * 2500L);
//check that the session was not scavenged over on server1 by ensuring that the SessionListener destroy method wasn't called
assertFalse(listener1.destroyed);
+ assertAfterScavenge(m1);
}
finally
{
@@ -135,6 +143,23 @@ public abstract class AbstractLastAccessTimeTest
server1.stop();
}
}
+
+ public void assertAfterSessionCreated (AbstractSessionManager m)
+ {
+ assertSessionCounts(1, 1, 1, m);
+ }
+
+ public void assertAfterScavenge (AbstractSessionManager manager)
+ {
+ assertSessionCounts(1,1,1, manager);
+ }
+
+ public void assertSessionCounts (int current, int max, int total, AbstractSessionManager manager)
+ {
+ assertEquals(current, manager.getSessions());
+ assertEquals(max, manager.getSessionsMax());
+ assertEquals(total, manager.getSessionsTotal());
+ }
public static class TestSessionListener implements HttpSessionListener
{
diff --git a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractRemoveSessionTest.java b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractRemoveSessionTest.java
index c344d72530..bb126f68db 100644
--- a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractRemoveSessionTest.java
+++ b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractRemoveSessionTest.java
@@ -39,6 +39,12 @@ import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.junit.Test;
+/**
+ * AbstractRemoveSessionTest
+ *
+ * Test that invalidating a session does not return the session on the next request.
+ *
+ */
public abstract class AbstractRemoveSessionTest
{
public abstract AbstractTestServer createServer(int port, int max, int scavenge);
@@ -55,6 +61,7 @@ public abstract class AbstractRemoveSessionTest
context.addServlet(TestServlet.class, servletMapping);
TestEventListener testListener = new TestEventListener();
context.getSessionHandler().addEventListener(testListener);
+ AbstractSessionManager m = (AbstractSessionManager)context.getSessionHandler().getSessionManager();
try
{
server.start();
@@ -72,7 +79,10 @@ public abstract class AbstractRemoveSessionTest
sessionCookie = sessionCookie.replaceFirst("(\\W)(P|p)ath=", "$1\\$Path=");
//ensure sessionCreated listener is called
assertTrue (testListener.isCreated());
-
+ assertEquals(1, m.getSessions());
+ assertEquals(1, m.getSessionsMax());
+ assertEquals(1, m.getSessionsTotal());
+
//now delete the session
Request request = client.newRequest("http://localhost:" + port + contextPath + servletMapping + "?action=delete");
request.header("Cookie", sessionCookie);
@@ -80,13 +90,18 @@ public abstract class AbstractRemoveSessionTest
assertEquals(HttpServletResponse.SC_OK,response.getStatus());
//ensure sessionDestroyed listener is called
assertTrue(testListener.isDestroyed());
-
+ assertEquals(0, m.getSessions());
+ assertEquals(1, m.getSessionsMax());
+ assertEquals(1, m.getSessionsTotal());
// The session is not there anymore, even if we present an old cookie
request = client.newRequest("http://localhost:" + port + contextPath + servletMapping + "?action=check");
request.header("Cookie", sessionCookie);
response = request.send();
assertEquals(HttpServletResponse.SC_OK,response.getStatus());
+ assertEquals(0, m.getSessions());
+ assertEquals(1, m.getSessionsMax());
+ assertEquals(1, m.getSessionsTotal());
}
finally
{
diff --git a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSameNodeLoadTest.java b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSameNodeLoadTest.java
index 999858fb5b..28e6cbdc1e 100644
--- a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSameNodeLoadTest.java
+++ b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSameNodeLoadTest.java
@@ -19,8 +19,8 @@
package org.eclipse.jetty.server.session;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
import java.io.IOException;
import java.io.PrintWriter;
diff --git a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractServerCrossContextSessionTest.java b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractServerCrossContextSessionTest.java
index 30a080a732..ed7737f359 100644
--- a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractServerCrossContextSessionTest.java
+++ b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractServerCrossContextSessionTest.java
@@ -22,7 +22,6 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.io.IOException;
-import java.util.Collections;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
@@ -87,10 +86,9 @@ public abstract class AbstractServerCrossContextSessionTest
{
HttpSession session = request.getSession(false);
if (session == null) session = request.getSession(true);
-
// Add something to the session
session.setAttribute("A", "A");
- System.out.println("A: session.getAttributeNames() = " + Collections.list(session.getAttributeNames()));
+
// Perform cross context dispatch to another context
// Over there we will check that the session attribute added above is not visible
@@ -101,7 +99,6 @@ public abstract class AbstractServerCrossContextSessionTest
// Check that we don't see things put in session by contextB
Object objectB = session.getAttribute("B");
assertTrue(objectB == null);
- System.out.println("A: session.getAttributeNames() = " + Collections.list(session.getAttributeNames()));
}
}
@@ -119,7 +116,6 @@ public abstract class AbstractServerCrossContextSessionTest
// Add something, so in contextA we can check if it is visible (it must not).
session.setAttribute("B", "B");
- System.out.println("B: session.getAttributeNames() = " + Collections.list(session.getAttributeNames()));
}
}
}
diff --git a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSessionCookieTest.java b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSessionCookieTest.java
index b79a0a33ac..a867711ce4 100644
--- a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSessionCookieTest.java
+++ b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSessionCookieTest.java
@@ -29,8 +29,6 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
-import junit.framework.Assert;
-
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.client.api.Request;
@@ -38,6 +36,8 @@ import org.eclipse.jetty.servlet.ServletContextHandler;
import org.junit.Ignore;
import org.junit.Test;
+import junit.framework.Assert;
+
/**
* AbstractSessionCookieTest
*/
diff --git a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSessionExpiryTest.java b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSessionExpiryTest.java
index 5c11f8b35c..0c221a2860 100644
--- a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSessionExpiryTest.java
+++ b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSessionExpiryTest.java
@@ -40,6 +40,11 @@ import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.junit.Test;
+/**
+ * AbstractSessionExpiryTest
+ *
+ *
+ */
public abstract class AbstractSessionExpiryTest
{
public abstract AbstractTestServer createServer(int port, int max, int scavenge);
@@ -104,6 +109,7 @@ public abstract class AbstractSessionExpiryTest
//now stop the server
server1.stop();
+
//start the server again, before the session times out
server1.start();
@@ -161,12 +167,12 @@ public abstract class AbstractSessionExpiryTest
sessionCookie = sessionCookie.replaceFirst("(\\W)(P|p)ath=", "$1\\$Path=");
String sessionId = AbstractTestServer.extractSessionId(sessionCookie);
-
+
verifySessionCreated(listener,sessionId);
//now stop the server
server1.stop();
-
+
//and wait until the expiry time has passed
pause(inactivePeriod);
diff --git a/tests/test-webapps/test-jetty-webapp/src/main/config/demo-base/webapps/test.xml b/tests/test-webapps/test-jetty-webapp/src/main/config/demo-base/webapps/test.xml
index 0037cb4906..cae730e89d 100644
--- a/tests/test-webapps/test-jetty-webapp/src/main/config/demo-base/webapps/test.xml
+++ b/tests/test-webapps/test-jetty-webapp/src/main/config/demo-base/webapps/test.xml
@@ -11,7 +11,7 @@ directory, additional configuration may be specified and hot deployments
detected.
===================================================================== -->
-<Configure class="org.eclipse.jetty.webapp.WebAppContext">
+<Configure id="testWebapp" class="org.eclipse.jetty.webapp.WebAppContext">
<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
<!-- Required minimal context configuration : -->
@@ -41,6 +41,13 @@ detected.
</New>
</Set>
+ <!-- Set Caching Classloader that improves performance on resource searching webapps -->
+ <Set name="classLoader">
+ <New class="org.eclipse.jetty.webapp.CachingWebAppClassLoader">
+ <Arg><Ref refid="testWebapp"/></Arg>
+ </New>
+ </Set>
+
<!-- Enable symlinks
<Call name="addAliasCheck">
<Arg><New class="org.eclipse.jetty.server.handler.AllowSymLinkAliasChecker"/></Arg>

Back to the top