package config import ( "reflect" "sync" "unicode" "unicode/utf8" ) import ( log "github.com/AlexStocks/log4go" ) // rpc service interface type RPCService interface { Service() string // Service Interface Version() string } var ( // A value sent as a placeholder for the server's response value when the server // receives an invalid request. It is never decoded by the client since the Response // contains an error when it is used. invalidRequest = struct{}{} // Precompute the reflect type for error. Can't use error directly // because Typeof takes an empty interface value. This is annoying. typeOfError = reflect.TypeOf((*error)(nil)).Elem() ServiceMap = &serviceMap{ serviceMap: make(map[string]*Service), } ) // info of method type MethodType struct { method reflect.Method ctxType reflect.Type // type of the request context argType reflect.Type replyType reflect.Type } func (m *MethodType) Method() reflect.Method { return m.method } func (m *MethodType) CtxType() reflect.Type { return m.ctxType } func (m *MethodType) ArgType() reflect.Type { return m.argType } func (m *MethodType) ReplyType() reflect.Type { return m.replyType } // info of service interface type Service struct { name string rcvr reflect.Value rcvrType reflect.Type method map[string]*MethodType } func (s *Service) Method() map[string]*MethodType { return s.method } func (s *Service) RcvrType() reflect.Type { return s.rcvrType } func (s *Service) Rcvr() reflect.Value { return s.rcvr } type serviceMap struct { mutex sync.Mutex // protects the serviceMap serviceMap map[string]*Service // service name -> service } func (sm *serviceMap) GetService(name string) *Service { return sm.serviceMap[name] } // Is this an exported - upper case - name func isExported(name string) bool { rune, _ := utf8.DecodeRuneInString(name) return unicode.IsUpper(rune) } // Is this type exported or a builtin? func isExportedOrBuiltinType(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() == "" } // suitableMethods returns suitable Rpc methods of typ func suitableMethods(typ reflect.Type) (string, map[string]*MethodType) { methods := make(map[string]*MethodType) mts := "" for m := 0; m < typ.NumMethod(); m++ { method := typ.Method(m) if mt := suiteMethod(method); mt != nil { methods[method.Name] = mt mts += method.Name + "," } } return mts, methods } // suiteMethod returns a suitable Rpc methodType func suiteMethod(method reflect.Method) *MethodType { mtype := method.Type mname := method.Name // Method must be exported. if method.PkgPath != "" { return nil } var replyType, argType, ctxType reflect.Type switch mtype.NumIn() { case 3: argType = mtype.In(1) replyType = mtype.In(2) case 4: ctxType = mtype.In(1) argType = mtype.In(2) replyType = mtype.In(3) default: log.Error("method %s of mtype %v has wrong number of in parameters %d; needs exactly 3/4", mname, mtype, mtype.NumIn()) return nil } // First arg need not be a pointer. if !isExportedOrBuiltinType(argType) { log.Error("argument type of method %q is not exported %v", mname, argType) return nil } // Second arg must be a pointer. if replyType.Kind() != reflect.Ptr { log.Error("reply type of method %q is not a pointer %v", mname, replyType) return nil } // Reply type must be exported. if !isExportedOrBuiltinType(replyType) { log.Error("reply type of method %s not exported{%v}", mname, replyType) return nil } // Method needs one out. if mtype.NumOut() != 1 { log.Error("method %q has %d out parameters; needs exactly 1", mname, mtype.NumOut()) return nil } // The return type of the method must be error. if returnType := mtype.Out(0); returnType != typeOfError { log.Error("return type %s of method %q is not error", returnType, mname) return nil } return &MethodType{method: method, argType: argType, replyType: replyType, ctxType: ctxType} }