github.com/tickoalcantara12/micro/v3@v3.0.0-20221007104245-9d75b9bcbab9/service/server/grpc/server.go (about) 1 // Copyright 2020 Asim Aslam 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // https://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 // 15 // Original source: github.com/micro/go-micro/v3/server/grpc/server.go 16 17 package grpc 18 19 // Copyright 2009 The Go Authors. All rights reserved. 20 // Use of this source code is governed by a BSD-style 21 // license that can be found in the LICENSE file. 22 // 23 // Meh, we need to get rid of this shit 24 25 import ( 26 "context" 27 "errors" 28 "reflect" 29 "sync" 30 "unicode" 31 "unicode/utf8" 32 33 "github.com/tickoalcantara12/micro/v3/service/logger" 34 "github.com/tickoalcantara12/micro/v3/service/server" 35 ) 36 37 var ( 38 // Precompute the reflect type for error. Can't use error directly 39 // because Typeof takes an empty interface value. This is annoying. 40 typeOfError = reflect.TypeOf((*error)(nil)).Elem() 41 ) 42 43 type methodType struct { 44 method reflect.Method 45 ArgType reflect.Type 46 ReplyType reflect.Type 47 ContextType reflect.Type 48 stream bool 49 } 50 51 type service struct { 52 name string // name of service 53 rcvr reflect.Value // receiver of methods for the service 54 typ reflect.Type // type of the receiver 55 method map[string]*methodType // registered methods 56 } 57 58 // server represents an RPC Server. 59 type rServer struct { 60 mu sync.Mutex // protects the serviceMap 61 serviceMap map[string]*service 62 } 63 64 // Is this an exported - upper case - name? 65 func isExported(name string) bool { 66 rune, _ := utf8.DecodeRuneInString(name) 67 return unicode.IsUpper(rune) 68 } 69 70 // Is this type exported or a builtin? 71 func isExportedOrBuiltinType(t reflect.Type) bool { 72 for t.Kind() == reflect.Ptr { 73 t = t.Elem() 74 } 75 // PkgPath will be non-empty even for an exported type, 76 // so we need to check the type name as well. 77 return isExported(t.Name()) || t.PkgPath() == "" 78 } 79 80 // prepareEndpoint() returns a methodType for the provided method or nil 81 // in case if the method was unsuitable. 82 func prepareEndpoint(method reflect.Method) *methodType { 83 mtype := method.Type 84 mname := method.Name 85 var replyType, argType, contextType reflect.Type 86 var stream bool 87 88 // Endpoint() must be exported. 89 if method.PkgPath != "" { 90 return nil 91 } 92 93 switch mtype.NumIn() { 94 case 3: 95 // assuming streaming 96 argType = mtype.In(2) 97 contextType = mtype.In(1) 98 stream = true 99 case 4: 100 // method that takes a context 101 argType = mtype.In(2) 102 replyType = mtype.In(3) 103 contextType = mtype.In(1) 104 default: 105 if logger.V(logger.ErrorLevel, logger.DefaultLogger) { 106 logger.Errorf("method %v of %v has wrong number of ins: %v", mname, mtype, mtype.NumIn()) 107 } 108 return nil 109 } 110 111 if stream { 112 // check stream type 113 streamType := reflect.TypeOf((*server.Stream)(nil)).Elem() 114 if !argType.Implements(streamType) { 115 if logger.V(logger.ErrorLevel, logger.DefaultLogger) { 116 logger.Errorf("%v argument does not implement Streamer interface: %v", mname, argType) 117 } 118 return nil 119 } 120 } else { 121 // if not stream check the replyType 122 123 // First arg need not be a pointer. 124 if !isExportedOrBuiltinType(argType) { 125 if logger.V(logger.ErrorLevel, logger.DefaultLogger) { 126 logger.Errorf("%v argument type not exported: %v", mname, argType) 127 } 128 return nil 129 } 130 131 if replyType.Kind() != reflect.Ptr { 132 if logger.V(logger.ErrorLevel, logger.DefaultLogger) { 133 logger.Errorf("method %v reply type not a pointer: %v", mname, replyType) 134 } 135 return nil 136 } 137 138 // Reply type must be exported. 139 if !isExportedOrBuiltinType(replyType) { 140 if logger.V(logger.ErrorLevel, logger.DefaultLogger) { 141 logger.Errorf("method %v reply type not exported: %v", mname, replyType) 142 } 143 return nil 144 } 145 } 146 147 // Endpoint() needs one out. 148 if mtype.NumOut() != 1 { 149 if logger.V(logger.ErrorLevel, logger.DefaultLogger) { 150 logger.Errorf("method %v has wrong number of outs: %v", mname, mtype.NumOut()) 151 } 152 return nil 153 } 154 // The return type of the method must be error. 155 if returnType := mtype.Out(0); returnType != typeOfError { 156 if logger.V(logger.ErrorLevel, logger.DefaultLogger) { 157 logger.Errorf("method %v returns %v not error", mname, returnType.String()) 158 } 159 return nil 160 } 161 return &methodType{method: method, ArgType: argType, ReplyType: replyType, ContextType: contextType, stream: stream} 162 } 163 164 func (server *rServer) register(rcvr interface{}) error { 165 server.mu.Lock() 166 defer server.mu.Unlock() 167 if server.serviceMap == nil { 168 server.serviceMap = make(map[string]*service) 169 } 170 s := new(service) 171 s.typ = reflect.TypeOf(rcvr) 172 s.rcvr = reflect.ValueOf(rcvr) 173 sname := reflect.Indirect(s.rcvr).Type().Name() 174 if sname == "" { 175 logger.Fatalf("rpc: no service name for type %v", s.typ.String()) 176 } 177 if !isExported(sname) { 178 s := "rpc Register: type " + sname + " is not exported" 179 if logger.V(logger.ErrorLevel, logger.DefaultLogger) { 180 logger.Error(s) 181 } 182 return errors.New(s) 183 } 184 if _, present := server.serviceMap[sname]; present { 185 return errors.New("rpc: service already defined: " + sname) 186 } 187 s.name = sname 188 s.method = make(map[string]*methodType) 189 190 // Install the methods 191 for m := 0; m < s.typ.NumMethod(); m++ { 192 method := s.typ.Method(m) 193 if mt := prepareEndpoint(method); mt != nil { 194 s.method[method.Name] = mt 195 } 196 } 197 198 if len(s.method) == 0 { 199 s := "rpc Register: type " + sname + " has no exported methods of suitable type" 200 if logger.V(logger.ErrorLevel, logger.DefaultLogger) { 201 logger.Error(s) 202 } 203 return errors.New(s) 204 } 205 server.serviceMap[s.name] = s 206 return nil 207 } 208 209 func (m *methodType) prepareContext(ctx context.Context) reflect.Value { 210 if contextv := reflect.ValueOf(ctx); contextv.IsValid() { 211 return contextv 212 } 213 return reflect.Zero(m.ContextType) 214 }