util.ts 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. import * as BinaryPack from "js-binarypack";
  2. import { adapter as _ } from './adapter';
  3. const DEFAULT_CONFIG = {
  4. iceServers: [
  5. { urls: "stun:stun.l.google.com:19302" },
  6. { urls: "turn:0.peerjs.com:3478", username: "peerjs", credential: "peerjsp" }
  7. ],
  8. sdpSemantics: "unified-plan"
  9. };
  10. export class util {
  11. static noop(): void { }
  12. static readonly CLOUD_HOST = "0.peerjs.com";
  13. static readonly CLOUD_PORT = 443;
  14. // Browsers that need chunking:
  15. static readonly chunkedBrowsers = { Chrome: 1 };
  16. static 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.
  17. // Returns browser-agnostic default config
  18. static readonly defaultConfig = DEFAULT_CONFIG;
  19. // Returns the current browser.
  20. static readonly browser: string = (function (global) {
  21. // @ts-ignore
  22. if (global.mozRTCPeerConnection) {
  23. return "Firefox";
  24. }
  25. // @ts-ignore
  26. if (global.webkitRTCPeerConnection) {
  27. return "Chrome";
  28. }
  29. if (global.RTCPeerConnection) {
  30. return "Supported";
  31. }
  32. return "Unsupported";
  33. })(window);
  34. // Lists which features are supported
  35. static readonly supports = (function () {
  36. if (typeof RTCPeerConnection === "undefined") {
  37. return {};
  38. }
  39. let data = true;
  40. let audioVideo = true;
  41. let binaryBlob = false;
  42. let sctp = false;
  43. const onnegotiationneeded = !!window.webkitRTCPeerConnection;
  44. let pc, dc;
  45. try {
  46. pc = new RTCPeerConnection(DEFAULT_CONFIG);
  47. } catch (e) {
  48. data = false;
  49. audioVideo = false;
  50. }
  51. if (data) {
  52. try {
  53. dc = pc.createDataChannel("_PEERJSTEST");
  54. } catch (e) {
  55. data = false;
  56. }
  57. }
  58. if (data) {
  59. // Binary test
  60. try {
  61. dc.binaryType = "blob";
  62. binaryBlob = true;
  63. } catch (e) { }
  64. // Reliable test.
  65. // Unfortunately Chrome is a bit unreliable about whether or not they
  66. // support reliable.
  67. const reliablePC = new RTCPeerConnection(DEFAULT_CONFIG);
  68. try {
  69. const reliableDC = reliablePC.createDataChannel(
  70. "_PEERJSRELIABLETEST",
  71. {}
  72. );
  73. sctp = reliableDC.ordered;
  74. } catch (e) { }
  75. reliablePC.close();
  76. }
  77. // FIXME: not really the best check...
  78. if (audioVideo) {
  79. audioVideo = !!pc.addStream;
  80. }
  81. if (pc) {
  82. pc.close();
  83. }
  84. return {
  85. audioVideo: audioVideo,
  86. data: data,
  87. binaryBlob: binaryBlob,
  88. binary: sctp, // deprecated; sctp implies binary support.
  89. reliable: sctp, // deprecated; sctp implies reliable data.
  90. sctp: sctp,
  91. onnegotiationneeded: onnegotiationneeded
  92. };
  93. })();
  94. // Ensure alphanumeric ids
  95. static validateId(id: string): boolean {
  96. // Allow empty ids
  97. return !id || /^[A-Za-z0-9]+(?:[ _-][A-Za-z0-9]+)*$/.test(id);
  98. }
  99. static pack = BinaryPack.pack;
  100. static unpack = BinaryPack.unpack;
  101. // Binary stuff
  102. private static _dataCount = 1;
  103. // chunks a blob.
  104. static chunk(bl: Blob): any[] {
  105. const chunks = [];
  106. const size = bl.size;
  107. const total = Math.ceil(size / util.chunkedMTU);
  108. let index;
  109. let start = (index = 0);
  110. while (start < size) {
  111. const end = Math.min(size, start + util.chunkedMTU);
  112. const b = bl.slice(start, end);
  113. const chunk = {
  114. __peerData: this._dataCount,
  115. n: index,
  116. data: b,
  117. total: total
  118. };
  119. chunks.push(chunk);
  120. start = end;
  121. index++;
  122. }
  123. this._dataCount++;
  124. return chunks;
  125. }
  126. static blobToArrayBuffer(blob: Blob, cb: (arg: string | ArrayBuffer | null) => void): void {
  127. const fr = new FileReader();
  128. fr.onload = function (evt) {
  129. if (evt.target) {
  130. cb(evt.target.result);
  131. }
  132. };
  133. fr.readAsArrayBuffer(blob);
  134. }
  135. static blobToBinaryString(blob: Blob, cb: (arg: string | ArrayBuffer | null) => void): void {
  136. const fr = new FileReader();
  137. fr.onload = function (evt) {
  138. if (evt.target) {
  139. cb(evt.target.result);
  140. }
  141. };
  142. fr.readAsBinaryString(blob);
  143. }
  144. static binaryStringToArrayBuffer(binary: string): ArrayBuffer | SharedArrayBuffer {
  145. let byteArray = new Uint8Array(binary.length);
  146. for (let i = 0; i < binary.length; i++) {
  147. byteArray[i] = binary.charCodeAt(i) & 0xff;
  148. }
  149. return byteArray.buffer;
  150. }
  151. static randomToken(): string {
  152. return Math.random()
  153. .toString(36)
  154. .substr(2);
  155. }
  156. static isSecure(): boolean {
  157. return location.protocol === "https:";
  158. }
  159. }