[whatwg] Issues with Web Sockets API

On Fri, 26 Jun 2009, James Robinson wrote:
> 
> 0) postMessage() looks as if it is intended to mimic 
> MessagePort.postMessage(), but the arguments and error conditions are 
> different.  While it would be conceptually nice to treat a web socket in 
> the same way as a message port, it's not possible to treat the two 
> postMessage() functions in the same way.  I'd recommend the WebSocket 
> version be renamed to something like "send()" to avoid confusion and 
> false expectations.

Fair enough. Done.



> There's similar oddness with receiving events that satisfy the MessageEvent
> interface - since all fields except 'data' will necessarily be invalid I
> don't see the value in receiving something more complex.

I would like to avoid introducing four different event types for the four 
different types of 'message' events being introduced, which is why I 
overloaded the same interface for all four. I don't think it's a problem.


> 1) The 'readyState' attribute can never actually be used by an application
> and is redundant.
> 
> Initially, the 'readyState' attribute is set to CONNECTING, but while 
> the object is in this state the user is not permitted to interact with 
> the WebSocket in any way.  The only useful thing that a user could do is 
> set event handlers and wait for the 'open' event to fire.  When the 
> WebSocket becomes connected, the readyState becomes 1 and the 'open' 
> event is fired. Once the WebSocket is open, the spec states that 
> whenever the connection is closed the readyState changes to CLOSED and a 
> 'close' event is enqueued. However, users can't usefully check the 
> readyState to see if the WebSocket is still open because there are not 
> and cannot be any synchronization guarantees about when the WebSocket 
> may close.  A user will have to wrap all calls to postMessage() (or 
> send() if the function is renamed) in a try/catch block in order to 
> handle INVALID_STATE_ERRs.  Once the 'close' event has been received the 
> readyState attribute is useless since the state of the WebSocket is 
> known and can never change.
>
> I think 'readyState' should just go away since an application will have 
> to keep track of state updates through the fired events and use 
> try/catch blocks around all API calls anyway.

The attribute is mostly present for debugging purposes. I wouldn't expect 
anyone to actually use it for production work.


On Fri, 26 Jun 2009, Drew Wilson wrote:
>
> Yes, but the "closed" state of a given WebSocket doesn't have to exactly 
> match the state of the underlying TCP connection, in the same way that 
> document.cookies doesn't exactly match the current set of cookies that 
> the network stack may be tracking (they can differ when HTTP responses 
> are received in the background while JS is executing).
> 
> So if the remote server closes the TCP connection, it generates a 
> "close" event which marks the WebSocket as closed. It means that you 
> could have a situation where you post messages to a WebSocket which 
> aren't received by the server because the connection is closed, but 
> that's true regardless due to the asynchronous nature of the networking 
> protocol.

I've changed the spec to not throw an exception on send() if the 
connection is closed.


On Fri, 26 Jun 2009, Kelly Norton wrote:
>
> One thing about postMessage that I'm curious about. Since it has to 
> report failure synchronously by throwing an INVALID_STATE_ERR, that 
> seems to imply that all data must be written to a socket before 
> returning and cannot be asynchronously delivered to an I/O thread 
> without adding some risk of silently dropping messages. Seems like the 
> right choice would be to allow outbound messages to drop, which would 
> mean that developers would be forced to do their own handshaking.

send() doesn't report I/O errors, it only throws an exception if the 
connection isn't open yet (and previously, if the connection had died 
prior to it being called, though that is no longer the case), or if the 
input is invalid.


> I'm also not sure there is good coverage of error conditions in the 
> spec. The only methods of error notification are exceptions in 
> postMessage and onclose.

In fact, only onclose actually reports an error; the exception from send() 
now only reports a misuse of the API, not a network error.


> I had assumed that a WebSocket that fails to connect would invoke 
> onclose asynchronously, but I didn't see that in the spec.

It's there, though subtle. :-)

The constructor in the API spec invokes the "Establish a Web Socket 
connection" algorithm from the protocol spec. That algorithm then invokes 
the "fail the Web Socket connection" algorithm upon failure, and that 
algorithm says to invoke the "close the Web Socket connection" algorithm, 
and that algorithm says that this means that "Web Socket connection is 
closed", and the API spec says "When the Web Socket connection is closed, 
the readyState attribute's value must be changed to CLOSED (2), and the 
user agent must queue a task to fire a simple event called close at the 
WebSocket object".

