Merge pull request #57 from fangyincheng/master

Fix:java exception
diff --git a/hessian.go b/hessian.go
index ff39d92..24aa2e1 100644
--- a/hessian.go
+++ b/hessian.go
@@ -17,6 +17,7 @@
 import (
 	"bufio"
 	"encoding/binary"
+	"reflect"
 	"time"
 )
 
@@ -26,11 +27,12 @@
 
 // enum part
 const (
-	PackageError          = PackageType(0x01)
-	PackageRequest        = PackageType(0x02)
-	PackageResponse       = PackageType(0x04)
-	PackageHeartbeat      = PackageType(0x08)
-	PackageRequest_TwoWay = PackageType(0x10)
+	PackageError              = PackageType(0x01)
+	PackageRequest            = PackageType(0x02)
+	PackageResponse           = PackageType(0x04)
+	PackageHeartbeat          = PackageType(0x08)
+	PackageRequest_TwoWay     = PackageType(0x10)
+	PackageResponse_Exception = PackageType(0x20)
 )
 
 // PackageType ...
@@ -131,18 +133,8 @@
 	} else {
 		header.Type |= PackageResponse
 		header.ResponseStatus = buf[3]
-
-		// Header{status}
-		if buf[3] != Response_OK {
-			err = ErrJavaException
-			header.Type |= PackageError
-			bufSize := h.reader.Buffered()
-			if bufSize > 1 {
-				expBuf, expErr := h.reader.Peek(bufSize)
-				if expErr == nil {
-					err = perrors.Errorf("java exception:%s", string(expBuf[1:bufSize-1]))
-				}
-			}
+		if header.ResponseStatus != Response_OK {
+			header.Type |= PackageResponse_Exception
 		}
 	}
 
@@ -158,6 +150,10 @@
 	h.pkgType = header.Type
 	h.bodyLen = header.BodyLen
 
+	if h.reader.Buffered() < h.bodyLen {
+		return ErrBodyNotEnough
+	}
+
 	return perrors.WithStack(err)
 
 }
