Hello Rossen,

> On 22 Feb 2018, at 02:51, Rossen Stoyanchev <rstoyanc...@pivotal.io> wrote:
> ​​hi,
> > Simone Bordet:
> > As the WebSocket APIs were moved to incubation and are now slated
> > for JDK 11, it may be worth considering again whether it's the
> > case to provide a Flow-based APIs.
> I second that. There were two main issues pointed out in this thread:
> > The first one is how to communicate errors back to the user's
> > code that publishes messages to WebSocket.
> Isn't it mostly low level, transport errors about which the application can't 
> do much about, but clean up and close the connection? Arguably a cancellation 
> is adequate, or do you have more specific errors and cases in mind? 
> Alternatively, the JSR-356 WebSocket API provides a single onError callback 
> on the Endpoint type. I haven't come across any limitations around that.

I can't see how Flow (RS) can be seamlessly married with this low-level
WebSocket API. Maybe you might help.

1. If there is no error reporting, then how would the user know if there's
something wrong with the data they send? I'm talking about incomplete/malformed
UTF-8, fragments intermixing, sending messages after a Close message has been
sent, etc. How should the user troubleshoot a situation where the WebSocket's
internal Subscriber cancels the subscription unexpectedly? We can't perform all
the checks during a message creation. Some messages could not be checked up
until they appear in some context. For example, a text fragment is a okay per
se. But if it appears in the midst of a fragmented binary message, it is not
okay. Should the API not allow fragmented (partial) messages then? Or should we
provide error reporting out-of-band in relation to Flow? If we allow only whole
messages, then we are not flexible enough to support streaming. In other words
the case of an arbitrarily large message that does not have to be assembled
prior to consumption.

2. How should the user close their side of the communication? There seems to be
a mismatch in the protocols. WebSocket is bidirectional and relies on Close
messages. Flow is unidirectional-ish and relies on cancellation through
onComplete/onError and cancel signals. If the user has no more messages to
publish, the user may wish to send a Close message with a status code and a
reason. If instead we rely on onComplete/onError being called then we loose the
ability to provide these extra pieces of information. What should we choose
here? To not allow a Close message to be sent or to require the user to signal
onComplete() after a Close message has been signalled through onNext(). Maybe
there should not be a representation of a Close message at all. Maybe we should
leave it as a protocol low-level detail. But then, how should the API deliver
the received status code and the reason of a Close message it receives? Should
it translate the received Close message to onComplete()/onError() depending on
this message's contents?

3. WebSocket protocol mandates a peer should send a Close message as soon as
practical after receiving one. In the current API it is implemented using a
CompletionStage returned from the WebSocket.Listener.onClose method. How should
we implement this using Flow?

Having said that, it's either (but not both):

  a) We ditch low-level API (to Ping/Pong/Close and (possibly) incomplete
     messages and start using Flow, or
  b) We leave the API as it is and instead provide a Flow-adaptor suitable
     for the anticipated most common use case. As for now let's not dwell into
     details of who and when provides this adaptor.

> <snip>
> James Roper​ mentioned this already, but the Netty PooledByteBufAllocator 
> demonstrates it's feasible to recycle buffers transparently without the need 
> for a per-message confirmation. I don't think the argument holds that not 
> having per message completion signals forces unnecessary allocations, garbage 
> creation and copying.

Sure one may recycle buffers transparently. But only if the
buffer type supports it. io.netty.buffer.PooledByteBufAllocator provides
instances of io.netty.buffer.ByteBuf. Not java.nio.ByteBuffer. The latter one
does not have any release/free/dispose/etc. methods and semantics.

> There were a "dozen of smaller issues" mentioned. However there were no links 
> to earlier discussions, so I'm not sure what those were. 

If you want you can spend some time tracking the evolution of the API:


I'm sorry I don't have more relevant links to hand.

> The main purpose of Reactive Streams is to connect components from different 
> libraries. The currently proposed WebSocket API cannot be easily integrated 
> into a Reactive Streams pipeline. One would have to build a bridge to 
> Reactive Streams first and I don't think that should be left as a separate 
> exercise, since Flow is in the JDK and it's the reason why it was introduced, 
> to provide a common contract for such scenarios.

I completely understand the value of RS (Flow) and personally think it has a lot
of scenarios it is applicable to. I'm not sure it is the case here though.

> <snip>​

P.S. How may purely RS WebSocket APIs are there? If there are some, we can 
learn from their experience and make things a bit better. Otherwise I don't see
why we should create one more of them. Would you rather have something
low-level, you could then implement your high-level API that is most relevant to
your case?


Reply via email to