github.com/unicornultrafoundation/go-u2u@v1.0.0-rc1.0.20240205080301-e74a83d3fadc/rpc/handler.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  	"encoding/json"
    22  	"reflect"
    23  	"strconv"
    24  	"strings"
    25  	"sync"
    26  	"time"
    27  
    28  	"github.com/unicornultrafoundation/go-u2u/log"
    29  )
    30  
    31  var (
    32  	executionTimeLimit = 5 * time.Second
    33  )
    34  
    35  // SetExecutionTimeLimit sets execution limit for RPC method calls
    36  func SetExecutionTimeLimit(limit time.Duration) {
    37  	executionTimeLimit = limit
    38  }
    39  
    40  // handler handles JSON-RPC messages. There is one handler per connection. Note that
    41  // handler is not safe for concurrent use. Message handling never blocks indefinitely
    42  // because RPCs are processed on background goroutines launched by handler.
    43  //
    44  // The entry points for incoming messages are:
    45  //
    46  //	h.handleMsg(message)
    47  //	h.handleBatch(message)
    48  //
    49  // Outgoing calls use the requestOp struct. Register the request before sending it
    50  // on the connection:
    51  //
    52  //	op := &requestOp{ids: ...}
    53  //	h.addRequestOp(op)
    54  //
    55  // Now send the request, then wait for the reply to be delivered through handleMsg:
    56  //
    57  //	if err := op.wait(...); err != nil {
    58  //	    h.removeRequestOp(op) // timeout, etc.
    59  //	}
    60  type handler struct {
    61  	reg            *serviceRegistry
    62  	unsubscribeCb  *callback
    63  	idgen          func() ID                      // subscription ID generator
    64  	respWait       map[string]*requestOp          // active client requests
    65  	clientSubs     map[string]*ClientSubscription // active client subscriptions
    66  	callWG         sync.WaitGroup                 // pending call goroutines
    67  	rootCtx        context.Context                // canceled by close()
    68  	cancelRoot     func()                         // cancel function for rootCtx
    69  	conn           jsonWriter                     // where responses will be sent
    70  	log            log.Logger
    71  	allowSubscribe bool
    72  
    73  	subLock    sync.Mutex
    74  	serverSubs map[ID]*Subscription
    75  }
    76  
    77  type callProc struct {
    78  	ctx       context.Context
    79  	notifiers []*Notifier
    80  }
    81  
    82  func newHandler(connCtx context.Context, conn jsonWriter, idgen func() ID, reg *serviceRegistry) *handler {
    83  	rootCtx, cancelRoot := context.WithCancel(connCtx)
    84  	h := &handler{
    85  		reg:            reg,
    86  		idgen:          idgen,
    87  		conn:           conn,
    88  		respWait:       make(map[string]*requestOp),
    89  		clientSubs:     make(map[string]*ClientSubscription),
    90  		rootCtx:        rootCtx,
    91  		cancelRoot:     cancelRoot,
    92  		allowSubscribe: true,
    93  		serverSubs:     make(map[ID]*Subscription),
    94  		log:            log.Root(),
    95  	}
    96  	if conn.remoteAddr() != "" {
    97  		h.log = h.log.New("conn", conn.remoteAddr())
    98  	}
    99  	h.unsubscribeCb = newCallback(reflect.Value{}, reflect.ValueOf(h.unsubscribe))
   100  	return h
   101  }
   102  
   103  // handleBatch executes all messages in a batch and returns the responses.
   104  func (h *handler) handleBatch(msgs []*jsonrpcMessage) {
   105  	// Emit error response for empty batches:
   106  	if len(msgs) == 0 {
   107  		h.startCallProc(func(cp *callProc) {
   108  			h.conn.writeJSON(cp.ctx, errorMessage(&invalidRequestError{"empty batch"}))
   109  		})
   110  		return
   111  	}
   112  
   113  	// Handle non-call messages first:
   114  	calls := make([]*jsonrpcMessage, 0, len(msgs))
   115  	for _, msg := range msgs {
   116  		if handled := h.handleImmediate(msg); !handled {
   117  			calls = append(calls, msg)
   118  		}
   119  	}
   120  	if len(calls) == 0 {
   121  		return
   122  	}
   123  	// Process calls on a goroutine because they may block indefinitely:
   124  	h.startCallProc(func(cp *callProc) {
   125  		answers := make([]*jsonrpcMessage, 0, len(msgs))
   126  		for _, msg := range calls {
   127  			if answer := h.handleCallMsg(cp, msg); answer != nil {
   128  				answers = append(answers, answer)
   129  			}
   130  		}
   131  		h.addSubscriptions(cp.notifiers)
   132  		if len(answers) > 0 {
   133  			h.conn.writeJSON(cp.ctx, answers)
   134  		}
   135  		for _, n := range cp.notifiers {
   136  			n.activate()
   137  		}
   138  	})
   139  }
   140  
   141  // handleMsg handles a single message.
   142  func (h *handler) handleMsg(msg *jsonrpcMessage) {
   143  	if ok := h.handleImmediate(msg); ok {
   144  		return
   145  	}
   146  	h.startCallProc(func(cp *callProc) {
   147  		answer := h.handleCallMsg(cp, msg)
   148  		h.addSubscriptions(cp.notifiers)
   149  		if answer != nil {
   150  			h.conn.writeJSON(cp.ctx, answer)
   151  		}
   152  		for _, n := range cp.notifiers {
   153  			n.activate()
   154  		}
   155  	})
   156  }
   157  
   158  // close cancels all requests except for inflightReq and waits for
   159  // call goroutines to shut down.
   160  func (h *handler) close(err error, inflightReq *requestOp) {
   161  	h.cancelAllRequests(err, inflightReq)
   162  	h.callWG.Wait()
   163  	h.cancelRoot()
   164  	h.cancelServerSubscriptions(err)
   165  }
   166  
   167  // addRequestOp registers a request operation.
   168  func (h *handler) addRequestOp(op *requestOp) {
   169  	for _, id := range op.ids {
   170  		h.respWait[string(id)] = op
   171  	}
   172  }
   173  
   174  // removeRequestOps stops waiting for the given request IDs.
   175  func (h *handler) removeRequestOp(op *requestOp) {
   176  	for _, id := range op.ids {
   177  		delete(h.respWait, string(id))
   178  	}
   179  }
   180  
   181  // cancelAllRequests unblocks and removes pending requests and active subscriptions.
   182  func (h *handler) cancelAllRequests(err error, inflightReq *requestOp) {
   183  	didClose := make(map[*requestOp]bool)
   184  	if inflightReq != nil {
   185  		didClose[inflightReq] = true
   186  	}
   187  
   188  	for id, op := range h.respWait {
   189  		// Remove the op so that later calls will not close op.resp again.
   190  		delete(h.respWait, id)
   191  
   192  		if !didClose[op] {
   193  			op.err = err
   194  			close(op.resp)
   195  			didClose[op] = true
   196  		}
   197  	}
   198  	for id, sub := range h.clientSubs {
   199  		delete(h.clientSubs, id)
   200  		sub.close(err)
   201  	}
   202  }
   203  
   204  func (h *handler) addSubscriptions(nn []*Notifier) {
   205  	h.subLock.Lock()
   206  	defer h.subLock.Unlock()
   207  
   208  	for _, n := range nn {
   209  		if sub := n.takeSubscription(); sub != nil {
   210  			h.serverSubs[sub.ID] = sub
   211  		}
   212  	}
   213  }
   214  
   215  // cancelServerSubscriptions removes all subscriptions and closes their error channels.
   216  func (h *handler) cancelServerSubscriptions(err error) {
   217  	h.subLock.Lock()
   218  	defer h.subLock.Unlock()
   219  
   220  	for id, s := range h.serverSubs {
   221  		s.err <- err
   222  		close(s.err)
   223  		delete(h.serverSubs, id)
   224  	}
   225  }
   226  
   227  // startCallProc runs fn in a new goroutine and starts tracking it in the h.calls wait group.
   228  func (h *handler) startCallProc(fn func(*callProc)) {
   229  	h.callWG.Add(1)
   230  	h.reg.callWG.Add(1)
   231  	go func() {
   232  		defer h.callWG.Done()
   233  		defer h.reg.callWG.Done()
   234  
   235  		ctx, cancel := context.WithCancel(h.rootCtx)
   236  		defer cancel()
   237  		fn(&callProc{ctx: ctx})
   238  	}()
   239  }
   240  
   241  // handleImmediate executes non-call messages. It returns false if the message is a
   242  // call or requires a reply.
   243  func (h *handler) handleImmediate(msg *jsonrpcMessage) bool {
   244  	start := time.Now()
   245  	switch {
   246  	case msg.isNotification():
   247  		if strings.HasSuffix(msg.Method, notificationMethodSuffix) {
   248  			h.handleSubscriptionResult(msg)
   249  			return true
   250  		}
   251  		return false
   252  	case msg.isResponse():
   253  		h.handleResponse(msg)
   254  		h.log.Trace("Handled RPC response", "reqid", idForLog{msg.ID}, "t", time.Since(start))
   255  		return true
   256  	default:
   257  		return false
   258  	}
   259  }
   260  
   261  // handleSubscriptionResult processes subscription notifications.
   262  func (h *handler) handleSubscriptionResult(msg *jsonrpcMessage) {
   263  	var result subscriptionResult
   264  	if err := json.Unmarshal(msg.Params, &result); err != nil {
   265  		h.log.Debug("Dropping invalid subscription message")
   266  		return
   267  	}
   268  	if h.clientSubs[result.ID] != nil {
   269  		h.clientSubs[result.ID].deliver(result.Result)
   270  	}
   271  }
   272  
   273  // handleResponse processes method call responses.
   274  func (h *handler) handleResponse(msg *jsonrpcMessage) {
   275  	op := h.respWait[string(msg.ID)]
   276  	if op == nil {
   277  		h.log.Debug("Unsolicited RPC response", "reqid", idForLog{msg.ID})
   278  		return
   279  	}
   280  	delete(h.respWait, string(msg.ID))
   281  	// For normal responses, just forward the reply to Call/BatchCall.
   282  	if op.sub == nil {
   283  		op.resp <- msg
   284  		return
   285  	}
   286  	// For subscription responses, start the subscription if the server
   287  	// indicates success. EthSubscribe gets unblocked in either case through
   288  	// the op.resp channel.
   289  	defer close(op.resp)
   290  	if msg.Error != nil {
   291  		op.err = msg.Error
   292  		return
   293  	}
   294  	if op.err = json.Unmarshal(msg.Result, &op.sub.subid); op.err == nil {
   295  		go op.sub.run()
   296  		h.clientSubs[op.sub.subid] = op.sub
   297  	}
   298  }
   299  
   300  // handleCallMsg executes a call message and returns the answer.
   301  func (h *handler) handleCallMsg(ctx *callProc, msg *jsonrpcMessage) *jsonrpcMessage {
   302  	start := time.Now()
   303  	switch {
   304  	case msg.isNotification():
   305  		h.handleCall(ctx, msg)
   306  		h.log.Debug("Served "+msg.Method, "t", time.Since(start))
   307  		return nil
   308  	case msg.isCall():
   309  		resp := h.handleCall(ctx, msg)
   310  		var ctx []interface{}
   311  		ctx = append(ctx, "reqid", idForLog{msg.ID}, "t", time.Since(start))
   312  		if resp.Error != nil {
   313  			ctx = append(ctx, "err", resp.Error.Message)
   314  			if resp.Error.Data != nil {
   315  				ctx = append(ctx, "errdata", resp.Error.Data)
   316  			}
   317  			h.log.Warn("Served "+msg.Method, ctx...)
   318  		} else {
   319  			h.log.Debug("Served "+msg.Method, ctx...)
   320  		}
   321  		return resp
   322  	case msg.hasValidID():
   323  		return msg.errorResponse(&invalidRequestError{"invalid request"})
   324  	default:
   325  		return errorMessage(&invalidRequestError{"invalid request"})
   326  	}
   327  }
   328  
   329  // handleCall processes method calls.
   330  func (h *handler) handleCall(cp *callProc, msg *jsonrpcMessage) *jsonrpcMessage {
   331  	if msg.isSubscribe() {
   332  		return h.handleSubscribe(cp, msg)
   333  	}
   334  	var callb *callback
   335  	if msg.isUnsubscribe() {
   336  		callb = h.unsubscribeCb
   337  	} else {
   338  		callb = h.reg.callback(msg.Method)
   339  	}
   340  	if callb == nil {
   341  		return msg.errorResponse(&methodNotFoundError{method: msg.Method})
   342  	}
   343  	args, err := parsePositionalArguments(msg.Params, callb.argTypes)
   344  	if err != nil {
   345  		return msg.errorResponse(&invalidParamsError{err.Error()})
   346  	}
   347  	start := time.Now()
   348  
   349  	ctx, cancel := context.WithTimeout(cp.ctx, executionTimeLimit)
   350  	defer cancel()
   351  
   352  	answer := h.runMethod(ctx, msg, callb, args)
   353  
   354  	// Collect the statistics for RPC calls if metrics is enabled.
   355  	// We only care about pure rpc call. Filter out subscription.
   356  	if callb != h.unsubscribeCb {
   357  		rpcRequestGauge.Inc(1)
   358  		if answer.Error != nil {
   359  			failedReqeustGauge.Inc(1)
   360  		} else {
   361  			successfulRequestGauge.Inc(1)
   362  		}
   363  		rpcServingTimer.UpdateSince(start)
   364  		newRPCServingTimer(msg.Method, answer.Error == nil).UpdateSince(start)
   365  	}
   366  	return answer
   367  }
   368  
   369  // handleSubscribe processes *_subscribe method calls.
   370  func (h *handler) handleSubscribe(cp *callProc, msg *jsonrpcMessage) *jsonrpcMessage {
   371  	if !h.allowSubscribe {
   372  		return msg.errorResponse(ErrNotificationsUnsupported)
   373  	}
   374  
   375  	// Subscription method name is first argument.
   376  	name, err := parseSubscriptionName(msg.Params)
   377  	if err != nil {
   378  		return msg.errorResponse(&invalidParamsError{err.Error()})
   379  	}
   380  	namespace := msg.namespace()
   381  	callb := h.reg.subscription(namespace, name)
   382  	if callb == nil {
   383  		return msg.errorResponse(&subscriptionNotFoundError{namespace, name})
   384  	}
   385  
   386  	// Parse subscription name arg too, but remove it before calling the callback.
   387  	argTypes := append([]reflect.Type{stringType}, callb.argTypes...)
   388  	args, err := parsePositionalArguments(msg.Params, argTypes)
   389  	if err != nil {
   390  		return msg.errorResponse(&invalidParamsError{err.Error()})
   391  	}
   392  	args = args[1:]
   393  
   394  	// Install notifier in context so the subscription handler can find it.
   395  	n := &Notifier{h: h, namespace: namespace}
   396  	cp.notifiers = append(cp.notifiers, n)
   397  	ctx := context.WithValue(cp.ctx, notifierKey{}, n)
   398  
   399  	return h.runMethod(ctx, msg, callb, args)
   400  }
   401  
   402  // runMethod runs the Go callback for an RPC method.
   403  func (h *handler) runMethod(ctx context.Context, msg *jsonrpcMessage, callb *callback, args []reflect.Value) *jsonrpcMessage {
   404  	result, err := callb.call(ctx, msg.Method, args)
   405  	if err != nil {
   406  		return msg.errorResponse(err)
   407  	}
   408  	return msg.response(result)
   409  }
   410  
   411  // unsubscribe is the callback function for all *_unsubscribe calls.
   412  func (h *handler) unsubscribe(ctx context.Context, id ID) (bool, error) {
   413  	h.subLock.Lock()
   414  	defer h.subLock.Unlock()
   415  
   416  	s := h.serverSubs[id]
   417  	if s == nil {
   418  		return false, ErrSubscriptionNotFound
   419  	}
   420  	close(s.err)
   421  	delete(h.serverSubs, id)
   422  	return true, nil
   423  }
   424  
   425  type idForLog struct{ json.RawMessage }
   426  
   427  func (id idForLog) String() string {
   428  	if s, err := strconv.Unquote(string(id.RawMessage)); err == nil {
   429  		return s
   430  	}
   431  	return string(id.RawMessage)
   432  }