util.ts 4.8 KB

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