diff --git a/hessian.go b/hessian.go deleted file mode 100644 index 231dbeaa..00000000 --- a/hessian.go +++ /dev/null @@ -1,249 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// This file contains the implementation of -// [dubbo2 rpc protocol](https://dubbo.apache.org/zh/docs/concepts/rpc-protocol/#dubbo2), which is deprecated. -// Use the [implementation](https://github.com/apache/dubbo-go/tree/master/protocol/dubbo/hessian2) in dubbo-go project instead. - -package hessian - -import ( - "bufio" - "encoding/binary" - "time" -) - -import ( - perrors "github.com/pkg/errors" -) - -// enum part -const ( - PackageError = PackageType(0x01) - PackageRequest = PackageType(0x02) - PackageResponse = PackageType(0x04) - PackageHeartbeat = PackageType(0x08) - PackageRequest_TwoWay = PackageType(0x10) - PackageResponse_Exception = PackageType(0x20) - PackageType_BitSize = 0x2f -) - -// PackageType defines dubbo package type. -type PackageType int - -// DubboHeader dubbo header -type DubboHeader struct { - SerialID byte - Type PackageType - ID int64 - BodyLen int - ResponseStatus byte -} - -// Service defines service instance -type Service struct { - Path string - Interface string - Group string - Version string - Method string - Timeout time.Duration // request timeout -} - -// HessianCodec defines hessian codec -type HessianCodec struct { - pkgType PackageType - reader *bufio.Reader - bodyLen int -} - -// NewHessianCodec generate a new hessian codec instance -func NewHessianCodec(reader *bufio.Reader) *HessianCodec { - return &HessianCodec{ - reader: reader, - } -} - -// NewHessianCodecCustom 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: - if header.ResponseStatus == Zero { - return packRequest(service, header, body) - } - return packResponse(header, body) - - case PackageRequest, PackageRequest_TwoWay: - return packRequest(service, header, body) - - case PackageResponse: - return packResponse(header, body) - - default: - return nil, perrors.Errorf("Unrecognized message type: %v", header.Type) - } -} - -// ReadHeader uses hessian codec to read dubbo header -func (h *HessianCodec) ReadHeader(header *DubboHeader) error { - var err error - - if h.reader.Size() < HEADER_LENGTH { - return ErrHeaderNotEnough - } - buf, err := h.reader.Peek(HEADER_LENGTH) - if err != nil { // this is impossible - return perrors.WithStack(err) - } - _, err = h.reader.Discard(HEADER_LENGTH) - if err != nil { // this is impossible - return perrors.WithStack(err) - } - - //// read header - - if buf[0] != MAGIC_HIGH && buf[1] != MAGIC_LOW { - return ErrIllegalPackage - } - - // Header{serialization id(5 bit), event, two way, req/response} - if header.SerialID = buf[2] & SERIAL_MASK; header.SerialID == Zero { - return perrors.Errorf("serialization ID:%v", header.SerialID) - } - - headerFlag := buf[2] & FLAG_EVENT - if headerFlag != Zero { - header.Type |= PackageHeartbeat - } - headerFlag = buf[2] & FLAG_REQUEST - if headerFlag != Zero { - header.Type |= PackageRequest - headerFlag = buf[2] & FLAG_TWOWAY - if headerFlag != Zero { - header.Type |= PackageRequest_TwoWay - } - } else { - header.Type |= PackageResponse - header.ResponseStatus = buf[3] - if header.ResponseStatus != Response_OK { - header.Type |= PackageResponse_Exception - } - } - - // Header{req id} - header.ID = int64(binary.BigEndian.Uint64(buf[4:])) - - // Header{body len} - header.BodyLen = int(binary.BigEndian.Uint32(buf[12:])) - if header.BodyLen < 0 { - return ErrIllegalPackage - } - - h.pkgType = header.Type - h.bodyLen = header.BodyLen - - if h.reader.Buffered() < h.bodyLen { - return ErrBodyNotEnough - } - - return perrors.WithStack(err) -} - -// ReadBody uses hessian codec to read response body -func (h *HessianCodec) ReadBody(rspObj interface{}) error { - if h.reader.Buffered() < h.bodyLen { - return ErrBodyNotEnough - } - buf, err := h.reader.Peek(h.bodyLen) - if err != nil { - return perrors.WithStack(err) - } - _, err = h.reader.Discard(h.bodyLen) - if err != nil { // this is impossible - return perrors.WithStack(err) - } - - switch h.pkgType & PackageType_BitSize { - case PackageResponse | PackageHeartbeat | PackageResponse_Exception, PackageResponse | PackageResponse_Exception: - decoder := NewDecoder(buf[:]) - exception, decErr := decoder.Decode() - if decErr != nil { - return perrors.WithStack(decErr) - } - rsp, ok := rspObj.(*Response) - if !ok { - return perrors.Errorf("java exception:%s", exception.(string)) - } - rsp.Exception = perrors.Errorf("java exception:%s", exception.(string)) - return nil - case PackageRequest | PackageHeartbeat, PackageResponse | PackageHeartbeat: - case PackageRequest: - if rspObj != nil { - if err = unpackRequestBody(NewStrictDecoder(buf[:]), rspObj); err != nil { - return perrors.WithStack(err) - } - } - case PackageResponse: - if rspObj != nil { - if err = unpackResponseBody(NewDecoder(buf[:]), rspObj); err != nil { - return perrors.WithStack(err) - } - } - } - - return nil -} - -// ReadAttachments 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/hessian_test.go b/hessian_test.go deleted file mode 100644 index 5a46b13b..00000000 --- a/hessian_test.go +++ /dev/null @@ -1,234 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package hessian - -import ( - "bufio" - "bytes" - "reflect" - "testing" - "time" -) - -import ( - "github.com/stretchr/testify/assert" -) - -type Case struct { - A string - B int -} - -type CaseA struct { - A string - B int - C Case -} - -type CaseB struct { - A string - B CaseA -} - -func (c *CaseB) JavaClassName() string { - return "com.test.caseb" -} - -func (c CaseA) JavaClassName() string { - return "com.test.casea" -} - -// JavaClassName java fully qualified path -func (c Case) JavaClassName() string { - return "com.test.case" -} - -func doTestHessianEncodeHeader(t *testing.T, packageType PackageType, responseStatus byte, body interface{}) ([]byte, error) { - RegisterPOJO(&Case{}) - codecW := NewHessianCodec(nil) - resp, err := codecW.Write(Service{ - Path: "test", - Interface: "ITest", - Version: "v1.0", - Method: "test", - Timeout: time.Second * 10, - }, DubboHeader{ - SerialID: 2, - Type: packageType, - ID: 1, - ResponseStatus: responseStatus, - }, body) - assert.Nil(t, err) - return resp, err -} - -func doTestResponse(t *testing.T, packageType PackageType, responseStatus byte, body interface{}, decodedResponse *Response, assertFunc func()) { - resp, err := doTestHessianEncodeHeader(t, packageType, responseStatus, body) - assert.Nil(t, err) - - codecR := NewHessianCodec(bufio.NewReader(bytes.NewReader(resp))) - - h := &DubboHeader{} - err = codecR.ReadHeader(h) - 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) - assert.Equal(t, responseStatus, h.ResponseStatus) - - err = codecR.ReadBody(decodedResponse) - assert.Nil(t, err) - t.Log(decodedResponse) - - if assertFunc != nil { - assertFunc() - return - } - - if h.ResponseStatus != Zero && h.ResponseStatus != Response_OK { - assert.Equal(t, "java exception:"+body.(string), decodedResponse.Exception.Error()) - return - } - - in := EnsureRawAny(UnpackPtrValue(EnsurePackValue(body))) - out := EnsureRawAny(UnpackPtrValue(EnsurePackValue(decodedResponse.RspObj))) - assert.Equal(t, in, out) -} - -func TestResponse(t *testing.T) { - caseObj := Case{A: "a", B: 1} - decodedResponse := &Response{} - RegisterPOJO(&caseObj) - - arr := []*Case{&caseObj} - decodedResponse.RspObj = nil - doTestResponse(t, PackageResponse, Response_OK, arr, decodedResponse, func() { - arrRes, ok := decodedResponse.RspObj.([]*Case) - if !ok { - t.Errorf("expect []*Case, but get %s", reflect.TypeOf(decodedResponse.RspObj).String()) - return - } - assert.Equal(t, 1, len(arrRes)) - assert.Equal(t, &caseObj, arrRes[0]) - }) - - doTestResponse(t, PackageResponse, Response_OK, &caseObj, decodedResponse, func() { - assert.Equal(t, &caseObj, decodedResponse.RspObj) - }) - - s := "ok!!!!!" - doTestResponse(t, PackageResponse, Response_OK, s, decodedResponse, func() { - assert.Equal(t, s, decodedResponse.RspObj) - }) - - doTestResponse(t, PackageResponse, Response_OK, int64(3), decodedResponse, func() { - assert.Equal(t, int64(3), decodedResponse.RspObj) - }) - - doTestResponse(t, PackageResponse, Response_OK, true, decodedResponse, func() { - assert.Equal(t, true, decodedResponse.RspObj) - }) - - errorMsg := "error!!!!!" - decodedResponse.RspObj = nil - doTestResponse(t, PackageResponse, Response_SERVER_ERROR, errorMsg, decodedResponse, func() { - assert.Equal(t, "java exception:error!!!!!", decodedResponse.Exception.Error()) - }) - - decodedResponse.RspObj = nil - decodedResponse.Exception = nil - mapObj := map[string][]*Case{"key": {&caseObj}} - doTestResponse(t, PackageResponse, Response_OK, mapObj, decodedResponse, func() { - mapRes, ok := decodedResponse.RspObj.(map[interface{}]interface{}) - if !ok { - t.Errorf("expect map[string][]*Case, but get %s", reflect.TypeOf(decodedResponse.RspObj).String()) - return - } - c, ok := mapRes["key"] - if !ok { - assert.FailNow(t, "no key in decoded response map") - } - - mapValueArr, ok := c.([]*Case) - if !ok { - assert.FailNow(t, "invalid decoded response map value", "expect []*Case, but get %v", reflect.TypeOf(c)) - } - assert.Equal(t, 1, len(mapValueArr)) - assert.Equal(t, &caseObj, mapValueArr[0]) - }) -} - -func doTestRequest(t *testing.T, packageType PackageType, responseStatus byte, body interface{}) { - resp, err := doTestHessianEncodeHeader(t, packageType, responseStatus, body) - assert.Nil(t, err) - - codecR := NewHessianCodec(bufio.NewReader(bytes.NewReader(resp))) - - h := &DubboHeader{} - err = codecR.ReadHeader(h) - 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) - assert.Equal(t, responseStatus, h.ResponseStatus) - - c := make([]interface{}, 7) - err = codecR.ReadBody(c) - assert.Nil(t, err) - t.Log(c) - assert.True(t, len(body.([]interface{})) == len(c[5].([]interface{}))) -} - -func TestRequest(t *testing.T) { - doTestRequest(t, PackageRequest, Zero, []interface{}{"a"}) - doTestRequest(t, PackageRequest, Zero, []interface{}{"a", 3}) - doTestRequest(t, PackageRequest, Zero, []interface{}{"a", true}) - doTestRequest(t, PackageRequest, Zero, []interface{}{"a", 3, true}) - doTestRequest(t, PackageRequest, Zero, []interface{}{3.2, true}) - doTestRequest(t, PackageRequest, Zero, []interface{}{"a", 3, true, &Case{A: "a", B: 3}}) - doTestRequest(t, PackageRequest, Zero, []interface{}{"a", 3, true, []*Case{{A: "a", B: 3}}}) - doTestRequest(t, PackageRequest, Zero, []interface{}{map[string][]*Case{"key": {{A: "a", B: 3}}}}) -} - -func TestHessianCodec_ReadAttachments(t *testing.T) { - body := &Response{ - RspObj: &CaseB{A: "A", B: CaseA{A: "a", B: 1, C: Case{A: "c", B: 2}}}, - Exception: nil, - Attachments: map[string]string{DUBBO_VERSION_KEY: "2.6.4"}, - } - resp, err := doTestHessianEncodeHeader(t, PackageResponse, Response_OK, body) - assert.NoError(t, err) - UnRegisterPOJOs(&CaseB{}, &CaseA{}) - codecR1 := NewHessianCodec(bufio.NewReader(bytes.NewReader(resp))) - codecR2 := NewHessianCodec(bufio.NewReader(bytes.NewReader(resp))) - h := &DubboHeader{} - assert.NoError(t, codecR1.ReadHeader(h)) - t.Log(h) - assert.NoError(t, codecR2.ReadHeader(h)) - t.Log(h) - - err = codecR1.ReadBody(body) - assert.NoError(t, err) - // assert.Equal(t, "can not find go type name com.test.caseb in registry", err.Error()) - attrs, err := codecR2.ReadAttachments() - assert.NoError(t, err) - assert.Equal(t, "2.6.4", attrs[DUBBO_VERSION_KEY]) - - t.Log(attrs) -} diff --git a/hessian_test/dup_struct_name_test.go b/hessian_test/dup_struct_name_test.go index 0c4954f1..b5d69506 100644 --- a/hessian_test/dup_struct_name_test.go +++ b/hessian_test/dup_struct_name_test.go @@ -18,10 +18,7 @@ package hessian_test import ( - "bufio" - "bytes" "testing" - "time" ) import ( @@ -45,129 +42,6 @@ func (CaseZ) JavaClassName() string { return "com.test.caseZ" } -func doTestHessianEncodeHeader(t *testing.T, packageType hessian.PackageType, responseStatus byte, body interface{}) ([]byte, error) { - codecW := hessian.NewHessianCodec(nil) - resp, err := codecW.Write(hessian.Service{ - Path: "test", - Interface: "ITest", - Version: "v1.0", - Method: "test", - Timeout: time.Second * 10, - }, hessian.DubboHeader{ - SerialID: 2, - Type: packageType, - ID: 1, - ResponseStatus: responseStatus, - }, body) - assert.Nil(t, err) - return resp, err -} - -func TestDupStructNameRequest(t *testing.T) { - hessian.RegisterPOJO(&dupclass.CaseZ{}) - hessian.RegisterPOJO(&CaseZ{}) - - packageType := hessian.PackageRequest - responseStatus := hessian.Zero - var body interface{} - body = []interface{}{"a"} - resp, err := doTestHessianEncodeHeader(t, packageType, responseStatus, body) - assert.Nil(t, err) - - codecR := hessian.NewHessianCodec(bufio.NewReader(bytes.NewReader(resp))) - - h := &hessian.DubboHeader{} - err = codecR.ReadHeader(h) - assert.Nil(t, err) - assert.Equal(t, byte(2), h.SerialID) - assert.Equal(t, packageType, h.Type&(hessian.PackageRequest|hessian.PackageResponse|hessian.PackageHeartbeat)) - assert.Equal(t, int64(1), h.ID) - assert.Equal(t, responseStatus, h.ResponseStatus) - - c := make([]interface{}, 7) - err = codecR.ReadBody(c) - assert.Nil(t, err) - t.Log(c) - assert.True(t, len(body.([]interface{})) == len(c[5].([]interface{}))) -} - -func TestDupStructNameResponse(t *testing.T) { - defer func() { - if err := recover(); err != nil { - if errStr, ok := err.(string); ok { - assert.Equal(t, ExpectedErrorMsg, errStr) - } - } - }() - - var body interface{} - body = &CaseZ{Name: "TestDupStructNameResponse"} - err, codecR, h := doTestHeader(t, body) - assert.Nil(t, err) - - decodedResponse := &hessian.Response{} - err = codecR.ReadBody(decodedResponse) - assert.Nil(t, err) - - checkResponseBody(t, decodedResponse, h, body) -} - -func TestDupStructNameResponse2(t *testing.T) { - defer func() { - if err := recover(); err != nil { - if errStr, ok := err.(string); ok { - assert.Equal(t, ExpectedErrorMsg, errStr) - } - } - }() - - var body interface{} - body = &dupclass.CaseZ{Name: "TestDupStructNameResponse"} - err, codecR, h := doTestHeader(t, body) - assert.Nil(t, err) - - decodedResponse := &hessian.Response{} - err = codecR.ReadBody(decodedResponse) - assert.Nil(t, err) - - checkResponseBody(t, decodedResponse, h, body) -} - -func doTestHeader(t *testing.T, body interface{}) (error, *hessian.HessianCodec, *hessian.DubboHeader) { - hessian.RegisterPOJO(&dupclass.CaseZ{}) - hessian.RegisterPOJO(&CaseZ{}) - - packageType := hessian.PackageResponse - responseStatus := hessian.Response_OK - resp, err := doTestHessianEncodeHeader(t, packageType, responseStatus, body) - assert.Nil(t, err) - - codecR := hessian.NewHessianCodec(bufio.NewReader(bytes.NewReader(resp))) - - h := &hessian.DubboHeader{} - err = codecR.ReadHeader(h) - assert.Nil(t, err) - - assert.Equal(t, byte(2), h.SerialID) - assert.Equal(t, packageType, h.Type&(hessian.PackageRequest|hessian.PackageResponse|hessian.PackageHeartbeat)) - assert.Equal(t, int64(1), h.ID) - assert.Equal(t, responseStatus, h.ResponseStatus) - return err, codecR, h -} - -func checkResponseBody(t *testing.T, decodedResponse *hessian.Response, h *hessian.DubboHeader, body interface{}) { - t.Log(decodedResponse) - - if h.ResponseStatus != hessian.Zero && h.ResponseStatus != hessian.Response_OK { - assert.Equal(t, "java exception:"+body.(string), decodedResponse.Exception.Error()) - return - } - - in := hessian.EnsureRawAny(hessian.UnpackPtrValue(hessian.EnsurePackValue(body))) - out := hessian.EnsureRawAny(hessian.UnpackPtrValue(hessian.EnsurePackValue(decodedResponse.RspObj))) - assert.Equal(t, in, out) -} - func TestDuplicatedClassGetGoType(t *testing.T) { assert.Equal(t, "github.com/apache/dubbo-go-hessian2/hessian_test_test/hessian_test.CaseZ", hessian.GetGoType(&CaseZ{})) assert.Equal(t, "github.com/apache/dubbo-go-hessian2/hessian_test/hessian_test/hessian_test.CaseZ", hessian.GetGoType(&dupclass.CaseZ{})) diff --git a/request.go b/request.go deleted file mode 100644 index a6def73e..00000000 --- a/request.go +++ /dev/null @@ -1,358 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// This file contains the implementation of -// [dubbo2 rpc protocol](https://dubbo.apache.org/zh/docs/concepts/rpc-protocol/#dubbo2), which is deprecated. -// Use the [implementation](https://github.com/apache/dubbo-go/tree/master/protocol/dubbo/hessian2) in dubbo-go project instead. - -package hessian - -import ( - "encoding/binary" - "reflect" - "strconv" - "strings" - "time" -) - -import ( - perrors "github.com/pkg/errors" -) - -///////////////////////////////////////// -// dubbo -///////////////////////////////////////// - -func getArgType(v interface{}) string { - if v == nil { - return "V" - } - - switch v.(type) { - // Serialized tags for base types - case nil: - return "V" - case bool: - return "Z" - case []bool: - return "[Z" - case byte: - return "B" - case []byte: - return "[B" - case int8: - return "B" - case []int8: - return "[B" - case int16: - return "S" - case []int16: - return "[S" - case uint16: // Equivalent to Char of Java - return "C" - case []uint16: - return "[C" - // case rune: - // return "C" - case int: - return "J" - case []int: - return "[J" - case int32: - return "I" - case []int32: - return "[I" - case int64: - return "J" - case []int64: - return "[J" - case time.Time: - return "java.util.Date" - case []time.Time: - return "[Ljava.util.Date" - case float32: - return "F" - case []float32: - return "[F" - case float64: - return "D" - case []float64: - return "[D" - case string: - return "java.lang.String" - case []string: - return "[Ljava.lang.String;" - case []Object: - return "[Ljava.lang.Object;" - case map[interface{}]interface{}: - // return "java.util.HashMap" - return "java.util.Map" - case POJOEnum: - return v.(POJOEnum).JavaClassName() - // Serialized tags for complex types - default: - t := reflect.TypeOf(v) - if reflect.Ptr == t.Kind() { - t = t.Elem() - } - switch t.Kind() { - case reflect.Struct: - v, ok := v.(POJO) - if ok { - return v.JavaClassName() - } - return "java.lang.Object" - case reflect.Slice, reflect.Array: - if t.Elem().Kind() == reflect.Struct { - return "[Ljava.lang.Object;" - } - // return "java.util.ArrayList" - return "java.util.List" - case reflect.Map: // Enter here, map may be map[string]int - return "java.util.Map" - default: - return "" - } - } - - // unreachable - // return "java.lang.RuntimeException" -} - -func getArgsTypeList(args []interface{}) (string, error) { - var ( - typ string - types string - ) - - for i := range args { - typ = getArgType(args[i]) - if typ == "" { - return types, perrors.Errorf("cat not get arg %#v type", args[i]) - } - if !strings.Contains(typ, ".") { - types += typ - } else if strings.Index(typ, "[") == 0 { - types += strings.Replace(typ, ".", "/", -1) - } else { - // java.util.List -> Ljava/util/List; - types += "L" + strings.Replace(typ, ".", "/", -1) + ";" - } - } - - return types, nil -} - -type Request struct { - Params interface{} - Attachments map[string]string -} - -// NewRequest create a new Request -func NewRequest(params interface{}, atta map[string]string) *Request { - if atta == nil { - atta = make(map[string]string) - } - return &Request{ - Params: params, - Attachments: atta, - } -} - -func EnsureRequest(body interface{}) *Request { - if req, ok := body.(*Request); ok { - return req - } - return NewRequest(body, nil) -} - -func packRequest(service Service, header DubboHeader, req interface{}) ([]byte, error) { - var ( - err error - types string - byteArray []byte - pkgLen int - ) - - request := EnsureRequest(req) - - args, ok := request.Params.([]interface{}) - if !ok { - return nil, perrors.Errorf("@params is not of type: []interface{}") - } - - hb := header.Type == PackageHeartbeat - - ////////////////////////////////////////// - // byteArray - ////////////////////////////////////////// - // magic - switch header.Type { - case PackageHeartbeat: - byteArray = append(byteArray, DubboRequestHeartbeatHeader[:]...) - case PackageRequest_TwoWay: - byteArray = append(byteArray, DubboRequestHeaderBytesTwoWay[:]...) - default: - byteArray = append(byteArray, DubboRequestHeaderBytes[:]...) - } - - // serialization id, two way flag, event, request/response flag - // SerialID is id of serialization approach in java dubbo - byteArray[2] |= header.SerialID & SERIAL_MASK - // request id - binary.BigEndian.PutUint64(byteArray[4:], uint64(header.ID)) - - encoder := NewEncoder() - encoder.Append(byteArray[:HEADER_LENGTH]) - - ////////////////////////////////////////// - // body - ////////////////////////////////////////// - if hb { - encoder.Encode(nil) - goto END - } - - // dubbo version + path + version + method - encoder.Encode(DEFAULT_DUBBO_PROTOCOL_VERSION) - encoder.Encode(service.Path) - encoder.Encode(service.Version) - encoder.Encode(service.Method) - - // args = args type list + args value list - if types, err = getArgsTypeList(args); err != nil { - return nil, perrors.Wrapf(err, " PackRequest(args:%+v)", args) - } - encoder.Encode(types) - for _, v := range args { - encoder.Encode(v) - } - - request.Attachments[PATH_KEY] = service.Path - request.Attachments[VERSION_KEY] = service.Version - if len(service.Group) > 0 { - request.Attachments[GROUP_KEY] = service.Group - } - if len(service.Interface) > 0 { - request.Attachments[INTERFACE_KEY] = service.Interface - } - if service.Timeout != 0 { - request.Attachments[TIMEOUT_KEY] = strconv.Itoa(int(service.Timeout / time.Millisecond)) - } - - encoder.Encode(request.Attachments) - -END: - byteArray = encoder.Buffer() - pkgLen = len(byteArray) - if pkgLen > int(DEFAULT_LEN) { // 8M - return nil, perrors.Errorf("Data length %d too large, max payload %d", pkgLen, DEFAULT_LEN) - } - // byteArray{body length} - binary.BigEndian.PutUint32(byteArray[12:], uint32(pkgLen-HEADER_LENGTH)) - return byteArray, nil -} - -// hessian decode request body -func unpackRequestBody(decoder *Decoder, reqObj interface{}) error { - if decoder == nil { - return perrors.Errorf("@decoder is nil") - } - - req, ok := reqObj.([]interface{}) - if !ok { - return perrors.Errorf("@reqObj is not of type: []interface{}") - } - if len(req) < 7 { - return perrors.New("length of @reqObj should be 7") - } - - var ( - err error - dubboVersion, target, serviceVersion, method, argsTypes interface{} - args []interface{} - ) - - dubboVersion, err = decoder.Decode() - if err != nil { - return perrors.WithStack(err) - } - req[0] = dubboVersion - - target, err = decoder.Decode() - if err != nil { - return perrors.WithStack(err) - } - req[1] = target - - serviceVersion, err = decoder.Decode() - if err != nil { - return perrors.WithStack(err) - } - req[2] = serviceVersion - - method, err = decoder.Decode() - if err != nil { - return perrors.WithStack(err) - } - req[3] = method - - argsTypes, err = decoder.Decode() - if err != nil { - return perrors.WithStack(err) - } - req[4] = argsTypes - - ats := DescRegex.FindAllString(argsTypes.(string), -1) - var arg interface{} - for i := 0; i < len(ats); i++ { - arg, err = decoder.Decode() - if err != nil { - return perrors.WithStack(err) - } - args = append(args, arg) - } - req[5] = args - - attachments, err := decoder.Decode() - if err != nil { - return perrors.WithStack(err) - } - if v, ok := attachments.(map[interface{}]interface{}); ok { - v[DUBBO_VERSION_KEY] = dubboVersion - req[6] = ToMapStringString(v) - return nil - } - - return perrors.Errorf("get wrong attachments: %+v", attachments) -} - -func ToMapStringString(origin map[interface{}]interface{}) map[string]string { - dest := make(map[string]string, len(origin)) - for k, v := range origin { - if kv, ok := k.(string); ok { - if v == nil { - dest[kv] = "" - continue - } - if vv, ok := v.(string); ok { - dest[kv] = vv - } - } - } - return dest -} diff --git a/request_test.go b/request_test.go deleted file mode 100644 index 2b7f1f33..00000000 --- a/request_test.go +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package hessian - -import ( - "reflect" - "strconv" - "testing" - "time" -) - -import ( - "github.com/stretchr/testify/assert" -) - -type TestEnumGender JavaEnum - -const ( - MAN JavaEnum = iota - WOMAN -) - -var genderName = map[JavaEnum]string{ - MAN: "MAN", - WOMAN: "WOMAN", -} - -var genderValue = map[string]JavaEnum{ - "MAN": MAN, - "WOMAN": WOMAN, -} - -func (g TestEnumGender) JavaClassName() string { - return "com.ikurento.test.TestEnumGender" -} - -func (g TestEnumGender) String() string { - s, ok := genderName[JavaEnum(g)] - if ok { - return s - } - - return strconv.Itoa(int(g)) -} - -func (g TestEnumGender) EnumValue(s string) JavaEnum { - v, ok := genderValue[s] - if ok { - return v - } - - return InvalidJavaEnum -} - -func TestPackRequest(t *testing.T) { - bytes, err := packRequest(Service{ - Path: "test", - Interface: "ITest", - Version: "v1.0", - Method: "test", - Timeout: time.Second * 10, - }, DubboHeader{ - SerialID: 0, - Type: PackageRequest, - ID: 123, - }, []interface{}{1, 2}) - - assert.Nil(t, err) - - if bytes != nil { - t.Logf("pack request: %s", string(bytes)) - } -} - -func TestGetArgsTypeList(t *testing.T) { - type Test struct{} - str, err := getArgsTypeList([]interface{}{nil, 1, []int{2}, true, []bool{false}, "a", []string{"b"}, Test{}, &Test{}, []Test{}, map[string]Test{}, TestEnumGender(MAN)}) - assert.NoError(t, err) - assert.Equal(t, "VJ[JZ[ZLjava/lang/String;[Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Object;[Ljava/lang/Object;Ljava/util/Map;Lcom/ikurento/test/TestEnumGender;", str) -} - -func TestDescRegex(t *testing.T) { - results := DescRegex.FindAllString("Ljava/lang/String;", -1) - assert.Equal(t, 1, len(results)) - assert.Equal(t, "Ljava/lang/String;", results[0]) - - results = DescRegex.FindAllString("Ljava/lang/String;I", -1) - assert.Equal(t, 2, len(results)) - assert.Equal(t, "Ljava/lang/String;", results[0]) - assert.Equal(t, "I", results[1]) - - results = DescRegex.FindAllString("ILjava/lang/String;", -1) - assert.Equal(t, 2, len(results)) - assert.Equal(t, "I", results[0]) - assert.Equal(t, "Ljava/lang/String;", results[1]) - - results = DescRegex.FindAllString("ILjava/lang/String;IZ", -1) - assert.Equal(t, 4, len(results)) - assert.Equal(t, "I", results[0]) - assert.Equal(t, "Ljava/lang/String;", results[1]) - assert.Equal(t, "I", results[2]) - assert.Equal(t, "Z", results[3]) - - results = DescRegex.FindAllString("[Ljava/lang/String;[I", -1) - assert.Equal(t, 2, len(results)) - assert.Equal(t, "[Ljava/lang/String;", results[0]) - assert.Equal(t, "[I", results[1]) -} - -func TestIssue192(t *testing.T) { - type args struct { - origin map[interface{}]interface{} - } - tests := []struct { - name string - args args - want map[string]string - }{ - { - name: "not null", - args: args{ - origin: map[interface{}]interface{}{ - "1": nil, - "2": "3", - "": "", - }, - }, - want: map[string]string{ - "1": "", - "2": "3", - "": "", - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if got := ToMapStringString(tt.args.origin); !reflect.DeepEqual(got, tt.want) { - t.Errorf("ToMapStringString() = %v, want %v", got, tt.want) - } - }) - } -} diff --git a/response.go b/response.go deleted file mode 100644 index 5f905924..00000000 --- a/response.go +++ /dev/null @@ -1,370 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// This file contains the implementation of -// [dubbo2 rpc protocol](https://dubbo.apache.org/zh/docs/concepts/rpc-protocol/#dubbo2), which is deprecated. -// Use the [implementation](https://github.com/apache/dubbo-go/tree/master/protocol/dubbo/hessian2) in dubbo-go project instead. - -package hessian - -import ( - "encoding/binary" - "math" - "reflect" - "strconv" - "strings" - - "github.com/apache/dubbo-go-hessian2/java_exception" - perrors "github.com/pkg/errors" -) - -// Response dubbo response -type Response struct { - RspObj interface{} - Exception error - Attachments map[string]string -} - -// NewResponse create a new Response -func NewResponse(rspObj interface{}, exception error, attachments map[string]string) *Response { - if attachments == nil { - attachments = make(map[string]string, 8) - } - return &Response{ - RspObj: rspObj, - Exception: exception, - Attachments: attachments, - } -} - -// EnsureResponse check body type, make sure it's a Response or package it as a Response -func EnsureResponse(body interface{}) *Response { - if res, ok := body.(*Response); ok { - return res - } - if exp, ok := body.(error); ok { - return NewResponse(nil, exp, nil) - } - return NewResponse(body, nil, nil) -} - -// https://github.com/apache/dubbo/blob/dubbo-2.7.1/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/codec/ExchangeCodec.java#L256 -// hessian encode response -func packResponse(header DubboHeader, ret interface{}) ([]byte, error) { - var byteArray []byte - - response := EnsureResponse(ret) - - hb := header.Type == PackageHeartbeat - - // magic - if hb { - byteArray = append(byteArray, DubboResponseHeartbeatHeader[:]...) - } else { - byteArray = append(byteArray, DubboResponseHeaderBytes[:]...) - } - // set serialID, identify serialization types, eg: fastjson->6, hessian2->2 - byteArray[2] |= header.SerialID & SERIAL_MASK - // response status - if header.ResponseStatus != 0 { - byteArray[3] = header.ResponseStatus - } - - // request id - binary.BigEndian.PutUint64(byteArray[4:], uint64(header.ID)) - - // body - encoder := NewEncoder() - encoder.Append(byteArray[:HEADER_LENGTH]) - - if header.ResponseStatus == Response_OK { - if hb { - encoder.Encode(nil) - } else { - atta := isSupportResponseAttachment(response.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 - } else { - resWithException = RESPONSE_WITH_EXCEPTION - resValue = RESPONSE_VALUE - resNullValue = RESPONSE_NULL_VALUE - } - - if response.Exception != nil { // throw error - encoder.Encode(resWithException) - if t, ok := response.Exception.(java_exception.Throwabler); ok { - encoder.Encode(t) - } else { - encoder.Encode(java_exception.NewThrowable(response.Exception.Error())) - } - } else { - if response.RspObj == nil { - encoder.Encode(resNullValue) - } else { - encoder.Encode(resValue) - encoder.Encode(response.RspObj) // result - } - } - - if atta { - encoder.Encode(response.Attachments) // attachments - } - } - } else { - if response.Exception != nil { // throw error - encoder.Encode(response.Exception.Error()) - } else { - encoder.Encode(response.RspObj) - } - } - - byteArray = encoder.Buffer() - byteArray = EncNull(byteArray) // if not, "java client" will throw exception "unexpected end of file" - pkgLen := len(byteArray) - if pkgLen > int(DEFAULT_LEN) { // 8M - return nil, perrors.Errorf("Data length %d too large, max payload %d", pkgLen, DEFAULT_LEN) - } - // byteArray{body length} - binary.BigEndian.PutUint32(byteArray[12:], uint32(pkgLen-HEADER_LENGTH)) - return byteArray, nil -} - -// hessian decode response body -func unpackResponseBody(decoder *Decoder, resp interface{}) error { - // body - if decoder == nil { - return perrors.Errorf("@decoder is nil") - } - rspType, err := decoder.Decode() - if err != nil { - return perrors.WithStack(err) - } - - response := EnsureResponse(resp) - - switch rspType { - case RESPONSE_WITH_EXCEPTION, RESPONSE_WITH_EXCEPTION_WITH_ATTACHMENTS: - expt, decErr := decoder.Decode() - if decErr != nil { - return perrors.WithStack(decErr) - } - if rspType == RESPONSE_WITH_EXCEPTION_WITH_ATTACHMENTS { - attachments, attErr := decoder.Decode() - if attErr != nil { - return perrors.WithStack(attErr) - } - if v, ok := attachments.(map[interface{}]interface{}); ok { - atta := ToMapStringString(v) - response.Attachments = atta - } else { - return perrors.Errorf("get wrong attachments: %+v", attachments) - } - } - - if e, ok := expt.(error); ok { - response.Exception = e - } else { - response.Exception = perrors.Errorf("got exception: %+v", expt) - } - return nil - - case RESPONSE_VALUE, RESPONSE_VALUE_WITH_ATTACHMENTS: - rsp, decErr := decoder.Decode() - if decErr != nil { - return perrors.WithStack(decErr) - } - if rspType == RESPONSE_VALUE_WITH_ATTACHMENTS { - attachments, attErr := decoder.Decode() - if attErr != nil { - return perrors.WithStack(attErr) - } - if v, ok := attachments.(map[interface{}]interface{}); ok { - response.Attachments = ToMapStringString(v) - } else { - return perrors.Errorf("get wrong attachments: %+v", attachments) - } - } - - response.RspObj = rsp - - return nil - - case RESPONSE_NULL_VALUE, RESPONSE_NULL_VALUE_WITH_ATTACHMENTS: - if rspType == RESPONSE_NULL_VALUE_WITH_ATTACHMENTS { - attachments, decErr := decoder.Decode() - if decErr != nil { - return perrors.WithStack(decErr) - } - if v, ok := attachments.(map[interface{}]interface{}); ok { - atta := ToMapStringString(v) - response.Attachments = atta - } else { - return perrors.Errorf("get wrong attachments: %+v", attachments) - } - } - return nil - } - - return nil -} - -// CopySlice copy from inSlice to outSlice -func CopySlice(inSlice, outSlice reflect.Value) error { - if inSlice.IsNil() { - return perrors.New("@in is nil") - } - if inSlice.Kind() != reflect.Slice { - return perrors.Errorf("@in is not slice, but %v", inSlice.Kind()) - } - - for outSlice.Kind() == reflect.Ptr { - outSlice = outSlice.Elem() - } - - size := inSlice.Len() - outSlice.Set(reflect.MakeSlice(outSlice.Type(), size, size)) - - for i := 0; i < size; i++ { - inSliceValue := inSlice.Index(i) - if !inSliceValue.Type().AssignableTo(outSlice.Index(i).Type()) { - return perrors.Errorf("in element type [%s] can not assign to out element type [%s]", - inSliceValue.Type().String(), outSlice.Type().String()) - } - outSlice.Index(i).Set(inSliceValue) - } - - return nil -} - -// CopyMap copy from in map to out map -func CopyMap(inMapValue, outMapValue reflect.Value) error { - if inMapValue.IsNil() { - return perrors.New("@in is nil") - } - if !inMapValue.CanInterface() { - return perrors.New("@in's Interface can not be used.") - } - if inMapValue.Kind() != reflect.Map { - return perrors.Errorf("@in is not map, but %v", inMapValue.Kind()) - } - - outMapType := UnpackPtrType(outMapValue.Type()) - SetValue(outMapValue, reflect.MakeMap(outMapType)) - - outKeyType := outMapType.Key() - - outMapValue = UnpackPtrValue(outMapValue) - outValueType := outMapValue.Type().Elem() - - for _, inKey := range inMapValue.MapKeys() { - inValue := inMapValue.MapIndex(inKey) - - if !inKey.Type().AssignableTo(outKeyType) { - return perrors.Errorf("in Key:{type:%s, value:%#v} can not assign to out Key:{type:%s} ", - inKey.Type().String(), inKey, outKeyType.String()) - } - if !inValue.Type().AssignableTo(outValueType) { - return perrors.Errorf("in Value:{type:%s, value:%#v} can not assign to out value:{type:%s}", - inValue.Type().String(), inValue, outValueType.String()) - } - outMapValue.SetMapIndex(inKey, inValue) - } - - return nil -} - -// ReflectResponse reflect return value -// TODO response object should not be copied again to another object, it should be the exact type of the object -func ReflectResponse(in interface{}, out interface{}) error { - if in == nil { - return perrors.Errorf("@in is nil") - } - - if out == nil { - return perrors.Errorf("@out is nil") - } - if reflect.TypeOf(out).Kind() != reflect.Ptr { - return perrors.Errorf("@out should be a pointer") - } - - inValue := EnsurePackValue(in) - outValue := EnsurePackValue(out) - - outType := outValue.Type().String() - if outType == "interface {}" || outType == "*interface {}" { - SetValue(outValue, inValue) - return nil - } - - switch inValue.Type().Kind() { - case reflect.Slice, reflect.Array: - return CopySlice(inValue, outValue) - case reflect.Map: - return CopyMap(inValue, outValue) - default: - SetValue(outValue, inValue) - } - - return nil -} - -var versionInt = make(map[string]int) - -// https://github.com/apache/dubbo/blob/dubbo-2.7.1/dubbo-common/src/main/java/org/apache/dubbo/common/Version.java#L96 -// isSupportResponseAttachment is for compatibility among some dubbo version -func isSupportResponseAttachment(version string) bool { - if len(version) == 0 { - return false - } - - v, ok := versionInt[version] - if !ok { - v = version2Int(version) - if v == -1 { - return false - } - } - - if v >= 2001000 && v <= 2060200 { // 2.0.10 ~ 2.6.2 - return false - } - return v >= LOWEST_VERSION_FOR_RESPONSE_ATTACHMENT -} - -func version2Int(version string) int { - if len(version) == 0 { - return 0 - } - v := 0 - varr := strings.Split(version, ".") - length := len(varr) - for key, value := range varr { - v0, err := strconv.Atoi(value) - if err != nil { - return -1 - } - v += v0 * int(math.Pow10((length-key-1)*2)) - } - if length == 3 { - return v * 100 - } - return v -} diff --git a/response_test.go b/response_test.go deleted file mode 100644 index 7632657b..00000000 --- a/response_test.go +++ /dev/null @@ -1,220 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package hessian - -import ( - "reflect" - "testing" -) - -import ( - "github.com/stretchr/testify/assert" -) - -func doTestReflectResponse(t *testing.T, in interface{}, out interface{}) { - err := ReflectResponse(in, out) - if err != nil { - t.Error(err) - t.FailNow() - } - - result := UnpackPtrValue(reflect.ValueOf(out)).Interface() - - equal := reflect.DeepEqual(in, result) - if !equal { - t.Errorf("expect [%v]: %v, but got [%v]: %v", reflect.TypeOf(in), in, reflect.TypeOf(result), result) - } -} - -func TestReflectResponse(t *testing.T) { - var b bool - doTestReflectResponse(t, true, &b) - doTestReflectResponse(t, false, &b) - - var i int - doTestReflectResponse(t, 123, &i) - doTestReflectResponse(t, 234, &i) - - var i16 int16 - doTestReflectResponse(t, int16(456), &i16) - - var i64 int64 - doTestReflectResponse(t, int64(789), &i64) - - var s string - doTestReflectResponse(t, "hello world", &s) - - type rr struct { - Name string - Num int - } - - var r1 rr - doTestReflectResponse(t, rr{"dubbogo", 32}, &r1) - - // ------ map test ------- - m1 := make(map[interface{}]interface{}) - var m1r map[interface{}]interface{} - m1["hello"] = "world" - m1[1] = "go" - m1["dubbo"] = 666 - doTestReflectResponse(t, m1, &m1r) - - m2 := make(map[string]string) - var m2r map[string]string - m2["hello"] = "world" - m2["dubbo"] = "666" - doTestReflectResponse(t, m2, &m2r) - - m3 := make(map[string]rr) - var m3r map[string]rr - m3["dubbo"] = rr{"hello", 123} - m3["go"] = rr{"world", 456} - doTestReflectResponse(t, m3, &m3r) - - // ------ slice test ------- - s1 := []string{"abc", "def", "hello", "world"} - var s1r []string - doTestReflectResponse(t, s1, &s1r) - - s2 := []rr{{"dubbo", 666}, {"go", 999}} - var s2r []rr - doTestReflectResponse(t, s2, &s2r) - - s3 := []interface{}{rr{"dubbo", 666}, 123, "hello"} - var s3r []interface{} - doTestReflectResponse(t, s3, &s3r) - - // ------ interface test ------- - in1 := []interface{}{rr{"dubbo", 666}, 123, "hello"} - var inr1 *interface{} - doTestReflectResponse(t, in1, reflect.New(reflect.TypeOf(inr1).Elem()).Interface()) - - in2 := make(map[string]rr) - var inr2 map[string]rr - m3["dubbo"] = rr{"hello", 123} - m3["go"] = rr{"world", 456} - doTestReflectResponse(t, in2, &inr2) -} - -// separately test copy normal map to map[interface{}]interface{} -func TestCopyMap(t *testing.T) { - type rr struct { - Name string - Num int - } - - m3 := make(map[string]rr) - var m3r map[interface{}]interface{} - r1 := rr{"hello", 123} - r2 := rr{"world", 456} - m3["dubbo"] = r1 - m3["go"] = r2 - - err := ReflectResponse(m3, &m3r) - if err != nil { - t.Error(err) - t.FailNow() - } - - assert.Equal(t, 2, len(m3r)) - - rr1, ok := m3r["dubbo"] - assert.True(t, ok) - assert.True(t, reflect.DeepEqual(r1, rr1)) - - rr2, ok := m3r["go"] - assert.True(t, ok) - assert.True(t, reflect.DeepEqual(r2, rr2)) -} - -// separately test copy normal slice to []interface{} -func TestCopySlice(t *testing.T) { - type rr struct { - Name string - Num int - } - - r1 := rr{"hello", 123} - r2 := rr{"world", 456} - - s1 := []rr{r1, r2} - var s1r []interface{} - - err := ReflectResponse(s1, &s1r) - if err != nil { - t.Error(err) - t.FailNow() - } - - assert.Equal(t, 2, len(s1r)) - assert.True(t, reflect.DeepEqual(r1, s1r[0])) - assert.True(t, reflect.DeepEqual(r2, s1r[1])) -} - -func TestIsSupportResponseAttachment(t *testing.T) { - is := isSupportResponseAttachment("2.0.10") - assert.False(t, is) - - is = isSupportResponseAttachment("2.5.3") - assert.False(t, is) - - is = isSupportResponseAttachment("2.6.2") - assert.False(t, is) - - is = isSupportResponseAttachment("1.5.5") - assert.False(t, is) - - is = isSupportResponseAttachment("0.0.0") - assert.False(t, is) - - is = isSupportResponseAttachment("2.0.2") - assert.True(t, is) - - is = isSupportResponseAttachment("2.7.2") - assert.True(t, is) -} - -func TestVersion2Int(t *testing.T) { - v := version2Int("2.1.3") - assert.Equal(t, 2010300, v) - - v = version2Int("22.11.33") - assert.Equal(t, 22113300, v) - - v = version2Int("222.111.333") - assert.Equal(t, 223143300, v) - - v = version2Int("220.110.333") - assert.Equal(t, 221133300, v) - - v = version2Int("229.119.333") - assert.Equal(t, 230223300, v) - - v = version2Int("2222.1111.3333") - assert.Equal(t, 2233443300, v) - - v = version2Int("2.11") - assert.Equal(t, 211, v) - - v = version2Int("2.1.3.4") - assert.Equal(t, 2010304, v) - - v = version2Int("2.1.3.4.5") - assert.Equal(t, 201030405, v) -}