go-micro.dev/v5@v5.12.0/server/grpc/server.go (about) 1 package grpc 2 3 // Copyright 2009 The Go Authors. All rights reserved. 4 // Use of this source code is governed by a BSD-style 5 // license that can be found in the LICENSE file. 6 // 7 // Meh, we need to get rid of this shit 8 9 import ( 10 "context" 11 "errors" 12 "reflect" 13 "sync" 14 "unicode" 15 "unicode/utf8" 16 17 "go-micro.dev/v5/logger" 18 "go-micro.dev/v5/server" 19 ) 20 21 var ( 22 // Precompute the reflect type for error. Can't use error directly 23 // because Typeof takes an empty interface value. This is annoying. 24 typeOfError = reflect.TypeOf((*error)(nil)).Elem() 25 ) 26 27 type methodType struct { 28 method reflect.Method 29 ArgType reflect.Type 30 ReplyType reflect.Type 31 ContextType reflect.Type 32 stream bool 33 } 34 35 type service struct { 36 name string // name of service 37 rcvr reflect.Value // receiver of methods for the service 38 typ reflect.Type // type of the receiver 39 method map[string]*methodType // registered methods 40 } 41 42 // server represents an RPC Server. 43 type rServer struct { 44 mu sync.Mutex // protects the serviceMap 45 serviceMap map[string]*service 46 logger logger.Logger 47 } 48 49 // Is this an exported - upper case - name? 50 func isExported(name string) bool { 51 rune, _ := utf8.DecodeRuneInString(name) 52 return unicode.IsUpper(rune) 53 } 54 55 // Is this type exported or a builtin? 56 func isExportedOrBuiltinType(t reflect.Type) bool { 57 for t.Kind() == reflect.Ptr { 58 t = t.Elem() 59 } 60 // PkgPath will be non-empty even for an exported type, 61 // so we need to check the type name as well. 62 return isExported(t.Name()) || t.PkgPath() == "" 63 } 64 65 // prepareEndpoint() returns a methodType for the provided method or nil 66 // in case if the method was unsuitable. 67 func prepareEndpoint(method reflect.Method, log logger.Logger) *methodType { 68 mtype := method.Type 69 mname := method.Name 70 var replyType, argType, contextType reflect.Type 71 var stream bool 72 73 // Endpoint() must be exported. 74 if method.PkgPath != "" { 75 return nil 76 } 77 78 switch mtype.NumIn() { 79 case 3: 80 // assuming streaming 81 argType = mtype.In(2) 82 contextType = mtype.In(1) 83 stream = true 84 case 4: 85 // method that takes a context 86 argType = mtype.In(2) 87 replyType = mtype.In(3) 88 contextType = mtype.In(1) 89 default: 90 log.Logf(logger.ErrorLevel, "method %v of %v has wrong number of ins: %v", mname, mtype, mtype.NumIn()) 91 return nil 92 } 93 94 if stream { 95 // check stream type 96 streamType := reflect.TypeOf((*server.Stream)(nil)).Elem() 97 if !argType.Implements(streamType) { 98 log.Logf(logger.ErrorLevel, "%v argument does not implement Streamer interface: %v", mname, argType) 99 return nil 100 } 101 } else { 102 // if not stream check the replyType 103 104 // First arg need not be a pointer. 105 if !isExportedOrBuiltinType(argType) { 106 log.Logf(logger.ErrorLevel, "%v argument type not exported: %v", mname, argType) 107 return nil 108 } 109 110 if replyType.Kind() != reflect.Ptr { 111 log.Logf(logger.ErrorLevel, "method %v reply type not a pointer: %v", mname, replyType) 112 return nil 113 } 114 115 // Reply type must be exported. 116 if !isExportedOrBuiltinType(replyType) { 117 log.Logf(logger.ErrorLevel, "method %v reply type not exported: %v", mname, replyType) 118 return nil 119 } 120 } 121 122 // Endpoint() needs one out. 123 if mtype.NumOut() != 1 { 124 log.Logf(logger.ErrorLevel, "method %v has wrong number of outs: %v", mname, mtype.NumOut()) 125 return nil 126 } 127 // The return type of the method must be error. 128 if returnType := mtype.Out(0); returnType != typeOfError { 129 log.Logf(logger.ErrorLevel, "method %v returns %v not error", mname, returnType.String()) 130 return nil 131 } 132 return &methodType{method: method, ArgType: argType, ReplyType: replyType, ContextType: contextType, stream: stream} 133 } 134 135 func (server *rServer) register(rcvr interface{}) error { 136 server.mu.Lock() 137 defer server.mu.Unlock() 138 log := server.logger 139 if server.serviceMap == nil { 140 server.serviceMap = make(map[string]*service) 141 } 142 s := new(service) 143 s.typ = reflect.TypeOf(rcvr) 144 s.rcvr = reflect.ValueOf(rcvr) 145 sname := reflect.Indirect(s.rcvr).Type().Name() 146 if sname == "" { 147 logger.Fatalf("rpc: no service name for type %v", s.typ.String()) 148 } 149 if !isExported(sname) { 150 s := "rpc Register: type " + sname + " is not exported" 151 log.Log(logger.ErrorLevel, s) 152 return errors.New(s) 153 } 154 if _, present := server.serviceMap[sname]; present { 155 return errors.New("rpc: service already defined: " + sname) 156 } 157 s.name = sname 158 s.method = make(map[string]*methodType) 159 160 // Install the methods 161 for m := 0; m < s.typ.NumMethod(); m++ { 162 method := s.typ.Method(m) 163 if mt := prepareEndpoint(method, log); mt != nil { 164 s.method[method.Name] = mt 165 } 166 } 167 168 if len(s.method) == 0 { 169 s := "rpc Register: type " + sname + " has no exported methods of suitable type" 170 log.Log(logger.ErrorLevel, s) 171 return errors.New(s) 172 } 173 server.serviceMap[s.name] = s 174 return nil 175 } 176 177 func (m *methodType) prepareContext(ctx context.Context) reflect.Value { 178 if contextv := reflect.ValueOf(ctx); contextv.IsValid() { 179 return contextv 180 } 181 return reflect.Zero(m.ContextType) 182 }