stomp.js 15 KB

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