Merge remote-tracking branch 'origin/master' into session-refactor

This commit is contained in:
Jan Bartel 2015-09-09 16:13:10 +10:00
commit bf2a1fa29e
134 changed files with 859 additions and 293 deletions

View File

@ -1,4 +1,35 @@
jetty-9.3.3-SNAPSHOT jetty-9.3.4-SNAPSHOT
jetty-9.3.3.v20150827 - 27 August 2015
+ 470311 Introduce a proxy-protocol module.
+ 471055 Restore legacy/experimental WebSocket extensions (deflate-frame)
+ 472411 PathResource.checkAliasPath() typo
+ 473321 Overriding SSL context KeyStoreType requires explicit override of
TrustStoreType
+ 474025 SslContextFactory does not work with JCEKS Keystore
+ 474068 Update WebSocket Extension for permessage-deflate draft-22
+ 474319 Reintroduce blocking connect().
+ 474321 Allow synchronous address resolution.
+ 474344 apache-jstl includes test dependencies
+ 474358 DefaultServlet bad Content-Type on compressed content
+ 474361 Handle JVM version extensions like -internal
+ 474453 Tiny buffers (under 7 bytes) fail to compress in permessage-deflate
+ 474454 Backport permessage-deflate from Jetty 9.3.x to 9.2.x
+ 474455 Enable permessage-deflate WebSocket extension
+ 474558 Debug log ServletContainerInitializer @HandlesTypes contents
+ 474617 AsyncListener.onError not called for errors
+ 474618 AsyncListener.onComplete not called when error occurs
+ 474634 AsyncListener.onError() handling.
+ 474685 GzipHandler configuration supports csv paths and mimetypes.
+ 474888 HttpClient JMX support.
+ 474936 WebSocketSessions are not always cleaned out from openSessions
+ 474961 Close input stream for classes in AnnotationParser after scanning
+ 475195 SNI matching fails when keystore does not contain wild certificates.
+ 475483 Starting Jetty with [exec] should use properties file.
+ 475546 ClosedChannelException when connecting to HTTPS over HTTP proxy with
CONNECT.
+ 475605 Add support for multi-homed destinations.
+ 475927 SecureRequestCustomizer fails to match host.
jetty-9.3.2.v20150730 - 30 July 2015 jetty-9.3.2.v20150730 - 30 July 2015
+ 470351 Fixed SNI matching of wildcard certificates + 470351 Fixed SNI matching of wildcard certificates

View File

@ -2,7 +2,7 @@
<parent> <parent>
<groupId>org.eclipse.jetty</groupId> <groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId> <artifactId>jetty-project</artifactId>
<version>9.3.3-SNAPSHOT</version> <version>9.3.4-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath> <relativePath>../../pom.xml</relativePath>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@ -2,7 +2,7 @@
<parent> <parent>
<groupId>org.eclipse.jetty</groupId> <groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId> <artifactId>jetty-project</artifactId>
<version>9.3.3-SNAPSHOT</version> <version>9.3.4-SNAPSHOT</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>apache-jsp</artifactId> <artifactId>apache-jsp</artifactId>

View File

@ -2,7 +2,7 @@
<parent> <parent>
<groupId>org.eclipse.jetty</groupId> <groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId> <artifactId>jetty-project</artifactId>
<version>9.3.3-SNAPSHOT</version> <version>9.3.4-SNAPSHOT</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>apache-jstl</artifactId> <artifactId>apache-jstl</artifactId>

View File

@ -2,7 +2,7 @@
<parent> <parent>
<groupId>org.eclipse.jetty</groupId> <groupId>org.eclipse.jetty</groupId>
<artifactId>example-async-rest</artifactId> <artifactId>example-async-rest</artifactId>
<version>9.3.3-SNAPSHOT</version> <version>9.3.4-SNAPSHOT</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>org.eclipse.jetty.example-async-rest</groupId> <groupId>org.eclipse.jetty.example-async-rest</groupId>

View File

@ -2,7 +2,7 @@
<parent> <parent>
<groupId>org.eclipse.jetty</groupId> <groupId>org.eclipse.jetty</groupId>
<artifactId>example-async-rest</artifactId> <artifactId>example-async-rest</artifactId>
<version>9.3.3-SNAPSHOT</version> <version>9.3.4-SNAPSHOT</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>org.eclipse.jetty.example-async-rest</groupId> <groupId>org.eclipse.jetty.example-async-rest</groupId>

View File

@ -2,7 +2,7 @@
<parent> <parent>
<groupId>org.eclipse.jetty.examples</groupId> <groupId>org.eclipse.jetty.examples</groupId>
<artifactId>examples-parent</artifactId> <artifactId>examples-parent</artifactId>
<version>9.3.3-SNAPSHOT</version> <version>9.3.4-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@ -2,7 +2,7 @@
<parent> <parent>
<groupId>org.eclipse.jetty.examples</groupId> <groupId>org.eclipse.jetty.examples</groupId>
<artifactId>examples-parent</artifactId> <artifactId>examples-parent</artifactId>
<version>9.3.3-SNAPSHOT</version> <version>9.3.4-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@ -21,7 +21,7 @@
<parent> <parent>
<groupId>org.eclipse.jetty</groupId> <groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId> <artifactId>jetty-project</artifactId>
<version>9.3.3-SNAPSHOT</version> <version>9.3.4-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>
<groupId>org.eclipse.jetty.examples</groupId> <groupId>org.eclipse.jetty.examples</groupId>

View File

@ -2,7 +2,7 @@
<parent> <parent>
<groupId>org.eclipse.jetty</groupId> <groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-alpn-parent</artifactId> <artifactId>jetty-alpn-parent</artifactId>
<version>9.3.3-SNAPSHOT</version> <version>9.3.4-SNAPSHOT</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>jetty-alpn-client</artifactId> <artifactId>jetty-alpn-client</artifactId>

View File

@ -2,7 +2,7 @@
<parent> <parent>
<groupId>org.eclipse.jetty</groupId> <groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-alpn-parent</artifactId> <artifactId>jetty-alpn-parent</artifactId>
<version>9.3.3-SNAPSHOT</version> <version>9.3.4-SNAPSHOT</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>jetty-alpn-server</artifactId> <artifactId>jetty-alpn-server</artifactId>

View File

@ -2,7 +2,7 @@
<parent> <parent>
<groupId>org.eclipse.jetty</groupId> <groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId> <artifactId>jetty-project</artifactId>
<version>9.3.3-SNAPSHOT</version> <version>9.3.4-SNAPSHOT</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>jetty-alpn-parent</artifactId> <artifactId>jetty-alpn-parent</artifactId>

View File

@ -2,7 +2,7 @@
<parent> <parent>
<groupId>org.eclipse.jetty</groupId> <groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId> <artifactId>jetty-project</artifactId>
<version>9.3.3-SNAPSHOT</version> <version>9.3.4-SNAPSHOT</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>jetty-annotations</artifactId> <artifactId>jetty-annotations</artifactId>

View File

@ -2,7 +2,7 @@
<parent> <parent>
<groupId>org.eclipse.jetty</groupId> <groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId> <artifactId>jetty-project</artifactId>
<version>9.3.3-SNAPSHOT</version> <version>9.3.4-SNAPSHOT</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>jetty-ant</artifactId> <artifactId>jetty-ant</artifactId>

View File

@ -2,7 +2,7 @@
<parent> <parent>
<groupId>org.eclipse.jetty.cdi</groupId> <groupId>org.eclipse.jetty.cdi</groupId>
<artifactId>jetty-cdi-parent</artifactId> <artifactId>jetty-cdi-parent</artifactId>
<version>9.3.3-SNAPSHOT</version> <version>9.3.4-SNAPSHOT</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>cdi-core</artifactId> <artifactId>cdi-core</artifactId>

View File

@ -2,7 +2,7 @@
<parent> <parent>
<groupId>org.eclipse.jetty.cdi</groupId> <groupId>org.eclipse.jetty.cdi</groupId>
<artifactId>jetty-cdi-parent</artifactId> <artifactId>jetty-cdi-parent</artifactId>
<version>9.3.3-SNAPSHOT</version> <version>9.3.4-SNAPSHOT</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>cdi-full-servlet</artifactId> <artifactId>cdi-full-servlet</artifactId>

View File

@ -2,7 +2,7 @@
<parent> <parent>
<groupId>org.eclipse.jetty.cdi</groupId> <groupId>org.eclipse.jetty.cdi</groupId>
<artifactId>jetty-cdi-parent</artifactId> <artifactId>jetty-cdi-parent</artifactId>
<version>9.3.3-SNAPSHOT</version> <version>9.3.4-SNAPSHOT</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>cdi-servlet</artifactId> <artifactId>cdi-servlet</artifactId>

View File

@ -2,7 +2,7 @@
<parent> <parent>
<groupId>org.eclipse.jetty.cdi</groupId> <groupId>org.eclipse.jetty.cdi</groupId>
<artifactId>jetty-cdi-parent</artifactId> <artifactId>jetty-cdi-parent</artifactId>
<version>9.3.3-SNAPSHOT</version> <version>9.3.4-SNAPSHOT</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>cdi-websocket</artifactId> <artifactId>cdi-websocket</artifactId>

View File

@ -2,7 +2,7 @@
<parent> <parent>
<groupId>org.eclipse.jetty</groupId> <groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId> <artifactId>jetty-project</artifactId>
<version>9.3.3-SNAPSHOT</version> <version>9.3.4-SNAPSHOT</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>org.eclipse.jetty.cdi</groupId> <groupId>org.eclipse.jetty.cdi</groupId>

View File

@ -20,7 +20,7 @@
<parent> <parent>
<groupId>org.eclipse.jetty.cdi</groupId> <groupId>org.eclipse.jetty.cdi</groupId>
<artifactId>jetty-cdi-parent</artifactId> <artifactId>jetty-cdi-parent</artifactId>
<version>9.3.3-SNAPSHOT</version> <version>9.3.4-SNAPSHOT</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>test-cdi-webapp</artifactId> <artifactId>test-cdi-webapp</artifactId>

View File

