diff options
Diffstat (limited to 'jetty-servlet/src/test/java/org/eclipse/jetty/servlet/AsyncListenerTest.java')
-rw-r--r-- | jetty-servlet/src/test/java/org/eclipse/jetty/servlet/AsyncListenerTest.java | 813 |
1 files changed, 294 insertions, 519 deletions
diff --git a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/AsyncListenerTest.java b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/AsyncListenerTest.java index dd7d6303b4..fb5493a71b 100644 --- a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/AsyncListenerTest.java +++ b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/AsyncListenerTest.java @@ -18,631 +18,406 @@ package org.eclipse.jetty.servlet; -import static org.hamcrest.Matchers.*; -import static org.junit.Assert.*; - import java.io.IOException; -import java.io.PrintWriter; -import java.nio.charset.StandardCharsets; -import java.util.EnumSet; -import java.util.LinkedList; -import java.util.List; +import java.util.concurrent.TimeUnit; import javax.servlet.AsyncContext; import javax.servlet.AsyncEvent; import javax.servlet.AsyncListener; -import javax.servlet.DispatcherType; -import javax.servlet.Filter; -import javax.servlet.FilterChain; -import javax.servlet.FilterConfig; import javax.servlet.ServletException; -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; +import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.server.LocalConnector; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.util.log.Log; -import org.eclipse.jetty.util.log.Logger; -import org.junit.Ignore; +import org.junit.After; import org.junit.Test; -@Ignore("Not handling Exceptions during Async very well") +import static org.hamcrest.Matchers.containsString; +import static org.junit.Assert.assertThat; + public class AsyncListenerTest { - // Unique named RuntimeException to help during debugging / assertions - @SuppressWarnings("serial") - public static class FooRuntimeException extends RuntimeException + private Server server; + private LocalConnector connector; + + public void startServer(ServletContextHandler context) throws Exception { + server = new Server(); + connector = new LocalConnector(server); + connector.setIdleTimeout(20 * 60 * 1000L); + server.addConnector(connector); + server.setHandler(context); + server.start(); } - // Unique named Exception to help during debugging / assertions - @SuppressWarnings("serial") - public static class FooException extends Exception + @After + public void dispose() throws Exception { + if (server != null) + server.stop(); } - // Unique named Throwable to help during debugging / assertions - @SuppressWarnings("serial") - public static class FooThrowable extends Throwable + @Test + public void test_StartAsync_Throw_OnError_Dispatch() throws Exception { + test_StartAsync_Throw_OnError(event -> event.getAsyncContext().dispatch("/dispatch")); + String httpResponse = connector.getResponses("" + + "GET /ctx/path HTTP/1.1\r\n" + + "Host: localhost\r\n" + + "Connection: close\r\n" + + "\r\n"); + assertThat(httpResponse, containsString("HTTP/1.1 200 ")); } - // Unique named Error to help during debugging / assertions - @SuppressWarnings("serial") - public static class FooError extends Error + @Test + public void test_StartAsync_Throw_OnError_Complete() throws Exception { + test_StartAsync_Throw_OnError(event -> + { + HttpServletResponse response = (HttpServletResponse)event.getAsyncContext().getResponse(); + response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR_500); + ServletOutputStream output = response.getOutputStream(); + output.println(event.getThrowable().getClass().getName()); + output.println("COMPLETE"); + event.getAsyncContext().complete(); + }); + String httpResponse = connector.getResponses("" + + "GET /ctx/path HTTP/1.1\r\n" + + "Host: localhost\r\n" + + "Connection: close\r\n" + + "\r\n"); + assertThat(httpResponse, containsString("HTTP/1.1 500 ")); + assertThat(httpResponse, containsString(TestRuntimeException.class.getName())); + assertThat(httpResponse, containsString("COMPLETE")); } - /** - * Basic AsyncListener adapter that simply logs (and makes testcase writing easier) - */ - public static class AsyncListenerAdapter implements AsyncListener + @Test + public void test_StartAsync_Throw_OnError_Throw() throws Exception { - private static final Logger LOG = Log.getLogger(AsyncListenerTest.AsyncListenerAdapter.class); - - @Override - public void onComplete(AsyncEvent event) throws IOException - { - LOG.info("onComplete({})",event); - } - - @Override - public void onTimeout(AsyncEvent event) throws IOException - { - LOG.info("onTimeout({})",event); - } - - @Override - public void onError(AsyncEvent event) throws IOException - { - LOG.info("onError({})",event); - } - - @Override - public void onStartAsync(AsyncEvent event) throws IOException - { - LOG.info("onStartAsync({})",event); - } + test_StartAsync_Throw_OnError(event -> + { + throw new IOException(); + }); + String httpResponse = connector.getResponses("" + + "GET /ctx/path HTTP/1.1\r\n" + + "Host: localhost\r\n" + + "Connection: close\r\n" + + "\r\n"); + assertThat(httpResponse, containsString("HTTP/1.1 500 ")); + assertThat(httpResponse, containsString(TestRuntimeException.class.getName())); } - /** - * Common ErrorContext for normal and async error handling - */ - public static class ErrorContext implements AsyncListener + @Test + public void test_StartAsync_Throw_OnError_Nothing() throws Exception { - private static final Logger LOG = Log.getLogger(AsyncListenerTest.ErrorContext.class); - - public void report(Throwable t, ServletRequest req, ServletResponse resp) throws IOException - { - if (resp instanceof HttpServletResponse) - { - ((HttpServletResponse)resp).setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); - } - resp.setContentType("text/plain"); - resp.setCharacterEncoding(StandardCharsets.UTF_8.name()); - PrintWriter out = resp.getWriter(); - t.printStackTrace(out); - } - - private void reportThrowable(AsyncEvent event) throws IOException - { - Throwable t = event.getThrowable(); - if (t == null) - { - return; - } - ServletRequest req = event.getAsyncContext().getRequest(); - ServletResponse resp = event.getAsyncContext().getResponse(); - report(t,req,resp); - } - - @Override - public void onComplete(AsyncEvent event) throws IOException - { - LOG.info("onComplete({})",event); - reportThrowable(event); - } - - @Override - public void onTimeout(AsyncEvent event) throws IOException - { - LOG.info("onTimeout({})",event); - reportThrowable(event); - } - - @Override - public void onError(AsyncEvent event) throws IOException - { - LOG.info("onError({})",event); - reportThrowable(event); - } - - @Override - public void onStartAsync(AsyncEvent event) throws IOException - { - LOG.info("onStartAsync({})",event); - reportThrowable(event); - } + test_StartAsync_Throw_OnError(event -> {}); + String httpResponse = connector.getResponses("" + + "GET /ctx/path HTTP/1.1\r\n" + + "Host: localhost\r\n" + + "Connection: close\r\n" + + "\r\n"); + assertThat(httpResponse, containsString("HTTP/1.1 500 ")); + assertThat(httpResponse, containsString(TestRuntimeException.class.getName())); } - /** - * Common filter for all test cases that should handle Errors in a consistent way - * regardless of how the exception / error occurred in the servlets in the chain. - */ - public static class ErrorFilter implements Filter + @Test + public void test_StartAsync_Throw_OnError_SendError() throws Exception { - private final List<ErrorContext> tracking; - - public ErrorFilter(List<ErrorContext> tracking) - { - this.tracking = tracking; - } + test_StartAsync_Throw_OnError(event -> + { + HttpServletResponse response = (HttpServletResponse)event.getAsyncContext().getResponse(); + response.sendError(HttpStatus.BAD_GATEWAY_502); + }); + String httpResponse = connector.getResponses("" + + "GET /ctx/path HTTP/1.1\r\n" + + "Host: localhost\r\n" + + "Connection: close\r\n" + + "\r\n"); + assertThat(httpResponse, containsString("HTTP/1.1 502 ")); + assertThat(httpResponse, containsString(TestRuntimeException.class.getName())); + } - @Override - public void destroy() - { - } + @Test + public void test_StartAsync_Throw_OnError_SendError_CustomErrorPage() throws Exception + { + test_StartAsync_Throw_OnError(event -> + { + HttpServletResponse response = (HttpServletResponse)event.getAsyncContext().getResponse(); + response.sendError(HttpStatus.BAD_GATEWAY_502); + }); + + // Add a custom error page. + ErrorPageErrorHandler errorHandler = new ErrorPageErrorHandler(); + errorHandler.setServer(server); + errorHandler.addErrorPage(HttpStatus.BAD_GATEWAY_502, "/error"); + server.addManaged(errorHandler); + + String httpResponse = connector.getResponses("" + + "GET /ctx/path HTTP/1.1\r\n" + + "Host: localhost\r\n" + + "Connection: close\r\n" + + "\r\n", 10, TimeUnit.MINUTES); + assertThat(httpResponse, containsString("HTTP/1.1 502 ")); + assertThat(httpResponse, containsString("CUSTOM")); + } - @Override - public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException + private void test_StartAsync_Throw_OnError(IOConsumer<AsyncEvent> consumer) throws Exception + { + ServletContextHandler context = new ServletContextHandler(); + context.setContextPath("/ctx"); + context.addServlet(new ServletHolder(new HttpServlet() { - ErrorContext err = new ErrorContext(); - tracking.add(err); - try + @Override + protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { - chain.doFilter(request,response); + AsyncContext asyncContext = request.startAsync(); + asyncContext.setTimeout(0); + asyncContext.addListener(new AsyncListenerAdapter() + { + @Override + public void onError(AsyncEvent event) throws IOException + { + consumer.accept(event); + } + }); + throw new TestRuntimeException(); } - catch (Throwable t) + }), "/path/*"); + context.addServlet(new ServletHolder(new HttpServlet() + { + @Override + protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { - err.report(t,request,response); + response.setStatus(HttpStatus.OK_200); } - finally + }), "/dispatch/*"); + context.addServlet(new ServletHolder(new HttpServlet() + { + @Override + protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { - if (request.isAsyncStarted()) - { - request.getAsyncContext().addListener(err); - } + response.getOutputStream().print("CUSTOM"); } - } + }), "/error/*"); - @Override - public void init(FilterConfig filterConfig) throws ServletException - { - } + startServer(context); } - /** - * Normal non-async testcase of error handling from a filter - * - * @throws Exception - * on test failure - */ @Test - public void testFilterErrorNoAsync() throws Exception + public void test_StartAsync_OnTimeout_Dispatch() throws Exception { - Server server = new Server(); - LocalConnector conn = new LocalConnector(server); - conn.setIdleTimeout(10000); - server.addConnector(conn); - - ServletContextHandler context = new ServletContextHandler(); - context.setContextPath("/"); - @SuppressWarnings("serial") - HttpServlet servlet = new HttpServlet() - { - public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException - { - throw new FooRuntimeException(); - } - }; - ServletHolder holder = new ServletHolder(servlet); - holder.setAsyncSupported(true); - context.addServlet(holder,"/err/*"); - List<ErrorContext> tracking = new LinkedList<ErrorContext>(); - ErrorFilter filter = new ErrorFilter(tracking); - context.addFilter(new FilterHolder(filter),"/*",EnumSet.allOf(DispatcherType.class)); - - server.setHandler(context); - - try - { - server.start(); - String resp = conn.getResponses("GET /err/ HTTP/1.1\n" + "Host: localhost\n" + "Connection: close\n" + "\n"); - assertThat("Response status",resp,containsString("HTTP/1.1 500 Server Error")); - assertThat("Response",resp,containsString(FooRuntimeException.class.getName())); - } - finally - { - server.stop(); - } + test_StartAsync_OnTimeout(500, event -> event.getAsyncContext().dispatch("/dispatch")); + String httpResponse = connector.getResponses("" + + "GET / HTTP/1.1\r\n" + + "Host: localhost\r\n" + + "Connection: close\r\n" + + "\r\n"); + assertThat(httpResponse, containsString("HTTP/1.1 200 ")); } - /** - * async testcase of error handling from a filter. - * - * Async Started, then application Exception - * - * @throws Exception - * on test failure - */ @Test - public void testFilterErrorAsyncStart_Exception() throws Exception + public void test_StartAsync_OnTimeout_Complete() throws Exception { - Server server = new Server(); - LocalConnector conn = new LocalConnector(server); - conn.setIdleTimeout(10000); - server.addConnector(conn); - - ServletContextHandler context = new ServletContextHandler(); - context.setContextPath("/"); - @SuppressWarnings("serial") - HttpServlet servlet = new HttpServlet() - { - public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException - { - req.startAsync(); - // before listeners are added, toss Exception - throw new FooRuntimeException(); - } - }; - ServletHolder holder = new ServletHolder(servlet); - holder.setAsyncSupported(true); - context.addServlet(holder,"/err/*"); - List<ErrorContext> tracking = new LinkedList<ErrorContext>(); - ErrorFilter filter = new ErrorFilter(tracking); - context.addFilter(new FilterHolder(filter),"/*",EnumSet.allOf(DispatcherType.class)); - - server.setHandler(context); - - try - { - server.start(); - String resp = conn.getResponses("GET /err/ HTTP/1.1\n" + "Host: localhost\n" + "Connection: close\n" + "\n"); - assertThat("Response status",resp,containsString("HTTP/1.1 500 Server Error")); - assertThat("Response",resp,containsString(FooRuntimeException.class.getName())); - } - finally - { - server.stop(); - } + test_StartAsync_OnTimeout(500, event -> + { + HttpServletResponse response = (HttpServletResponse)event.getAsyncContext().getResponse(); + response.setStatus(HttpStatus.OK_200); + ServletOutputStream output = response.getOutputStream(); + output.println("COMPLETE"); + event.getAsyncContext().complete(); + + }); + String httpResponse = connector.getResponses("" + + "GET / HTTP/1.1\r\n" + + "Host: localhost\r\n" + + "Connection: close\r\n" + + "\r\n"); + assertThat(httpResponse, containsString("HTTP/1.1 200 ")); + assertThat(httpResponse, containsString("COMPLETE")); } - /** - * async testcase of error handling from a filter. - * - * Async Started, add listener that does nothing, then application Exception - * - * @throws Exception - * on test failure - */ @Test - public void testFilterErrorAsyncStart_AddEmptyListener_Exception() throws Exception + public void test_StartAsync_OnTimeout_Throw() throws Exception { - Server server = new Server(); - LocalConnector conn = new LocalConnector(server); - conn.setIdleTimeout(10000); - server.addConnector(conn); - - ServletContextHandler context = new ServletContextHandler(); - context.setContextPath("/"); - @SuppressWarnings("serial") - HttpServlet servlet = new HttpServlet() - { - public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException - { - AsyncContext ctx = req.startAsync(); - ctx.addListener(new AsyncListenerAdapter()); - throw new FooRuntimeException(); - } - }; - ServletHolder holder = new ServletHolder(servlet); - holder.setAsyncSupported(true); - context.addServlet(holder,"/err/*"); - List<ErrorContext> tracking = new LinkedList<ErrorContext>(); - ErrorFilter filter = new ErrorFilter(tracking); - context.addFilter(new FilterHolder(filter),"/*",EnumSet.allOf(DispatcherType.class)); - - server.setHandler(context); - - try - { - server.start(); - String resp = conn.getResponses("GET /err/ HTTP/1.1\n" + "Host: localhost\n" + "Connection: close\n" + "\n"); - assertThat("Response status",resp,containsString("HTTP/1.1 500 Server Error")); - assertThat("Response",resp,containsString(FooRuntimeException.class.getName())); - } - finally - { - server.stop(); - } + test_StartAsync_OnTimeout(500, event -> + { + throw new TestRuntimeException(); + }); + String httpResponse = connector.getResponses("" + + "GET / HTTP/1.1\r\n" + + "Host: localhost\r\n" + + "Connection: close\r\n" + + "\r\n"); + assertThat(httpResponse, containsString("HTTP/1.1 500 ")); + assertThat(httpResponse, containsString(TestRuntimeException.class.getName())); } - /** - * async testcase of error handling from a filter. - * - * Async Started, add listener that completes only, then application Exception - * - * @throws Exception - * on test failure - */ @Test - public void testFilterErrorAsyncStart_AddListener_Exception() throws Exception + public void test_StartAsync_OnTimeout_Nothing() throws Exception { - Server server = new Server(); - LocalConnector conn = new LocalConnector(server); - conn.setIdleTimeout(10000); - server.addConnector(conn); - - ServletContextHandler context = new ServletContextHandler(); - context.setContextPath("/"); - @SuppressWarnings("serial") - HttpServlet servlet = new HttpServlet() - { - public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException - { - AsyncContext ctx = req.startAsync(); - ctx.addListener(new AsyncListenerAdapter() - { - @Override - public void onError(AsyncEvent event) throws IOException - { - System.err.println("### ONERROR"); - event.getThrowable().printStackTrace(System.err); - event.getAsyncContext().complete(); - } - }); - throw new FooRuntimeException(); - } - }; - ServletHolder holder = new ServletHolder(servlet); - holder.setAsyncSupported(true); - context.addServlet(holder,"/err/*"); - List<ErrorContext> tracking = new LinkedList<ErrorContext>(); - ErrorFilter filter = new ErrorFilter(tracking); - context.addFilter(new FilterHolder(filter),"/*",EnumSet.allOf(DispatcherType.class)); - - server.setHandler(context); + test_StartAsync_OnTimeout(500, event -> { + }); + String httpResponse = connector.getResponses("" + + "GET / HTTP/1.1\r\n" + + "Host: localhost\r\n" + + "Connection: close\r\n" + + "\r\n"); + assertThat(httpResponse, containsString("HTTP/1.1 500 ")); + } - try - { - server.start(); - String resp = conn.getResponses("GET /err/ HTTP/1.1\n" + "Host: localhost\n" + "Connection: close\n" + "\n"); - assertThat("Response status",resp,containsString("HTTP/1.1 500 Server Error")); - assertThat("Response",resp,containsString(FooRuntimeException.class.getName())); - } - finally - { - server.stop(); - } + @Test + public void test_StartAsync_OnTimeout_SendError() throws Exception + { + test_StartAsync_OnTimeout(500, event -> + { + HttpServletResponse response = (HttpServletResponse)event.getAsyncContext().getResponse(); + response.sendError(HttpStatus.BAD_GATEWAY_502); + }); + String httpResponse = connector.getResponses("" + + "GET / HTTP/1.1\r\n" + + "Host: localhost\r\n" + + "Connection: close\r\n" + + "\r\n"); + assertThat(httpResponse, containsString("HTTP/1.1 502 ")); } - /** - * async testcase of error handling from a filter. - * - * Async Started, add listener, in onStartAsync throw Exception - * - * @throws Exception - * on test failure - */ @Test - public void testFilterErrorAsyncStart_AddListener_ExceptionDuringOnStart() throws Exception + public void test_StartAsync_OnTimeout_SendError_CustomErrorPage() throws Exception { - Server server = new Server(); - LocalConnector conn = new LocalConnector(server); - conn.setIdleTimeout(10000); - server.addConnector(conn); + test_StartAsync_OnTimeout(500, event -> + { + AsyncContext asyncContext = event.getAsyncContext(); + HttpServletResponse response = (HttpServletResponse)asyncContext.getResponse(); + response.sendError(HttpStatus.BAD_GATEWAY_502); + asyncContext.complete(); + }); + + // Add a custom error page. + ErrorPageErrorHandler errorHandler = new ErrorPageErrorHandler(); + errorHandler.setServer(server); + errorHandler.addErrorPage(HttpStatus.BAD_GATEWAY_502, "/error"); + server.addManaged(errorHandler); + + String httpResponse = connector.getResponses("" + + "GET / HTTP/1.1\r\n" + + "Host: localhost\r\n" + + "Connection: close\r\n" + + "\r\n"); + assertThat(httpResponse, containsString("HTTP/1.1 502 ")); + assertThat(httpResponse, containsString("CUSTOM")); + } + private void test_StartAsync_OnTimeout(long timeout, IOConsumer<AsyncEvent> consumer) throws Exception + { ServletContextHandler context = new ServletContextHandler(); - context.setContextPath("/"); - @SuppressWarnings("serial") - HttpServlet servlet = new HttpServlet() + context.addServlet(new ServletHolder(new HttpServlet() { - public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException + @Override + protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { - AsyncContext ctx = req.startAsync(); - ctx.addListener(new AsyncListenerAdapter() + AsyncContext asyncContext = request.startAsync(); + asyncContext.setTimeout(timeout); + asyncContext.addListener(new AsyncListenerAdapter() { @Override - public void onStartAsync(AsyncEvent event) throws IOException + public void onTimeout(AsyncEvent event) throws IOException { - throw new FooRuntimeException(); + consumer.accept(event); } }); } - }; - ServletHolder holder = new ServletHolder(servlet); - holder.setAsyncSupported(true); - context.addServlet(holder,"/err/*"); - List<ErrorContext> tracking = new LinkedList<ErrorContext>(); - ErrorFilter filter = new ErrorFilter(tracking); - context.addFilter(new FilterHolder(filter),"/*",EnumSet.allOf(DispatcherType.class)); - - server.setHandler(context); - - try + }), "/*"); + context.addServlet(new ServletHolder(new HttpServlet() { - server.start(); - String resp = conn.getResponses("GET /err/ HTTP/1.1\n" + "Host: localhost\n" + "Connection: close\n" + "\n"); - assertThat("Response status",resp,containsString("HTTP/1.1 500 Server Error")); - assertThat("Response",resp,containsString(FooRuntimeException.class.getName())); - } - finally - { - server.stop(); - } - } - - /** - * async testcase of error handling from a filter. - * - * Async Started, add listener, in onComplete throw Exception - * - * @throws Exception - * on test failure - */ - @Test - public void testFilterErrorAsyncStart_AddListener_ExceptionDuringOnComplete() throws Exception - { - Server server = new Server(); - LocalConnector conn = new LocalConnector(server); - conn.setIdleTimeout(10000); - server.addConnector(conn); - - ServletContextHandler context = new ServletContextHandler(); - context.setContextPath("/"); - @SuppressWarnings("serial") - HttpServlet servlet = new HttpServlet() + @Override + protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException + { + response.setStatus(HttpStatus.OK_200); + } + }), "/dispatch/*"); + context.addServlet(new ServletHolder(new HttpServlet() { - public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException + @Override + protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { - AsyncContext ctx = req.startAsync(); - ctx.addListener(new AsyncListenerAdapter() - { - @Override - public void onComplete(AsyncEvent event) throws IOException - { - throw new FooRuntimeException(); - } - }); - ctx.complete(); + response.getOutputStream().print("CUSTOM"); } - }; - ServletHolder holder = new ServletHolder(servlet); - holder.setAsyncSupported(true); - context.addServlet(holder,"/err/*"); - List<ErrorContext> tracking = new LinkedList<ErrorContext>(); - ErrorFilter filter = new ErrorFilter(tracking); - context.addFilter(new FilterHolder(filter),"/*",EnumSet.allOf(DispatcherType.class)); - - server.setHandler(context); + }), "/error/*"); - try - { - server.start(); - String resp = conn.getResponses("GET /err/ HTTP/1.1\n" + "Host: localhost\n" + "Connection: close\n" + "\n"); - assertThat("Response status",resp,containsString("HTTP/1.1 500 Server Error")); - assertThat("Response",resp,containsString(FooRuntimeException.class.getName())); - } - finally - { - server.stop(); - } + startServer(context); } - /** - * async testcase of error handling from a filter. - * - * Async Started, add listener, in onTimeout throw Exception - * - * @throws Exception - * on test failure - */ @Test - public void testFilterErrorAsyncStart_AddListener_ExceptionDuringOnTimeout() throws Exception + public void test_StartAsync_OnComplete_Throw() throws Exception { - Server server = new Server(); - LocalConnector conn = new LocalConnector(server); - conn.setIdleTimeout(10000); - server.addConnector(conn); - ServletContextHandler context = new ServletContextHandler(); - context.setContextPath("/"); - @SuppressWarnings("serial") - HttpServlet servlet = new HttpServlet() + context.addServlet(new ServletHolder(new HttpServlet() { - public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException + @Override + protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { - AsyncContext ctx = req.startAsync(); - ctx.setTimeout(1000); - ctx.addListener(new AsyncListenerAdapter() + AsyncContext asyncContext = request.startAsync(); + asyncContext.setTimeout(0); + asyncContext.addListener(new AsyncListenerAdapter() { @Override - public void onTimeout(AsyncEvent event) throws IOException + public void onComplete(AsyncEvent event) throws IOException { - throw new FooRuntimeException(); + throw new TestRuntimeException(); } }); + response.getOutputStream().print("DATA"); + asyncContext.complete(); } - }; - ServletHolder holder = new ServletHolder(servlet); - holder.setAsyncSupported(true); - context.addServlet(holder,"/err/*"); - List<ErrorContext> tracking = new LinkedList<ErrorContext>(); - ErrorFilter filter = new ErrorFilter(tracking); - context.addFilter(new FilterHolder(filter),"/*",EnumSet.allOf(DispatcherType.class)); + }), "/*"); - server.setHandler(context); + startServer(context); - try - { - server.start(); - String resp = conn.getResponses("GET /err/ HTTP/1.1\n" + "Host: localhost\n" + "Connection: close\n" + "\n"); - assertThat("Response status",resp,containsString("HTTP/1.1 500 Server Error")); - assertThat("Response",resp,containsString(FooRuntimeException.class.getName())); - } - finally - { - server.stop(); - } + String httpResponse = connector.getResponses("" + + "GET / HTTP/1.1\r\n" + + "Host: localhost\r\n" + + "Connection: close\r\n" + + "\r\n"); + assertThat(httpResponse, containsString("HTTP/1.1 200 ")); + assertThat(httpResponse, containsString("DATA")); } - /** - * async testcase of error handling from a filter. - * - * Async Started, no listener, in start() throw Exception - * - * @throws Exception - * on test failure - */ - @Test - public void testFilterErrorAsyncStart_NoListener_ExceptionDuringStart() throws Exception + + // Unique named RuntimeException to help during debugging / assertions. + public static class TestRuntimeException extends RuntimeException { - Server server = new Server(); - LocalConnector conn = new LocalConnector(server); - conn.setIdleTimeout(10000); - server.addConnector(conn); + } - ServletContextHandler context = new ServletContextHandler(); - context.setContextPath("/"); - @SuppressWarnings("serial") - HttpServlet servlet = new HttpServlet() + public static class AsyncListenerAdapter implements AsyncListener + { + @Override + public void onComplete(AsyncEvent event) throws IOException { - public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException - { - AsyncContext ctx = req.startAsync(); - ctx.setTimeout(1000); - ctx.start(new Runnable() - { - @Override - public void run() - { - throw new FooRuntimeException(); - } - }); - } - }; - ServletHolder holder = new ServletHolder(servlet); - holder.setAsyncSupported(true); - context.addServlet(holder,"/err/*"); - List<ErrorContext> tracking = new LinkedList<ErrorContext>(); - ErrorFilter filter = new ErrorFilter(tracking); - context.addFilter(new FilterHolder(filter),"/*",EnumSet.allOf(DispatcherType.class)); + } - server.setHandler(context); + @Override + public void onTimeout(AsyncEvent event) throws IOException + { + } - try + @Override + public void onError(AsyncEvent event) throws IOException { - server.start(); - String resp = conn.getResponses("GET /err/ HTTP/1.1\n" + "Host: localhost\n" + "Connection: close\n" + "\n"); - assertThat("Response status",resp,containsString("HTTP/1.1 500 Server Error")); - assertThat("Response",resp,containsString(FooRuntimeException.class.getName())); } - finally + + @Override + public void onStartAsync(AsyncEvent event) throws IOException { - server.stop(); } } + + @FunctionalInterface + private interface IOConsumer<T> + { + void accept(T t) throws IOException; + } } |