[wildfly-dev] WF 8.0 HTTP Upgrade help needed

Stuart Douglas stuart.w.douglas at gmail.com
Fri Apr 4 03:02:49 EDT 2014


Try something like the code below:


public class AsyncEchoUpgradeServlet extends HttpServlet {

     @Override
     protected void doGet(final HttpServletRequest req, final 
HttpServletResponse resp) throws ServletException, IOException {
         req.upgrade(Handler.class);
     }

     public static class Handler implements HttpUpgradeHandler {

         @Override
         public void init(final WebConnection wc) {
             Listener listener = new Listener(wc);
             try {
                 //we have to set the write listener before the read 
listener
                 //otherwise the output stream could be written to 
before it is
                 //in async mode
                 wc.getOutputStream().setWriteListener(listener);
                 wc.getInputStream().setReadListener(listener);
             } catch (IOException e) {
                 UndertowLogger.REQUEST_IO_LOGGER.ioException(e);
             }
         }

         @Override
         public void destroy() {

         }
     }


     private static class Listener implements WriteListener, ReadListener {

         private final WebConnection connection;
         private final Queue<String> queue = new ArrayDeque<String>();

         private Listener(final WebConnection connection) {
             this.connection = connection;
         }

         @Override
         public synchronized void onDataAvailable() throws IOException {
             byte[] data = new byte[100];
             while (connection.getInputStream().isReady()) {
                 int read;
                 if ((read = connection.getInputStream().read(data)) != 
-1) {
                     queue.add(new String(data, 0, read));
                 }
                 onWritePossible();
             }
         }

         @Override
         public void onAllDataRead() throws IOException {

         }

         @Override
         public synchronized void onWritePossible() throws IOException {

             while (!queue.isEmpty() && 
connection.getOutputStream().isReady()) {
                 String data = queue.poll();
                 connection.getOutputStream().write(data.getBytes());
             }
         }

         @Override
         public void onError(final Throwable t) {

         }
     }
}



