Skip to content

Commit 86e6ec0

Browse files
committed
Fix hang caused by race condition in test for reset of kept-alive connection
Previously, a race between the server starting to reject requests on a kept-alive connection and the request reaching the blocking servlet could result in a response never being sent. This commit updates the test to disable blocking once graceful shutdown with an in-flight request has being. Awaitility is then used to make a request on an idle kept-alive connection until it fails due to the connection reset. This may not happen immediately due to the aforementioned race.
1 parent 453ca01 commit 86e6ec0

File tree

2 files changed

+28
-15
lines changed

2 files changed

+28
-15
lines changed

spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/tomcat/TomcatServletWebServerFactoryTests.java

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@
6666
import org.apache.tomcat.JarScanFilter;
6767
import org.apache.tomcat.JarScanType;
6868
import org.assertj.core.api.ThrowableAssert.ThrowingCallable;
69+
import org.awaitility.Awaitility;
6970
import org.junit.jupiter.api.AfterEach;
7071
import org.junit.jupiter.api.Test;
7172
import org.mockito.ArgumentCaptor;
@@ -599,17 +600,21 @@ void whenServerIsShuttingDownARequestOnAnIdleConnectionResultsInConnectionReset(
599600
assertThat(keepAliveRequest.get()).isInstanceOf(HttpResponse.class);
600601
Future<Object> request = initiateGetRequest(port, "/blocking");
601602
blockingServlet.awaitQueue();
603+
blockingServlet.setBlocking(false);
602604
this.webServer.shutDownGracefully((result) -> {
603605
});
604-
Future<Object> idleConnectionRequest = initiateGetRequest(httpClient, port, "/blocking");
605-
blockingServlet.admitOne();
606-
Object response = request.get();
607-
assertThat(response).isInstanceOf(HttpResponse.class);
608-
Object idleConnectionRequestResult = idleConnectionRequest.get();
606+
Object idleConnectionRequestResult = Awaitility.await().until(() -> {
607+
Future<Object> idleConnectionRequest = initiateGetRequest(httpClient, port, "/blocking");
608+
Object result = idleConnectionRequest.get();
609+
return result;
610+
}, (result) -> result instanceof Exception);
609611
assertThat(idleConnectionRequestResult).isInstanceOfAny(SocketException.class, NoHttpResponseException.class);
610612
if (idleConnectionRequestResult instanceof SocketException) {
611613
assertThat((SocketException) idleConnectionRequestResult).hasMessage("Connection reset");
612614
}
615+
blockingServlet.admitOne();
616+
Object response = request.get();
617+
assertThat(response).isInstanceOf(HttpResponse.class);
613618
this.webServer.stop();
614619
}
615620

spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/server/AbstractServletWebServerFactoryTests.java

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1445,22 +1445,26 @@ protected static class BlockingServlet extends HttpServlet {
14451445

14461446
private final BlockingQueue<CyclicBarrier> barriers = new ArrayBlockingQueue<>(10);
14471447

1448+
protected volatile boolean blocking = true;
1449+
14481450
public BlockingServlet() {
14491451

14501452
}
14511453

14521454
@Override
14531455
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
1454-
CyclicBarrier barrier = new CyclicBarrier(2);
1455-
this.barriers.add(barrier);
1456-
try {
1457-
barrier.await();
1458-
}
1459-
catch (InterruptedException ex) {
1460-
Thread.currentThread().interrupt();
1461-
}
1462-
catch (BrokenBarrierException ex) {
1463-
throw new ServletException(ex);
1456+
if (this.blocking) {
1457+
CyclicBarrier barrier = new CyclicBarrier(2);
1458+
this.barriers.add(barrier);
1459+
try {
1460+
barrier.await();
1461+
}
1462+
catch (InterruptedException ex) {
1463+
Thread.currentThread().interrupt();
1464+
}
1465+
catch (BrokenBarrierException ex) {
1466+
throw new ServletException(ex);
1467+
}
14641468
}
14651469
}
14661470

@@ -1491,6 +1495,10 @@ public void awaitQueue(int size) throws InterruptedException {
14911495
}
14921496
}
14931497

1498+
public void setBlocking(boolean blocking) {
1499+
this.blocking = blocking;
1500+
}
1501+
14941502
}
14951503

14961504
static class BlockingAsyncServlet extends HttpServlet {

0 commit comments

Comments
 (0)