@@ -166,7 +162,7 @@
 func (h *HessianCodec) ReadBody(rspObj interface{}) error {
 
 	if h.reader.Buffered() < h.bodyLen {
-		return ErrHeaderNotEnough
+		return ErrBodyNotEnough
 	}
 	buf, err := h.reader.Peek(h.bodyLen)
 	if err != nil {
@@ -177,23 +173,29 @@
 		return perrors.WithStack(err)
 	}
 
-	switch h.pkgType & 0x0f {
-	case PackageRequest | PackageHeartbeat, PackageResponse | PackageHeartbeat:
+	switch h.pkgType & 0x2f {
+	case PackageResponse | PackageHeartbeat | PackageResponse_Exception, PackageResponse | PackageResponse_Exception:
+		rsp, ok := rspObj.(*Response)
+		if !ok {
+			return perrors.Errorf("@rspObj is not *Response, it is %s", reflect.TypeOf(rspObj).String())
+		}
+		rsp.Exception = ErrJavaException
+		if h.bodyLen > 1 {
+			rsp.Exception = perrors.Errorf("java exception:%s", string(buf[1:h.bodyLen-1]))
+		}
 		return nil
+	case PackageRequest | PackageHeartbeat, PackageResponse | PackageHeartbeat:
 	case PackageRequest:
 		if rspObj != nil {
 			if err = unpackRequestBody(buf, rspObj); err != nil {
 				return perrors.WithStack(err)
 			}
 		}
-
-		return nil
-
 	case PackageResponse:
 		if rspObj != nil {
 			rsp, ok := rspObj.(*Response)
 			if !ok {
-				break
+				rsp = &Response{RspObj: rspObj}
 			}
 			if err = unpackResponseBody(buf, rsp); err != nil {
 				return perrors.WithStack(err)
diff --git a/hessian_test.go b/hessian_test.go
index e9d62c0..45b4768 100644
--- a/hessian_test.go
+++ b/hessian_test.go
@@ -62,13 +62,8 @@
 
 	h := &DubboHeader{}
 	err = codecR.ReadHeader(h)
-	if responseStatus == Response_OK {
-		assert.Nil(t, err)
-	} else {
-		t.Log(err)
-		assert.NotNil(t, err)
-		return
-	}
+	assert.Nil(t, err)
+
 	assert.Equal(t, byte(2), h.SerialID)
 	assert.Equal(t, packageType, h.Type&(PackageRequest|PackageResponse|PackageHeartbeat))
 	assert.Equal(t, int64(1), h.ID)
@@ -83,6 +78,11 @@
 		return
 	}
 
+	if h.ResponseStatus != Zero && h.ResponseStatus != Response_OK {
+		assert.Equal(t, "java exception:"+body.(string), decodedResponse.Exception.Error())
+		return
+	}
+
 	in, _ := EnsureInterface(UnpackPtrValue(EnsurePackValue(body)), nil)
 	out, _ := EnsureInterface(UnpackPtrValue(EnsurePackValue(decodedResponse.RspObj)), nil)
 	assert.Equal(t, in, out)
diff --git a/response.go b/response.go
index 2eb4b20..63a2307 100644
--- a/response.go
+++ b/response.go
@@ -62,43 +62,45 @@
 	encoder := NewEncoder()
 	encoder.Append(byteArray[:HEADER_LENGTH])
 
-	if hb {
-		encoder.Encode(nil)
-	} else if header.ResponseStatus == Response_OK {
-		// com.alibaba.dubbo.rpc.protocol.dubbo.DubboCodec.DubboCodec.java
-		// v2.7.1 line191 encodeRequestData
-
-		atta := isSupportResponseAttachment(attachments[DUBBO_VERSION_KEY])
-
-		var resWithException, resValue, resNullValue int32
-		if atta {
-			resWithException = RESPONSE_WITH_EXCEPTION_WITH_ATTACHMENTS
-			resValue = RESPONSE_VALUE_WITH_ATTACHMENTS
-			resNullValue = RESPONSE_NULL_VALUE_WITH_ATTACHMENTS
+	if header.ResponseStatus == Response_OK {
+		if hb {
+			encoder.Encode(nil)
 		} else {
-			resWithException = RESPONSE_WITH_EXCEPTION
-			resValue = RESPONSE_VALUE
-			resNullValue = RESPONSE_NULL_VALUE
-		}
+			// com.alibaba.dubbo.rpc.protocol.dubbo.DubboCodec.DubboCodec.java
+			// v2.7.1 line191 encodeResponseData
 
-		if e, ok := ret.(error); ok { // throw error
-			encoder.Encode(resWithException)
-			if t, ok := e.(Throwabler); ok {
-				encoder.Encode(t)
-			} else {
-				encoder.Encode(e.Error())
-			}
-		} else {
-			if ret == nil {
-				encoder.Encode(resNullValue)
-			} else {
-				encoder.Encode(resValue)
-				encoder.Encode(ret) // result
-			}
-		}
+			atta := isSupportResponseAttachment(attachments[DUBBO_VERSION_KEY])
 
-		if atta {
-			encoder.Encode(attachments) // attachments
+			var resWithException, resValue, resNullValue int32
+			if atta {
+				resWithException = RESPONSE_WITH_EXCEPTION_WITH_ATTACHMENTS
+				resValue = RESPONSE_VALUE_WITH_ATTACHMENTS
+				resNullValue = RESPONSE_NULL_VALUE_WITH_ATTACHMENTS
+			} else {
+				resWithException = RESPONSE_WITH_EXCEPTION
+				resValue = RESPONSE_VALUE
+				resNullValue = RESPONSE_NULL_VALUE
+			}
+
+			if e, ok := ret.(error); ok { // throw error
+				encoder.Encode(resWithException)
+				if t, ok := e.(Throwabler); ok {
+					encoder.Encode(t)
+				} else {
+					encoder.Encode(e.Error())
+				}
+			} else {
+				if ret == nil {
+					encoder.Encode(resNullValue)
+				} else {
+					encoder.Encode(resValue)
+					encoder.Encode(ret) // result
+				}
+			}
+
+			if atta {
+				encoder.Encode(attachments) // attachments
+			}
 		}
 	} else {
 		// com.alibaba.dubbo.remoting.exchange.codec.ExchangeCodec