Repository: apache/dubbo-go-hessian2
Pull request #128 (2019-09-02 02:15:49)

support to read only attachments by "ReadAttachments"
**What this PR does**:

**Which issue(s) this PR fixes**:

**Special notes for your reviewer**:

**Does this PR introduce a user-facing change?**:



Patch:
diff --git a/decode.go b/decode.go
index 859cbd6..4bb782d 100644
--- a/decode.go
+++ b/decode.go
@@ -33,6 +33,7 @@ type Decoder struct {
 	// todo: map
 	typeRefs      *TypeRefs
 	classInfoList []classInfo
+	isSkip        bool
 }
 
 // Error part
@@ -46,6 +47,11 @@ func NewDecoder(b []byte) *Decoder {
 	return &Decoder{reader: bufio.NewReader(bytes.NewReader(b)), typeRefs: &TypeRefs{records: map[string]bool{}}}
 }
 
+// NewDecoder generate a decoder instance with skip
+func NewDecoderWithSkip(b []byte) *Decoder {
+	return &Decoder{reader: bufio.NewReader(bytes.NewReader(b)), typeRefs: &TypeRefs{records: map[string]bool{}}, isSkip: true}
+}
+
 /////////////////////////////////////////
 // utilities
 /////////////////////////////////////////
@@ -232,5 +238,8 @@ func (t *TypeRefs) appendTypeRefs(name string, p reflect.Type) {
 }
 
 func (t *TypeRefs) Get(index int) reflect.Type {
+	if len(t.typeRefs) <= index {
+		return nil
+	}
 	return t.typeRefs[index]
 }
diff --git a/hessian.go b/hessian.go
index 8fb34ae..759293b 100644
--- a/hessian.go
+++ b/hessian.go
@@ -32,6 +32,7 @@ const (
 	PackageHeartbeat          = PackageType(0x08)
 	PackageRequest_TwoWay     = PackageType(0x10)
 	PackageResponse_Exception = PackageType(0x20)
+	PackageType_BitSize       = 0x2f
 )
 
 // PackageType ...
@@ -70,6 +71,15 @@ func NewHessianCodec(reader *bufio.Reader) *HessianCodec {
 	}
 }
 
