Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
emscripten-core
GitHub Repository: emscripten-core/emscripten
Path: blob/main/third_party/wrtcp.js
4129 views
1
/*
2
The MIT License
3
4
Copyright (c) 2012, Mozilla Foundation
5
Copyright (c) 2012, Alan Kligman
6
All rights reserved.
7
8
Permission is hereby granted, free of charge, to any person obtaining a copy
9
of this software and associated documentation files (the "Software"), to deal
10
in the Software without restriction, including without limitation the rights
11
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12
copies of the Software, and to permit persons to whom the Software is
13
furnished to do so, subject to the following conditions:
14
15
The above copyright notice and this permission notice shall be included in
16
all copies or substantial portions of the Software.
17
18
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24
THE SOFTWARE.
25
*/
26
27
(function() {
28
29
/* Notes
30
*
31
* - Continue using prefixed names for now.
32
*
33
*/
34
35
var io = SocketIO;
36
var webrtcSupported = true;
37
38
var RTCPeerConnection;
39
if(window.mozRTCPeerConnection)
40
RTCPeerConnection = window.mozRTCPeerConnection;
41
else if(window.webkitRTCPeerConnection)
42
RTCPeerConnection = window.webkitRTCPeerConnection;
43
else if(window.RTCPeerConnection)
44
RTCPeerConnection = window.RTCPeerConnection
45
else
46
webrtcSupported = false;
47
48
var RTCSessionDescription;
49
if(window.mozRTCSessionDescription)
50
RTCSessionDescription = window.mozRTCSessionDescription;
51
else if(window.webkitRTCSessionDescription)
52
RTCSessionDescription = window.webkitRTCSessionDescription;
53
else if(window.RTCSessionDescription)
54
RTCSessionDescription = window.RTCSessionDescription
55
else
56
webrtcSupported = false;
57
58
var RTCIceCandidate;
59
if(window.mozRTCIceCandidate)
60
RTCIceCandidate = window.mozRTCIceCandidate;
61
else if(window.webkitRTCIceCandidate)
62
RTCIceCandidate = window.webkitRTCIceCandidate;
63
else if(window.RTCIceCandidate)
64
RTCIceCandidate = window.RTCIceCandidate;
65
else
66
webrtcSupported = false;
67
68
var getUserMedia;
69
if(!navigator.getUserMedia) {
70
if(navigator.mozGetUserMedia)
71
getUserMedia = navigator.mozGetUserMedia.bind(navigator);
72
else if(navigator.webkitGetUserMedia)
73
getUserMedia = navigator.webkitGetUserMedia.bind(navigator);
74
else
75
webrtcSupported = false;
76
} else {
77
getUserMedia = navigator.getUserMedia.bind(navigator);
78
}
79
80
// FIXME: browser detection is gross, but I don't see another way to do this
81
var RTCConnectProtocol;
82
if(window.mozRTCPeerConnection) {
83
RTCConnectProtocol = mozRTCConnectProtocol;
84
} else if(window.webkitRTCPeerConnection) {
85
RTCConnectProtocol = webkitRTCConnectProtocol;
86
} else {
87
webrtcSupported = false;
88
}
89
90
function callback(object, method, args) {
91
if(!Array.isArray(args))
92
args = [args];
93
if(method in object && 'function' === typeof object[method]) {
94
object[method].apply(object, args);
95
}
96
};
97
98
function fail(object, method, error) {
99
if (!(error instanceof Error))
100
error = new Error(error);
101
callback(object, method, [error]);
102
};
103
104
function defer(queue, object, method, args) {
105
if(queue) {
106
queue.push([object, method, args]);
107
return true;
108
} else {
109
return false;
110
}
111
};
112
113
function processDeferredQueue(queue) {
114
while(queue.length) {
115
var deferred = queue.shift();
116
callback(deferred[0], deferred[1], deferred[2]);
117
}
118
};
119
120
var ONE_SECOND = 1000; // milliseconds
121
var DEFAULT_CONNECTION_TIMEOUT = 10 * ONE_SECOND;
122
var DEFAULT_PING_TIMEOUT = 1 * ONE_SECOND;
123
var RELIABLE_CHANNEL_OPTIONS = {
124
reliable: false
125
};
126
var UNRELIABLE_CHANNEL_OPTIONS = {
127
outOfOrderAllowed: true,
128
maxRetransmitNum: 0,
129
reliable: false
130
};
131
132
function PendingConnectionAbortError(message) {
133
this.name = "PendingConnectionAbortError";
134
this.message = (message || "");
135
};
136
PendingConnectionAbortError.prototype = Error.prototype;
137
138
function ConnectionFailedError(message) {
139
this.name = "ConnectionFailedError";
140
this.message = (message || "");
141
};
142
ConnectionFailedError.prototype = Error.prototype;
143
144
var E = {
145
PendingConnectionAbortError: PendingConnectionAbortError,
146
ConnectionFailedError: ConnectionFailedError
147
};
148
149
function WebSocketBroker(brokerUrl) {
150
this.brokerUrl = brokerUrl;
151
this.state = WebSocketBroker.OFFLINE;
152
153
this.onstatechange = null;
154
this.onreceive = null;
155
this.onerror = null;
156
157
this.socket = null;
158
this.route = null;
159
};
160
161
// States
162
WebSocketBroker.OFFLINE = 0x00;
163
WebSocketBroker.CONNECTING = 0x01;
164
WebSocketBroker.CONNECTED = 0x02;
165
// Flags
166
WebSocketBroker.ROUTED = 0x10;
167
WebSocketBroker.LISTENING = 0x20;
168
169
WebSocketBroker.prototype.setState = function setState(state, clearFlags) {
170
var clear = clearFlags ? 0x00 : 0xF0;
171
this.state &= clear >>> 0;
172
this.state |= state >>> 0;
173
callback(this, 'onstatechange', [this.state, (state | (clear & 0x0)) >>> 0]);
174
};
175
WebSocketBroker.prototype.setFlag = function setFlag(flag) {
176
this.state = (this.state | flag) >>> 0;
177
callback(this, 'onstatechange', [this.state, flag])
178
};
179
WebSocketBroker.prototype.clearFlag = function clearFlag(flag) {
180
flag = (~flag) >>> 0;
181
this.state = (this.state & flag) >>> 0;
182
callback(this, 'onstatechange', [this.state, flag])
183
};
184
WebSocketBroker.prototype.checkState = function checkState(mask) {
185
return !!(this.state & mask);
186
};
187
WebSocketBroker.prototype.connect = function connect() {
188
var that = this;
189
var socket = io.connect(this.brokerUrl + '/peer', {
190
'sync disconnect on unload': true // partially fixes 'interrupted while page loading' warning
191
});
192
193
socket.on('connecting', function onconnecting() {
194
that.setState(WebSocketBroker.CONNECTING, true);
195
});
196
197
socket.on('connect', function onconnect() {
198
that.setState(WebSocketBroker.CONNECTED, true);
199
});
200
201
socket.on('connect_failed', function onconnect_failed() {
202
that.setState(WebSocketBroker.OFFLINE, true);
203
});
204
205
socket.on('route', function onroute(route) {
206
that.route = route;
207
that.setFlag(WebSocketBroker.ROUTED);
208
});
209
210
socket.on('disconnect', function ondisconnect() {
211
that.setState(WebSocketBroker.OFFLINE, true);
212
});
213
214
socket.on('error', function onerror(error) {
215
fail(that, 'onerror', error);
216
});
217
218
socket.on('receive', function onreceive(message) {
219
var from = message['from'];
220
var data = message['data'];
221
callback(that, 'onreceive', [from, data]);
222
});
223
224
this.socket = socket;
225
};
226
WebSocketBroker.prototype.disconnect = function disconnect() {
227
if(this.checkState(WebSocketBroker.CONNECTED)) {
228
this.socket.disconnect();
229
this.setState(WebSocketBroker.OFFLINE, true);
230
return true;
231
} else {
232
return false;
233
}
234
};
235
WebSocketBroker.prototype.listen = function listen(options) {
236
var that = this;
237
if(this.checkState(WebSocketBroker.CONNECTED)) {
238
this.socket.emit('listen', options, function onresponse(response) {
239
if(response && response['error']) {
240
var error = new Error(response['error']);
241
fail(that, 'onerror', error);
242
} else {
243
that.setFlag(WebSocketBroker.LISTENING);
244
}
245
});
246
}
247
};
248
WebSocketBroker.prototype.ignore = function ignore() {
249
var that = this;
250
if(this.checkState(WebSocketBroker.CONNECTED)) {
251
this.socket.emit('ignore', null, function onresponse(response) {
252
if(response && response['error']) {
253
var error = new Error(response['error']);
254
fail(that, 'onerror', error)
255
} else {
256
that.clearFlag(WebSocketBroker.LISTENING);
257
}
258
});
259
}
260
};
261
WebSocketBroker.prototype.send = function send(to, message) {
262
var that = this;
263
if(this.checkState(WebSocketBroker.CONNECTED)) {
264
this.socket.emit('send', {'to': to, 'data': message}, function onresponse(response) {
265
if(response && response['error']) {
266
var error = new Error(response['error']);
267
fail(that, 'onerror', error)
268
}
269
});
270
};
271
};
272
273
var dataChannels = {
274
'reliable': 'RELIABLE',
275
'unreliable': 'UNRELIABLE',
276
'@control': 'RELIABLE'
277
};
278
var nextDataConnectionPort = 1;
279
function CommonRTCConnectProtocol() {
280
// FIXME: these timeouts should be configurable
281
this.connectionTimeout = 10 * ONE_SECOND;
282
this.pingTimeout = 1 * ONE_SECOND;
283
};
284
CommonRTCConnectProtocol.prototype.process = function process(message) {
285
var that = this;
286
287
var type = message['type'];
288
switch (type) {
289
case 'ice':
290
var candidate = JSON.parse(message['candidate']);
291
if(candidate)
292
this.handleIce(candidate);
293
break;
294
295
case 'offer':
296
that.ports.remote = message['port'];
297
var offer = {
298
'type': 'offer',
299
'sdp': message['description']
300
};
301
this.handleOffer(offer);
302
break;
303
304
case 'answer':
305
that.ports.remote = message['port'];
306
var answer = {
307
'type': 'answer',
308
'sdp': message['description']
309
};
310
this.handleAnswer(answer);
311
break;
312
313
case 'abort':
314
this.handleAbort();
315
break;
316
317
default:
318
fail(this, 'onerror', 'unknown message');
319
}
320
};
321
CommonRTCConnectProtocol.prototype.handleAbort = function handleAbort() {
322
fail(this, 'onerror', new Error(E.RTCConnectProtocolAbort));
323
};
324
CommonRTCConnectProtocol.prototype.initialize = function initialize(cb) {
325
var that = this;
326
327
if(this.peerConnection)
328
return cb();
329
330
// FIXME: peer connection servers should be configurable
331
this.peerConnection = new RTCPeerConnection(this.connectionServers, this.connectionOptions);
332
this.peerConnection.onicecandidate = function(event) {
333
var message = {
334
'type': 'ice',
335
'candidate': JSON.stringify(event.candidate)
336
};
337
callback(that, 'onmessage', message);
338
};
339
this.peerConnection.onaddstream = function(event) {
340
that.streams['remote'] = event.stream;
341
};
342
this.peerConnection.onstatechange = function(event) {
343
console.log(event.target.readyState);
344
};
345
346
function createStream(useFake) {
347
useFake = (!useVideo && !useAudio) ? true : useFake;
348
var useVideo = !!that.options['video'];
349
var useAudio = !!that.options['audio'];
350
var mediaOptions = {
351
video: useVideo,
352
audio: (!useVideo && !useAudio) ? true : useAudio,
353
fake: useFake
354
};
355
getUserMedia(mediaOptions,
356
function(stream) {
357
that.peerConnection.addStream(stream);
358
that.streams['local'] = stream;
359
cb();
360
},
361
function(error) {
362
console.error('!', error);
363
if(!useFake)
364
createStream(true);
365
else
366
fail(that, 'onerror', error);
367
}
368
);
369
}
370
371
createStream();
372
};
373
CommonRTCConnectProtocol.prototype.handleIce = function handleIce(candidate) {
374
var that = this;
375
376
function setIce() {
377
if(!that.peerConnection.remoteDescription) {
378
return
379
}
380
that.peerConnection.addIceCandidate(new RTCIceCandidate(candidate),
381
function() {},
382
function(error) {
383
fail(that, 'onerror', error);
384
}
385
);
386
};
387
388
this.initialize(setIce);
389
};
390
CommonRTCConnectProtocol.prototype.initiate = function initiate() {
391
var that = this;
392
this.initiator = true;
393
394
function createDataChannels() {
395
var labels = Object.keys(dataChannels);
396
labels.forEach(function(label) {
397
var channelOptions = that.channelOptions[dataChannels[label]];
398
var channel = that._pending[label] = that.peerConnection.createDataChannel(label, channelOptions);
399
channel.binaryType = that.options['binaryType'];
400
channel.onopen = function() {
401
that.channels[label] = channel;
402
delete that._pending[label];
403
if(Object.keys(that.channels).length === labels.length) {
404
that.complete = true;
405
callback(that, 'oncomplete', []);
406
}
407
};
408
channel.onerror = function(error) {
409
console.error(error);
410
fail(that, 'onerror', error);
411
};
412
});
413
createOffer();
414
};
415
416
function createOffer() {
417
that.peerConnection.createOffer(setLocal,
418
function(error) {
419
fail(that, 'onerror', error);
420
}
421
);
422
};
423
424
function setLocal(description) {
425
that.peerConnection.setLocalDescription(new RTCSessionDescription(description), complete,
426
function(error) {
427
fail(that, 'onerror', error);
428
}
429
);
430
431
function complete() {
432
var message = {
433
'type': 'offer',
434
'description': description['sdp'],
435
'port': that.ports.local
436
};
437
callback(that, 'onmessage', message);
438
};
439
};
440
441
this.initialize(createDataChannels);
442
};
443
CommonRTCConnectProtocol.prototype.handleOffer = function handleOffer(offer) {
444
var that = this;
445
446
function handleDataChannels() {
447
var labels = Object.keys(dataChannels);
448
that.peerConnection.ondatachannel = function(event) {
449
var channel = event.channel;
450
var label = channel.label;
451
that._pending[label] = channel;
452
channel.binaryType = that.options['binaryType'];
453
channel.onopen = function() {
454
that.channels[label] = channel;
455
delete that._pending[label];
456
if(Object.keys(that.channels).length === labels.length) {
457
that.complete = true;
458
callback(that, 'oncomplete', []);
459
}
460
};
461
channel.onerror = function(error) {
462
console.error(error);
463
fail(that, 'onerror', error);
464
};
465
};
466
setRemote();
467
};
468
469
function setRemote() {
470
that.peerConnection.setRemoteDescription(new RTCSessionDescription(offer), createAnswer,
471
function(error) {
472
fail(that, 'onerror', error);
473
}
474
);
475
};
476
477
function createAnswer() {
478
that.peerConnection.createAnswer(setLocal,
479
function(error) {
480
fail(that, 'onerror', error);
481
}
482
);
483
};
484
485
function setLocal(description) {
486
that.peerConnection.setLocalDescription(new RTCSessionDescription(description), complete,
487
function(error) {
488
fail(that, 'onerror', error);
489
}
490
);
491
492
function complete() {
493
var message = {
494
'type': 'answer',
495
'description': description['sdp'],
496
'port': that.ports.local
497
};
498
callback(that, 'onmessage', message);
499
};
500
};
501
502
this.initialize(handleDataChannels);
503
};
504
CommonRTCConnectProtocol.prototype.handleAnswer = function handleAnswer(answer) {
505
var that = this;
506
507
function setRemote() {
508
that.peerConnection.setRemoteDescription(new RTCSessionDescription(answer), complete,
509
function(error) {
510
fail(that, 'onerror', error);
511
}
512
);
513
};
514
515
function complete() {
516
};
517
518
this.initialize(setRemote);
519
};
520
521
function mozRTCConnectProtocol(options) {
522
this.options = options;
523
this.onmessage = null;
524
this.oncomplete = null;
525
this.onerror = null;
526
527
this.complete = false;
528
this.ports = {
529
local: nextDataConnectionPort ++,
530
remote: null
531
};
532
this.streams = {
533
local: null,
534
remote: null
535
};
536
this.initiator = false;
537
538
this.peerConnection = null;
539
this.channels = {};
540
this._pending = {};
541
this.connectionServers = null;
542
this.connectionOptions = null;
543
this.channelOptions = {
544
RELIABLE: {
545
// defaults
546
},
547
UNRELIABLE: {
548
outOfOrderAllowed: true,
549
maxRetransmitNum: 0
550
}
551
};
552
};
553
mozRTCConnectProtocol.prototype = new CommonRTCConnectProtocol();
554
mozRTCConnectProtocol.prototype.constructor = mozRTCConnectProtocol;
555
556
function webkitRTCConnectProtocol(options) {
557
this.options = options;
558
this.onmessage = null;
559
this.oncomplete = null;
560
this.onerror = null;
561
562
this.complete = false;
563
this.ports = {
564
local: nextDataConnectionPort ++,
565
remote: null
566
};
567
this.streams = {
568
local: null,
569
remote: null
570
};
571
this.initiator = false;
572
573
this.peerConnection = null;
574
this.channels = {};
575
this._pending = {};
576
this.connectionServers = {iceServers:[{url:'stun:23.21.150.121'}]};
577
this.connectionOptions = {
578
'optional': [{ 'RtpDataChannels': true }]
579
};
580
this.channelOptions = {
581
RELIABLE: {
582
// FIXME: reliable channels do not work in chrome yet
583
reliable: false
584
},
585
UNRELIABLE: {
586
reliable: false
587
}
588
};
589
};
590
webkitRTCConnectProtocol.prototype = new CommonRTCConnectProtocol();
591
webkitRTCConnectProtocol.prototype.constructor = webkitRTCConnectProtocol;
592
593
// FIXME: this could use a cleanup
594
var nextConnectionId = 1;
595
function Connection(options, peerConnection, streams, channels) {
596
var that = this;
597
this.id = nextConnectionId ++;
598
this.streams = streams;
599
this.connected = false;
600
this.messageFlag = false;
601
602
this.onmessage = null;
603
this.ondisconnect = null;
604
this.onerror = null;
605
606
this.peerConnection = peerConnection;
607
608
// DataChannels
609
this.channels = channels;
610
611
this.connectionTimer = null;
612
this.pingTimer = null;
613
614
function handleConnectionTimerExpired() {
615
if(!that.connected)
616
return
617
this.connectionTimer = null;
618
if(false === that.messageFlag) {
619
that.channels['@control'].send('ping');
620
this.pingTimer = window.setTimeout(handlePingTimerExpired, options['pingTimeout']);
621
} else {
622
that.messageFlag = false;
623
this.connectionTimer = window.setTimeout(handleConnectionTimerExpired, options['connectionTimeout']);
624
}
625
};
626
function handlePingTimerExpired() {
627
if(!that.connected)
628
return
629
this.pingTimer = null;
630
if(false === that.messageFlag) {
631
that.connected = false;
632
that.close();
633
} else {
634
that.messageFlag = false;
635
this.connectionTimer = window.setTimeout(handleConnectionTimerExpired, options['connectionTimeout']);
636
}
637
};
638
639
Object.keys(this.channels).forEach(function(label) {
640
var channel = that.channels[label];
641
if(label.match('^@')) // check for internal channels
642
return;
643
644
channel.onmessage = function onmessage(message) {
645
that.messageFlag = true;
646
callback(that, 'onmessage', [label, message]);
647
};
648
});
649
this.channels['@control'].onmessage = function onmessage(message) {
650
that.messageFlag = true;
651
if(that.connected) {
652
var data = message.data;
653
if('ping' === data) {
654
that.channels['@control'].send('pong');
655
} else if('pong' === data) {
656
// ok
657
} else if('quit' === data) {
658
that.close();
659
}
660
}
661
};
662
663
this.connected = true;
664
this.connectionTimer = window.setTimeout(handleConnectionTimerExpired, options['connectionTimeout']);
665
};
666
Connection.prototype.close = function close() {
667
console.log('close connection');
668
if(this.connected) {
669
this.channels['@control'].send('quit');
670
}
671
this.connected = false;
672
this.peerConnection.close();
673
if(this.connectionTimer) {
674
window.clearInterval(this.connectionTimer);
675
this.connectionTimer = null;
676
}
677
if(this.pingTimer) {
678
window.clearInterval(this.pingTimer);
679
this.pingTimer = null;
680
}
681
this.peerConnection = null;
682
callback(this, 'ondisconnect', []);
683
};
684
Connection.prototype.send = function send(label, message) {
685
this.channels[label].send(message);
686
};
687
688
function PendingConnection(route, incoming) {
689
this.route = route;
690
this.incoming = incoming;
691
this.proceed = true;
692
};
693
PendingConnection.prototype.accept = function accept() {
694
this.proceed = true;
695
};
696
PendingConnection.prototype.reject = function reject() {
697
this.proceed = false;
698
};
699
700
function Peer(brokerUrl, options) {
701
if(!webrtcSupported)
702
throw new Error("WebRTC not supported");
703
704
var that = this;
705
this.brokerUrl = brokerUrl;
706
this.options = options = options || {};
707
options['binaryType'] = options['binaryType'] || 'arraybuffer';
708
options['connectionTimeout'] = options['connectionTimeout'] || 10 * ONE_SECOND;
709
options['pingTimeout'] = options['pingTimeout'] || 1 * ONE_SECOND;
710
711
this.onconnection = null;
712
this.onpending = null;
713
this.onroute = null;
714
this.onerror = null;
715
716
this.broker = new WebSocketBroker(brokerUrl);
717
this.pending = {};
718
719
this.queues = {
720
connected: [],
721
listening: []
722
};
723
724
this.broker.onstatechange = function onstatechange(state, mask) {
725
if(that.queues.connected.length && that.broker.checkState(WebSocketBroker.ROUTED)) {
726
processDeferredQueue(that.queues.connected);
727
if(that.queues.listening.length && that.broker.checkState(WebSocketBroker.LISTENING)) {
728
processDeferredQueue(that.queues.listening);
729
}
730
}
731
if(mask & WebSocketBroker.ROUTED) {
732
callback(that, 'onroute', that.broker.route);
733
}
734
};
735
736
this.broker.onreceive = function onreceive(from, message) {
737
var handshake;
738
if(!that.pending.hasOwnProperty(from)) {
739
if(!that.broker.checkState(WebSocketBroker.LISTENING)) {
740
return;
741
}
742
743
var pendingConnection = new PendingConnection(from, /*incoming*/ true);
744
callback(that, 'onpending', [pendingConnection]);
745
if(!pendingConnection['proceed'])
746
return;
747
748
var handshake = that.pending[from] = new RTCConnectProtocol(that.options);
749
handshake.oncomplete = function() {
750
var connection = new Connection(that.options, handshake.peerConnection, handshake.streams, handshake.channels);
751
connection['route'] = from;
752
delete that.pending[from];
753
callback(that, 'onconnection', [connection]);
754
};
755
handshake.onmessage = function(message) {
756
that.broker.send(from, message);
757
};
758
handshake.onerror = function(error) {
759
delete that.pending[from];
760
callback(that, 'onerror', [error]);
761
};
762
} else {
763
handshake = that.pending[from];
764
}
765
handshake.process(message);
766
};
767
768
this.broker.connect();
769
};
770
Peer.prototype.listen = function listen(options) {
771
if(!this.broker.checkState(WebSocketBroker.ROUTED))
772
return defer(this.queues.connected, this, 'listen', [options]);
773
774
options = options || {};
775
options['url'] = options['url'] || window.location.toString();
776
options['listed'] = (undefined !== options['listed']) ? options['listed'] : true;
777
options['metadata'] = options['metadata'] || {};
778
779
this.broker.listen(options);
780
};
781
Peer.prototype.ignore = function ignore() {
782
throw new Error('not implemented');
783
};
784
Peer.prototype.connect = function connect(route) {
785
if(!this.broker.checkState(WebSocketBroker.ROUTED))
786
return defer(this.queues.connected, this, 'connect', [route]);
787
788
var that = this;
789
790
if(this.pending.hasOwnProperty(route))
791
throw new Error('already connecting to this host'); // FIXME: we can handle this better
792
793
var pendingConnection = new PendingConnection(route, /*incoming*/ false);
794
callback(that, 'onpending', [pendingConnection]);
795
if(!pendingConnection['proceed'])
796
return;
797
798
var handshake = this.pending[route] = new RTCConnectProtocol(this.options);
799
handshake.oncomplete = function() {
800
var connection = new Connection(this.options, handshake.peerConnection, handshake.streams, handshake.channels);
801
connection['route'] = route;
802
delete that.pending[route];
803
callback(that, 'onconnection', [connection]);
804
};
805
handshake.onmessage = function(message) {
806
that.broker.send(route, message);
807
};
808
handshake.onerror = function(error) {
809
delete that.pending[route];
810
fail(that, 'onerror', error);
811
};
812
813
handshake.initiate();
814
};
815
Peer.prototype.close = function close() {
816
this.broker.disconnect();
817
};
818
Peer.E = E;
819
820
return Peer;
821
822
})();
823