@ -2,7 +2,7 @@
<parent> <parent>
<groupId>org.eclipse.jetty</groupId> <groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId> <artifactId>jetty-project</artifactId>
<version>9.3.3-SNAPSHOT</version> <version>9.3.4-SNAPSHOT</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@ -21,9 +21,9 @@ package org.eclipse.jetty.client;
import java.io.Closeable; import java.io.Closeable;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Deque;
import java.util.List; import java.util.List;
import java.util.concurrent.BlockingDeque; import java.util.Queue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingDeque; import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantLock;
@ -44,15 +44,15 @@ import org.eclipse.jetty.util.thread.Sweeper;
@ManagedObject("The connection pool") @ManagedObject("The connection pool")
public class ConnectionPool implements Closeable, Dumpable, Sweeper.Sweepable public class ConnectionPool implements Closeable, Dumpable, Sweeper.Sweepable
{ {
protected static final Logger LOG = Log.getLogger(ConnectionPool.class); private static final Logger LOG = Log.getLogger(ConnectionPool.class);
private final AtomicInteger connectionCount = new AtomicInteger(); private final AtomicInteger connectionCount = new AtomicInteger();
private final ReentrantLock lock = new ReentrantLock(); private final ReentrantLock lock = new ReentrantLock();
private final Destination destination; private final Destination destination;
private final int maxConnections; private final int maxConnections;
private final Callback requester; private final Callback requester;
private final BlockingDeque<Connection> idleConnections; private final Deque<Connection> idleConnections;
private final BlockingQueue<Connection> activeConnections; private final Queue<Connection> activeConnections;
public ConnectionPool(Destination destination, int maxConnections, Callback requester) public ConnectionPool(Destination destination, int maxConnections, Callback requester)
{ {
@ -81,12 +81,12 @@ public class ConnectionPool implements Closeable, Dumpable, Sweeper.Sweepable
return activeConnections.size(); return activeConnections.size();
} }
public BlockingQueue<Connection> getIdleConnections() public Queue<Connection> getIdleConnections()
{ {
return idleConnections; return idleConnections;
} }
public BlockingQueue<Connection> getActiveConnections() public Queue<Connection> getActiveConnections()
{ {
return activeConnections; return activeConnections;
} }
@ -129,7 +129,7 @@ public class ConnectionPool implements Closeable, Dumpable, Sweeper.Sweepable
idleCreated(connection); idleCreated(connection);
requester.succeeded(); proceed();
} }
@Override @Override
@ -150,11 +150,15 @@ public class ConnectionPool implements Closeable, Dumpable, Sweeper.Sweepable
} }
} }
protected void proceed()
{
requester.succeeded();
}
protected void idleCreated(Connection connection) protected void idleCreated(Connection connection)
{ {
boolean idle; boolean idle;
final ReentrantLock lock = this.lock; lock();
lock.lock();
try try
{ {
// Use "cold" new connections as last. // Use "cold" new connections as last.
@ -162,7 +166,7 @@ public class ConnectionPool implements Closeable, Dumpable, Sweeper.Sweepable
} }
finally finally
{ {
lock.unlock(); unlock();
} }
idle(connection, idle); idle(connection, idle);
@ -172,8 +176,7 @@ public class ConnectionPool implements Closeable, Dumpable, Sweeper.Sweepable
{ {
boolean acquired; boolean acquired;
Connection connection; Connection connection;
final ReentrantLock lock = this.lock; lock();
lock.lock();
try try
{ {
connection = idleConnections.pollFirst(); connection = idleConnections.pollFirst();
@ -183,7 +186,7 @@ public class ConnectionPool implements Closeable, Dumpable, Sweeper.Sweepable
} }
finally finally
{ {
lock.unlock(); unlock();
} }
if (acquired) if (acquired)
@ -209,24 +212,28 @@ public class ConnectionPool implements Closeable, Dumpable, Sweeper.Sweepable
public boolean release(Connection connection) public boolean release(Connection connection)
{ {
boolean idle; boolean idle;
final ReentrantLock lock = this.lock; lock();
lock.lock();
try try
{ {
if (!activeConnections.remove(connection)) if (!activeConnections.remove(connection))
return false; return false;
// Make sure we use "hot" connections first. // Make sure we use "hot" connections first.
idle = idleConnections.offerFirst(connection); idle = offerIdle(connection);
} }
finally finally
{ {
lock.unlock(); unlock();
} }
released(connection); released(connection);
return idle(connection, idle); return idle(connection, idle);
} }
protected boolean offerIdle(Connection connection)
{
return idleConnections.offerFirst(connection);
}
protected boolean idle(Connection connection, boolean idle) protected boolean idle(Connection connection, boolean idle)
{ {
if (idle) if (idle)
@ -249,11 +256,15 @@ public class ConnectionPool implements Closeable, Dumpable, Sweeper.Sweepable
} }
public boolean remove(Connection connection) public boolean remove(Connection connection)
{
return remove(connection, false);
}
protected boolean remove(Connection connection, boolean force)
{ {
boolean activeRemoved; boolean activeRemoved;
boolean idleRemoved; boolean idleRemoved;
final ReentrantLock lock = this.lock; lock();
lock.lock();
try try
{ {
activeRemoved = activeConnections.remove(connection); activeRemoved = activeConnections.remove(connection);
@ -261,12 +272,12 @@ public class ConnectionPool implements Closeable, Dumpable, Sweeper.Sweepable
} }
finally finally
{ {
lock.unlock(); unlock();
} }
if (activeRemoved) if (activeRemoved)
released(connection); released(connection);
boolean removed = activeRemoved || idleRemoved; boolean removed = activeRemoved || idleRemoved || force;
if (removed) if (removed)
{ {
int pooled = connectionCount.decrementAndGet(); int pooled = connectionCount.decrementAndGet();
@ -278,29 +289,27 @@ public class ConnectionPool implements Closeable, Dumpable, Sweeper.Sweepable
public boolean isActive(Connection connection) public boolean isActive(Connection connection)
{ {
final ReentrantLock lock = this.lock; lock();
lock.lock();
try try
{ {
return activeConnections.contains(connection); return activeConnections.contains(connection);
} }
finally finally
{ {
lock.unlock(); unlock();
} }
} }
public boolean isIdle(Connection connection) public boolean isIdle(Connection connection)
{ {
final ReentrantLock lock = this.lock; lock();
lock.lock();
try try
{ {
return idleConnections.contains(connection); return idleConnections.contains(connection);
} }
finally finally
{ {
lock.unlock(); unlock();
} }
} }
@ -313,8 +322,7 @@ public class ConnectionPool implements Closeable, Dumpable, Sweeper.Sweepable
{ {
List<Connection> idles = new ArrayList<>(); List<Connection> idles = new ArrayList<>();
List<Connection> actives = new ArrayList<>(); List<Connection> actives = new ArrayList<>();
final ReentrantLock lock = this.lock; lock();
lock.lock();
try try
{ {
idles.addAll(idleConnections); idles.addAll(idleConnections);
@ -324,7 +332,7 @@ public class ConnectionPool implements Closeable, Dumpable, Sweeper.Sweepable
} }
finally finally
{ {
lock.unlock(); unlock();
} }
connectionCount.set(0); connectionCount.set(0);
@ -348,8 +356,7 @@ public class ConnectionPool implements Closeable, Dumpable, Sweeper.Sweepable
{ {
List<Connection> actives = new ArrayList<>(); List<Connection> actives = new ArrayList<>();
List<Connection> idles = new ArrayList<>(); List<Connection> idles = new ArrayList<>();
final ReentrantLock lock = this.lock; lock();
lock.lock();
try try
{ {
actives.addAll(activeConnections); actives.addAll(activeConnections);
@ -357,7 +364,7 @@ public class ConnectionPool implements Closeable, Dumpable, Sweeper.Sweepable
} }
finally finally
{ {
lock.unlock(); unlock();
} }
ContainerLifeCycle.dumpObject(out, this); ContainerLifeCycle.dumpObject(out, this);
@ -368,8 +375,7 @@ public class ConnectionPool implements Closeable, Dumpable, Sweeper.Sweepable
public boolean sweep() public boolean sweep()
{ {
List<Sweeper.Sweepable> toSweep = new ArrayList<>(); List<Sweeper.Sweepable> toSweep = new ArrayList<>();
final ReentrantLock lock = this.lock; lock();
lock.lock();
try try
{ {
for (Connection connection : getActiveConnections()) for (Connection connection : getActiveConnections())
@ -380,7 +386,7 @@ public class ConnectionPool implements Closeable, Dumpable, Sweeper.Sweepable
} }
finally finally
{ {
lock.unlock(); unlock();
} }
for (Sweeper.Sweepable candidate : toSweep) for (Sweeper.Sweepable candidate : toSweep)
@ -400,13 +406,22 @@ public class ConnectionPool implements Closeable, Dumpable, Sweeper.Sweepable
return false; return false;
} }
protected void lock()
{
lock.lock();
}
protected void unlock()
{
lock.unlock();
}
@Override @Override
public String toString() public String toString()
{ {
int activeSize; int activeSize;
int idleSize; int idleSize;
final ReentrantLock lock = this.lock; lock();
lock.lock();
try try
{ {
activeSize = activeConnections.size(); activeSize = activeConnections.size();
@ -414,7 +429,7 @@ public class ConnectionPool implements Closeable, Dumpable, Sweeper.Sweepable
} }
finally finally
{ {
lock.unlock(); unlock();
} }
return String.format("%s[c=%d/%d,a=%d,i=%d]", return String.format("%s[c=%d/%d,a=%d,i=%d]",

View File

@ -22,9 +22,13 @@ import org.eclipse.jetty.client.api.Connection;
import org.eclipse.jetty.client.api.Destination; import org.eclipse.jetty.client.api.Destination;
import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.LeakDetector; 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 ConnectionPool
{ {
private static final Logger LOG = Log.getLogger(LeakTrackingConnectionPool.class);
private final LeakDetector<Connection> leakDetector = new LeakDetector<Connection>() private final LeakDetector<Connection> leakDetector = new LeakDetector<Connection>()
{ {
@Override @Override

View File

@ -71,9 +71,7 @@ public abstract class PoolingHttpDestination<C extends Connection> extends HttpD
{ {
if (getHttpExchanges().isEmpty()) if (getHttpExchanges().isEmpty())
return; return;
C connection = acquire(); process();
if (connection != null)
process(connection);
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@ -82,6 +80,13 @@ public abstract class PoolingHttpDestination<C extends Connection> extends HttpD
return (C)connectionPool.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>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 * <p>A new connection is created when a request needs to be executed; it is possible that the request that
@ -137,18 +142,21 @@ public abstract class PoolingHttpDestination<C extends Connection> extends HttpD
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
C connection = (C)c; C connection = (C)c;
if (LOG.isDebugEnabled()) if (LOG.isDebugEnabled())
LOG.debug("{} released", connection); LOG.debug("Released {}", connection);
HttpClient client = getHttpClient(); HttpClient client = getHttpClient();
if (client.isRunning()) if (client.isRunning())
{ {
if (connectionPool.isActive(connection)) if (connectionPool.isActive(connection))
{ {
process(connection); if (connectionPool.release(connection))
send();
else
connection.close();
} }
else else
{ {
if (LOG.isDebugEnabled()) if (LOG.isDebugEnabled())
LOG.debug("{} explicit", connection); LOG.debug("Released explicit {}", connection);
} }
} }
else else
@ -184,9 +192,7 @@ public abstract class PoolingHttpDestination<C extends Connection> extends HttpD
// We need to execute queued requests even if this connection failed. // We need to execute queued requests even if this connection failed.
// We may create a connection that is not needed, but it will eventually // We may create a connection that is not needed, but it will eventually
// idle timeout, so no worries. // idle timeout, so no worries.
C newConnection = acquire(); process();
if (newConnection != null)
process(newConnection);
} }
} }

View File

@ -0,0 +1,209 @@
//
// ========================================================================
// 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.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import org.eclipse.jetty.client.api.Connection;
import org.eclipse.jetty.client.api.Destination;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.annotation.ManagedAttribute;
import org.eclipse.jetty.util.component.ContainerLifeCycle;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.thread.Scheduler;
/**
* <p>A {@link ConnectionPool} that validates connections before
* making them available for use.</p>
* <p>Connections that have just been opened are not validated.
* Connections that are {@link #release(Connection) released} will
* be validated.</p>
* <p>Validation by reading from the EndPoint is not reliable,
* since the TCP FIN may arrive just after the validation read.</p>
* <p>This class validates connections by putting them in a
* "quarantine" for a configurable timeout, where they cannot
* be used to send requests. When the timeout expires, the
* quarantined connection is made idle and therefore available
* to send requests.</p>
* <p>The existing HttpClient mechanism to detect server closes
* will trigger and close quarantined connections, before they
* are made idle (and reusable) again.</p>
* <p>There still is a small chance that the timeout expires,
* the connection is made idle and available again, it is used
* to send a request exactly when the server decides to close.
* This case is however unavoidable and may be mitigated by
* tuning the idle timeout of the servers to be larger than
* that of the client.</p>
*/
public class ValidatingConnectionPool extends ConnectionPool
{
private static final Logger LOG = Log.getLogger(ValidatingConnectionPool.class);
private final Scheduler scheduler;
private final long timeout;
private final Map<Connection, Holder> quarantine;
public ValidatingConnectionPool(Destination destination, int maxConnections, Callback requester, Scheduler scheduler, long timeout)
{
super(destination, maxConnections, requester);
this.scheduler = scheduler;
this.timeout = timeout;
this.quarantine = new HashMap<>(maxConnections);
}
@ManagedAttribute(value = "The number of validating connections", readonly = true)
public int getValidatingConnectionCount()
{
return quarantine.size();
}
@Override
public boolean release(Connection connection)
{
lock();
try
{
if (!getActiveConnections().remove(connection))
return false;
Holder holder = new Holder(connection);
holder.task = scheduler.schedule(holder, timeout, TimeUnit.MILLISECONDS);
quarantine.put(connection, holder);
if (LOG.isDebugEnabled())
LOG.debug("Validating for {}ms {}", timeout, connection);
}
finally
{
unlock();
}
released(connection);
return true;
}
@Override
public boolean remove(Connection connection)
{
Holder holder;
lock();
try
{
holder = quarantine.remove(connection);
}
finally
{
unlock();
}
if (holder == null)
return super.remove(connection);
if (LOG.isDebugEnabled())
LOG.debug("Removed while validating {}", connection);
boolean cancelled = holder.cancel();
if (cancelled)
return remove(connection, true);
return super.remove(connection);
}
@Override
public void dump(Appendable out, String indent) throws IOException
{
super.dump(out, indent);
ContainerLifeCycle.dump(out, indent, quarantine.values());
}
@Override
public String toString()
{
int size;
lock();
try
{
size = quarantine.size();
}
finally
{
unlock();
}
return String.format("%s[v=%d]", super.toString(), size);
}
private class Holder implements Runnable
{
private final long timestamp = System.nanoTime();
private final AtomicBoolean latch = new AtomicBoolean();
private final Connection connection;
public Scheduler.Task task;
public Holder(Connection connection)
{
this.connection = connection;
}
@Override
public void run()
{
if (latch.compareAndSet(false, true))
{
boolean idle;
lock();
try
{
quarantine.remove(connection);
idle = offerIdle(connection);
if (LOG.isDebugEnabled())
LOG.debug("Validated {}", connection);
}
finally
{
unlock();
}
if (idle(connection, idle))
proceed();
}
}
public boolean cancel()
{
if (latch.compareAndSet(false, true))
{
task.cancel();
return true;
}
return false;
}
@Override
public String toString()
{
return String.format("%s[validationLeft=%dms]",
connection,
timeout - TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - timestamp)
);
}
}
}

View File

@ -98,7 +98,7 @@ public class HttpConnectionOverHTTP extends AbstractConnection implements Connec
protected boolean onReadTimeout() protected boolean onReadTimeout()
{ {
if (LOG.isDebugEnabled()) if (LOG.isDebugEnabled())
LOG.debug("{} idle timeout", this); LOG.debug("Idle timeout {}", this);
close(new TimeoutException()); close(new TimeoutException());
return false; return false;
} }
@ -141,10 +141,10 @@ public class HttpConnectionOverHTTP extends AbstractConnection implements Connec
getHttpDestination().close(this); getHttpDestination().close(this);
getEndPoint().shutdownOutput(); getEndPoint().shutdownOutput();
if (LOG.isDebugEnabled()) if (LOG.isDebugEnabled())
LOG.debug("{} oshut", this); LOG.debug("Shutdown {}", this);
getEndPoint().close(); getEndPoint().close();
if (LOG.isDebugEnabled()) if (LOG.isDebugEnabled())
LOG.debug("{} closed", this); LOG.debug("Closed {}", this);
abort(failure); abort(failure);
} }
@ -166,10 +166,8 @@ public class HttpConnectionOverHTTP extends AbstractConnection implements Connec
{ {
if (!closed.get()) if (!closed.get())
return false; return false;
if (sweeps.incrementAndGet() < 4) if (sweeps.incrementAndGet() < 4)
return false; return false;
return true; return true;
} }

View File

@ -21,6 +21,7 @@ package org.eclipse.jetty.client;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import org.eclipse.jetty.client.http.HttpClientTransportOverHTTP;
import org.eclipse.jetty.http.HttpScheme; import org.eclipse.jetty.http.HttpScheme;
import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.NetworkConnector; import org.eclipse.jetty.server.NetworkConnector;
@ -84,10 +85,15 @@ public abstract class AbstractHttpClientServerTest
} }
protected void startClient() throws Exception protected void startClient() throws Exception
{
startClient(new HttpClientTransportOverHTTP(1));
}
protected void startClient(HttpClientTransport transport) throws Exception
{ {
QueuedThreadPool clientThreads = new QueuedThreadPool(); QueuedThreadPool clientThreads = new QueuedThreadPool();
clientThreads.setName("client"); clientThreads.setName("client");
client = new HttpClient(sslContextFactory); client = new HttpClient(transport, sslContextFactory);
client.setExecutor(clientThreads); client.setExecutor(clientThreads);
client.start(); client.start();
} }

View File

@ -881,9 +881,16 @@ public class HttpClientTest extends AbstractHttpClientServerTest
@Test @Test
public void testConnectHostWithMultipleAddresses() throws Exception public void testConnectHostWithMultipleAddresses() throws Exception
{ {
// Likely that the DNS for google.com returns multiple addresses.
String host = "google.com"; String host = "google.com";
Assume.assumeTrue(InetAddress.getAllByName(host).length > 1); try
{
// Likely that the DNS for google.com returns multiple addresses.
Assume.assumeTrue(InetAddress.getAllByName(host).length > 1);
}
catch (Throwable x)
{
Assume.assumeNoException(x);
}
startClient(); startClient();
client.setFollowRedirects(false); // Avoid redirects from 80 to 443. client.setFollowRedirects(false); // Avoid redirects from 80 to 443.

View File

@ -21,7 +21,7 @@ package org.eclipse.jetty.client;
import java.io.IOException; import java.io.IOException;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.Arrays; import java.util.Arrays;
import java.util.concurrent.BlockingQueue; import java.util.Queue;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@ -71,10 +71,10 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, host, port); HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, host, port);
ConnectionPool connectionPool = destination.getConnectionPool(); ConnectionPool connectionPool = destination.getConnectionPool();
final BlockingQueue<Connection> idleConnections = connectionPool.getIdleConnections(); final Queue<Connection> idleConnections = connectionPool.getIdleConnections();
Assert.assertEquals(0, idleConnections.size()); Assert.assertEquals(0, idleConnections.size());
final BlockingQueue<Connection> activeConnections = connectionPool.getActiveConnections(); final Queue<Connection> activeConnections = connectionPool.getActiveConnections();
Assert.assertEquals(0, activeConnections.size()); Assert.assertEquals(0, activeConnections.size());
final CountDownLatch headersLatch = new CountDownLatch(1); final CountDownLatch headersLatch = new CountDownLatch(1);
@ -132,10 +132,10 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, host, port); HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, host, port);
ConnectionPool connectionPool = destination.getConnectionPool(); ConnectionPool connectionPool = destination.getConnectionPool();
final BlockingQueue<Connection> idleConnections = connectionPool.getIdleConnections(); final Queue<Connection> idleConnections = connectionPool.getIdleConnections();
Assert.assertEquals(0, idleConnections.size()); Assert.assertEquals(0, idleConnections.size());
final BlockingQueue<Connection> activeConnections = connectionPool.getActiveConnections(); final Queue<Connection> activeConnections = connectionPool.getActiveConnections();
Assert.assertEquals(0, activeConnections.size()); Assert.assertEquals(0, activeConnections.size());
final CountDownLatch beginLatch = new CountDownLatch(1); final CountDownLatch beginLatch = new CountDownLatch(1);
@ -183,10 +183,10 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, host, port); HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, host, port);
ConnectionPool connectionPool = destination.getConnectionPool(); ConnectionPool connectionPool = destination.getConnectionPool();
final BlockingQueue<Connection> idleConnections = connectionPool.getIdleConnections(); final Queue<Connection> idleConnections = connectionPool.getIdleConnections();
Assert.assertEquals(0, idleConnections.size()); Assert.assertEquals(0, idleConnections.size());
final BlockingQueue<Connection> activeConnections = connectionPool.getActiveConnections(); final Queue<Connection> activeConnections = connectionPool.getActiveConnections();
Assert.assertEquals(0, activeConnections.size()); Assert.assertEquals(0, activeConnections.size());
final CountDownLatch successLatch = new CountDownLatch(3); final CountDownLatch successLatch = new CountDownLatch(3);
@ -243,10 +243,10 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, host, port); HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, host, port);
ConnectionPool connectionPool = destination.getConnectionPool(); ConnectionPool connectionPool = destination.getConnectionPool();
final BlockingQueue<Connection> idleConnections = connectionPool.getIdleConnections(); final Queue<Connection> idleConnections = connectionPool.getIdleConnections();
Assert.assertEquals(0, idleConnections.size()); Assert.assertEquals(0, idleConnections.size());
final BlockingQueue<Connection> activeConnections = connectionPool.getActiveConnections(); final Queue<Connection> activeConnections = connectionPool.getActiveConnections();
Assert.assertEquals(0, activeConnections.size()); Assert.assertEquals(0, activeConnections.size());
final long delay = 1000; final long delay = 1000;
@ -316,10 +316,10 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, host, port); HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, host, port);
ConnectionPool connectionPool = destination.getConnectionPool(); ConnectionPool connectionPool = destination.getConnectionPool();
final BlockingQueue<Connection> idleConnections = connectionPool.getIdleConnections(); final Queue<Connection> idleConnections = connectionPool.getIdleConnections();
Assert.assertEquals(0, idleConnections.size()); Assert.assertEquals(0, idleConnections.size());
final BlockingQueue<Connection> activeConnections = connectionPool.getActiveConnections(); final Queue<Connection> activeConnections = connectionPool.getActiveConnections();
Assert.assertEquals(0, activeConnections.size()); Assert.assertEquals(0, activeConnections.size());
server.stop(); server.stop();
@ -369,10 +369,10 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, host, port); HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, host, port);
ConnectionPool connectionPool = destination.getConnectionPool(); ConnectionPool connectionPool = destination.getConnectionPool();
final BlockingQueue<Connection> idleConnections = connectionPool.getIdleConnections(); final Queue<Connection> idleConnections = connectionPool.getIdleConnections();
Assert.assertEquals(0, idleConnections.size()); Assert.assertEquals(0, idleConnections.size());
final BlockingQueue<Connection> activeConnections = connectionPool.getActiveConnections(); final Queue<Connection> activeConnections = connectionPool.getActiveConnections();
Assert.assertEquals(0, activeConnections.size()); Assert.assertEquals(0, activeConnections.size());
final CountDownLatch latch = new CountDownLatch(1); final CountDownLatch latch = new CountDownLatch(1);
@ -419,10 +419,10 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, host, port); HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, host, port);
ConnectionPool connectionPool = destination.getConnectionPool(); ConnectionPool connectionPool = destination.getConnectionPool();
final BlockingQueue<Connection> idleConnections = connectionPool.getIdleConnections(); final Queue<Connection> idleConnections = connectionPool.getIdleConnections();
Assert.assertEquals(0, idleConnections.size()); Assert.assertEquals(0, idleConnections.size());
final BlockingQueue<Connection> activeConnections = connectionPool.getActiveConnections(); final Queue<Connection> activeConnections = connectionPool.getActiveConnections();
Assert.assertEquals(0, activeConnections.size()); Assert.assertEquals(0, activeConnections.size());
Log.getLogger(HttpConnection.class).info("Expecting java.lang.IllegalStateException: HttpParser{s=CLOSED,..."); Log.getLogger(HttpConnection.class).info("Expecting java.lang.IllegalStateException: HttpParser{s=CLOSED,...");
@ -449,7 +449,7 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
Assert.assertEquals(0, idleConnections.size()); Assert.assertEquals(0, idleConnections.size());
Assert.assertEquals(0, activeConnections.size()); Assert.assertEquals(0, activeConnections.size());
server.stop(); server.stop();
} }
finally finally
@ -469,10 +469,10 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, host, port); HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, host, port);
ConnectionPool connectionPool = destination.getConnectionPool(); ConnectionPool connectionPool = destination.getConnectionPool();
final BlockingQueue<Connection> idleConnections = connectionPool.getIdleConnections(); final Queue<Connection> idleConnections = connectionPool.getIdleConnections();
Assert.assertEquals(0, idleConnections.size()); Assert.assertEquals(0, idleConnections.size());
final BlockingQueue<Connection> activeConnections = connectionPool.getActiveConnections(); final Queue<Connection> activeConnections = connectionPool.getActiveConnections();
Assert.assertEquals(0, activeConnections.size()); Assert.assertEquals(0, activeConnections.size());
ContentResponse response = client.newRequest(host, port) ContentResponse response = client.newRequest(host, port)
@ -501,10 +501,10 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, host, port); HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, host, port);
ConnectionPool connectionPool = destination.getConnectionPool(); ConnectionPool connectionPool = destination.getConnectionPool();
final BlockingQueue<Connection> idleConnections = connectionPool.getIdleConnections(); final Queue<Connection> idleConnections = connectionPool.getIdleConnections();
Assert.assertEquals(0, idleConnections.size()); Assert.assertEquals(0, idleConnections.size());
final BlockingQueue<Connection> activeConnections = connectionPool.getActiveConnections(); final Queue<Connection> activeConnections = connectionPool.getActiveConnections();
Assert.assertEquals(0, activeConnections.size()); Assert.assertEquals(0, activeConnections.size());
client.setStrictEventOrdering(false); client.setStrictEventOrdering(false);

