protocol.js 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356
  1. (function (exports, ByteArray, global) {
  2. var Protocol = exports;
  3. var PKG_HEAD_BYTES = 4;
  4. var MSG_FLAG_BYTES = 1;
  5. var MSG_ROUTE_CODE_BYTES = 2;
  6. var MSG_ID_MAX_BYTES = 5;
  7. var MSG_ROUTE_LEN_BYTES = 1;
  8. var MSG_ROUTE_CODE_MAX = 0xffff;
  9. var MSG_COMPRESS_ROUTE_MASK = 0x1;
  10. var MSG_TYPE_MASK = 0x7;
  11. var Package = Protocol.Package = {};
  12. var Message = Protocol.Message = {};
  13. Package.TYPE_HANDSHAKE = 1;
  14. Package.TYPE_HANDSHAKE_ACK = 2;
  15. Package.TYPE_HEARTBEAT = 3;
  16. Package.TYPE_DATA = 4;
  17. Package.TYPE_KICK = 5;
  18. Message.TYPE_REQUEST = 0;
  19. Message.TYPE_NOTIFY = 1;
  20. Message.TYPE_RESPONSE = 2;
  21. Message.TYPE_PUSH = 3;
  22. /**
  23. * pomele client encode
  24. * id message id;
  25. * route message route
  26. * msg message body
  27. * socketio current support string
  28. */
  29. Protocol.strencode = function(str) {
  30. var byteArray = new ByteArray(str.length * 3);
  31. var offset = 0;
  32. for(var i = 0; i < str.length; i++){
  33. var charCode = str.charCodeAt(i);
  34. var codes = null;
  35. if(charCode <= 0x7f){
  36. codes = [charCode];
  37. }else if(charCode <= 0x7ff){
  38. codes = [0xc0|(charCode>>6), 0x80|(charCode & 0x3f)];
  39. }else{
  40. codes = [0xe0|(charCode>>12), 0x80|((charCode & 0xfc0)>>6), 0x80|(charCode & 0x3f)];
  41. }
  42. for(var j = 0; j < codes.length; j++){
  43. byteArray[offset] = codes[j];
  44. ++offset;
  45. }
  46. }
  47. var _buffer = new ByteArray(offset);
  48. copyArray(_buffer, 0, byteArray, 0, offset);
  49. return _buffer;
  50. };
  51. /**
  52. * client decode
  53. * msg String data
  54. * return Message Object
  55. */
  56. Protocol.strdecode = function(buffer) {
  57. var bytes = new ByteArray(buffer);
  58. var array = [];
  59. var offset = 0;
  60. var charCode = 0;
  61. var end = bytes.length;
  62. var res = '';
  63. while(offset < end){
  64. if(bytes[offset] < 128){
  65. charCode = bytes[offset];
  66. offset += 1;
  67. }else if(bytes[offset] < 224){
  68. charCode = ((bytes[offset] & 0x3f)<<6) + (bytes[offset+1] & 0x3f);
  69. offset += 2;
  70. }else{
  71. charCode = ((bytes[offset] & 0x0f)<<12) + ((bytes[offset+1] & 0x3f)<<6) + (bytes[offset+2] & 0x3f);
  72. offset += 3;
  73. }
  74. array.push(charCode);
  75. if(array.length >= 5000){
  76. res += String.fromCharCode.apply(null, array);
  77. array = [];
  78. }
  79. }
  80. res +=String.fromCharCode.apply(null, array);
  81. return res;
  82. };
  83. /**
  84. * Package protocol encode.
  85. *
  86. * Pomelo package format:
  87. * +------+-------------+------------------+
  88. * | type | body length | body |
  89. * +------+-------------+------------------+
  90. *
  91. * Head: 4bytes
  92. * 0: package type,
  93. * 1 - handshake,
  94. * 2 - handshake ack,
  95. * 3 - heartbeat,
  96. * 4 - data
  97. * 5 - kick
  98. * 1 - 3: big-endian body length
  99. * Body: body length bytes
  100. *
  101. * @param {Number} type package type
  102. * @param {ByteArray} body body content in bytes
  103. * @return {ByteArray} new byte array that contains encode result
  104. */
  105. Package.encode = function(type, body){
  106. var length = body ? body.length : 0;
  107. var buffer = new ByteArray(PKG_HEAD_BYTES + length);
  108. var index = 0;
  109. buffer[index++] = type & 0xff;
  110. buffer[index++] = (length >> 16) & 0xff;
  111. buffer[index++] = (length >> 8) & 0xff;
  112. buffer[index++] = length & 0xff;
  113. if(body) {
  114. copyArray(buffer, index, body, 0, length);
  115. }
  116. return buffer;
  117. };
  118. /**
  119. * Package protocol decode.
  120. * See encode for package format.
  121. *
  122. * @param {ByteArray} buffer byte array containing package content
  123. * @return {Object} {type: package type, buffer: body byte array}
  124. */
  125. Package.decode = function(buffer){
  126. var offset = 0;
  127. var bytes = new ByteArray(buffer);
  128. var length = 0;
  129. var rs = [];
  130. while(offset < bytes.length) {
  131. var type = bytes[offset++];
  132. length = ((bytes[offset++]) << 16 | (bytes[offset++]) << 8 | bytes[offset++]) >>> 0;
  133. var body = length ? new ByteArray(length) : null;
  134. if(body){
  135. copyArray(body, 0, bytes, offset, length);
  136. }
  137. offset += length;
  138. rs.push({'type': type, 'body': body});
  139. }
  140. return rs.length === 1 ? rs[0]: rs;
  141. };
  142. /**
  143. * Message protocol encode.
  144. *
  145. * @param {Number} id message id
  146. * @param {Number} type message type
  147. * @param {Number} compressRoute whether compress route
  148. * @param {Number|String} route route code or route string
  149. * @param {Buffer} msg message body bytes
  150. * @return {Buffer} encode result
  151. */
  152. Message.encode = function(id, type, compressRoute, route, msg){
  153. // caculate message max length
  154. var idBytes = msgHasId(type) ? caculateMsgIdBytes(id) : 0;
  155. var msgLen = MSG_FLAG_BYTES + idBytes;
  156. if(msgHasRoute(type)) {
  157. if(compressRoute) {
  158. if(typeof route !== 'number'){
  159. throw new Error('error flag for number route!');
  160. }
  161. msgLen += MSG_ROUTE_CODE_BYTES;
  162. } else {
  163. msgLen += MSG_ROUTE_LEN_BYTES;
  164. if(route) {
  165. route = Protocol.strencode(route);
  166. if(route.length>255) {
  167. throw new Error('route maxlength is overflow');
  168. }
  169. msgLen += route.length;
  170. }
  171. }
  172. }
  173. if(msg) {
  174. msgLen += msg.length;
  175. }
  176. var buffer = new ByteArray(msgLen);
  177. var offset = 0;
  178. // add flag
  179. offset = encodeMsgFlag(type, compressRoute, buffer, offset);
  180. // add message id
  181. if(msgHasId(type)) {
  182. offset = encodeMsgId(id, buffer, offset);
  183. }
  184. // add route
  185. if(msgHasRoute(type)) {
  186. offset = encodeMsgRoute(compressRoute, route, buffer, offset);
  187. }
  188. // add body
  189. if(msg) {
  190. offset = encodeMsgBody(msg, buffer, offset);
  191. }
  192. return buffer;
  193. };
  194. /**
  195. * Message protocol decode.
  196. *
  197. * @param {Buffer|Uint8Array} buffer message bytes
  198. * @return {Object} message object
  199. */
  200. Message.decode = function(buffer) {
  201. var bytes = new ByteArray(buffer);
  202. var bytesLen = bytes.length || bytes.byteLength;
  203. var offset = 0;
  204. var id = 0;
  205. var route = null;
  206. // parse flag
  207. var flag = bytes[offset++];
  208. var compressRoute = flag & MSG_COMPRESS_ROUTE_MASK;
  209. var type = (flag >> 1) & MSG_TYPE_MASK;
  210. // parse id
  211. if(msgHasId(type)) {
  212. var m = parseInt(bytes[offset]);
  213. var i = 0;
  214. do{
  215. var m = parseInt(bytes[offset]);
  216. id = id + ((m & 0x7f) * Math.pow(2,(7*i)));
  217. offset++;
  218. i++;
  219. }while(m >= 128);
  220. }
  221. // parse route
  222. if(msgHasRoute(type)) {
  223. if(compressRoute) {
  224. route = (bytes[offset++]) << 8 | bytes[offset++];
  225. } else {
  226. var routeLen = bytes[offset++];
  227. if(routeLen) {
  228. route = new ByteArray(routeLen);
  229. copyArray(route, 0, bytes, offset, routeLen);
  230. route = Protocol.strdecode(route);
  231. } else {
  232. route = '';
  233. }
  234. offset += routeLen;
  235. }
  236. }
  237. // parse body
  238. var bodyLen = bytesLen - offset;
  239. var body = new ByteArray(bodyLen);
  240. copyArray(body, 0, bytes, offset, bodyLen);
  241. return {'id': id, 'type': type, 'compressRoute': compressRoute,
  242. 'route': route, 'body': body};
  243. };
  244. var copyArray = function(dest, doffset, src, soffset, length) {
  245. if('function' === typeof src.copy) {
  246. // Buffer
  247. src.copy(dest, doffset, soffset, soffset + length);
  248. } else {
  249. // Uint8Array
  250. for(var index=0; index<length; index++){
  251. dest[doffset++] = src[soffset++];
  252. }
  253. }
  254. };
  255. var msgHasId = function(type) {
  256. return type === Message.TYPE_REQUEST || type === Message.TYPE_RESPONSE;
  257. };
  258. var msgHasRoute = function(type) {
  259. return type === Message.TYPE_REQUEST || type === Message.TYPE_NOTIFY ||
  260. type === Message.TYPE_PUSH;
  261. };
  262. var caculateMsgIdBytes = function(id) {
  263. var len = 0;
  264. do {
  265. len += 1;
  266. id >>= 7;
  267. } while(id > 0);
  268. return len;
  269. };
  270. var encodeMsgFlag = function(type, compressRoute, buffer, offset) {
  271. if(type !== Message.TYPE_REQUEST && type !== Message.TYPE_NOTIFY &&
  272. type !== Message.TYPE_RESPONSE && type !== Message.TYPE_PUSH) {
  273. throw new Error('unkonw message type: ' + type);
  274. }
  275. buffer[offset] = (type << 1) | (compressRoute ? 1 : 0);
  276. return offset + MSG_FLAG_BYTES;
  277. };
  278. var encodeMsgId = function(id, buffer, offset) {
  279. do{
  280. var tmp = id % 128;
  281. var next = Math.floor(id/128);
  282. if(next !== 0){
  283. tmp = tmp + 128;
  284. }
  285. buffer[offset++] = tmp;
  286. id = next;
  287. } while(id !== 0);
  288. return offset;
  289. };
  290. var encodeMsgRoute = function(compressRoute, route, buffer, offset) {
  291. if (compressRoute) {
  292. if(route > MSG_ROUTE_CODE_MAX){
  293. throw new Error('route number is overflow');
  294. }
  295. buffer[offset++] = (route >> 8) & 0xff;
  296. buffer[offset++] = route & 0xff;
  297. } else {
  298. if(route) {
  299. buffer[offset++] = route.length & 0xff;
  300. copyArray(buffer, offset, route, 0, route.length);
  301. offset += route.length;
  302. } else {
  303. buffer[offset++] = 0;
  304. }
  305. }
  306. return offset;
  307. };
  308. var encodeMsgBody = function(msg, buffer, offset) {
  309. copyArray(buffer, offset, msg, 0, msg.length);
  310. return offset + msg.length;
  311. };
  312. module.exports = Protocol;
  313. })(module.exports, Buffer, this);