jsr356-experts@websocket-spec.java.net

[jsr356-experts] Re: Streaming API: was For Review: v002 API and example code

From: Greg Wilkins <gregw_at_intalio.com>
Date: Thu, 12 Jul 2012 09:43:33 +0200

On 12 July 2012 01:19, Scott Ferguson <ferg_at_caucho.com> wrote:
> "async" is a bit imprecise here, which matters for this discussion.

It is true that all the APIs proposed are async, but some also have
some blocking nature.
When I say that the API should be async I mean that it should NEVER
hold a thread while waiting for IO to arrive.

When receiving a message the simplest callback pattern is something
like (names are just generic and not from any proposal):

   wsSession.onMessage(ByteBuffer entireMessage);

but as messages can be large (infinite!) this is not a good API, as it
will fail if there is not enough memory available for the message.
Some applications can live with a configured max message size, but
others cannot. So we need a way to deliver partial messages to the
application. One suggestion is something like:

  wsSession.onFrame(ByteBuffer frame, boolean last);

This actually does not help because frames themselves can be stupidly
large. It also exposes a transport boundary that should not be
visible to the application and may encourage applications to
incorrectly give frame boundaries some semantic. However the very
similar

  wsSession.onMessagePart(ByteBuffer partialMessage, boolean last);

Does solve the memory problem as the partial message is decoupled from
the frame. A single frame may be delivered in several partial message
callbacks or several frames might be aggregated into a single partial
message callback. This style is fully asynchronous but perhaps a
little ugly with that last boolean.

The alternate that has been proposed is

  wsSession.onMessageStream(InputStream messageStream);

This style is only partially asynchronous because the message is read
via the blocking stream API, so the application will start reading
from the stream when the first frame arrives, but will then block
waiting for more frames to read. The pros of this style are that the
input stream can be given to libraries that know about stream and also
the application gets to control the size of the partial message
delivery as it allocates the byte arrays that are passed to the input
stream. However it is pretty simple to wrap the onMessagePart API
above in an InputStream if that is needed, so I don't think this
legacy library thing is a big deal.

The other con with streams is it puts us firmly back into byte[] land,
when surely we should be using ByteBuffers! ByteBuffers are better
that byte[] as using them can often avoid a copy of data (as the pos
and limit can be set to ignore surrounding data) and other types of
memory can be used (eg sending file mapped buffers).

Also the blocking nature of the read is just asking for a DOS attack
where a single frame is sent and then the rest of the message is never
delivered - this will hold a thread forever or until some timeout.
With multiplexing, it would be easy for a single connection to consume
many threads in this way.


I guess we could consider

 wsSession.onMessageChannel(AsynchronousByteChannel message);

but channels are bidirectional, which is kind of confusing.


So currently I think the application should be given the option of using either

   wsSession.onMessage(ByteBuffer entireMessage);

or

  wsSession.onMessagePart(ByteBuffer partialMessage, boolean last);



-- 
Greg Wilkins <gregw_at_intalio.com>
www.webtide.com
Developer advice, services and support
from the Jetty & CometD experts.