Merge remote-tracking branch 'origin/jetty-9.4.x' into issue-1640

This commit is contained in:
Jan Bartel 2017-08-24 09:39:51 +10:00
commit 82fa665d38
20 changed files with 294 additions and 132 deletions

View File

@ -1,12 +1,12 @@
Contributing to Jetty
=====================
Thanks for your interest in this project.
Thank you for your interest in this project!
Project description
--------------------
Jetty is a lightweight highly scalable java based web server and servlet engine.
Our goal is to support web protocols like HTTP, HTTP/2, and WebSocket in a high
volume low latency way that provides maximum performance while retaining the ease
volume, low latency way that provides maximum performance while retaining the ease
of use and compatibility with years of servlet development.
Jetty is a modern fully async web server that has a long history as a component
oriented technology easily embedded into applications while still offering a solid
@ -20,18 +20,17 @@ Information regarding source code management, builds, coding standards, and more
- [https://www.eclipse.org/jetty/documentation/current/advanced-contributing.html](https://www.eclipse.org/jetty/documentation/current/advanced-contributing.html)
The canonical Jetty git repository is located at GitHub. Providing you have
The canonical Jetty git repository is located at [GitHub.](https://github.com/eclipse/jetty.project) Providing you have
completed the contributors agreement mentioned below we will endeavor to pull
your commit into Jetty proper.
Contributor License Agreement
Eclipse Contributor Agreement
------------------------------
Before your contribution can be accepted by the project, you need to create and electronically sign the
Eclipse Foundation [Contributor License Agreement](https://www.eclipse.org/legal/CLA.php) (CLA):
Before your contribution can be accepted by the project, you need to create and electronically sign a [Eclipse Contributor Agreement (ECA)](http://www.eclipse.org/legal/ecafaq.php):
1. Log in to the [Eclipse projects forge](https://projects.eclipse.org/user/login/sso). You will need to
1. Log in to the [Eclipse foundation website](https://accounts.eclipse.org/user/login/). You will need to
create an account with the Eclipse Foundation if you have not already done so.
2. Click on "Contributor License Agreement", and complete the form.
2. Click on "Eclipse ECA", and complete the form.
Be sure to use the same email address in your Eclipse account that you intend to use when you commit to Git.

View File

@ -42,6 +42,7 @@ import org.eclipse.jetty.util.annotation.ManagedAttribute;
import org.eclipse.jetty.util.annotation.ManagedObject;
import org.eclipse.jetty.util.component.ContainerLifeCycle;
import org.eclipse.jetty.util.component.Dumpable;
import org.eclipse.jetty.util.component.DumpableCollection;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.thread.Sweeper;
@ -446,17 +447,11 @@ public abstract class HttpDestination extends ContainerLifeCycle implements Dest
exchange.getRequest().abort(cause);
}
@Override
public String dump()
{
return ContainerLifeCycle.dump(this);
}
@Override
public void dump(Appendable out, String indent) throws IOException
{
ContainerLifeCycle.dumpObject(out, toString());
ContainerLifeCycle.dump(out, indent, Collections.singletonList(connectionPool));
super.dump(out, indent);
ContainerLifeCycle.dump(out, indent, Collections.singleton(new DumpableCollection("exchanges", exchanges)));
}
public String asString()

View File

@ -67,7 +67,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;
@ -439,7 +438,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());
}
@ -466,7 +464,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();
@ -475,7 +472,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();
@ -544,7 +540,6 @@ public class StreamResetTest extends AbstractTest
Session client = newClient(new Session.Listener.Adapter());
Log.getLogger(HttpChannel.class).info("Expecting java.lang.IllegalStateException: explictly_thrown_by_test");
MetaData.Request request = newRequest("GET", new HttpFields());
HeadersFrame frame = new HeadersFrame(request, null, false);
FuturePromise<Stream> promise = new FuturePromise<>();

View File

@ -181,6 +181,7 @@ public class HTTP2Connection extends AbstractConnection
{
private final Callback fillableCallback = new FillableCallback();
private ByteBuffer buffer;
private boolean shutdown;
@Override
public Runnable produce()
@ -191,7 +192,7 @@ public class HTTP2Connection extends AbstractConnection
if (task != null)
return task;
if (isFillInterested())
if (isFillInterested() || shutdown)
return null;
if (buffer == null)
@ -227,6 +228,7 @@ public class HTTP2Connection extends AbstractConnection
else if (filled < 0)
{
release();
shutdown = true;
session.onShutdown();
return null;
}

View File

@ -435,8 +435,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;
@ -476,8 +475,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
@ -1003,8 +1001,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()
@ -1066,11 +1063,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)
{
@ -1091,11 +1088,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)
{
@ -1330,4 +1327,99 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
promise.failed(x);
}
}
private class CloseCallback implements Callback
{
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();
}
@Override
public InvocationType getInvocationType()
{
return InvocationType.NON_BLOCKING;
}
private void complete()
{
close(error, reason, Callback.NOOP);
}
}
private class DisconnectCallback implements Callback
{
@Override
public void succeeded()
{
complete();
}
@Override
public void failed(Throwable x)
{
complete();
}
@Override
public InvocationType getInvocationType()
{
return InvocationType.NON_BLOCKING;
}
private void complete()
{
control(null, Callback.NOOP, new DisconnectFrame());
}
}
private class TerminateCallback implements Callback
{
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();
}
@Override
public InvocationType getInvocationType()
{
return InvocationType.NON_BLOCKING;
}
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

@ -23,6 +23,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.atomic.AtomicLong;
@ -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.ReservedThreadExecutor;
@ -199,19 +201,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)
@ -222,20 +232,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

