Merge branch 'jetty-9.3.x' of github.com:eclipse/jetty.project into jetty-9.3.x

This commit is contained in:
Joakim Erdfelt 2017-09-20 14:29:17 -07:00
commit 36a649a66c
21 changed files with 359 additions and 107 deletions

View File

@ -15,13 +15,6 @@
<bundle-symbolic-name>${project.groupId}.embedded</bundle-symbolic-name>
</properties>
<dependencies>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>18.0</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-util-ajax</artifactId>

View File

@ -103,22 +103,22 @@
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>cdi-core</artifactId>
<version>9.3.20-SNAPSHOT</version>
<version>9.3.22-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>cdi-full-servlet</artifactId>
<version>9.3.20-SNAPSHOT</version>
<version>9.3.22-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>cdi-servlet</artifactId>
<version>9.3.20-SNAPSHOT</version>
<version>9.3.22-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>cdi-websocket</artifactId>
<version>9.3.20-SNAPSHOT</version>
<version>9.3.22-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
@ -130,21 +130,61 @@
<artifactId>jetty-continuation</artifactId>
<version>9.3.22-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.fcgi</groupId>
<artifactId>fcgi-server</artifactId>
<version>9.3.22-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.fcgi</groupId>
<artifactId>fcgi-server</artifactId>
<version>9.3.22-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-gcloud-memcached-session-manager</artifactId>
<version>9.3.20-SNAPSHOT</version>
<version>9.3.22-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-gcloud-session-manager</artifactId>
<version>9.3.20-SNAPSHOT</version>
<version>9.3.22-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-hazelcast</artifactId>
<version>9.3.22-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-http</artifactId>
<version>9.3.22-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.http2</groupId>
<artifactId>http2-client</artifactId>
<version>9.3.22-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.http2</groupId>
<artifactId>http2-common</artifactId>
<version>9.3.22-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.http2</groupId>
<artifactId>http2-hpack</artifactId>
<version>9.3.22-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.http2</groupId>
<artifactId>http2-http-client-transport</artifactId>
<version>9.3.22-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.http2</groupId>
<artifactId>http2-server</artifactId>
<version>9.3.22-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-http-spi</artifactId>
@ -193,17 +233,17 @@
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-osgi-boot</artifactId>
<version>9.3.20-SNAPSHOT</version>
<version>9.3.22-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-osgi-boot-jsp</artifactId>
<version>9.3.20-SNAPSHOT</version>
<version>9.3.22-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-osgi-boot-warurl</artifactId>
<version>9.3.20-SNAPSHOT</version>
<version>9.3.22-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
@ -268,37 +308,37 @@
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>javax-websocket-client-impl</artifactId>
<version>9.3.20-SNAPSHOT</version>
<version>9.3.22-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>javax-websocket-server-impl</artifactId>
<version>9.3.20-SNAPSHOT</version>
<version>9.3.22-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>javax-websocket-api</artifactId>
<version>9.3.20-SNAPSHOT</version>
<version>9.3.22-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>javax-websocket-client</artifactId>
<version>9.3.20-SNAPSHOT</version>
<version>9.3.22-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>javax-websocket-common</artifactId>
<version>9.3.20-SNAPSHOT</version>
<version>9.3.22-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>javax-websocket-server</artifactId>
<version>9.3.20-SNAPSHOT</version>
<version>9.3.22-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>javax-websocket-servlet</artifactId>
<version>9.3.20-SNAPSHOT</version>
<version>9.3.22-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>

View File

@ -60,6 +60,7 @@ public class HttpReceiverOverHTTPTest
client = new HttpClient();
client.start();
destination = new HttpDestinationOverHTTP(client, new Origin("http", "localhost", 8080));
destination.start();
endPoint = new ByteArrayEndPoint();
connection = new HttpConnectionOverHTTP(endPoint, destination, new Promise.Adapter<>());
endPoint.setConnection(connection);

View File

