broker.js 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. /* global log, dbg, snowflake */
  2. /*
  3. Communication with the snowflake broker.
  4. Browser snowflakes must register with the broker in order
  5. to get assigned to clients.
  6. */
  7. // Represents a broker running remotely.
  8. class Broker {
  9. // When interacting with the Broker, snowflake must generate a unique session
  10. // ID so the Broker can keep track of each proxy's signalling channels.
  11. // On construction, this Broker object does not do anything until
  12. // |getClientOffer| is called.
  13. constructor(config) {
  14. this.getClientOffer = this.getClientOffer.bind(this);
  15. this._postRequest = this._postRequest.bind(this);
  16. this.config = config
  17. this.url = config.brokerUrl;
  18. this.clients = 0;
  19. if (0 === this.url.indexOf('localhost', 0)) {
  20. // Ensure url has the right protocol + trailing slash.
  21. this.url = 'http://' + this.url;
  22. }
  23. if (0 !== this.url.indexOf('http', 0)) {
  24. this.url = 'https://' + this.url;
  25. }
  26. if ('/' !== this.url.substr(-1)) {
  27. this.url += '/';
  28. }
  29. }
  30. // Promises some client SDP Offer.
  31. // Registers this Snowflake with the broker using an HTTP POST request, and
  32. // waits for a response containing some client offer that the Broker chooses
  33. // for this proxy..
  34. // TODO: Actually support multiple clients.
  35. getClientOffer(id) {
  36. return new Promise((fulfill, reject) => {
  37. var xhr;
  38. xhr = new XMLHttpRequest();
  39. xhr.onreadystatechange = function() {
  40. if (xhr.DONE !== xhr.readyState) {
  41. return;
  42. }
  43. switch (xhr.status) {
  44. case Broker.CODE.OK:
  45. var response = JSON.parse(xhr.responseText);
  46. if (response.Status == Broker.STATUS.MATCH) {
  47. return fulfill(response.Offer); // Should contain offer.
  48. } else if (response.Status == Broker.STATUS.TIMEOUT) {
  49. return reject(Broker.MESSAGE.TIMEOUT);
  50. } else {
  51. log('Broker ERROR: Unexpected ' + response.Status);
  52. return reject(Broker.MESSAGE.UNEXPECTED);
  53. }
  54. default:
  55. log('Broker ERROR: Unexpected ' + xhr.status + ' - ' + xhr.statusText);
  56. snowflake.ui.setStatus(' failure. Please refresh.');
  57. return reject(Broker.MESSAGE.UNEXPECTED);
  58. }
  59. };
  60. this._xhr = xhr; // Used by spec to fake async Broker interaction
  61. var data = {"Version": "1.1", "Sid": id, "Type": this.config.proxyType}
  62. return this._postRequest(xhr, 'proxy', JSON.stringify(data));
  63. });
  64. }
  65. // Assumes getClientOffer happened, and a WebRTC SDP answer has been generated.
  66. // Sends it back to the broker, which passes it to back to the original client.
  67. sendAnswer(id, answer) {
  68. var xhr;
  69. dbg(id + ' - Sending answer back to broker...\n');
  70. dbg(answer.sdp);
  71. xhr = new XMLHttpRequest();
  72. xhr.onreadystatechange = function() {
  73. if (xhr.DONE !== xhr.readyState) {
  74. return;
  75. }
  76. switch (xhr.status) {
  77. case Broker.CODE.OK:
  78. dbg('Broker: Successfully replied with answer.');
  79. return dbg(xhr.responseText);
  80. default:
  81. dbg('Broker ERROR: Unexpected ' + xhr.status + ' - ' + xhr.statusText);
  82. return snowflake.ui.setStatus(' failure. Please refresh.');
  83. }
  84. };
  85. var data = {"Version": "1.0", "Sid": id, "Answer": JSON.stringify(answer)};
  86. return this._postRequest(xhr, 'answer', JSON.stringify(data));
  87. }
  88. // urlSuffix for the broker is different depending on what action
  89. // is desired.
  90. _postRequest(xhr, urlSuffix, payload) {
  91. var err;
  92. try {
  93. xhr.open('POST', this.url + urlSuffix);
  94. } catch (error) {
  95. err = error;
  96. /*
  97. An exception happens here when, for example, NoScript allows the domain
  98. on which the proxy badge runs, but not the domain to which it's trying
  99. to make the HTTP xhr. The exception message is like "Component
  100. returned failure code: 0x805e0006 [nsIXMLHttpRequest.open]" on Firefox.
  101. */
  102. log('Broker: exception while connecting: ' + err.message);
  103. return;
  104. }
  105. return xhr.send(payload);
  106. }
  107. }
  108. Broker.CODE = {
  109. OK: 200,
  110. BAD_REQUEST: 400,
  111. INTERNAL_SERVER_ERROR: 500
  112. };
  113. Broker.STATUS = {
  114. MATCH: "client match",
  115. TIMEOUT: "no match"
  116. };
  117. Broker.MESSAGE = {
  118. TIMEOUT: 'Timed out waiting for a client offer.',
  119. UNEXPECTED: 'Unexpected status.'
  120. };
  121. Broker.prototype.clients = 0;