rpc/service_map.go

221 lines
5.3 KiB
Go
Raw Normal View History

2017-10-25 14:52:47 +00:00
package rpc
import (
"fmt"
"reflect"
"strings"
"sync"
"unicode"
"unicode/utf8"
)
var (
// Precompute the reflect.Type of error and http.Request
2017-10-27 03:06:50 +00:00
typeOfError = reflect.TypeOf((*error)(nil)).Elem()
2017-10-25 14:52:47 +00:00
)
// ----------------------------------------------------------------------------
// service
// ----------------------------------------------------------------------------
type service struct {
2017-11-02 10:29:37 +00:00
name string // name of service
rcvrV reflect.Value // receiver of methods for the service
rcvrT reflect.Type // type of the receiver
methods map[string]*serviceMethod // registered methods
2017-10-25 14:52:47 +00:00
}
type serviceMethod struct {
2017-11-03 06:33:50 +00:00
method reflect.Method // receiver method
paramTypes []reflect.Type // type of the request argument
returnType reflect.Type // type of the response argument
2017-10-25 14:52:47 +00:00
}
2017-11-03 06:33:50 +00:00
func (sm *serviceMethod) getParamValues() (values []reflect.Value, instances []interface{}) {
if nil == sm.paramTypes || 0 == len(sm.paramTypes) {
return nil, nil
2017-11-02 06:39:30 +00:00
}
2017-11-03 06:33:50 +00:00
pCount := len(sm.paramTypes)
values = make([]reflect.Value, pCount)
2017-11-02 06:39:30 +00:00
instances = make([]interface{}, pCount)
for indexI := 0; indexI < pCount; indexI++ {
2017-11-03 06:33:50 +00:00
values[indexI] = getValue(sm.paramTypes[indexI])
instances[indexI] = values[indexI].Interface()
2017-11-02 06:39:30 +00:00
}
return
}
2017-10-31 09:25:44 +00:00
2017-11-03 06:33:50 +00:00
func getValue(t reflect.Type) reflect.Value {
rt := t
if rt.Kind() == reflect.Ptr {
rt = rt.Elem()
}
2017-11-03 12:23:12 +00:00
// rv := reflect.New(rt)
// if rt.Kind() != reflect.Struct {
// rv = reflect.Indirect(rv)
// }
// var rv reflect.Value
// switch rt.Kind() {
// case reflect.Slice:
// rv = reflect.New(reflect.SliceOf(rt.Elem()))
// default:
// rv = reflect.New(rt)
// }
return reflect.New(rt)
2017-11-03 06:33:50 +00:00
}
2017-10-25 14:52:47 +00:00
// ----------------------------------------------------------------------------
// serviceMap
// ----------------------------------------------------------------------------
// serviceMap is a registry for services.
type serviceMap struct {
mutex sync.Mutex
services map[string]*service
}
// register adds a new service using reflection to extract its methods.
2017-11-02 10:41:51 +00:00
func (sm *serviceMap) register(rcvr interface{}, name string) error {
2017-10-25 14:52:47 +00:00
// Setup service.
s := &service{
2017-11-02 10:29:37 +00:00
name: name,
rcvrV: reflect.ValueOf(rcvr),
rcvrT: reflect.TypeOf(rcvr),
methods: make(map[string]*serviceMethod),
2017-10-25 14:52:47 +00:00
}
if name == "" {
2017-11-02 10:29:37 +00:00
s.name = reflect.Indirect(s.rcvrV).Type().Name()
2017-10-25 14:52:47 +00:00
if !isExported(s.name) {
return fmt.Errorf("rpc: type %q is not exported", s.name)
}
}
if s.name == "" {
return fmt.Errorf("rpc: no service name for type %q",
2017-11-02 10:29:37 +00:00
s.rcvrT.String())
2017-10-25 14:52:47 +00:00
}
2017-10-27 04:00:30 +00:00
2017-11-02 10:29:37 +00:00
var err error
2017-10-25 14:52:47 +00:00
// Setup methods.
2017-11-02 06:39:30 +00:00
Loop:
2017-11-02 10:29:37 +00:00
for i := 0; i < s.rcvrT.NumMethod(); i++ {
m := s.rcvrT.Method(i)
mt := m.Type
2017-10-25 14:52:47 +00:00
// Method must be exported.
2017-11-02 10:29:37 +00:00
if m.PkgPath != "" {
2017-10-25 14:52:47 +00:00
continue
}
2017-10-27 03:06:50 +00:00
2017-11-02 06:39:30 +00:00
var paramTypes []reflect.Type
2017-11-02 10:29:37 +00:00
var returnType reflect.Type
pCount := mt.NumIn() - 1
if 0 < pCount {
paramTypes = make([]reflect.Type, pCount)
2017-11-02 06:39:30 +00:00
2017-11-02 10:29:37 +00:00
for indexI := 0; indexI < pCount; indexI++ {
2017-11-03 06:33:50 +00:00
pt := mt.In(indexI + 1)
if err = validateType(mt.In(indexI + 1)); nil != err {
2017-11-02 10:29:37 +00:00
return err
2017-11-02 06:39:30 +00:00
}
2017-11-03 06:33:50 +00:00
paramTypes[indexI] = pt
2017-11-02 06:39:30 +00:00
}
2017-10-25 14:52:47 +00:00
}
2017-11-02 06:39:30 +00:00
2017-11-02 10:29:37 +00:00
switch mt.NumOut() {
2017-11-02 06:39:30 +00:00
case 1:
2017-11-02 10:29:37 +00:00
if t := mt.Out(0); t != typeOfError {
2017-11-02 06:39:30 +00:00
continue Loop
}
case 2:
2017-11-02 10:29:37 +00:00
if t := mt.Out(0); !isExportedOrBuiltin(t) {
2017-11-02 06:39:30 +00:00
continue Loop
}
2017-11-02 10:29:37 +00:00
if t := mt.Out(1); t != typeOfError {
2017-11-02 06:39:30 +00:00
continue Loop
}
2017-11-03 06:33:50 +00:00
rt := mt.Out(0)
if err = validateType(rt); nil != err {
2017-11-02 10:29:37 +00:00
return err
}
2017-11-03 06:33:50 +00:00
returnType = rt
2017-11-02 06:39:30 +00:00
default:
2017-10-25 14:52:47 +00:00
continue
}
2017-11-02 06:39:30 +00:00
2017-11-02 10:29:37 +00:00
s.methods[m.Name] = &serviceMethod{
2017-11-03 06:33:50 +00:00
method: m,
paramTypes: paramTypes,
returnType: returnType,
2017-10-25 14:52:47 +00:00
}
}
if len(s.methods) == 0 {
2017-11-02 06:39:30 +00:00
return fmt.Errorf("rpc: %q has no exported methods of suitable type", s.name)
2017-10-25 14:52:47 +00:00
}
// Add to the map.
2017-11-02 10:41:51 +00:00
sm.mutex.Lock()
defer sm.mutex.Unlock()
if sm.services == nil {
sm.services = make(map[string]*service)
} else if _, ok := sm.services[s.name]; ok {
2017-10-25 14:52:47 +00:00
return fmt.Errorf("rpc: service already defined: %q", s.name)
}
2017-11-02 10:41:51 +00:00
sm.services[s.name] = s
2017-10-25 14:52:47 +00:00
return nil
}
2017-11-03 06:33:50 +00:00
func validateType(t reflect.Type) error {
2017-11-02 10:29:37 +00:00
if t.Kind() == reflect.Struct {
2017-11-03 06:33:50 +00:00
return fmt.Errorf("Type is Struct. Pass by reference, i.e. *%s", t)
2017-11-02 10:29:37 +00:00
}
2017-11-03 06:33:50 +00:00
return nil
2017-11-02 10:29:37 +00:00
}
2017-10-25 14:52:47 +00:00
// get returns a registered service given a method name.
//
// The method name uses a dotted notation as in "Service.Method".
2017-11-02 10:41:51 +00:00
func (sm *serviceMap) get(method string) (*service, *serviceMethod, error) {
2017-10-25 14:52:47 +00:00
parts := strings.Split(method, ".")
if len(parts) != 2 {
err := fmt.Errorf("rpc: service/method request ill-formed: %q", method)
return nil, nil, err
}
2017-11-02 10:41:51 +00:00
sm.mutex.Lock()
service := sm.services[parts[0]]
sm.mutex.Unlock()
2017-10-25 14:52:47 +00:00
if service == nil {
err := fmt.Errorf("rpc: can't find service %q", method)
return nil, nil, err
}
serviceMethod := service.methods[parts[1]]
if serviceMethod == nil {
err := fmt.Errorf("rpc: can't find method %q", method)
return nil, nil, err
}
return service, serviceMethod, nil
}
// isExported returns true of a string is an exported (upper case) name.
func isExported(name string) bool {
rune, _ := utf8.DecodeRuneInString(name)
return unicode.IsUpper(rune)
}
// isExportedOrBuiltin returns true if a type is exported or a builtin.
func isExportedOrBuiltin(t reflect.Type) bool {
for t.Kind() == reflect.Ptr {
t = t.Elem()
}
// PkgPath will be non-empty even for an exported type,
// so we need to check the type name as well.
return isExported(t.Name()) || t.PkgPath() == ""
}