@ -122,7 +122,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)
@ -130,13 +130,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
@ -168,7 +168,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

@ -21,6 +21,7 @@ package org.eclipse.jetty.http2.server;
import java.io.Closeable;
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;
@ -300,40 +301,33 @@ public class HttpChannelOverHTTP2 extends HttpChannel implements Closeable
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))
{
ContextHandler handler = getState().getContextHandler();
if (handler != null)
handler.handle(getRequest(), this);
else
handle();
}
else
{
getState().asyncError(failure);
}
boolean handle = getRequest().getHttpInput().failed(failure);
consumeInput();
return new FailureTask(failure, callback, handle);
}
protected void consumeInput()
@ -341,6 +335,15 @@ public class HttpChannelOverHTTP2 extends HttpChannel implements Closeable
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
@ -385,4 +388,35 @@ public class HttpChannelOverHTTP2 extends HttpChannel implements Closeable
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

@ -330,9 +330,9 @@ public class HttpTransportOverHTTP2 implements HttpTransport
if (state == State.WRITING)
{
this.state = State.FAILED;
this.failure = failure;
callback = this.callback;
this.callback = null;
this.failure = failure;
}
}
if (LOG.isDebugEnabled())

View File

@ -165,6 +165,7 @@ public class HttpInput extends ServletInputStream implements Runnable
_contentConsumed = 0;
_firstByteTimeStamp = -1;
_blockUntil = 0;
_waitingForContent = false;
if (_interceptor instanceof Destroyable)
((Destroyable)_interceptor).destroy();
_interceptor = null;

View File

