stomp.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488
  1. // Generated by CoffeeScript 1.7.1
  2. /*
  3. Stomp Over WebSocket http://www.jmesnil.net/stomp-websocket/doc/ | Apache License V2.0
  4. Copyright (C) 2010-2013 [Jeff Mesnil](http://jmesnil.net/)
  5. Copyright (C) 2012 [FuseSource, Inc.](http://fusesource.com)
  6. */
  7. (function() {
  8. var Byte, Client, Frame, Stomp,
  9. __hasProp = {}.hasOwnProperty,
  10. __slice = [].slice;
  11. Byte = {
  12. LF: '\x0A',
  13. NULL: '\x00'
  14. };
  15. Frame = (function() {
  16. var unmarshallSingle;
  17. function Frame(command, headers, body) {
  18. this.command = command;
  19. this.headers = headers != null ? headers : {};
  20. this.body = body != null ? body : '';
  21. }
  22. Frame.prototype.toString = function() {
  23. var lines, name, skipContentLength, value, _ref;
  24. lines = [this.command];
  25. skipContentLength = this.headers['content-length'] === false ? true : false;
  26. if (skipContentLength) {
  27. delete this.headers['content-length'];
  28. }
  29. _ref = this.headers;
  30. for (name in _ref) {
  31. if (!__hasProp.call(_ref, name)) continue;
  32. value = _ref[name];
  33. lines.push("" + name + ":" + value);
  34. }
  35. if (this.body && !skipContentLength) {
  36. lines.push("content-length:" + (Frame.sizeOfUTF8(this.body)));
  37. }
  38. lines.push(Byte.LF + this.body);
  39. return lines.join(Byte.LF);
  40. };
  41. Frame.sizeOfUTF8 = function(s) {
  42. if (s) {
  43. return encodeURI(s).match(/%..|./g).length;
  44. } else {
  45. return 0;
  46. }
  47. };
  48. unmarshallSingle = function(data) {
  49. var body, chr, command, divider, headerLines, headers, i, idx, len, line, start, trim, _i, _j, _len, _ref, _ref1;
  50. divider = data.search(RegExp("" + Byte.LF + Byte.LF));
  51. headerLines = data.substring(0, divider).split(Byte.LF);
  52. command = headerLines.shift();
  53. headers = {};
  54. trim = function(str) {
  55. return str.replace(/^\s+|\s+$/g, '');
  56. };
  57. _ref = headerLines.reverse();
  58. for (_i = 0, _len = _ref.length; _i < _len; _i++) {
  59. line = _ref[_i];
  60. idx = line.indexOf(':');
  61. headers[trim(line.substring(0, idx))] = trim(line.substring(idx + 1));
  62. }
  63. body = '';
  64. start = divider + 2;
  65. if (headers['content-length']) {
  66. len = parseInt(headers['content-length']);
  67. body = ('' + data).substring(start, start + len);
  68. } else {
  69. chr = null;
  70. for (i = _j = start, _ref1 = data.length; start <= _ref1 ? _j < _ref1 : _j > _ref1; i = start <= _ref1 ? ++_j : --_j) {
  71. chr = data.charAt(i);
  72. if (chr === Byte.NULL) {
  73. break;
  74. }
  75. body += chr;
  76. }
  77. }
  78. return new Frame(command, headers, body);
  79. };
  80. Frame.unmarshall = function(datas) {
  81. var data;
  82. return (function() {
  83. var _i, _len, _ref, _results;
  84. _ref = datas.split(RegExp("" + Byte.NULL + Byte.LF + "*"));
  85. _results = [];
  86. for (_i = 0, _len = _ref.length; _i < _len; _i++) {
  87. data = _ref[_i];
  88. if ((data != null ? data.length : void 0) > 0) {
  89. _results.push(unmarshallSingle(data));
  90. }
  91. }
  92. return _results;
  93. })();
  94. };
  95. Frame.marshall = function(command, headers, body) {
  96. var frame;
  97. frame = new Frame(command, headers, body);
  98. return frame.toString() + Byte.NULL;
  99. };
  100. return Frame;
  101. })();
  102. Client = (function() {
  103. var now;
  104. function Client(ws) {
  105. this.ws = ws;
  106. this.ws.binaryType = "arraybuffer";
  107. this.counter = 0;
  108. this.connected = false;
  109. this.heartbeat = {
  110. outgoing: 10000,
  111. incoming: 10000
  112. };
  113. this.maxWebSocketFrameSize = 16 * 1024;
  114. this.subscriptions = {};
  115. }
  116. Client.prototype.debug = function(message) {
  117. var _ref;
  118. return typeof window !== "undefined" && window !== null ? (_ref = window.console) != null ? _ref.log(message) : void 0 : void 0;
  119. };
  120. now = function() {
  121. if (Date.now) {
  122. return Date.now();
  123. } else {
  124. return new Date().valueOf;
  125. }
  126. };
  127. Client.prototype._transmit = function(command, headers, body) {
  128. var out;
  129. out = Frame.marshall(command, headers, body);
  130. if (typeof this.debug === "function") {
  131. this.debug(">>> " + out);
  132. }
  133. while (true) {
  134. if (out.length > this.maxWebSocketFrameSize) {
  135. this.ws.send(out.substring(0, this.maxWebSocketFrameSize));
  136. out = out.substring(this.maxWebSocketFrameSize);
  137. if (typeof this.debug === "function") {
  138. this.debug("remaining = " + out.length);
  139. }
  140. } else {
  141. return this.ws.send(out);
  142. }
  143. }
  144. };
  145. Client.prototype._setupHeartbeat = function(headers) {
  146. var serverIncoming, serverOutgoing, ttl, v, _ref, _ref1;
  147. if ((_ref = headers.version) !== Stomp.VERSIONS.V1_1 && _ref !== Stomp.VERSIONS.V1_2) {
  148. return;
  149. }
  150. _ref1 = (function() {
  151. var _i, _len, _ref1, _results;
  152. _ref1 = headers['heart-beat'].split(",");
  153. _results = [];
  154. for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
  155. v = _ref1[_i];
  156. _results.push(parseInt(v));
  157. }
  158. return _results;
  159. })(), serverOutgoing = _ref1[0], serverIncoming = _ref1[1];
  160. if (!(this.heartbeat.outgoing === 0 || serverIncoming === 0)) {
  161. ttl = Math.max(this.heartbeat.outgoing, serverIncoming);
  162. if (typeof this.debug === "function") {
  163. this.debug("send PING every " + ttl + "ms");
  164. }
  165. this.pinger = Stomp.setInterval(ttl, (function(_this) {
  166. return function() {
  167. _this.ws.send(Byte.LF);
  168. return typeof _this.debug === "function" ? _this.debug(">>> PING") : void 0;
  169. };
  170. })(this));
  171. }
  172. if (!(this.heartbeat.incoming === 0 || serverOutgoing === 0)) {
  173. ttl = Math.max(this.heartbeat.incoming, serverOutgoing);
  174. if (typeof this.debug === "function") {
  175. this.debug("check PONG every " + ttl + "ms");
  176. }
  177. return this.ponger = Stomp.setInterval(ttl, (function(_this) {
  178. return function() {
  179. var delta;
  180. delta = now() - _this.serverActivity;
  181. if (delta > ttl * 2) {
  182. if (typeof _this.debug === "function") {
  183. _this.debug("did not receive server activity for the last " + delta + "ms");
  184. }
  185. return _this.ws.close();
  186. }
  187. };
  188. })(this));
  189. }
  190. };
  191. Client.prototype._parseConnect = function() {
  192. var args, connectCallback, errorCallback, headers;
  193. args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
  194. headers = {};
  195. switch (args.length) {
  196. case 2:
  197. headers = args[0], connectCallback = args[1];
  198. break;
  199. case 3:
  200. if (args[1] instanceof Function) {
  201. headers = args[0], connectCallback = args[1], errorCallback = args[2];
  202. } else {
  203. headers.login = args[0], headers.passcode = args[1], connectCallback = args[2];
  204. }
  205. break;
  206. case 4:
  207. headers.login = args[0], headers.passcode = args[1], connectCallback = args[2], errorCallback = args[3];
  208. break;
  209. default:
  210. headers.login = args[0], headers.passcode = args[1], connectCallback = args[2], errorCallback = args[3], headers.host = args[4];
  211. }
  212. return [headers, connectCallback, errorCallback];
  213. };
  214. Client.prototype.connect = function() {
  215. var args, errorCallback, headers, out;
  216. args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
  217. out = this._parseConnect.apply(this, args);
  218. headers = out[0], this.connectCallback = out[1], errorCallback = out[2];
  219. if (typeof this.debug === "function") {
  220. this.debug("Opening Web Socket...");
  221. }
  222. this.ws.onmessage = (function(_this) {
  223. return function(evt) {
  224. var arr, c, client, data, frame, messageID, onreceive, subscription, _i, _len, _ref, _results;
  225. data = typeof ArrayBuffer !== 'undefined' && evt.data instanceof ArrayBuffer ? (arr = new Uint8Array(evt.data), typeof _this.debug === "function" ? _this.debug("--- got data length: " + arr.length) : void 0, ((function() {
  226. var _i, _len, _results;
  227. _results = [];
  228. for (_i = 0, _len = arr.length; _i < _len; _i++) {
  229. c = arr[_i];
  230. _results.push(String.fromCharCode(c));
  231. }
  232. return _results;
  233. })()).join('')) : evt.data;
  234. _this.serverActivity = now();
  235. if (data === Byte.LF) {
  236. if (typeof _this.debug === "function") {
  237. _this.debug("<<< PONG");
  238. }
  239. return;
  240. }
  241. if (typeof _this.debug === "function") {
  242. _this.debug("<<< " + data);
  243. }
  244. _ref = Frame.unmarshall(data);
  245. _results = [];
  246. for (_i = 0, _len = _ref.length; _i < _len; _i++) {
  247. frame = _ref[_i];
  248. switch (frame.command) {
  249. case "CONNECTED":
  250. if (typeof _this.debug === "function") {
  251. _this.debug("connected to server " + frame.headers.server);
  252. }
  253. _this.connected = true;
  254. _this._setupHeartbeat(frame.headers);
  255. _results.push(typeof _this.connectCallback === "function" ? _this.connectCallback(frame) : void 0);
  256. break;
  257. case "MESSAGE":
  258. subscription = frame.headers.subscription;
  259. onreceive = _this.subscriptions[subscription] || _this.onreceive;
  260. if (onreceive) {
  261. client = _this;
  262. messageID = frame.headers["message-id"];
  263. frame.ack = function(headers) {
  264. if (headers == null) {
  265. headers = {};
  266. }
  267. return client.ack(messageID, subscription, headers);
  268. };
  269. frame.nack = function(headers) {
  270. if (headers == null) {
  271. headers = {};
  272. }
  273. return client.nack(messageID, subscription, headers);
  274. };
  275. _results.push(onreceive(frame));
  276. } else {
  277. _results.push(typeof _this.debug === "function" ? _this.debug("Unhandled received MESSAGE: " + frame) : void 0);
  278. }
  279. break;
  280. case "RECEIPT":
  281. _results.push(typeof _this.onreceipt === "function" ? _this.onreceipt(frame) : void 0);
  282. break;
  283. case "ERROR":
  284. _results.push(typeof errorCallback === "function" ? errorCallback(frame) : void 0);
  285. break;
  286. default:
  287. _results.push(typeof _this.debug === "function" ? _this.debug("Unhandled frame: " + frame) : void 0);
  288. }
  289. }
  290. return _results;
  291. };
  292. })(this);
  293. this.ws.onclose = (function(_this) {
  294. return function() {
  295. var msg;
  296. msg = "Whoops! Lost connection to " + _this.ws.url;
  297. if (typeof _this.debug === "function") {
  298. _this.debug(msg);
  299. }
  300. _this._cleanUp();
  301. return typeof errorCallback === "function" ? errorCallback(msg) : void 0;
  302. };
  303. })(this);
  304. return this.ws.onopen = (function(_this) {
  305. return function() {
  306. if (typeof _this.debug === "function") {
  307. _this.debug('Web Socket Opened...');
  308. }
  309. headers["accept-version"] = Stomp.VERSIONS.supportedVersions();
  310. headers["heart-beat"] = [_this.heartbeat.outgoing, _this.heartbeat.incoming].join(',');
  311. return _this._transmit("CONNECT", headers);
  312. };
  313. })(this);
  314. };
  315. Client.prototype.disconnect = function(disconnectCallback, headers) {
  316. if (headers == null) {
  317. headers = {};
  318. }
  319. this._transmit("DISCONNECT", headers);
  320. this.ws.onclose = null;
  321. this.ws.close();
  322. this._cleanUp();
  323. return typeof disconnectCallback === "function" ? disconnectCallback() : void 0;
  324. };
  325. Client.prototype._cleanUp = function() {
  326. this.connected = false;
  327. if (this.pinger) {
  328. Stomp.clearInterval(this.pinger);
  329. }
  330. if (this.ponger) {
  331. return Stomp.clearInterval(this.ponger);
  332. }
  333. };
  334. Client.prototype.send = function(destination, headers, body) {
  335. if (headers == null) {
  336. headers = {};
  337. }
  338. if (body == null) {
  339. body = '';
  340. }
  341. headers.destination = destination;
  342. return this._transmit("SEND", headers, body);
  343. };
  344. Client.prototype.subscribe = function(destination, callback, headers) {
  345. var client;
  346. if (headers == null) {
  347. headers = {};
  348. }
  349. if (!headers.id) {
  350. headers.id = "sub-" + this.counter++;
  351. }
  352. headers.destination = destination;
  353. this.subscriptions[headers.id] = callback;
  354. this._transmit("SUBSCRIBE", headers);
  355. client = this;
  356. return {
  357. id: headers.id,
  358. unsubscribe: function() {
  359. return client.unsubscribe(headers.id);
  360. }
  361. };
  362. };
  363. Client.prototype.unsubscribe = function(id) {
  364. delete this.subscriptions[id];
  365. return this._transmit("UNSUBSCRIBE", {
  366. id: id
  367. });
  368. };
  369. Client.prototype.begin = function(transaction) {
  370. var client, txid;
  371. txid = transaction || "tx-" + this.counter++;
  372. this._transmit("BEGIN", {
  373. transaction: txid
  374. });
  375. client = this;
  376. return {
  377. id: txid,
  378. commit: function() {
  379. return client.commit(txid);
  380. },
  381. abort: function() {
  382. return client.abort(txid);
  383. }
  384. };
  385. };
  386. Client.prototype.commit = function(transaction) {
  387. return this._transmit("COMMIT", {
  388. transaction: transaction
  389. });
  390. };
  391. Client.prototype.abort = function(transaction) {
  392. return this._transmit("ABORT", {
  393. transaction: transaction
  394. });
  395. };
  396. Client.prototype.ack = function(messageID, subscription, headers) {
  397. if (headers == null) {
  398. headers = {};
  399. }
  400. headers["message-id"] = messageID;
  401. headers.subscription = subscription;
  402. return this._transmit("ACK", headers);
  403. };
  404. Client.prototype.nack = function(messageID, subscription, headers) {
  405. if (headers == null) {
  406. headers = {};
  407. }
  408. headers["message-id"] = messageID;
  409. headers.subscription = subscription;
  410. return this._transmit("NACK", headers);
  411. };
  412. return Client;
  413. })();
  414. Stomp = {
  415. VERSIONS: {
  416. V1_0: '1.0',
  417. V1_1: '1.1',
  418. V1_2: '1.2',
  419. supportedVersions: function() {
  420. return '1.1,1.0';
  421. }
  422. },
  423. client: function(url, protocols) {
  424. var klass, ws;
  425. if (protocols == null) {
  426. protocols = ['v10.stomp', 'v11.stomp'];
  427. }
  428. klass = Stomp.WebSocketClass || WebSocket;
  429. ws = new klass(url, protocols);
  430. return new Client(ws);
  431. },
  432. over: function(ws) {
  433. return new Client(ws);
  434. },
  435. Frame: Frame
  436. };
  437. if (typeof exports !== "undefined" && exports !== null) {
  438. exports.Stomp = Stomp;
  439. }
  440. if (typeof window !== "undefined" && window !== null) {
  441. Stomp.setInterval = function(interval, f) {
  442. return window.setInterval(f, interval);
  443. };
  444. Stomp.clearInterval = function(id) {
  445. return window.clearInterval(id);
  446. };
  447. window.Stomp = Stomp;
  448. } else if (!exports) {
  449. self.Stomp = Stomp;
  450. }
  451. }).call(this);