I've added a note that says this.


> Without that you don't even have the ability to know if a 
> socket failed to establish a connection (short of readyState polling). 
> The spec also doesn't indicate that the readyState should transition to 
> CLOSED on connection failure.

This is part of the same sequence of events as described above.


On Fri, 26 Jun 2009, Kelly Norton wrote:
>
> Doesn't it seem strange that disconnect() causes an onclose event to be 
> dispatched? Should the method not be close() to be consistent with 
> open(), onopen, onclose?

I've renamed disconnect() to close() in both EventSource and WebSocket.


On Fri, 26 Jun 2009, Michael Nordman wrote:
>
> Does disconnect() attempt to flush pending messages or drop them? There 
> isn't a way to determine if the WebSocket is successfully sending the 
> postMessage data? For all the caller knows, its just backing up and not 
> going anywhere.
> 
> Something that might add value is an onmessagesent event that fires 
> after a postMessage has put the bits on the wire.

If you want acknowledgements, implement app-level acks -- in practice, 
there's not much difference between hitting the network or not, if the 
other side hasn't yet received the packets.


> postMessage() may want another exception condition... 'too much data 
> pending exception'... consider calling postMessage in a while(true) 
> loop... at some point the system is going to have to give up queing the 
> data if its not actually making its way out on the wire.

The spec doesn't specify how UAs are to handle hitting hardware 
limitations or system limitations, because it's often difficult to truly 
control how those cases are handled.


On Fri, 26 Jun 2009, James Robinson wrote:
>
> Not changing variables out from under executing JavaScript makes a lot 
> of sense, but if that was the case then it's not clear when the 
> readyState could be updated.  The spec states "When the *Web Socket 
> connection is closed*, the readyState attribute's value must be changed 
> to CLOSED (2), and the user agent must queue a task to fire a simple 
> event called close at the WebSocket object." If the browser cannot 
> mutate the readyState until JavaScript stops running then it would 
> either have to either enqueue a second task to change readyState at some 
> point in the future or set the readyState right before dispatching the 
> 'close' event.  The latter would be much nicer to implement - but then 
> it does make the readyState completely useless as it would always be 
> exactly equivalent to the last event that was fired on a given 
> WebSocket.

I've left it as is (the attribute changes on the fly), which is possibly 
risky, but more consistent with how such attributes are handled in 
general.


> I think a better way to do error handling is to have an asynchronous 
> onerror callback or event when the browser notes that a message did not 
> make it to the other side.

The client can't really always know this anyway. I think it's better to do 
app-level acking if you care enough.


On Fri, 26 Jun 2009, James Robinson wrote:
> 
> The concept of a port being in a closed state is not very well defined - 
> if the state means only the readyState status, then when can the state 
> legally be updated?  If it has some meaning closer to the state of the 
> underlying connection, then it can't be queried synchronously without 
> very expensive synching to the I/O thread or process.
> 
> Forcing applications to build their own send/ack functionality would be 
> pretty tragic considering that WebSockets are built on top of TCP.

If we had access to the underlying TCP packets, I guess we could return a 
number with each send() and have some way to query the number of the 
mesage that the most recent packet that got all the way to other side 
contained, but in practice I don't think that implementations are always 
going to have access to that, and also it's not just that the other stack 
got the message that matters, but that the remote server actually got the 
messages and processed it.


On Fri, 26 Jun 2009, Michael Nordman wrote:
>
> If you're uploading a large data set incrementally across many distinct 
> postMessage calls (perhaps to leave room for other control messages 
> interspersed amoungst them, or to present progress info), how do you 
> know when to queue more data to be sent.

I think when we add support for file upload, we'll make it so that it 
automagically supports this case. That is, you'll say "upload this file in 
small bits" and then if you later say "send this text message", the text 
message will be sent before any pending file bits. We can use a separate 
type of packet in the WebSocket stream to do this.

-- 
Ian Hickson               U+1047E                )\._.,--....,'``.    fL
http://ln.hixie.ch/       U+263A                /,   _.. \   _\  ;`._ ,.
Things that are impossible just take longer.   `._.-(,_..'--(,_..'`-.;.'

Received on Monday, 6 July 2009 21:30:18 UTC