You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

141 lines
3.7 KiB

4 years ago
  1. 'use strict';
  2. // Few cool transports do work only for same-origin. In order to make
  3. // them work cross-domain we shall use iframe, served from the
  4. // remote domain. New browsers have capabilities to communicate with
  5. // cross domain iframe using postMessage(). In IE it was implemented
  6. // from IE 8+, but of course, IE got some details wrong:
  7. // http://msdn.microsoft.com/en-us/library/cc197015(v=VS.85).aspx
  8. // http://stevesouders.com/misc/test-postmessage.php
  9. var inherits = require('inherits')
  10. , JSON3 = require('json3')
  11. , EventEmitter = require('events').EventEmitter
  12. , version = require('../version')
  13. , urlUtils = require('../utils/url')
  14. , iframeUtils = require('../utils/iframe')
  15. , eventUtils = require('../utils/event')
  16. , random = require('../utils/random')
  17. ;
  18. var debug = function() {};
  19. if (process.env.NODE_ENV !== 'production') {
  20. debug = require('debug')('sockjs-client:transport:iframe');
  21. }
  22. function IframeTransport(transport, transUrl, baseUrl) {
  23. if (!IframeTransport.enabled()) {
  24. throw new Error('Transport created when disabled');
  25. }
  26. EventEmitter.call(this);
  27. var self = this;
  28. this.origin = urlUtils.getOrigin(baseUrl);
  29. this.baseUrl = baseUrl;
  30. this.transUrl = transUrl;
  31. this.transport = transport;
  32. this.windowId = random.string(8);
  33. var iframeUrl = urlUtils.addPath(baseUrl, '/iframe.html') + '#' + this.windowId;
  34. debug(transport, transUrl, iframeUrl);
  35. this.iframeObj = iframeUtils.createIframe(iframeUrl, function(r) {
  36. debug('err callback');
  37. self.emit('close', 1006, 'Unable to load an iframe (' + r + ')');
  38. self.close();
  39. });
  40. this.onmessageCallback = this._message.bind(this);
  41. eventUtils.attachEvent('message', this.onmessageCallback);
  42. }
  43. inherits(IframeTransport, EventEmitter);
  44. IframeTransport.prototype.close = function() {
  45. debug('close');
  46. this.removeAllListeners();
  47. if (this.iframeObj) {
  48. eventUtils.detachEvent('message', this.onmessageCallback);
  49. try {
  50. // When the iframe is not loaded, IE raises an exception
  51. // on 'contentWindow'.
  52. this.postMessage('c');
  53. } catch (x) {
  54. // intentionally empty
  55. }
  56. this.iframeObj.cleanup();
  57. this.iframeObj = null;
  58. this.onmessageCallback = this.iframeObj = null;
  59. }
  60. };
  61. IframeTransport.prototype._message = function(e) {
  62. debug('message', e.data);
  63. if (!urlUtils.isOriginEqual(e.origin, this.origin)) {
  64. debug('not same origin', e.origin, this.origin);
  65. return;
  66. }
  67. var iframeMessage;
  68. try {
  69. iframeMessage = JSON3.parse(e.data);
  70. } catch (ignored) {
  71. debug('bad json', e.data);
  72. return;
  73. }
  74. if (iframeMessage.windowId !== this.windowId) {
  75. debug('mismatched window id', iframeMessage.windowId, this.windowId);
  76. return;
  77. }
  78. switch (iframeMessage.type) {
  79. case 's':
  80. this.iframeObj.loaded();
  81. // window global dependency
  82. this.postMessage('s', JSON3.stringify([
  83. version
  84. , this.transport
  85. , this.transUrl
  86. , this.baseUrl
  87. ]));
  88. break;
  89. case 't':
  90. this.emit('message', iframeMessage.data);
  91. break;
  92. case 'c':
  93. var cdata;
  94. try {
  95. cdata = JSON3.parse(iframeMessage.data);
  96. } catch (ignored) {
  97. debug('bad json', iframeMessage.data);
  98. return;
  99. }
  100. this.emit('close', cdata[0], cdata[1]);
  101. this.close();
  102. break;
  103. }
  104. };
  105. IframeTransport.prototype.postMessage = function(type, data) {
  106. debug('postMessage', type, data);
  107. this.iframeObj.post(JSON3.stringify({
  108. windowId: this.windowId
  109. , type: type
  110. , data: data || ''
  111. }), this.origin);
  112. };
  113. IframeTransport.prototype.send = function(message) {
  114. debug('send', message);
  115. this.postMessage('m', message);
  116. };
  117. IframeTransport.enabled = function() {
  118. return iframeUtils.iframeEnabled;
  119. };
  120. IframeTransport.transportName = 'iframe';
  121. IframeTransport.roundTrips = 2;
  122. module.exports = IframeTransport;