protocol.js 9.1 KB

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