Skip to content

Commit

Permalink
Client reconnects on connection issues
Browse files Browse the repository at this point in the history
  • Loading branch information
ekuiter committed Nov 19, 2018
1 parent dc29257 commit 0402bff
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 39 deletions.
4 changes: 0 additions & 4 deletions API.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,6 @@ may comprise any number of users.)

#### Client

TODO: As of now, the client is oblivious to the project-artifact-organization on
the server and therefore incompatible. This is about to be fixed. The following
would be desirable:

The client knows as which user it is registered, which artifacts are available,
in which collaborative sessions it participates, as well as the associated state
context and other participating users. It receives all messages related to any
Expand Down
1 change: 1 addition & 0 deletions client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"redux": "^4.0.1",
"redux-thunk": "^2.3.0",
"reselect": "^4.0.0",
"sockette": "^2.0.3",
"svg2pdf.js": "1.2.1",
"typesafe-actions": "^2.0.4"
},
Expand Down
79 changes: 44 additions & 35 deletions client/src/server/webSocket.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,51 +7,60 @@ import {Message, MessageType, ArtifactPath} from '../types';
import logger from '../helpers/logger';
import {wait} from '../helpers/wait';
import {State} from '../store/types';
import Sockette from 'sockette';

type HandleMessageFunction = (data: Message) => void;

let handleMessage: HandleMessageFunction;
const tag = 'socket';

const getWebSocket = ((): () => Promise<WebSocket> => {
let promise: Promise<WebSocket>;
const getWebSocket = ((): () => Promise<Sockette> => {
let promise: Promise<Sockette> | undefined;

function connect(): Promise<WebSocket> {
function connect(): Promise<Sockette> {
return promise = new Promise((resolve, reject) => {
let webSocket = new WebSocket(constants.server.webSocket);

webSocket.onopen = () => {
logger.logTagged({tag: 'socket'}, () => 'open');
resolve(webSocket);
};

webSocket.onclose = e => {
logger.warnTagged({tag: 'socket'}, () => `closed with code ${e.code} and reason ${e.reason}`);
// TODO: notify user that WebSocket was closed (with button to reopen)
};

webSocket.onerror = e => {
logger.warnTagged({tag: 'socket'}, () => e);
// TODO: notify user of error (and IF WebSocket is closed now, a button to reopen it)
reject(e);
};

webSocket.onmessage = message => {
let state: State | undefined = (window as any).app && (window as any).app.store && (window as any).app.store.getState();
if (!state)
logger.warn(() => 'store not accessible, can not simulate message delay');
wait(state ? state.settings.developer.delay : 0).then(() => {
let data = JSON.parse(message.data);
logger.logTagged({tag: 'receive'}, () => data);
if (handleMessage)
handleMessage(data);
});
};
let sockette = new Sockette(constants.server.webSocket, {
onopen() {
logger.logTagged({tag}, () => 'open');
resolve(sockette);
},

onclose(e) {
logger.warnTagged({tag}, () => `closed with code ${e.code} and reason ${e.reason}`);
promise = undefined;
// TODO: notify user that WebSocket was closed
},

onerror(e) {
logger.warnTagged({tag}, () => e);
// TODO: notify user of error
reject(e);
},

onreconnect() {
logger.logTagged({tag}, () => 'reconnect');
// TODO: notify user of reconnect
// Re-opening a WebSocket is involved because the existing Redux state has to be considered.
// For example, we should log in as the same user participating in the same collaboration sessions,
// as if she never left. (For optimistic UI, pending operations have to be submitted as well.)
},

onmessage(message) {
let state: State | undefined = (window as any).app && (window as any).app.store && (window as any).app.store.getState();
if (!state)
logger.warn(() => 'store not accessible, can not simulate message delay');
wait(state ? state.settings.developer.delay : 0).then(() => {
let data = JSON.parse(message.data);
logger.logTagged({tag: 'receive'}, () => data);
if (handleMessage)
handleMessage(data);
});
}
});
});
}

return () => promise
? promise.then(webSocket => webSocket.readyState !== webSocket.CLOSED ? webSocket : connect())
: connect();
return () => promise || connect();
})();

export async function openWebSocket(_handleMessage?: HandleMessageFunction): Promise<void> {
Expand Down
4 changes: 4 additions & 0 deletions client/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -6879,6 +6879,10 @@ snapdragon@^0.8.1:
source-map-resolve "^0.5.0"
use "^3.1.0"

sockette@^2.0.3:
version "2.0.3"
resolved "https://registry.yarnpkg.com/sockette/-/sockette-2.0.3.tgz#f9204f67accae3505cacaf696c780d15fead22b1"

[email protected]:
version "1.1.5"
resolved "https://registry.yarnpkg.com/sockjs-client/-/sockjs-client-1.1.5.tgz#1bb7c0f7222c40f42adf14f4442cbd1269771a83"
Expand Down

0 comments on commit 0402bff

Please sign in to comment.