View File

@ -0,0 +1,204 @@
//
// ========================================================================
// 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.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.client.http.HttpClientTransportOverHTTP;
import org.eclipse.jetty.client.http.HttpDestinationOverHTTP;
import org.eclipse.jetty.client.util.FutureResponseListener;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpHeaderValue;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.handler.AbstractHandler;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.junit.Assert;
import org.junit.Test;
public class ValidatingConnectionPoolTest extends AbstractHttpClientServerTest
{
public ValidatingConnectionPoolTest(SslContextFactory sslContextFactory)
{
super(sslContextFactory);
}
@Override
protected void startClient() throws Exception
{
startClient(new ValidatingHttpClientTransportOverHTTP(1000));
}
@Test
public void testRequestAfterValidation() throws Exception
{
start(new EmptyServerHandler());
client.setMaxConnectionsPerDestination(1);
ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
.scheme(scheme)
.send();
Assert.assertEquals(200, response.getStatus());
// The second request should be sent after the validating timeout.
response = client.newRequest("localhost", connector.getLocalPort())
.scheme(scheme)
.send();
Assert.assertEquals(200, response.getStatus());
}
@Test
public void testServerClosesConnectionAfterRedirectWithoutConnectionCloseHeader() throws Exception
{
start(new AbstractHandler()
{
@Override
public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
{
baseRequest.setHandled(true);
if (target.endsWith("/redirect"))
{
response.setStatus(HttpStatus.TEMPORARY_REDIRECT_307);
response.setContentLength(0);
response.setHeader(HttpHeader.LOCATION.asString(), scheme + "://localhost:" + connector.getLocalPort() + "/");
response.flushBuffer();
baseRequest.getHttpChannel().getEndPoint().shutdownOutput();
}
else
{
response.setStatus(HttpStatus.OK_200);
response.setContentLength(0);
response.setHeader(HttpHeader.CONNECTION.asString(), HttpHeaderValue.CLOSE.asString());
}
}
});
ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
.scheme(scheme)
.path("/redirect")
.send();
Assert.assertEquals(200, response.getStatus());
}
@Test
public void testServerClosesConnectionAfterResponseWithQueuedRequestWithMaxConnectionsWithConnectionCloseHeader() throws Exception
{
testServerClosesConnectionAfterResponseWithQueuedRequestWithMaxConnections(new AbstractHandler()
{
@Override
public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
{
baseRequest.setHandled(true);
response.setStatus(HttpStatus.OK_200);
response.setContentLength(0);
response.setHeader(HttpHeader.CONNECTION.asString(), HttpHeaderValue.CLOSE.asString());
}
});
}
@Test
public void testServerClosesConnectionAfterResponseWithQueuedRequestWithMaxConnectionsWithoutConnectionCloseHeader() throws Exception
{
testServerClosesConnectionAfterResponseWithQueuedRequestWithMaxConnections(new AbstractHandler()
{
@Override
public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
{
baseRequest.setHandled(true);
response.setStatus(HttpStatus.OK_200);
response.setContentLength(0);
response.flushBuffer();
baseRequest.getHttpChannel().getEndPoint().shutdownOutput();
}
});
}
private void testServerClosesConnectionAfterResponseWithQueuedRequestWithMaxConnections(Handler handler) throws Exception
{
start(handler);
client.setMaxConnectionsPerDestination(1);
final CountDownLatch latch = new CountDownLatch(1);
Request request1 = client.newRequest("localhost", connector.getLocalPort())
.scheme(scheme)
.path("/one")
.onRequestBegin(r ->
{
try
{
latch.await();
}
catch (InterruptedException x)
{
r.abort(x);
}
});
FutureResponseListener listener1 = new FutureResponseListener(request1);
request1.send(listener1);
Request request2 = client.newRequest("localhost", connector.getLocalPort())
.scheme(scheme)
.path("/two");
FutureResponseListener listener2 = new FutureResponseListener(request2);
request2.send(listener2);
// Now we have one request about to be sent, and one queued.
latch.countDown();
ContentResponse response1 = listener1.get(5, TimeUnit.SECONDS);
Assert.assertEquals(200, response1.getStatus());
ContentResponse response2 = listener2.get(5, TimeUnit.SECONDS);
Assert.assertEquals(200, response2.getStatus());
}
private static class ValidatingHttpClientTransportOverHTTP extends HttpClientTransportOverHTTP
{
private final long timeout;
public ValidatingHttpClientTransportOverHTTP(long timeout)
{
super(1);
this.timeout = timeout;
}
@Override
public HttpDestination newHttpDestination(Origin origin)
{
return new HttpDestinationOverHTTP(getHttpClient(), origin)
{
@Override
protected ConnectionPool newConnectionPool(HttpClient client)
{
return new ValidatingConnectionPool(this, client.getMaxConnectionsPerDestination(), this, client.getScheduler(), timeout);
}
};
}
}
}

