##// END OF EJS Templates
feat(configs): deprecared old hooks protocol and ssh wrapper....
feat(configs): deprecared old hooks protocol and ssh wrapper. New defaults are now set on v2 keys, so previous installation are automatically set to new keys. Fallback mode is still available.

File last commit:

r3192:689a0c93 merge stable
r5496:cab50adf default
Show More
channelstream-connection.js
577 lines | 15.7 KiB | application/javascript | JavascriptLexer
import {Polymer, html} from '@polymer/polymer/polymer-legacy';
import '@polymer/iron-ajax/iron-ajax.js';
const elemTemplate = html`
<iron-ajax
id="ajaxConnect"
url=""
handle-as="json"
method="post"
content-type="application/json"
loading="{{loadingConnect}}"
last-response="{{connectLastResponse}}"
on-response="_handleConnect"
on-error="_handleConnectError"
debounce-duration="100"></iron-ajax>
<iron-ajax
id="ajaxDisconnect"
url=""
handle-as="json"
method="post"
content-type="application/json"
loading="{{loadingDisconnect}}"
last-response="{{_disconnectLastResponse}}"
on-response="_handleDisconnect"
debounce-duration="100"></iron-ajax>
<iron-ajax
id="ajaxSubscribe"
url=""
handle-as="json"
method="post"
content-type="application/json"
loading="{{loadingSubscribe}}"
last-response="{{subscribeLastResponse}}"
on-response="_handleSubscribe"
debounce-duration="100"></iron-ajax>
<iron-ajax
id="ajaxUnsubscribe"
url=""
handle-as="json"
method="post"
content-type="application/json"
loading="{{loadingUnsubscribe}}"
last-response="{{unsubscribeLastResponse}}"
on-response="_handleUnsubscribe"
debounce-duration="100"></iron-ajax>
<iron-ajax
id="ajaxMessage"
url=""
handle-as="json"
method="post"
content-type="application/json"
loading="{{loadingMessage}}"
last-response="{{messageLastResponse}}"
on-response="_handleMessage"
on-error="_handleMessageError"
debounce-duration="100"></iron-ajax>
<iron-ajax
id="ajaxListen"
url=""
handle-as="text"
loading="{{loadingListen}}"
last-response="{{listenLastResponse}}"
on-request="_handleListenOpen"
on-error="_handleListenError"
on-response="_handleListenMessageEvent"
debounce-duration="100"></iron-ajax>
`
Polymer({
is: 'channelstream-connection',
_template: elemTemplate,
/**
* Fired when `channels` array changes.
*
* @event channelstream-channels-changed
*/
/**
* Fired when `connect()` method succeeds.
*
* @event channelstream-connected
*/
/**
* Fired when `connect` fails.
*
* @event channelstream-connect-error
*/
/**
* Fired when `disconnect()` succeeds.
*
* @event channelstream-disconnected
*/
/**
* Fired when `message()` succeeds.
*
* @event channelstream-message-sent
*/
/**
* Fired when `message()` fails.
*
* @event channelstream-message-error
*/
/**
* Fired when `subscribe()` succeeds.
*
* @event channelstream-subscribed
*/
/**
* Fired when `subscribe()` fails.
*
* @event channelstream-subscribe-error
*/
/**
* Fired when `unsubscribe()` succeeds.
*
* @event channelstream-unsubscribed
*/
/**
* Fired when `unsubscribe()` fails.
*
* @event channelstream-unsubscribe-error
*/
/**
* Fired when listening connection receives a message.
*
* @event channelstream-listen-message
*/
/**
* Fired when listening connection is opened.
*
* @event channelstream-listen-opened
*/
/**
* Fired when listening connection is closed.
*
* @event channelstream-listen-closed
*/
/**
* Fired when listening connection suffers an error.
*
* @event channelstream-listen-error
*/
properties: {
isReady: Boolean,
/** List of channels user should be subscribed to. */
channels: {
type: Array,
value: function () {
return []
},
notify: true
},
/** Username of connecting user. */
username: {
type: String,
value: 'Anonymous',
reflectToAttribute: true
},
/** Connection identifier. */
connectionId: {
type: String,
reflectToAttribute: true
},
/** Websocket instance. */
websocket: {
type: Object,
value: null
},
/** Websocket connection url. */
websocketUrl: {
type: String,
value: ''
},
/** URL used in `connect()`. */
connectUrl: {
type: String,
value: ''
},
/** URL used in `disconnect()`. */
disconnectUrl: {
type: String,
value: ''
},
/** URL used in `subscribe()`. */
subscribeUrl: {
type: String,
value: ''
},
/** URL used in `unsubscribe()`. */
unsubscribeUrl: {
type: String,
value: ''
},
/** URL used in `message()`. */
messageUrl: {
type: String,
value: ''
},
/** Long-polling connection url. */
longPollUrl: {
type: String,
value: ''
},
/** Long-polling connection url. */
shouldReconnect: {
type: Boolean,
value: true
},
/** Should send heartbeats. */
heartbeats: {
type: Boolean,
value: true
},
/** How much should every retry interval increase (in milliseconds) */
increaseBounceIv: {
type: Number,
value: 2000
},
_currentBounceIv: {
type: Number,
reflectToAttribute: true,
value: 0
},
/** Should use websockets or long-polling by default */
useWebsocket: {
type: Boolean,
reflectToAttribute: true,
value: true
},
connected: {
type: Boolean,
reflectToAttribute: true,
value: false
}
},
observers: [
'_handleChannelsChange(channels.splices)'
],
listeners: {
'channelstream-connected': 'startListening',
'channelstream-connect-error': 'retryConnection',
},
/**
* Mutators hold functions that you can set locally to change the data
* that the client is sending to all endpoints
* you can call it like `elem.mutators('connect', yourFunc())`
* mutators will be executed in order they were pushed onto arrays
*
*/
mutators: {
connect: function () {
return []
}(),
message: function () {
return []
}(),
subscribe: function () {
return []
}(),
unsubscribe: function () {
return []
}(),
disconnect: function () {
return []
}()
},
ready: function () {
this.isReady = true;
},
/**
* Connects user and fetches connection id from the server.
*
*/
connect: function () {
var request = this.$['ajaxConnect'];
request.url = this.connectUrl;
request.body = {
username: this.username,
channels: this.channels
};
for (var i = 0; i < this.mutators.connect.length; i++) {
this.mutators.connect[i](request);
}
request.generateRequest()
},
/**
* Overwrite with custom function that will
*/
addMutator: function (type, func) {
this.mutators[type].push(func);
},
/**
* Subscribes user to channels.
*
*/
subscribe: function (channels) {
var request = this.$['ajaxSubscribe'];
request.url = this.subscribeUrl;
request.body = {
channels: channels,
conn_id: this.connectionId
};
for (var i = 0; i < this.mutators.subscribe.length; i++) {
this.mutators.subscribe[i](request);
}
if (request.body.channels.length) {
request.generateRequest();
}
},
/**
* Unsubscribes user from channels.
*
*/
unsubscribe: function (unsubscribe) {
var request = this.$['ajaxUnsubscribe'];
request.url = this.unsubscribeUrl;
request.body = {
channels: unsubscribe,
conn_id: this.connectionId
};
for (var i = 0; i < this.mutators.unsubscribe.length; i++) {
this.mutators.unsubscribe[i](request);
}
request.generateRequest()
},
/**
* calculates list of channels we should add user to based on difference
* between channels property and passed channel list
*/
calculateSubscribe: function (channels) {
var currentlySubscribed = this.channels;
var toSubscribe = [];
for (var i = 0; i < channels.length; i++) {
if (currentlySubscribed.indexOf(channels[i]) === -1) {
toSubscribe.push(channels[i]);
}
}
return toSubscribe
},
/**
* calculates list of channels we should remove user from based difference
* between channels property and passed channel list
*/
calculateUnsubscribe: function (channels) {
var currentlySubscribed = this.channels;
var toUnsubscribe = [];
for (var i = 0; i < channels.length; i++) {
if (currentlySubscribed.indexOf(channels[i]) !== -1) {
toUnsubscribe.push(channels[i]);
}
}
return toUnsubscribe
},
/**
* Marks the connection as expired.
*
*/
disconnect: function () {
var request = this.$['ajaxDisconnect'];
request.url = this.disconnectUrl;
request.params = {
conn_id: this.connectionId
};
for (var i = 0; i < this.mutators.disconnect.length; i++) {
this.mutators.disconnect[i](request);
}
// mark connection as expired
request.generateRequest();
// disconnect existing connection
this.closeConnection();
},
/**
* Sends a message to the server.
*
*/
message: function (message) {
var request = this.$['ajaxMessage'];
request.url = this.messageUrl;
request.body = message;
for (var i = 0; i < this.mutators.message.length; i++) {
this.mutators.message[i](request)
}
request.generateRequest();
},
/**
* Opens "long lived" (websocket/longpoll) connection to the channelstream server.
*
*/
startListening: function (event) {
this.fire('start-listening', {});
if (this.useWebsocket) {
this.useWebsocket = window.WebSocket ? true : false;
}
if (this.useWebsocket) {
this.openWebsocket();
}
else {
this.openLongPoll();
}
},
/**
* Opens websocket connection.
*
*/
openWebsocket: function () {
var url = this.websocketUrl + '?conn_id=' + this.connectionId;
this.websocket = new WebSocket(url);
this.websocket.onopen = this._handleListenOpen.bind(this);
this.websocket.onclose = this._handleListenCloseEvent.bind(this);
this.websocket.onerror = this._handleListenErrorEvent.bind(this);
this.websocket.onmessage = this._handleListenMessageEvent.bind(this);
},
/**
* Opens long-poll connection.
*
*/
openLongPoll: function () {
var request = this.$['ajaxListen'];
request.url = this.longPollUrl + '?conn_id=' + this.connectionId;
request.generateRequest()
},
/**
* Retries `connect()` call while incrementing interval between tries up to 1 minute.
*
*/
retryConnection: function () {
if (!this.shouldReconnect) {
return;
}
if (this._currentBounceIv < 60000) {
this._currentBounceIv = this._currentBounceIv + this.increaseBounceIv;
}
else {
this._currentBounceIv = 60000;
}
setTimeout(this.connect.bind(this), this._currentBounceIv);
},
/**
* Closes listening connection.
*
*/
closeConnection: function () {
var request = this.$['ajaxListen'];
if (this.websocket && this.websocket.readyState === WebSocket.OPEN) {
this.websocket.onclose = null;
this.websocket.onerror = null;
this.websocket.close();
}
if (request.loading) {
request.lastRequest.abort();
}
this.connected = false;
},
_handleChannelsChange: function (event) {
// do not fire the event if set() didn't mutate anything
// is this a reliable way to do it?
if (!this.isReady || event === undefined) {
return
}
this.fire('channelstream-channels-changed', event)
},
_handleListenOpen: function (event) {
this.connected = true;
this.fire('channelstream-listen-opened', event);
this.createHeartBeats();
},
createHeartBeats: function () {
if (typeof self._heartbeat === 'undefined' && this.websocket !== null
&& this.heartbeats) {
self._heartbeat = setInterval(this._sendHeartBeat.bind(this), 10000);
}
},
_sendHeartBeat: function () {
if (this.websocket.readyState === WebSocket.OPEN && this.heartbeats) {
this.websocket.send(JSON.stringify({type: 'heartbeat'}));
}
},
_handleListenError: function (event) {
this.connected = false;
this.retryConnection();
},
_handleConnectError: function (event) {
this.connected = false;
this.fire('channelstream-connect-error', event.detail);
},
_handleListenMessageEvent: function (event) {
var data = null;
// comes from iron-ajax
if (event.detail) {
data = JSON.parse(event.detail.response)
// comes from websocket
setTimeout(this.openLongPoll.bind(this), 0);
} else {
data = JSON.parse(event.data)
}
this.fire('channelstream-listen-message', data);
},
_handleListenCloseEvent: function (event) {
this.connected = false;
this.fire('channelstream-listen-closed', event.detail);
this.retryConnection();
},
_handleListenErrorEvent: function (event) {
this.connected = false;
this.fire('channelstream-listen-error', {})
},
_handleConnect: function (event) {
this.currentBounceIv = 0;
this.connectionId = event.detail.response.conn_id;
this.fire('channelstream-connected', event.detail.response);
},
_handleDisconnect: function (event) {
this.connected = false;
this.fire('channelstream-disconnected', {});
},
_handleMessage: function (event) {
this.fire('channelstream-message-sent', event.detail.response);
},
_handleMessageError: function (event) {
this.fire('channelstream-message-error', event.detail);
},
_handleSubscribe: function (event) {
this.fire('channelstream-subscribed', event.detail.response);
},
_handleSubscribeError: function (event) {
this.fire('channelstream-subscribe-error', event.detail);
},
_handleUnsubscribe: function (event) {
this.fire('channelstream-unsubscribed', event.detail.response);
},
_handleUnsubscribeError: function (event) {
this.fire('channelstream-unsubscribe-error', event.detail);
}
});