response.go (282 lines of code) (raw):

/* * 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 }