From cf7ac0d1cc12da176bd9b15800327bbae537487a Mon Sep 17 00:00:00 2001 From: Joseph Frazier Date: Thu, 16 Oct 2014 15:46:36 -0400 Subject: [PATCH 01/14] test/spec/SpecUA.js: add failing test for #93 (cherry picked from commit 5f589b8dd370dcda0a107bef1096203c093d4f11) --- test/spec/SpecUA.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/spec/SpecUA.js b/test/spec/SpecUA.js index eda2f00fc..4568f2aae 100644 --- a/test/spec/SpecUA.js +++ b/test/spec/SpecUA.js @@ -81,6 +81,10 @@ describe('UA', function() { }]); }); + it('can be created with empty stunServers list', function () { + expect(new SIP.UA({stunServers: []}).configuration.stunServers).toEqual([]); + }); + it('sets the instance variables', function() { UA = undefined; From 66d12ceb00d4bce6bc32530708fa9c32a64f6646 Mon Sep 17 00:00:00 2001 From: Joseph Frazier Date: Thu, 16 Oct 2014 15:34:34 -0400 Subject: [PATCH 02/14] UA: allow configuration options 'stunServers' and 'turnServers' to be [] resolves #93 (cherry picked from commit 18ce4d80678b4dce6afd4b0c0b61f09abf823b41) --- src/UA.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/UA.js b/src/UA.js index 1ef1040b4..d6481d4d0 100644 --- a/src/UA.js +++ b/src/UA.js @@ -944,14 +944,19 @@ UA.prototype.loadConfig = function(configuration) { SIP.Utils.optionsOverride(configuration, 'rel100', 'reliable', true, this.logger, SIP.C.supported.UNSUPPORTED); + var emptyArraysAllowed = ['stunServers', 'turnServers']; + // Check Optional parameters for(parameter in UA.configuration_check.optional) { aliasUnderscored(parameter, this.logger); if(configuration.hasOwnProperty(parameter)) { value = configuration[parameter]; - // If the parameter value is null, empty string,undefined, or empty array then apply its default value. - if(value === null || value === "" || value === undefined || (value instanceof Array && value.length === 0)) { continue; } + // If the parameter value is an empty array, but shouldn't be, apply its default value. + if (value instanceof Array && value.length === 0 && emptyArraysAllowed.indexOf(parameter) < 0) { continue; } + + // If the parameter value is null, empty string, or undefined then apply its default value. + if(value === null || value === "" || value === undefined) { continue; } // If it's a number with NaN value then also apply its default value. // NOTE: JS does not allow "value === NaN", the following does the work: else if(typeof(value) === 'number' && isNaN(value)) { continue; } From 4b6e532b0dfaa4e1160a5a0b1365e3e17ca5d504 Mon Sep 17 00:00:00 2001 From: Eric Green Date: Fri, 24 Oct 2014 15:02:35 -0400 Subject: [PATCH 03/14] Add hackWssInTransport to UA configuration to fix Asterisk problems. Fixes #97 (cherry picked from commit 32bffbea37d91d185851c46e930be8f663e52d13) --- src/UA.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/UA.js b/src/UA.js index d6481d4d0..6f1d993b4 100644 --- a/src/UA.js +++ b/src/UA.js @@ -895,6 +895,7 @@ UA.prototype.loadConfig = function(configuration) { // Hacks hackViaTcp: false, hackIpInContact: false, + hackWssInTransport: false, //autostarting autostart: true, @@ -1022,7 +1023,7 @@ UA.prototype.loadConfig = function(configuration) { this.contact = { pub_gruu: null, temp_gruu: null, - uri: new SIP.URI('sip', SIP.Utils.createRandomToken(8), settings.viaHost, null, {transport: 'ws'}), + uri: new SIP.URI('sip', SIP.Utils.createRandomToken(8), settings.viaHost, null, {transport: ((settings.hackWssInTransport)?'wss':'ws')}), toString: function(options){ options = options || {}; @@ -1102,6 +1103,7 @@ UA.configuration_skeleton = (function() { "displayName", "hackViaTcp", // false. "hackIpInContact", //false + "hackWssInTransport", //false "instanceId", "noAnswerTimeout", // 30 seconds. "password", @@ -1276,6 +1278,12 @@ UA.configuration_check = { } }, + hackWssInTransport: function(hackWssInTransport) { + if (typeof hackWssInTransport === 'boolean') { + return hackWssInTransport; + } + }, + instanceId: function(instanceId) { if(typeof instanceId !== 'string') { return; From a998d9c29e94333609b116e4f07126dd96c2f545 Mon Sep 17 00:00:00 2001 From: Eric Green Date: Fri, 24 Oct 2014 15:06:22 -0400 Subject: [PATCH 04/14] allow hackWssInTransport for anonymous registrations (cherry picked from commit 145a55c29b883d7fe5d02aa45806ca88baa43135) --- src/UA.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/UA.js b/src/UA.js index 6f1d993b4..fec44d03a 100644 --- a/src/UA.js +++ b/src/UA.js @@ -1033,7 +1033,7 @@ UA.prototype.loadConfig = function(configuration) { contact = '<'; if (anonymous) { - contact += (this.temp_gruu || 'sip:anonymous@anonymous.invalid;transport=ws').toString(); + contact += (this.temp_gruu || ('sip:anonymous@anonymous.invalid;transport='+(settings.hackWssInTransport)?'wss':'ws'))).toString(); } else { contact += (this.pub_gruu || this.uri).toString(); } From d9bcf1f8676de113f2eb224fdba578327a9d324c Mon Sep 17 00:00:00 2001 From: Eric Green Date: Fri, 24 Oct 2014 15:12:01 -0400 Subject: [PATCH 05/14] Fix typo: too many ) (cherry picked from commit 6552a7e089f0ec54612fd60e567176eb79266601) --- src/UA.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/UA.js b/src/UA.js index fec44d03a..8834e3e41 100644 --- a/src/UA.js +++ b/src/UA.js @@ -1033,7 +1033,7 @@ UA.prototype.loadConfig = function(configuration) { contact = '<'; if (anonymous) { - contact += (this.temp_gruu || ('sip:anonymous@anonymous.invalid;transport='+(settings.hackWssInTransport)?'wss':'ws'))).toString(); + contact += (this.temp_gruu || ('sip:anonymous@anonymous.invalid;transport='+(settings.hackWssInTransport)?'wss':'ws')).toString(); } else { contact += (this.pub_gruu || this.uri).toString(); } From 0d205235903d2d92ea08b37b9fcf169ba0e08c2d Mon Sep 17 00:00:00 2001 From: Joseph Frazier Date: Tue, 28 Oct 2014 22:38:18 -0400 Subject: [PATCH 06/14] WebRTC.MediaHandler: split stunServers into separate objects This avoids the following sort of situation, which Firefox (as of 33.0.2) has trouble with: var RTCPeerConnection = window.webkitRTCPeerConnection || window.mozRTCPeerConnection; new RTCPeerConnection({ 'iceServers': [{ "url": ["stun:stun.l.google.com", "stun:stun2.l.google.com"] }] }) addresses #117 (cherry picked from commit 28523b6d81e8785330e578b9456fb9ec565e1b4e) --- src/WebRTC/MediaHandler.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/WebRTC/MediaHandler.js b/src/WebRTC/MediaHandler.js index 633541f89..bfc4a16b6 100644 --- a/src/WebRTC/MediaHandler.js +++ b/src/WebRTC/MediaHandler.js @@ -55,7 +55,9 @@ var MediaHandler = function(session, options) { /* Change 'url' to 'urls' whenever this issue is solved: * https://code.google.com/p/webrtc/issues/detail?id=2096 */ - servers.push({'url': stunServers}); + [].concat(stunServers).forEach(function (server) { + servers.push({'url': server}); + }); length = turnServers.length; for (idx = 0; idx < length; idx++) { From 75875191d27acfabdeb5fa8d9868f2d562833aff Mon Sep 17 00:00:00 2001 From: Eric Green Date: Fri, 31 Oct 2014 09:58:28 -0400 Subject: [PATCH 07/14] Remove hack for Firefox handling relay candidates (cherry picked from commit 098f8a4e25a74ee0a59ba9b94545b5987d6ef7ac) --- src/Hacks.js | 6 ------ src/Session.js | 4 ---- 2 files changed, 10 deletions(-) diff --git a/src/Hacks.js b/src/Hacks.js index 5a8d70fc3..44f025591 100644 --- a/src/Hacks.js +++ b/src/Hacks.js @@ -39,12 +39,6 @@ Hacks = { return window.mozRTCPeerConnection !== undefined; }, - cannotHandleRelayCandidates: function (message) { - if (this.isFirefox() && message.body) { - message.body = message.body.replace(/relay/g, 'host generation 0'); - } - }, - cannotHandleExtraWhitespace: function (message) { if (this.isFirefox() && message.body) { message.body = message.body.replace(/ \r\n/g, "\r\n"); diff --git a/src/Session.js b/src/Session.js index 3d99589e4..492d77219 100644 --- a/src/Session.js +++ b/src/Session.js @@ -972,7 +972,6 @@ InviteServerContext = function(ua, request) { } //TODO: move this into media handler - SIP.Hacks.Firefox.cannotHandleRelayCandidates(request); SIP.Hacks.Firefox.cannotHandleExtraWhitespace(request); SIP.Hacks.AllBrowsers.maskDtls(request); @@ -1448,7 +1447,6 @@ InviteServerContext.prototype = { if (!this.hasAnswer) { if(request.body && request.getHeader('content-type') === 'application/sdp') { // ACK contains answer to an INVITE w/o SDP negotiation - SIP.Hacks.Firefox.cannotHandleRelayCandidates(request); SIP.Hacks.Firefox.cannotHandleExtraWhitespace(request); SIP.Hacks.AllBrowsers.maskDtls(request); @@ -1818,7 +1816,6 @@ InviteClientContext.prototype = { return; } - SIP.Hacks.Firefox.cannotHandleRelayCandidates(response); SIP.Hacks.Firefox.cannotHandleExtraWhitespace(response); SIP.Hacks.AllBrowsers.maskDtls(response); @@ -1945,7 +1942,6 @@ InviteClientContext.prototype = { break; } - SIP.Hacks.Firefox.cannotHandleRelayCandidates(response); SIP.Hacks.Firefox.cannotHandleExtraWhitespace(response); SIP.Hacks.AllBrowsers.maskDtls(response); From ff1618d95fb8f9714ebc90895c3ca04e35e1ad8b Mon Sep 17 00:00:00 2001 From: Will Mitchell Date: Thu, 6 Nov 2014 14:39:55 -0500 Subject: [PATCH 08/14] Syntax sugar for rendering media. ua.invite and session.accept can both be passed an HTMLMediaElement (audio or video) in lieu of options. Remote media will render to that element, and constraints will be inferred from the type of element (audio only for audio, audio-video for video). Example: new SIP.UA().invite('welcome@onsip.com', document.getElementsByTagName('audio')[0]); (cherry picked from commit d238523754ad4f6e2845ffd487c2961596a7c9ad) --- src/Session.js | 18 ++++++++++++++++++ src/UA.js | 17 +++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/src/Session.js b/src/Session.js index 492d77219..8872f4a42 100644 --- a/src/Session.js +++ b/src/Session.js @@ -1176,6 +1176,7 @@ InviteServerContext.prototype = { */ progress: function (options) { options = options || {}; + var statusCode = options.statusCode || 180, reasonPhrase = options.reasonPhrase, @@ -1270,7 +1271,24 @@ InviteServerContext.prototype = { accept: function(options) { options = options || {}; + if (global.HTMLMediaElement && options instanceof global.HTMLMediaElement) { + options = { + media: { + constraints: { + audio: true, + video: options.tagName === 'VIDEO' + }, + render: { + remote: { + video: options + } + } + } + }; + } + SIP.Utils.optionsOverride(options, 'media', 'mediaConstraints', true, this.logger, this.ua.configuration.media); + this.mediaHint = options.media; // commented out now-unused hold-related variables for jshint. See below. JMF 2014-1-21 diff --git a/src/UA.js b/src/UA.js index 8834e3e41..1dea43773 100644 --- a/src/UA.js +++ b/src/UA.js @@ -249,6 +249,23 @@ UA.prototype.isConnected = function() { */ UA.prototype.invite = function(target, options) { options = options || {}; + + if (global.HTMLMediaElement && options instanceof global.HTMLMediaElement) { + options = { + media: { + constraints: { + audio: true, + video: options.tagName === 'VIDEO' + }, + render: { + remote: { + video: options + } + } + } + }; + } + SIP.Utils.optionsOverride(options, 'media', 'mediaConstraints', true, this.logger); var context = new SIP.InviteClientContext(this, target, options); From e8d89074a57ab453f2e095162fddada0145ccab9 Mon Sep 17 00:00:00 2001 From: Will Mitchell Date: Thu, 6 Nov 2014 14:54:58 -0500 Subject: [PATCH 09/14] Whitespace removal so James doesn't have a fit. (cherry picked from commit f9b3cbdf04d3a2b862d57dc20ac4999b12e12992) --- src/Session.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Session.js b/src/Session.js index 8872f4a42..5bb567d79 100644 --- a/src/Session.js +++ b/src/Session.js @@ -1176,7 +1176,6 @@ InviteServerContext.prototype = { */ progress: function (options) { options = options || {}; - var statusCode = options.statusCode || 180, reasonPhrase = options.reasonPhrase, @@ -1288,7 +1287,6 @@ InviteServerContext.prototype = { } SIP.Utils.optionsOverride(options, 'media', 'mediaConstraints', true, this.logger, this.ua.configuration.media); - this.mediaHint = options.media; // commented out now-unused hold-related variables for jshint. See below. JMF 2014-1-21 From 6be8a7a1a9e581abd5861a9c90a4215c47e17f0e Mon Sep 17 00:00:00 2001 From: Joseph Frazier Date: Thu, 20 Nov 2014 23:39:43 -0500 Subject: [PATCH 10/14] dedupe "Syntax sugar for rendering media" to Utils.desugarSessionOptions see d238523754ad4f6e2845ffd487c2961596a7c9ad (cherry picked from commit d16e173e502d483d33afb96f78b9217f28e78ce8) --- src/Session.js | 18 +----------------- src/UA.js | 18 +----------------- src/Utils.js | 19 +++++++++++++++++++ 3 files changed, 21 insertions(+), 34 deletions(-) diff --git a/src/Session.js b/src/Session.js index 5bb567d79..01886ff68 100644 --- a/src/Session.js +++ b/src/Session.js @@ -1269,23 +1269,7 @@ InviteServerContext.prototype = { */ accept: function(options) { options = options || {}; - - if (global.HTMLMediaElement && options instanceof global.HTMLMediaElement) { - options = { - media: { - constraints: { - audio: true, - video: options.tagName === 'VIDEO' - }, - render: { - remote: { - video: options - } - } - } - }; - } - + options = SIP.Utils.desugarSessionOptions(options); SIP.Utils.optionsOverride(options, 'media', 'mediaConstraints', true, this.logger, this.ua.configuration.media); this.mediaHint = options.media; diff --git a/src/UA.js b/src/UA.js index 1dea43773..af429bb1d 100644 --- a/src/UA.js +++ b/src/UA.js @@ -249,23 +249,7 @@ UA.prototype.isConnected = function() { */ UA.prototype.invite = function(target, options) { options = options || {}; - - if (global.HTMLMediaElement && options instanceof global.HTMLMediaElement) { - options = { - media: { - constraints: { - audio: true, - video: options.tagName === 'VIDEO' - }, - render: { - remote: { - video: options - } - } - } - }; - } - + options = SIP.Utils.desugarSessionOptions(options); SIP.Utils.optionsOverride(options, 'media', 'mediaConstraints', true, this.logger); var context = new SIP.InviteClientContext(this, target, options); diff --git a/src/Utils.js b/src/Utils.js index 19b678999..eb6cb5017 100644 --- a/src/Utils.js +++ b/src/Utils.js @@ -34,6 +34,25 @@ Utils= { options[winner] = options[winner] || options[loser] || defaultValue; }, + desugarSessionOptions: function desugarSessionOptions (options) { + if (global.HTMLMediaElement && options instanceof global.HTMLMediaElement) { + options = { + media: { + constraints: { + audio: true, + video: options.tagName === 'VIDEO' + }, + render: { + remote: { + video: options + } + } + } + }; + } + return options; + }, + str_utf8_length: function(string) { return encodeURIComponent(string).replace(/%[A-F\d]{2}/g, 'U').length; }, From 67fbc4c7d1d862272e78b6cf7240521c1a3dccca Mon Sep 17 00:00:00 2001 From: Joseph Frazier Date: Thu, 20 Nov 2014 23:44:43 -0500 Subject: [PATCH 11/14] WebRTC.MediaHandler: emit 'iceCandidate' from pc.onicecandidate see #23 (cherry picked from commit a7250d754f538e55c0407614acb300ac0cdc6003) --- src/WebRTC/MediaHandler.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/WebRTC/MediaHandler.js b/src/WebRTC/MediaHandler.js index bfc4a16b6..247289e4b 100644 --- a/src/WebRTC/MediaHandler.js +++ b/src/WebRTC/MediaHandler.js @@ -18,6 +18,7 @@ var MediaHandler = function(session, options) { 'userMedia', 'userMediaFailed', 'iceGathering', + 'iceCandidate', 'iceComplete', 'iceFailed', 'getDescription', @@ -82,6 +83,7 @@ var MediaHandler = function(session, options) { }; this.peerConnection.onicecandidate = function(e) { + self.emit('iceCandidate', e); if (e.candidate) { self.logger.log('ICE candidate received: '+ (e.candidate.candidate === null ? null : e.candidate.candidate.trim())); } else if (self.onIceCompleted !== undefined) { From 5449fdfc31e1fdd9ea830d13570d486cfe3c9cf9 Mon Sep 17 00:00:00 2001 From: Eric Green Date: Mon, 24 Nov 2014 15:49:20 -0500 Subject: [PATCH 12/14] Hack to fix Firefox SDP Interop With FF 34+ (cherry picked from commit fcd816152490c7d1e0a84a1c692edd3492ba3706) Conflicts: src/Hacks.js src/SIP.js src/WebRTC/MediaHandler.js --- src/Hacks.js | 25 +++++++++++++++++-------- src/SIP.js | 2 +- src/WebRTC/MediaHandler.js | 1 + 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/src/Hacks.js b/src/Hacks.js index 44f025591..abf271a92 100644 --- a/src/Hacks.js +++ b/src/Hacks.js @@ -6,12 +6,8 @@ * as to most easily track when particular hacks may not be necessary anymore. */ -module.exports = function (window) { - -var Hacks; - -Hacks = { - +module.exports = function (SIP) { +var Hacks = { AllBrowsers: { maskDtls: function (message) { if (message.body) { @@ -71,7 +67,7 @@ Hacks = { if (mlines[i].toString().search(/i=.*/) >= 0) { insertAt = sdp.indexOf(mlines[i].toString())+mlines[i].toString().length; if (sdp.substr(insertAt,2)!=='c=') { - sdp = sdp.substr(0,insertAt) + '\r\nc=IN IP 4 0.0.0.0' + sdp.substr(insertAt); + sdp = sdp.substr(0,insertAt) + '\r\nc=IN IP4 0.0.0.0' + sdp.substr(insertAt); } // else add the C line if it's missing @@ -82,6 +78,19 @@ Hacks = { } } return sdp; + }, + + hasIncompatibleCLineWithSomeSIPEndpoints: function(sdp) { + /* + * Firefox appears to be following https://tools.ietf.org/html/rfc5245#section-9.1.1.1 + * and using a c line IP address of 0.0.0.0. This is completely valid, however it is + * causing some endpoints (such as FreeSWITCH) to interpret the SDP as being on hold + * https://freeswitch.org/jira/browse/FS-6955. To get around this issue we pull the + * replace the c line with 1.1.1.1 which SIP clients do not interpret as hold. + * This makes the other endpoint believe that the call is not on hold and audio flows + * because ICE determines the media pathway (not the c line). + */ + return sdp.replace(/(0\.0\.0\.0)/gmi, SIP.Utils.getRandomTestNetIP()); } }, @@ -109,6 +118,6 @@ Hacks = { } }; - return Hacks; }; + diff --git a/src/SIP.js b/src/SIP.js index f2da6f118..a87ae935b 100644 --- a/src/SIP.js +++ b/src/SIP.js @@ -45,7 +45,7 @@ module.exports = (function(window) { var WebRTCMediaStreamManager = require('./WebRTC/MediaStreamManager.js')(SIP); SIP.WebRTC = require('./WebRTC.js')(SIP.Utils, window, WebRTCMediaHandler, WebRTCMediaStreamManager); require('./UA.js')(SIP, window); - SIP.Hacks = require('./Hacks.js')(window); + SIP.Hacks = require('./Hacks.js')(SIP); require('./SanityCheck.js')(SIP); SIP.DigestAuthentication = require('./DigestAuthentication.js')(SIP.Utils); SIP.Grammar = require('./Grammar/dist/Grammar')(SIP); diff --git a/src/WebRTC/MediaHandler.js b/src/WebRTC/MediaHandler.js index 247289e4b..f13bceacf 100644 --- a/src/WebRTC/MediaHandler.js +++ b/src/WebRTC/MediaHandler.js @@ -418,6 +418,7 @@ MediaHandler.prototype = Object.create(SIP.MediaHandler.prototype, { sdp = SIP.Hacks.Chrome.needsExplicitlyInactiveSDP(sdp); sdp = SIP.Hacks.AllBrowsers.unmaskDtls(sdp); + sdp = SIP.Hacks.Firefox.hasIncompatibleCLineWithSomeSIPEndpoints(sdp); var sdpWrapper = { type: methodName === 'createOffer' ? 'offer' : 'answer', From 8d1e4baed147a4cfb002e0d8920053307df46522 Mon Sep 17 00:00:00 2001 From: Eric Green Date: Mon, 24 Nov 2014 16:55:27 -0500 Subject: [PATCH 13/14] Bump version to 0.6.4 --- bower.json | 2 +- package.json | 2 +- plugin.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/bower.json b/bower.json index d5f574497..ca276ad99 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name": "sip.js", - "version": "0.6.3", + "version": "0.6.4", "authors": [ "Will Mitchell ", "James Criscuolo ", diff --git a/package.json b/package.json index cd1c428a8..57748819b 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "sip.js", "title": "SIP.js", "description": "A simple, intuitive, and powerful JavaScript signaling library", - "version": "0.6.3", + "version": "0.6.4", "main": "src/SIP.js", "homepage": "http://sipjs.com", "author": "Will Mitchell ", diff --git a/plugin.xml b/plugin.xml index 72df67d27..9d7162ee8 100644 --- a/plugin.xml +++ b/plugin.xml @@ -1,5 +1,5 @@ - + sip.js