From 6caa9b9a5c08a88fbd64604ae34e99c2ae6e73ed Mon Sep 17 00:00:00 2001 From: Greg Wilkins Date: Wed, 11 Dec 2019 15:09:15 +1100 Subject: [PATCH] Issue #4331 Close Complete Additional test of complete during blocking write. Signed-off-by: Greg Wilkins --- .../jetty/server/AsyncContextState.java | 6 +- .../jetty/server/AsyncCompletionTest.java | 66 +++++++++++++++++++ 2 files changed, 71 insertions(+), 1 deletion(-) diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/AsyncContextState.java b/jetty-server/src/main/java/org/eclipse/jetty/server/AsyncContextState.java index d02f9768e1f4..eb30265ba33c 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/AsyncContextState.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/AsyncContextState.java @@ -146,7 +146,11 @@ public void start(final Runnable task) @Override public void run() { - state().getAsyncContextEvent().getContext().getContextHandler().handle(channel.getRequest(), task); + ContextHandler.Context context = state().getAsyncContextEvent().getContext(); + if (context == null) + task.run(); + else + context.getContextHandler().handle(channel.getRequest(), task); } }); } diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/AsyncCompletionTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/AsyncCompletionTest.java index e9687451275c..5c7c1ed5d935 100644 --- a/jetty-server/src/test/java/org/eclipse/jetty/server/AsyncCompletionTest.java +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/AsyncCompletionTest.java @@ -178,6 +178,7 @@ public static Stream tests() tests.add(new Object[]{new AsyncWriteCompleteHandler(false, true), true, 200, __data}); tests.add(new Object[]{new AsyncWriteCompleteHandler(true, false), false, 200, __data}); tests.add(new Object[]{new AsyncWriteCompleteHandler(true, true), true, 200, __data}); + tests.add(new Object[]{new BlockingWriteCompleteHandler(), true, 200, __data}); return tests.stream().map(Arguments::of); } @@ -246,6 +247,10 @@ private static class AsyncReadyCompleteHandler extends AbstractHandler @Override public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { + // start async + // register WriteListener + // if ready write bytes + // if ready complete AsyncContext context = request.startAsync(); ServletOutputStream out = response.getOutputStream(); out.setWriteListener(new WriteListener() @@ -297,6 +302,14 @@ private static class AsyncWriteCompleteHandler extends AbstractHandler @Override public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { + // start async + // register WriteListener + // if ready + // if not written write bytes + // if _unReady check that isReady() returns false and return + // if _close then call close without checking isReady() + // context.complete() without checking is ready + AsyncContext context = request.startAsync(); ServletOutputStream out = response.getOutputStream(); out.setWriteListener(new WriteListener() { @@ -341,4 +354,57 @@ public String toString() return String.format("%s@%x{ur=%b,c=%b}", this.getClass().getSimpleName(), hashCode(), _unReady, _close); } } + + private static class BlockingWriteCompleteHandler extends AbstractHandler + { + final CountDownLatch _unReadySeen = new CountDownLatch(1); + boolean _written; + + BlockingWriteCompleteHandler() + { + } + + @Override + public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException + { + // Start async + // Do a blocking write in another thread + // call complete while the write is still blocking + AsyncContext context = request.startAsync(); + ServletOutputStream out = response.getOutputStream(); + CountDownLatch writing = new CountDownLatch(1); + context.start(() -> + { + try + { + byte[] bytes = __data.getBytes(StandardCharsets.ISO_8859_1); + response.setContentType("text/plain"); + response.setContentLength(bytes.length); + writing.countDown(); + out.write(bytes); + } + catch(Exception e) + { + e.printStackTrace(); + } + }); + + try + { + writing.await(5, TimeUnit.SECONDS); + Thread.sleep(200); + context.complete(); + } + catch(Exception e) + { + throw new ServletException(e); + } + } + + @Override + public String toString() + { + return String.format("%s@%x{}", this.getClass().getSimpleName(), hashCode()); + } + } }