Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Would a "closing" connection state be of any use? #240

Closed
tidoust opened this issue Jan 12, 2016 · 12 comments
Closed

Would a "closing" connection state be of any use? #240

tidoust opened this issue Jan 12, 2016 · 12 comments
Labels

Comments

@tidoust
Copy link
Member

tidoust commented Jan 12, 2016

Both WebSockets and WebRTC data channels switch to a closing state when the closing procedure is started. The state switches to closed at the end of the closing handshake, in other words when the underlying protocol triggers some signal that the connection should be regarded as closed.

The closing state makes sense from a procedural perspective (this matches how the user agent is going to handle the closing process). I do not know why this state is exposed to Web apps, though. It seems to me that all use cases I can think of would equally work with only the closed state that we have in the Presentation API.

However, the rationale that led to the closing state in WebSockets and WebRTC data channels probably applies to PresentationConnection as well. So, question is: does anyone know that rationale?

@tidoust
Copy link
Member Author

tidoust commented Jan 15, 2016

As suggested in #239 (comment)
... the closing state is useful internally in any case, so we may want to add a side note that this internal state would be helpful to implementation.

@tomoyukilabs
Copy link
Contributor

In case of WebSocket, for example, when a client calls close() method during data transmission from a server to the client, the state of the client becomes closing immediately, but the server would continue its data transmission and then send the close frame to the client. Therefore, the closing state of the closing initiator peer might mean that the remote peer might still be sending any data and the initiator peer should wait for receiving it if necessary.

@tidoust
Copy link
Member Author

tidoust commented Jan 15, 2016

Thanks @tomoyukilabs,

Your comment raises an important point, which I overlooked in my review of PR #239: it is good practice to continue listening to data as long as the underlying connection has not been closed. For instance, the Web Socket Protocol (TCP and WebRTC data channels have similar expectations, I believe) says:

By sending a Close frame and waiting for a Close frame in response, certain cases are avoided where data may be unnecessarily lost.
http://tools.ietf.org/html/rfc6455#section-1.4

In particular, the closed event should only fire when the client can no longer receive any message from the other side, which should thus happen once the closing handshake has been completed (ack received from the other side, or timeout).

From an app perspective, it feels strange to be able to receive a message after calling close(), but that matches how most communication protocols seem to operate.

@mfoltzgoogle that is how I had formulated my initial PR #238 (waiting for a signal from the other side becore triggering the closed event), although I had not really done that on purpose. This is no longer the case in PR #239, where the closed event is fired regardless of the status of the underlying connection.

@tomoyukilabs, back to the notion of closing state, I am still struggling to see how an app would use an explicit closing state. When would code such as if (connection.state === 'closing') {...} be needed?

It seems to me that, for all practical purpose, an app would only need to have code such as if (connection.state === 'closed') {...} or if (connection.state !== 'connected') {...}, and of course to listen to the closed event to detect when the connection is closed.

@tomoyukilabs
Copy link
Contributor

@tidoust closing state indicates that an app is not allowed to send any data and it should wait for receiving all remaining data if necessary. On the other hand, codes such as if (connection.state === 'connected') {...} might be sufficient to confirm whether send() method would be available or not, and if (connection.state === 'closed') {...} or closed event might be sufficient to know when all remaining data would be received.

Although closing state should be distinguished from connected and closed, eventually, explicit use of closing state might usually be unnecessary, as you mentioned.

(TCP and WebRTC data channels have similar expectations, I believe)

Yes, SCTP (underlying transport protocol of WebRTC data channel) also says:

  1. In the SHUTDOWN-RECEIVED state, the endpoint MUST transmit or retransmit data and leave this state when all data in queue is transmitted.
    https://tools.ietf.org/html/rfc4960#section-4

@tomoyukilabs
Copy link
Contributor

To see actual behavior of closing state, I have made example codes of WebSocket, like below:

window.addEventListener('load', function() {
  var state = ['CONNECTING', 'OPEN', 'CLOSING', 'CLOSED'];
  var log = document.getElementById('log');
  var ws = new WebSocket('wss://websocket-server/path');
  ws.addEventListener('open', function() {
    log.innerHTML += 'WebSocket: OPEN<br>';
    // close WebSocket connection during receiving message from a server
    setTimeout(function() {
      log.innerHTML += "WebSocket: close()<br>";
      ws.close();
    }, 50);
  });
  ws.addEventListener('message', function() {
    log.innerHTML += 'WebSocket: message received (readyState === "' + state[ws.readyState] + '")<br>';
  });
  ws.addEventListener('close', function() {
    log.innerHTML += 'WebSocket: CLOSED<br>';
  });
});

This example assumes that the WebSocket server would send a large message to the client as soon as the connection would have been established.

I tried this example, and several browsers such as Chrome, Firefox, Edge and IE11 showed the same result like below:

WebSocket: OPEN
WebSocket: close()
WebSocket: CLOSED

This result indicates that WebSocket client in most browsers would discard messages sent from a server after calling close(). In this result, State of the client became closed when the client finished receiving all messages from the server, i.e. state of the WebSocket connection had been closing until then.