Przemyslaw Bielicki wrote:
> Hi Stuart,
>
> thx for the explanations. There is a problem anyway - if you set both
> read and write listeners from the handler it won't work at all as in the
> write listener ServletOutputStream.isReady() will be returning true all
> the time (why not? the output is ready to send some data at the same
> time in is ready to read some data), and the read listener will be
> ignored (as the tread is focused on the writer). The solution here is
> not to have while (out.isReady()) loop in the writer but a simple check
> if (out.isReady()), but then you have to call onWritePossible yourself.
> It means you need to pass a write listener reference to the read
> listener - it's smells more than a bit.
>
> I think I have my conclusion, that is: it is impossible to achieve
> full-duplex (thus multiplexing) in an upgraded protocol using Servlet
> 3.1 API as there is "only ever one IO thread per connection" (I didn't
> find such limitation in the specification - can you point out the
> section in which it is said there should be on thread per connection?).
> In order to achieve multiplexing one have to explicitly create a
> separate thread dealing with either reading or writing the data.
> Also, using Servlet 3.1 it is impossible to achieve non-blocking read
> and write and multiplexing on the same connection, even using a separate
> thread.
>
> I will contact the specification owners to see their opinion.
>
> Many thanks,
> Przemyslaw
>
>
> On Fri, Apr 4, 2014 at 12:33 AM, Stuart Douglas
> <stuart.w.douglas at gmail.com <mailto:stuart.w.douglas at gmail.com>> wrote:
>
>
>
>     Przemyslaw Bielicki wrote:
>
>         I tried - exactly the same results.
>
>         Another weird observation is that ServletOutputStream.isReady() is
>         returning true even after the connection is closed
>         (ServletInputStream.__isFinished() is correctly returning true).
>
>         Here's the scenario that works but I can write back the data
>         only once:
>         1. In HttpUpgradeHandler I set only  the ReadListener
>         2. I switch the protocol and send some data
>         3. ReadListener gets activated i.e. onDataAvailable() is called.
>         4. I process the input data, read as much as possible and put
>         the input
>         into the queue
>         5. From within ReadListener I set the WriteListener
>         6. WriteListener.onWritePossible(__) gets called and I process
>         the data -
>         I clean the queue
>         7. As long as I'm in WriteListener.onWritePossible(__)
>         (while.out.isReady() is constantly returning true, which is a
>         correct
>         bahavior) the ReadListener is on-hold. I can send as much data
>         as I like
>         but onDataAvailable() is not called
>         8. Only when I leave WriteListener.onWritePossible(__) method the
>         ReadListener.onDataAvailable() is called again and I can consume the
>         input data again.
>         9. I can process the input data again i.e. put it into the queue but
>         WriteListener.onWritePossible(__) is never called again. When I
>         try to
>         reset it I get IllegalStateException
>
>         Either the specification or implementation seem not very mature....
>         Wildfly behavior is consistent with the one of Tomcat.
>
>
>     As Remy said this is expected.
>     Basically there is only ever one IO thread per connection, so only
>     one method will be active at a time.
>
>     The reason why your listener method is not being called again would
>     become apparent if you look at the javadoc for the read/write listeners:
>
>     Subsequently the container will invoke this method if and only
>     if {@link javax.servlet.__ServletOutputStream#isReady()} method
>     has been called and has returned <code>false</code>.
>
>     Basically what this means is that the listener is only invoked if
>     isReady() returns false at some point. If you have read some data
>     and then you want to echo it you should call the onWritePossible
>     method yourself, after you have received the data.
>
>     Stuart
>
>
>
>
>         At the moment I conclude that the non-blocking write is not
>         possible in
>         Servlet 3.1.
>
>         I would appreciate if someone can provide an example that
>         actually works
>         or explain why the weird behavior I observe is correct (is it?)
>
>         Cheers,
>         Przemyslaw
>
>
>
>
>         On Thu, Apr 3, 2014 at 6:18 AM, Stuart Douglas
>         <stuart.w.douglas at gmail.com <mailto:stuart.w.douglas at gmail.com>
>         <mailto:stuart.w.douglas at __gmail.com
>         <mailto:stuart.w.douglas at gmail.com>>> wrote:
>
>              Can you try with the latest development build of Wildfly (from
>         https://ci.jboss.org/hudson/____job/WildFly-latest-master/
>         <https://ci.jboss.org/hudson/__job/WildFly-latest-master/>
>         <https://ci.jboss.org/hudson/__job/WildFly-latest-master/
>         <https://ci.jboss.org/hudson/job/WildFly-latest-master/>>).
>
>
>              There have been some fixes in this area, so your problem
>         may have
>              already been fixed.
>
>              Stuart
>
>
>              PB wrote:
>
>                  Hi,
>
>                  I'm testing the HTTP Upgrade feature of WF 8.0 and I'm
>         facing
>                  some banal
>                  problem. Basically my ReadListener is NEVER called.
>                  Here's the code:
>
>                  @WebServlet(urlPatterns = "/upgrade")
>                  public class UpgradeServlet extends HttpServlet {
>                      @Override
>                      protected void doGet(HttpServletRequest req,
>         HttpServletResponse
>                  resp) throws ServletException, IOException {
>                        if
>
>         ("upgrade".equalsIgnoreCase(____req.getHeader("Connection"))) {
>                          req.upgrade(EchoHandler.class)____;
>
>                        }
>                      }
>                  }
>
>                  public class EchoHandler implements HttpUpgradeHandler {
>                      @Override
>                      public void init(WebConnection wc) {
>                        try {
>                          ServletInputStream in = wc.getInputStream();
>                          ServletOutputStream out = wc.getOutputStream();
>
>                          BlockingQueue<String> queue = new
>                  LinkedBlockingQueue<String>();
>                          in.setReadListener(new EchoReadListener(queue,
>         in));
>                          out.setWriteListener(new
>         EchoWriteListener(queue, out));
>                        } catch (IOException e) {
>                          throw new IllegalStateException(e);
>                        }
>                      }
>
>                  public class EchoReadListener implements ReadListener {
>                      @Override
>                      public void onDataAvailable() throws IOException {
>                        while (in.isReady()) {
>                          int length = in.read(buffer);
>                          String input = new String(buffer, 0, length);
>                          if (false == queue.offer(input)) {
>                            System.err.println("'" + input + "' input was
>         ignored");
>                          }
>                        }
>                      }
>
>                  I'm connecting to WF using telnet and sending the
>         upgrade request:
>                  GET /example-webapp/upgrade HTTP/1.1
>                  Host: localhost
>                  Connection: upgrade
>                  Upgrade: echo
>
>                  and I'm getting correct response:
>
>                  HTTP/1.1 101 Switching Protocols
>                  Connection: Upgrade
>                  X-Powered-By: Undertow 1
>                  Server: Wildfly 8
>                  Content-Length: 0
>
>                  which means that from now on the protocol between my telnet
>                  client and
>                  WF is pure TCP.
>                  So, I start typing some text, hit Enter and.... nothing
>         happens.
>                  onDataAvailable() is NEVER called. More so, this makes
>         WF totally
>                  irresponsive - my whole webapp is dead.
>
>                  I believe, I'm doing something wrong - any ideas what
>         exactly?
>                  There is
>                  also a slight chance that Upgrade feature in WF is
>         f****d :)
>                  Anyway, WF should not block even in case my upgraded
>         protocol is not
>                  working correctly?
>
>                  Many thanks,
>                  Przemyslaw
>
>                  ___________________________________________________
>                  wildfly-dev mailing list
>         wildfly-dev at lists.jboss.org <mailto:wildfly-dev at lists.jboss.org>
>         <mailto:wildfly-dev at lists.__jboss.org
>         <mailto:wildfly-dev at lists.jboss.org>>
>         https://lists.jboss.org/____mailman/listinfo/wildfly-dev
>         <https://lists.jboss.org/__mailman/listinfo/wildfly-dev>
>         <https://lists.jboss.org/__mailman/listinfo/wildfly-dev
>         <https://lists.jboss.org/mailman/listinfo/wildfly-dev>>
>
>
>
> _______________________________________________
> wildfly-dev mailing list
> wildfly-dev at lists.jboss.org
> https://lists.jboss.org/mailman/listinfo/wildfly-dev


More information about the wildfly-dev mailing list