hos-client.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580
  1. function hashSize(obj) {
  2. var size = 0, key;
  3. for (key in obj) {
  4. if (obj.hasOwnProperty(key)) size++;
  5. }
  6. return size;
  7. }
  8. function uuid() {
  9. //http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript
  10. return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
  11. var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
  12. return v.toString(16);
  13. });
  14. }
  15. function string2Buffer(str) {
  16. var buf = new ArrayBuffer(str.length * 2); // 2 bytes for each char
  17. var bufView = new Int16Array(buf);
  18. for (var i = 0, strLen = str.length; i < strLen; i++) {
  19. bufView[i] = str.charCodeAt(i);
  20. }
  21. return bufView;
  22. }
  23. function buffer2String(buf) {
  24. return String.fromCharCode.apply(null, buf);
  25. }
  26. //First, checks if it isn't implemented yet.
  27. if (!String.prototype.format) {
  28. String.prototype.format = function () {
  29. var args = arguments;
  30. return this.replace(/{(\d+)}/g, function (match, number) {
  31. return typeof args[number] != 'undefined'
  32. ? args[number]
  33. : match
  34. ;
  35. });
  36. };
  37. }
  38. function inherits(ctor, superCtor) {
  39. ctor.super_ = superCtor;
  40. ctor.prototype = Object.create(superCtor.prototype, {
  41. constructor: {
  42. value: ctor,
  43. enumerable: false,
  44. writable: true,
  45. configurable: true
  46. }
  47. });
  48. };
  49. // /////////////////////////////////////////////////////////////////
  50. function Meta(meta) {
  51. this.status = null;
  52. this.method = "GET";
  53. this.url = "/";
  54. this.path = null;
  55. this.params = null;
  56. if (!meta || meta == "") return;
  57. var blocks = meta.split(" ");
  58. var method = blocks[0];
  59. if (Meta.HttpMethod.indexOf(method) == -1) {
  60. this.status = blocks[1];
  61. return;
  62. }
  63. this.url = blocks[1];
  64. this.decodeUrl(this.url);
  65. }
  66. Meta.HttpMethod = ["GET", "POST", "PUT", "DELETE", "HEAD", "OPTIONS"];
  67. Meta.HttpStatus = {
  68. "200": "OK",
  69. "201": "Created",
  70. "202": "Accepted",
  71. "204": "No Content",
  72. "206": "Partial Content",
  73. "301": "Moved Permanently",
  74. "304": "Not Modified",
  75. "400": "Bad Request",
  76. "401": "Unauthorized",
  77. "403": "Forbidden",
  78. "404": "Not Found",
  79. "405": "Method Not Allowed",
  80. "416": "Requested Range Not Satisfiable",
  81. "500": "Internal Server Error"
  82. };
  83. Meta.prototype.toString = function () {
  84. if (this.status) {
  85. var desc = Meta.HttpStatus[this.status];
  86. if (!desc) desc = "Unknown Status";
  87. return "HTTP/1.1 {0} {1}".format(this.status, desc);
  88. }
  89. return "{0} {1} HTTP/1.1".format(this.method, this.url);
  90. };
  91. Meta.prototype.getParam = function (key) {
  92. if (!this.params) {
  93. return undefined;
  94. }
  95. return this.params[key];
  96. };
  97. Meta.prototype.setParam = function (key, val) {
  98. if (!this.params) {
  99. this.params = {};
  100. }
  101. this.params[key] = val;
  102. };
  103. Meta.prototype.setUrl = function (url) {
  104. this.url = url;
  105. this.decodeUrl(url);
  106. }
  107. Meta.prototype.decodeUrl = function (cmdStr) {
  108. var idx = cmdStr.indexOf("?");
  109. if (idx < 0) {
  110. this.path = cmdStr;
  111. } else {
  112. this.path = cmdStr.substring(0, idx);
  113. }
  114. if (this.path.charAt(0) == '/') {
  115. this.path = this.path.substring(1);
  116. }
  117. if (idx < 0) return;
  118. var paramStr = cmdStr.substring(idx + 1);
  119. this.params = {};
  120. var kvs = paramStr.split("&");
  121. for (var i in kvs) {
  122. var kv = kvs[i];
  123. idx = kv.indexOf("=");
  124. if (idx < 0) {
  125. util.debug("omit: " + kv);
  126. continue;
  127. }
  128. var key = kv.substring(0, idx);
  129. var val = kv.substring(idx + 1);
  130. this.params[key] = val;
  131. }
  132. };
  133. //HTTP Message
  134. function Message(body) {
  135. this.meta = new Meta();
  136. this.head = {};
  137. this.setBody(body);
  138. }
  139. Message.HEARTBEAT = "heartbeat";
  140. Message.REMOTE_ADDR = "remote-addr";
  141. Message.ENCODING = "encoding";
  142. Message.CMD = "cmd";
  143. Message.BROKER = "broker";
  144. Message.TOPIC = "topic";
  145. Message.MQ = "mq";
  146. Message.ID = "id";
  147. Message.ACK = "ack";
  148. Message.SENDER = "sender";
  149. Message.RECVER = "recver";
  150. Message.ORIGIN_URL = "origin_url";
  151. Message.ORIGIN_ID = "rawid";
  152. Message.ORIGIN_STATUS = "reply_code"
  153. Message.prototype.getHead = function (key) {
  154. return this.head[key];
  155. };
  156. Message.prototype.setHead = function (key, val) {
  157. this.head[key] = val;
  158. };
  159. Message.prototype.removeHead = function (key) {
  160. delete this.head[key];
  161. };
  162. Message.prototype.getMq = function () {
  163. return this.getHead(Message.MQ);
  164. };
  165. Message.prototype.setMq = function (val) {
  166. this.setHead(Message.MQ, val);
  167. };
  168. Message.prototype.getId = function () {
  169. return this.getHead(Message.ID);
  170. };
  171. Message.prototype.setId = function (val) {
  172. this.setHead(Message.ID, val);
  173. };
  174. Message.prototype.getTopic = function () {
  175. return this.getHead(Message.TOPIC);
  176. };
  177. Message.prototype.setTopic = function (val) {
  178. this.setHead(Message.TOPIC, val);
  179. };
  180. Message.prototype.getEncoding = function () {
  181. return this.getHead(Message.ENCODING);
  182. };
  183. Message.prototype.setEncoding = function (val) {
  184. this.setHead(Message.ENCODING, val);
  185. };
  186. Message.prototype.isAck = function () {
  187. var ack = this.getHead(Message.ACK);
  188. if (!ack) return true;//default to true
  189. return ack == '1';
  190. };
  191. Message.prototype.setAck = function (val) {
  192. this.setHead(Message.ACK, val);
  193. };
  194. Message.prototype.getCmd = function () {
  195. return this.getHead(Message.CMD);
  196. };
  197. Message.prototype.setCmd = function (val) {
  198. this.setHead(Message.CMD, val);
  199. };
  200. Message.prototype.getSender = function () {
  201. return this.getHead(Message.SENDER);
  202. };
  203. Message.prototype.setSender = function (val) {
  204. this.setHead(Message.SENDER, val);
  205. };
  206. Message.prototype.getRecver = function () {
  207. return this.getHead(Message.RECVER);
  208. };
  209. Message.prototype.setRecver = function (val) {
  210. this.setHead(Message.RECVER, val);
  211. };
  212. Message.prototype.getOriginUrl = function () {
  213. return this.getHead(Message.ORIGIN_URL);
  214. };
  215. Message.prototype.setOriginUrl = function (val) {
  216. this.setHead(Message.ORIGIN_URL, val);
  217. };
  218. Message.prototype.getOriginStatus = function () {
  219. return this.getHead(Message.ORIGIN_STATUS);
  220. };
  221. Message.prototype.setOriginStatus = function (val) {
  222. this.setHead(Message.ORIGIN_STATUS, val);
  223. };
  224. Message.prototype.getOriginId = function () {
  225. return this.getHead(Message.ORIGIN_ID);
  226. };
  227. Message.prototype.setOriginId = function (val) {
  228. this.setHead(Message.ORIGIN_ID, val);
  229. };
  230. Message.prototype.getPath = function () {
  231. return this.meta.path;
  232. };
  233. Message.prototype.getUrl = function () {
  234. return this.meta.url;
  235. };
  236. Message.prototype.setUrl = function (url) {
  237. this.meta.status = null;
  238. return this.meta.setUrl(url);
  239. };
  240. Message.prototype.getStatus = function () {
  241. return this.meta.status;
  242. };
  243. Message.prototype.setStatus = function (val) {
  244. this.meta.status = val;
  245. };
  246. Message.prototype.getBodyString = function () {
  247. if (!this.body) return null;
  248. return buffer2String(this.body);
  249. };
  250. Message.prototype.getBody = function () {
  251. if (!this.body) return null;
  252. return this.body;
  253. };
  254. Message.prototype.setBody = function (val) {
  255. if (val === undefined) return;
  256. if (val instanceof Int16Array) {
  257. this.body = val;
  258. } else {
  259. this.body = string2Buffer(val);
  260. }
  261. this.setHead('content-length', this.body.length);
  262. };
  263. Message.prototype.setJsonBody = function (json) {
  264. this.setBody(json);
  265. this.setHead('content-type', 'application/json');
  266. }
  267. Message.prototype.setBodyFormat = function (format) {
  268. var args = Array.prototype.slice.call(arguments, 1);
  269. var body = format.replace(/{(\d+)}/g, function (match, number) {
  270. return typeof args[number] != 'undefined'
  271. ? args[number]
  272. : match
  273. ;
  274. });
  275. this.setBody(body);
  276. };
  277. Message.prototype.isStatus200 = function () {
  278. return "200" == this.getStatus();
  279. };
  280. Message.prototype.isStatus404 = function () {
  281. return "404" == this.getStatus();
  282. };
  283. Message.prototype.isStatus500 = function () {
  284. return "500" == this.getStatus();
  285. };
  286. Message.prototype.toString = function () {
  287. var lines = new Array();
  288. lines.push("{0}".format(this.meta.toString()));
  289. for (var key in this.head) {
  290. lines.push("{0}: {1}".format(key, this.head[key]));
  291. }
  292. var bodyLen = 0;
  293. if (this.body) {
  294. bodyLen = this.body.length;
  295. }
  296. var lenKey = "content-length";
  297. if (!(lenKey in this.head)) {
  298. lines.push("{0}: {1}".format(lenKey, bodyLen));
  299. }
  300. var bodyString = "\r\n";
  301. if (bodyLen > 0) {
  302. bodyString += buffer2String(this.body);
  303. }
  304. lines.push(bodyString);
  305. return lines.join("\r\n");
  306. };
  307. Message.parse = function (str) {
  308. var blocks = str.split("\r\n");
  309. var lines = [];
  310. for (var i in blocks) {
  311. var line = blocks[i];
  312. if (line == '') continue;
  313. lines.push(line);
  314. }
  315. var lenKey = "content-length";
  316. var lenVal = 0;
  317. var msg = new Message();
  318. msg.meta = new Meta(lines[0]);
  319. for (var i = 1; i < lines.length; i++) {
  320. var line = lines[i];
  321. if (i == lines.length - 1) {
  322. if (lenVal > 0) {
  323. msg.setBody(line);
  324. continue;
  325. }
  326. }
  327. var p = line.indexOf(":");
  328. if (p == -1) continue;
  329. var key = line.substring(0, p).trim().toLowerCase();
  330. var val = line.substring(p + 1).trim();
  331. if (key == lenKey) {
  332. lenVal = val;
  333. }
  334. msg.setHead(key, val);
  335. }
  336. return msg;
  337. };
  338. function Ticket(reqMsg, callback) {
  339. this.id = uuid();
  340. this.request = reqMsg;
  341. this.response = null;
  342. this.callback = callback;
  343. reqMsg.setId(this.id);
  344. }
  345. var WebSocket = window.WebSocket;
  346. if (!WebSocket) {
  347. WebSocket = window.MozWebSocket;
  348. }
  349. function MessageClient(address) {
  350. this.address = address;
  351. this.autoReconnect = true;
  352. this.reconnectInterval = 3000;
  353. this.ticketTable = {};
  354. }
  355. MessageClient.prototype.connect = function (connectedHandler) {
  356. console.log("Trying to connect to " + this.address);
  357. this.socket = new WebSocket(this.address);
  358. var client = this;
  359. this.socket.onopen = function (event) {
  360. console.log("Connected to " + client.address);
  361. if (connectedHandler) {
  362. connectedHandler(event);
  363. }
  364. client.heartbeatInterval = setInterval(function () {
  365. var msg = new Message();
  366. msg.setCmd(Message.HEARTBEAT);
  367. client.invokeAsync(msg);
  368. }, 300 * 1000);
  369. };
  370. this.socket.onclose = function (event) {
  371. clearInterval(client.heartbeatInterval);
  372. setTimeout(function () {
  373. try {
  374. client.connect(connectedHandler);
  375. } catch (e) {
  376. }//ignore
  377. }, client.reconnectInterval);
  378. };
  379. this.socket.onmessage = function (event) {
  380. var msg = Message.parse(event.data);
  381. var msgid = msg.getId();
  382. var ticket = client.ticketTable[msgid];
  383. if (ticket) {
  384. ticket.response = msg;
  385. if (ticket.callback) {
  386. ticket.callback(msg);
  387. }
  388. delete client.ticketTable[msgid];
  389. } else {
  390. console.log("Warn: drop message\n" + msg.toString());
  391. }
  392. }
  393. this.socket.onerror = function (data) {
  394. console.log("Error: " + data);
  395. }
  396. }
  397. MessageClient.prototype.invokeAsync = function (msg, callback) {
  398. if (this.socket.readyState != WebSocket.OPEN) {
  399. console.log("socket is not open, invalid");
  400. return;
  401. }
  402. if (callback) {
  403. var ticket = new Ticket(msg, callback);
  404. this.ticketTable[ticket.id] = ticket;
  405. }
  406. this.socket.send(msg);
  407. };
  408. function Proto() {
  409. }
  410. Proto.Produce = "produce";
  411. Proto.Consume = "consume";
  412. Proto.Route = "route";
  413. Proto.Heartbeat = "heartbeat";
  414. Proto.Admin = "admin";
  415. Proto.CreateMQ = "create_mq";
  416. function MqMode() {
  417. }
  418. MqMode.MQ = 1 << 0;
  419. MqMode.PubSub = 1 << 1;
  420. MqMode.Memory = 1 << 2;
  421. var Broker = MessageClient;
  422. //define more brokers
  423. function MqAdmin(broker, mq) {
  424. this.broker = broker;
  425. this.mq = mq;
  426. this.mode = 0;
  427. var args = Array.prototype.slice.call(arguments, 2);
  428. for (var i in args) {
  429. this.mode |= args[i];
  430. }
  431. }
  432. MqAdmin.prototype.createMq = function (callback) {
  433. var params = {};
  434. params["mq_name"] = this.mq;
  435. params["mq_mode"] = "" + this.mode;
  436. var msg = new Message();
  437. msg.setCmd(Proto.CreateMQ);
  438. msg.setHead("mq_name", this.mq);
  439. msg.setHead("mq_mode", "" + this.mode);
  440. this.broker.invokeAsync(msg, callback);
  441. };
  442. function Producer(broker, mq) {
  443. MqAdmin.call(this, broker, mq);
  444. }
  445. inherits(Producer, MqAdmin)
  446. Producer.prototype.sendAsync = function (msg, callback) {
  447. msg.setCmd(Proto.Produce);
  448. msg.setMq(this.mq);
  449. this.broker.invokeAsync(msg, callback);
  450. };
  451. function Consumer(broker, mq) {
  452. MqAdmin.call(this, broker, mq);
  453. }
  454. inherits(Consumer, MqAdmin);
  455. Consumer.prototype.take = function (callback) {
  456. var msg = new Message();
  457. msg.setCmd(Proto.Consume);
  458. msg.setMq(this.mq);
  459. if (this.topic) msg.setTopic(this.topic);
  460. var consumer = this;
  461. this.broker.invokeAsync(msg, function (res) {
  462. if (res.isStatus404()) {
  463. consumer.createMq(function (res) {
  464. if (res.isStatus200()) {
  465. console.log(consumer.mq + " created");
  466. }
  467. consumer.take(callback);
  468. });
  469. return;
  470. }
  471. if (res.isStatus200()) {
  472. var originUrl = res.getOriginUrl();
  473. var id = res.getOriginId();
  474. res.removeHead(Message.ORIGIN_ID);
  475. if (originUrl == null) {
  476. originUrl = "/";
  477. } else {
  478. res.removeHead(Message.ORIGIN_URL);
  479. }
  480. res.setId(id);
  481. res.setUrl(originUrl);
  482. try {
  483. callback(res);
  484. } catch (error) {
  485. console.log(error);
  486. }
  487. }
  488. return consumer.take(callback);
  489. });
  490. };
  491. Consumer.prototype.route = function (msg) {
  492. msg.setCmd(Proto.Route);
  493. msg.setAck(false);
  494. this.broker.invokeAsync(msg);
  495. };