Skip to content

Commit da50e06

Browse files
committed
Fixes #6254 - Total timeout not enforced for queued requests.
Fixed logic in HttpDestination.RequestTimeouts, where now a timeout is scheduled only when the expiration time is less than the existing one. Signed-off-by: Simone Bordet <simone.bordet@gmail.com>
1 parent 5f23689 commit da50e06

File tree

3 files changed

+88
-3
lines changed

3 files changed

+88
-3
lines changed

jetty-client/src/main/java/org/eclipse/jetty/client/HttpDestination.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -597,7 +597,7 @@ private void schedule(long expiresAt)
597597
// When the timeout expires, scan the exchange queue for the next
598598
// earliest exchange that may expire, and reschedule a new timeout.
599599
long earliest = earliestTimeout.getAndUpdate(t -> Math.min(t, expiresAt));
600-
if (expiresAt != earliest)
600+
if (expiresAt < earliest)
601601
{
602602
// A new request expires earlier than previous requests, schedule it.
603603
long delay = Math.max(0, expiresAt - System.nanoTime());

tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/HttpClientTimeoutTest.java

Lines changed: 80 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,12 +54,14 @@
5454
import org.eclipse.jetty.util.FuturePromise;
5555
import org.eclipse.jetty.util.IO;
5656
import org.eclipse.jetty.util.ssl.SslContextFactory;
57+
import org.hamcrest.Matchers;
5758
import org.junit.jupiter.api.Assumptions;
5859
import org.junit.jupiter.params.ParameterizedTest;
5960
import org.junit.jupiter.params.provider.ArgumentsSource;
6061
import org.opentest4j.TestAbortedException;
6162

6263
import static org.eclipse.jetty.http.client.Transport.UNIX_SOCKET;
64+
import static org.hamcrest.MatcherAssert.assertThat;
6365
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
6466
import static org.junit.jupiter.api.Assertions.assertEquals;
6567
import static org.junit.jupiter.api.Assertions.assertFalse;
@@ -501,6 +503,83 @@ protected void service(String target, org.eclipse.jetty.server.Request jettyRequ
501503
assertTrue(latch.await(5, TimeUnit.SECONDS));
502504
}
503505

506+
@ParameterizedTest
507+
@ArgumentsSource(TransportProvider.class)
508+
public void testRequestQueuedDoesNotCancelTimeoutOfQueuedRequests(Transport transport) throws Exception
509+
{
510+
init(transport);
511+
512+
CountDownLatch serverLatch = new CountDownLatch(1);
513+
scenario.start(new EmptyServerHandler()
514+
{
515+
@Override
516+
protected void service(String target, org.eclipse.jetty.server.Request jettyRequest, HttpServletRequest request, HttpServletResponse response) throws IOException
517+
{
518+
if (request.getRequestURI().startsWith("/one"))
519+
{
520+
try
521+
{
522+
serverLatch.await();
523+
}
524+
catch (InterruptedException x)
525+
{
526+
throw new InterruptedIOException();
527+
}
528+
}
529+
}
530+
});
531+
532+
scenario.client.setMaxConnectionsPerDestination(1);
533+
scenario.setMaxRequestsPerConnection(1);
534+
535+
// Send the first request so that the others get queued.
536+
CountDownLatch latch1 = new CountDownLatch(1);
537+
scenario.client.newRequest(scenario.newURI())
538+
.path("/one")
539+
.send(result ->
540+
{
541+
assertTrue(result.isSucceeded());
542+
assertEquals(HttpStatus.OK_200, result.getResponse().getStatus());
543+
latch1.countDown();
544+
});
545+
546+
// Queue a second request, it should expire in the queue.
547+
long timeout = 1000;
548+
CountDownLatch latch2 = new CountDownLatch(1);
549+
scenario.client.newRequest(scenario.newURI())
550+
.path("/two")
551+
.timeout(2 * timeout, TimeUnit.MILLISECONDS)
552+
.send(result ->
553+
{
554+
assertTrue(result.isFailed());
555+
assertThat(result.getFailure(), Matchers.instanceOf(TimeoutException.class));
556+
latch2.countDown();
557+
});
558+
559+
Thread.sleep(timeout);
560+
561+
// Queue a third request, it should not reset the timeout of the second request.
562+
CountDownLatch latch3 = new CountDownLatch(1);
563+
scenario.client.newRequest(scenario.newURI())
564+
.path("/three")
565+
.timeout(2 * timeout, TimeUnit.MILLISECONDS)
566+
.send(result ->
567+
{
568+
assertTrue(result.isSucceeded());
569+
assertEquals(HttpStatus.OK_200, result.getResponse().getStatus());
570+
latch3.countDown();
571+
});
572+
573+
// We have already slept a timeout, expect the second request to be back in another timeout.
574+
assertTrue(latch2.await(2 * timeout, TimeUnit.MILLISECONDS));
575+
576+
// Release the first request so the third can be served as well.
577+
serverLatch.countDown();
578+
579+
assertTrue(latch1.await(2 * timeout, TimeUnit.MILLISECONDS));
580+
assertTrue(latch3.await(2 * timeout, TimeUnit.MILLISECONDS));
581+
}
582+
504583
private void assumeConnectTimeout(String host, int port, int connectTimeout)
505584
{
506585
try (Socket socket = new Socket())
@@ -516,7 +595,6 @@ private void assumeConnectTimeout(String host, int port, int connectTimeout)
516595
catch (SocketTimeoutException x)
517596
{
518597
// Expected timeout during connect, continue the test.
519-
return;
520598
}
521599
catch (Throwable x)
522600
{
@@ -525,7 +603,7 @@ private void assumeConnectTimeout(String host, int port, int connectTimeout)
525603
}
526604
}
527605

528-
private class TimeoutHandler extends AbstractHandler
606+
private static class TimeoutHandler extends AbstractHandler
529607
{
530608
private final long timeout;
531609

tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/TransportScenario.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,13 @@ public void setServerIdleTimeout(long idleTimeout)
286286
setConnectionIdleTimeout(idleTimeout);
287287
}
288288

289+
public void setMaxRequestsPerConnection(int maxRequestsPerConnection)
290+
{
291+
AbstractHTTP2ServerConnectionFactory h2 = connector.getConnectionFactory(AbstractHTTP2ServerConnectionFactory.class);
292+
if (h2 != null)
293+
h2.setMaxConcurrentStreams(maxRequestsPerConnection);
294+
}
295+
289296
public void start(Handler handler) throws Exception
290297
{
291298
start(handler, null);

0 commit comments

Comments
 (0)