On the other hand, Safari showed a different result like below:

WebSocket: OPEN
WebSocket: close()
WebSocket: message received (readyState === "CLOSING")
WebSocket: CLOSED

@tidoust
Copy link
Member Author

tidoust commented Jan 20, 2016

@tomoyukilabs that's very interesting, thanks for the investigation!

If I understand the specs, example (note the server seems down), and results properly, only Safari has the right behavior according to specs. All other browsers close the WebSocket too early.

Now, when one looks at it from an application perspective, it seems more straightforward not to have to code the logic needed to handle a message received after calling close, so "too early" may be what an application wants, which matches how the Presentation API is currently phrased.

I'll try to create a similar test example for WebRTC.

@tomoyukilabs
Copy link
Contributor

@tidoust Sorry, I found I had misconfigured my server. Now I have fixed it and the server is working now.

By the way, I tried to read the WebSocket interface spec, but I have not found any description about behavior in case of receiving messages during closing handshake, yet. The spec does not seem to say explicitly either such messages would be discarded or a message event could be fired in closing state.

I'll try to create a similar test example for WebRTC.

Thanks so much!

@tidoust
Copy link
Member Author

tidoust commented Jan 21, 2016

@tomoyukilabs Thanks, your example works fine now!

I realize that I misread the the WebSocket interface spec and that it explicitly instructs user agents to discard messages received after a call to close:

When a WebSocket message has been received with type type and data data, the user agent must queue a task to follow these steps: [WSP]
1.If the readyState attribute's value is not OPEN (1), then abort these steps.
https://html.spec.whatwg.org/multipage/comms.html#feedback-from-the-protocol

(Calling close sets readyState to CLOSING, so it's not OPEN anymore when the message is received).

Thus I was wrong: your tests suggest that all tested Web browsers behave according to spec, except Safari.

I also misread the WebSocket protocol as it does not suggest that an on-going transmission should be finished but rather says "An endpoint MAY delay sending a Close frame until its current message is sent [...] However, there is no guarantee that the endpoint that has already sent a Close frame will continue to process data":
https://tools.ietf.org/html/rfc6455#section-5.5.1

The initial questions remain, though:

  1. why expose the closing state to Web applications?
  2. why expose the closing duration to Web applications in the first place?

Running your example clearly shows that user agents wait for the server ack before they transition the WebSocket connection to "closed" (as specified). Knowing that there is still a pending connection encourages the app to wait in situations where it wants to re-connect to the WebSocket server, perhaps?

@tomoyukilabs
Copy link
Contributor

@tidoust Thanks for clarification. It turns out I also misunderstood the task during closing handshake.

Knowing that there is still a pending connection encourages the app to wait in situations where it wants to re-connect to the WebSocket server, perhaps?

At least, such a situation may imply that the app tries to close WebSocket connection when the server has been still in the middle of sending something to the app, or that the server might be down or still continue unexpected data transfer.

One of best practices for the former case seems to be that the server should notify the app of server's intention to send any data beforehand, within application-level implementation. Otherwise, the server can abort sending data as soon as it has received a close frame from the app. (Note: this may require frame-level handling for the server.) Of course, the app may choose to re-connect and ask the server to send the same data again.

In my opinion, exposing the closing state may be helpful for the latter case, i.e. use for debugging. For example, if the state of one endpoint remains closing for a while, developers can doubt that some error or bug has occurred in the other endpoint.

@mfoltzgoogle
Copy link
Contributor

Trying to generalize this beyond WebSockets a bit, the use case for closing seems to be the following:

  • One of the parties (A) in the PresentationConnection calls close().
  • The other party (B) will be allowed to transmit messages while the request to close is sent to B.
  • A wants to receive these final messages from B.
  • Once B gets the close request, it notifies A that it's done sending and and A's connection can transition to closed.

This could be useful, perhaps B would transmit some state to help A reconnect in the future. However, there's no guarantee that A will receive them, in case the connection is closed by navigation, termination, or error (i.e., a reason other than close()).

Also, B could be misbehaving: spamming A with messages and ignoring the request to close. We would probably need to specify that the close event must happen within a reasonable time, even if there are pending messages.

@mfoltzgoogle mfoltzgoogle added the v2 label Feb 1, 2016
@tidoust
Copy link
Member Author

tidoust commented Feb 2, 2016

@mfoltzgoogle wrote:

A wants to receive these final messages from B.

Despite what I thought initially, at the API level in both WebRTC and WebSockets, A cannot receive these final messages even if it wants to (state is switched to closing immediately when close() is called and the specs require user agents to discard any message when state is not connected). This makes sense, receiving a message after calling close() seems counter-intuitive.

So, for me, the main use case for closing seems to be for debugging. In the WebSocket case, this gives the web app a way to detect that the WebSocket server is misbehaving or taking an unusual amount of time... but that could well be done by the user agent itself without exposing this transient state (e.g. through warnings in the console). I'm not convinced this warrants the introduction of a closing state.

@mfoltzgoogle
Copy link
Contributor

If that's true, then I agree that a closing state doesn't seem warranted. It seems like it doesn't add any functionality that either connected party could take advantage of.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants