websockets

Chris Hegarty chris.hegarty at oracle.com
Sat Feb 24 11:55:27 UTC 2018


Rossen,

> On 23 Feb 2018, at 21:15, Rossen Stoyanchev <rstoyanchev at pivotal.io> wrote:
> 
> hi Pavel,
> 
> On Thu, Feb 22, 2018 at 8:43 AM, Pavel Rappo <pavel.rappo at oracle.com <mailto:pavel.rappo at oracle.com>> wrote:
> 
> 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.
> 
> Charset encoding issues aside, are these represented with specific exceptions, and how much can the application do with them?

An application can handle, and possibly recover.

From WebSocketMessage [1]:

    /**
     * Return the message payload as UTF-8 text. This is a useful for text
     * WebSocket messages.
     */
    public String getPayloadAsText() {
	byte[] bytes = new byte[this.payload.readableByteCount()];
	this.payload.read(bytes);
	return new String(bytes, StandardCharsets.UTF_8);
    }

This seems woefully inadequate, and underspecified. Not to mention what
happens if the message is not text. The Java SE API attempts to avoid such.


> I don't know the fragments intermixing issue but can the application recover from that?

With the Java SE API, yes.

> It is of course more useful for the API to expose error information for each sent message, but if those are low level errors, treated as terminal, then only the first one practically matters.

That is not always the case. And this is a low-level API.

> I'm unsure about the exact meaning of fragments​. JSR-356 has partial messages, which if I recall were explicitly called out in the spec as not being different from (and not 1-for-1 with) WebSocket fragments. I'll assume that you mean the same (i.e. the send methods that take an extra isLast flag). WebSocket is not an application-level protocol and in my experience, streaming by splitting large content, is a higher level concern. For example the JavaScript client for STOMP over WebSocket some time ago started splitting larger STOMP messages along 16K boundaries to work better with the default buffer sizes of most servers. I'm not saying such feature shouldn't be present but I don't think it's a must.

Why preclude it, when it is not necessary to do so?

> If required, one way to represent it is to allow writing with a Publisher<Publisher<ByteBuffer>>.

Yuck!

> 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?
> 
> I think the API can provide such a close(CloseStatus) method. That would result in a cancellation of the write Publisher.
>  
> 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?
> 
> It is hard to discuss on this level of detail without a POC. 

We have gone through many POC’s over the past 3 or more years, bringing
us to where we are today. Much of this work and is archived on the net-dev
mailing list, if you wish to review it. It’s the only way to validate API
suggestions.

> I'd assume the API exposes a "read" Publisher<WebSocket>, a way to send with a "write" Publisher<WebSocket> and some Handler/Listener that returns Publisher<Void> (or CompletionStage) for when handling is complete. The "read" Publisher would complete when the server closes. The Publisher<Void> from the Handler/Listener would complete when all is handled. 
> 
>  
> 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.
> 
> ​If the API exposed some Consumer<ByteBuffer> callback to be applied to buffers that have been written, then such a pool could also be implemented externally. 
> ​​
> P.S. How may purely RS WebSocket APIs are there? If there are some, we can probably
> 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?
> 
> You can see the reactive WebSocket API facade (abstracting different clients) in the Spring Framework:
> https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/reactive/socket/package-summary.html <https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/reactive/socket/package-summary.html>
This is an example of just one way that WebSocket could be modelled.
It appears reasonably straight forward, but restrictive in terms of the
underlying protocol, not exposing partial messages for example,
requiring an additional allocation of a message type for each message
sent / received. I would expect that such a higher-level API could be
implemented on top of the Java SE API without too much work.

-Chris.

[1] https://github.com/spring-projects/spring-framework/blob/master/spring-webflux/src/main/java/org/springframework/web/reactive/socket/WebSocketMessage.java

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.openjdk.java.net/pipermail/net-dev/attachments/20180224/b68d5819/attachment-0001.html>


More information about the net-dev mailing list