您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

connection.js 8.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. /* global APP, JitsiMeetJS, config */
  2. import { jitsiLocalStorage } from '@jitsi/js-utils';
  3. import Logger from 'jitsi-meet-logger';
  4. import { redirectToTokenAuthService } from './modules/UI/authentication/AuthHandler';
  5. import { LoginDialog } from './react/features/authentication/components';
  6. import { isTokenAuthEnabled } from './react/features/authentication/functions';
  7. import {
  8. connectionEstablished,
  9. connectionFailed
  10. } from './react/features/base/connection/actions';
  11. import { openDialog } from './react/features/base/dialog/actions';
  12. import {
  13. isFatalJitsiConnectionError,
  14. JitsiConnectionErrors,
  15. JitsiConnectionEvents
  16. } from './react/features/base/lib-jitsi-meet';
  17. import { setPrejoinDisplayNameRequired } from './react/features/prejoin/actions';
  18. const logger = Logger.getLogger(__filename);
  19. /**
  20. * The feature announced so we can distinguish jibri participants.
  21. *
  22. * @type {string}
  23. */
  24. export const DISCO_JIBRI_FEATURE = 'http://jitsi.org/protocol/jibri';
  25. /**
  26. * Checks if we have data to use attach instead of connect. If we have the data
  27. * executes attach otherwise check if we have to wait for the data. If we have
  28. * to wait for the attach data we are setting handler to APP.connect.handler
  29. * which is going to be called when the attach data is received otherwise
  30. * executes connect.
  31. *
  32. * @param {string} [id] user id
  33. * @param {string} [password] password
  34. * @param {string} [roomName] the name of the conference.
  35. */
  36. function checkForAttachParametersAndConnect(id, password, connection) {
  37. if (window.XMPPAttachInfo) {
  38. APP.connect.status = 'connecting';
  39. // When connection optimization is not deployed or enabled the default
  40. // value will be window.XMPPAttachInfo.status = "error"
  41. // If the connection optimization is deployed and enabled and there is
  42. // a failure the value will be window.XMPPAttachInfo.status = "error"
  43. if (window.XMPPAttachInfo.status === 'error') {
  44. connection.connect({
  45. id,
  46. password
  47. });
  48. return;
  49. }
  50. const attachOptions = window.XMPPAttachInfo.data;
  51. if (attachOptions) {
  52. connection.attach(attachOptions);
  53. delete window.XMPPAttachInfo.data;
  54. } else {
  55. connection.connect({
  56. id,
  57. password
  58. });
  59. }
  60. } else {
  61. APP.connect.status = 'ready';
  62. APP.connect.handler
  63. = checkForAttachParametersAndConnect.bind(
  64. null,
  65. id, password, connection);
  66. }
  67. }
  68. /**
  69. * Try to open connection using provided credentials.
  70. * @param {string} [id]
  71. * @param {string} [password]
  72. * @param {string} [roomName]
  73. * @returns {Promise<JitsiConnection>} connection if
  74. * everything is ok, else error.
  75. */
  76. export function connect(id, password, roomName) {
  77. const connectionConfig = Object.assign({}, config);
  78. const { jwt } = APP.store.getState()['features/base/jwt'];
  79. // Use Websocket URL for the web app if configured. Note that there is no 'isWeb' check, because there's assumption
  80. // that this code executes only on web browsers/electron. This needs to be changed when mobile and web are unified.
  81. let serviceUrl = connectionConfig.websocket || connectionConfig.bosh;
  82. serviceUrl += `?room=${roomName}`;
  83. // FIXME Remove deprecated 'bosh' option assignment at some point(LJM will be accepting only 'serviceUrl' option
  84. // in future). It's included for the time being for Jitsi Meet and lib-jitsi-meet versions interoperability.
  85. connectionConfig.serviceUrl = connectionConfig.bosh = serviceUrl;
  86. if (connectionConfig.websocketKeepAliveUrl) {
  87. connectionConfig.websocketKeepAliveUrl += `?room=${roomName}`;
  88. }
  89. const connection = new JitsiMeetJS.JitsiConnection(null, jwt, connectionConfig);
  90. if (config.iAmRecorder) {
  91. connection.addFeature(DISCO_JIBRI_FEATURE);
  92. }
  93. return new Promise((resolve, reject) => {
  94. connection.addEventListener(
  95. JitsiConnectionEvents.CONNECTION_ESTABLISHED,
  96. handleConnectionEstablished);
  97. connection.addEventListener(
  98. JitsiConnectionEvents.CONNECTION_FAILED,
  99. handleConnectionFailed);
  100. connection.addEventListener(
  101. JitsiConnectionEvents.CONNECTION_FAILED,
  102. connectionFailedHandler);
  103. connection.addEventListener(
  104. JitsiConnectionEvents.DISPLAY_NAME_REQUIRED,
  105. displayNameRequiredHandler
  106. );
  107. /* eslint-disable max-params */
  108. /**
  109. *
  110. */
  111. function connectionFailedHandler(error, message, credentials, details) {
  112. /* eslint-enable max-params */
  113. APP.store.dispatch(
  114. connectionFailed(
  115. connection, {
  116. credentials,
  117. details,
  118. message,
  119. name: error
  120. }));
  121. if (isFatalJitsiConnectionError(error)) {
  122. connection.removeEventListener(
  123. JitsiConnectionEvents.CONNECTION_FAILED,
  124. connectionFailedHandler);
  125. }
  126. }
  127. /**
  128. *
  129. */
  130. function unsubscribe() {
  131. connection.removeEventListener(
  132. JitsiConnectionEvents.CONNECTION_ESTABLISHED,
  133. handleConnectionEstablished);
  134. connection.removeEventListener(
  135. JitsiConnectionEvents.CONNECTION_FAILED,
  136. handleConnectionFailed);
  137. }
  138. /**
  139. *
  140. */
  141. function handleConnectionEstablished() {
  142. APP.store.dispatch(connectionEstablished(connection, Date.now()));
  143. unsubscribe();
  144. resolve(connection);
  145. }
  146. /**
  147. *
  148. */
  149. function handleConnectionFailed(err) {
  150. unsubscribe();
  151. logger.error('CONNECTION FAILED:', err);
  152. reject(err);
  153. }
  154. /**
  155. * Marks the display name for the prejoin screen as required.
  156. * This can happen if a user tries to join a room with lobby enabled.
  157. */
  158. function displayNameRequiredHandler() {
  159. APP.store.dispatch(setPrejoinDisplayNameRequired());
  160. }
  161. checkForAttachParametersAndConnect(id, password, connection);
  162. });
  163. }
  164. /**
  165. * Open JitsiConnection using provided credentials.
  166. * If retry option is true it will show auth dialog on PASSWORD_REQUIRED error.
  167. *
  168. * @param {object} options
  169. * @param {string} [options.id]
  170. * @param {string} [options.password]
  171. * @param {string} [options.roomName]
  172. * @param {boolean} [retry] if we should show auth dialog
  173. * on PASSWORD_REQUIRED error.
  174. *
  175. * @returns {Promise<JitsiConnection>}
  176. */
  177. export function openConnection({ id, password, retry, roomName }) {
  178. const usernameOverride
  179. = jitsiLocalStorage.getItem('xmpp_username_override');
  180. const passwordOverride
  181. = jitsiLocalStorage.getItem('xmpp_password_override');
  182. if (usernameOverride && usernameOverride.length > 0) {
  183. id = usernameOverride; // eslint-disable-line no-param-reassign
  184. }
  185. if (passwordOverride && passwordOverride.length > 0) {
  186. password = passwordOverride; // eslint-disable-line no-param-reassign
  187. }
  188. return connect(id, password, roomName).catch(err => {
  189. if (retry) {
  190. const { jwt } = APP.store.getState()['features/base/jwt'];
  191. if (err === JitsiConnectionErrors.PASSWORD_REQUIRED && !jwt) {
  192. return requestAuth(roomName);
  193. }
  194. }
  195. throw err;
  196. });
  197. }
  198. /**
  199. * Show Authentication Dialog and try to connect with new credentials.
  200. * If failed to connect because of PASSWORD_REQUIRED error
  201. * then ask for password again.
  202. * @param {string} [roomName] name of the conference room
  203. *
  204. * @returns {Promise<JitsiConnection>}
  205. */
  206. function requestAuth(roomName) {
  207. const config = APP.store.getState()['features/base/config'];
  208. if (isTokenAuthEnabled(config)) {
  209. // This Promise never resolves as user gets redirected to another URL
  210. return new Promise(() => redirectToTokenAuthService(roomName));
  211. }
  212. return new Promise(resolve => {
  213. const onSuccess = connection => {
  214. resolve(connection);
  215. };
  216. APP.store.dispatch(
  217. openDialog(LoginDialog, { onSuccess,
  218. roomName })
  219. );
  220. });
  221. }