123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356 |
- (function (exports, ByteArray, global) {
- var Protocol = exports;
- var PKG_HEAD_BYTES = 4;
- var MSG_FLAG_BYTES = 1;
- var MSG_ROUTE_CODE_BYTES = 2;
- var MSG_ID_MAX_BYTES = 5;
- var MSG_ROUTE_LEN_BYTES = 1;
- var MSG_ROUTE_CODE_MAX = 0xffff;
- var MSG_COMPRESS_ROUTE_MASK = 0x1;
- var MSG_TYPE_MASK = 0x7;
- var Package = Protocol.Package = {};
- var Message = Protocol.Message = {};
- Package.TYPE_HANDSHAKE = 1;
- Package.TYPE_HANDSHAKE_ACK = 2;
- Package.TYPE_HEARTBEAT = 3;
- Package.TYPE_DATA = 4;
- Package.TYPE_KICK = 5;
- Message.TYPE_REQUEST = 0;
- Message.TYPE_NOTIFY = 1;
- Message.TYPE_RESPONSE = 2;
- Message.TYPE_PUSH = 3;
- /**
- * pomele client encode
- * id message id;
- * route message route
- * msg message body
- * socketio current support string
- */
- Protocol.strencode = function(str) {
- var byteArray = new ByteArray(str.length * 3);
- var offset = 0;
- for(var i = 0; i < str.length; i++){
- var charCode = str.charCodeAt(i);
- var codes = null;
- if(charCode <= 0x7f){
- codes = [charCode];
- }else if(charCode <= 0x7ff){
- codes = [0xc0|(charCode>>6), 0x80|(charCode & 0x3f)];
- }else{
- codes = [0xe0|(charCode>>12), 0x80|((charCode & 0xfc0)>>6), 0x80|(charCode & 0x3f)];
- }
- for(var j = 0; j < codes.length; j++){
- byteArray[offset] = codes[j];
- ++offset;
- }
- }
- var _buffer = new ByteArray(offset);
- copyArray(_buffer, 0, byteArray, 0, offset);
- return _buffer;
- };
- /**
- * client decode
- * msg String data
- * return Message Object
- */
- Protocol.strdecode = function(buffer) {
- var bytes = new ByteArray(buffer);
- var array = [];
- var offset = 0;
- var charCode = 0;
- var end = bytes.length;
- var res = '';
- while(offset < end){
- if(bytes[offset] < 128){
- charCode = bytes[offset];
- offset += 1;
- }else if(bytes[offset] < 224){
- charCode = ((bytes[offset] & 0x3f)<<6) + (bytes[offset+1] & 0x3f);
- offset += 2;
- }else{
- charCode = ((bytes[offset] & 0x0f)<<12) + ((bytes[offset+1] & 0x3f)<<6) + (bytes[offset+2] & 0x3f);
- offset += 3;
- }
- array.push(charCode);
- if(array.length >= 5000){
- res += String.fromCharCode.apply(null, array);
- array = [];
- }
- }
- res +=String.fromCharCode.apply(null, array);
- return res;
- };
- /**
- * Package protocol encode.
- *
- * Pomelo package format:
- * +------+-------------+------------------+
- * | type | body length | body |
- * +------+-------------+------------------+
- *
- * Head: 4bytes
- * 0: package type,
- * 1 - handshake,
- * 2 - handshake ack,
- * 3 - heartbeat,
- * 4 - data
- * 5 - kick
- * 1 - 3: big-endian body length
- * Body: body length bytes
- *
- * @param {Number} type package type
- * @param {ByteArray} body body content in bytes
- * @return {ByteArray} new byte array that contains encode result
- */
- Package.encode = function(type, body){
- var length = body ? body.length : 0;
- var buffer = new ByteArray(PKG_HEAD_BYTES + length);
- var index = 0;
- buffer[index++] = type & 0xff;
- buffer[index++] = (length >> 16) & 0xff;
- buffer[index++] = (length >> 8) & 0xff;
- buffer[index++] = length & 0xff;
- if(body) {
- copyArray(buffer, index, body, 0, length);
- }
- return buffer;
- };
- /**
- * Package protocol decode.
- * See encode for package format.
- *
- * @param {ByteArray} buffer byte array containing package content
- * @return {Object} {type: package type, buffer: body byte array}
- */
- Package.decode = function(buffer){
- var offset = 0;
- var bytes = new ByteArray(buffer);
- var length = 0;
- var rs = [];
- while(offset < bytes.length) {
- var type = bytes[offset++];
- length = ((bytes[offset++]) << 16 | (bytes[offset++]) << 8 | bytes[offset++]) >>> 0;
- var body = length ? new ByteArray(length) : null;
- if(body){
- copyArray(body, 0, bytes, offset, length);
- }
- offset += length;
- rs.push({'type': type, 'body': body});
- }
- return rs.length === 1 ? rs[0]: rs;
- };
- /**
- * Message protocol encode.
- *
- * @param {Number} id message id
- * @param {Number} type message type
- * @param {Number} compressRoute whether compress route
- * @param {Number|String} route route code or route string
- * @param {Buffer} msg message body bytes
- * @return {Buffer} encode result
- */
- Message.encode = function(id, type, compressRoute, route, msg){
- // caculate message max length
- var idBytes = msgHasId(type) ? caculateMsgIdBytes(id) : 0;
- var msgLen = MSG_FLAG_BYTES + idBytes;
- if(msgHasRoute(type)) {
- if(compressRoute) {
- if(typeof route !== 'number'){
- throw new Error('error flag for number route!');
- }
- msgLen += MSG_ROUTE_CODE_BYTES;
- } else {
- msgLen += MSG_ROUTE_LEN_BYTES;
- if(route) {
- route = Protocol.strencode(route);
- if(route.length>255) {
- throw new Error('route maxlength is overflow');
- }
- msgLen += route.length;
- }
- }
- }
- if(msg) {
- msgLen += msg.length;
- }
- var buffer = new ByteArray(msgLen);
- var offset = 0;
- // add flag
- offset = encodeMsgFlag(type, compressRoute, buffer, offset);
- // add message id
- if(msgHasId(type)) {
- offset = encodeMsgId(id, buffer, offset);
- }
- // add route
- if(msgHasRoute(type)) {
- offset = encodeMsgRoute(compressRoute, route, buffer, offset);
- }
- // add body
- if(msg) {
- offset = encodeMsgBody(msg, buffer, offset);
- }
- return buffer;
- };
- /**
- * Message protocol decode.
- *
- * @param {Buffer|Uint8Array} buffer message bytes
- * @return {Object} message object
- */
- Message.decode = function(buffer) {
- var bytes = new ByteArray(buffer);
- var bytesLen = bytes.length || bytes.byteLength;
- var offset = 0;
- var id = 0;
- var route = null;
- // parse flag
- var flag = bytes[offset++];
- var compressRoute = flag & MSG_COMPRESS_ROUTE_MASK;
- var type = (flag >> 1) & MSG_TYPE_MASK;
- // parse id
- if(msgHasId(type)) {
- var m = parseInt(bytes[offset]);
- var i = 0;
- do{
- var m = parseInt(bytes[offset]);
- id = id + ((m & 0x7f) * Math.pow(2,(7*i)));
- offset++;
- i++;
- }while(m >= 128);
- }
- // parse route
- if(msgHasRoute(type)) {
- if(compressRoute) {
- route = (bytes[offset++]) << 8 | bytes[offset++];
- } else {
- var routeLen = bytes[offset++];
- if(routeLen) {
- route = new ByteArray(routeLen);
- copyArray(route, 0, bytes, offset, routeLen);
- route = Protocol.strdecode(route);
- } else {
- route = '';
- }
- offset += routeLen;
- }
- }
- // parse body
- var bodyLen = bytesLen - offset;
- var body = new ByteArray(bodyLen);
- copyArray(body, 0, bytes, offset, bodyLen);
- return {'id': id, 'type': type, 'compressRoute': compressRoute,
- 'route': route, 'body': body};
- };
- var copyArray = function(dest, doffset, src, soffset, length) {
- if('function' === typeof src.copy) {
- // Buffer
- src.copy(dest, doffset, soffset, soffset + length);
- } else {
- // Uint8Array
- for(var index=0; index<length; index++){
- dest[doffset++] = src[soffset++];
- }
- }
- };
- var msgHasId = function(type) {
- return type === Message.TYPE_REQUEST || type === Message.TYPE_RESPONSE;
- };
- var msgHasRoute = function(type) {
- return type === Message.TYPE_REQUEST || type === Message.TYPE_NOTIFY ||
- type === Message.TYPE_PUSH;
- };
- var caculateMsgIdBytes = function(id) {
- var len = 0;
- do {
- len += 1;
- id >>= 7;
- } while(id > 0);
- return len;
- };
- var encodeMsgFlag = function(type, compressRoute, buffer, offset) {
- if(type !== Message.TYPE_REQUEST && type !== Message.TYPE_NOTIFY &&
- type !== Message.TYPE_RESPONSE && type !== Message.TYPE_PUSH) {
- throw new Error('unkonw message type: ' + type);
- }
- buffer[offset] = (type << 1) | (compressRoute ? 1 : 0);
- return offset + MSG_FLAG_BYTES;
- };
- var encodeMsgId = function(id, buffer, offset) {
- do{
- var tmp = id % 128;
- var next = Math.floor(id/128);
- if(next !== 0){
- tmp = tmp + 128;
- }
- buffer[offset++] = tmp;
- id = next;
- } while(id !== 0);
- return offset;
- };
- var encodeMsgRoute = function(compressRoute, route, buffer, offset) {
- if (compressRoute) {
- if(route > MSG_ROUTE_CODE_MAX){
- throw new Error('route number is overflow');
- }
- buffer[offset++] = (route >> 8) & 0xff;
- buffer[offset++] = route & 0xff;
- } else {
- if(route) {
- buffer[offset++] = route.length & 0xff;
- copyArray(buffer, offset, route, 0, route.length);
- offset += route.length;
- } else {
- buffer[offset++] = 0;
- }
- }
- return offset;
- };
- var encodeMsgBody = function(msg, buffer, offset) {
- copyArray(buffer, offset, msg, 0, msg.length);
- return offset + msg.length;
- };
- module.exports = Protocol;
- })(module.exports, Buffer, this);
|