[wildfly-dev] WF 8.0 HTTP Upgrade help needed

Przemyslaw Bielicki pbielicki at gmail.com
Fri Apr 4 03:30:27 EDT 2014


cool, thx a lot - that should do the trick :)



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

>
>
>
> On Fri, Apr 4, 2014 at 6:19 PM, Przemyslaw Bielicki <pbielicki at gmail.com>wrote:
>
>> 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
>>
>
> oops, that was a copy paste error, it 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
>>
>
> Web sockets is exactly the same. There is generally only one thread, and
> it handles both read and write notifications. If you are worried about the
> read thread just reading constantly and the write thread never getting a go
> then just put a limit on how much data can be queued before it is written
> out.
>
> At one point XNIO did have separate read and write threads, however it
> ended up being much slower as you end up needing to synchronise. A single
> IO thread is faster, you just have to be careful of how much you buffer on
> the read side before writing.
>
> Stuart
>
>
>>
>> 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/b61cccbf/attachment-0001.html 


More information about the wildfly-dev mailing list