[wildfly-dev] WF 8.0 HTTP Upgrade help needed

Przemyslaw Bielicki pbielicki at gmail.com
Fri Apr 4 03:19:37 EDT 2014


thanks a lot - it seems to work but:

1. Synchronized is bad :) anyway, you wrote that there is only one thread
so synchronization is not needed
2. Main issue: it's not bi-directional full-duplex (like WebSockets) i.e.
you are not able to receive and send data at the same time, it's still
synchronous

many thanks anyway - nice try :)


On Fri, Apr 4, 2014 at 9:02 AM, Stuart Douglas
<stuart.w.douglas at gmail.com>wrote:

> 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
>>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.jboss.org/pipermail/wildfly-dev/attachments/20140404/66a34c85/attachment-0001.html 


More information about the wildfly-dev mailing list