github.com/theQRL/go-zond@v0.1.1/rpc/service.go (about) 1 // Copyright 2019 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package rpc 18 19 import ( 20 "context" 21 "fmt" 22 "reflect" 23 "runtime" 24 "strings" 25 "sync" 26 "unicode" 27 28 "github.com/theQRL/go-zond/log" 29 ) 30 31 var ( 32 contextType = reflect.TypeOf((*context.Context)(nil)).Elem() 33 errorType = reflect.TypeOf((*error)(nil)).Elem() 34 subscriptionType = reflect.TypeOf(Subscription{}) 35 stringType = reflect.TypeOf("") 36 ) 37 38 type serviceRegistry struct { 39 mu sync.Mutex 40 services map[string]service 41 } 42 43 // service represents a registered object. 44 type service struct { 45 name string // name for service 46 callbacks map[string]*callback // registered handlers 47 subscriptions map[string]*callback // available subscriptions/notifications 48 } 49 50 // callback is a method callback which was registered in the server 51 type callback struct { 52 fn reflect.Value // the function 53 rcvr reflect.Value // receiver object of method, set if fn is method 54 argTypes []reflect.Type // input argument types 55 hasCtx bool // method's first argument is a context (not included in argTypes) 56 errPos int // err return idx, of -1 when method cannot return error 57 isSubscribe bool // true if this is a subscription callback 58 } 59 60 func (r *serviceRegistry) registerName(name string, rcvr interface{}) error { 61 rcvrVal := reflect.ValueOf(rcvr) 62 if name == "" { 63 return fmt.Errorf("no service name for type %s", rcvrVal.Type().String()) 64 } 65 callbacks := suitableCallbacks(rcvrVal) 66 if len(callbacks) == 0 { 67 return fmt.Errorf("service %T doesn't have any suitable methods/subscriptions to expose", rcvr) 68 } 69 70 r.mu.Lock() 71 defer r.mu.Unlock() 72 if r.services == nil { 73 r.services = make(map[string]service) 74 } 75 svc, ok := r.services[name] 76 if !ok { 77 svc = service{ 78 name: name, 79 callbacks: make(map[string]*callback), 80 subscriptions: make(map[string]*callback), 81 } 82 r.services[name] = svc 83 } 84 for name, cb := range callbacks { 85 if cb.isSubscribe { 86 svc.subscriptions[name] = cb 87 } else { 88 svc.callbacks[name] = cb 89 } 90 } 91 return nil 92 } 93 94 // callback returns the callback corresponding to the given RPC method name. 95 func (r *serviceRegistry) callback(method string) *callback { 96 elem := strings.SplitN(method, serviceMethodSeparator, 2) 97 if len(elem) != 2 { 98 return nil 99 } 100 r.mu.Lock() 101 defer r.mu.Unlock() 102 return r.services[elem[0]].callbacks[elem[1]] 103 } 104 105 // subscription returns a subscription callback in the given service. 106 func (r *serviceRegistry) subscription(service, name string) *callback { 107 r.mu.Lock() 108 defer r.mu.Unlock() 109 return r.services[service].subscriptions[name] 110 } 111 112 // suitableCallbacks iterates over the methods of the given type. It determines if a method 113 // satisfies the criteria for a RPC callback or a subscription callback and adds it to the 114 // collection of callbacks. See server documentation for a summary of these criteria. 115 func suitableCallbacks(receiver reflect.Value) map[string]*callback { 116 typ := receiver.Type() 117 callbacks := make(map[string]*callback) 118 for m := 0; m < typ.NumMethod(); m++ { 119 method := typ.Method(m) 120 if method.PkgPath != "" { 121 continue // method not exported 122 } 123 cb := newCallback(receiver, method.Func) 124 if cb == nil { 125 continue // function invalid 126 } 127 name := formatName(method.Name) 128 callbacks[name] = cb 129 } 130 return callbacks 131 } 132 133 // newCallback turns fn (a function) into a callback object. It returns nil if the function 134 // is unsuitable as an RPC callback. 135 func newCallback(receiver, fn reflect.Value) *callback { 136 fntype := fn.Type() 137 c := &callback{fn: fn, rcvr: receiver, errPos: -1, isSubscribe: isPubSub(fntype)} 138 // Determine parameter types. They must all be exported or builtin types. 139 c.makeArgTypes() 140 141 // Verify return types. The function must return at most one error 142 // and/or one other non-error value. 143 outs := make([]reflect.Type, fntype.NumOut()) 144 for i := 0; i < fntype.NumOut(); i++ { 145 outs[i] = fntype.Out(i) 146 } 147 if len(outs) > 2 { 148 return nil 149 } 150 // If an error is returned, it must be the last returned value. 151 switch { 152 case len(outs) == 1 && isErrorType(outs[0]): 153 c.errPos = 0 154 case len(outs) == 2: 155 if isErrorType(outs[0]) || !isErrorType(outs[1]) { 156 return nil 157 } 158 c.errPos = 1 159 } 160 return c 161 } 162 163 // makeArgTypes composes the argTypes list. 164 func (c *callback) makeArgTypes() { 165 fntype := c.fn.Type() 166 // Skip receiver and context.Context parameter (if present). 167 firstArg := 0 168 if c.rcvr.IsValid() { 169 firstArg++ 170 } 171 if fntype.NumIn() > firstArg && fntype.In(firstArg) == contextType { 172 c.hasCtx = true 173 firstArg++ 174 } 175 // Add all remaining parameters. 176 c.argTypes = make([]reflect.Type, fntype.NumIn()-firstArg) 177 for i := firstArg; i < fntype.NumIn(); i++ { 178 c.argTypes[i-firstArg] = fntype.In(i) 179 } 180 } 181 182 // call invokes the callback. 183 func (c *callback) call(ctx context.Context, method string, args []reflect.Value) (res interface{}, errRes error) { 184 // Create the argument slice. 185 fullargs := make([]reflect.Value, 0, 2+len(args)) 186 if c.rcvr.IsValid() { 187 fullargs = append(fullargs, c.rcvr) 188 } 189 if c.hasCtx { 190 fullargs = append(fullargs, reflect.ValueOf(ctx)) 191 } 192 fullargs = append(fullargs, args...) 193 194 // Catch panic while running the callback. 195 defer func() { 196 if err := recover(); err != nil { 197 const size = 64 << 10 198 buf := make([]byte, size) 199 buf = buf[:runtime.Stack(buf, false)] 200 log.Error("RPC method " + method + " crashed: " + fmt.Sprintf("%v\n%s", err, buf)) 201 errRes = &internalServerError{errcodePanic, "method handler crashed"} 202 } 203 }() 204 // Run the callback. 205 results := c.fn.Call(fullargs) 206 if len(results) == 0 { 207 return nil, nil 208 } 209 if c.errPos >= 0 && !results[c.errPos].IsNil() { 210 // Method has returned non-nil error value. 211 err := results[c.errPos].Interface().(error) 212 return reflect.Value{}, err 213 } 214 return results[0].Interface(), nil 215 } 216 217 // Does t satisfy the error interface? 218 func isErrorType(t reflect.Type) bool { 219 return t.Implements(errorType) 220 } 221 222 // Is t Subscription or *Subscription? 223 func isSubscriptionType(t reflect.Type) bool { 224 for t.Kind() == reflect.Ptr { 225 t = t.Elem() 226 } 227 return t == subscriptionType 228 } 229 230 // isPubSub tests whether the given method has as as first argument a context.Context and 231 // returns the pair (Subscription, error). 232 func isPubSub(methodType reflect.Type) bool { 233 // numIn(0) is the receiver type 234 if methodType.NumIn() < 2 || methodType.NumOut() != 2 { 235 return false 236 } 237 return methodType.In(1) == contextType && 238 isSubscriptionType(methodType.Out(0)) && 239 isErrorType(methodType.Out(1)) 240 } 241 242 // formatName converts to first character of name to lowercase. 243 func formatName(name string) string { 244 ret := []rune(name) 245 if len(ret) > 0 { 246 ret[0] = unicode.ToLower(ret[0]) 247 } 248 return string(ret) 249 }