| // |
| // ======================================================================== |
| // 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); |
| } |
| } |
| } |
| |
| } |