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  }