474634 - AsyncListener.onError() handling.

Removed special termination case handling. Unhandle can be avoided with a break loop.
Replaced actions COMPLETING and COMPLETED with COMPLETE (which is an action)

Refactored test harness to use a static history array rather than headers
This commit is contained in:
Greg Wilkins 2015-08-13 16:22:02 +10:00
parent 79086f3fe3
commit d1aa9ce993
3 changed files with 339 additions and 340 deletions

View File

@ -275,13 +275,8 @@ public class HttpChannel implements Runnable, HttpOutput.Interceptor
// The loop is controlled by the call to async.unhandle in the // The loop is controlled by the call to async.unhandle in the
// finally block below. Unhandle will return false only if an async dispatch has // finally block below. Unhandle will return false only if an async dispatch has
// already happened when unhandle is called. // already happened when unhandle is called.
while (!getServer().isStopped()) loop: while (!getServer().isStopped())
{ {
// Early exit out of the loop if the action
// is a terminal one to skip unhandle().
if (action == Action.TERMINATED || action == Action.WAIT)
break;
boolean error=false; boolean error=false;
try try
{ {
@ -290,6 +285,10 @@ public class HttpChannel implements Runnable, HttpOutput.Interceptor
switch(action) switch(action)
{ {
case TERMINATED:
case WAIT:
break loop;
case DISPATCH: case DISPATCH:
{ {
if (!_request.hasMetaData()) if (!_request.hasMetaData())
@ -379,7 +378,7 @@ public class HttpChannel implements Runnable, HttpOutput.Interceptor
break; break;
} }
case COMPLETING: case COMPLETE:
{ {
try try
{ {
@ -387,33 +386,18 @@ public class HttpChannel implements Runnable, HttpOutput.Interceptor
_response.sendError(404); _response.sendError(404);
else else
_response.closeOutput(); _response.closeOutput();
// _state=COMPLETE;
} }
catch (Throwable x) finally
{
// state = ERROR;
}
break;
// async.complete();
// state -> COMPLETING
// flush output
// try { flush(); state -> COMPLETED; }
// catch (x) { state -> ERROR }
// unhandle();
// COMPLETED -> call asyncListeners.onComplete() -> TERMINATED
// ERROR -> call asyncListeners.onError(); -> TERMINATED
// unhandle();
// TERMINATED -> break out of loop.
}
case COMPLETED:
{ {
_state.onComplete(); _state.onComplete();
}
// TODO: verify this code is needed and whether // TODO: verify this code is needed and whether
// TODO: it's needed for onError() case too. // TODO: it's needed for onError() case too.
_request.setHandled(true); _request.setHandled(true);
onCompleted(); onCompleted();
// TODO: set action to TERMINATED.
break; break loop;
} }
default: default:
@ -458,18 +442,17 @@ public class HttpChannel implements Runnable, HttpOutput.Interceptor
handleException(e); handleException(e);
} }
} }
finally
{
if (error && _state.isAsyncStarted()) if (error && _state.isAsyncStarted())
_state.errorComplete(); _state.errorComplete();
action = _state.unhandle(); action = _state.unhandle();
} }
}
if (LOG.isDebugEnabled()) if (LOG.isDebugEnabled())
LOG.debug("{} handle exit, result {}", this, action); LOG.debug("{} handle exit, result {}", this, action);
return action!=Action.WAIT; boolean suspended=action==Action.WAIT;
return !suspended;
} }
/** /**
@ -611,7 +594,7 @@ public class HttpChannel implements Runnable, HttpOutput.Interceptor
finally finally
{ {
// TODO: review whether it's the right state to check. // TODO: review whether it's the right state to check.
if (_state.unhandle()==Action.COMPLETING) if (_state.unhandle()==Action.COMPLETE)
_state.onComplete(); _state.onComplete();
else else
throw new IllegalStateException(); // TODO: don't throw from finally blocks ! throw new IllegalStateException(); // TODO: don't throw from finally blocks !

View File

@ -70,8 +70,7 @@ public class HttpChannelState
ASYNC_ERROR, // handle an async error ASYNC_ERROR, // handle an async error
WRITE_CALLBACK, // handle an IO write callback WRITE_CALLBACK, // handle an IO write callback
READ_CALLBACK, // handle an IO read callback READ_CALLBACK, // handle an IO read callback
COMPLETING, // Completing the response COMPLETE, // Complete the response
COMPLETED, // Response completed
TERMINATED, // No further actions TERMINATED, // No further actions
WAIT, // Wait for further events WAIT, // Wait for further events
} }
@ -197,10 +196,10 @@ public class HttpChannelState
return Action.DISPATCH; return Action.DISPATCH;
case COMPLETING: case COMPLETING:
return Action.COMPLETING; return Action.COMPLETE;
case COMPLETED: case COMPLETED:
return Action.WAIT; return Action.TERMINATED;
case ASYNC_WOKEN: case ASYNC_WOKEN:
if (_asyncReadPossible) if (_asyncReadPossible)
@ -210,7 +209,6 @@ public class HttpChannelState
return Action.READ_CALLBACK; return Action.READ_CALLBACK;
} }
// TODO refactor the same as read
if (_asyncWrite) if (_asyncWrite)
{ {
_state=State.ASYNC_IO; _state=State.ASYNC_IO;
@ -225,7 +223,7 @@ public class HttpChannelState
{ {
case COMPLETE: case COMPLETE:
_state=State.COMPLETING; _state=State.COMPLETING;
return Action.COMPLETING; return Action.COMPLETE;
case DISPATCH: case DISPATCH:
_state=State.DISPATCHED; _state=State.DISPATCHED;
_async=null; _async=null;
@ -241,15 +239,21 @@ public class HttpChannelState
case ERRORING: case ERRORING:
_state=State.DISPATCHED; _state=State.DISPATCHED;
return Action.ASYNC_ERROR; return Action.ASYNC_ERROR;
default: default:
throw new IllegalStateException(String.valueOf(async)); throw new IllegalStateException(getStatusStringLocked());
} }
} }
return Action.WAIT; return Action.WAIT;
case ASYNC_IO:
case ASYNC_WAIT:
case DISPATCHED:
case UPGRADED:
default: default:
return Action.WAIT; throw new IllegalStateException(getStatusStringLocked());
} }
} }
} }
@ -316,7 +320,7 @@ public class HttpChannelState
switch(_state) switch(_state)
{ {
case COMPLETED: case COMPLETED:
return Action.COMPLETED; return Action.TERMINATED;
case DISPATCHED: case DISPATCHED:
case ASYNC_IO: case ASYNC_IO:
@ -334,7 +338,7 @@ public class HttpChannelState
case COMPLETE: case COMPLETE:
_state=State.COMPLETING; _state=State.COMPLETING;
_async=null; _async=null;
action=Action.COMPLETING; action=Action.COMPLETE;
break; break;
case DISPATCH: case DISPATCH:
@ -379,25 +383,25 @@ public class HttpChannelState
case ERRORING: case ERRORING:
_state=State.DISPATCHED; _state=State.DISPATCHED;
action=Action.ERROR_DISPATCH; action=Action.ASYNC_ERROR;
break; break;
case ERRORED: case ERRORED:
_state=State.DISPATCHED; _state=State.DISPATCHED;
action=Action.ASYNC_ERROR; action=Action.ERROR_DISPATCH;
_async=null; _async=null;
break; break;
default: default:
_state=State.COMPLETING; _state=State.COMPLETING;
action=Action.COMPLETING; action=Action.COMPLETE;
break; break;
} }
} }
else else
{ {
_state=State.COMPLETING; _state=State.COMPLETING;
action=Action.COMPLETING; action=Action.COMPLETE;
} }
} }

View File

@ -26,6 +26,8 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Timer; import java.util.Timer;
import java.util.TimerTask; import java.util.TimerTask;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import javax.servlet.AsyncContext; import javax.servlet.AsyncContext;
import javax.servlet.AsyncEvent; import javax.servlet.AsyncEvent;
@ -59,8 +61,10 @@ import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.startsWith; import static org.hamcrest.Matchers.startsWith;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat; import static org.junit.Assert.assertThat;
@ -76,6 +80,8 @@ public class AsyncServletTest
protected List<String> _log; protected List<String> _log;
protected int _expectedLogs; protected int _expectedLogs;
protected String _expectedCode; protected String _expectedCode;
protected static List<String> __history=new ArrayList<>();
protected static CountDownLatch __latch;
@Before @Before
public void setUp() throws Exception public void setUp() throws Exception
@ -105,6 +111,8 @@ public class AsyncServletTest
_servletHandler.addServletWithMapping(new ServletHolder(new FwdServlet()),"/fwd/*"); _servletHandler.addServletWithMapping(new ServletHolder(new FwdServlet()),"/fwd/*");
_server.start(); _server.start();
_port=_connector.getLocalPort(); _port=_connector.getLocalPort();
__history.clear();
__latch=new CountDownLatch(1);
} }
@After @After
@ -119,26 +127,26 @@ public class AsyncServletTest
public void testNormal() throws Exception public void testNormal() throws Exception
{ {
String response=process(null,null); String response=process(null,null);
assertThat(response,startsWith("HTTP/1.1 200 OK\r\n")); assertThat(response,startsWith("HTTP/1.1 200 OK"));
assertContains( assertThat(__history,contains(
"history: REQUEST /ctx/path/info\r\n"+ "REQUEST /ctx/path/info",
"history: initial\r\n",response); "initial"));
assertContains("NORMAL",response); assertContains("NORMAL",response);
assertNotContains("history: onTimeout",response); assertFalse(__history.contains("onTimeout"));
assertNotContains("history: onComplete",response); assertFalse(__history.contains("onComplete"));
} }
@Test @Test
public void testSleep() throws Exception public void testSleep() throws Exception
{ {
String response=process("sleep=200",null); String response=process("sleep=200",null);
assertThat(response,startsWith("HTTP/1.1 200 OK\r\n")); assertThat(response,startsWith("HTTP/1.1 200 OK"));
assertContains( assertThat(__history,contains(
"history: REQUEST /ctx/path/info\r\n"+ "REQUEST /ctx/path/info",
"history: initial\r\n",response); "initial"));
assertContains("SLEPT",response); assertContains("SLEPT",response);
assertNotContains("history: onTimeout",response); assertFalse(__history.contains("onTimeout"));
assertNotContains("history: onComplete",response); assertFalse(__history.contains("onComplete"));
} }
@Test @Test
@ -147,14 +155,14 @@ public class AsyncServletTest
_expectedCode="500 "; _expectedCode="500 ";
String response=process("start=200",null); String response=process("start=200",null);
Assert.assertThat(response,Matchers.startsWith("HTTP/1.1 500 Async Timeout")); Assert.assertThat(response,Matchers.startsWith("HTTP/1.1 500 Async Timeout"));
assertContains( assertThat(__history,contains(
"history: REQUEST /ctx/path/info\r\n"+ "REQUEST /ctx/path/info",
"history: initial\r\n"+ "initial",
"history: start\r\n"+ "start",
"history: onTimeout\r\n"+ "onTimeout",
"history: ERROR /ctx/path/info\r\n"+ "ERROR /ctx/path/info",
"history: !initial\r\n"+ "!initial",
"history: onComplete\r\n",response); "onComplete"));
assertContains("ERROR DISPATCH: /ctx/path/info",response); assertContains("ERROR DISPATCH: /ctx/path/info",response);
} }
@ -163,16 +171,16 @@ public class AsyncServletTest
public void testStartOnTimeoutDispatch() throws Exception public void testStartOnTimeoutDispatch() throws Exception
{ {
String response=process("start=200&timeout=dispatch",null); String response=process("start=200&timeout=dispatch",null);
assertThat(response,startsWith("HTTP/1.1 200 OK\r\n")); assertThat(response,startsWith("HTTP/1.1 200 OK"));
assertContains( assertThat(__history,contains(
"history: REQUEST /ctx/path/info\r\n"+ "REQUEST /ctx/path/info",
"history: initial\r\n"+ "initial",
"history: start\r\n"+ "start",
"history: onTimeout\r\n"+ "onTimeout",
"history: dispatch\r\n"+ "dispatch",
"history: ASYNC /ctx/path/info\r\n"+ "ASYNC /ctx/path/info",
"history: !initial\r\n"+ "!initial",
"history: onComplete\r\n",response); "onComplete"));
assertContains("DISPATCHED",response); assertContains("DISPATCHED",response);
} }
@ -182,17 +190,17 @@ public class AsyncServletTest
{ {
_expectedCode="500 "; _expectedCode="500 ";
String response=process("start=200&timeout=error",null); String response=process("start=200&timeout=error",null);
assertThat(response,startsWith("HTTP/1.1 500 Async Exception\r\n")); assertThat(response,startsWith("HTTP/1.1 500 Async Exception"));
assertContains( assertThat(__history,contains(
"history: REQUEST /ctx/path/info\r\n"+ "REQUEST /ctx/path/info",
"history: initial\r\n"+ "initial",
"history: start\r\n"+ "start",
"history: onTimeout\r\n"+ "onTimeout",
"history: error\r\n"+ "error",
"history: onError\r\n"+ "onError",
"history: ERROR /ctx/path/info\r\n"+ "ERROR /ctx/path/info",
"history: !initial\r\n"+ "!initial",
"history: onComplete\r\n",response); "onComplete"));
assertContains("ERROR DISPATCH",response); assertContains("ERROR DISPATCH",response);
} }
@ -201,15 +209,16 @@ public class AsyncServletTest
public void testStartOnTimeoutErrorComplete() throws Exception public void testStartOnTimeoutErrorComplete() throws Exception
{ {
String response=process("start=200&timeout=error&error=complete",null); String response=process("start=200&timeout=error&error=complete",null);
assertThat(response,startsWith("HTTP/1.1 200 OK\r\n")); assertThat(response,startsWith("HTTP/1.1 200 OK"));
assertContains( assertThat(__history,contains(
"history: REQUEST /ctx/path/info\r\n"+ "REQUEST /ctx/path/info",
"history: initial\r\n"+ "initial",
"history: start\r\n"+ "start",
"history: onTimeout\r\n"+ "onTimeout",
"history: error\r\n"+ "error",
"history: onError\r\n"+ "onError",
"history: complete\r\n",response); "complete",
"onComplete"));
assertContains("COMPLETED",response); assertContains("COMPLETED",response);
} }
@ -218,18 +227,18 @@ public class AsyncServletTest
public void testStartOnTimeoutErrorDispatch() throws Exception public void testStartOnTimeoutErrorDispatch() throws Exception
{ {
String response=process("start=200&timeout=error&error=dispatch",null); String response=process("start=200&timeout=error&error=dispatch",null);
assertThat(response,startsWith("HTTP/1.1 200 OK\r\n")); assertThat(response,startsWith("HTTP/1.1 200 OK"));
assertContains( assertThat(__history,contains(
"history: REQUEST /ctx/path/info\r\n"+ "REQUEST /ctx/path/info",
"history: initial\r\n"+ "initial",
"history: start\r\n"+ "start",
"history: onTimeout\r\n"+ "onTimeout",
"history: error\r\n"+ "error",
"history: onError\r\n"+ "onError",
"history: dispatch\r\n"+ "dispatch",
"history: ASYNC /ctx/path/info\r\n"+ "ASYNC /ctx/path/info",
"history: !initial\r\n"+ "!initial",
"history: onComplete\r\n",response); "onComplete"));
assertContains("DISPATCHED",response); assertContains("DISPATCHED",response);
} }
@ -238,14 +247,14 @@ public class AsyncServletTest
public void testStartOnTimeoutComplete() throws Exception public void testStartOnTimeoutComplete() throws Exception
{ {
String response=process("start=200&timeout=complete",null); String response=process("start=200&timeout=complete",null);
assertThat(response,startsWith("HTTP/1.1 200 OK\r\n")); assertThat(response,startsWith("HTTP/1.1 200 OK"));
assertContains( assertThat(__history,contains(
"history: REQUEST /ctx/path/info\r\n"+ "REQUEST /ctx/path/info",
"history: initial\r\n"+ "initial",
"history: start\r\n"+ "start",
"history: onTimeout\r\n"+ "onTimeout",
"history: complete\r\n"+ "complete",
"history: onComplete\r\n",response); "onComplete"));
assertContains("COMPLETED",response); assertContains("COMPLETED",response);
} }
@ -254,32 +263,31 @@ public class AsyncServletTest
public void testStartWaitDispatch() throws Exception public void testStartWaitDispatch() throws Exception
{ {
String response=process("start=200&dispatch=10",null); String response=process("start=200&dispatch=10",null);
assertThat(response,startsWith("HTTP/1.1 200 OK\r\n")); assertThat(response,startsWith("HTTP/1.1 200 OK"));
assertContains( assertThat(__history,contains(
"history: REQUEST /ctx/path/info\r\n"+ "REQUEST /ctx/path/info",
"history: initial\r\n"+ "initial",
"history: start\r\n"+ "start",
"history: dispatch\r\n"+ "dispatch",
"history: ASYNC /ctx/path/info\r\n"+ "ASYNC /ctx/path/info",
"history: !initial\r\n"+ "!initial",
"history: onComplete\r\n",response); "onComplete"));
assertNotContains("history: onTimeout",response); assertFalse(__history.contains("onTimeout"));
} }
@Test @Test
public void testStartDispatch() throws Exception public void testStartDispatch() throws Exception
{ {
String response=process("start=200&dispatch=0",null); String response=process("start=200&dispatch=0",null);
assertThat(response,startsWith("HTTP/1.1 200 OK\r\n")); assertThat(response,startsWith("HTTP/1.1 200 OK"));
assertContains( assertThat(__history,contains(
"history: REQUEST /ctx/path/info\r\n"+ "REQUEST /ctx/path/info",
"history: initial\r\n"+ "initial",
"history: start\r\n"+ "start",
"history: dispatch\r\n"+ "dispatch",
"history: ASYNC /ctx/path/info\r\n"+ "ASYNC /ctx/path/info",
"history: !initial\r\n"+ "!initial",
"history: onComplete\r\n",response); "onComplete"));
assertContains("history: onComplete",response);
} }
@Test @Test
@ -287,16 +295,16 @@ public class AsyncServletTest
{ {
_expectedCode="500 "; _expectedCode="500 ";
String response=process("start=200&throw=1",null); String response=process("start=200&throw=1",null);
assertThat(response,startsWith("HTTP/1.1 500 Server Error\r\n")); assertThat(response,startsWith("HTTP/1.1 500 Server Error"));
assertContains( assertThat(__history,contains(
"history: REQUEST /ctx/path/info\r\n"+ "REQUEST /ctx/path/info",
"history: initial\r\n"+ "initial",
"history: start\r\n"+ "start",
/* TODO should there be an onError call? /* TODO should there be an onError call?
"history: onError\r\n"+ "onError",
"history: onComplete\r\n" "history: onComplete\r\n"
*/"", */""
response); ));
assertContains("HTTP ERROR: 500",response); assertContains("HTTP ERROR: 500",response);
} }
@ -304,52 +312,52 @@ public class AsyncServletTest
public void testStartWaitComplete() throws Exception public void testStartWaitComplete() throws Exception
{ {
String response=process("start=200&complete=50",null); String response=process("start=200&complete=50",null);
assertThat(response,startsWith("HTTP/1.1 200 OK\r\n")); assertThat(response,startsWith("HTTP/1.1 200 OK"));
assertContains( assertThat(__history,contains(
"history: REQUEST /ctx/path/info\r\n"+ "REQUEST /ctx/path/info",
"history: initial\r\n"+ "initial",
"history: start\r\n"+ "start",
"history: complete\r\n"+ "complete",
"history: onComplete\r\n",response); "onComplete"));
assertContains("COMPLETED",response); assertContains("COMPLETED",response);
assertNotContains("history: onTimeout",response); assertFalse(__history.contains("onTimeout"));
assertNotContains("history: !initial",response); assertFalse(__history.contains("!initial"));
} }
@Test @Test
public void testStartComplete() throws Exception public void testStartComplete() throws Exception
{ {
String response=process("start=200&complete=0",null); String response=process("start=200&complete=0",null);
assertThat(response,startsWith("HTTP/1.1 200 OK\r\n")); assertThat(response,startsWith("HTTP/1.1 200 OK"));
assertContains( assertThat(__history,contains(
"history: REQUEST /ctx/path/info\r\n"+ "REQUEST /ctx/path/info",
"history: initial\r\n"+ "initial",
"history: start\r\n"+ "start",
"history: complete\r\n"+ "complete",
"history: onComplete\r\n",response); "onComplete"));
assertContains("COMPLETED",response); assertContains("COMPLETED",response);
assertNotContains("history: onTimeout",response); assertFalse(__history.contains("onTimeout"));
assertNotContains("history: !initial",response); assertFalse(__history.contains("!initial"));
} }
@Test @Test
public void testStartWaitDispatchStartWaitDispatch() throws Exception public void testStartWaitDispatchStartWaitDispatch() throws Exception
{ {
String response=process("start=1000&dispatch=10&start2=1000&dispatch2=10",null); String response=process("start=1000&dispatch=10&start2=1000&dispatch2=10",null);
assertThat(response,startsWith("HTTP/1.1 200 OK\r\n")); assertThat(response,startsWith("HTTP/1.1 200 OK"));
assertContains( assertThat(__history,contains(
"history: REQUEST /ctx/path/info\r\n"+ "REQUEST /ctx/path/info",
"history: initial\r\n"+ "initial",
"history: start\r\n"+ "start",
"history: dispatch\r\n"+ "dispatch",
"history: ASYNC /ctx/path/info\r\n"+ "ASYNC /ctx/path/info",
"history: !initial\r\n"+ "!initial",
"history: onStartAsync\r\n"+ "onStartAsync",
"history: start\r\n"+ "start",
"history: dispatch\r\n"+ "dispatch",
"history: ASYNC /ctx/path/info\r\n"+ "ASYNC /ctx/path/info",
"history: !initial\r\n"+ "!initial",
"history: onComplete\r\n",response); "onComplete"));
assertContains("DISPATCHED",response); assertContains("DISPATCHED",response);
} }
@ -357,18 +365,18 @@ public class AsyncServletTest
public void testStartWaitDispatchStartComplete() throws Exception public void testStartWaitDispatchStartComplete() throws Exception
{ {
String response=process("start=1000&dispatch=10&start2=1000&complete2=10",null); String response=process("start=1000&dispatch=10&start2=1000&complete2=10",null);
assertThat(response,startsWith("HTTP/1.1 200 OK\r\n")); assertThat(response,startsWith("HTTP/1.1 200 OK"));
assertContains( assertThat(__history,contains(
"history: REQUEST /ctx/path/info\r\n"+ "REQUEST /ctx/path/info",
"history: initial\r\n"+ "initial",
"history: start\r\n"+ "start",
"history: dispatch\r\n"+ "dispatch",
"history: ASYNC /ctx/path/info\r\n"+ "ASYNC /ctx/path/info",
"history: !initial\r\n"+ "!initial",
"history: onStartAsync\r\n"+ "onStartAsync",
"history: start\r\n"+ "start",
"history: complete\r\n"+ "complete",
"history: onComplete\r\n",response); "onComplete"));
assertContains("COMPLETED",response); assertContains("COMPLETED",response);
} }
@ -378,19 +386,19 @@ public class AsyncServletTest
_expectedCode="500 "; _expectedCode="500 ";
String response=process("start=1000&dispatch=10&start2=10",null); String response=process("start=1000&dispatch=10&start2=10",null);
assertEquals("HTTP/1.1 500 Async Timeout",response.substring(0,26)); assertEquals("HTTP/1.1 500 Async Timeout",response.substring(0,26));
assertContains( assertThat(__history,contains(
"history: REQUEST /ctx/path/info\r\n"+ "REQUEST /ctx/path/info",
"history: initial\r\n"+ "initial",
"history: start\r\n"+ "start",
"history: dispatch\r\n"+ "dispatch",
"history: ASYNC /ctx/path/info\r\n"+ "ASYNC /ctx/path/info",
"history: !initial\r\n"+ "!initial",
"history: onStartAsync\r\n"+ "onStartAsync",
"history: start\r\n"+ "start",
"history: onTimeout\r\n"+ "onTimeout",
"history: ERROR /ctx/path/info\r\n"+ "ERROR /ctx/path/info",
"history: !initial\r\n"+ "!initial",
"history: onComplete\r\n",response); "onComplete"));
assertContains("ERROR DISPATCH: /ctx/path/info",response); assertContains("ERROR DISPATCH: /ctx/path/info",response);
} }
@ -398,20 +406,20 @@ public class AsyncServletTest
public void testStartTimeoutStartDispatch() throws Exception public void testStartTimeoutStartDispatch() throws Exception
{ {
String response=process("start=10&start2=1000&dispatch2=10",null); String response=process("start=10&start2=1000&dispatch2=10",null);
assertThat(response,startsWith("HTTP/1.1 200 OK\r\n")); assertThat(response,startsWith("HTTP/1.1 200 OK"));
assertContains( assertThat(__history,contains(
"history: REQUEST /ctx/path/info\r\n"+ "REQUEST /ctx/path/info",
"history: initial\r\n"+ "initial",
"history: start\r\n"+ "start",
"history: onTimeout\r\n"+ "onTimeout",
"history: ERROR /ctx/path/info\r\n"+ "ERROR /ctx/path/info",
"history: !initial\r\n"+ "!initial",
"history: onStartAsync\r\n"+ "onStartAsync",
"history: start\r\n"+ "start",
"history: dispatch\r\n"+ "dispatch",
"history: ASYNC /ctx/path/info\r\n"+ "ASYNC /ctx/path/info",
"history: !initial\r\n"+ "!initial",
"history: onComplete\r\n",response); "onComplete"));
assertContains("DISPATCHED",response); assertContains("DISPATCHED",response);
} }
@ -419,18 +427,18 @@ public class AsyncServletTest
public void testStartTimeoutStartComplete() throws Exception public void testStartTimeoutStartComplete() throws Exception
{ {
String response=process("start=10&start2=1000&complete2=10",null); String response=process("start=10&start2=1000&complete2=10",null);
assertThat(response,startsWith("HTTP/1.1 200 OK\r\n")); assertThat(response,startsWith("HTTP/1.1 200 OK"));
assertContains( assertThat(__history,contains(
"history: REQUEST /ctx/path/info\r\n"+ "REQUEST /ctx/path/info",
"history: initial\r\n"+ "initial",
"history: start\r\n"+ "start",
"history: onTimeout\r\n"+ "onTimeout",
"history: ERROR /ctx/path/info\r\n"+ "ERROR /ctx/path/info",
"history: !initial\r\n"+ "!initial",
"history: onStartAsync\r\n"+ "onStartAsync",
"history: start\r\n"+ "start",
"history: complete\r\n"+ "complete",
"history: onComplete\r\n",response); "onComplete"));
assertContains("COMPLETED",response); assertContains("COMPLETED",response);
} }
@ -439,19 +447,19 @@ public class AsyncServletTest
{ {
_expectedCode="500 "; _expectedCode="500 ";
String response=process("start=10&start2=10",null); String response=process("start=10&start2=10",null);
assertContains( assertThat(__history,contains(
"history: REQUEST /ctx/path/info\r\n"+ "REQUEST /ctx/path/info",
"history: initial\r\n"+ "initial",
"history: start\r\n"+ "start",
"history: onTimeout\r\n"+ "onTimeout",
"history: ERROR /ctx/path/info\r\n"+ "ERROR /ctx/path/info",
"history: !initial\r\n"+ "!initial",
"history: onStartAsync\r\n"+ "onStartAsync",
"history: start\r\n"+ "start",
"history: onTimeout\r\n"+ "onTimeout",
"history: ERROR /ctx/path/info\r\n"+ "ERROR /ctx/path/info",
"history: !initial\r\n"+ "!initial",
"history: onComplete\r\n",response); "onComplete"));
assertContains("ERROR DISPATCH: /ctx/path/info",response); assertContains("ERROR DISPATCH: /ctx/path/info",response);
} }
@ -459,16 +467,16 @@ public class AsyncServletTest
public void testWrapStartDispatch() throws Exception public void testWrapStartDispatch() throws Exception
{ {
String response=process("wrap=true&start=200&dispatch=20",null); String response=process("wrap=true&start=200&dispatch=20",null);
assertThat(response,startsWith("HTTP/1.1 200 OK\r\n")); assertThat(response,startsWith("HTTP/1.1 200 OK"));
assertContains( assertThat(__history,contains(
"history: REQUEST /ctx/path/info\r\n"+ "REQUEST /ctx/path/info",
"history: initial\r\n"+ "initial",
"history: start\r\n"+ "start",
"history: dispatch\r\n"+ "dispatch",
"history: ASYNC /ctx/path/info\r\n"+ "ASYNC /ctx/path/info",
"history: wrapped REQ RSP\r\n"+ "wrapped REQ RSP",
"history: !initial\r\n"+ "!initial",
"history: onComplete\r\n",response); "onComplete"));
assertContains("DISPATCHED",response); assertContains("DISPATCHED",response);
} }
@ -477,15 +485,15 @@ public class AsyncServletTest
public void testStartDispatchEncodedPath() throws Exception public void testStartDispatchEncodedPath() throws Exception
{ {
String response=process("start=200&dispatch=20&path=/p%20th3",null); String response=process("start=200&dispatch=20&path=/p%20th3",null);
assertThat(response,startsWith("HTTP/1.1 200 OK\r\n")); assertThat(response,startsWith("HTTP/1.1 200 OK"));
assertContains( assertThat(__history,contains(
"history: REQUEST /ctx/path/info\r\n"+ "REQUEST /ctx/path/info",
"history: initial\r\n"+ "initial",
"history: start\r\n"+ "start",
"history: dispatch\r\n"+ "dispatch",
"history: ASYNC /ctx/p%20th3\r\n"+ "ASYNC /ctx/p%20th3",
"history: !initial\r\n"+ "!initial",
"history: onComplete\r\n",response); "onComplete"));
assertContains("DISPATCHED",response); assertContains("DISPATCHED",response);
} }
@ -494,17 +502,17 @@ public class AsyncServletTest
public void testFwdStartDispatch() throws Exception public void testFwdStartDispatch() throws Exception
{ {
String response=process("fwd","start=200&dispatch=20",null); String response=process("fwd","start=200&dispatch=20",null);
assertThat(response,startsWith("HTTP/1.1 200 OK\r\n")); assertThat(response,startsWith("HTTP/1.1 200 OK"));
assertContains( assertThat(__history,contains(
"history: FWD REQUEST /ctx/fwd/info\r\n"+ "FWD REQUEST /ctx/fwd/info",
"history: FORWARD /ctx/path1\r\n"+ "FORWARD /ctx/path1",
"history: initial\r\n"+ "initial",
"history: start\r\n"+ "start",
"history: dispatch\r\n"+ "dispatch",
"history: FWD ASYNC /ctx/fwd/info\r\n"+ "FWD ASYNC /ctx/fwd/info",
"history: FORWARD /ctx/path1\r\n"+ "FORWARD /ctx/path1",
"history: !initial\r\n"+ "!initial",
"history: onComplete\r\n",response); "onComplete"));
assertContains("DISPATCHED",response); assertContains("DISPATCHED",response);
} }
@ -512,16 +520,16 @@ public class AsyncServletTest
public void testFwdStartDispatchPath() throws Exception public void testFwdStartDispatchPath() throws Exception
{ {
String response=process("fwd","start=200&dispatch=20&path=/path2",null); String response=process("fwd","start=200&dispatch=20&path=/path2",null);
assertThat(response,startsWith("HTTP/1.1 200 OK\r\n")); assertThat(response,startsWith("HTTP/1.1 200 OK"));
assertContains( assertThat(__history,contains(
"history: FWD REQUEST /ctx/fwd/info\r\n"+ "FWD REQUEST /ctx/fwd/info",
"history: FORWARD /ctx/path1\r\n"+ "FORWARD /ctx/path1",
"history: initial\r\n"+ "initial",
"history: start\r\n"+ "start",
"history: dispatch\r\n"+ "dispatch",
"history: ASYNC /ctx/path2\r\n"+ "ASYNC /ctx/path2",
"history: !initial\r\n"+ "!initial",
"history: onComplete\r\n",response); "onComplete"));
assertContains("DISPATCHED",response); assertContains("DISPATCHED",response);
} }
@ -529,17 +537,17 @@ public class AsyncServletTest
public void testFwdWrapStartDispatch() throws Exception public void testFwdWrapStartDispatch() throws Exception
{ {
String response=process("fwd","wrap=true&start=200&dispatch=20",null); String response=process("fwd","wrap=true&start=200&dispatch=20",null);
assertThat(response,startsWith("HTTP/1.1 200 OK\r\n")); assertThat(response,startsWith("HTTP/1.1 200 OK"));
assertContains( assertThat(__history,contains(
"history: FWD REQUEST /ctx/fwd/info\r\n"+ "FWD REQUEST /ctx/fwd/info",
"history: FORWARD /ctx/path1\r\n"+ "FORWARD /ctx/path1",
"history: initial\r\n"+ "initial",
"history: start\r\n"+ "start",
"history: dispatch\r\n"+ "dispatch",
"history: ASYNC /ctx/path1\r\n"+ "ASYNC /ctx/path1",
"history: wrapped REQ RSP\r\n"+ "wrapped REQ RSP",
"history: !initial\r\n"+ "!initial",
"history: onComplete\r\n",response); "onComplete"));
assertContains("DISPATCHED",response); assertContains("DISPATCHED",response);
} }
@ -547,17 +555,17 @@ public class AsyncServletTest
public void testFwdWrapStartDispatchPath() throws Exception public void testFwdWrapStartDispatchPath() throws Exception
{ {
String response=process("fwd","wrap=true&start=200&dispatch=20&path=/path2",null); String response=process("fwd","wrap=true&start=200&dispatch=20&path=/path2",null);
assertThat(response,startsWith("HTTP/1.1 200 OK\r\n")); assertThat(response,startsWith("HTTP/1.1 200 OK"));
assertContains( assertThat(__history,contains(
"history: FWD REQUEST /ctx/fwd/info\r\n"+ "FWD REQUEST /ctx/fwd/info",
"history: FORWARD /ctx/path1\r\n"+ "FORWARD /ctx/path1",
"history: initial\r\n"+ "initial",
"history: start\r\n"+ "start",
"history: dispatch\r\n"+ "dispatch",
"history: ASYNC /ctx/path2\r\n"+ "ASYNC /ctx/path2",
"history: wrapped REQ RSP\r\n"+ "wrapped REQ RSP",
"history: !initial\r\n"+ "!initial",
"history: onComplete\r\n",response); "onComplete"));
assertContains("DISPATCHED",response); assertContains("DISPATCHED",response);
} }
@ -587,16 +595,16 @@ public class AsyncServletTest
socket.getOutputStream().write(close.getBytes(StandardCharsets.ISO_8859_1)); socket.getOutputStream().write(close.getBytes(StandardCharsets.ISO_8859_1));
String response = IO.toString(socket.getInputStream()); String response = IO.toString(socket.getInputStream());
assertThat(response,startsWith("HTTP/1.1 200 OK\r\n")); assertThat(response,startsWith("HTTP/1.1 200 OK"));
assertContains( assertThat(__history,contains(
"history: REQUEST /ctx/path/info\r\n"+ "REQUEST /ctx/path/info",
"history: initial\r\n"+ "initial",
"history: start\r\n"+ "start",
"history: async-read=10\r\n"+ "async-read=10",
"history: dispatch\r\n"+ "dispatch",
"history: ASYNC /ctx/path/info\r\n"+ "ASYNC /ctx/path/info",
"history: !initial\r\n"+ "!initial",
"history: onComplete\r\n",response); "onComplete"));
} }
} }
@ -628,7 +636,9 @@ public class AsyncServletTest
socket.setSoTimeout(1000000); socket.setSoTimeout(1000000);
socket.getOutputStream().write(request.getBytes(StandardCharsets.UTF_8)); socket.getOutputStream().write(request.getBytes(StandardCharsets.UTF_8));
socket.getOutputStream().flush(); socket.getOutputStream().flush();
return IO.toString(socket.getInputStream()); String response = IO.toString(socket.getInputStream());
__latch.await(1,TimeUnit.SECONDS);
return response;
} }
catch(Exception e) catch(Exception e)
{ {
@ -636,6 +646,7 @@ public class AsyncServletTest
e.printStackTrace(); e.printStackTrace();
throw e; throw e;
} }
} }
protected void assertContains(String content,String response) protected void assertContains(String content,String response)
@ -653,9 +664,9 @@ public class AsyncServletTest
@Override @Override
public void doGet(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException public void doGet(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException
{ {
response.addHeader("history","FWD "+request.getDispatcherType()+" "+request.getRequestURI()); __history.add("FWD "+request.getDispatcherType()+" "+request.getRequestURI());
if (request instanceof ServletRequestWrapper || response instanceof ServletResponseWrapper) if (request instanceof ServletRequestWrapper || response instanceof ServletResponseWrapper)
response.addHeader("history","wrapped"+((request instanceof ServletRequestWrapper)?" REQ":"")+((response instanceof ServletResponseWrapper)?" RSP":"")); __history.add("wrapped"+((request instanceof ServletRequestWrapper)?" REQ":"")+((response instanceof ServletResponseWrapper)?" RSP":""));
request.getServletContext().getRequestDispatcher("/path1").forward(request,response); request.getServletContext().getRequestDispatcher("/path1").forward(request,response);
} }
} }
@ -680,9 +691,9 @@ public class AsyncServletTest
} }
// System.err.println(request.getDispatcherType()+" "+request.getRequestURI()); // System.err.println(request.getDispatcherType()+" "+request.getRequestURI());
response.addHeader("history",request.getDispatcherType()+" "+request.getRequestURI()); __history.add(request.getDispatcherType()+" "+request.getRequestURI());
if (request instanceof ServletRequestWrapper || response instanceof ServletResponseWrapper) if (request instanceof ServletRequestWrapper || response instanceof ServletResponseWrapper)
response.addHeader("history","wrapped"+((request instanceof ServletRequestWrapper)?" REQ":"")+((response instanceof ServletResponseWrapper)?" RSP":"")); __history.add("wrapped"+((request instanceof ServletRequestWrapper)?" REQ":"")+((response instanceof ServletResponseWrapper)?" RSP":""));
boolean wrap="true".equals(request.getParameter("wrap")); boolean wrap="true".equals(request.getParameter("wrap"));
int read_before=0; int read_before=0;
@ -716,7 +727,7 @@ public class AsyncServletTest
if (request.getAttribute("State")==null) if (request.getAttribute("State")==null)
{ {
request.setAttribute("State",new Integer(1)); request.setAttribute("State",new Integer(1));
response.addHeader("history","initial"); __history.add("initial");
if (read_before>0) if (read_before>0)
{ {
byte[] buf=new byte[read_before]; byte[] buf=new byte[read_before];
@ -744,7 +755,7 @@ public class AsyncServletTest
while(b!=-1) while(b!=-1)
if((b=in.read())>=0) if((b=in.read())>=0)
c++; c++;
response.addHeader("history","async-read="+c); __history.add("async-read="+c);
} }
catch(Exception e) catch(Exception e)
{ {
@ -760,7 +771,7 @@ public class AsyncServletTest
if (start_for>0) if (start_for>0)
async.setTimeout(start_for); async.setTimeout(start_for);
async.addListener(__listener); async.addListener(__listener);
response.addHeader("history","start"); __history.add("start");
if ("1".equals(request.getParameter("throw"))) if ("1".equals(request.getParameter("throw")))
throw new QuietServletException(new Exception("test throw in async 1")); throw new QuietServletException(new Exception("test throw in async 1"));
@ -776,7 +787,7 @@ public class AsyncServletTest
{ {
response.setStatus(200); response.setStatus(200);
response.getOutputStream().println("COMPLETED\n"); response.getOutputStream().println("COMPLETED\n");
response.addHeader("history","complete"); __history.add("complete");
async.complete(); async.complete();
} }
catch(Exception e) catch(Exception e)
@ -794,7 +805,7 @@ public class AsyncServletTest
{ {
response.setStatus(200); response.setStatus(200);
response.getOutputStream().println("COMPLETED\n"); response.getOutputStream().println("COMPLETED\n");
response.addHeader("history","complete"); __history.add("complete");
async.complete(); async.complete();
} }
else if (dispatch_after>0) else if (dispatch_after>0)
@ -804,7 +815,7 @@ public class AsyncServletTest
@Override @Override
public void run() public void run()
{ {
((HttpServletResponse)async.getResponse()).addHeader("history","dispatch"); __history.add("dispatch");
if (path!=null) if (path!=null)
{ {
int q=path.indexOf('?'); int q=path.indexOf('?');
@ -824,7 +835,7 @@ public class AsyncServletTest
} }
else if (dispatch_after==0) else if (dispatch_after==0)
{ {
((HttpServletResponse)async.getResponse()).addHeader("history","dispatch"); __history.add("dispatch");
if (path!=null) if (path!=null)
async.dispatch(path); async.dispatch(path);
else else
@ -853,7 +864,7 @@ public class AsyncServletTest
} }
else else
{ {
response.addHeader("history","!initial"); __history.add("!initial");
if (start2_for>=0 && request.getAttribute("2nd")==null) if (start2_for>=0 && request.getAttribute("2nd")==null)
{ {
@ -865,7 +876,7 @@ public class AsyncServletTest
{ {
async.setTimeout(start2_for); async.setTimeout(start2_for);
} }
response.addHeader("history","start"); __history.add("start");
if ("2".equals(request.getParameter("throw"))) if ("2".equals(request.getParameter("throw")))
throw new QuietServletException(new Exception("test throw in async 2")); throw new QuietServletException(new Exception("test throw in async 2"));
@ -881,7 +892,7 @@ public class AsyncServletTest
{ {
response.setStatus(200); response.setStatus(200);
response.getOutputStream().println("COMPLETED\n"); response.getOutputStream().println("COMPLETED\n");
response.addHeader("history","complete"); __history.add("complete");
async.complete(); async.complete();
} }
catch(Exception e) catch(Exception e)
@ -899,7 +910,7 @@ public class AsyncServletTest
{ {
response.setStatus(200); response.setStatus(200);
response.getOutputStream().println("COMPLETED\n"); response.getOutputStream().println("COMPLETED\n");
response.addHeader("history","complete"); __history.add("complete");
async.complete(); async.complete();
} }
else if (dispatch2_after>0) else if (dispatch2_after>0)
@ -909,7 +920,7 @@ public class AsyncServletTest
@Override @Override
public void run() public void run()
{ {
response.addHeader("history","dispatch"); __history.add("dispatch");
async.dispatch(); async.dispatch();
} }
}; };
@ -920,7 +931,7 @@ public class AsyncServletTest
} }
else if (dispatch2_after==0) else if (dispatch2_after==0)
{ {
response.addHeader("history","dispatch"); __history.add("dispatch");
async.dispatch(); async.dispatch();
} }
} }
@ -943,11 +954,11 @@ public class AsyncServletTest
@Override @Override
public void onTimeout(AsyncEvent event) throws IOException public void onTimeout(AsyncEvent event) throws IOException
{ {
((HttpServletResponse)event.getSuppliedResponse()).addHeader("history","onTimeout"); __history.add("onTimeout");
String action=event.getSuppliedRequest().getParameter("timeout"); String action=event.getSuppliedRequest().getParameter("timeout");
if (action!=null) if (action!=null)
{ {
((HttpServletResponse)event.getSuppliedResponse()).addHeader("history",action); __history.add(action);
switch(action) switch(action)
{ {
@ -969,17 +980,17 @@ public class AsyncServletTest
@Override @Override
public void onStartAsync(AsyncEvent event) throws IOException public void onStartAsync(AsyncEvent event) throws IOException
{ {
((HttpServletResponse)event.getSuppliedResponse()).addHeader("history","onStartAsync"); __history.add("onStartAsync");
} }
@Override @Override
public void onError(AsyncEvent event) throws IOException public void onError(AsyncEvent event) throws IOException
{ {
((HttpServletResponse)event.getSuppliedResponse()).addHeader("history","onError"); __history.add("onError");
String action=event.getSuppliedRequest().getParameter("error"); String action=event.getSuppliedRequest().getParameter("error");
if (action!=null) if (action!=null)
{ {
((HttpServletResponse)event.getSuppliedResponse()).addHeader("history",action); __history.add(action);
switch(action) switch(action)
{ {
@ -998,7 +1009,8 @@ public class AsyncServletTest
@Override @Override
public void onComplete(AsyncEvent event) throws IOException public void onComplete(AsyncEvent event) throws IOException
{ {
((HttpServletResponse)event.getSuppliedResponse()).addHeader("history","onComplete"); __history.add("onComplete");
__latch.countDown();
} }
}; };