Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nextcloud/spreed.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoachim Bauch <bauch@struktur.de>2017-07-20 14:43:41 +0300
committerJoachim Bauch <bauch@struktur.de>2017-11-02 13:23:09 +0300
commit547c69c91ca76cb2642d84235f10683833a8bd83 (patch)
treeb6ba003411b7e7d7791025a25e3ac19058fd1626
parentbe33ec8d9c30b1acd0336976fd64cc0dfff96c27 (diff)
Add frontend support for the standalone signaling server.
See #339 for a description of the API. Signed-off-by: Joachim Bauch <bauch@struktur.de>
-rw-r--r--js/signaling.js302
-rw-r--r--templates/index-public.php2
-rw-r--r--templates/index.php2
3 files changed, 301 insertions, 5 deletions
diff --git a/js/signaling.js b/js/signaling.js
index 1bac8b641..70ebf021e 100644
--- a/js/signaling.js
+++ b/js/signaling.js
@@ -431,10 +431,306 @@
}.bind(this));
};
+ function StandaloneSignaling(url) {
+ SignalingBase.prototype.constructor.apply(this, arguments);
+ // Make sure we are using websocket urls.
+ if (url.indexOf("https://") === 0) {
+ url = "wss://" + url.substr(8);
+ } else if (url.indexOf("http://") === 0) {
+ url = "ws://" + url.substr(7);
+ }
+ if (url[url.length - 1] === "/") {
+ url = url.substr(0, url.length - 1);
+ }
+ this.url = url + "/spreed";
+ this.initialReconnectIntervalMs = 1000;
+ this.maxReconnectIntervalMs = 16000;
+ this.reconnectIntervalMs = this.initialReconnectIntervalMs;
+ this.connect();
+ }
+
+ StandaloneSignaling.prototype = new SignalingBase();
+ StandaloneSignaling.prototype.constructor = StandaloneSignaling;
+
+ StandaloneSignaling.prototype.reconnect = function() {
+ if (this.reconnectTimer) {
+ return;
+ }
+
+ // Wiggle interval a little bit to prevent all clients from connecting
+ // simultaneously in case the server connection is interrupted.
+ var interval = this.reconnectIntervalMs - (this.reconnectIntervalMs / 2) + (this.reconnectIntervalMs * Math.random());
+ console.log("Reconnect in", interval);
+ this.reconnectTimer = window.setTimeout(function() {
+ this.reconnectTimer = null;
+ this.connect();
+ }.bind(this), interval);
+ this.reconnectIntervalMs = this.reconnectIntervalMs * 2;
+ if (this.reconnectIntervalMs > this.maxReconnectIntervalMs) {
+ this.reconnectIntervalMs = this.maxReconnectIntervalMs;
+ }
+ if (this.socket) {
+ this.socket.close();
+ this.socket = null;
+ }
+ };
+
+ StandaloneSignaling.prototype.connect = function() {
+ console.log("Connecting to", this.url);
+ this.callbacks = {};
+ this.id = 1;
+ this.pendingMessages = [];
+ this.connected = false;
+ this.socket = new WebSocket(this.url);
+ window.signalingSocket = this.socket;
+ this.socket.onopen = function(event) {
+ console.log("Connected", event);
+ this.reconnectIntervalMs = this.initialReconnectIntervalMs;
+ this.sendHello();
+ }.bind(this);
+ this.socket.onerror = function(event) {
+ console.log("Error", event);
+ this.reconnect();
+ }.bind(this);
+ this.socket.onclose = function(event) {
+ console.log("Close", event);
+ this.reconnect();
+ }.bind(this);
+ this.socket.onmessage = function(event) {
+ var data = event.data;
+ if (typeof(data) === "string") {
+ data = JSON.parse(data);
+ }
+ console.log("Received", data);
+ var id = data.id;
+ if (id && this.callbacks.hasOwnProperty(id)) {
+ var cb = this.callbacks[id];
+ delete this.callbacks[id];
+ cb(data);
+ }
+ switch (data.type) {
+ case "hello":
+ if (!id) {
+ // Only process if not received as result of our "hello".
+ this.helloResponseReceived(data);
+ }
+ break;
+ case "room":
+ // No special processing required for now.
+ break;
+ case "event":
+ this.processEvent(data);
+ break;
+ case "message":
+ data.message.data.from = data.message.sender.sessionid;
+ this._trigger("message", [data.message.data]);
+ break;
+ default:
+ if (!id) {
+ console.log("Ignore unknown event", data);
+ }
+ break;
+ }
+ }.bind(this);
+ };
+
+ StandaloneSignaling.prototype.disconnect = function() {
+ if (this.socket) {
+ this.doSend({
+ "type": "bye",
+ "bye": {}
+ });
+ this.socket.close();
+ this.socket = null;
+ }
+ SignalingBase.prototype.disconnect.apply(this, arguments);
+ };
+
+ StandaloneSignaling.prototype.on = function(ev/*, handler*/) {
+ SignalingBase.prototype.on.apply(this, arguments);
+
+ switch (ev) {
+ case "stunservers":
+ case "turnservers":
+ // TODO(fancycode): Implement getting STUN/TURN settings.
+ break;
+ }
+ };
+
+ StandaloneSignaling.prototype.sendCallMessage = function(data) {
+ this.doSend({
+ "type": "message",
+ "message": {
+ "recipient": {
+ "type": "session",
+ "sessionid": data.to
+ },
+ "data": data
+ }
+ });
+ };
+
+ StandaloneSignaling.prototype.doSend = function(msg, callback) {
+ if (!this.connected && msg.type !== "hello") {
+ // Defer sending any messages until the hello rsponse has been
+ // received.
+ this.pendingMessages.push([msg, callback]);
+ return;
+ }
+
+ if (callback) {
+ var id = this.id++;
+ this.callbacks[id] = callback;
+ msg["id"] = ""+id;
+ }
+ console.log("Sending", msg);
+ this.socket.send(JSON.stringify(msg));
+ };
+
+ StandaloneSignaling.prototype.sendHello = function() {
+ var msg;
+ if (this.sessionId) {
+ console.log("Trying to resume session", this.sessionId);
+ msg = {
+ "type": "hello",
+ "hello": {
+ "version": "1.0",
+ "sessionid": this.sessionId
+ }
+ };
+ } else {
+ var user = OC.getCurrentUser();
+ var url = OC.generateUrl("/apps/spreed/signalling/backend");
+ var ticket = $("#app").attr("data-signalingticket");
+ msg = {
+ "type": "hello",
+ "hello": {
+ "version": "1.0",
+ "auth": {
+ "url": OC.getProtocol() + "://" + OC.getHost() + url,
+ "params": {
+ "userid": user.uid,
+ "ticket": ticket,
+ }
+ }
+ }
+ };
+ }
+ this.doSend(msg, this.helloResponseReceived.bind(this));
+ };
+
+ StandaloneSignaling.prototype.helloResponseReceived = function(data) {
+ console.log("Hello response received", data);
+ if (data.type !== "hello") {
+ if (this.sessionId) {
+ // Resuming the session failed, reconnect as new session.
+ this.sessionId = '';
+ this.sendHello();
+ return;
+ }
+
+ // TODO(fancycode): How should this be handled better?
+ console.error("Could not connect to server", data);
+ this.reconnect();
+ return;
+ }
+
+ var resumedSession = !!this.sessionId;
+ this.connected = true;
+ this.sessionId = data.hello.sessionid;
+
+ var messages = this.pendingMessages;
+ this.pendingMessages = [];
+ for (var i = 0; i < messages.length; i++) {
+ var msg = messages[i][0];
+ var callback = messages[i][1];
+ this.doSend(msg, callback);
+ }
+
+ this._trigger("connect");
+ if (!resumedSession && this.currentCallToken) {
+ this.joinCall(this.currentCallToken);
+ }
+ };
+
+ StandaloneSignaling.prototype.joinCall = function(token, callback) {
+ console.log("Join call", token);
+ this.doSend({
+ "type": "room",
+ "room": {
+ "roomid": token
+ }
+ }, function(data) {
+ this.joinResponseReceived(data, token, callback);
+ }.bind(this));
+ };
+
+ StandaloneSignaling.prototype.joinResponseReceived = function(data, token, callback) {
+ console.log("Joined", data, token);
+ this.currentCallToken = token;
+ if (callback) {
+ var roomDescription = {
+ "clients": {}
+ };
+ callback('', roomDescription);
+ }
+ };
+
+ StandaloneSignaling.prototype.leaveCall = function(token) {
+ console.log("Leave call", token);
+ this.doSend({
+ "type": "room",
+ "room": {
+ "roomid": ""
+ }
+ }, function(data) {
+ console.log("Left", data);
+ this.currentCallToken = null;
+ }.bind(this));
+ };
+
+ StandaloneSignaling.prototype.processEvent = function(data) {
+ switch (data.event.target) {
+ case "room":
+ this.processRoomEvent(data);
+ break;
+ case "roomlist":
+ this.processRoomListEvent(data);
+ break;
+ default:
+ console.log("Unsupported event target", data);
+ break;
+ }
+ };
+
+ StandaloneSignaling.prototype.processRoomEvent = function(data) {
+ switch (data.event.type) {
+ case "join":
+ console.log("Users joined", data.event.join);
+ this._trigger("usersJoined", [data.event.join]);
+ break;
+ case "leave":
+ console.log("Users left", data.event.leave);
+ this._trigger("usersLeft", [data.event.leave]);
+ break;
+ default:
+ console.log("Unknown room event", data);
+ break;
+ }
+ };
+
+ StandaloneSignaling.prototype.processRoomListEvent = function(data) {
+ console.log("Room list event", data);
+ this.syncRooms();
+ };
+
OCA.SpreedMe.createSignalingConnection = function() {
- // TODO(fancycode): Create different type of signaling connection
- // depending on configuration.
- return new InternalSignaling();
+ var url = $("#app").attr("data-signalingserver");
+ if (url) {
+ return new StandaloneSignaling(url);
+ } else {
+ return new InternalSignaling();
+ }
};
})(OCA, OC);
diff --git a/templates/index-public.php b/templates/index-public.php
index 08d44f78e..2b909976c 100644
--- a/templates/index-public.php
+++ b/templates/index-public.php
@@ -33,7 +33,7 @@ script(
<div id="notification-container">
<div id="notification" style="display: none;"></div>
</div>
-<div id="app" class="nc-enable-screensharing-extension" data-token="<?php p($_['token']) ?>">
+<div id="app" class="nc-enable-screensharing-extension" data-token="<?php p($_['token']) ?>" data-signalingServer="<?php p($_['signaling-server']) ?>" data-signalingTicket="<?php p($_['signaling-ticket']) ?>">
<div id="app-content" class="participants-1">
<header>
diff --git a/templates/index.php b/templates/index.php
index 85082363e..b53d2cba6 100644
--- a/templates/index.php
+++ b/templates/index.php
@@ -33,7 +33,7 @@ script(
);
?>
-<div id="app" class="nc-enable-screensharing-extension" data-token="<?php p($_['token']) ?>">
+<div id="app" class="nc-enable-screensharing-extension" data-token="<?php p($_['token']) ?>" data-signalingServer="<?php p($_['signaling-server']) ?>" data-signalingTicket="<?php p($_['signaling-ticket']) ?>">
<div id="app-navigation" class="icon-loading">
<form id="oca-spreedme-add-room">
<input id="select-participants" class="select2-offscreen" type="text" placeholder="<?php p($l->t('Choose person …')) ?>"/>