From 2971f868d582beb3c2da7a7983d50e4b0c199e09 Mon Sep 17 00:00:00 2001 From: Greg Wilkins Date: Thu, 5 Feb 2015 08:46:02 +1100 Subject: [PATCH 1/3] fixed header --- .../main/java/org/eclipse/jetty/embedded/ExampleServer.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ExampleServer.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ExampleServer.java index 61e4ecca194..54e1261f824 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ExampleServer.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ExampleServer.java @@ -37,8 +37,9 @@ public class ExampleServer server.setConnectors(new Connector[] { connector }); ServletContextHandler context = new ServletContextHandler(); - context.setContextPath("/hello"); - context.addServlet(HelloServlet.class, "/"); + context.setContextPath("/"); + context.addServlet(HelloServlet.class, "/hello"); + context.addServlet(AsyncEchoServlet.class, "/echo/*"); HandlerCollection handlers = new HandlerCollection(); handlers.setHandlers(new Handler[] { context, new DefaultHandler() }); From 1576c68a529cee00a02e6a4c80528d0f0d631f53 Mon Sep 17 00:00:00 2001 From: Greg Wilkins Date: Thu, 5 Feb 2015 09:36:05 +1100 Subject: [PATCH 2/3] fixed finger trouble bugs --- .../jetty/embedded/AsyncEchoServlet.java | 107 ++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 examples/embedded/src/main/java/org/eclipse/jetty/embedded/AsyncEchoServlet.java diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/AsyncEchoServlet.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/AsyncEchoServlet.java new file mode 100644 index 00000000000..74231763c8f --- /dev/null +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/AsyncEchoServlet.java @@ -0,0 +1,107 @@ +// +// ======================================================================== +// Copyright (c) 1995-2015 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.embedded; + +import java.io.IOException; +import java.util.concurrent.atomic.AtomicBoolean; + +import javax.servlet.AsyncContext; +import javax.servlet.ReadListener; +import javax.servlet.ServletException; +import javax.servlet.ServletInputStream; +import javax.servlet.ServletOutputStream; +import javax.servlet.WriteListener; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + + +public class AsyncEchoServlet extends HttpServlet +{ + @Override + protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException + { + AsyncContext asyncContext = request.startAsync(request, response); + asyncContext.setTimeout(0); + Echoer echoer = new Echoer(asyncContext); + request.getInputStream().setReadListener(echoer); + response.getOutputStream().setWriteListener(echoer); + } + + private class Echoer implements ReadListener, WriteListener + { + private final byte[] buffer = new byte[4096]; + private final AsyncContext asyncContext; + private final ServletInputStream input; + private final ServletOutputStream output; + private final AtomicBoolean complete = new AtomicBoolean(false); + + private Echoer(AsyncContext asyncContext) throws IOException + { + this.asyncContext = asyncContext; + this.input = asyncContext.getRequest().getInputStream(); + this.output = asyncContext.getResponse().getOutputStream(); + } + + @Override + public void onDataAvailable() throws IOException + { + onWritePossible(); + } + + @Override + public void onAllDataRead() throws IOException + { + onWritePossible(); + } + + @Override + public void onWritePossible() throws IOException + { + // This method is called: + // 1) after first registering a WriteListener (ready for first write) + // 2) after first registering a ReadListener iff write is ready + // 3) when a previous write completes after an output.isReady() returns false + // 4) from an input callback + + // We should try to read, only if we are able to write! + while (output.isReady() && input.isReady()) + { + int read = input.read(buffer); + if (read<0) + { + if (complete.compareAndSet(false,true)) + asyncContext.complete(); + break; + } + else if (read>0) + { + output.write(buffer, 0, read); + } + } + } + + @Override + public void onError(Throwable failure) + { + failure.printStackTrace(); + asyncContext.complete(); + } + } +} From d08fced01065292ea1e48f6d81ac0fcfa5553887 Mon Sep 17 00:00:00 2001 From: Greg Wilkins Date: Thu, 5 Feb 2015 14:27:05 +1100 Subject: [PATCH 3/3] handle slow arriving input --- .../org/eclipse/jetty/server/HttpInput.java | 29 ++++++++++++++----- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpInput.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpInput.java index ece4ce7bc07..b1cb67f985b 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpInput.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpInput.java @@ -365,16 +365,29 @@ public abstract class HttpInput extends ServletInputStream implements Runnabl @Override public void setReadListener(ReadListener readListener) { - readListener = Objects.requireNonNull(readListener); - synchronized (lock()) + try { - if (_contentState != STREAM) - throw new IllegalStateException("state=" + _contentState); - _contentState = ASYNC; - _listener = readListener; - _notReady = true; + readListener = Objects.requireNonNull(readListener); + boolean content; + synchronized (lock()) + { + if (_contentState != STREAM) + throw new IllegalStateException("state=" + _contentState); + _contentState = ASYNC; + _listener = readListener; + _notReady = true; + + content = getNextContent()!=null; + } + if (content) + _channelState.onReadPossible(); + else + unready(); + } + catch(IOException e) + { + throw new RuntimeIOException(e); } - _channelState.onReadPossible(); } public void failed(Throwable x)