View File

@ -18,6 +18,7 @@
package org.eclipse.jetty.client.http; package org.eclipse.jetty.client.http;
import java.util.Queue;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@ -62,7 +63,7 @@ public class HttpDestinationOverHTTPTest extends AbstractHttpClientServerTest
if (connection == null) if (connection == null)
{ {
// There are no queued requests, so the newly created connection will be idle // There are no queued requests, so the newly created connection will be idle
connection = destination.getConnectionPool().getIdleConnections().poll(5, TimeUnit.SECONDS); connection = timedPoll(destination.getConnectionPool().getIdleConnections(), 5, TimeUnit.SECONDS);
} }
Assert.assertNotNull(connection); Assert.assertNotNull(connection);
} }
@ -133,9 +134,10 @@ public class HttpDestinationOverHTTPTest extends AbstractHttpClientServerTest
latch.countDown(); latch.countDown();
// There must be 2 idle connections. // There must be 2 idle connections.
Connection connection = destination.getConnectionPool().getIdleConnections().poll(5, TimeUnit.SECONDS); Queue<Connection> idleConnections = destination.getConnectionPool().getIdleConnections();
Connection connection = timedPoll(idleConnections, 5, TimeUnit.SECONDS);
Assert.assertNotNull(connection); Assert.assertNotNull(connection);
connection = destination.getConnectionPool().getIdleConnections().poll(5, TimeUnit.SECONDS); connection = timedPoll(idleConnections, 5, TimeUnit.SECONDS);
Assert.assertNotNull(connection); Assert.assertNotNull(connection);
} }
@ -272,4 +274,17 @@ public class HttpDestinationOverHTTPTest extends AbstractHttpClientServerTest
destinationAfter = client.getDestination(scheme, host, port); destinationAfter = client.getDestination(scheme, host, port);
Assert.assertNotSame(destinationBefore, destinationAfter); Assert.assertNotSame(destinationBefore, destinationAfter);
} }
private Connection timedPoll(Queue<Connection> connections, long time, TimeUnit unit) throws InterruptedException
{
long start = System.nanoTime();
while (unit.toNanos(time) > System.nanoTime() - start)
{
Connection connection = connections.poll();
if (connection != null)
return connection;
TimeUnit.MILLISECONDS.sleep(5);
}
return connections.poll();
}
} }

View File

@ -2,7 +2,7 @@
<parent> <parent>
<groupId>org.eclipse.jetty</groupId> <groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId> <artifactId>jetty-project</artifactId>
<version>9.3.3-SNAPSHOT</version> <version>9.3.4-SNAPSHOT</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>jetty-continuation</artifactId> <artifactId>jetty-continuation</artifactId>

View File

@ -2,7 +2,7 @@
<parent> <parent>
<groupId>org.eclipse.jetty</groupId> <groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId> <artifactId>jetty-project</artifactId>
<version>9.3.3-SNAPSHOT</version> <version>9.3.4-SNAPSHOT</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>jetty-deploy</artifactId> <artifactId>jetty-deploy</artifactId>

View File

@ -3,7 +3,7 @@
<parent> <parent>
<groupId>org.eclipse.jetty</groupId> <groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId> <artifactId>jetty-project</artifactId>
<version>9.3.3-SNAPSHOT</version> <version>9.3.4-SNAPSHOT</version>
</parent> </parent>
<artifactId>jetty-distribution</artifactId> <artifactId>jetty-distribution</artifactId>
<name>Jetty :: Distribution Assemblies</name> <name>Jetty :: Distribution Assemblies</name>

