This test creates an ibm webrtc Signaling Client (does not do the audio video portion yet)
27 |
28 |
29 |
30 |
31 |
32 |
MQTT:
33 |
TopicName:
34 |
35 |
36 |
37 |
38 |
39 |
1.
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
2.
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
3.
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
Connected to:
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
211 |
212 |
213 |
214 |
--------------------------------------------------------------------------------
/tests/stress/stressTest.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2013 IBM Corp.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | **/
16 |
17 |
18 | define([
19 | 'intern',
20 | 'intern!object',
21 | 'intern/chai!assert',
22 | 'intern/node_modules/dojo/Deferred',
23 | (typeof window === 'undefined' && global)
24 | ?'intern/dojo/node!../support/mqttws31_shim':
25 | 'lib/mqttws31',
26 | 'support/config',
27 | 'umd/rtcomm/EndpointProvider'
28 | ], function (intern, registerSuite, assert, Deferred, globals,config, EndpointProvider) {
29 |
30 | var DEBUG = (intern.args.DEBUG === 'true')? true: false;
31 |
32 | var MAX_CONNS = parseInt(intern.args.MAX_CONNS) || 50;
33 | var duration = parseInt(intern.args.duration) || 20000;
34 |
35 | var createProvider = function createProvider(cfg,appContext) {
36 | var dfd = new Deferred();
37 | var EP = new EndpointProvider();
38 | DEBUG && EP.setLogLevel('DEBUG');
39 | EP.setAppContext(appContext);
40 | EP.init(cfg,
41 | function(message) {
42 | dfd.resolve(EP);
43 | },
44 | function(message) { console.error('init failed', message); dfd.reject(message);}
45 | );
46 | return dfd.promise;
47 | };
48 |
49 | var createAutoConnectEP = function createAutoConnectEP(provider) {
50 | var ep = provider.createRtcommEndpoint();
51 | //ep.on('session:started', function() {});
52 | // ep.on('session:trying', function() {});
53 | //ep.on('session:ringing', function() {});
54 | ep.on('session:alerting', function() {
55 | ep.accept();
56 | });
57 | //ep.on('chat:connected', function() {});
58 | //ep.on('chat:disconnected', function() {});
59 | //ep.on('chat:message', function() {});
60 | return ep;
61 | };
62 |
63 | var createConnection= function createConnection(EP1,EP2,duration) {
64 | var dfd = new Deferred();
65 | var ep1 = createAutoConnectEP(EP1);
66 | var ep2 = createAutoConnectEP(EP2);
67 |
68 | function failure(message) {
69 | console.log('>>>> createConnection FAILURE '+message);
70 | dfd.reject(false);
71 | }
72 |
73 | ep1.on('session:started', function() {
74 | // set a timer and wait...
75 | setTimeout(function(){
76 | ep1.disconnect();
77 | dfd.resolve(true);
78 | },duration);
79 | });
80 |
81 | ep1.on('session:failed', failure);
82 | ep2.on('session:failed', failure);
83 |
84 | ep1.connect(EP2.getUserID());
85 | return dfd.promise;
86 | };
87 |
88 | var cfg= config.clientConfig1();
89 | var appContext = 'internChatTest';
90 | var callers= {};
91 | var callees= {};
92 | registerSuite({
93 | name: 'Stress Test - '+MAX_CONNS,
94 | setup: function() {
95 | console.log('*************setup!**************');
96 | var setupDfd = new Deferred();
97 | /* init the EndpointProvider */
98 | var resolved = 0;
99 |
100 | function resolve() {
101 | if (resolved === 2*MAX_CONNS) {
102 | console.log('******** Finished Setup '+resolved);
103 | if (Object.keys(callers).length === MAX_CONNS &&
104 | Object.keys(callees).length === MAX_CONNS ) {
105 | // Wait 5 seconds to ensure everything is completed.
106 | setTimeout(function() {
107 | setupDfd.resolve()
108 | },5000);;
109 | } else {
110 | console.log('CALLERS? '+Object.keys(callers).length);
111 | console.log('CALLEES? '+Object.keys(callees).length);
112 | setupDfd.reject("All Callers & Clients not actually created");
113 | }
114 | }
115 | }
116 |
117 | for(var i=0; i>>>TEST accepting call');
121 | setTimeout(function() {
122 | ep2.accept();
123 | }, 1000);
124 | });
125 | ep1.connect(cfg2.userid);
126 | });
127 | },
128 | "GenericMessagEndpoint() A calls B - sends messages": function() {
129 | console.log('************* ' + this.name + ' **************');
130 | // SKIP_ALL && this.skip(false);
131 | var dfd = this.async(T2);
132 |
133 | // Our endpoints
134 | var ep1;
135 | var ep2;
136 |
137 | var finish = dfd.callback(function(object) {
138 | console.log("******************Asserting now...***********************");
139 | console.log(object);
140 | // Object should be an event_object
141 | assert.ok(ep1.sessionStarted(), "Endpoint 1 is Session Started");
142 | assert.ok(ep2.sessionStarted(), "Endpoint2 is session Started");
143 | assert.equal(object.message, "Hello!", "Received the message");
144 | });
145 |
146 | var cfg1 = getConfig('testuser1');
147 | var cfg2 = getConfig('testuser2');
148 |
149 | createAndInitTwoProviders(cfg1, cfg2)
150 | .then(function(obj) {
151 | // Save the providers
152 | var EP1 = g.EP1 = obj.provider1;
153 | var EP2 = g.EP2 = obj.provider2;
154 | // create Endpoints
155 | ep1 = EP1.getMessageEndpoint();
156 | ep2 = EP2.getMessageEndpoint();
157 |
158 | ep1.on('session:ringing', function() {
159 | ep1_ringing = true
160 | })
161 | ep1.on('session:trying', function() {
162 | ep1_trying = true
163 | })
164 | ep1.on('session:started', function() {
165 | ep1.generic_message.send("Hello!");
166 | });
167 |
168 | // We will finish when we receive a generic_message
169 | ep2.on('generic_message:message', finish);
170 |
171 | ep2.on('session:alerting', function(obj) {
172 | ep2_alerting = true;
173 | console.log('>>>>TEST accepting call');
174 | setTimeout(function() {
175 | ep2.accept();
176 | }, 1000);
177 | });
178 | ep1.connect(cfg2.userid);
179 | });
180 | }
181 | })
182 | })
183 |
--------------------------------------------------------------------------------
/tests/functional/MqttEndpoint.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2013 IBM Corp.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | **/
16 | define([
17 | 'intern',
18 | 'intern!object',
19 | 'intern/chai!assert',
20 | (typeof window === 'undefined' && global)
21 | ?'intern/dojo/node!../support/mqttws31_shim':
22 | 'bower_components/bower-mqttws/mqttws31',
23 | 'support/config',
24 | 'bower_components/webrtc-adapter/adapter',
25 | 'umd/rtcomm/EndpointProvider',
26 | 'support/rtcommFatUtils'
27 |
28 | ], function (intern, registerSuite, assert, globals,config, adapter, EndpointProvider, Fat) {
29 | var suiteName = Fat.createSuiteName("FVT: MqttEndpoint");
30 | var DEBUG = (intern.args.DEBUG === 'true')? true: false;
31 | /* if (typeof window === 'undefined' && global) {
32 | require(['intern/dojo/node!./tests_intern/mock/mqttws31_shim'], function(globals) {
33 | console.log('********** Paho should now be defined **********');
34 | });
35 | } else {
36 | require(['lib/mqttws31'], function(globals) {
37 | console.log('Paho? ', Paho);
38 | console.log('********** Paho should now be defined **********');
39 | });
40 | } */
41 | var cfg = config.clientConfig1();
42 | var ep = null;
43 | var mq1 = null;
44 | var mq2 = null;
45 | var globals = null;
46 |
47 | var mqttPublish = function(topic, message, ms) {
48 | // publish 'message' from mq1 to mq2 on topic
49 | // pass means we expect it to work or not.
50 | ms = ms || 1000;
51 | var p = new Promise(
52 | function(resolve, reject) {
53 | var msgrecv1 = null;
54 | var msgrecv2 = null;
55 | // Wait to publish
56 | mq1.clearEventListeners();
57 | setTimeout(function() {
58 | mq1.on('message', function(msg) {
59 | console.log('MQ1 Received Message: '+msg.content);
60 | // Should not receive message;
61 | resolve(false);
62 | });
63 | mq2.on('message', function(msg) {
64 | console.log('MQ2 Received Message: '+msg.content);
65 | if (msg.content === message) {
66 | // Should not receive message;
67 | resolve(true);
68 | } else {
69 | console.log('Received Weird Message:' + msg.content);
70 | resolve(false);
71 | }
72 | });
73 | mq1.publish(topic, message);
74 | }, ms);
75 | setTimeout(function() {
76 | resolve(false);
77 | },ms+1000);
78 | });
79 | return p;
80 | };
81 |
82 | registerSuite({
83 | name: suiteName,
84 | setup: function() {
85 | console.log('************* SETUP: '+this.name+' **************');
86 | var p = new Promise(
87 | function(resolve, reject) {
88 | Fat.createProvider(cfg, 'mqttEndpoint').then(
89 | function(endpointProvider) {
90 | console.log('*** Creating MqttEndpoints ***');
91 | ep = endpointProvider;
92 | mq1 = ep.getMqttEndpoint();
93 | mq2 = ep.getMqttEndpoint();
94 | mq1.subscribe('/test1');
95 | mq2.subscribe('/test2/#');
96 | console.log('*** mq1 ***', mq1);
97 | resolve();
98 | });
99 | });
100 | return p;
101 | },
102 | teardown: function() {
103 | console.log('************* TEARDOWN: '+this.name+' **************');
104 | ep.destroy();
105 | ep = null;
106 | },
107 | 'mqtt /test2 topic':function() {
108 | console.log('************* '+this.name+' **************');
109 | var dfd = this.async(3000);
110 | mqttPublish('/test2', '1 - Hello from 1').then(
111 | dfd.callback(function(pass) {
112 | assert.isTrue(pass,'messsage was received');
113 | })
114 | );
115 | },
116 | 'mqtt /test3 topic':function() {
117 | console.log('************* '+this.name+' **************');
118 | var dfd = this.async(3000);
119 | mqttPublish('/test3', '2 - Hello from 1').then(
120 | dfd.callback(function(pass) {
121 | assert.isFalse(pass,'Message should not be received');
122 | })
123 | );
124 | },
125 | 'mqtt /test2/something topic':function() {
126 | console.log('************* '+this.name+' **************');
127 | var dfd = this.async(3000);
128 | mqttPublish('/test2/something', '3 - Hello from 1').then(
129 | dfd.callback(function(pass) {
130 | assert.isTrue(pass,'Message should be received');
131 | })
132 | );
133 | },
134 | 'mqtt /test2something topic':function() {
135 | console.log('************* '+this.name+' **************');
136 | var dfd = this.async(3000);
137 | mqttPublish('/test2something', '4 - Hello from 1').then(
138 | dfd.callback(function(pass) {
139 | assert.isFalse(pass,'Message should not be received');
140 | })
141 | );
142 | },
143 | 'mqtt /test2something -> /test2 topic':function() {
144 | console.log('************* '+this.name+' **************');
145 | var dfd = this.async(3000);
146 | // Overwrite the mq2 stuff (clean out, start over);
147 | mq2 = null;
148 | mq2 = ep.getMqttEndpoint();
149 | mq2.subscribe('/test2');
150 | mqttPublish('/test2something', '5 - Hello from 1').then(
151 | dfd.callback(function(pass) {
152 | assert.isFalse(pass,'Message should not be received');
153 | })
154 | );
155 | },
156 |
157 | 'mqtt /test2/something -> /test2 topic':function() {
158 | console.log('************* '+this.name+' **************');
159 | var dfd = this.async(3000);
160 | // Overwrite the mq2 stuff (clean out, start over);
161 | mq2 = null;
162 | mq2 = ep.getMqttEndpoint();
163 | mq2.subscribe('/test2');
164 | mqttPublish('/test2/something', '6 - Hello from 1').then(
165 | dfd.callback(function(pass) {
166 | assert.isFalse(pass,'Message should not be received');
167 | })
168 | );
169 | }
170 | });
171 | });
172 |
--------------------------------------------------------------------------------
/tests/manual/old/deprecated/devTestClient.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
6 |
7 |
8 | Signaling Service
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
23 |
24 |
ibmrtc.webrtc & ibmrtc.rtcomm manual test client
25 |
26 |
This test creates an ibm webrtc Signaling Client (does not do the audio video portion yet)
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
MQTT:
36 |
TopicName:
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
1.
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
2.
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
3.
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
Connected to:
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
229 |
230 |
231 |
232 |
--------------------------------------------------------------------------------
/src/rtcomm/util/RtcommBaseObject.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014 IBM Corp.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | **/
16 | /** Base Rtcomm class that provides event functionality
17 | * @class
18 | * @memberof module:rtcomm.util
19 | */
20 | var RtcommBaseObject = {
21 | /** @lends module:rtcomm.util.RtcommBaseObject.prototype */
22 | /*
23 | * Properties
24 |
25 | objName : 'Base',
26 | id : 'unknown',
27 | config: {},
28 | dependencies: {},
29 | ready: false,
30 | state: 'unknown',
31 | states: {},
32 | events: {},
33 | */
34 | /*
35 | * Methods
36 | */
37 | setState : function(value, object) {
38 | if (typeof this.state !== 'undefined') {
39 | if (this.state !== value) {
40 | this.state = value;
41 | this.emit(value,object);
42 | } else {
43 | l('DEBUG') && console.log(this + '.setState(): State already set, ignoring '+value );
44 | }
45 | } else {
46 | this.emit(value,object);
47 | }
48 | },
49 | listEvents : function() {
50 |
51 | console.log('******* ' + this+' Configured events ***********');
52 | /*jslint forin: true */
53 | for(var event in this.events) {
54 | if (this.events.hasOwnProperty(event)) {
55 | console.log('******* ['+event+'] has '+this.events[event].length+' listeners registered');
56 | }
57 |
58 | }
59 | },
60 | clearEventListeners : function() {
61 | for(var event in this.events) {
62 | if (this.events.hasOwnProperty(event)) {
63 | this.events[event] = [];
64 | }
65 | }
66 | },
67 | createEvent: function(event) {
68 | if (this.hasOwnProperty('events')){
69 | this.events[event] = [];
70 | } else {
71 | throw new Error('createEvent() requires an events property to store the events');
72 | }
73 | },
74 | removeEvent: function(event) {
75 | if (event in this.events) {
76 | delete this.events[event];
77 | }
78 | },
79 |
80 | hasEventListener: function(event){
81 | return (event in this.events) && (this.events[event].length > 0);
82 | },
83 | /** Establish a listener for an event */
84 | on : function(event,callback) {
85 | //console.log('on -- this.events is: '+ JSON.stringify(this.events));
86 | // This function requires an events object on whatever object is attached to. and event needs to be defined there.
87 | if (this.events) {
88 | if(typeof event === 'object') {
89 | // this is an object of events:
90 | for (var key in event) {
91 | if (event.hasOwnProperty(key)) {
92 | if (this.events[key] && Array.isArray(this.events[key])) {
93 | l('EVENT', this) && console.log(this+' Adding a listener callback for event['+key+']');
94 | l('TRACE', this) && console.log(this+' Callback for event['+key+'] is', event[key]);
95 | this.events[key].push(event[key]);
96 | }
97 | }
98 | }
99 | } else {
100 | if (this.events[event] && Array.isArray(this.events[event])) {
101 | l('EVENT', this) && console.log(this+' Adding a listener callback for event['+event+']');
102 | l('TRACE', this) && console.log(this+' Callback for event['+event+'] is', callback);
103 | this.events[event].push(callback);
104 | }
105 | }
106 | } else {
107 | throw new Error("on() requires an events property listing the events. this.events["+event+"] = [];");
108 | }
109 | },
110 | /** attach a callback to ALL events */
111 | bubble : function(callback) {
112 | if (this.events) {
113 | for(var event in this.events) {
114 | if (this.events.hasOwnProperty(event) ) {
115 | this.events[event].push(callback);
116 | }
117 | }
118 | }
119 | },
120 | // Clear callbacks for a particular event.
121 | off : function(event) {
122 | if (this.events && this.events[event]) {
123 | l('EVENT', this) && console.log(this+' Removing listeners for event['+event+']');
124 | this.events[event] = [];
125 | }
126 | },
127 | /** emit an event from the object */
128 | emit : function(event, object) {
129 | var event_object = object || {};
130 | var self = this;
131 | // We have an event format specified, normalize the event before emitting.
132 | if (this._Event && typeof this._Event === 'function') {
133 | event_object = this._Event(event, event_object);
134 | }
135 | // event_object.name = (event_object.name) ? event_object.name : event;
136 | if (this.events && this.events[event] ) {
137 | // console.log('>>>>>>>> Firing event '+event);
138 | l('EVENT', this) && console.log(this+".emit() for event["+event+"]", self.events[event].length);
139 | // Save the event
140 | if (typeof self.lastEvent !== 'undefined') {
141 | self.lastEvent = event;
142 | };
143 | // Event exists, call all callbacks
144 | self.events[event].forEach(function(callback) {
145 | if (typeof callback === 'function') {
146 | l('EVENT', self) && console.log(self+".emit() executing callback for event["+event+"]");
147 | try {
148 | callback(event_object);
149 | } catch(e) {
150 | var m = 'Event['+event+'] callback failed with message: '+e.message;
151 | throw new Error(m);
152 | }
153 | } else {
154 | l('EVENT', self) && console.log(self+' Emitting, but no callback for event['+event+']');
155 | }
156 | });
157 | } else {
158 | throw new Error('emit() requires an events property listing the events. this.events['+event+'] = [];');
159 | }
160 | },
161 | extend: function(props) {
162 | var prop, obj;
163 | obj = Object.create(this);
164 | for (prop in props) {
165 | if (props.hasOwnProperty(prop)) {
166 | obj[prop] = props[prop];
167 | }
168 | }
169 | return obj;
170 | },
171 | // Test Function
172 | _l: function(level){
173 | if (typeof l === 'function') {
174 | return l(level,this);
175 | } else {
176 | return 'unknown';
177 | }
178 | },
179 | toString: function() {
180 | var name = (this._ && this._.objName)? this._.objName : this.objName || this.name || 'Unknown';
181 | var id = (this._ && this._.id)? this._.id: this.id || 'Unknown';
182 | return name + '['+id+']';
183 | }
184 | };
185 |
186 | exports.RtcommBaseObject = RtcommBaseObject;
187 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | #lib.rtcomm.clientjs
2 |
3 | The rtcomm.js library is a JavaScript Universal Module Description(UMD) formatted module that provides an API for client side web application developers to enable WebRTC functionality. This module handles signaling and creation of WebRTC PeerConnections between endpoints in a simple and flexible way. The only requirement for peer-to-peer calling is an MQTT Broker that supports MQTT protocol version 3.1 or higher. A nice open source MQTT broker solution if your a fan of Node.js is [Mosca](https://github.com/mcollina/mosca).
4 |
5 | If you need additional capabilities like SIP federation, a backend programming model for services (e.g media server integration for record/playback), a registry, third party call control and call queues, the Liberty profile of the WebSphere Application Server comes with the 'rtcomm-1.0' feature which can be configured to connect with the same broker used for peer-to-peer calling. This page provides additional details on how to setup Liberty to work with your MQTT broker:[Using Liberty and rtcomm](docs/rtcomm_and_liberty_setup.md)
6 |
7 | ##Requirements
8 |
9 | 1. An MQTT Server such as [IBM MessageSite](https://developer.ibm.com/messaging/messagesight/) or [Mosca](https://github/mcollina/mosca). For prototyping and development, it is possible to use `messagesight.demos.ibm.com`.
10 | 2. A web browsers that support WebRTC (tested w/ Chrome and Firefox)
11 |
12 | ##Dependencies
13 |
14 | The rtcomm.js library is dependent on the following libraries (which will be installed via bower). If you do not use bower, then you can get the files in the links below:
15 |
16 | 1. Paho MQTT JavaScript client [link](http://git.eclipse.org/c/paho/org.eclipse.paho.mqtt.javascript.git/tree/src/mqttws31.js)
17 | 2. WebRTC Adapter [link] (https://github.com/webrtc/adapter)
18 |
19 | ** Regarding SSL & WebRTC [BEHAVIOR CHANGE]**
20 |
21 | As of Chrome 47([WebRTC Release Notes](https://developers.google.com/web/updates/2015/10/chrome-47-webrtc)) `getUserMedia` will fail with a permissions error unless the site is served over SSL. This has the side effect of requiring a secure connection to the MQTT Server as well. By default, when served over `https` the client will attempt to connect to the `sslport` if configured or the `port` if not. Make sure your MQTT Server supports SSL.
22 |
23 | ##Installation
24 |
25 | ###Bower
26 |
27 | *`rtcomm`* is a registered bower module and can be installed using bower.
28 | ```
29 | bower install rtcomm
30 | ```
31 | This will handle installing the `mqttws31` and `webrtc-adapter` dependencies as well as the rtcomm library. Once installed, the scripts still need to be loaded in the application html file based on where bower installed the libraries.
32 |
33 | ### Inclusion in Browser
34 |
35 | Add the following you your html file:
36 |
37 | ```html
38 |
39 |
40 |
41 | ```
42 |
43 | ### Write some HTML
44 |
45 | ```html
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 | ```
54 | ### Add some JavaScript
55 |
56 | Define the configuration to connect to an MQTT Server.
57 |
58 | ```javascript
59 |
117 | ```
118 |
119 | ## More information
120 | To get started with a sample, checkout the [Getting Started with the Sample](docs/sample.md).
121 |
122 | For more detailed information on the API, see an [extended explanation](docs/extended_explanation.md).
123 |
124 | Further information on the RtcommEndpoint API is located in the *jsdoc* which is available in the sample zip file or when you clone and build the project using `grunt`.
125 |
126 | #Building the code
127 |
128 | If you want to clone the repository and build this yourself, you will need to:
129 |
130 | 1. Clone the repository:
131 | ```
132 | git clone https://github.com/WASdev/lib.rtcomm.clientjs.git
133 | ```
134 | 2. Install node.js and npm (http://nodejs.org/download/)
135 | 3. Install necessary dependencies via npm from the repository directory (assuming lib.rtcomm.clientjs)
136 | ```
137 | lib.rtcomm.clientjs/ # npm install
138 | ```
139 | 4. Install the grunt-cli (globally if not installed)
140 | ```
141 | npm install -g grunt-cli
142 | ```
143 | 5. Build the library:
144 | ```
145 | grunt
146 | ```
147 | This will create a **dist** directory with the following contents:
148 | ```
149 | |-jsdoc
150 | |--rtcomm.js
151 | |--rtcomm.min.js
152 | |-umd
153 | |--rtcomm.js
154 | |--rtcomm
155 | |---connection.js
156 | |---util.js
157 | ```
158 |
159 | 6. Download the Bower dependencies(necessary for tests)
160 | ```
161 | bower install
162 | ```
163 | # Running the tests
164 |
165 | Reference the [README.md](tests/README.md) file in the tests/ directory.
166 |
167 |
--------------------------------------------------------------------------------
/tests/functional/SessionQueue.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2013 IBM Corp.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | **/
16 | define([
17 | 'intern',
18 | 'intern!object',
19 | 'intern/chai!assert',
20 | (typeof window === 'undefined' && global) ?
21 | 'intern/dojo/node!../support/mqttws31_shim':
22 | 'bower_components/bower-mqttws/mqttws31',
23 | 'support/config',
24 | 'bower_components/webrtc-adapter/adapter',
25 | 'umd/rtcomm/EndpointProvider',
26 | 'support/rtcommFatUtils'
27 | ], function (intern, registerSuite, assert, globals, config, adapter, EndpointProvider,Fat) {
28 | var suiteName = Fat.createSuiteName("FVT: Session Queue");
29 |
30 | var DEBUG = (intern.args.DEBUG === 'true')? true: false;
31 | // endpointProvider
32 | var ep = null;
33 | // Endpoint
34 | var rtcommEP = null;
35 | var mqtt = null;
36 | var cfg = config.clientConfig1();
37 |
38 | var noQueuesConfigured = true;
39 | var START_SESSION = {
40 | 'rtcommVer': 'v1.0.0',
41 | 'method': 'START_SESSION',
42 | 'fromTopic': null,
43 | 'protocols': ['chat'],
44 | 'sigSessID':'1111-fake-test-1111',
45 | 'transID':'2222-fake-test-2222',
46 | 'toEndpointID': 'queueid',
47 | 'payload': { type: 'offer' },
48 | 'appContext': 'rtcommTest'
49 | };
50 | function getPublishTopic(sharedTopic) {
51 | console.log('**********'+sharedTopic);
52 | return sharedTopic.replace(/^\$SharedSubscription.+\/\//,'\/')
53 | .replace(/\/#$/g,'\/');
54 | }
55 | registerSuite({
56 | name: suiteName,
57 | setup: function() {
58 | console.log('************* SETUP: '+this.name+' **************');
59 | var p = new Promise(
60 | function(resolve, reject) {
61 | ep = new EndpointProvider();
62 | DEBUG && ep.setLogLevel('DEBUG');
63 | cfg.userid = 'intern';
64 | cfg.appContext = 'rtcommTest';
65 | ep.init(cfg,
66 | function() {
67 | console.log('***** Setup Complete *****');
68 | },
69 | function(error){
70 | console.log('**** Setup Failed *****', error);
71 | reject(error);
72 | });
73 | ep.on('queueupdate', function(queues) {
74 | noQueuesConfigured = false;
75 | console.log('*******QUEUES!', queues);
76 | clearTimeout(timer);
77 | resolve();
78 | });
79 |
80 | var timer = setTimeout(function(){
81 | console.log('******* Resolving -- no Queues Configured');
82 | resolve();
83 | },5000);
84 | });
85 | return p;
86 | },
87 | beforeEach: function() {
88 | // Destroy the endpoint.
89 | console.log('>>>>>>>>>>>> Reset the rtcommEP[New Test] <<<<<<<<<<<<<<<');
90 | rtcommEP && rtcommEP.destroy();
91 | rtcommEP = ep.createRtcommEndpoint({chat:true, webrtc: false});
92 | console.log('reset the mqtt');
93 | mqtt && mqtt.destroy();
94 | mqtt = ep.getMqttEndpoint();
95 | },
96 | teardown: function() {
97 | console.log('************* TEARDOWN: '+this.name+' **************');
98 | ep.destroy();
99 | ep = null;
100 | },
101 |
102 | 'Init of EndpointProvider creates Queues' : function() {
103 | console.log('************* '+this.name+' **************');
104 | noQueuesConfigured && this.skip("There are no Queues configured on the server");
105 | console.log('queues: ', ep.listQueues());
106 | assert.ok(ep.listQueues().length > 0 , 'queues is defined');
107 | },
108 |
109 | 'Join bad queue throws exception': function () {
110 | console.log('************* '+this.name+' **************');
111 | var error;
112 | try {
113 | ep.joinQueue('/TestTopic/#');
114 | } catch(e) {
115 | error = e;
116 | console.log(e);
117 | }
118 | assert.isDefined(error, 'An error was thrown correctly');
119 | },
120 | "Join/Leave queue": function() {
121 | console.log('************* '+this.name+' **************');
122 | noQueuesConfigured && this.skip("There are no Queues configured on the server");
123 | var dfd = this.async();
124 | var endpoint = ep.createRtcommEndpoint({webrtc: false, chat:true});
125 | var initObj = null;
126 | var success = false;
127 | var self = this;
128 | var testConfig = config.clientConfig();
129 | testConfig.userid = 'Agent';
130 | var finish = dfd.callback(function(object) {
131 | console.log('************ Finish called w/ OBJECT: ',object);
132 | var e = false;
133 | try{
134 | if (ep.listQueues().length > 0) {
135 | ep.joinQueue(ep.listQueues()[0]);
136 | }
137 | } catch(error) {
138 | e= true;
139 | }
140 | ep.leaveQueue(ep.listQueues()[0]);
141 | console.log('TEST -> userid: ' + endpoint.userid);
142 | assert.ok(/^Agent/.test(endpoint.userid));
143 | console.log("TEST => ready: "+ endpoint);
144 | assert.ok(endpoint);
145 | console.log("JoinQueue was successful: "+ endpoint);
146 | assert.notOk(e);
147 | });
148 | ep.init(testConfig,finish, finish);
149 | },
150 |
151 | 'Send a start session to a queue': function () {
152 | console.log('************* '+this.name+' **************');
153 | noQueuesConfigured && this.skip("There are no Queues configured on the server");
154 | var dfd = this.async(5000);
155 | var error;
156 | var queue = ep.getAllQueues()[ep.listQueues()[0]];
157 | console.log('>>>>>>> queue', queue.endpointID);
158 | ep.joinQueue(queue.endpointID);
159 | var publishTopic = getPublishTopic(queue.topic);
160 | console.log('publishTopic: ', publishTopic);
161 | var finish = dfd.callback( function(obj) {
162 | var endpoint = obj.endpoint;
163 | //obj should be a WebRTCConnection
164 | // and Source should match our topic we know...
165 | console.log('FINISH Called!', obj);
166 | assert.equal(endpoint._.activeSession.source, publishTopic+'/intern', 'source topic is right');
167 | });
168 | rtcommEP.on('session:alerting', finish);
169 | // Create a Message
170 | var msg = ep.dependencies.endpointConnection.createMessage('START_SESSION');
171 | msg.protocols = ['chat'];
172 | msg.fromTopic = '/scott';
173 | msg.sigSessID = '1111-fake-test-1111';
174 | msg.transID = '2222-fake-test-2222';
175 | msg.toEndpointID = 'queueid';
176 | msg.appContext='rtcommTest' ;
177 | mqtt.publish(publishTopic+'/intern', msg);
178 | },
179 | });
180 | });
181 |
--------------------------------------------------------------------------------
/src/mock/MockRtcommServer.js:
--------------------------------------------------------------------------------
1 | /*
2 | * This is a Paho Client and Mock Rtcomm Server for testing purposes only. It ONLY handles registration and
3 | * forwarding messages to another user. It does not fail gracefully yet.
4 | *
5 | */
6 |
7 | var Paho = (function(){
8 | console.log('************** Loading Mock Paho Client *****************');
9 | var l = function(level) {
10 | return true;
11 | };
12 | /*global mockMqtt:false*/
13 | var mockMqtt = (function () {
14 | // This is a fake to make sure everythign is logged
15 |
16 | /* build a regular expression to match the topic */
17 | var buildTopicRegex= function(topic) {
18 | // If it starts w/ a $ its a Shared subscription. Essentially:
19 | // $SharedSubscription/something//
20 | // We need to Remove the $-> //
21 | // /^\$.+\/\//, ''
22 | var regex = topic.replace(/^\$SharedSubscription.+\/\//, '\\/')
23 | .replace(/\/\+/g,'\\/.+')
24 | .replace(/\/#$/g,'($|\\/.+$)')
25 | .replace(/(\\)?\//g, function($0, $1){
26 | return $1 ? $0 : '\\/';
27 | });
28 |
29 | // The ^ at the beginning in the return ensures that it STARTS w/ the topic passed.
30 | return new RegExp('^'+regex+'$');
31 | };
32 |
33 | var mqtt_clients = {};
34 | var topics = {};
35 |
36 | var findTopic = function(topic) {
37 | for (var t in topics) {
38 | //console.log('findTopic looking for '+topic+' against :'+t);
39 | if (buildTopicRegex(t).test(topic)){
40 | // console.log('returning : ',t);
41 | return t;
42 | }
43 | }
44 | };
45 | return {
46 | add : function(client) {
47 | mqtt_clients[client._.clientid] = client;
48 | return mqtt_clients[client._.clientid];
49 | },
50 | subscribe : function(topic, mqttClient) {
51 | // only one client is subscribed to a topic in this case... which is wrong...
52 | topics[topic] = mqttClient;
53 | },
54 | send : function(message) {
55 | var topic = message.destinationName;
56 | console.log('mockMqtt.send() topic: '+topic +' message: '+ message);
57 | var matchedTopic = findTopic(topic);
58 | if (matchedTopic) {
59 | //console.log('mockMqtt.send() emitting on ', topics[matchedTopic]);
60 | // console.log('mockMqtt.send() message is', message);
61 | topics[matchedTopic].emit('message', message);
62 | }
63 | },
64 | _getClients : function() {
65 | return mqtt_clients;
66 | },
67 | _getSubscriptions : function () {
68 | return topics;
69 | }
70 | };
71 |
72 | })();
73 |
74 |
75 | var MockMqttClient = function MockMqttClient(server, port, clientid) {
76 |
77 | this._ = {
78 | server: server,
79 | port: port,
80 | clientid: clientid };
81 | console.log('***************** Using a Mock MQTT Client *****************', this._);
82 |
83 | this.events = {
84 | 'message': []
85 | };
86 |
87 | this.onMessageArrived = function(message) {
88 | console.log('Not Defined', message);
89 | };
90 |
91 | function connect(options){
92 | l('DEBUG') && console.log('MockMqttClient.connect()', options);
93 | // Add to the server.
94 | mockMqtt.add(this);
95 | var self = this;
96 | var onSuccess = (options && options.onSuccess) ? options.onSuccess : function(){ console.log('MockMqttClient.connect onSuccess not defined');};
97 | var onFailure = (options && options.onFailure) ? options.onFailure: function(){ console.log('MockMqttClient.connect onFailure not defined');};
98 | self.on('message', self.onMessageArrived);
99 | onSuccess();
100 | };
101 |
102 | function send(message) {
103 | l('DEBUG') && console.log('MockMqttClient.send()', message);
104 | mockMqtt.send(message);
105 | };
106 |
107 | function subscribe(topic) {
108 | l('DEBUG') && console.log('MockMqttClient.subscribe()', topic);
109 | var self = this;
110 | mockMqtt.subscribe(topic, self);
111 | };
112 |
113 | function unsubscribe(topic) {
114 | l('DEBUG') && console.log('MockMqttClient.unsubscribe()', topic);
115 | };
116 |
117 | function disconnect(topic) {
118 | l('DEBUG') && console.log('MockMqttClient.disconnect()', topic);
119 | };
120 |
121 | this.connect= connect;
122 | this.subscribe= subscribe;
123 | this.unsubscribe= unsubscribe;
124 | this.send= send;
125 | this.disconnect= disconnect;
126 | };
127 |
128 | /*global util:false */
129 | MockMqttClient.prototype = util.RtcommBaseObject.extend({});
130 |
131 | var MockMqttMessage = function MockMqttMessage(message) {
132 | return {destinationName: null, payloadString: message};
133 | };
134 |
135 | var MockRtcommServer = (function MockRtcommServer() {
136 |
137 | var rtcommTopicPath = "/rtcomm/";
138 | var topics = {
139 | // Just the SERVICE_QUERY topic
140 | management: "",
141 | // The presence Topic
142 | sphere: "",
143 | // The main topic (connector)
144 | connector: ""
145 | //
146 | };
147 | var registry = {};
148 | var conn = null;
149 |
150 | function setTopics(rootTopic) {
151 | for (var key in topics) {
152 | topics[key] = rootTopic + key + "/#";
153 | }
154 | }
155 | return {
156 |
157 | /**
158 | * config.rtcommTopicPath is only option
159 | */
160 |
161 | init : function init(config) {
162 | console.log('********** Using a Mock Rtcomm Server ****************');
163 | rtcommTopicPath = (config && config.rtcommTopicPath) ? config.rtcommTopicPath : rtcommTopicPath;
164 | setTopics(rtcommTopicPath);
165 | conn = new connection.MqttConnection({server:'localhost', port: 1883, 'rtcommTopicPath': rtcommTopicPath});
166 | conn.setLogLevel('DEBUG');
167 | conn.connect();
168 | conn.subscribe(topics.management);
169 | conn.subscribe(topics.sphere);
170 | conn.subscribe(topics.connector);
171 | conn.on('message', function(message) {
172 | console.log('MockRtcommServer --> Received a message!', message);
173 | if (message) {
174 | if (message.content === '') {
175 | // If it is an empty message, its a unregister LWT, remove the entry.
176 | if (message.fromEndpoint) {
177 | delete registry[message.fromEndpointID];
178 | }
179 | } else {
180 | var rtcommMessage = (typeof message.content === 'string')? JSON.parse(message.content): message.content;
181 | switch(rtcommMessage.method) {
182 | case 'DOCUMENT':
183 | console.log('DOCUMENT received');
184 | registry[message.fromEndpointID] = rtcommMessage.addressTopic;
185 | break;
186 | case 'SERVICE_QUERY':
187 | console.log('SERVICE QUERY!');
188 | // We should actually respond to this w/ mock data.
189 | break;
190 | default:
191 | console.log('DEFAULT: ', rtcommMessage);
192 | if(rtcommMessage.toEndpointID in registry) {
193 | conn.publish(registry[rtcommMessage.toEndpointID] +'/'+message.fromEndpointID, rtcommMessage);
194 | } else {
195 | console.error('toEndpointID not found, should send a Failure response');
196 | }
197 | break;
198 | }
199 | }
200 | }
201 | });
202 | },
203 | getRegistry: function() {
204 | return registry;
205 | }
206 | };
207 | })();
208 |
209 | return {
210 | MQTT: {
211 | Client: MockMqttClient,
212 | Message: MockMqttMessage
213 | },
214 | MockRtcommServer: MockRtcommServer
215 | };
216 | })();
217 |
218 |
219 |
--------------------------------------------------------------------------------
/src/rtcomm/connection/MessageFactory.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014 IBM Corp.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | **/
16 | /** @class
17 | * @memberof module:rtcomm.connector
18 | * @private
19 | */
20 | /* Constructor */
21 |
22 | var MessageFactory = (function (){
23 | // base Template used for everything.
24 | var _baseHeaders = {
25 | 'rtcommVer': 'v1.0.0',
26 | 'method' : null,
27 | 'fromTopic': null
28 | };
29 |
30 | var _optionalHeaders = {
31 | 'sigSessID':null,
32 | 'transID':null,
33 | 'reason': null,
34 | 'toEndpointID': null,
35 | 'appContext': null,
36 | 'holdTimeout': null,
37 | 'queuePosition': null
38 | };
39 |
40 | // Override base headers and add new headers for the OUTBOUND message
41 | // If it is a transaction, it will have a transID
42 |
43 | var _messageTemplates = {
44 | 'SERVICE_QUERY' : {
45 | 'method': 'SERVICE_QUERY',
46 | 'transID': null,
47 | },
48 | 'START_SESSION' : {
49 | 'method': 'START_SESSION',
50 | 'protocols': [],
51 | 'sigSessID':null,
52 | 'transID':null,
53 | 'toEndpointID': null,
54 | 'payload': null,
55 | },
56 | 'REFER' : {
57 | 'method': 'REFER',
58 | 'transID':null,
59 | 'toEndpointID': null,
60 | 'details': null,
61 | },
62 | 'STOP_SESSION' : {
63 | 'method': 'STOP_SESSION',
64 | 'sigSessID':null,
65 | 'payload': null,
66 | },
67 | 'PRANSWER': {
68 | 'method': 'PRANSWER',
69 | 'protocols': [],
70 | 'payload': null
71 | },
72 | // Message is generic and could be anything...
73 | 'MESSAGE':{
74 | 'method':'MESSAGE',
75 | 'payload': null
76 | },
77 | 'DOCUMENT': {
78 | 'method': 'DOCUMENT',
79 | 'type': 'ENDPOINT',
80 | 'addressTopic':null,
81 | 'appContext':null,
82 | 'state': null,
83 | 'alias': null,
84 | 'userDefines':[]
85 | },
86 | 'DOCUMENT_REPLACED': {
87 | 'method': 'DOCUMENT_REPLACED'
88 | }
89 | };
90 |
91 | var _baseResponseTemplate = {
92 | 'RESPONSE' : {
93 | 'method': 'RESPONSE',
94 | 'orig': null,
95 | 'transID': null,
96 | 'result': null,
97 | }
98 | };
99 |
100 | var _responseTemplates = {
101 | 'SERVICE_QUERY' : {
102 | 'orig': 'SERVICE_QUERY',
103 | 'services':null
104 | },
105 | 'START_SESSION' : {
106 | 'orig': 'START_SESSION',
107 | 'protocols': [],
108 | 'sigSessID': null,
109 | 'result': null,
110 | 'payload': null,
111 | 'transID': null,
112 | },
113 | 'REFER' : {
114 | 'orig': 'REFER',
115 | 'transID':null,
116 | 'result': null,
117 | }
118 | };
119 |
120 | function getMessageTemplate(type) {
121 | var template = {};
122 | objMerge(template,_baseHeaders);
123 | if (_messageTemplates.hasOwnProperty(type)) {
124 | objMerge(template,_messageTemplates[type]);
125 | return template;
126 | } else {
127 | console.error('Message Type: '+type+' Not found!');
128 | return null;
129 | }
130 | }
131 |
132 | function getResponseTemplate(type) {
133 | var template = {};
134 | objMerge(template,_baseHeaders);
135 | objMerge(template, _baseResponseTemplate.RESPONSE);
136 | if (_responseTemplates.hasOwnProperty(type)) {
137 | objMerge(template,_responseTemplates[type]);
138 | return template;
139 | } else {
140 | console.error('Message Type: '+type+' Not found!');
141 | return null;
142 | }
143 | }
144 |
145 | function objMerge(obj1,obj2) {
146 | // Take Right Object and place on top of left object.
147 | for (var key in obj2) {
148 | if (obj2.hasOwnProperty(key)) {
149 | obj1[key] = obj2[key];
150 | }
151 | }
152 | }
153 |
154 | var SigMessage = function SigMessage(template) {
155 | if (template) {
156 | for (var key in template) {
157 | if (template.hasOwnProperty(key)) {
158 | this[key] = template[key];
159 | }
160 | }
161 | }
162 | };
163 |
164 | SigMessage.prototype = {
165 | /** Convert message to a specific JSON object
166 | *
167 | * @returns {JSON}
168 | *
169 | */
170 | toJSON: function() {
171 | var obj = {};
172 | for (var key in this) {
173 | if (this.hasOwnProperty(key)) {
174 | obj[key] = this[key];
175 | }
176 | }
177 | return obj;
178 | },
179 | /* Override */
180 | toString: function() {
181 | // When converted to a string, we return a SPECIFIC object content that matches the Message Template
182 | return JSON.stringify(this.toJSON());
183 | }
184 | };
185 |
186 | function createResponse(type) {
187 | var message = null;
188 | var template = getResponseTemplate(type);
189 | if (template) {
190 | message = new SigMessage(template);
191 | } else {
192 | throw new TypeError('Invalid Message type:'+type+', should be one of: '+ Object.keys(_messageTemplates));
193 | }
194 | return message;
195 | }
196 |
197 | function createMessage(type) {
198 | type = type || 'MESSAGE';
199 | var message = null;
200 | var template = getMessageTemplate(type);
201 | if (template) {
202 | message = new SigMessage(template);
203 | } else {
204 | throw new TypeError('Invalid Message type:'+type+', should be one of: '+ Object.keys(_messageTemplates));
205 | }
206 | return message;
207 | }
208 |
209 | function isValid(message) {
210 | try {
211 | var tmpmsg = cast(message);
212 | } catch(e) {
213 | // unable to cast, not a good message.
214 | return false;
215 | }
216 | return true;
217 | }
218 |
219 | function cast(obj) {
220 | /*global l:false*/
221 | l('TRACE') && console.log('MessageFactory.cast() Attempting to cast message: ', obj);
222 |
223 | if ( typeof obj === 'string') {
224 | l('TRACE') && console.log('MessageFactory.cast() It is a string... ', obj);
225 | /* if its a 'STRING' then convert to a object */
226 | try {
227 | obj = JSON.parse(obj);
228 | } catch (e) {
229 | throw new TypeError('Unable to cast object as a SigMessage');
230 | }
231 | l('TRACE') && console.log('MessageFactory.cast() After JSON.parse... ', obj);
232 | }
233 | var template = null;
234 | if (obj.method) {
235 | template = (obj.method === 'RESPONSE') ? getResponseTemplate(obj.orig):getMessageTemplate(obj.method);
236 | } else {
237 | throw new TypeError('Unable to cast object as a SigMessage');
238 | }
239 | var castedMessage = new SigMessage(template);
240 | for (var prop in obj){
241 | // console.log("key:" + prop + " = " + obj[prop]);
242 | if (template.hasOwnProperty(prop) || _optionalHeaders.hasOwnProperty(prop)){
243 | // console.log("key:" + prop + " = " + obj[prop]);
244 | castedMessage[prop] = obj[prop];
245 | } else {
246 | l('DEBUG') && console.log('MessageFactory.cast() dropped header: '+prop);
247 | }
248 | }
249 | l('TRACE') && console.log('MessageFactory.cast() returning casted message:', castedMessage);
250 | return castedMessage;
251 | }
252 |
253 | return {
254 | createMessage: createMessage,
255 | createResponse: createResponse,
256 | cast : cast
257 | };
258 | })();
259 |
260 | exports.MessageFactory = MessageFactory;
261 |
--------------------------------------------------------------------------------
/tests/functional/SessionEndpoint.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2013 IBM Corp.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | **/
16 | define([
17 | 'intern',
18 | 'intern!object',
19 | 'intern/chai!assert',
20 | 'intern/node_modules/dojo/Promise',
21 | (typeof window === 'undefined' && global)
22 | ?'intern/dojo/node!../support/mqttws31_shim':
23 | 'bower_components/bower-mqttws/mqttws31',
24 | 'support/config',
25 | 'bower_components/webrtc-adapter/adapter',
26 | 'umd/rtcomm/EndpointProvider',
27 | 'support/rtcommFatUtils'
28 | ], function (intern, registerSuite, assert, Deferred, globals, config, adapter, EndpointProvider,Fat) {
29 | var suiteName = Fat.createSuiteName("FVT: SessionEndpoint");
30 | var DEBUG = (intern.args.DEBUG === 'true')? true: false;
31 | var SKIP_ALL=false;
32 | // anything in here will get destroyed (but not recreated) in beforeEach;
33 | var g ={};
34 | var destroy = function() {
35 | Object.keys(g).forEach(function(key) {
36 | if (typeof g[key].destroy === 'function' ) {
37 | g[key].destroy();
38 | delete g[key];
39 | }
40 | });
41 | };
42 | // used to run this Suite 2 times (one w/ rtcomm server & 1 w/out)
43 | var getConfig = function getConfig(id) {
44 | var c = config.clientConfig(id);
45 | return c;
46 | }
47 | // Timings
48 | var T1 = 3000; // How long we wait to setup, before sending messages.
49 | var T2 = T1 + 3000; // How long we wait to check results
50 | var T3 = T2 +3000; // How long we wait to timeout test.
51 |
52 | var createAndInitTwoProviders = function createAndInitTwoProviders(cfg1, cfg2) {
53 | var p = new Promise(
54 | function(resolve, reject){
55 | Fat.createProvider(cfg1, DEBUG).then( function(EP) {
56 | Fat.createProvider(cfg2, DEBUG)
57 | .then( function(EP2) {
58 | // Wait 1 second to resolve
59 | setTimeout(function() {
60 | resolve({provider1: EP, provider2: EP2});
61 | },T1);
62 | })
63 | .catch(function(message){
64 | reject(message);
65 | });
66 | }).catch(function(message){
67 | reject(message);
68 | });
69 | });
70 | return p;
71 | };
72 |
73 | registerSuite({
74 | name: suiteName,
75 | setup: function() {
76 | console.log('************* SETUP: '+this.name+' **************');
77 | },
78 | teardown: function() {
79 | console.log('************* TEARDOWN: '+this.name+' **************');
80 | destroy();
81 | },
82 | beforeEach: function() {
83 | destroy();
84 | },
85 | "SessionEndpoint(No protocols) A calls B": function() {
86 | console.log('************* '+this.name+' **************');
87 | // SKIP_ALL && this.skip(false);
88 | var dfd = this.async(T2);
89 | // Our endpoints
90 | var ep1;
91 | var ep2;
92 | var finish = dfd.callback(function(object){
93 | console.log("******************Asserting now...***********************");
94 | console.log('endpoint1: ',ep1);
95 | console.log('endpoint2: ',ep2);
96 | assert.ok(ep1_trying, 'Caller generated trying event');
97 | assert.ok(ep1_ringing, 'Caller generated ringing event');
98 | assert.ok(ep2_alerting, 'Callee generated alerting event');
99 | assert.ok(ep1.sessionStarted());
100 | assert.ok(ep2.sessionStarted());
101 | });
102 |
103 | var ep1_trying = false;
104 | var ep1_ringing= false;
105 | var ep2_alerting= false;
106 | // receive PRANSWER --> ringing
107 | // receive ANSWER --> started
108 | // ep2(callee)
109 | // receive call --> alerting
110 | // send ANSWER --> started
111 | var cfg1 = getConfig('testuser1');
112 | var cfg2 = getConfig('testuser2');
113 | createAndInitTwoProviders(cfg1,cfg2)
114 | .then(function(obj){
115 | var EP1 = g.EP1 = obj.provider1;
116 | var EP2 = g.EP2 = obj.provider2;
117 | DEBUG && EP1.setLogLevel('DEBUG');
118 | DEBUG && EP2.setLogLevel('DEBUG');
119 | console.log('>>>> EP1:', EP1.currentState());
120 | console.log('>>>> EP2:', EP2.currentState());
121 | ep1 = EP1.getSessionEndpoint();
122 | ep2 = EP2.getSessionEndpoint();
123 | ep1.on('session:ringing', function() { ep1_ringing = true});
124 | ep1.on('session:trying', function() { ep1_trying = true});
125 | ep1.on('session:started', finish);
126 | ep2.on('session:alerting', function(obj) {
127 | ep2_alerting = true;
128 | console.log('>>>>TEST accepting call');
129 | setTimeout(function() {
130 | ep2.accept();
131 | },1000);
132 | });
133 | ep1.connect(cfg2.userid);
134 | });
135 | },
136 | "in Browser A calls B(disconnect while ringing)": function() {
137 | console.log('************* '+this.name+' **************');
138 | // SKIP_ALL && this.skip(false);
139 | var dfd = this.async(T2);
140 | // Our endpoints
141 | var ep1;
142 | var ep2;
143 |
144 | var finish = dfd.callback(function(object){
145 | console.log("******************Asserting now...***********************");
146 | console.log('endpoint1: ',ep1);
147 | console.log('endpoint2: ',ep2);
148 | assert.notOk(ep1_failed, 'Caller generated failed event');
149 | assert.notOk(ep2_failed, 'Callee generated failed event');
150 | assert.notOk(ep1_started, 'Caller generated started event');
151 | assert.notOk(ep2_started, 'Callee generated started event');
152 | assert.ok(ep1_ringing, 'Caller generated ringing event');
153 | assert.ok(ep2_alerting, 'Callee generated alerting event');
154 | });
155 |
156 | var ep1_trying = false;
157 | var ep1_ringing= false;
158 | var ep2_stopped= false;
159 | var ep1_stopped = false;
160 | var ep2_alerting= false;
161 | var ep1_failed= false;
162 | var ep2_failed= false;
163 | var ep1_started= false;
164 | var ep2_started= false;
165 |
166 |
167 | var cfg1 = getConfig('testuser1');
168 | var cfg2 = getConfig('testuser2');
169 | createAndInitTwoProviders(cfg1,cfg2)
170 | .then(function(obj){
171 | var EP1 = g.EP1 = obj.provider1;
172 | var EP2 = g.EP2 = obj.provider2;
173 | ep1 = EP1.getSessionEndpoint();
174 | ep2 = EP2.getSessionEndpoint();
175 | // receive PRANSWER --> ringing //
176 | // // receive ANSWER --> started
177 | // ep2(callee)
178 | // receive call --> alerting
179 | // send ANSWER --> started
180 | //
181 | ep1.on('session:ringing', function() {
182 | console.log('>>> ep1 session:ringing');
183 | ep1_ringing = true;});
184 | ep1.on('session:failed', function() { ep1_failed= true});
185 | ep2.on('session:failed', function() { ep2_failed= true});
186 | ep1.on('session:trying', function() { ep1_trying = true});
187 | ep1.on('session:started', function() { ep1_started = true});
188 | ep2.on('session:started', function() { ep2_started = true});
189 | ep1.on('session:stopped', function() {console.log('EP1 ***session:stopped'); ep1_stopped = true;});
190 | ep2.on('session:stopped', finish );
191 | ep2.on('session:alerting', function(obj) {
192 | // At this state, cancel the call.
193 | ep2_alerting = true;
194 | setTimeout(function() {
195 | console.log('********* Disconnecting call **************');
196 | ep1.disconnect();
197 | },1000);
198 | });
199 | ep1.connect(cfg2.userid);
200 | });
201 | },
202 | });
203 | });
204 |
205 |
206 |
--------------------------------------------------------------------------------
/src/rtcomm/EndpointProvider/endpoints/RtcommEndpoint.js:
--------------------------------------------------------------------------------
1 | var RtcommEndpoint = (function invocation() {
2 | /**
3 | * @memberof module:rtcomm.EndpointProvider
4 | * @description
5 | * This object can only be created with the {@link module:rtcomm.EndpointProvider#getRtcommEndpoint|getRtcommEndpoint} function.
6 | *
7 | * The RtcommEndpoint object provides an interface for the UI Developer to attach
8 | * Video and Audio input/output. Essentially mapping a broadcast stream(a MediaStream that
9 | * is intended to be sent) to a RTCPeerConnection output stream. When an inbound stream
10 | * is added to a RTCPeerConnection, then this also informs the RTCPeerConnection
11 | * where to send that stream in the User Interface.
12 | *
13 | * See the example under {@link module:rtcomm.EndpointProvider#getRtcommEndpoint|getRtcommEndpoint}
14 | * @constructor
15 | *
16 | * @extends module:rtcomm.SessionEndpoint
17 | */
18 | var RtcommEndpoint = function RtcommEndpoint(config) {
19 | /**
20 | * @typedef {object} module:rtcomm.RtcommEndpoint~config
21 | *
22 | * @property {boolean} [autoEnable=false] Automatically enable webrtc/chat upon connect if feature is supported (webrtc/chat = true);
23 | * @property {string} [userid=null] UserID the endpoint will use (generally provided by the EndpointProvider
24 | * @property {string} [appContext=null] UI Component to attach outbound media stream
25 | * @property {string} [ringtone=null] Path to a ringtone to play when we are ringing on inbound callh
26 | * @property {string} [ringbacktone=null] path to a ringbacktone to play on outbound call
27 | * @property {boolean} [webrtc=true] Whether the endpoint supports webrtc
28 | * @property {module:rtcomm.RtcommEndpoint.WebRTCConnection~webrtcConfig} webrtcConfig - Object to configure webrtc with (rather than on enable)
29 | * @property {boolean} [chat=true] Whether the endpoint supports chat
30 | * @property {module:rtcomm.RtcommEndpoint.WebRTCConnection~chatConfig} chatConfig - object to pre-configure chat with (rather than on enable)
31 | * @property {module:rtcomm.EndpointProvider} [parent] - set the parent Should be done automatically.
32 | *
33 | */
34 | var defaultConfig = {
35 | // if a feature is supported, enable by default.
36 | autoEnable: false,
37 | ignoreAppContext: true,
38 | appContext: null,
39 | userid: null,
40 | ringtone: null,
41 | ringbacktone: null,
42 | // Always shold be true
43 | generic_message: true,
44 | chat: true,
45 | chatConfig: {},
46 | webrtc: true,
47 | webrtcConfig: {}
48 | };
49 |
50 |
51 | function addChatHandlers(ep) {
52 | var chat = ep.chat;
53 | // Configure chat event handling...
54 | //
55 | chat.on('ringing', function(event_obj) {
56 | (ep.lastEvent !== 'session:ringing') && ep.emit('session:ringing');
57 | });
58 | ep.createEvent('chat:message');
59 | chat.on('message', function(message) {
60 | // Should be '{message: blah, from: blah}'
61 | // This is for backward compatibility
62 | ep.emit('chat:message', {'message':message});
63 | });
64 | chat.on('alerting', function(message) {
65 | l('DEBUG') && console.log('RtcommEndpoint emitting session:alerting event');
66 | var obj = {};
67 | obj.message = message;
68 | obj.protocols = 'chat';
69 | // Have to do setState here because the ep state needs to change.
70 | (ep.lastEvent !== 'session:alerting') && ep.setState('session:alerting', obj);
71 | });
72 | ep.createEvent('chat:connected');
73 | chat.on('connected', function() {
74 | ep.emit('chat:connected');
75 | });
76 | ep.createEvent('chat:disconnected');
77 | chat.on('disconnected', function() {
78 | ep.emit('chat:disconnected');
79 | });
80 | };
81 |
82 | function addWebrtcHandlers(ep) {
83 | // Webrtc protocol...
84 | var webrtc = ep.webrtc;
85 |
86 | webrtc.on('ringing', function(event_obj) {
87 | l('DEBUG') && console.log("on ringing - play a ringback tone ", ep._.ringbackTone);
88 | ep._playRingback();
89 | (ep.lastEvent !== 'session:ringing') && ep.emit('session:ringing');
90 | });
91 |
92 | webrtc.on('trying', function(event_obj) {
93 | l('DEBUG') && console.log("on trying - play a ringback tone ", ep._.ringbackTone);
94 | ep._playRingback();
95 | (ep.lastEvent !== 'session:trying') && ep.emit('session:trying');
96 | });
97 |
98 | webrtc.on('alerting', function(event_obj) {
99 | ep._playRingtone();
100 | (ep.lastEvent !== 'session:alerting') && ep.emit('session:alerting', { protocols: 'webrtc'});
101 | });
102 |
103 | ep.createEvent('webrtc:connecting');
104 | webrtc.on('connecting', function(event_obj) {
105 | l('DEBUG') && console.log(ep+" webrtc.connecting - stop ringing ");
106 | ep._stopRing();
107 | ep.emit('webrtc:connecting');
108 | });
109 |
110 | ep.createEvent('webrtc:connected');
111 | webrtc.on('connected', function(event_obj) {
112 | l('DEBUG') && console.log("on connected - stop ringing ");
113 | ep._stopRing();
114 | ep.emit('webrtc:connected');
115 | });
116 | ep.createEvent('webrtc:disconnected');
117 | webrtc.on('disconnected', function(event_obj) {
118 | l('DEBUG') && console.log("on disconnected - stop ringing ");
119 | ep._stopRing();
120 | ep.emit('webrtc:disconnected');
121 | });
122 | ep.createEvent('webrtc:remotemuted');
123 | webrtc.on('remotemuted', function(event_obj) {
124 | ep.emit('webrtc:remotemuted', event_obj);
125 | });
126 | };
127 |
128 | function addGenericMessageHandlers(ep) {
129 | ep.createEvent('onetimemessage');
130 | ep.createEvent('generic_message:message');
131 | ep.generic_message.on('message', function(event_obj) {
132 | console.log('eventObject?', event_obj);
133 | // This shoudl be deprecated (onetimemessage) that is.
134 | var deprecatedEvent = {
135 | 'onetimemessage': event_obj.message
136 | };
137 | ep.emit('onetimemessage', deprecatedEvent);
138 | ep.emit('generic_message:message', event_obj);
139 | });
140 | };
141 |
142 | config = util.combineObjects(config, defaultConfig);
143 | // Call the Super Constructor
144 | SessionEndpoint.call(this, config);
145 | // Add the protocols
146 | this.addProtocol(new ChatProtocol());
147 | this.addProtocol(new WebRTCConnection(this));
148 | this.addProtocol(new GenericMessageProtocol());
149 | // Add the handlers to the protocols;
150 | addChatHandlers(this);
151 | addWebrtcHandlers(this);
152 | addGenericMessageHandlers(this);
153 | if (this.config.autoEnable) {
154 | this.config.chat && this.chat.enable();
155 | this.config.webrtc && this.webrtc.enable();
156 | };
157 |
158 | this.config.generic_message && this.generic_message.enable();
159 | this.config.chat && this.chat.enable();
160 |
161 | // WebRTC Specific configuration.
162 | // TODO: MOve to the webrtc protocol
163 | this._.ringTone = (this.config.ringtone) ? util.Sound(this.config.ringtone).load() : null;
164 | this._.ringbackTone = (this.config.ringbacktone) ? util.Sound(this.config.ringbacktone).load() : null;
165 | this._.inboundMedia = null;
166 | this._.attachMedia = false;
167 | this._.localStream = null;
168 | this._.media = {
169 | In: null,
170 | Out: null
171 | };
172 |
173 | } // End of Constructor
174 |
175 | RtcommEndpoint.prototype = Object.create(SessionEndpoint.prototype);
176 | RtcommEndpoint.prototype.constructor = RtcommEndpoint;
177 |
178 | // RtcommEndpoint Specific additions to the SessioNEndpoint
179 | RtcommEndpoint.prototype._playRingtone = function() {
180 | this._.ringTone && this._.ringTone.play();
181 | };
182 | RtcommEndpoint.prototype._playRingback = function() {
183 | this._.ringbackTone && this._.ringbackTone.play();
184 | };
185 |
186 | RtcommEndpoint.prototype._stopRing = function() {
187 | l('DEBUG') && console.log(this + '._stopRing() should stop ring if ringing... ', this._.ringbackTone);
188 | l('DEBUG') && console.log(this + '._stopRing() should stop ring if ringing... ', this._.ringTone);
189 | this._.ringbackTone && this._.ringbackTone.playing && this._.ringbackTone.stop();
190 | this._.ringTone && this._.ringTone.playing && this._.ringTone.stop();
191 | };
192 |
193 | /* deprecated , use RtcommEndpoint.generic-message.send(message) instead */
194 | RtcommEndpoint.prototype.sendOneTimeMessage = function sendOneTimeMessage(message) {
195 | this.generic_message.send(message);
196 | };
197 |
198 | return RtcommEndpoint;
199 | })();
200 |
--------------------------------------------------------------------------------