@ -67,6 +67,7 @@ public class HttpSenderOverHTTPTest
{
ByteArrayEndPoint endPoint = new ByteArrayEndPoint();
HttpDestinationOverHTTP destination = new HttpDestinationOverHTTP(client, new Origin("http", "localhost", 8080));
destination.start();
HttpConnectionOverHTTP connection = new HttpConnectionOverHTTP(endPoint, destination, new Promise.Adapter<Connection>());
Request request = client.newRequest(URI.create("http://localhost/"));
final CountDownLatch headersLatch = new CountDownLatch(1);
@ -100,6 +101,7 @@ public class HttpSenderOverHTTPTest
{
ByteArrayEndPoint endPoint = new ByteArrayEndPoint("", 16);
HttpDestinationOverHTTP destination = new HttpDestinationOverHTTP(client, new Origin("http", "localhost", 8080));
destination.start();
HttpConnectionOverHTTP connection = new HttpConnectionOverHTTP(endPoint, destination, new Promise.Adapter<Connection>());
Request request = client.newRequest(URI.create("http://localhost/"));
connection.send(request, null);
@ -129,6 +131,7 @@ public class HttpSenderOverHTTPTest
// Shutdown output to trigger the exception on write
endPoint.shutdownOutput();
HttpDestinationOverHTTP destination = new HttpDestinationOverHTTP(client, new Origin("http", "localhost", 8080));
destination.start();
HttpConnectionOverHTTP connection = new HttpConnectionOverHTTP(endPoint, destination, new Promise.Adapter<Connection>());
Request request = client.newRequest(URI.create("http://localhost/"));
final CountDownLatch failureLatch = new CountDownLatch(2);
@ -158,6 +161,7 @@ public class HttpSenderOverHTTPTest
{
ByteArrayEndPoint endPoint = new ByteArrayEndPoint("", 16);
HttpDestinationOverHTTP destination = new HttpDestinationOverHTTP(client, new Origin("http", "localhost", 8080));
destination.start();
HttpConnectionOverHTTP connection = new HttpConnectionOverHTTP(endPoint, destination, new Promise.Adapter<Connection>());
Request request = client.newRequest(URI.create("http://localhost/"));
final CountDownLatch failureLatch = new CountDownLatch(2);
@ -193,6 +197,7 @@ public class HttpSenderOverHTTPTest
{
ByteArrayEndPoint endPoint = new ByteArrayEndPoint();
HttpDestinationOverHTTP destination = new HttpDestinationOverHTTP(client, new Origin("http", "localhost", 8080));
destination.start();
HttpConnectionOverHTTP connection = new HttpConnectionOverHTTP(endPoint, destination, new Promise.Adapter<Connection>());
Request request = client.newRequest(URI.create("http://localhost/"));
String content = "abcdef";
@ -227,6 +232,7 @@ public class HttpSenderOverHTTPTest
{
ByteArrayEndPoint endPoint = new ByteArrayEndPoint();
HttpDestinationOverHTTP destination = new HttpDestinationOverHTTP(client, new Origin("http", "localhost", 8080));
destination.start();
HttpConnectionOverHTTP connection = new HttpConnectionOverHTTP(endPoint, destination, new Promise.Adapter<Connection>());
Request request = client.newRequest(URI.create("http://localhost/"));
String content1 = "0123456789";
@ -262,6 +268,7 @@ public class HttpSenderOverHTTPTest
{
ByteArrayEndPoint endPoint = new ByteArrayEndPoint();
HttpDestinationOverHTTP destination = new HttpDestinationOverHTTP(client, new Origin("http", "localhost", 8080));
destination.start();
HttpConnectionOverHTTP connection = new HttpConnectionOverHTTP(endPoint, destination, new Promise.Adapter<Connection>());
Request request = client.newRequest(URI.create("http://localhost/"));
String content1 = "0123456789";

View File

@ -600,10 +600,6 @@ public class HttpGenerator
for (int f=0;f<n;f++)
{
HttpField field = fields.getField(f);
String v = field.getValue();
if (v==null || v.length()==0)
continue; // rfc7230 does not allow no value
HttpHeader h = field.getHeader();
if (h==null)
putTo(field,header);

View File

@ -66,7 +66,6 @@ import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.FutureCallback;
import org.eclipse.jetty.util.FuturePromise;
import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.StacklessLogging;
import org.eclipse.jetty.util.thread.QueuedThreadPool;
import org.hamcrest.Matchers;
@ -428,7 +427,6 @@ public class StreamResetTest extends AbstractTest
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
Log.getLogger(StreamResetTest.class).info("SIMON: uri={}", request.getRequestURI());
phaser.get().countDown();
IO.copy(request.getInputStream(), response.getOutputStream());
}
@ -455,7 +453,6 @@ public class StreamResetTest extends AbstractTest
@Override
public void onHeaders(Stream stream, HeadersFrame frame)
{
Log.getLogger(StreamResetTest.class).info("SIMON: response={}/{}", stream.getId(), frame.getMetaData());
MetaData.Response response = (MetaData.Response)frame.getMetaData();
if (response.getStatus() == HttpStatus.OK_200)
latch.get().countDown();
@ -464,7 +461,6 @@ public class StreamResetTest extends AbstractTest
@Override
public void onData(Stream stream, DataFrame frame, Callback callback)
{
Log.getLogger(StreamResetTest.class).info("SIMON: data={}/{}", stream.getId(), frame);
callback.succeeded();
if (frame.isEndStream())
latch.get().countDown();

View File

@ -175,6 +175,7 @@ public class HTTP2Connection extends AbstractConnection
{
private final Callback fillCallback = new FillCallback();
private ByteBuffer buffer;
private boolean shutdown;
@Override
public Runnable produce()
@ -185,7 +186,7 @@ public class HTTP2Connection extends AbstractConnection
if (task != null)
return task;
if (isFillInterested())
if (isFillInterested() || shutdown)
return null;
if (buffer == null)
@ -221,6 +222,7 @@ public class HTTP2Connection extends AbstractConnection
else if (filled < 0)
{
release();
shutdown = true;
session.onShutdown();
return null;
}

View File

@ -421,8 +421,7 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
{
// We received a GO_AWAY, so try to write
// what's in the queue and then disconnect.
notifyClose(this, frame);
control(null, Callback.NOOP, new DisconnectFrame());
notifyClose(this, frame, new DisconnectCallback());
return;
}
break;
@ -462,8 +461,7 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
@Override
public void onConnectionFailure(int error, String reason)
{
notifyFailure(this, new IOException(String.format("%d/%s", error, reason)));
close(error, reason, Callback.NOOP);
notifyFailure(this, new IOException(String.format("%d/%s", error, reason)), new CloseCallback(error, reason));
}
@Override
@ -991,8 +989,7 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
protected void abort(Throwable failure)
{
notifyFailure(this, failure);
terminate(failure);
notifyFailure(this, failure, new TerminateCallback(failure));
}
public boolean isDisconnected()
@ -1054,11 +1051,11 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
}
}
protected void notifyClose(Session session, GoAwayFrame frame)
protected void notifyClose(Session session, GoAwayFrame frame, Callback callback)
{
try
{
listener.onClose(session, frame);
listener.onClose(session, frame, callback);
}
catch (Throwable x)
{
@ -1079,11 +1076,11 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
}
}
protected void notifyFailure(Session session, Throwable failure)
protected void notifyFailure(Session session, Throwable failure, Callback callback)
{
try
{
listener.onFailure(session, failure);
listener.onFailure(session, failure, callback);
}
catch (Throwable x)
{
@ -1322,4 +1319,81 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
promise.failed(x);
}
}
private class CloseCallback implements Callback.NonBlocking
{
private final int error;
private final String reason;
private CloseCallback(int error, String reason)
{
this.error = error;
this.reason = reason;
}
@Override
public void succeeded()
{
complete();
}
@Override
public void failed(Throwable x)
{
complete();
}
private void complete()
{
close(error, reason, Callback.NOOP);
}
}
private class DisconnectCallback implements Callback.NonBlocking
{
@Override
public void succeeded()
{
complete();
}
@Override
public void failed(Throwable x)
{
complete();
}
private void complete()
{
control(null, Callback.NOOP, new DisconnectFrame());
}
}
private class TerminateCallback implements Callback.NonBlocking
{
private final Throwable failure;
private TerminateCallback(Throwable failure)
{
this.failure = failure;
}
@Override
public void succeeded()
{
complete();
}
@Override
public void failed(Throwable x)
{
failure.addSuppressed(x);
complete();
}
private void complete()
{
terminate(failure);
}
}
}

View File

@ -195,9 +195,23 @@ public interface Session
/**
* <p>Callback method invoked when a GOAWAY frame has been received.</p>
*
* @param session the session
* @param frame the GOAWAY frame received
* @param session the session
* @param frame the GOAWAY frame received
* @param callback the callback to notify of the GOAWAY processing
*/
public default void onClose(Session session, GoAwayFrame frame, Callback callback)
{
try
{
onClose(session, frame);
callback.succeeded();
}
catch (Throwable x)
{
callback.failed(x);
}
}
public void onClose(Session session, GoAwayFrame frame);
/**
@ -210,9 +224,23 @@ public interface Session
/**
* <p>Callback method invoked when a failure has been detected for this session.</p>
*
* @param session the session
* @param failure the failure
* @param session the session
* @param failure the failure
* @param callback the callback to notify of failure processing
*/
public default void onFailure(Session session, Throwable failure, Callback callback)
{
try
{
onFailure(session, failure);
callback.succeeded();
}
catch (Throwable x)
{
callback.failed(x);
}
}
public void onFailure(Session session, Throwable failure);
/**

View File

@ -22,6 +22,7 @@ import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.Executor;
@ -55,6 +56,7 @@ import org.eclipse.jetty.server.HttpConfiguration;
import org.eclipse.jetty.util.B64Code;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.CountingCallback;
import org.eclipse.jetty.util.TypeUtil;
import org.eclipse.jetty.util.thread.ExecutionStrategy;
@ -156,19 +158,27 @@ public class HTTP2ServerConnection extends HTTP2Connection implements Connection
public boolean onStreamTimeout(IStream stream, Throwable failure)
{
HttpChannelOverHTTP2 channel = (HttpChannelOverHTTP2)stream.getAttribute(IStream.CHANNEL_ATTRIBUTE);
boolean result = channel != null && channel.onStreamTimeout(failure);
boolean result = channel != null && channel.onStreamTimeout(failure, task -> offerTask(task, true));
if (LOG.isDebugEnabled())
LOG.debug("{} idle timeout on {}: {}", result ? "Processed" : "Ignored", stream, failure);
return result;
}
public void onStreamFailure(IStream stream, Throwable failure)
public void onStreamFailure(IStream stream, Throwable failure, Callback callback)
{
if (LOG.isDebugEnabled())
LOG.debug("Processing failure on {}: {}", stream, failure);
HttpChannelOverHTTP2 channel = (HttpChannelOverHTTP2)stream.getAttribute(IStream.CHANNEL_ATTRIBUTE);
if (channel != null)
channel.onFailure(failure);
{
Runnable task = channel.onFailure(failure, callback);
if (task != null)
offerTask(task, true);
}
else
{
callback.succeeded();
}
}
public boolean onSessionTimeout(Throwable failure)
@ -179,20 +189,29 @@ public class HTTP2ServerConnection extends HTTP2Connection implements Connection
{
HttpChannelOverHTTP2 channel = (HttpChannelOverHTTP2)stream.getAttribute(IStream.CHANNEL_ATTRIBUTE);
if (channel != null)
result &= !channel.isRequestExecuting();
result &= channel.isRequestIdle();
}
if (LOG.isDebugEnabled())
LOG.debug("{} idle timeout on {}: {}", result ? "Processed" : "Ignored", session, failure);
return result;
}
public void onSessionFailure(Throwable failure)
public void onSessionFailure(Throwable failure, Callback callback)
{
ISession session = getSession();
if (LOG.isDebugEnabled())
LOG.debug("Processing failure on {}: {}", session, failure);
for (Stream stream : session.getStreams())
onStreamFailure((IStream)stream, failure);
Collection<Stream> streams = session.getStreams();
if (streams.isEmpty())
{
callback.succeeded();
}
else
{
CountingCallback counter = new CountingCallback(callback, streams.size());
for (Stream stream : streams)
onStreamFailure((IStream)stream, failure, counter);
}
}
public void push(Connector connector, IStream stream, MetaData.Request request)

View File

@ -123,7 +123,7 @@ public class HTTP2ServerConnectionFactory extends AbstractHTTP2ServerConnectionF
}
@Override
public void onClose(Session session, GoAwayFrame frame)
public void onClose(Session session, GoAwayFrame frame, Callback callback)
{
ErrorCode error = ErrorCode.from(frame.getError());
if (error == null)
@ -131,13 +131,13 @@ public class HTTP2ServerConnectionFactory extends AbstractHTTP2ServerConnectionF
String reason = frame.tryConvertPayload();
if (reason != null && !reason.isEmpty())
reason = " (" + reason + ")";
getConnection().onSessionFailure(new EofException("HTTP/2 " + error + reason));
getConnection().onSessionFailure(new EofException("HTTP/2 " + error + reason), callback);
}
@Override
public void onFailure(Session session, Throwable failure)
public void onFailure(Session session, Throwable failure, Callback callback)
{
getConnection().onSessionFailure(failure);
getConnection().onSessionFailure(failure, callback);
}
@Override
@ -167,7 +167,7 @@ public class HTTP2ServerConnectionFactory extends AbstractHTTP2ServerConnectionF
ErrorCode error = ErrorCode.from(frame.getError());
if (error == null)
error = ErrorCode.CANCEL_STREAM_ERROR;
getConnection().onStreamFailure((IStream)stream, new EofException("HTTP/2 " + error));
getConnection().onStreamFailure((IStream)stream, new EofException("HTTP/2 " + error), Callback.NOOP);
}
@Override

View File

@ -20,6 +20,7 @@ package org.eclipse.jetty.http2.server;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.function.Consumer;
import org.eclipse.jetty.http.BadMessageException;
import org.eclipse.jetty.http.HttpField;
@ -40,6 +41,7 @@ import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.HttpChannel;
import org.eclipse.jetty.server.HttpConfiguration;
import org.eclipse.jetty.server.HttpInput;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.log.Log;
@ -277,32 +279,33 @@ public class HttpChannelOverHTTP2 extends HttpChannel
return handle || wasDelayed ? this : null;
}
public boolean isRequestExecuting()
public boolean isRequestIdle()
{
return !getState().isIdle();
return getState().isIdle();
}
public boolean onStreamTimeout(Throwable failure)
public boolean onStreamTimeout(Throwable failure, Consumer<Runnable> consumer)
{
boolean result = false;
if (isRequestIdle())
{
consumeInput();
result = true;
}
getHttpTransport().onStreamTimeout(failure);
if (getRequest().getHttpInput().onIdleTimeout(failure))
handle();
consumer.accept(this::handleWithContext);
if (isRequestExecuting())
return false;
consumeInput();
return true;
return result;
}
public void onFailure(Throwable failure)
public Runnable onFailure(Throwable failure, Callback callback)
{
getHttpTransport().onStreamFailure(failure);
if (getRequest().getHttpInput().failed(failure))
handle();
else
getState().asyncError(failure);
boolean handle = getRequest().getHttpInput().failed(failure);
consumeInput();
return new FailureTask(failure, callback, handle);
}
protected void consumeInput()
@ -310,6 +313,15 @@ public class HttpChannelOverHTTP2 extends HttpChannel
getRequest().getHttpInput().consumeAll();
}
private void handleWithContext()
{
ContextHandler context = getState().getContextHandler();
if (context != null)
context.handle(getRequest(), this);
else
handle();
}
/**
* If the associated response has the Expect header set to 100 Continue,
* then accessing the input stream indicates that the handler/servlet
@ -348,4 +360,35 @@ public class HttpChannelOverHTTP2 extends HttpChannel
streamId = stream.getId();
return String.format("%s#%d", super.toString(), getStream() == null ? -1 : streamId);
}
private class FailureTask implements Runnable
{
private final Throwable failure;
private final Callback callback;
private final boolean handle;
public FailureTask(Throwable failure, Callback callback, boolean handle)
{
this.failure = failure;
this.callback = callback;
this.handle = handle;
}
@Override
public void run()
{
try
{
if (handle)
handleWithContext();
else
getState().asyncError(failure);
callback.succeeded();
}
catch (Throwable x)
{
callback.failed(x);
}
}
}
}

View File

@ -1121,7 +1121,7 @@ public class SslConnection extends AbstractConnection
@Override
public boolean isInputShutdown()
{
return _sslEngine.isInboundDone();
return getEndPoint().isInputShutdown() || _sslEngine.isInboundDone();
}
private void notifyHandshakeSucceeded(SSLEngine sslEngine)

View File

@ -91,6 +91,7 @@ public class HttpInput extends ServletInputStream implements Runnable
_contentConsumed = 0;
_firstByteTimeStamp = -1;
_blockUntil = 0;
_waitingForContent = false;
}
}

View File

@ -30,7 +30,7 @@ public interface Callback
* Instance of Adapter that can be used when the callback methods need an empty
* implementation without incurring in the cost of allocating a new Adapter object.
*/
Callback NOOP = new Callback()
Callback NOOP = new Callback.NonBlocking()
{
};

View File

@ -45,6 +45,8 @@ public class CountingCallback extends Callback.Nested
public CountingCallback(Callback callback, int count)
{
super(callback);
if (count < 1)
throw new IllegalArgumentException();
this.count = new AtomicInteger(count);
}

View File

@ -72,51 +72,47 @@ public abstract class Credential implements Serializable
}
/**
* <p>Utility method that replaces String.equals() to avoid timing attacks.</p>
* <p>Utility method that replaces String.equals() to avoid timing attacks.
* The length of the loop executed will always be the length of the unknown credential</p>
*
* @param s1 the first string to compare
* @param s2 the second string to compare
* @param known the first string to compare (should be known string)
* @param unknown the second string to compare (should be the unknown string)
* @return whether the two strings are equal
*/
protected static boolean stringEquals(String s1, String s2)
protected static boolean stringEquals(String known, String unknown)
{
if (s1 == s2)
if (known == unknown)
return true;
if (s1 == null || s2 == null)
if (known == null || unknown == null)
return false;
boolean result = true;
int l1 = s1.length();
int l2 = s2.length();
if (l1 != l2)
result = false;
int l = Math.min(l1, l2);
for (int i = 0; i < l; ++i)
result &= s1.charAt(i) == s2.charAt(i);
return result;
int l1 = known.length();
int l2 = unknown.length();
for (int i = 0; i < l2; ++i)
result &= known.charAt(i%l1) == unknown.charAt(i);
return result && l1 == l2;
}
/**
* <p>Utility method that replaces Arrays.equals() to avoid timing attacks.</p>
* <p>Utility method that replaces Arrays.equals() to avoid timing attacks.
* The length of the loop executed will always be the length of the unknown credential</p>
*
* @param b1 the first byte array to compare
* @param b2 the second byte array to compare
* @param known the first byte array to compare (should be known value)
* @param unknown the second byte array to compare (should be unknown value)
* @return whether the two byte arrays are equal
*/
protected static boolean byteEquals(byte[] b1, byte[] b2)
protected static boolean byteEquals(byte[] known, byte[] unknown)
{
if (b1 == b2)
if (known == unknown)
return true;
if (b1 == null || b2 == null)
if (known == null || unknown == null)
return false;
boolean result = true;
int l1 = b1.length;
int l2 = b2.length;
if (l1 != l2)
result = false;
int l = Math.min(l1, l2);
for (int i = 0; i < l; ++i)
result &= b1[i] == b2[i];
return result;
int l1 = known.length;
int l2 = unknown.length;
for (int i = 0; i < l2; ++i)
result &= known[i%l1] == unknown[i];
return result && l1 == l2;
}
/**

View File

@ -51,6 +51,7 @@ import java.util.concurrent.atomic.AtomicReference;
import org.eclipse.jetty.util.ArrayQueue;
import org.eclipse.jetty.util.LazyList;
import org.eclipse.jetty.util.Loader;
import org.eclipse.jetty.util.MultiException;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.TypeUtil;
import org.eclipse.jetty.util.component.LifeCycle;
@ -482,6 +483,8 @@ public class XmlConfiguration
if (LOG.isDebugEnabled())
LOG.debug("XML " + (obj != null?obj.toString():oClass.getName()) + "." + name + "(" + value + ")");
MultiException me = new MultiException();
// Try for trivial match
try
{
@ -492,6 +495,7 @@ public class XmlConfiguration
catch (IllegalArgumentException | IllegalAccessException | NoSuchMethodException e)
{
LOG.ignore(e);
me.add(e);
}
// Try for native match
@ -506,6 +510,7 @@ public class XmlConfiguration
catch (NoSuchFieldException | IllegalArgumentException | IllegalAccessException | NoSuchMethodException e)
{
LOG.ignore(e);
me.add(e);
}
// Try a field
@ -521,16 +526,19 @@ public class XmlConfiguration
catch (NoSuchFieldException e)
{
LOG.ignore(e);
me.add(e);
}
// Search for a match by trying all the set methods
Method[] sets = oClass.getMethods();
Method set = null;
String types = null;
for (int s = 0; sets != null && s < sets.length; s++)
{
Class<?>[] paramTypes = sets[s].getParameterTypes();
if (name.equals(sets[s].getName()) && paramTypes.length == 1)
{
types = types==null?paramTypes[0].getName():(types+","+paramTypes[0].getName());
// lets try it
try
{
@ -541,6 +549,7 @@ public class XmlConfiguration
catch (IllegalArgumentException | IllegalAccessException e)
{
LOG.ignore(e);
me.add(e);
}
try
@ -555,6 +564,7 @@ public class XmlConfiguration
catch (IllegalAccessException e)
{
LOG.ignore(e);
me.add(e);
}
}
}
@ -585,11 +595,21 @@ public class XmlConfiguration
catch (NoSuchMethodException | IllegalAccessException | InstantiationException e)
{
LOG.ignore(e);
me.add(e);
}
}
// No Joy
throw new NoSuchMethodException(oClass + "." + name + "(" + vClass[0] + ")");
String message = oClass + "." + name + "(" + vClass[0] + ")";
if (types!=null)
message += ". Found setters for "+types;
throw new NoSuchMethodException(message)
{
{
for (int i=0; i<me.size(); i++)
addSuppressed(me.getThrowable(i));
}
};
}
/**

View File

@ -26,6 +26,8 @@ import java.util.Map;
import java.util.Properties;
import java.util.concurrent.atomic.AtomicInteger;
import org.eclipse.jetty.util.log.Log;
import org.hamcrest.Matchers;
import org.junit.Assert;
import org.junit.Ignore;
import org.junit.Test;
@ -33,6 +35,7 @@ import org.junit.Test;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.CoreMatchers.nullValue;
import static org.hamcrest.Matchers.containsString;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
@ -248,6 +251,23 @@ public class XmlConfigurationTest
assertEquals("Set Type 3", 2, tc.testInt);
}
@Test
public void testMeaningfullSetException() throws Exception
{
XmlConfiguration configuration =
new XmlConfiguration("<Configure class=\"org.eclipse.jetty.xml.TestConfiguration\"><Set name=\"PropertyTest\"><Property name=\"null\"/></Set></Configure>");
TestConfiguration tc = new TestConfiguration();
try
{
configuration.configure(tc);
Assert.fail();
}
catch(NoSuchMethodException e)
{
assertThat(e.getMessage(),containsString("Found setters for int"));
}
}
@Test
public void testListConstructorArg() throws Exception
{

18
pom.xml
View File

@ -496,7 +496,7 @@
<plugin>
<groupId>org.eclipse.jetty.toolchain</groupId>
<artifactId>jetty-version-maven-plugin</artifactId>
<version>2.4</version>
<version>2.5</version>
</plugin>
<plugin>
<groupId>org.apache.felix</groupId>
@ -717,7 +717,7 @@
<dependency>
<groupId>org.eclipse.jetty.toolchain</groupId>
<artifactId>jetty-test-helper</artifactId>
<version>4.0</version>
<version>4.2</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.toolchain</groupId>
@ -827,6 +827,20 @@
<copyGenerated>true</copyGenerated>
<attachArtifact>false</attachArtifact>
<updateDate>true</updateDate>
<versionTagHeader>Tag for release: jetty-${project.version}</versionTagHeader>
<filenameExcludes>
<filenameExclude>jetty-documentation/.*</filenameExclude>
<filenameExclude>examples/.*</filenameExclude>
<filenameExclude>aggregates/.*</filenameExclude>
<filenameExclude>.*/test-.*</filenameExclude>
<filenameExclude>.*/.*-test/.*</filenameExclude>
<filenameExclude>.*/.*-tests/.*</filenameExclude>
<filenameExclude>.*/src/test/.*</filenameExclude>
<filenameExclude>\.git.*</filenameExclude>
<filenameExclude>.*\.md$</filenameExclude>
<filenameExclude>.*\.txt$</filenameExclude>
<filenameExclude>Jenkinsfile</filenameExclude>
</filenameExcludes>
</configuration>
</execution>
</executions>

View File

@ -694,7 +694,7 @@ public class ServerTimeoutsTest extends AbstractTest
{
try
{
Thread.sleep(2 * idleTimeout);
Thread.sleep(idleTimeout + idleTimeout / 2);
IO.copy(request.getInputStream(), response.getOutputStream());
}
catch (InterruptedException x)
@ -729,7 +729,7 @@ public class ServerTimeoutsTest extends AbstractTest
});
// Wait for the server application to block reading.
Thread.sleep(3 * idleTimeout);
Thread.sleep(2 * idleTimeout);
content.offer(ByteBuffer.wrap(data2));
content.close();