View File

@ -3,7 +3,7 @@
<parent> <parent>
<groupId>org.eclipse.jetty.fcgi</groupId> <groupId>org.eclipse.jetty.fcgi</groupId>
<artifactId>fcgi-parent</artifactId> <artifactId>fcgi-parent</artifactId>
<version>9.3.3-SNAPSHOT</version> <version>9.3.4-SNAPSHOT</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@ -3,7 +3,7 @@
<parent> <parent>
<groupId>org.eclipse.jetty.fcgi</groupId> <groupId>org.eclipse.jetty.fcgi</groupId>
<artifactId>fcgi-parent</artifactId> <artifactId>fcgi-parent</artifactId>
<version>9.3.3-SNAPSHOT</version> <version>9.3.4-SNAPSHOT</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@ -3,7 +3,7 @@
<parent> <parent>
<groupId>org.eclipse.jetty</groupId> <groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId> <artifactId>jetty-project</artifactId>
<version>9.3.3-SNAPSHOT</version> <version>9.3.4-SNAPSHOT</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@ -2,7 +2,7 @@
<parent> <parent>
<groupId>org.eclipse.jetty</groupId> <groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId> <artifactId>jetty-project</artifactId>
<version>9.3.3-SNAPSHOT</version> <version>9.3.4-SNAPSHOT</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>jetty-http-spi</artifactId> <artifactId>jetty-http-spi</artifactId>

View File

@ -3,7 +3,7 @@
<parent> <parent>
<artifactId>jetty-project</artifactId> <artifactId>jetty-project</artifactId>
<groupId>org.eclipse.jetty</groupId> <groupId>org.eclipse.jetty</groupId>
<version>9.3.3-SNAPSHOT</version> <version>9.3.4-SNAPSHOT</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>jetty-http</artifactId> <artifactId>jetty-http</artifactId>

View File

@ -86,6 +86,19 @@ public class HttpParserTest
assertEquals(HttpMethod.GET,HttpMethod.lookAheadGet(b)); assertEquals(HttpMethod.GET,HttpMethod.lookAheadGet(b));
} }
@Test
public void testLineParse_Mock_IP() throws Exception
{
ByteBuffer buffer= BufferUtil.toBuffer("POST /mock/127.0.0.1 HTTP/1.1\015\012" + "\015\012");
HttpParser.RequestHandler handler = new Handler();
HttpParser parser= new HttpParser(handler);
parseAll(parser,buffer);
assertEquals("POST", _methodOrVersion);
assertEquals("/mock/127.0.0.1", _uriOrStatus);
assertEquals("HTTP/1.1", _versionOrReason);
assertEquals(-1, _headers);
}
@Test @Test
public void testLineParse0() throws Exception public void testLineParse0() throws Exception

View File

@ -3,7 +3,7 @@
<parent> <parent>
<groupId>org.eclipse.jetty.http2</groupId> <groupId>org.eclipse.jetty.http2</groupId>
<artifactId>http2-parent</artifactId> <artifactId>http2-parent</artifactId>
<version>9.3.3-SNAPSHOT</version> <version>9.3.4-SNAPSHOT</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@ -3,7 +3,7 @@
<parent> <parent>
<groupId>org.eclipse.jetty.http2</groupId> <groupId>org.eclipse.jetty.http2</groupId>
<artifactId>http2-parent</artifactId> <artifactId>http2-parent</artifactId>
<version>9.3.3-SNAPSHOT</version> <version>9.3.4-SNAPSHOT</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@ -3,7 +3,7 @@
<parent> <parent>
<groupId>org.eclipse.jetty.http2</groupId> <groupId>org.eclipse.jetty.http2</groupId>
<artifactId>http2-parent</artifactId> <artifactId>http2-parent</artifactId>
<version>9.3.3-SNAPSHOT</version> <version>9.3.4-SNAPSHOT</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@ -3,7 +3,7 @@
<parent> <parent>
<groupId>org.eclipse.jetty.http2</groupId> <groupId>org.eclipse.jetty.http2</groupId>
<artifactId>http2-parent</artifactId> <artifactId>http2-parent</artifactId>
<version>9.3.3-SNAPSHOT</version> <version>9.3.4-SNAPSHOT</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@ -3,7 +3,7 @@
<parent> <parent>
<groupId>org.eclipse.jetty.http2</groupId> <groupId>org.eclipse.jetty.http2</groupId>
<artifactId>http2-parent</artifactId> <artifactId>http2-parent</artifactId>
<version>9.3.3-SNAPSHOT</version> <version>9.3.4-SNAPSHOT</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@ -3,7 +3,7 @@
<parent> <parent>
<groupId>org.eclipse.jetty.http2</groupId> <groupId>org.eclipse.jetty.http2</groupId>
<artifactId>http2-parent</artifactId> <artifactId>http2-parent</artifactId>
<version>9.3.3-SNAPSHOT</version> <version>9.3.4-SNAPSHOT</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@ -3,7 +3,7 @@
<parent> <parent>
<artifactId>jetty-project</artifactId> <artifactId>jetty-project</artifactId>
<groupId>org.eclipse.jetty</groupId> <groupId>org.eclipse.jetty</groupId>
<version>9.3.3-SNAPSHOT</version> <version>9.3.4-SNAPSHOT</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@ -2,7 +2,7 @@
<parent> <parent>
<groupId>org.eclipse.jetty</groupId> <groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId> <artifactId>jetty-project</artifactId>
<version>9.3.3-SNAPSHOT</version> <version>9.3.4-SNAPSHOT</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>jetty-infinispan</artifactId> <artifactId>jetty-infinispan</artifactId>

View File

@ -2,7 +2,7 @@
<parent> <parent>
<artifactId>jetty-project</artifactId> <artifactId>jetty-project</artifactId>
<groupId>org.eclipse.jetty</groupId> <groupId>org.eclipse.jetty</groupId>
<version>9.3.3-SNAPSHOT</version> <version>9.3.4-SNAPSHOT</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>jetty-io</artifactId> <artifactId>jetty-io</artifactId>

View File

@ -2,7 +2,7 @@
<parent> <parent>
<groupId>org.eclipse.jetty</groupId> <groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId> <artifactId>jetty-project</artifactId>
<version>9.3.3-SNAPSHOT</version> <version>9.3.4-SNAPSHOT</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>jetty-jaas</artifactId> <artifactId>jetty-jaas</artifactId>

View File

@ -2,7 +2,7 @@
<parent> <parent>
<groupId>org.eclipse.jetty</groupId> <groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId> <artifactId>jetty-project</artifactId>
<version>9.3.3-SNAPSHOT</version> <version>9.3.4-SNAPSHOT</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>jetty-jaspi</artifactId> <artifactId>jetty-jaspi</artifactId>

View File

@ -2,7 +2,7 @@
<parent> <parent>
<groupId>org.eclipse.jetty</groupId> <groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId> <artifactId>jetty-project</artifactId>
<version>9.3.3-SNAPSHOT</version> <version>9.3.4-SNAPSHOT</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>jetty-jmx</artifactId> <artifactId>jetty-jmx</artifactId>

View File

@ -2,7 +2,7 @@
<parent> <parent>
<groupId>org.eclipse.jetty</groupId> <groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId> <artifactId>jetty-project</artifactId>
<version>9.3.3-SNAPSHOT</version> <version>9.3.4-SNAPSHOT</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>jetty-jndi</artifactId> <artifactId>jetty-jndi</artifactId>

View File

@ -2,7 +2,7 @@
<parent> <parent>
<groupId>org.eclipse.jetty</groupId> <groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId> <artifactId>jetty-project</artifactId>
<version>9.3.3-SNAPSHOT</version> <version>9.3.4-SNAPSHOT</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>jetty-jspc-maven-plugin</artifactId> <artifactId>jetty-jspc-maven-plugin</artifactId>

View File

@ -2,7 +2,7 @@
<parent> <parent>
<groupId>org.eclipse.jetty</groupId> <groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId> <artifactId>jetty-project</artifactId>
<version>9.3.3-SNAPSHOT</version> <version>9.3.4-SNAPSHOT</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>jetty-maven-plugin</artifactId> <artifactId>jetty-maven-plugin</artifactId>

View File

@ -19,7 +19,7 @@
<parent> <parent>
<groupId>org.eclipse.jetty</groupId> <groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId> <artifactId>jetty-project</artifactId>
<version>9.3.3-SNAPSHOT</version> <version>9.3.4-SNAPSHOT</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>jetty-monitor</artifactId> <artifactId>jetty-monitor</artifactId>

View File

@ -2,7 +2,7 @@
<parent> <parent>
<groupId>org.eclipse.jetty</groupId> <groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId> <artifactId>jetty-project</artifactId>
<version>9.3.3-SNAPSHOT</version> <version>9.3.4-SNAPSHOT</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>jetty-nosql</artifactId> <artifactId>jetty-nosql</artifactId>

View File

@ -2,7 +2,7 @@
<parent> <parent>
<groupId>org.eclipse.jetty.osgi</groupId> <groupId>org.eclipse.jetty.osgi</groupId>
<artifactId>jetty-osgi-project</artifactId> <artifactId>jetty-osgi-project</artifactId>
<version>9.3.3-SNAPSHOT</version> <version>9.3.4-SNAPSHOT</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>jetty-osgi-alpn</artifactId> <artifactId>jetty-osgi-alpn</artifactId>

View File

@ -2,7 +2,7 @@
<parent> <parent>
<groupId>org.eclipse.jetty.osgi</groupId> <groupId>org.eclipse.jetty.osgi</groupId>
<artifactId>jetty-osgi-project</artifactId> <artifactId>jetty-osgi-project</artifactId>
<version>9.3.3-SNAPSHOT</version> <version>9.3.4-SNAPSHOT</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>jetty-osgi-boot-jsp</artifactId> <artifactId>jetty-osgi-boot-jsp</artifactId>

View File

@ -2,7 +2,7 @@
<parent> <parent>
<groupId>org.eclipse.jetty.osgi</groupId> <groupId>org.eclipse.jetty.osgi</groupId>
<artifactId>jetty-osgi-project</artifactId> <artifactId>jetty-osgi-project</artifactId>
<version>9.3.3-SNAPSHOT</version> <version>9.3.4-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@ -2,7 +2,7 @@
<parent> <parent>
<groupId>org.eclipse.jetty.osgi</groupId> <groupId>org.eclipse.jetty.osgi</groupId>
<artifactId>jetty-osgi-project</artifactId> <artifactId>jetty-osgi-project</artifactId>
<version>9.3.3-SNAPSHOT</version> <version>9.3.4-SNAPSHOT</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>jetty-osgi-boot</artifactId> <artifactId>jetty-osgi-boot</artifactId>

View File

@ -2,7 +2,7 @@
<parent> <parent>
<groupId>org.eclipse.jetty.osgi</groupId> <groupId>org.eclipse.jetty.osgi</groupId>
<artifactId>jetty-osgi-project</artifactId> <artifactId>jetty-osgi-project</artifactId>
<version>9.3.3-SNAPSHOT</version> <version>9.3.4-SNAPSHOT</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>jetty-httpservice</artifactId> <artifactId>jetty-httpservice</artifactId>

