blob: 010023e22881985263d6c4b1bce787c306b19f8f [file] [log] [blame]
//
// ========================================================================
// 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);
}
}
}
}