service.go 8.1 KB

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