diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/jetty/JettyReactiveWebServerFactory.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/jetty/JettyReactiveWebServerFactory.java index fb195d6c55f..e2b014f703a 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/jetty/JettyReactiveWebServerFactory.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/jetty/JettyReactiveWebServerFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors. + * Copyright 2012-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -169,6 +169,7 @@ public class JettyReactiveWebServerFactory extends AbstractReactiveWebServerFact InetSocketAddress address = new InetSocketAddress(getAddress(), port); Server server = new Server(getThreadPool()); server.addConnector(createConnector(address, server)); + server.setStopTimeout(0); ServletHolder servletHolder = new ServletHolder(servlet); servletHolder.setAsyncSupported(true); ServletContextHandler contextHandler = new ServletContextHandler(server, "/", false, false); diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/jetty/JettyServletWebServerFactory.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/jetty/JettyServletWebServerFactory.java index fde52183670..fb77852da07 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/jetty/JettyServletWebServerFactory.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/jetty/JettyServletWebServerFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors. + * Copyright 2012-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -165,6 +165,7 @@ public class JettyServletWebServerFactory extends AbstractServletWebServerFactor private Server createServer(InetSocketAddress address) { Server server = new Server(getThreadPool()); server.setConnectors(new Connector[] { createConnector(address, server) }); + server.setStopTimeout(0); return server; } diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/reactive/server/AbstractReactiveWebServerFactoryTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/reactive/server/AbstractReactiveWebServerFactoryTests.java index 11f379fa5aa..779a30ebc7f 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/reactive/server/AbstractReactiveWebServerFactoryTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/reactive/server/AbstractReactiveWebServerFactoryTests.java @@ -437,6 +437,30 @@ public abstract class AbstractReactiveWebServerFactoryTests { blockingHandler.completeOne(); } + @Test + void whenARequestIsActiveAfterGracefulShutdownEndsThenStopWillComplete() throws InterruptedException { + AbstractReactiveWebServerFactory factory = getFactory(); + factory.setShutdown(Shutdown.GRACEFUL); + BlockingHandler blockingHandler = new BlockingHandler(); + this.webServer = factory.getWebServer(blockingHandler); + this.webServer.start(); + Mono> request = getWebClient(this.webServer.getPort()).build().get().retrieve() + .toBodilessEntity(); + AtomicReference> responseReference = new AtomicReference<>(); + CountDownLatch responseLatch = new CountDownLatch(1); + request.subscribe((response) -> { + responseReference.set(response); + responseLatch.countDown(); + }); + blockingHandler.awaitQueue(); + AtomicReference result = new AtomicReference<>(); + this.webServer.shutDownGracefully(result::set); + this.webServer.stop(); + Awaitility.await().atMost(Duration.ofSeconds(30)) + .until(() -> GracefulShutdownResult.REQUESTS_ACTIVE == result.get()); + blockingHandler.completeOne(); + } + @Test void whenARequestIsActiveThenStopWillComplete() throws InterruptedException, BrokenBarrierException { AbstractReactiveWebServerFactory factory = getFactory(); diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/server/AbstractServletWebServerFactoryTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/server/AbstractServletWebServerFactoryTests.java index 965e63c3098..1807c310e6a 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/server/AbstractServletWebServerFactoryTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/server/AbstractServletWebServerFactoryTests.java @@ -1119,6 +1119,31 @@ public abstract class AbstractServletWebServerFactoryTests { } } + @Test + void whenARequestIsActiveAfterGracefulShutdownEndsThenStopWillComplete() + throws InterruptedException, BrokenBarrierException { + AbstractServletWebServerFactory factory = getFactory(); + factory.setShutdown(Shutdown.GRACEFUL); + BlockingServlet blockingServlet = new BlockingServlet(); + this.webServer = factory + .getWebServer((context) -> context.addServlet("blockingServlet", blockingServlet).addMapping("/")); + this.webServer.start(); + int port = this.webServer.getPort(); + initiateGetRequest(port, "/"); + blockingServlet.awaitQueue(); + AtomicReference result = new AtomicReference<>(); + this.webServer.shutDownGracefully(result::set); + this.webServer.stop(); + Awaitility.await().atMost(Duration.ofSeconds(30)) + .until(() -> GracefulShutdownResult.REQUESTS_ACTIVE == result.get()); + try { + blockingServlet.admitOne(); + } + catch (RuntimeException ex) { + + } + } + protected Future initiateGetRequest(int port, String path) { return initiateGetRequest(HttpClients.createMinimal(), port, path); }