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