gitlab.com/flarenetwork/coreth@v0.1.1/rpc/handler.go (about)

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