|
21 | 21 | import java.util.Arrays;
|
22 | 22 | import java.util.Collection;
|
23 | 23 | import java.util.Collections;
|
24 |
| -import java.util.List; |
| 24 | +import java.util.concurrent.ExecutionException; |
25 | 25 | import java.util.concurrent.Future;
|
26 | 26 | import java.util.regex.Matcher;
|
27 | 27 | import java.util.regex.Pattern;
|
|
30 | 30 | import javax.servlet.ServletContextListener;
|
31 | 31 | import javax.servlet.ServletRegistration.Dynamic;
|
32 | 32 |
|
| 33 | +import org.apache.http.Header; |
33 | 34 | import org.apache.http.HttpResponse;
|
| 35 | +import org.apache.http.client.HttpClient; |
34 | 36 | import org.apache.http.conn.HttpHostConnectException;
|
| 37 | +import org.apache.http.impl.client.HttpClients; |
35 | 38 | import org.eclipse.jetty.server.Connector;
|
36 | 39 | import org.eclipse.jetty.server.Handler;
|
37 | 40 | import org.eclipse.jetty.server.Server;
|
@@ -189,19 +192,80 @@ void whenServerIsShuttingDownGracefullyThenNewConnectionsCannotBeMade() throws E
|
189 | 192 | registration.setAsyncSupported(true);
|
190 | 193 | });
|
191 | 194 | this.webServer.start();
|
192 |
| - Future<Object> request = initiateGetRequest("/blocking"); |
| 195 | + int port = this.webServer.getPort(); |
| 196 | + Future<Object> request = initiateGetRequest(port, "/blocking"); |
193 | 197 | blockingServlet.awaitQueue();
|
194 | 198 | Future<Boolean> shutdownResult = initiateGracefulShutdown();
|
195 |
| - // Jetty accepts one additional request after a connector has been told to stop |
196 |
| - // accepting requests |
197 |
| - Future<Object> unconnectableRequest1 = initiateGetRequest("/"); |
198 |
| - Future<Object> unconnectableRequest2 = initiateGetRequest("/"); |
| 199 | + Future<Object> unconnectableRequest = initiateGetRequest(port, "/"); |
199 | 200 | assertThat(shutdownResult.get()).isEqualTo(false);
|
200 | 201 | blockingServlet.admitOne();
|
201 |
| - assertThat(request.get()).isInstanceOf(HttpResponse.class); |
| 202 | + Object response = request.get(); |
| 203 | + assertThat(response).isInstanceOf(HttpResponse.class); |
| 204 | + assertThat(unconnectableRequest.get()).isInstanceOf(HttpHostConnectException.class); |
202 | 205 | this.webServer.stop();
|
203 |
| - List<Object> results = Arrays.asList(unconnectableRequest1.get(), unconnectableRequest2.get()); |
204 |
| - assertThat(results).anySatisfy((result) -> assertThat(result).isInstanceOf(HttpHostConnectException.class)); |
| 206 | + } |
| 207 | + |
| 208 | + @Test |
| 209 | + void whenServerIsShuttingDownGracefullyThenResponseToRequestOnIdleConnectionWillHaveAConnectionCloseHeader() |
| 210 | + throws Exception { |
| 211 | + AbstractServletWebServerFactory factory = getFactory(); |
| 212 | + Shutdown shutdown = new Shutdown(); |
| 213 | + shutdown.setGracePeriod(Duration.ofSeconds(5)); |
| 214 | + factory.setShutdown(shutdown); |
| 215 | + BlockingServlet blockingServlet = new BlockingServlet(); |
| 216 | + this.webServer = factory.getWebServer((context) -> { |
| 217 | + Dynamic registration = context.addServlet("blockingServlet", blockingServlet); |
| 218 | + registration.addMapping("/blocking"); |
| 219 | + registration.setAsyncSupported(true); |
| 220 | + }); |
| 221 | + this.webServer.start(); |
| 222 | + int port = this.webServer.getPort(); |
| 223 | + HttpClient client = HttpClients.createMinimal(); |
| 224 | + Future<Object> request = initiateGetRequest(client, port, "/blocking"); |
| 225 | + blockingServlet.awaitQueue(); |
| 226 | + blockingServlet.admitOne(); |
| 227 | + Object response = request.get(); |
| 228 | + assertThat(response).isInstanceOf(HttpResponse.class); |
| 229 | + assertThat(((HttpResponse) response).getStatusLine().getStatusCode()).isEqualTo(200); |
| 230 | + assertThat(((HttpResponse) response).getFirstHeader("Connection")).isNull(); |
| 231 | + this.webServer.shutDownGracefully(); |
| 232 | + request = initiateGetRequest(client, port, "/blocking"); |
| 233 | + blockingServlet.awaitQueue(); |
| 234 | + blockingServlet.admitOne(); |
| 235 | + response = request.get(); |
| 236 | + assertThat(response).isInstanceOf(HttpResponse.class); |
| 237 | + assertThat(((HttpResponse) response).getStatusLine().getStatusCode()).isEqualTo(200); |
| 238 | + assertThat(((HttpResponse) response).getFirstHeader("Connection")).isNotNull().extracting(Header::getValue) |
| 239 | + .isEqualTo("close"); |
| 240 | + this.webServer.stop(); |
| 241 | + } |
| 242 | + |
| 243 | + @Test |
| 244 | + void whenARequestCompletesAfterGracefulShutdownHasBegunThenItHasAConnectionCloseHeader() |
| 245 | + throws InterruptedException, ExecutionException { |
| 246 | + AbstractServletWebServerFactory factory = getFactory(); |
| 247 | + Shutdown shutdown = new Shutdown(); |
| 248 | + shutdown.setGracePeriod(Duration.ofSeconds(30)); |
| 249 | + factory.setShutdown(shutdown); |
| 250 | + BlockingServlet blockingServlet = new BlockingServlet(); |
| 251 | + this.webServer = factory.getWebServer((context) -> { |
| 252 | + Dynamic registration = context.addServlet("blockingServlet", blockingServlet); |
| 253 | + registration.addMapping("/blocking"); |
| 254 | + registration.setAsyncSupported(true); |
| 255 | + }); |
| 256 | + this.webServer.start(); |
| 257 | + int port = this.webServer.getPort(); |
| 258 | + Future<Object> request = initiateGetRequest(port, "/blocking"); |
| 259 | + blockingServlet.awaitQueue(); |
| 260 | + long start = System.currentTimeMillis(); |
| 261 | + Future<Boolean> shutdownResult = initiateGracefulShutdown(); |
| 262 | + blockingServlet.admitOne(); |
| 263 | + assertThat(shutdownResult.get()).isTrue(); |
| 264 | + long end = System.currentTimeMillis(); |
| 265 | + assertThat(end - start).isLessThanOrEqualTo(30000); |
| 266 | + Object requestResult = request.get(); |
| 267 | + assertThat(requestResult).isInstanceOf(HttpResponse.class); |
| 268 | + assertThat(((HttpResponse) requestResult).getFirstHeader("Connection").getValue()).isEqualTo("close"); |
205 | 269 | }
|
206 | 270 |
|
207 | 271 | private Ssl getSslSettings(String... enabledProtocols) {
|
|
0 commit comments