util.js 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. /* exported Util, Params, DummyRateLimit */
  2. /*
  3. A JavaScript WebRTC snowflake proxy
  4. Contains helpers for parsing query strings and other utilities.
  5. */
  6. class Util {
  7. static genSnowflakeID() {
  8. return Math.random().toString(36).substring(2);
  9. }
  10. static hasWebRTC() {
  11. return typeof PeerConnection === 'function';
  12. }
  13. static hasCookies() {
  14. return navigator.cookieEnabled;
  15. }
  16. }
  17. class Parse {
  18. // Parse a cookie data string (usually document.cookie). The return type is an
  19. // object mapping cookies names to values. Returns null on error.
  20. // http://www.w3.org/TR/DOM-Level-2-HTML/html.html#ID-8747038
  21. static cookie(cookies) {
  22. var i, j, len, name, result, string, strings, value;
  23. result = {};
  24. strings = [];
  25. if (cookies) {
  26. strings = cookies.split(';');
  27. }
  28. for (i = 0, len = strings.length; i < len; i++) {
  29. string = strings[i];
  30. j = string.indexOf('=');
  31. if (-1 === j) {
  32. return null;
  33. }
  34. name = decodeURIComponent(string.substr(0, j).trim());
  35. value = decodeURIComponent(string.substr(j + 1).trim());
  36. if (!(name in result)) {
  37. result[name] = value;
  38. }
  39. }
  40. return result;
  41. }
  42. // Parse an address in the form 'host:port'. Returns an Object with keys 'host'
  43. // (String) and 'port' (int). Returns null on error.
  44. static address(spec) {
  45. var host, m, port;
  46. m = null;
  47. if (!m) {
  48. // IPv6 syntax.
  49. m = spec.match(/^\[([\0-9a-fA-F:.]+)\]:([0-9]+)$/);
  50. }
  51. if (!m) {
  52. // IPv4 syntax.
  53. m = spec.match(/^([0-9.]+):([0-9]+)$/);
  54. }
  55. if (!m) {
  56. // TODO: Domain match
  57. return null;
  58. }
  59. host = m[1];
  60. port = parseInt(m[2], 10);
  61. if (isNaN(port) || port < 0 || port > 65535) {
  62. return null;
  63. }
  64. return {
  65. host: host,
  66. port: port
  67. };
  68. }
  69. // Parse a count of bytes. A suffix of 'k', 'm', or 'g' (or uppercase)
  70. // does what you would think. Returns null on error.
  71. static byteCount(spec) {
  72. let matches = spec.match(/^(\d+(?:\.\d*)?)(\w*)$/);
  73. if (matches === null) {
  74. return null;
  75. }
  76. let count = Number(matches[1]);
  77. if (isNaN(count)) {
  78. return null;
  79. }
  80. const UNITS = new Map([
  81. ['', 1],
  82. ['k', 1024],
  83. ['m', 1024*1024],
  84. ['g', 1024*1024*1024],
  85. ]);
  86. let unit = matches[2].toLowerCase();
  87. if (!UNITS.has(unit)) {
  88. return null;
  89. }
  90. let multiplier = UNITS.get(unit);
  91. return count * multiplier;
  92. }
  93. // Parse a connection-address out of the "c=" Connection Data field of a
  94. // session description. Return undefined if none is found.
  95. // https://tools.ietf.org/html/rfc4566#section-5.7
  96. static ipFromSDP(sdp) {
  97. var i, len, m, pattern, ref;
  98. ref = [/^c=IN IP4 ([\d.]+)(?:(?:\/\d+)?\/\d+)?(:? |$)/m, /^c=IN IP6 ([0-9A-Fa-f:.]+)(?:\/\d+)?(:? |$)/m];
  99. for (i = 0, len = ref.length; i < len; i++) {
  100. pattern = ref[i];
  101. m = pattern.exec(sdp);
  102. if (m != null) {
  103. return m[1];
  104. }
  105. }
  106. }
  107. }
  108. class Params {
  109. static getBool(query, param, defaultValue) {
  110. if (!query.has(param)) {
  111. return defaultValue;
  112. }
  113. var val;
  114. val = query.get(param);
  115. if ('true' === val || '1' === val || '' === val) {
  116. return true;
  117. }
  118. if ('false' === val || '0' === val) {
  119. return false;
  120. }
  121. return null;
  122. }
  123. // Get an object value and parse it as a byte count. Example byte counts are
  124. // '100' and '1.3m'. Returns |defaultValue| if param is not a key. Return null
  125. // on a parsing error.
  126. static getByteCount(query, param, defaultValue) {
  127. if (!query.has(param)) {
  128. return defaultValue;
  129. }
  130. return Parse.byteCount(query.get(param));
  131. }
  132. }
  133. class BucketRateLimit {
  134. constructor(capacity, time) {
  135. this.capacity = capacity;
  136. this.time = time;
  137. }
  138. age() {
  139. var delta, now;
  140. now = new Date();
  141. delta = (now - this.lastUpdate) / 1000.0;
  142. this.lastUpdate = now;
  143. this.amount -= delta * this.capacity / this.time;
  144. if (this.amount < 0.0) {
  145. return this.amount = 0.0;
  146. }
  147. }
  148. update(n) {
  149. this.age();
  150. this.amount += n;
  151. return this.amount <= this.capacity;
  152. }
  153. // How many seconds in the future will the limit expire?
  154. when() {
  155. this.age();
  156. return (this.amount - this.capacity) / (this.capacity / this.time);
  157. }
  158. isLimited() {
  159. this.age();
  160. return this.amount > this.capacity;
  161. }
  162. }
  163. BucketRateLimit.prototype.amount = 0.0;
  164. BucketRateLimit.prototype.lastUpdate = new Date();
  165. // A rate limiter that never limits.
  166. class DummyRateLimit {
  167. constructor(capacity, time) {
  168. this.capacity = capacity;
  169. this.time = time;
  170. }
  171. update() {
  172. return true;
  173. }
  174. when() {
  175. return 0.0;
  176. }
  177. isLimited() {
  178. return false;
  179. }
  180. }