[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