[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