File: source/ice-connection.js

  1. /**
  2. * <blockquote class="info">
  3. * Learn more about how ICE works in this
  4. * <a href="https://temasys.com.sg/ice-what-is-this-sorcery/">article here</a>.
  5. * </blockquote>
  6. * The list of Peer connection ICE connection states.
  7. * @attribute ICE_CONNECTION_STATE
  8. * @param {String} CHECKING <small>Value <code>"checking"</code></small>
  9. * The value of the state when Peer connection is checking for a suitable matching pair of
  10. * ICE candidates to establish ICE connection.
  11. * <small>Exchanging of ICE candidates happens during <a href="#event_candidateGenerationState">
  12. * <code>candidateGenerationState</code> event</a>.</small>
  13. * @param {String} CONNECTED <small>Value <code>"connected"</code></small>
  14. * The value of the state when Peer connection has found a suitable matching pair of
  15. * ICE candidates to establish ICE connection but is still checking for a better
  16. * suitable matching pair of ICE candidates for the best ICE connectivity.
  17. * <small>At this state, ICE connection is already established and audio, video and
  18. * data streaming has already started.</small>
  19. * @param {String} COMPLETED <small>Value <code>"completed"</code></small>
  20. * The value of the state when Peer connection has found the best suitable matching pair
  21. * of ICE candidates to establish ICE connection and checking has stopped.
  22. * <small>At this state, ICE connection is already established and audio, video and
  23. * data streaming has already started. This may happpen after <code>CONNECTED</code>.</small>
  24. * @param {String} FAILED <small>Value <code>"failed"</code></small>
  25. * The value of the state when Peer connection ICE connection has failed.
  26. * @param {String} DISCONNECTED <small>Value <code>"disconnected"</code></small>
  27. * The value of the state when Peer connection ICE connection is disconnected.
  28. * <small>At this state, the Peer connection may attempt to revive the ICE connection.
  29. * This may happen due to flaky network conditions.</small>
  30. * @param {String} CLOSED <small>Value <code>"closed"</code></small>
  31. * The value of the state when Peer connection ICE connection has closed.
  32. * <small>This happens when Peer connection is closed and no streaming can occur at this stage.</small>
  33. * @param {String} TRICKLE_FAILED <small>Value <code>"trickeFailed"</code></small>
  34. * The value of the state when Peer connection ICE connection has failed during trickle ICE.
  35. * <small>Trickle ICE is enabled in <a href="#method_init"><code>init()</code> method</a>
  36. * <code>enableIceTrickle</code> option.</small>
  37. * @type JSON
  38. * @readOnly
  39. * @for Skylink
  40. * @since 0.1.0
  41. */
  42. Skylink.prototype.ICE_CONNECTION_STATE = {
  43. STARTING: 'starting',
  44. CHECKING: 'checking',
  45. CONNECTED: 'connected',
  46. COMPLETED: 'completed',
  47. CLOSED: 'closed',
  48. FAILED: 'failed',
  49. TRICKLE_FAILED: 'trickleFailed',
  50. DISCONNECTED: 'disconnected'
  51. };
  52.  
  53. /**
  54. * <blockquote class="info">
  55. * Note that configuring the protocol may not necessarily result in the desired network transports protocol
  56. * used in the actual TURN network traffic as it depends which protocol the browser selects and connects with.
  57. * This simply configures the TURN ICE server urls <code?transport=(protocol)</code> query option when constructing
  58. * the Peer connection. When all protocols are selected, the ICE servers urls are duplicated with all protocols.
  59. * </blockquote>
  60. * The list of TURN network transport protocols options when constructing Peer connections
  61. * configured in the <a href="#method_init"><code>init()</code> method</a>.
  62. * <small>Example <code>.urls</code> inital input: [<code>"turn:server.com?transport=tcp"</code>,
  63. * <code>"turn:server1.com:3478"</code>, <code>"turn:server.com?transport=udp"</code>]</small>
  64. * @attribute TURN_TRANSPORT
  65. * @param {String} TCP <small>Value <code>"tcp"</code></small>
  66. * The value of the option to configure using only TCP network transport protocol.
  67. * <small>Example <code>.urls</code> output: [<code>"turn:server.com?transport=tcp"</code>,
  68. * <code>"turn:server1.com:3478?transport=tcp"</code>]</small>
  69. * @param {String} UDP <small>Value <code>"udp"</code></small>
  70. * The value of the option to configure using only UDP network transport protocol.
  71. * <small>Example <code>.urls</code> output: [<code>"turn:server.com?transport=udp"</code>,
  72. * <code>"turn:server1.com:3478?transport=udp"</code>]</small>
  73. * @param {String} ANY <small>Value <code>"any"</code></small>
  74. * The value of the option to configure using any network transport protocols configured from the Signaling server.
  75. * <small>Example <code>.urls</code> output: [<code>"turn:server.com?transport=tcp"</code>,
  76. * <code>"turn:server1.com:3478"</code>, <code>"turn:server.com?transport=udp"</code>]</small>
  77. * @param {String} NONE <small>Value <code>"none"</code></small>
  78. * The value of the option to not configure using any network transport protocols.
  79. * <small>Example <code>.urls</code> output: [<code>"turn:server.com"</code>, <code>"turn:server1.com:3478"</code>]</small>
  80. * <small>Configuring this does not mean that no protocols will be used, but
  81. * rather removing <code>?transport=(protocol)</code> query option in
  82. * the TURN ICE server <code>.urls</code> when constructing the Peer connection.</small>
  83. * @param {String} ALL <small>Value <code>"all"</code></small>
  84. * The value of the option to configure using both TCP and UDP network transport protocols.
  85. * <small>Example <code>.urls</code> output: [<code>"turn:server.com?transport=tcp"</code>,
  86. * <code>"turn:server.com?transport=udp"</code>, <code>"turn:server1.com:3478?transport=tcp"</code>,
  87. * <code>"turn:server1.com:3478?transport=udp"</code>]</small>
  88. * @type JSON
  89. * @readOnly
  90. * @for Skylink
  91. * @since 0.5.4
  92. */
  93. Skylink.prototype.TURN_TRANSPORT = {
  94. UDP: 'udp',
  95. TCP: 'tcp',
  96. ANY: 'any',
  97. NONE: 'none',
  98. ALL: 'all'
  99. };
  100.  
  101. /**
  102. * Stores the flag that indicates if Peer connections should trickle ICE.
  103. * @attribute _enableIceTrickle
  104. * @type Boolean
  105. * @default true
  106. * @private
  107. * @for Skylink
  108. * @since 0.3.0
  109. */
  110. Skylink.prototype._enableIceTrickle = true;
  111.  
  112. /**
  113. * Stores the flag that indicates if STUN ICE servers should be used when constructing Peer connection.
  114. * @attribute _enableSTUN
  115. * @type Boolean
  116. * @default true
  117. * @private
  118. * @for Skylink
  119. * @since 0.5.4
  120. */
  121. Skylink.prototype._enableSTUN = true;
  122.  
  123. /**
  124. * Stores the flag that indicates if TURN ICE servers should be used when constructing Peer connection.
  125. * @attribute _enableTURN
  126. * @type Boolean
  127. * @default true
  128. * @private
  129. * @for Skylink
  130. * @since 0.5.4
  131. */
  132. Skylink.prototype._enableTURN = true;
  133.  
  134. /**
  135. * Stores the flag that indicates if public STUN ICE servers should be used when constructing Peer connection.
  136. * @attribute _usePublicSTUN
  137. * @type Boolean
  138. * @default true
  139. * @private
  140. * @for Skylink
  141. * @since 0.6.1
  142. */
  143. Skylink.prototype._usePublicSTUN = true;
  144.  
  145. /**
  146. * Stores the option for the TURN protocols to use.
  147. * This should configure the TURN ICE servers urls <code>?transport=protocol</code> flag.
  148. * @attribute _TURNTransport
  149. * @type String
  150. * @default "any"
  151. * @private
  152. * @required
  153. * @for Skylink
  154. * @since 0.5.4
  155. */
  156. Skylink.prototype._TURNTransport = 'any';
  157.  
  158. /**
  159. * Stores the list of Peer connections ICE failures counter.
  160. * @attribute _ICEConnectionFailures
  161. * @param {Number} <#peerId> The Peer connection ICE failures counter.
  162. * @type JSON
  163. * @private
  164. * @for Skylink
  165. * @since 0.5.8
  166. */
  167. Skylink.prototype._ICEConnectionFailures = {};
  168.  
  169. /**
  170. * Function that filters and configures the ICE servers received from Signaling
  171. * based on the <code>init()</code> configuration and returns the updated
  172. * list of ICE servers to be used when constructing Peer connection.
  173. * @method _setIceServers
  174. * @private
  175. * @for Skylink
  176. * @since 0.5.4
  177. */
  178. Skylink.prototype._setIceServers = function(givenConfig) {
  179. var givenIceServers = clone(givenConfig.iceServers);
  180. var iceServersList = {};
  181. var newIceServers = [];
  182. // TURN SSL config
  183. var useTURNSSLProtocol = false;
  184. var useTURNSSLPort = false;
  185.  
  186.  
  187.  
  188. if (window.location.protocol === 'https:' || this._forceTURNSSL) {
  189. if (window.webrtcDetectedBrowser === 'chrome' ||
  190. window.webrtcDetectedBrowser === 'safari' ||
  191. window.webrtcDetectedBrowser === 'IE') {
  192. useTURNSSLProtocol = true;
  193. useTURNSSLPort = false;
  194. } else {
  195. useTURNSSLPort = true;
  196. }
  197. }
  198.  
  199. log.log('TURN server connections SSL configuration', {
  200. useTURNSSLProtocol: useTURNSSLProtocol,
  201. useTURNSSLPort: useTURNSSLPort
  202. });
  203.  
  204. var pushIceServer = function (username, credential, url, index) {
  205. if (!iceServersList[username]) {
  206. iceServersList[username] = {};
  207. }
  208.  
  209. if (!iceServersList[username][credential]) {
  210. iceServersList[username][credential] = [];
  211. }
  212.  
  213. if (iceServersList[username][credential].indexOf(url) === -1) {
  214. if (typeof index === 'number') {
  215. iceServersList[username][credential].splice(index, 0, url);
  216. } else {
  217. iceServersList[username][credential].push(url);
  218. }
  219. }
  220. };
  221.  
  222. var i, serverItem;
  223.  
  224. for (i = 0; i < givenIceServers.length; i++) {
  225. var server = givenIceServers[i];
  226.  
  227. if (typeof server.url !== 'string') {
  228. log.warn('Ignoring ICE server provided at index ' + i, clone(server));
  229. continue;
  230. }
  231.  
  232. if (server.url.indexOf('stun') === 0) {
  233. if (!this._enableSTUN) {
  234. log.warn('Ignoring STUN server provided at index ' + i, clone(server));
  235. continue;
  236. }
  237.  
  238. if (!this._usePublicSTUN && server.url.indexOf('temasys') === -1) {
  239. log.warn('Ignoring public STUN server provided at index ' + i, clone(server));
  240. continue;
  241. }
  242.  
  243. } else if (server.url.indexOf('turn') === 0) {
  244. if (!this._enableTURN) {
  245. log.warn('Ignoring TURN server provided at index ' + i, clone(server));
  246. continue;
  247. }
  248.  
  249. if (server.url.indexOf(':443') === -1 && useTURNSSLPort) {
  250. log.log('Ignoring TURN Server (non-SSL port) provided at index ' + i, clone(server));
  251. continue;
  252. }
  253.  
  254. if (useTURNSSLProtocol) {
  255. var parts = server.url.split(':');
  256. parts[0] = 'turns';
  257. server.url = parts.join(':');
  258. }
  259. }
  260.  
  261. // parse "@" settings
  262. if (server.url.indexOf('@') > 0) {
  263. var protocolParts = server.url.split(':');
  264. var urlParts = protocolParts[1].split('@');
  265. server.username = urlParts[0];
  266. server.url = protocolParts[0] + ':' + urlParts[1];
  267.  
  268. // add the ICE server port
  269. if (protocolParts[2]) {
  270. server.url += ':' + protocolParts[2];
  271. }
  272. }
  273.  
  274. var username = typeof server.username === 'string' ? server.username : 'none';
  275. var credential = typeof server.credential === 'string' ? server.credential : 'none';
  276.  
  277. if (server.url.indexOf('turn') === 0) {
  278. if (this._TURNTransport === this.TURN_TRANSPORT.ANY) {
  279. pushIceServer(username, credential, server.url);
  280.  
  281. } else {
  282. var rawUrl = server.url;
  283.  
  284. if (rawUrl.indexOf('?transport=') > 0) {
  285. rawUrl = rawUrl.split('?transport=')[0];
  286. }
  287.  
  288. if (this._TURNTransport === this.TURN_TRANSPORT.NONE) {
  289. pushIceServer(username, credential, rawUrl);
  290. } else if (this._TURNTransport === this.TURN_TRANSPORT.UDP) {
  291. pushIceServer(username, credential, rawUrl + '?transport=udp');
  292. } else if (this._TURNTransport === this.TURN_TRANSPORT.TCP) {
  293. pushIceServer(username, credential, rawUrl + '?transport=tcp');
  294. } else if (this._TURNTransport === this.TURN_TRANSPORT.ALL) {
  295. pushIceServer(username, credential, rawUrl + '?transport=tcp');
  296. pushIceServer(username, credential, rawUrl + '?transport=udp');
  297. } else {
  298. log.warn('Invalid TURN transport option "' + this._TURNTransport +
  299. '". Ignoring TURN server at index' + i, clone(server));
  300. continue;
  301. }
  302. }
  303. } else {
  304. pushIceServer(username, credential, server.url);
  305. }
  306. }
  307.  
  308. // add mozilla STUN for firefox
  309. if (this._enableSTUN && this._usePublicSTUN && window.webrtcDetectedBrowser === 'firefox') {
  310. pushIceServer('none', 'none', 'stun:stun.services.mozilla.com', 0);
  311. }
  312.  
  313. var hasUrlsSupport = false;
  314.  
  315. if (window.webrtcDetectedBrowser === 'chrome' && window.webrtcDetectedVersion > 34) {
  316. hasUrlsSupport = true;
  317. }
  318.  
  319. if (window.webrtcDetectedBrowser === 'firefox' && window.webrtcDetectedVersion > 38) {
  320. hasUrlsSupport = true;
  321. }
  322.  
  323. if (window.webrtcDetectedBrowser === 'opera' && window.webrtcDetectedVersion > 31) {
  324. hasUrlsSupport = true;
  325. }
  326.  
  327. // plugin supports .urls
  328. if (window.webrtcDetectedBrowser === 'safari' || window.webrtcDetectedBrowser === 'IE') {
  329. hasUrlsSupport = true;
  330. }
  331.  
  332. for (var serverUsername in iceServersList) {
  333. if (iceServersList.hasOwnProperty(serverUsername)) {
  334. for (var serverCred in iceServersList[serverUsername]) {
  335. if (iceServersList[serverUsername].hasOwnProperty(serverCred)) {
  336. if (hasUrlsSupport) {
  337. var urlsItem = {
  338. urls: iceServersList[serverUsername][serverCred]
  339. };
  340. if (serverUsername !== 'none') {
  341. urlsItem.username = serverUsername;
  342. }
  343. if (serverCred !== 'none') {
  344. urlsItem.credential = serverCred;
  345. }
  346. newIceServers.push(urlsItem);
  347. } else {
  348. for (var j = 0; j < iceServersList[serverUsername][serverCred].length; j++) {
  349. var urlItem = {
  350. url: iceServersList[serverUsername][serverCred][j]
  351. };
  352. if (serverUsername !== 'none') {
  353. urlItem.username = serverUsername;
  354. }
  355. if (serverCred !== 'none') {
  356. urlItem.credential = serverCred;
  357. }
  358. newIceServers.push(urlItem);
  359. }
  360. }
  361. }
  362. }
  363. }
  364. }
  365.  
  366. log.log('Output iceServers configuration:', newIceServers);
  367.  
  368. return {
  369. iceServers: newIceServers
  370. };
  371. };