Skip to content

Always update connection state using _setState #175

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

Merged
merged 4 commits into from
Jan 20, 2018

Conversation

gkubisa
Copy link
Contributor

@gkubisa gkubisa commented Oct 17, 2017

Before this PR the connection event sequence could be like:

  1. connected
  2. disconnected
  3. connected

when using a plain WebSocket and bindToSocket in a browser. If some kind of reconnecting socket were used (without bindToSocket), you might get the connecting events, too.

After this PR is merged, the connecting event would be emitted consistently regardless of the socket implementation, for example:

  1. connecting
  2. connected
  3. disconnected
  4. connecting
  5. connected

@coveralls
Copy link

coveralls commented Oct 17, 2017

Coverage Status

Coverage increased (+0.002%) to 96.108% when pulling 9548214 on Teamwork:connection-state-management into c0e2fee on share:master.

@nateps
Copy link
Contributor

nateps commented Dec 20, 2017

Thanks for highlighting this issue and proposing a solution! I agree that the inconsistency in events emitted when using bindToSocket() is an issue. The 'connecting' events should be emitted when transitioning from 'disconnected' to 'connected'.

A related but separate question is when constructing a ShareDB Connection, should a 'connecting' event be emitted immediately, and if you pass in a socket that is currently closed, should a 'disconnected' event be emitted immediately.

I'm inclined to maintain the current behavior, which is that no connection state events are emitted on construction. I think that instantiation triggering an event emission would be surprising. In addition, a typical use case for these events is showing a user a message to indicate that they are offline but the app is attempting to connect. Developers may or may not want to display the message on the initial connection, especially on page load. The preferred UX may be to show a connecting message only when the network unexpectedly had an issue and the client is attempting to reconnect for the user automatically. Emitting an event following construction would make this more complex to implement.

This may mean that developers have to take care to handle the initial connection state of the socket they are passing in when they initially create a Connection. This can be done by checking the connection.state or the socket.readyState. However, no additional check is needed in the case of synchronously creating a WebSocket and a Connection, since WebSockets are always created in the CONNECTING (0) readyState (https://html.spec.whatwg.org/multipage/web-sockets.html#the-websocket-interface:dom-websocket-readystate-2).

This means that the event sequence would typically be:

1. connected
2. disconnected
3. connecting
4. connected
etc

Or a repeated connection failure:

1. disconnected
2. connecting
3. disconnected
etc

I'll add inline comments for suggestions on the potential code changes.

Especially because this will change the behavior of the API in a subtle way, we'll also want to add unit tests before we merge this in. Started working on those with @rkstedman today: #188

@@ -48,6 +48,8 @@ function Connection(socket) {

this.debug = false;

this.state = 'disconnected'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To avoid emission on initial construction, I'd set this based on the state of the socket passed in:

this.state = connectionState(socket);

And add the function:

function connectionState(socket) {
  if (socket.readyState === 0 || socket.readyState === 1) return 'connecting';
  return 'disconnected';
}

@@ -90,7 +92,7 @@ Connection.prototype.bindToSocket = function(socket) {
// - 'disconnected' Connection is closed, but it will reconnect automatically
// - 'closed' The connection was closed by the client, and will not reconnect
// - 'stopped' The connection was closed by the server, and will not reconnect
this.state = (socket.readyState === 0 || socket.readyState === 1) ? 'connecting' : 'disconnected';
this._setState((socket.readyState === 0 || socket.readyState === 1) ? 'connecting' : 'disconnected');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update this line to use the function:

var newState = connectionState(socket);
this._setState(newState);

@coveralls
Copy link

coveralls commented Jan 2, 2018

Coverage Status

Coverage decreased (-0.04%) to 96.068% when pulling c2a5e13 on Teamwork:connection-state-management into c0e2fee on share:master.

@coveralls
Copy link

Coverage Status

Coverage increased (+0.06%) to 96.161% when pulling 90043af on Teamwork:connection-state-management into c0e2fee on share:master.

1 similar comment
@coveralls
Copy link

coveralls commented Jan 2, 2018

Coverage Status

Coverage increased (+0.06%) to 96.161% when pulling 90043af on Teamwork:connection-state-management into c0e2fee on share:master.

@gkubisa
Copy link
Contributor Author

gkubisa commented Jan 2, 2018

I agree with your arguments about the synchronous initial event and made the changes you requested. As a side note, I think that it does not affect the observable behaviour because since the initial event was synchronous, there was no way to attach a listener to catch it.

I've also added some tests for the connection state.

Copy link
Contributor

@nateps nateps left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

Great contribution. Thanks so much! I'll merge and publish a new beta version.

@@ -90,4 +91,71 @@ describe('client connection', function() {
});
});

describe('state management using setSocket', function() {
it('initial connection.state is connecting, if socket.readyState is CONNECTING', function () {
// https://html.spec.whatwg.org/multipage/web-sockets.html#dom-websocket-connecting
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Love the links to the spec! 🥇

describe('state management using setSocket', function() {
it('initial connection.state is connecting, if socket.readyState is CONNECTING', function () {
// https://html.spec.whatwg.org/multipage/web-sockets.html#dom-websocket-connecting
var socket = { readyState: 0 }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Style-wise, we tend not to put spaces inside of brackets, and use semicolons. I'll just go ahead and clean up after merging.

@nateps nateps merged commit 57fdd5c into share:master Jan 20, 2018
@gkubisa gkubisa deleted the connection-state-management branch April 3, 2018 15:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants