github.com/lzy4123/fabric@v2.1.1+incompatible/core/dispatcher/dispatcher.go (about) 1 /* 2 Copyright IBM Corp. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package dispatcher 8 9 import ( 10 "reflect" 11 12 "github.com/golang/protobuf/proto" 13 "github.com/pkg/errors" 14 ) 15 16 // Dispatcher is used to handle the boilerplate proto tasks of unmarshaling inputs and remarshaling outputs 17 // so that the receiver may focus on the implementation details rather than the proto hassles. 18 type Dispatcher struct { 19 // Protobuf should pass through to Google Protobuf in production paths 20 Protobuf Protobuf 21 } 22 23 // Dispatch deserializes the input bytes to the correct type for the method in the receiver, then 24 // if successful, marshals the output message to bytes and returns it. On error, it simply returns 25 // the error. The method on the receiver must take a single parameter which is a concrete proto 26 // message type and it should return a proto message and error. 27 func (d *Dispatcher) Dispatch(inputBytes []byte, methodName string, receiver interface{}) ([]byte, error) { 28 method := reflect.ValueOf(receiver).MethodByName(methodName) 29 30 if method == (reflect.Value{}) { 31 return nil, errors.Errorf("receiver %T.%s does not exist", receiver, methodName) 32 } 33 34 if method.Type().NumIn() != 1 { 35 return nil, errors.Errorf("receiver %T.%s has %d parameters but expected 1", receiver, methodName, method.Type().NumIn()) 36 } 37 38 inputType := method.Type().In(0) 39 if inputType.Kind() != reflect.Ptr { 40 return nil, errors.Errorf("receiver %T.%s does not accept a pointer as its argument", receiver, methodName) 41 } 42 43 if method.Type().NumOut() != 2 { 44 return nil, errors.Errorf("receiver %T.%s returns %d values but expected 2", receiver, methodName, method.Type().NumOut()) 45 } 46 47 if !method.Type().Out(0).Implements(reflect.TypeOf((*proto.Message)(nil)).Elem()) { 48 return nil, errors.Errorf("receiver %T.%s does not return a an implementor of proto.Message as its first return value", receiver, methodName) 49 } 50 51 if !method.Type().Out(1).Implements(reflect.TypeOf((*error)(nil)).Elem()) { 52 return nil, errors.Errorf("receiver %T.%s does not return an error as its second return value", receiver, methodName) 53 } 54 55 inputValue := reflect.New(inputType.Elem()) 56 inputMsg, ok := inputValue.Interface().(proto.Message) 57 if !ok { 58 return nil, errors.Errorf("receiver %T.%s does not accept a proto.Message as its argument, it is '%T'", receiver, methodName, inputValue.Interface()) 59 } 60 61 err := d.Protobuf.Unmarshal(inputBytes, inputMsg) 62 if err != nil { 63 return nil, errors.WithMessagef(err, "could not decode input arg for %T.%s", receiver, methodName) 64 } 65 66 outputVals := method.Call([]reflect.Value{inputValue}) 67 68 if !outputVals[1].IsNil() { 69 return nil, outputVals[1].Interface().(error) 70 } 71 72 if outputVals[0].IsNil() { 73 return nil, errors.Errorf("receiver %T.%s returned (nil, nil) which is not allowed", receiver, methodName) 74 } 75 76 outputMsg := outputVals[0].Interface().(proto.Message) 77 78 resultBytes, err := d.Protobuf.Marshal(outputMsg) 79 if err != nil { 80 return nil, errors.WithMessagef(err, "failed to marshal result for %T.%s", receiver, methodName) 81 } 82 83 return resultBytes, nil 84 }