github.com/amazechain/amc@v0.1.3/modules/rpc/jsonrpc/service.go (about) 1 // Copyright 2022 The AmazeChain Authors 2 // This file is part of the AmazeChain library. 3 // 4 // The AmazeChain 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 AmazeChain 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 AmazeChain library. If not, see <http://www.gnu.org/licenses/>. 16 17 package jsonrpc 18 19 import ( 20 "context" 21 "fmt" 22 "reflect" 23 "strings" 24 "sync" 25 "unicode" 26 ) 27 28 var ( 29 contextType = reflect.TypeOf((*context.Context)(nil)).Elem() 30 errorType = reflect.TypeOf((*error)(nil)).Elem() 31 subscriptionType = reflect.TypeOf(Subscription{}) 32 stringType = reflect.TypeOf("") 33 ) 34 35 type serviceRegistry struct { 36 mu sync.Mutex 37 services map[string]service 38 } 39 40 type service struct { 41 name string 42 callbacks map[string]*callback 43 subscriptions map[string]*callback // available subscriptions/notifications 44 } 45 46 type callback struct { 47 fn reflect.Value 48 rcvr reflect.Value 49 argTypes []reflect.Type 50 hasCtx bool 51 errPos int 52 isSubscribe bool // true if this is a subscription callback 53 } 54 55 func (r *serviceRegistry) registerName(name string, rcvr interface{}) error { 56 rcvrVal := reflect.ValueOf(rcvr) 57 if name == "" { 58 return fmt.Errorf("no service name for type %s", rcvrVal.Type().String()) 59 } 60 callbacks := suitableCallbacks(rcvrVal) 61 if len(callbacks) == 0 { 62 return fmt.Errorf("service %T doesn't have any suitable methods/subscriptions to expose", rcvr) 63 } 64 65 r.mu.Lock() 66 defer r.mu.Unlock() 67 if r.services == nil { 68 r.services = make(map[string]service) 69 } 70 svc, ok := r.services[name] 71 if !ok { 72 svc = service{ 73 name: name, 74 callbacks: make(map[string]*callback), 75 subscriptions: make(map[string]*callback), 76 } 77 r.services[name] = svc 78 } 79 for name, cb := range callbacks { 80 if cb.isSubscribe { 81 svc.subscriptions[name] = cb 82 } else { 83 svc.callbacks[name] = cb 84 } 85 } 86 return nil 87 } 88 89 func (r *serviceRegistry) callback(method string) *callback { 90 elem := strings.SplitN(method, serviceMethodSeparator, 2) 91 if len(elem) != 2 { 92 return nil 93 } 94 r.mu.Lock() 95 defer r.mu.Unlock() 96 // todo 97 //log.Info("meth %+v", r.services[elem[0]].callbacks) 98 return r.services[elem[0]].callbacks[elem[1]] 99 } 100 101 // subscription returns a subscription callback in the given service. 102 func (r *serviceRegistry) subscription(service, name string) *callback { 103 r.mu.Lock() 104 defer r.mu.Unlock() 105 return r.services[service].subscriptions[name] 106 } 107 108 func suitableCallbacks(receiver reflect.Value) map[string]*callback { 109 typ := receiver.Type() 110 callbacks := make(map[string]*callback) 111 for m := 0; m < typ.NumMethod(); m++ { 112 method := typ.Method(m) 113 if method.PkgPath != "" { 114 continue 115 } 116 cb := newCallback(receiver, method.Func) 117 if cb == nil { 118 continue 119 } 120 name := formatName(method.Name) 121 callbacks[name] = cb 122 } 123 return callbacks 124 } 125 126 func newCallback(receiver, fn reflect.Value) *callback { 127 fntype := fn.Type() 128 c := &callback{fn: fn, rcvr: receiver, errPos: -1, isSubscribe: isPubSub(fntype)} 129 c.makeArgTypes() 130 131 outs := make([]reflect.Type, fntype.NumOut()) 132 for i := 0; i < fntype.NumOut(); i++ { 133 outs[i] = fntype.Out(i) 134 } 135 if len(outs) > 2 { 136 return nil 137 } 138 switch { 139 case len(outs) == 1 && isErrorType(outs[0]): 140 c.errPos = 0 141 case len(outs) == 2: 142 if isErrorType(outs[0]) || !isErrorType(outs[1]) { 143 return nil 144 } 145 c.errPos = 1 146 } 147 return c 148 } 149 150 func (c *callback) makeArgTypes() { 151 fntype := c.fn.Type() 152 firstArg := 0 153 if c.rcvr.IsValid() { 154 firstArg++ 155 } 156 if fntype.NumIn() > firstArg && fntype.In(firstArg) == contextType { 157 c.hasCtx = true 158 firstArg++ 159 } 160 c.argTypes = make([]reflect.Type, fntype.NumIn()-firstArg) 161 for i := firstArg; i < fntype.NumIn(); i++ { 162 c.argTypes[i-firstArg] = fntype.In(i) 163 } 164 } 165 166 func (c *callback) call(ctx context.Context, method string, args []reflect.Value) (res interface{}, errRes error) { 167 fullargs := make([]reflect.Value, 0, 2+len(args)) 168 if c.rcvr.IsValid() { 169 fullargs = append(fullargs, c.rcvr) 170 } 171 if c.hasCtx { 172 fullargs = append(fullargs, reflect.ValueOf(ctx)) 173 } 174 fullargs = append(fullargs, args...) 175 176 defer func() { 177 // if err := recover(); err != nil { 178 // const size = 64 << 10 179 // buf := make([]byte, size) 180 // buf = buf[:runtime.Stack(buf, false)] 181 // log.Error("RPC method " + method + " crashed: " + fmt.Sprintf("%v\n%s", err, buf)) 182 // errRes = errors.New("method handler crashed") 183 // } 184 }() 185 results := c.fn.Call(fullargs) 186 if len(results) == 0 { 187 return nil, nil 188 } 189 if c.errPos >= 0 && !results[c.errPos].IsNil() { 190 err := results[c.errPos].Interface().(error) 191 return reflect.Value{}, err 192 } 193 return results[0].Interface(), nil 194 } 195 196 // Is t context.Context or *context.Context? 197 func isContextType(t reflect.Type) bool { 198 for t.Kind() == reflect.Ptr { 199 t = t.Elem() 200 } 201 return t == contextType 202 } 203 204 func isErrorType(t reflect.Type) bool { 205 for t.Kind() == reflect.Ptr { 206 t = t.Elem() 207 } 208 return t.Implements(errorType) 209 } 210 211 // Is t Subscription or *Subscription? 212 func isSubscriptionType(t reflect.Type) bool { 213 for t.Kind() == reflect.Ptr { 214 t = t.Elem() 215 } 216 return t == subscriptionType 217 } 218 219 // isPubSub tests whether the given method has as as first argument a context.Context and 220 // returns the pair (Subscription, error). 221 func isPubSub(methodType reflect.Type) bool { 222 // numIn(0) is the receiver type 223 if methodType.NumIn() < 2 || methodType.NumOut() != 2 { 224 return false 225 } 226 return isContextType(methodType.In(1)) && 227 isSubscriptionType(methodType.Out(0)) && 228 isErrorType(methodType.Out(1)) 229 } 230 231 func formatName(name string) string { 232 ret := []rune(name) 233 if len(ret) > 0 { 234 ret[0] = unicode.ToLower(ret[0]) 235 } 236 return string(ret) 237 }