+// NewHessianCodec generate a new hessian codec instance
+func NewHessianCodecCustom(pkgType PackageType, reader *bufio.Reader, bodyLen int) *HessianCodec {
+	return &HessianCodec{
+		pkgType: pkgType,
+		reader:  reader,
+		bodyLen: bodyLen,
+	}
+}
+
 func (h *HessianCodec) Write(service Service, header DubboHeader, body interface{}) ([]byte, error) {
 	switch header.Type {
 	case PackageHeartbeat:
@@ -173,7 +183,7 @@ func (h *HessianCodec) ReadBody(rspObj interface{}) error {
 		return perrors.WithStack(err)
 	}
 
-	switch h.pkgType & 0x2f {
+	switch h.pkgType & PackageType_BitSize {
 	case PackageResponse | PackageHeartbeat | PackageResponse_Exception, PackageResponse | PackageResponse_Exception:
 		decoder := NewDecoder(buf[:])
 		exception, err := decoder.Decode()
@@ -189,13 +199,13 @@ func (h *HessianCodec) ReadBody(rspObj interface{}) error {
 	case PackageRequest | PackageHeartbeat, PackageResponse | PackageHeartbeat:
 	case PackageRequest:
 		if rspObj != nil {
-			if err = unpackRequestBody(buf, rspObj); err != nil {
+			if err = unpackRequestBody(NewDecoder(buf[:]), rspObj); err != nil {
 				return perrors.WithStack(err)
 			}
 		}
 	case PackageResponse:
 		if rspObj != nil {
-			if err = unpackResponseBody(buf, rspObj); err != nil {
+			if err = unpackResponseBody(NewDecoder(buf[:]), rspObj); err != nil {
 				return perrors.WithStack(err)
 			}
 		}
@@ -203,3 +213,35 @@ func (h *HessianCodec) ReadBody(rspObj interface{}) error {
 
 	return nil
 }
+
+// ignore body, but only read attachments
+func (h *HessianCodec) ReadAttachments() (map[string]string, error) {
+	if h.reader.Buffered() < h.bodyLen {
+		return nil, ErrBodyNotEnough
+	}
+	buf, err := h.reader.Peek(h.bodyLen)
+	if err != nil {
+		return nil, perrors.WithStack(err)
+	}
+	_, err = h.reader.Discard(h.bodyLen)
+	if err != nil { // this is impossible
+		return nil, perrors.WithStack(err)
+	}
+
+	switch h.pkgType & PackageType_BitSize {
+	case PackageRequest:
+		rspObj := make([]interface{}, 7)
+		if err = unpackRequestBody(NewDecoderWithSkip(buf[:]), rspObj); err != nil {
+			return nil, perrors.WithStack(err)
+		}
+		return rspObj[6].(map[string]string), nil
+	case PackageResponse:
+		rspObj := &Response{}
+		if err = unpackResponseBody(NewDecoderWithSkip(buf[:]), rspObj); err != nil {
+			return nil, perrors.WithStack(err)
+		}
+		return rspObj.Attachments, nil
+	}
+
+	return nil, nil
+}
diff --git a/object.go b/object.go
index 71de3c1..58fe5d8 100644
--- a/object.go
+++ b/object.go
@@ -470,6 +470,7 @@ func (d *Decoder) getStructDefByIndex(idx int) (reflect.Type, classInfo, error)
 		ok  bool
 		cls classInfo
 		s   structInfo
+		err error
 	)
 
 	if len(d.classInfoList) <= idx || idx < 0 {
@@ -478,7 +479,10 @@ func (d *Decoder) getStructDefByIndex(idx int) (reflect.Type, classInfo, error)
 	cls = d.classInfoList[idx]
 	s, ok = getStructInfo(cls.javaName)
 	if !ok {
-		return nil, cls, perrors.Errorf("can not find go type name %s in registry", cls.javaName)
+		if !d.isSkip {
+			err = perrors.Errorf("can not find go type name %s in registry", cls.javaName)
+		}
+		return nil, cls, err
 	}
 
 	return s.typ, cls, nil
@@ -506,6 +510,15 @@ func (d *Decoder) decEnum(javaName string, flag int32) (JavaEnum, error) {
 	return enumValue, nil
 }
 
+// skip this object
+func (d *Decoder) skip(cls classInfo) error {
+	if len(cls.fieldNameList) < 1 {
+		return nil
+	}
+	_, err := d.DecodeValue()
+	return err
+}
+
 func (d *Decoder) decObject(flag int32) (interface{}, error) {
 	var (
 		tag byte
@@ -549,6 +562,9 @@ func (d *Decoder) decObject(flag int32) (interface{}, error) {
 		if err != nil {
 			return nil, err
 		}
+		if typ == nil {
+			return nil, d.skip(cls)
+		}
 		if typ.Implements(javaEnumType) {
 			return d.decEnum(cls.javaName, TAG_READ)
 		}
@@ -560,6 +576,9 @@ func (d *Decoder) decObject(flag int32) (interface{}, error) {
 		if err != nil {
 			return nil, err
 		}
+		if typ == nil {
+			return nil, d.skip(cls)
+		}
 		if typ.Implements(javaEnumType) {
 			return d.decEnum(cls.javaName, TAG_READ)
 		}
diff --git a/ref.go b/ref.go
index e9c08d9..a63689c 100644
--- a/ref.go
+++ b/ref.go
@@ -165,7 +165,8 @@ func (d *Decoder) decRef(flag int32) (interface{}, error) {
 		}
 
 		if len(d.refs) <= int(i) {
-			return nil, ErrIllegalRefIndex
+			return nil, nil
+			//return nil, ErrIllegalRefIndex
 		}
 		// return the exact ref object, which maybe a _refHolder
 		return d.refs[i], nil
diff --git a/request.go b/request.go
index f74a224..8e0fb26 100644
--- a/request.go
+++ b/request.go
@@ -259,7 +259,11 @@ END:
 }
 
 // hessian decode request body
-func unpackRequestBody(buf []byte, reqObj interface{}) error {
+func unpackRequestBody(decoder *Decoder, reqObj interface{}) error {
+
+	if decoder == nil {
+		return perrors.Errorf("@decoder is nil")
+	}
 
 	req, ok := reqObj.([]interface{})
 	if !ok {
@@ -274,7 +278,6 @@ func unpackRequestBody(buf []byte, reqObj interface{}) error {
 		dubboVersion, target, serviceVersion, method, argsTypes interface{}
 		args                                                    []interface{}
 	)
-	decoder := NewDecoder(buf[:])
 
 	dubboVersion, err = decoder.Decode()
 	if err != nil {
diff --git a/response.go b/response.go
index bf642d5..17f3528 100644
--- a/response.go
+++ b/response.go
@@ -153,9 +153,11 @@ func packResponse(header DubboHeader, ret interface{}) ([]byte, error) {
 }
 
 // hessian decode response body
-func unpackResponseBody(buf []byte, resp interface{}) error {
+func unpackResponseBody(decoder *Decoder, resp interface{}) error {
 	// body
-	decoder := NewDecoder(buf[:])
+	if decoder == nil {
+		return perrors.Errorf("@decoder is nil")
+	}
 	rspType, err := decoder.Decode()
 	if err != nil {
 		return perrors.WithStack(err)


Test patch:
diff --git a/date_test.go b/date_test.go
index 9044002..64e0822 100644
--- a/date_test.go
+++ b/date_test.go
@@ -64,7 +64,7 @@ func TestEncDate(t *testing.T) {
 }
 
 func testDateFramework(t *testing.T, method string, expected time.Time) {
-	r, e := decodeJavaResponse(method, "")
+	r, e := decodeJavaResponse(method, "", false)
 	if e != nil {
 		t.Errorf("%s: decode fail with error %+v", method, e)
 		return
diff --git a/decode_test.go b/decode_test.go
index e877b52..1a501c1 100644
--- a/decode_test.go
+++ b/decode_test.go
@@ -70,9 +70,14 @@ func getJavaReply(method, className string) []byte {
 	return out
 }
 
-func decodeJavaResponse(method, className string) (interface{}, error) {
+func decodeJavaResponse(method, className string, skip bool) (interface{}, error) {
 	b := getJavaReply(method, className)
-	d := NewDecoder(b)
+	var d *Decoder
+	if skip {
+		d = NewDecoderWithSkip(b)
+	} else {
+		d = NewDecoder(b)
+	}
 	r, e := d.Decode()
 	if e != nil {
 		return nil, e
@@ -81,11 +86,15 @@ func decodeJavaResponse(method, className string) (interface{}, error) {
 }
 
 func testDecodeFramework(t *testing.T, method string, expected interface{}) {
-	testDecodeJavaData(t, method, "", expected)
+	testDecodeJavaData(t, method, "", false, expected)
+}
+
+func testDecodeFrameworkWithSkip(t *testing.T, method string, expected interface{}) {
+	testDecodeJavaData(t, method, "", true, expected)
 }
 
-func testDecodeJavaData(t *testing.T, method, className string, expected interface{}) {
-	r, e := decodeJavaResponse(method, className)
+func testDecodeJavaData(t *testing.T, method, className string, skip bool, expected interface{}) {
+	r, e := decodeJavaResponse(method, className, skip)
 	if e != nil {
 		t.Errorf("%s: decode fail with error %v", method, e)
 		return
@@ -101,7 +110,7 @@ func testDecodeJavaData(t *testing.T, method, className string, expected interfa
 }
 
 func testDecodeFrameworkFunc(t *testing.T, method string, expected func(interface{})) {
-	r, e := decodeJavaResponse(method, "")
+	r, e := decodeJavaResponse(method, "", false)
 	if e != nil {
 		t.Errorf("%s: decode fail with error %v", method, e)
 		return
diff --git a/object_test.go b/object_test.go
index 7e3068e..ae6a537 100644
--- a/object_test.go
+++ b/object_test.go
@@ -417,7 +417,7 @@ func TestDecodeJavaTupleObject(t *testing.T) {
 
 	RegisterPOJO(tuple)
 
-	testDecodeJavaData(t, "getTheTuple", "test.tuple.TupleProviderImpl", tuple)
+	testDecodeJavaData(t, "getTheTuple", "test.tuple.TupleProviderImpl", false, tuple)
 }
 
 func TestEncodeDecodeTuple(t *testing.T) {
@@ -525,3 +525,33 @@ func doTestBasePointer(t *testing.T, base *BasePointer, expected *BasePointer) {
 		t.Errorf("expect: %v, but get: %v", base, decObj)
 	}
 }
+
+func TestSkip(t *testing.T) {
+	// clear pojo
+	pojoRegistry = POJORegistry{
+		j2g:      make(map[string]string),
+		registry: make(map[string]structInfo),
+	}
+	testDecodeFrameworkWithSkip(t, "replyObject_0", nil)
+	testDecodeFrameworkWithSkip(t, "replyObject_1", nil)
+	testDecodeFrameworkWithSkip(t, "replyObject_16", make([]interface{}, 17))
+	testDecodeFrameworkWithSkip(t, "replyObject_2a", make([]interface{}, 2))
+	testDecodeFrameworkWithSkip(t, "replyObject_3", nil)
+
+	testDecodeFrameworkWithSkip(t, "replyTypedMap_0", make(map[interface{}]interface{}))
+
+	testDecodeFrameworkWithSkip(t, "replyTypedFixedList_0", make([]string, 0))
+	testDecodeFrameworkWithSkip(t, "replyUntypedFixedList_0", []interface{}{})
+
+	testDecodeFrameworkWithSkip(t, "customReplyTypedFixedListHasNull", make([]Object, 3))
+	testDecodeFrameworkWithSkip(t, "customReplyTypedVariableListHasNull", make([]Object, 3))
+	testDecodeFrameworkWithSkip(t, "customReplyUntypedFixedListHasNull", make([]interface{}, 3))
+	testDecodeFrameworkWithSkip(t, "customReplyUntypedVariableListHasNull", make([]interface{}, 3))
+
+	testDecodeFrameworkWithSkip(t, "customReplyTypedFixedList_A0", make([]interface{}, 3))
+	testDecodeFrameworkWithSkip(t, "customReplyTypedVariableList_A0", make([]interface{}, 3))
+
+	testDecodeFrameworkWithSkip(t, "customReplyTypedFixedList_Test", nil)
+
+	testDecodeFrameworkWithSkip(t, "customReplyTypedFixedList_Object", make([]Object, 1))
+}