From 5da9d4ff47411d2eadd7f8d33b4694b0d1edecbb Mon Sep 17 00:00:00 2001 From: tangzhi Date: Mon, 4 Jan 2016 17:35:00 +0800 Subject: [PATCH 1/3] add readCall/writeReply in V1.0 --- lib/object.js | 6 +++ lib/v1/decoder.js | 49 +++++++++++++++++ lib/v1/encoder.js | 72 +++++++++++++++++++++++++ test/call-reply.test.js | 113 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 240 insertions(+) create mode 100644 test/call-reply.test.js diff --git a/lib/object.js b/lib/object.js index 4f70ab3..42e127a 100644 --- a/lib/object.js +++ b/lib/object.js @@ -83,6 +83,12 @@ var SERIALIZER_MAP = exports.SERIALIZER_MAP = {}; SERIALIZER_MAP[t] = 'Date'; }); +[ + 'reply' +].forEach(function (t) { + SERIALIZER_MAP[t] = "Reply"; +}); + // http://www.devthought.com/2011/12/22/a-string-is-not-an-error/ function JavaExceptionError(obj, withType) { Error.call(this); diff --git a/lib/v1/decoder.js b/lib/v1/decoder.js index bc04fc6..09e7db7 100644 --- a/lib/v1/decoder.js +++ b/lib/v1/decoder.js @@ -561,6 +561,55 @@ utils.addByteCodes(BYTE_CODES, [ 0x52, ], 'readRef'); +/** + * read an call from buffer + * + * v1.0 + * ``` + * call ::= c(x63) x01 x00 header* m b16 b8 method-string (object)* z + * ``` + * + * @return {Object} + * @api public + */ +proto.readCall = function(withType) { + this._checkLabel('readCall', 'c'); + + if (this.byteBuffer.get() !== 0x01 || this.byteBuffer.get() !== 0x00) { + new TypeError('hessian call error, expect code: 0x01 0x00'); + } + + + var header = {}; + var args = []; + var result = {header: header, arguments: args}; + + while (this.byteBuffer.getChar(this.byteBuffer.position()) === 'H') { + this.byteBuffer.position(this.byteBuffer.position() + 1); //skip 'H' char + var key = this.byteBuffer.readRawString( this.byteBuffer.getUInt16() ); + header[key] = this.read(withType); + } + + this._checkLabel('readCall.method', 'm'); + var methodLength = this.byteBuffer.getUInt16(); + result.method = this.byteBuffer.readRawString(methodLength); + + // + var label = this.byteBuffer.getChar(this.byteBuffer.position()); + while (label !== 'z') { + args.push(this.read(withType)); + label = this.byteBuffer.getChar(this.byteBuffer.position()); + } + // skip 'z' char + this.byteBuffer.position(this.byteBuffer.position() + 1); + + return result; +}; + +utils.addByteCodes(BYTE_CODES, [ + 0x63, +], 'readCall'); + /** * read any thing * diff --git a/lib/v1/encoder.js b/lib/v1/encoder.js index cb7a4b1..d948c94 100644 --- a/lib/v1/encoder.js +++ b/lib/v1/encoder.js @@ -512,4 +512,76 @@ proto.write = function (val) { return this[method](val); }; +/** + * encode fault + * @param {fault} The defined fields are code, message, and detail. code is one of a short list of strings defined below. message is a user-readable message. detail is an object representing the exception. In Java, detail will be a serialized exception. + * @return {this} + */ +proto._writeFault = function(fault) { + this._assertType('writeFault', 'object', fault); + + this._assertType('writeFault', 'string', fault.message); + + var code = fault.code || 'ServiceException'; + + this.byteBuffer.putChar('f'); + this.writeString('code'); + this.writeString(code); + this.writeString('message'); + this.writeString(fault.message); + if (fault.detail) { + this.writeString('detail'); + this.writeObject(fault.detail); + } + return this; +}; + +proto._writeHeader = function(header) { + this._assertType('writeHeader', 'object', header); + this.byteBuffer.putChar('H'); + + if (SUPPORT_ES6_MAP && header instanceof Map) { + header.forEach(function (value, key) { + this.write(key); + this.write(value); + }, this); + } else { + var keys = Object.keys(header).sort(); + for (var i = 0; i < keys.length; i++) { + var k = keys[i]; + this.writeString(k); + this.write(header[k]); + } + } + this.byteBuffer.putChar('z'); + return this; +}; + +/** + * encode reply + * v1.0 + * ``` + * valid-reply ::= r x01 x00 header* object z + * fault-reply ::= r x01 x00 header* fault z + * ``` + * @param {reply} + * @return {this} + */ +proto.writeReply = function(reply) { + this.byteBuffer.putChar('r').put(0x01).put(0x00); + if (reply.header) { + this._writeHeader(reply.header); + } + + if (reply.fault) { + this._writeFault(reply.fault); + }else { + this.write(reply.value); + } + + this.byteBuffer.putChar('z'); + return this; +}; + + module.exports = Encoder; diff --git a/test/call-reply.test.js b/test/call-reply.test.js new file mode 100644 index 0000000..9a3b287 --- /dev/null +++ b/test/call-reply.test.js @@ -0,0 +1,113 @@ +/*! + * hessian.js - test/call-reply.test.js + * + * Copyright(c) 2016 + * MIT Licensed + * + * Authors: + * tangzhi (http://github.com/tangzhi) + */ + +"use strict"; + +/** + * Module dependencies. + */ + +var should = require('should'); +var hessian = require('../'); +var utils = require('./utils'); + +describe('call-reply.test.js', function () { + //c x01 x00 + // m x00 x04 add2 + // I x00 x00 x00 x02 + // I x00 x00 x00 x03 + // z + var simpleBuffer = Buffer.concat([ + new Buffer(['c'.charCodeAt(0), 0x01, 0x00]), + new Buffer(['m'.charCodeAt(0), 0x00, 0x04]), new Buffer('add2'), + new Buffer(['I'.charCodeAt(0), 0x00, 0x00, 0x00, 0x02]), + new Buffer(['I'.charCodeAt(0), 0x00, 0x00, 0x00, 0x03]), + new Buffer('z') + ]); + + it('should read call [add2(2,3)]', function () { + hessian.decode(simpleBuffer).should.eql({ + method: 'add2', + header: {}, + arguments: [2,3] + }); + }); + + it('should write reply 5', function() { + var buf = hessian.encode({$class:"reply", $:{value: 5}}); + buf.should.be.a.Buffer; + // r x01 x00 + // I x00 x00 x00 x05 + // z + buf.should.eql(Buffer.concat([ + new Buffer(['r'.charCodeAt(0), 0x01, 0x00]), + new Buffer(['I'.charCodeAt(0), 0x00, 0x00, 0x00, 0x05]), + new Buffer('z') + ])); + }); + + it('should write reply fault', function() { + var fault = { + message: 'time out' + }; + var buf = hessian.encode({$class:"reply", $:{fault: fault}}); + buf.should.be.a.Buffer; + // r x01 x00 + // f + // S x00 x04 code + // S x00 x10 ServiceException + // S x00 x07 message + // S x00 x08 time out + // z + buf.should.eql(Buffer.concat([ + new Buffer(['r'.charCodeAt(0), 0x01, 0x00]), + new Buffer('f'), + new Buffer(['S'.charCodeAt(0), 0x00, 0x04]), new Buffer('code'), + new Buffer(['S'.charCodeAt(0), 0x00, 0x10]), new Buffer('ServiceException'), + new Buffer(['S'.charCodeAt(0), 0x00, 0x07]), new Buffer('message'), + new Buffer(['S'.charCodeAt(0), 0x00, 0x08]), new Buffer('time out'), + new Buffer('z') + ])); + }); + + it('should write reply fault with detail', function() { + var fault = { + message : 'time out', + detail : { + $class : 'java.io.FileNotFoundException', + $: {} + } + }; + var buf = hessian.encode({$class:"reply", $:{fault: fault}}); + buf.should.be.a.Buffer; + // r x01 x00 + // f + // S x00 x04 code + // S x00 x10 ServiceException + // S x00 x07 message + // S x00 x08 time out + // S x00 x06 detail + // M t x00 x1d java.io.FileNotFoundException + // z + // z + buf.should.eql(Buffer.concat([ + new Buffer(['r'.charCodeAt(0), 0x01, 0x00]), + new Buffer('f'), + new Buffer(['S'.charCodeAt(0), 0x00, 0x04]), new Buffer('code'), + new Buffer(['S'.charCodeAt(0), 0x00, 0x10]), new Buffer('ServiceException'), + new Buffer(['S'.charCodeAt(0), 0x00, 0x07]), new Buffer('message'), + new Buffer(['S'.charCodeAt(0), 0x00, 0x08]), new Buffer('time out'), + new Buffer(['S'.charCodeAt(0), 0x00, 0x06]), new Buffer('detail'), + new Buffer(['M'.charCodeAt(0), 't'.charCodeAt(0), 0x00, 0x1d]), new Buffer('java.io.FileNotFoundException'), + new Buffer('zz') + ])); + }); + +}); From be1c79efb796db905f44b3c674efbc738753e066 Mon Sep 17 00:00:00 2001 From: tangzhi Date: Wed, 6 Jan 2016 16:45:23 +0800 Subject: [PATCH 2/3] this.byteBuffer.skip(1) --- lib/v1/decoder.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/v1/decoder.js b/lib/v1/decoder.js index 09e7db7..0d51985 100644 --- a/lib/v1/decoder.js +++ b/lib/v1/decoder.js @@ -361,7 +361,7 @@ proto._readSparseObject = function (withType) { label = this.byteBuffer.getChar(this.byteBuffer.position()); } // skip 'z' char - this.byteBuffer.position(this.byteBuffer.position() + 1); + this.byteBuffer.skip(1); // default type info if (withType) { @@ -461,7 +461,7 @@ proto._readNoLengthArray = function (withType, type) { label = this.byteBuffer.getChar(this.byteBuffer.position()); } // skip 'z' char - this.byteBuffer.position(this.byteBuffer.position() + 1); + this.byteBuffer.skip(1); arr = withType ? { $class: type, $: arr } @@ -601,7 +601,7 @@ proto.readCall = function(withType) { label = this.byteBuffer.getChar(this.byteBuffer.position()); } // skip 'z' char - this.byteBuffer.position(this.byteBuffer.position() + 1); + this.byteBuffer.skip(1); return result; }; From d05de0f4e5bf4317f09723539c5d5bbaacde2c79 Mon Sep 17 00:00:00 2001 From: tangzhi Date: Thu, 7 Jan 2016 10:59:25 +0800 Subject: [PATCH 3/3] =?UTF-8?q?=E5=8E=BB=E6=8E=89=E6=B2=A1=E7=94=A8?= =?UTF-8?q?=E7=9A=84=E6=B3=A8=E9=87=8A=E7=AD=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/v1/decoder.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/v1/decoder.js b/lib/v1/decoder.js index 0d51985..0847229 100644 --- a/lib/v1/decoder.js +++ b/lib/v1/decoder.js @@ -585,8 +585,8 @@ proto.readCall = function(withType) { var result = {header: header, arguments: args}; while (this.byteBuffer.getChar(this.byteBuffer.position()) === 'H') { - this.byteBuffer.position(this.byteBuffer.position() + 1); //skip 'H' char - var key = this.byteBuffer.readRawString( this.byteBuffer.getUInt16() ); + this.byteBuffer.skip(1); //skip 'H' char + var key = this.byteBuffer.readRawString(this.byteBuffer.getUInt16()); header[key] = this.read(withType); } @@ -594,7 +594,6 @@ proto.readCall = function(withType) { var methodLength = this.byteBuffer.getUInt16(); result.method = this.byteBuffer.readRawString(methodLength); - // var label = this.byteBuffer.getChar(this.byteBuffer.position()); while (label !== 'z') { args.push(this.read(withType));