View File

@ -3,7 +3,7 @@
<parent> <parent>
<groupId>org.eclipse.jetty</groupId> <groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId> <artifactId>jetty-project</artifactId>
<version>9.3.3-SNAPSHOT</version> <version>9.3.4-SNAPSHOT</version>
</parent> </parent>
<groupId>org.eclipse.jetty.osgi</groupId> <groupId>org.eclipse.jetty.osgi</groupId>
<artifactId>jetty-osgi-project</artifactId> <artifactId>jetty-osgi-project</artifactId>

View File

@ -2,7 +2,7 @@
<parent> <parent>
<groupId>org.eclipse.jetty.osgi</groupId> <groupId>org.eclipse.jetty.osgi</groupId>
<artifactId>jetty-osgi-project</artifactId> <artifactId>jetty-osgi-project</artifactId>
<version>9.3.3-SNAPSHOT</version> <version>9.3.4-SNAPSHOT</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>test-jetty-osgi-context</artifactId> <artifactId>test-jetty-osgi-context</artifactId>

View File

@ -2,7 +2,7 @@
<parent> <parent>
<groupId>org.eclipse.jetty.osgi</groupId> <groupId>org.eclipse.jetty.osgi</groupId>
<artifactId>jetty-osgi-project</artifactId> <artifactId>jetty-osgi-project</artifactId>
<version>9.3.3-SNAPSHOT</version> <version>9.3.4-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@ -2,7 +2,7 @@
<parent> <parent>
<groupId>org.eclipse.jetty.osgi</groupId> <groupId>org.eclipse.jetty.osgi</groupId>
<artifactId>jetty-osgi-project</artifactId> <artifactId>jetty-osgi-project</artifactId>
<version>9.3.3-SNAPSHOT</version> <version>9.3.4-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@ -2,7 +2,7 @@
<parent> <parent>
<groupId>org.eclipse.jetty</groupId> <groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId> <artifactId>jetty-project</artifactId>
<version>9.3.3-SNAPSHOT</version> <version>9.3.4-SNAPSHOT</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>jetty-plus</artifactId> <artifactId>jetty-plus</artifactId>

View File

@ -2,7 +2,7 @@
<parent> <parent>
<groupId>org.eclipse.jetty</groupId> <groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId> <artifactId>jetty-project</artifactId>
<version>9.3.3-SNAPSHOT</version> <version>9.3.4-SNAPSHOT</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>jetty-proxy</artifactId> <artifactId>jetty-proxy</artifactId>

View File

@ -2,7 +2,7 @@
<parent> <parent>
<groupId>org.eclipse.jetty</groupId> <groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId> <artifactId>jetty-project</artifactId>
<version>9.3.3-SNAPSHOT</version> <version>9.3.4-SNAPSHOT</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>org.eclipse.jetty</groupId> <groupId>org.eclipse.jetty</groupId>

View File

@ -2,7 +2,7 @@
<parent> <parent>
<groupId>org.eclipse.jetty</groupId> <groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId> <artifactId>jetty-project</artifactId>
<version>9.3.3-SNAPSHOT</version> <version>9.3.4-SNAPSHOT</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>jetty-rewrite</artifactId> <artifactId>jetty-rewrite</artifactId>

View File

@ -2,7 +2,7 @@
<parent> <parent>
<groupId>org.eclipse.jetty</groupId> <groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId> <artifactId>jetty-project</artifactId>
<version>9.3.3-SNAPSHOT</version> <version>9.3.4-SNAPSHOT</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>jetty-runner</artifactId> <artifactId>jetty-runner</artifactId>

View File

@ -2,7 +2,7 @@
<parent> <parent>
<groupId>org.eclipse.jetty</groupId> <groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId> <artifactId>jetty-project</artifactId>
<version>9.3.3-SNAPSHOT</version> <version>9.3.4-SNAPSHOT</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>jetty-security</artifactId> <artifactId>jetty-security</artifactId>

View File

@ -2,7 +2,7 @@
<parent> <parent>
<groupId>org.eclipse.jetty</groupId> <groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId> <artifactId>jetty-project</artifactId>
<version>9.3.3-SNAPSHOT</version> <version>9.3.4-SNAPSHOT</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>jetty-server</artifactId> <artifactId>jetty-server</artifactId>

View File

