util.ts 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. // Types aren’t accurate
  2. //@ts-ignore
  3. import BinaryPack from "peerjs-js-binarypack";
  4. import { Supports } from "./supports";
  5. export interface UtilSupportsObj {
  6. /**
  7. * The current browser.
  8. * This property can be useful in determining whether two peers can connect.
  9. *
  10. * ```ts
  11. * if (util.browser === 'firefox') {
  12. * // OK to peer with Firefox peers.
  13. * }
  14. * ```
  15. *
  16. * `util.browser` can currently have the values
  17. * `'firefox', 'chrome', 'safari', 'edge', 'Not a supported browser.', 'Not a browser.' (unknown WebRTC-compatible agent).
  18. */
  19. browser: boolean;
  20. webRTC: boolean;
  21. /**
  22. * True if the current browser supports media streams and PeerConnection.
  23. */
  24. audioVideo: boolean;
  25. /**
  26. * True if the current browser supports DataChannel and PeerConnection.
  27. */
  28. data: boolean;
  29. binaryBlob: boolean;
  30. /**
  31. * True if the current browser supports reliable DataChannels.
  32. */
  33. reliable: boolean;
  34. }
  35. const DEFAULT_CONFIG = {
  36. iceServers: [
  37. { urls: "stun:stun.l.google.com:19302" },
  38. {
  39. urls: [
  40. "turn:eu-0.turn.peerjs.com:3478",
  41. "turn:us-0.turn.peerjs.com:3478",
  42. ],
  43. username: "peerjs",
  44. credential: "peerjsp",
  45. },
  46. ],
  47. sdpSemantics: "unified-plan",
  48. };
  49. export class Util {
  50. noop(): void {}
  51. readonly CLOUD_HOST = "0.peerjs.com";
  52. readonly CLOUD_PORT = 443;
  53. // Browsers that need chunking:
  54. readonly chunkedBrowsers = { Chrome: 1, chrome: 1 };
  55. readonly chunkedMTU = 16300; // The original 60000 bytes setting does not work when sending data from Firefox to Chrome, which is "cut off" after 16384 bytes and delivered individually.
  56. // Returns browser-agnostic default config
  57. readonly defaultConfig = DEFAULT_CONFIG;
  58. readonly browser = Supports.getBrowser();
  59. readonly browserVersion = Supports.getVersion();
  60. /**
  61. * A hash of WebRTC features mapped to booleans that correspond to whether the feature is supported by the current browser.
  62. *
  63. * :::caution
  64. * Only the properties documented here are guaranteed to be present on `util.supports`
  65. * :::
  66. */
  67. readonly supports = (function () {
  68. const supported: UtilSupportsObj = {
  69. browser: Supports.isBrowserSupported(),
  70. webRTC: Supports.isWebRTCSupported(),
  71. audioVideo: false,
  72. data: false,
  73. binaryBlob: false,
  74. reliable: false,
  75. };
  76. if (!supported.webRTC) return supported;
  77. let pc: RTCPeerConnection;
  78. try {
  79. pc = new RTCPeerConnection(DEFAULT_CONFIG);
  80. supported.audioVideo = true;
  81. let dc: RTCDataChannel;
  82. try {
  83. dc = pc.createDataChannel("_PEERJSTEST", { ordered: true });
  84. supported.data = true;
  85. supported.reliable = !!dc.ordered;
  86. // Binary test
  87. try {
  88. dc.binaryType = "blob";
  89. supported.binaryBlob = !Supports.isIOS;
  90. } catch (e) {}
  91. } catch (e) {
  92. } finally {
  93. if (dc) {
  94. dc.close();
  95. }
  96. }
  97. } catch (e) {
  98. } finally {
  99. if (pc) {
  100. pc.close();
  101. }
  102. }
  103. return supported;
  104. })();
  105. // Ensure alphanumeric ids
  106. validateId(id: string): boolean {
  107. // Allow empty ids
  108. return !id || /^[A-Za-z0-9]+(?:[ _-][A-Za-z0-9]+)*$/.test(id);
  109. }
  110. pack = BinaryPack.pack;
  111. unpack = BinaryPack.unpack;
  112. // Binary stuff
  113. private _dataCount: number = 1;
  114. chunk(
  115. blob: Blob,
  116. ): { __peerData: number; n: number; total: number; data: Blob }[] {
  117. const chunks = [];
  118. const size = blob.size;
  119. const total = Math.ceil(size / util.chunkedMTU);
  120. let index = 0;
  121. let start = 0;
  122. while (start < size) {
  123. const end = Math.min(size, start + util.chunkedMTU);
  124. const b = blob.slice(start, end);
  125. const chunk = {
  126. __peerData: this._dataCount,
  127. n: index,
  128. data: b,
  129. total,
  130. };
  131. chunks.push(chunk);
  132. start = end;
  133. index++;
  134. }
  135. this._dataCount++;
  136. return chunks;
  137. }
  138. blobToArrayBuffer(
  139. blob: Blob,
  140. cb: (arg: ArrayBuffer | null) => void,
  141. ): FileReader {
  142. const fr = new FileReader();
  143. fr.onload = function (evt) {
  144. if (evt.target) {
  145. cb(evt.target.result as ArrayBuffer);
  146. }
  147. };
  148. fr.readAsArrayBuffer(blob);
  149. return fr;
  150. }
  151. binaryStringToArrayBuffer(binary: string): ArrayBuffer | SharedArrayBuffer {
  152. const byteArray = new Uint8Array(binary.length);
  153. for (let i = 0; i < binary.length; i++) {
  154. byteArray[i] = binary.charCodeAt(i) & 0xff;
  155. }
  156. return byteArray.buffer;
  157. }
  158. randomToken(): string {
  159. return Math.random().toString(36).slice(2);
  160. }
  161. isSecure(): boolean {
  162. return location.protocol === "https:";
  163. }
  164. }
  165. /**
  166. * Provides a variety of helpful utilities.
  167. *
  168. * :::caution
  169. * Only the utilities documented here are guaranteed to be present on `util`.
  170. * Undocumented utilities can be removed without warning.
  171. * We don't consider these to be breaking changes.
  172. * :::
  173. */
  174. export const util = new Util();