@ -36,6 +36,11 @@ public interface Callback extends Invocable
*/
static Callback NOOP = new Callback()
{
@Override
public InvocationType getInvocationType()
{
return InvocationType.NON_BLOCKING;
}
};
/**

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

@ -86,51 +86,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

@ -76,4 +76,22 @@ public class CredentialTest
assertTrue (p1.equals(p2));
}
@Test
public void testStringEquals()
{
assertTrue(Credential.stringEquals("foo","foo"));
assertFalse(Credential.stringEquals("foo","fooo"));
assertFalse(Credential.stringEquals("foo","fo"));
assertFalse(Credential.stringEquals("foo","bar"));
}
@Test
public void testBytesEquals()
{
assertTrue(Credential.byteEquals("foo".getBytes(),"foo".getBytes()));
assertFalse(Credential.byteEquals("foo".getBytes(),"fooo".getBytes()));
assertFalse(Credential.byteEquals("foo".getBytes(),"fo".getBytes()));
assertFalse(Credential.byteEquals("foo".getBytes(),"bar".getBytes()));
}
}

View File

@ -19,17 +19,13 @@
package org.eclipse.jetty.websocket.jsr356;
import java.lang.reflect.Method;
import java.util.concurrent.Executor;
import javax.websocket.ContainerProvider;
import javax.websocket.WebSocketContainer;
import org.eclipse.jetty.io.ByteBufferPool;
import org.eclipse.jetty.io.MappedByteBufferPool;
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.QueuedThreadPool;
import org.eclipse.jetty.util.thread.ShutdownThread;
import org.eclipse.jetty.websocket.api.WebSocketPolicy;
import org.eclipse.jetty.websocket.common.scopes.SimpleContainerScope;
@ -47,11 +43,9 @@ public class JettyClientContainerProvider extends ContainerProvider
private static final Logger LOG = Log.getLogger(JettyClientContainerProvider.class);
private static boolean useSingleton = false;
private static WebSocketContainer INSTANCE;
private static boolean useServerContainer = false;
private static Executor commonExecutor;
private static ByteBufferPool commonBufferPool;
private static WebSocketContainer INSTANCE;
private static Object lock = new Object();
/**
@ -185,28 +179,14 @@ public class JettyClientContainerProvider extends ContainerProvider
// Still no instance?
if (webSocketContainer == null)
{
if (commonExecutor == null)
{
QueuedThreadPool threadPool = new QueuedThreadPool();
String name = "Jsr356Client@" + hashCode();
threadPool.setName(name);
threadPool.setDaemon(true);
commonExecutor = threadPool;
}
if (commonBufferPool == null)
{
commonBufferPool = new MappedByteBufferPool();
}
SimpleContainerScope containerScope = new SimpleContainerScope(WebSocketPolicy.newClientPolicy(), commonBufferPool, commonExecutor, null);
SimpleContainerScope containerScope = new SimpleContainerScope(WebSocketPolicy.newClientPolicy());
ClientContainer clientContainer = new ClientContainer(containerScope);
if (contextHandler != null && contextHandler instanceof ContainerLifeCycle)
{
// Add as bean to contextHandler
// Allow startup to follow Jetty lifecycle
((ContainerLifeCycle) contextHandler).addBean(clientContainer);
((ContainerLifeCycle) contextHandler).addManaged(clientContainer);
}
else
{

View File

@ -247,7 +247,7 @@ public class DelayedStartClientOnServerTest
assertThat("Response", response, startsWith("Connected to ws://"));
List<String> threadNames = getThreadNames(server);
assertNoHttpClientPoolThreads(threadNames);
assertThat("Threads", threadNames, hasItem(containsString("Jsr356Client@")));
assertThat("Threads", threadNames, hasItem(containsString("WebSocketContainer@")));
}
finally
{

View File

@ -392,11 +392,7 @@ public class WebSocketClient extends ContainerLifeCycle implements WebSocketCont
if (LOG.isDebugEnabled())
LOG.debug("Stopping {}",this);
if (ShutdownThread.isRegistered(this))
{
ShutdownThread.deregister(this);
}
ShutdownThread.deregister(this);
super.doStop();

View File

@ -936,7 +936,7 @@
<dependency>
<groupId>org.eclipse.jetty.toolchain</groupId>
<artifactId>jetty-test-helper</artifactId>
<version>4.1</version>
<version>4.2</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.toolchain</groupId>

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();