github.com/coltonfike/e2c@v21.1.0+incompatible/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  	"errors"
    22  	"fmt"
    23  	"reflect"
    24  	"runtime"
    25  	"strings"
    26  	"sync"
    27  	"unicode"
    28  	"unicode/utf8"
    29  
    30  	"github.com/ethereum/go-ethereum/log"
    31  	"github.com/ethereum/go-ethereum/params"
    32  )
    33  
    34  var (
    35  	contextType      = reflect.TypeOf((*context.Context)(nil)).Elem()
    36  	errorType        = reflect.TypeOf((*error)(nil)).Elem()
    37  	subscriptionType = reflect.TypeOf(Subscription{})
    38  	stringType       = reflect.TypeOf("")
    39  )
    40  
    41  type serviceRegistry struct {
    42  	mu       sync.Mutex
    43  	services map[string]service
    44  }
    45  
    46  // service represents a registered object.
    47  type service struct {
    48  	name          string               // name for service
    49  	callbacks     map[string]*callback // registered handlers
    50  	subscriptions map[string]*callback // available subscriptions/notifications
    51  }
    52  
    53  // callback is a method callback which was registered in the server
    54  type callback struct {
    55  	fn          reflect.Value  // the function
    56  	rcvr        reflect.Value  // receiver object of method, set if fn is method
    57  	argTypes    []reflect.Type // input argument types
    58  	hasCtx      bool           // method's first argument is a context (not included in argTypes)
    59  	errPos      int            // err return idx, of -1 when method cannot return error
    60  	isSubscribe bool           // true if this is a subscription callback
    61  }
    62  
    63  func (r *serviceRegistry) registerName(name string, rcvr interface{}) error {
    64  	rcvrVal := reflect.ValueOf(rcvr)
    65  	if name == "" {
    66  		return fmt.Errorf("no service name for type %s", rcvrVal.Type().String())
    67  	}
    68  	callbacks := suitableCallbacks(rcvrVal)
    69  	if len(callbacks) == 0 {
    70  		return fmt.Errorf("service %T doesn't have any suitable methods/subscriptions to expose", rcvr)
    71  	}
    72  
    73  	r.mu.Lock()
    74  	defer r.mu.Unlock()
    75  	if r.services == nil {
    76  		r.services = make(map[string]service)
    77  	}
    78  	svc, ok := r.services[name]
    79  	if !ok {
    80  		svc = service{
    81  			name:          name,
    82  			callbacks:     make(map[string]*callback),
    83  			subscriptions: make(map[string]*callback),
    84  		}
    85  		r.services[name] = svc
    86  	}
    87  	for name, cb := range callbacks {
    88  		if cb.isSubscribe {
    89  			svc.subscriptions[name] = cb
    90  		} else {
    91  			svc.callbacks[name] = cb
    92  		}
    93  	}
    94  	return nil
    95  }
    96  
    97  // callback returns the callback corresponding to the given RPC method name.
    98  func (r *serviceRegistry) callback(method string) *callback {
    99  	elem := strings.SplitN(method, serviceMethodSeparator, 2)
   100  	if len(elem) != 2 {
   101  		return nil
   102  	}
   103  	r.mu.Lock()
   104  	defer r.mu.Unlock()
   105  	return r.services[elem[0]].callbacks[elem[1]]
   106  }
   107  
   108  // subscription returns a subscription callback in the given service.
   109  func (r *serviceRegistry) subscription(service, name string) *callback {
   110  	r.mu.Lock()
   111  	defer r.mu.Unlock()
   112  	return r.services[service].subscriptions[name]
   113  }
   114  
   115  // suitableCallbacks iterates over the methods of the given type. It determines if a method
   116  // satisfies the criteria for a RPC callback or a subscription callback and adds it to the
   117  // collection of callbacks. See server documentation for a summary of these criteria.
   118  func suitableCallbacks(receiver reflect.Value) map[string]*callback {
   119  	typ := receiver.Type()
   120  	callbacks := make(map[string]*callback)
   121  	for m := 0; m < typ.NumMethod(); m++ {
   122  		method := typ.Method(m)
   123  		if method.PkgPath != "" {
   124  			continue // method not exported
   125  		}
   126  		cb := newCallback(receiver, method.Func)
   127  		if cb == nil {
   128  			continue // function invalid
   129  		}
   130  		name := formatName(method.Name)
   131  		callbacks[name] = cb
   132  	}
   133  	return callbacks
   134  }
   135  
   136  // newCallback turns fn (a function) into a callback object. It returns nil if the function
   137  // is unsuitable as an RPC callback.
   138  func newCallback(receiver, fn reflect.Value) *callback {
   139  	fntype := fn.Type()
   140  	c := &callback{fn: fn, rcvr: receiver, errPos: -1, isSubscribe: isPubSub(fntype)}
   141  	// Determine parameter types. They must all be exported or builtin types.
   142  	c.makeArgTypes()
   143  	if !allExportedOrBuiltin(c.argTypes) {
   144  		return nil
   145  	}
   146  	// Verify return types. The function must return at most one error
   147  	// and/or one other non-error value.
   148  	outs := make([]reflect.Type, fntype.NumOut())
   149  	for i := 0; i < fntype.NumOut(); i++ {
   150  		outs[i] = fntype.Out(i)
   151  	}
   152  	if len(outs) > 2 || !allExportedOrBuiltin(outs) {
   153  		return nil
   154  	}
   155  	// If an error is returned, it must be the last returned value.
   156  	switch {
   157  	case len(outs) == 1 && isErrorType(outs[0]):
   158  		c.errPos = 0
   159  	case len(outs) == 2:
   160  		if isErrorType(outs[0]) || !isErrorType(outs[1]) {
   161  			return nil
   162  		}
   163  		c.errPos = 1
   164  	}
   165  	return c
   166  }
   167  
   168  // makeArgTypes composes the argTypes list.
   169  func (c *callback) makeArgTypes() {
   170  	fntype := c.fn.Type()
   171  	// Skip receiver and context.Context parameter (if present).
   172  	firstArg := 0
   173  	if c.rcvr.IsValid() {
   174  		firstArg++
   175  	}
   176  	if fntype.NumIn() > firstArg && fntype.In(firstArg) == contextType {
   177  		c.hasCtx = true
   178  		firstArg++
   179  	}
   180  	// Add all remaining parameters.
   181  	c.argTypes = make([]reflect.Type, fntype.NumIn()-firstArg)
   182  	for i := firstArg; i < fntype.NumIn(); i++ {
   183  		c.argTypes[i-firstArg] = fntype.In(i)
   184  	}
   185  }
   186  
   187  // call invokes the callback.
   188  func (c *callback) call(ctx context.Context, method string, args []reflect.Value) (res interface{}, errRes error) {
   189  	// Create the argument slice.
   190  	fullargs := make([]reflect.Value, 0, 2+len(args))
   191  	if c.rcvr.IsValid() {
   192  		fullargs = append(fullargs, c.rcvr)
   193  	}
   194  	if c.hasCtx {
   195  		fullargs = append(fullargs, reflect.ValueOf(ctx))
   196  	}
   197  	fullargs = append(fullargs, args...)
   198  
   199  	// Catch panic while running the callback.
   200  	defer func() {
   201  		if err := recover(); err != nil {
   202  			const size = params.QuorumMaxPayloadBufferSize << 10
   203  			buf := make([]byte, size)
   204  			buf = buf[:runtime.Stack(buf, false)]
   205  			log.Error("RPC method " + method + " crashed: " + fmt.Sprintf("%v\n%s", err, buf))
   206  			errRes = errors.New("method handler crashed")
   207  		}
   208  	}()
   209  	// Run the callback.
   210  	results := c.fn.Call(fullargs)
   211  	if len(results) == 0 {
   212  		return nil, nil
   213  	}
   214  	if c.errPos >= 0 && !results[c.errPos].IsNil() {
   215  		// Method has returned non-nil error value.
   216  		err := results[c.errPos].Interface().(error)
   217  		return reflect.Value{}, err
   218  	}
   219  	return results[0].Interface(), nil
   220  }
   221  
   222  // Is this an exported - upper case - name?
   223  func isExported(name string) bool {
   224  	rune, _ := utf8.DecodeRuneInString(name)
   225  	return unicode.IsUpper(rune)
   226  }
   227  
   228  // Are all those types exported or built-in?
   229  func allExportedOrBuiltin(types []reflect.Type) bool {
   230  	for _, typ := range types {
   231  		for typ.Kind() == reflect.Ptr {
   232  			typ = typ.Elem()
   233  		}
   234  		// PkgPath will be non-empty even for an exported type,
   235  		// so we need to check the type name as well.
   236  		if !isExported(typ.Name()) && typ.PkgPath() != "" {
   237  			return false
   238  		}
   239  	}
   240  	return true
   241  }
   242  
   243  // Is t context.Context or *context.Context?
   244  func isContextType(t reflect.Type) bool {
   245  	for t.Kind() == reflect.Ptr {
   246  		t = t.Elem()
   247  	}
   248  	return t == contextType
   249  }
   250  
   251  // Does t satisfy the error interface?
   252  func isErrorType(t reflect.Type) bool {
   253  	for t.Kind() == reflect.Ptr {
   254  		t = t.Elem()
   255  	}
   256  	return t.Implements(errorType)
   257  }
   258  
   259  // Is t Subscription or *Subscription?
   260  func isSubscriptionType(t reflect.Type) bool {
   261  	for t.Kind() == reflect.Ptr {
   262  		t = t.Elem()
   263  	}
   264  	return t == subscriptionType
   265  }
   266  
   267  // isPubSub tests whether the given method has as as first argument a context.Context and
   268  // returns the pair (Subscription, error).
   269  func isPubSub(methodType reflect.Type) bool {
   270  	// numIn(0) is the receiver type
   271  	if methodType.NumIn() < 2 || methodType.NumOut() != 2 {
   272  		return false
   273  	}
   274  	return isContextType(methodType.In(1)) &&
   275  		isSubscriptionType(methodType.Out(0)) &&
   276  		isErrorType(methodType.Out(1))
   277  }
   278  
   279  // formatName converts to first character of name to lowercase.
   280  func formatName(name string) string {
   281  	ret := []rune(name)
   282  	if len(ret) > 0 {
   283  		ret[0] = unicode.ToLower(ret[0])
   284  	}
   285  	return string(ret)
   286  }