@ -19,7 +19,6 @@
package org.eclipse.jetty.server; package org.eclipse.jetty.server;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
import java.util.List;
import javax.net.ssl.SSLContext; import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLEngine;
@ -79,23 +78,26 @@ public class SecureRequestCustomizer implements HttpConfiguration.Customizer
} }
/** /**
* <p>Customizes the request attributes to be set for SSL requests.</p> * <p>
* <p>The requirements of the Servlet specs are:</p> * Customizes the request attributes to be set for SSL requests.
* </p>
* <p>
* The requirements of the Servlet specs are:
* </p>
* <ul> * <ul>
* <li> an attribute named "javax.servlet.request.ssl_session_id" of type * <li>an attribute named "javax.servlet.request.ssl_session_id" of type String (since Servlet Spec 3.0).</li>
* String (since Servlet Spec 3.0).</li> * <li>an attribute named "javax.servlet.request.cipher_suite" of type String.</li>
* <li> an attribute named "javax.servlet.request.cipher_suite" of type * <li>an attribute named "javax.servlet.request.key_size" of type Integer.</li>
* String.</li> * <li>an attribute named "javax.servlet.request.X509Certificate" of type java.security.cert.X509Certificate[]. This
* <li> an attribute named "javax.servlet.request.key_size" of type Integer.</li> * is an array of objects of type X509Certificate, the order of this array is defined as being in ascending order of
* <li> an attribute named "javax.servlet.request.X509Certificate" of type * trust. The first certificate in the chain is the one set by the client, the next is the one used to authenticate
* java.security.cert.X509Certificate[]. This is an array of objects of type * the first, and so on.</li>
* X509Certificate, the order of this array is defined as being in ascending
* order of trust. The first certificate in the chain is the one set by the
* client, the next is the one used to authenticate the first, and so on.
* </li>
* </ul> * </ul>
* *
* @param request HttpRequest to be customized. * @param sslEngine
* the sslEngine to be customized.
* @param request
* HttpRequest to be customized.
*/ */
public void customize(SSLEngine sslEngine, Request request) public void customize(SSLEngine sslEngine, Request request)
{ {
@ -105,16 +107,14 @@ public class SecureRequestCustomizer implements HttpConfiguration.Customizer
if (_sniHostCheck) if (_sniHostCheck)
{ {
String name = request.getServerName(); String name = request.getServerName();
@SuppressWarnings("unchecked")
X509 x509 = (X509)sslSession.getValue(SniX509ExtendedKeyManager.SNI_X509); X509 x509 = (X509)sslSession.getValue(SniX509ExtendedKeyManager.SNI_X509);
if (x509!=null && !x509.matches(name)) if (x509!=null && !x509.matches(name))
{ {
LOG.warn("Host {} does not match SNI {}",name,x509); LOG.warn("Host {} does not match SNI {}",name,x509);
throw new BadMessageException(400,"Host does not match SNI"); throw new BadMessageException(400,"Host does not match SNI");
} }
if (LOG.isDebugEnabled()) if (LOG.isDebugEnabled())
LOG.debug("Host {} matched SNI {}",name,x509); LOG.debug("Host {} matched SNI {}",name,x509);
} }

View File

@ -3,7 +3,7 @@
<parent> <parent>
<artifactId>jetty-project</artifactId> <artifactId>jetty-project</artifactId>
<groupId>org.eclipse.jetty</groupId> <groupId>org.eclipse.jetty</groupId>
<version>9.3.3-SNAPSHOT</version> <version>9.3.4-SNAPSHOT</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>jetty-servlet</artifactId> <artifactId>jetty-servlet</artifactId>

View File

@ -3,7 +3,7 @@
<parent> <parent>
<artifactId>jetty-project</artifactId> <artifactId>jetty-project</artifactId>
<groupId>org.eclipse.jetty</groupId> <groupId>org.eclipse.jetty</groupId>
<version>9.3.3-SNAPSHOT</version> <version>9.3.4-SNAPSHOT</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>jetty-servlets</artifactId> <artifactId>jetty-servlets</artifactId>

View File

@ -2,7 +2,7 @@
<parent> <parent>
<groupId>org.eclipse.jetty</groupId> <groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId> <artifactId>jetty-project</artifactId>
<version>9.3.3-SNAPSHOT</version> <version>9.3.4-SNAPSHOT</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>jetty-spring</artifactId> <artifactId>jetty-spring</artifactId>

View File

@ -2,7 +2,7 @@
<parent> <parent>
<groupId>org.eclipse.jetty</groupId> <groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId> <artifactId>jetty-project</artifactId>
<version>9.3.3-SNAPSHOT</version> <version>9.3.4-SNAPSHOT</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>jetty-start</artifactId> <artifactId>jetty-start</artifactId>

View File

@ -2,7 +2,7 @@
<parent> <parent>
<groupId>org.eclipse.jetty</groupId> <groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId> <artifactId>jetty-project</artifactId>
<version>9.3.3-SNAPSHOT</version> <version>9.3.4-SNAPSHOT</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>jetty-util-ajax</artifactId> <artifactId>jetty-util-ajax</artifactId>

View File

@ -2,7 +2,7 @@
<parent> <parent>
<groupId>org.eclipse.jetty</groupId> <groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId> <artifactId>jetty-project</artifactId>
<version>9.3.3-SNAPSHOT</version> <version>9.3.4-SNAPSHOT</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>jetty-util</artifactId> <artifactId>jetty-util</artifactId>

View File

@ -299,7 +299,7 @@ public class SslContextFactory extends AbstractLifeCycle
{ {
return _aliasX509.get(alias); return _aliasX509.get(alias);
} }
/** /**
* Create the SSLContext object and start the lifecycle * Create the SSLContext object and start the lifecycle
* @see org.eclipse.jetty.util.component.AbstractLifeCycle#doStart() * @see org.eclipse.jetty.util.component.AbstractLifeCycle#doStart()
@ -359,7 +359,7 @@ public class SslContextFactory extends AbstractLifeCycle
} }
X509 x509 = new X509(alias,x509C); X509 x509 = new X509(alias,x509C);
_aliasX509.put(alias,x509); _aliasX509.put(alias,x509);
if (_validateCerts) if (_validateCerts)
{ {
CertificateValidator validator = new CertificateValidator(trustStore, crls); CertificateValidator validator = new CertificateValidator(trustStore, crls);
@ -371,7 +371,7 @@ public class SslContextFactory extends AbstractLifeCycle
} }
LOG.info("x509={} for {}",x509,this); LOG.info("x509={} for {}",x509,this);
for (String h:x509.getHosts()) for (String h:x509.getHosts())
_certHosts.put(h,x509); _certHosts.put(h,x509);
for (String w:x509.getWilds()) for (String w:x509.getWilds())
@ -405,7 +405,7 @@ public class SslContextFactory extends AbstractLifeCycle
LOG.debug("Selected Ciphers {} of {}",Arrays.asList(_selectedCipherSuites),Arrays.asList(sslEngine.getSupportedCipherSuites())); LOG.debug("Selected Ciphers {} of {}",Arrays.asList(_selectedCipherSuites),Arrays.asList(sslEngine.getSupportedCipherSuites()));
} }
} }
@Override @Override
protected void doStop() throws Exception protected void doStop() throws Exception
{ {
@ -1710,15 +1710,15 @@ public class SslContextFactory extends AbstractLifeCycle
{ {
String host = _host = ((SNIHostName)serverName).getAsciiName(); String host = _host = ((SNIHostName)serverName).getAsciiName();
host=StringUtil.asciiToLowerCase(host); host=StringUtil.asciiToLowerCase(host);
// Try an exact match // Try an exact match
_x509 = _certHosts.get(host); _x509 = _certHosts.get(host);
// Else try an exact wild match // Else try an exact wild match
if (_x509==null) if (_x509==null)
{ {
_x509 = _certWilds.get(host); _x509 = _certWilds.get(host);
// Else try an 1 deep wild match // Else try an 1 deep wild match
if (_x509==null) if (_x509==null)
{ {
@ -1733,10 +1733,12 @@ public class SslContextFactory extends AbstractLifeCycle
if (LOG.isDebugEnabled()) if (LOG.isDebugEnabled())
LOG.debug("SNI matched {}->{}",host,_x509); LOG.debug("SNI matched {}->{}",host,_x509);
} }
else if (LOG.isDebugEnabled()) else
LOG.debug("SNI no match for {}", serverName); {
if (LOG.isDebugEnabled())
LOG.debug("SNI no match for {}", serverName);
}
// Return true and allow the KeyManager to accept or reject when choosing a certificate. // Return true and allow the KeyManager to accept or reject when choosing a certificate.
// If we don't have a SNI host, or didn't see any certificate aliases, // If we don't have a SNI host, or didn't see any certificate aliases,

View File

@ -32,14 +32,13 @@ import javax.naming.ldap.Rdn;
import javax.security.auth.x500.X500Principal; import javax.security.auth.x500.X500Principal;
import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.TypeUtil;
import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.log.Logger;
public class X509 public class X509
{ {
static final Logger LOG = Log.getLogger(X509.class); private static final Logger LOG = Log.getLogger(X509.class);
/* /*
* @see {@link X509Certificate#getKeyUsage()} * @see {@link X509Certificate#getKeyUsage()}
*/ */
@ -51,26 +50,22 @@ public class X509
*/ */
private static final int SUBJECT_ALTERNATIVE_NAMES__DNS_NAME=2; private static final int SUBJECT_ALTERNATIVE_NAMES__DNS_NAME=2;
public static boolean isCertSign(X509Certificate x509) public static boolean isCertSign(X509Certificate x509)
{ {
boolean[] key_usage=x509.getKeyUsage(); boolean[] key_usage=x509.getKeyUsage();
return key_usage!=null && key_usage[KEY_USAGE__KEY_CERT_SIGN]; return key_usage!=null && key_usage[KEY_USAGE__KEY_CERT_SIGN];
} }
private final X509Certificate _x509; private final X509Certificate _x509;
private final String _alias; private final String _alias;
private final List<String> _hosts=new ArrayList<>(); private final List<String> _hosts=new ArrayList<>();
private final List<String> _wilds=new ArrayList<>(); private final List<String> _wilds=new ArrayList<>();
public X509(String alias,X509Certificate x509) throws CertificateParsingException, InvalidNameException public X509(String alias,X509Certificate x509) throws CertificateParsingException, InvalidNameException
{ {
_alias=alias; _alias=alias;
_x509 = x509; _x509 = x509;
// Look for alternative name extensions // Look for alternative name extensions
boolean named=false; boolean named=false;
Collection<List<?>> altNames = x509.getSubjectAlternativeNames(); Collection<List<?>> altNames = x509.getSubjectAlternativeNames();
@ -82,7 +77,7 @@ public class X509
{ {
String cn = list.get(1).toString(); String cn = list.get(1).toString();
if (LOG.isDebugEnabled()) if (LOG.isDebugEnabled())
LOG.debug("Certificate SAN alias={} cn={} in {}",alias,cn,this); LOG.debug("Certificate SAN alias={} CN={} in {}",alias,cn,this);
if (cn!=null) if (cn!=null)
{ {
named=true; named=true;
@ -92,28 +87,27 @@ public class X509
} }
} }
// If no names found, look up the cn from the subject // If no names found, look up the CN from the subject
if (!named) if (!named)
{ {
LdapName name=new LdapName(x509.getSubjectX500Principal().getName(X500Principal.RFC2253)); LdapName name=new LdapName(x509.getSubjectX500Principal().getName(X500Principal.RFC2253));
for (Rdn rdn : name.getRdns()) for (Rdn rdn : name.getRdns())
{ {
if (rdn.getType().equalsIgnoreCase("cn")) if (rdn.getType().equalsIgnoreCase("CN"))
{ {
String cn = rdn.getValue().toString(); String cn = rdn.getValue().toString();
if (LOG.isDebugEnabled()) if (LOG.isDebugEnabled())
LOG.debug("Certificate cn alias={} cn={} in {}",alias,cn,this); LOG.debug("Certificate CN alias={} CN={} in {}",alias,cn,this);
if (cn!=null && cn.contains(".") && !cn.contains(" ")) if (cn!=null && cn.contains(".") && !cn.contains(" "))
addName(cn); addName(cn);
} }
} }
} }
} }
protected void addName(String cn) protected void addName(String cn)
{ {
cn=StringUtil.asciiToLowerCase(cn); cn=StringUtil.asciiToLowerCase(cn);
cn.toLowerCase();
if (cn.startsWith("*.")) if (cn.startsWith("*."))
_wilds.add(cn.substring(2)); _wilds.add(cn.substring(2));
else else
@ -124,17 +118,17 @@ public class X509
{ {
return _alias; return _alias;
} }
public X509Certificate getCertificate() public X509Certificate getCertificate()
{ {
return _x509; return _x509;
} }
public Set<String> getHosts() public Set<String> getHosts()
{ {
return new HashSet<>(_hosts); return new HashSet<>(_hosts);
} }
public Set<String> getWilds() public Set<String> getWilds()
{ {
return new HashSet<>(_wilds); return new HashSet<>(_wilds);
@ -156,7 +150,6 @@ public class X509
return false; return false;
} }
@Override @Override
public String toString() public String toString()
{ {

View File

@ -2,7 +2,7 @@
<parent> <parent>
<groupId>org.eclipse.jetty</groupId> <groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId> <artifactId>jetty-project</artifactId>
<version>9.3.3-SNAPSHOT</version> <version>9.3.4-SNAPSHOT</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>jetty-webapp</artifactId> <artifactId>jetty-webapp</artifactId>

View File

@ -470,7 +470,10 @@ public class WebAppClassLoader extends URLClassLoader
c= _parent.loadClass(name); c= _parent.loadClass(name);
if (c == null && ex!=null) if (c == null && ex!=null)
{
LOG.debug("not found {} from {}",name,this,ex);
throw ex; throw ex;
}
if (resolve) if (resolve)
resolveClass(c); resolveClass(c);

View File

@ -3,7 +3,7 @@
<parent> <parent>
<groupId>org.eclipse.jetty.websocket</groupId> <groupId>org.eclipse.jetty.websocket</groupId>
<artifactId>websocket-parent</artifactId> <artifactId>websocket-parent</artifactId>
<version>9.3.3-SNAPSHOT</version> <version>9.3.4-SNAPSHOT</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@ -3,7 +3,7 @@
<parent> <parent>
<groupId>org.eclipse.jetty.websocket</groupId> <groupId>org.eclipse.jetty.websocket</groupId>
<artifactId>websocket-parent</artifactId> <artifactId>websocket-parent</artifactId>
<version>9.3.3-SNAPSHOT</version> <version>9.3.4-SNAPSHOT</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@ -3,7 +3,7 @@
<parent> <parent>
<artifactId>jetty-project</artifactId> <artifactId>jetty-project</artifactId>
<groupId>org.eclipse.jetty</groupId> <groupId>org.eclipse.jetty</groupId>
<version>9.3.3-SNAPSHOT</version> <version>9.3.4-SNAPSHOT</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@ -3,7 +3,7 @@
<parent> <parent>
<groupId>org.eclipse.jetty.websocket</groupId> <groupId>org.eclipse.jetty.websocket</groupId>
<artifactId>websocket-parent</artifactId> <artifactId>websocket-parent</artifactId>
<version>9.3.3-SNAPSHOT</version> <version>9.3.4-SNAPSHOT</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@ -18,33 +18,38 @@
package org.eclipse.jetty.websocket.api; package org.eclipse.jetty.websocket.api;
import java.nio.charset.StandardCharsets;
public class CloseStatus public class CloseStatus
{ {
private static final int MAX_CONTROL_PAYLOAD = 125; private static final int MAX_CONTROL_PAYLOAD = 125;
private static final int MAX_REASON_PHRASE = MAX_CONTROL_PAYLOAD - 2; public static final int MAX_REASON_PHRASE = MAX_CONTROL_PAYLOAD - 2;
/** /**
* Convenience method for trimming a long reason phrase at the maximum reason phrase length. * Convenience method for trimming a long reason phrase at the maximum reason phrase length of 123 UTF-8 bytes (per WebSocket spec).
* *
* @param reason * @param reason
* the proposed reason phrase * the proposed reason phrase
* @return the reason phrase (trimmed if needed) * @return the reason phrase (trimmed if needed)
* @deprecated use of this method is strongly discouraged, as it creates too many new objects that are just thrown away to accomplish its goals.
*/ */
@Deprecated
public static String trimMaxReasonLength(String reason) public static String trimMaxReasonLength(String reason)
{ {
if (reason == null) if (reason == null)
{ {
return null; return null;
} }
byte[] reasonBytes = reason.getBytes(StandardCharsets.UTF_8);
if (reasonBytes.length > MAX_REASON_PHRASE)
{
byte[] trimmed = new byte[MAX_REASON_PHRASE];
System.arraycopy(reasonBytes,0,trimmed,0,MAX_REASON_PHRASE);
return new String(trimmed,StandardCharsets.UTF_8);
}
if (reason.length() > MAX_REASON_PHRASE) return reason;
{
return reason.substring(0,MAX_REASON_PHRASE);
}
else
{
return reason;
}
} }
private int code; private int code;

View File

@ -152,6 +152,15 @@ public interface RemoteEndpoint
*/ */
BatchMode getBatchMode(); BatchMode getBatchMode();
/**
* Set the batch mode with which messages are sent.
*
* @param mode
* the batch mode to use
* @see #flush()
*/
void setBatchMode(BatchMode mode);
/** /**
* Flushes messages that may have been batched by the implementation. * Flushes messages that may have been batched by the implementation.
* *

View File

@ -24,6 +24,8 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; import java.lang.annotation.Target;
import org.eclipse.jetty.websocket.api.BatchMode;
/** /**
* Tags a POJO as being a WebSocket class. * Tags a POJO as being a WebSocket class.
*/ */
@ -40,4 +42,6 @@ public @interface WebSocket
int maxIdleTime() default -2; int maxIdleTime() default -2;
int maxTextMessageSize() default -2; int maxTextMessageSize() default -2;
BatchMode batchMode() default BatchMode.AUTO;
} }

View File

@ -3,7 +3,7 @@
<parent> <parent>
<groupId>org.eclipse.jetty.websocket</groupId> <groupId>org.eclipse.jetty.websocket</groupId>
<artifactId>websocket-parent</artifactId> <artifactId>websocket-parent</artifactId>
<version>9.3.3-SNAPSHOT</version> <version>9.3.4-SNAPSHOT</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@ -3,7 +3,7 @@
<parent> <parent>
<groupId>org.eclipse.jetty.websocket</groupId> <groupId>org.eclipse.jetty.websocket</groupId>
<artifactId>websocket-parent</artifactId> <artifactId>websocket-parent</artifactId>
<version>9.3.3-SNAPSHOT</version> <version>9.3.4-SNAPSHOT</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@ -19,14 +19,13 @@
package org.eclipse.jetty.websocket.common; package org.eclipse.jetty.websocket.common;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.Utf8Appendable.NotUtf8Exception;
import org.eclipse.jetty.util.Utf8StringBuilder; import org.eclipse.jetty.util.Utf8StringBuilder;
import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.Utf8Appendable.NotUtf8Exception;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.websocket.api.BadPayloadException; import org.eclipse.jetty.websocket.api.BadPayloadException;
import org.eclipse.jetty.websocket.api.CloseStatus;
import org.eclipse.jetty.websocket.api.ProtocolException; import org.eclipse.jetty.websocket.api.ProtocolException;
import org.eclipse.jetty.websocket.api.StatusCode; import org.eclipse.jetty.websocket.api.StatusCode;
import org.eclipse.jetty.websocket.api.extensions.Frame; import org.eclipse.jetty.websocket.api.extensions.Frame;
@ -34,19 +33,23 @@ import org.eclipse.jetty.websocket.common.frames.CloseFrame;
public class CloseInfo public class CloseInfo
{ {
private static final Logger LOG = Log.getLogger(CloseInfo.class);
private int statusCode; private int statusCode;
private String reason; private byte[] reasonBytes;
public CloseInfo() public CloseInfo()
{ {
this(StatusCode.NO_CODE,null); this(StatusCode.NO_CODE,null);
} }
/**
* Parse the Close Frame payload.
*
* @param payload the raw close frame payload.
* @param validate true if payload should be validated per WebSocket spec.
*/
public CloseInfo(ByteBuffer payload, boolean validate) public CloseInfo(ByteBuffer payload, boolean validate)
{ {
this.statusCode = StatusCode.NO_CODE; this.statusCode = StatusCode.NO_CODE;
this.reason = null;
if ((payload == null) || (payload.remaining() == 0)) if ((payload == null) || (payload.remaining() == 0))
{ {
@ -77,34 +80,24 @@ public class CloseInfo
if (data.remaining() > 0) if (data.remaining() > 0)
{ {
// Reason // Reason (trimmed to max reason size)
try int len = Math.min(data.remaining(), CloseStatus.MAX_REASON_PHRASE);
reasonBytes = new byte[len];
data.get(reasonBytes,0,len);
// Spec Requirement : throw BadPayloadException on invalid UTF8
if(validate)
{ {
Utf8StringBuilder utf = new Utf8StringBuilder(); try
utf.append(data); {
reason = utf.toString(); Utf8StringBuilder utf = new Utf8StringBuilder();
} // if this throws, we know we have bad UTF8
catch (NotUtf8Exception e) utf.append(reasonBytes,0,reasonBytes.length);
{ }
if (validate) catch (NotUtf8Exception e)
{ {
throw new BadPayloadException("Invalid Close Reason",e); throw new BadPayloadException("Invalid Close Reason",e);
} }
else
{
LOG.warn(e);
}
}
catch (RuntimeException e)
{
if (validate)
{
throw new ProtocolException("Invalid Close Reason",e);
}
else
{
LOG.warn(e);
}
} }
} }
} }
@ -125,10 +118,28 @@ public class CloseInfo
this(statusCode,null); this(statusCode,null);
} }
/**
* Create a CloseInfo, trimming the reason to {@link CloseStatus#MAX_REASON_PHRASE} UTF-8 bytes if needed.
*
* @param statusCode the status code
* @param reason the raw reason code
*/
public CloseInfo(int statusCode, String reason) public CloseInfo(int statusCode, String reason)
{ {
this.statusCode = statusCode; this.statusCode = statusCode;
this.reason = reason; if (reason != null)
{
byte[] utf8Bytes = reason.getBytes(StandardCharsets.UTF_8);
if (utf8Bytes.length > CloseStatus.MAX_REASON_PHRASE)
{
this.reasonBytes = new byte[CloseStatus.MAX_REASON_PHRASE];
System.arraycopy(utf8Bytes,0,this.reasonBytes,0,CloseStatus.MAX_REASON_PHRASE);
}
else
{
this.reasonBytes = utf8Bytes;
}
}
} }
private ByteBuffer asByteBuffer() private ByteBuffer asByteBuffer()
@ -140,11 +151,10 @@ public class CloseInfo
} }
int len = 2; // status code int len = 2; // status code
byte utf[] = null; boolean hasReason = (this.reasonBytes != null) && (this.reasonBytes.length > 0);
if (StringUtil.isNotBlank(reason)) if (hasReason)
{ {
utf = StringUtil.getUtf8Bytes(reason); len += this.reasonBytes.length;
len += utf.length;
} }
ByteBuffer buf = BufferUtil.allocate(len); ByteBuffer buf = BufferUtil.allocate(len);
@ -152,9 +162,9 @@ public class CloseInfo
buf.put((byte)((statusCode >>> 8) & 0xFF)); buf.put((byte)((statusCode >>> 8) & 0xFF));
buf.put((byte)((statusCode >>> 0) & 0xFF)); buf.put((byte)((statusCode >>> 0) & 0xFF));
if (utf != null) if (hasReason)
{ {
buf.put(utf,0,utf.length); buf.put(this.reasonBytes,0,this.reasonBytes.length);
} }
BufferUtil.flipToFlush(buf,0); BufferUtil.flipToFlush(buf,0);
@ -178,7 +188,11 @@ public class CloseInfo
public String getReason() public String getReason()
{ {
return reason; if (this.reasonBytes == null)
{
return null;
}
return new String(this.reasonBytes,StandardCharsets.UTF_8);
} }
public int getStatusCode() public int getStatusCode()
@ -199,6 +213,6 @@ public class CloseInfo
@Override @Override
public String toString() public String toString()
{ {
return String.format("CloseInfo[code=%d,reason=%s]",statusCode,reason); return String.format("CloseInfo[code=%d,reason=%s]",statusCode,getReason());
} }
} }

View File

@ -436,9 +436,7 @@ public class WebSocketRemoteEndpoint implements RemoteEndpoint
return batchMode; return batchMode;
} }
// Only the JSR needs to have this method exposed. @Override
// In the Jetty implementation the batching is set
// at the moment of opening the session.
public void setBatchMode(BatchMode batchMode) public void setBatchMode(BatchMode batchMode)
{ {
this.batchMode = batchMode; this.batchMode = batchMode;

View File

@ -98,7 +98,7 @@ public class WebSocketSession extends ContainerLifeCycle implements Session, Web
@Override @Override
public void close() public void close()
{ {
this.close(StatusCode.NORMAL,null); connection.close();
} }
@Override @Override

View File

@ -25,6 +25,7 @@ import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.Utf8Appendable.NotUtf8Exception; import org.eclipse.jetty.util.Utf8Appendable.NotUtf8Exception;
import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.websocket.api.BatchMode;
import org.eclipse.jetty.websocket.api.CloseException; import org.eclipse.jetty.websocket.api.CloseException;
import org.eclipse.jetty.websocket.api.StatusCode; import org.eclipse.jetty.websocket.api.StatusCode;
import org.eclipse.jetty.websocket.api.WebSocketPolicy; import org.eclipse.jetty.websocket.api.WebSocketPolicy;
@ -208,6 +209,12 @@ public abstract class AbstractEventDriver implements IncomingFrames, EventDriver
{ {
/* TODO: provide annotation in future */ /* TODO: provide annotation in future */
} }
@Override
public BatchMode getBatchMode()
{
return null;
}
@Override @Override
public void openSession(WebSocketSession session) public void openSession(WebSocketSession session)

View File

@ -23,6 +23,7 @@ import java.io.InputStream;
import java.io.Reader; import java.io.Reader;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import org.eclipse.jetty.websocket.api.BatchMode;
import org.eclipse.jetty.websocket.api.WebSocketPolicy; import org.eclipse.jetty.websocket.api.WebSocketPolicy;
import org.eclipse.jetty.websocket.api.extensions.Frame; import org.eclipse.jetty.websocket.api.extensions.Frame;
import org.eclipse.jetty.websocket.api.extensions.IncomingFrames; import org.eclipse.jetty.websocket.api.extensions.IncomingFrames;
@ -34,6 +35,8 @@ public interface EventDriver extends IncomingFrames
public WebSocketPolicy getPolicy(); public WebSocketPolicy getPolicy();
public WebSocketSession getSession(); public WebSocketSession getSession();
public BatchMode getBatchMode();
public void onBinaryFrame(ByteBuffer buffer, boolean fin) throws IOException; public void onBinaryFrame(ByteBuffer buffer, boolean fin) throws IOException;

View File

@ -23,6 +23,7 @@ import java.io.InputStream;
import java.io.Reader; import java.io.Reader;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import org.eclipse.jetty.websocket.api.BatchMode;
import org.eclipse.jetty.websocket.api.WebSocketPolicy; import org.eclipse.jetty.websocket.api.WebSocketPolicy;
import org.eclipse.jetty.websocket.api.annotations.WebSocket; import org.eclipse.jetty.websocket.api.annotations.WebSocket;
import org.eclipse.jetty.websocket.api.extensions.Frame; import org.eclipse.jetty.websocket.api.extensions.Frame;
@ -40,6 +41,7 @@ public class JettyAnnotatedEventDriver extends AbstractEventDriver
{ {
private final JettyAnnotatedMetadata events; private final JettyAnnotatedMetadata events;
private boolean hasCloseBeenCalled = false; private boolean hasCloseBeenCalled = false;
private BatchMode batchMode;
public JettyAnnotatedEventDriver(WebSocketPolicy policy, Object websocket, JettyAnnotatedMetadata events) public JettyAnnotatedEventDriver(WebSocketPolicy policy, Object websocket, JettyAnnotatedMetadata events)
{ {
@ -64,6 +66,13 @@ public class JettyAnnotatedEventDriver extends AbstractEventDriver
{ {
this.policy.setIdleTimeout(anno.maxIdleTime()); this.policy.setIdleTimeout(anno.maxIdleTime());
} }
this.batchMode = anno.batchMode();
}
@Override
public BatchMode getBatchMode()
{
return this.batchMode;
} }
@Override @Override

View File

@ -244,10 +244,14 @@ public abstract class AbstractWebSocketConnection extends AbstractConnection imp
return super.getExecutor(); return super.getExecutor();
} }
/**
* Close without a close code or reason
*/
@Override @Override
public void close() public void close()
{ {
close(StatusCode.NORMAL,null); CloseInfo close = new CloseInfo();
this.outgoingFrame(close.asFrame(),new OnCloseLocalCallback(close),BatchMode.OFF);
} }
/** /**

Some files were not shown because too many files have changed in this diff Show More