// // ======================================================================== // Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.server; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.lessThan; import static org.junit.Assert.assertThat; import static org.junit.Assume.assumeTrue; import java.io.EOFException; import java.io.IOException; import java.io.InputStream; import java.net.ConnectException; import java.net.Socket; import java.nio.channels.ClosedChannelException; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.eclipse.jetty.io.EofException; import org.eclipse.jetty.server.handler.AbstractHandler; import org.eclipse.jetty.server.handler.StatisticsHandler; import org.eclipse.jetty.toolchain.test.OS; import org.eclipse.jetty.util.IO; import org.hamcrest.Matchers; import org.junit.Assert; import org.junit.Test; public class GracefulStopTest { /** * Test of standard graceful timeout mechanism when a block request does * not complete * @throws Exception on test failure */ @Test public void testGracefulNoWaiter() throws Exception { Server server= new Server(); server.setStopTimeout(1000); ServerConnector connector = new ServerConnector(server); connector.setPort(0); server.addConnector(connector); TestHandler handler = new TestHandler(); server.setHandler(handler); server.start(); final int port=connector.getLocalPort(); Socket client = new Socket("127.0.0.1", port); client.getOutputStream().write(( "POST / HTTP/1.0\r\n"+ "Host: localhost:"+port+"\r\n" + "Content-Type: plain/text\r\n" + "Content-Length: 10\r\n" + "\r\n"+ "12345" ).getBytes()); client.getOutputStream().flush(); handler.latch.await(); long start = System.nanoTime(); server.stop(); long stop = System.nanoTime(); // No Graceful waiters assertThat(TimeUnit.NANOSECONDS.toMillis(stop-start),lessThan(900L)); assertThat(client.getInputStream().read(),Matchers.is(-1)); assertThat(handler.handling.get(),Matchers.is(false)); assertThat(handler.thrown.get(), Matchers.anyOf( instanceOf(ClosedChannelException.class), instanceOf(EofException.class), instanceOf(EOFException.class)) ); client.close(); } /** * Test of standard graceful timeout mechanism when a block request does * not complete * @throws Exception on test failure */ @Test public void testGracefulTimeout() throws Exception { Server server= new Server(); server.setStopTimeout(1000); ServerConnector connector = new ServerConnector(server); connector.setPort(0); server.addConnector(connector); TestHandler handler = new TestHandler(); StatisticsHandler stats = new StatisticsHandler(); server.setHandler(stats); stats.setHandler(handler); server.start(); final int port=connector.getLocalPort(); Socket client = new Socket("127.0.0.1", port); client.getOutputStream().write(( "POST / HTTP/1.0\r\n"+ "Host: localhost:"+port+"\r\n" + "Content-Type: plain/text\r\n" + "Content-Length: 10\r\n" + "\r\n"+ "12345" ).getBytes()); client.getOutputStream().flush(); handler.latch.await(); long start = System.nanoTime(); try { server.stop(); Assert.fail(); } catch(TimeoutException e) { long stop = System.nanoTime(); // No Graceful waiters assertThat(TimeUnit.NANOSECONDS.toMillis(stop-start),greaterThan(900L)); } assertThat(client.getInputStream().read(),Matchers.is(-1)); assertThat(handler.handling.get(),Matchers.is(false)); assertThat(handler.thrown.get(),instanceOf(ClosedChannelException.class)); client.close(); } /** * Test of standard graceful timeout mechanism when a block request does * complete. Note that even though the request completes after 100ms, the * stop always takes 1000ms * @throws Exception on test failure */ @Test public void testGracefulComplete() throws Exception { assumeTrue(!OS.IS_WINDOWS); Server server= new Server(); server.setStopTimeout(10000); ServerConnector connector = new ServerConnector(server); connector.setPort(0); server.addConnector(connector); TestHandler handler = new TestHandler(); StatisticsHandler stats = new StatisticsHandler(); server.setHandler(stats); stats.setHandler(handler); server.start(); final int port=connector.getLocalPort(); try(final Socket client1 = new Socket("127.0.0.1", port);final Socket client2 = new Socket("127.0.0.1", port);) { client1.getOutputStream().write(( "POST / HTTP/1.0\r\n"+ "Host: localhost:"+port+"\r\n" + "Content-Type: plain/text\r\n" + "Content-Length: 10\r\n" + "\r\n"+ "12345" ).getBytes()); client1.getOutputStream().flush(); handler.latch.await(); new Thread() { @Override public void run() { long now = TimeUnit.NANOSECONDS.toMillis(System.nanoTime()); long end = now+500; try { Thread.sleep(100); // Try creating a new connection try { new Socket("127.0.0.1", port); throw new IllegalStateException(); } catch(ConnectException e) { } // Try another request on existing connection client2.getOutputStream().write(( "GET / HTTP/1.0\r\n"+ "Host: localhost:"+port+"\r\n" + "\r\n" ).getBytes()); client2.getOutputStream().flush(); String response2 = IO.toString(client2.getInputStream()); assertThat(response2, containsString(" 503 ")); now = TimeUnit.NANOSECONDS.toMillis(System.nanoTime()); Thread.sleep(Math.max(1,end-now)); client1.getOutputStream().write("567890".getBytes()); } catch(Exception e) { e.printStackTrace(); } } }.start(); long start = System.nanoTime(); server.stop(); long stop = System.nanoTime(); assertThat(TimeUnit.NANOSECONDS.toMillis(stop-start),greaterThan(490L)); assertThat(TimeUnit.NANOSECONDS.toMillis(stop-start),lessThan(10000L)); String response = IO.toString(client1.getInputStream()); assertThat(handler.handling.get(),Matchers.is(false)); assertThat(response, containsString(" 200 OK")); assertThat(response, containsString("read 10/10")); assertThat(stats.getRequests(),Matchers.is(2)); assertThat(stats.getResponses5xx(),Matchers.is(1)); } } static class TestHandler extends AbstractHandler { final CountDownLatch latch = new CountDownLatch(1); final AtomicReference<Throwable> thrown = new AtomicReference<Throwable>(); final AtomicBoolean handling = new AtomicBoolean(false); @Override public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { handling.set(true); latch.countDown(); int c=0; try { int content_length = request.getContentLength(); InputStream in = request.getInputStream(); while(true) { if (in.read()<0) break; c++; } baseRequest.setHandled(true); response.setStatus(200); response.getWriter().printf("read %d/%d%n",c,content_length); } catch(Throwable th) { thrown.set(th); } finally { handling.set(false); } } } }