Fixes #9720 - Http2Session.streamIdleTimeout should permit being disabled

Now allowing to specify a negative value for AbstractHTTP2ServerConnectionFactory.streamIdleTimeout, while 0 implies to use the default value (from the EndPoint).

Signed-off-by: Simone Bordet <simone.bordet@gmail.com>
This commit is contained in:
Simone Bordet 2023-07-29 18:51:24 +02:00
parent 87c24e7258
commit afef05a413
3 changed files with 85 additions and 3 deletions

View File

@ -26,6 +26,7 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.http.MetaData;
import org.eclipse.jetty.http2.FlowControlStrategy;
@ -676,6 +677,78 @@ public class IdleTimeoutTest extends AbstractTest
assertThat(((ISession)client).updateSendWindow(0), Matchers.greaterThan(0));
}
@Test
public void testDisableStreamIdleTimeout() throws Exception
{
// Set the stream idle timeout to a negative value to disable it.
long streamIdleTimeout = -1;
start(new ServerSessionListener.Adapter()
{
@Override
public Stream.Listener onNewStream(Stream stream, HeadersFrame frame)
{
return new Stream.Listener.Adapter()
{
@Override
public void onData(Stream stream, DataFrame frame, Callback callback)
{
callback.succeeded();
if (frame.isEndStream())
{
MetaData.Response response = new MetaData.Response(HttpVersion.HTTP_2, HttpStatus.OK_200, HttpFields.EMPTY);
stream.headers(new HeadersFrame(stream.getId(), response, null, true));
}
}
};
}
}, h2 -> h2.setStreamIdleTimeout(streamIdleTimeout));
connector.setIdleTimeout(idleTimeout);
CountDownLatch responseLatch = new CountDownLatch(2);
CountDownLatch resetLatch = new CountDownLatch(1);
Session session = newClient(new Session.Listener.Adapter());
MetaData.Request metaData1 = newRequest("GET", "/1", HttpFields.EMPTY);
Stream stream1 = session.newStream(new HeadersFrame(metaData1, null, false), new Stream.Listener.Adapter()
{
@Override
public void onHeaders(Stream stream, HeadersFrame frame)
{
responseLatch.countDown();
}
@Override
public void onReset(Stream stream, ResetFrame frame)
{
resetLatch.countDown();
}
}).get(5, TimeUnit.SECONDS);
MetaData.Request metaData2 = newRequest("GET", "/2", HttpFields.EMPTY);
Stream stream2 = session.newStream(new HeadersFrame(metaData2, null, false), new Stream.Listener.Adapter()
{
@Override
public void onHeaders(Stream stream, HeadersFrame frame)
{
responseLatch.countDown();
}
}).get(5, TimeUnit.SECONDS);
// Keep the connection busy with the stream2, stream1 must not idle timeout.
for (int i = 0; i < 3; ++i)
{
Thread.sleep(idleTimeout / 2);
stream2.data(new DataFrame(stream2.getId(), ByteBuffer.allocate(64), false));
}
// Stream1 must not have idle timed out.
assertFalse(resetLatch.await(idleTimeout / 2, TimeUnit.MILLISECONDS));
// Finish the streams.
stream1.data(new DataFrame(stream1.getId(), ByteBuffer.allocate(128), true));
stream2.data(new DataFrame(stream2.getId(), ByteBuffer.allocate(64), true));
assertTrue(responseLatch.await(5, TimeUnit.SECONDS));
}
private void sleep(long value)
{
try

View File

@ -122,7 +122,6 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
this.maxLocalStreams = -1;
this.maxRemoteStreams = -1;
this.localStreamIds.set(initialStreamId);
this.streamIdleTimeout = endPoint.getIdleTimeout();
this.sendWindow.set(FlowControlStrategy.DEFAULT_WINDOW_SIZE);
this.recvWindow.set(FlowControlStrategy.DEFAULT_WINDOW_SIZE);
this.writeThreshold = 32 * 1024;

View File

@ -198,6 +198,15 @@ public abstract class AbstractHTTP2ServerConnectionFactory extends AbstractConne
return streamIdleTimeout;
}
/**
* <p>Sets the HTTP/2 stream idle timeout.</p>
* <p>Value {@code -1} disables the idle timeout,
* value {@code 0} implies using the default idle timeout,
* positive values specify the idle timeout in milliseconds.</p>
*
* @param streamIdleTimeout the idle timeout in milliseconds,
* {@code 0} for the default, {@code -1} to disable the idle timeout
*/
public void setStreamIdleTimeout(long streamIdleTimeout)
{
this.streamIdleTimeout = streamIdleTimeout;
@ -331,8 +340,9 @@ public abstract class AbstractHTTP2ServerConnectionFactory extends AbstractConne
// the typical case is that the connection will be busier and the
// stream idle timeout will expire earlier than the connection's.
long streamIdleTimeout = getStreamIdleTimeout();
if (streamIdleTimeout > 0)
session.setStreamIdleTimeout(streamIdleTimeout);
if (streamIdleTimeout == 0)
streamIdleTimeout = endPoint.getIdleTimeout();
session.setStreamIdleTimeout(streamIdleTimeout);
session.setInitialSessionRecvWindow(getInitialSessionRecvWindow());
session.setWriteThreshold(getHttpConfiguration().getOutputBufferSize());
session.setConnectProtocolEnabled(isConnectProtocolEnabled());