github.com/jimmyx0x/go-ethereum@v1.10.28/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/ethereum/go-ethereum/log"
    29  )
    30  
    31  // handler handles JSON-RPC messages. There is one handler per connection. Note that
    32  // handler is not safe for concurrent use. Message handling never blocks indefinitely
    33  // because RPCs are processed on background goroutines launched by handler.
    34  //
    35  // The entry points for incoming messages are:
    36  //
    37  //	h.handleMsg(message)
    38  //	h.handleBatch(message)
    39  //
    40  // Outgoing calls use the requestOp struct. Register the request before sending it
    41  // on the connection:
    42  //
    43  //	op := &requestOp{ids: ...}
    44  //	h.addRequestOp(op)
    45  //
    46  // Now send the request, then wait for the reply to be delivered through handleMsg:
    47  //
    48  //	if err := op.wait(...); err != nil {
    49  //		h.removeRequestOp(op) // timeout, etc.
    50  //	}
    51  type handler struct {
    52  	reg            *serviceRegistry
    53  	unsubscribeCb  *callback
    54  	idgen          func() ID                      // subscription ID generator
    55  	respWait       map[string]*requestOp          // active client requests
    56  	clientSubs     map[string]*ClientSubscription // active client subscriptions
    57  	callWG         sync.WaitGroup                 // pending call goroutines
    58  	rootCtx        context.Context                // canceled by close()
    59  	cancelRoot     func()                         // cancel function for rootCtx
    60  	conn           jsonWriter                     // where responses will be sent
    61  	log            log.Logger
    62  	allowSubscribe bool
    63  
    64  	subLock    sync.Mutex
    65  	serverSubs map[ID]*Subscription
    66  }
    67  
    68  type callProc struct {
    69  	ctx       context.Context
    70  	notifiers []*Notifier
    71  }
    72  
    73  func newHandler(connCtx context.Context, conn jsonWriter, idgen func() ID, reg *serviceRegistry) *handler {
    74  	rootCtx, cancelRoot := context.WithCancel(connCtx)
    75  	h := &handler{
    76  		reg:            reg,
    77  		idgen:          idgen,
    78  		conn:           conn,
    79  		respWait:       make(map[string]*requestOp),
    80  		clientSubs:     make(map[string]*ClientSubscription),
    81  		rootCtx:        rootCtx,
    82  		cancelRoot:     cancelRoot,
    83  		allowSubscribe: true,
    84  		serverSubs:     make(map[ID]*Subscription),
    85  		log:            log.Root(),
    86  	}
    87  	if conn.remoteAddr() != "" {
    88  		h.log = h.log.New("conn", conn.remoteAddr())
    89  	}
    90  	h.unsubscribeCb = newCallback(reflect.Value{}, reflect.ValueOf(h.unsubscribe))
    91  	return h
    92  }
    93  
    94  // batchCallBuffer manages in progress call messages and their responses during a batch
    95  // call. Calls need to be synchronized between the processing and timeout-triggering
    96  // goroutines.
    97  type batchCallBuffer struct {
    98  	mutex sync.Mutex
    99  	calls []*jsonrpcMessage
   100  	resp  []*jsonrpcMessage
   101  	wrote bool
   102  }
   103  
   104  // nextCall returns the next unprocessed message.
   105  func (b *batchCallBuffer) nextCall() *jsonrpcMessage {
   106  	b.mutex.Lock()
   107  	defer b.mutex.Unlock()
   108  
   109  	if len(b.calls) == 0 {
   110  		return nil
   111  	}
   112  	// The popping happens in `pushAnswer`. The in progress call is kept
   113  	// so we can return an error for it in case of timeout.
   114  	msg := b.calls[0]
   115  	return msg
   116  }
   117  
   118  // pushResponse adds the response to last call returned by nextCall.
   119  func (b *batchCallBuffer) pushResponse(answer *jsonrpcMessage) {
   120  	b.mutex.Lock()
   121  	defer b.mutex.Unlock()
   122  
   123  	if answer != nil {
   124  		b.resp = append(b.resp, answer)
   125  	}
   126  	b.calls = b.calls[1:]
   127  }
   128  
   129  // write sends the responses.
   130  func (b *batchCallBuffer) write(ctx context.Context, conn jsonWriter) {
   131  	b.mutex.Lock()
   132  	defer b.mutex.Unlock()
   133  
   134  	b.doWrite(ctx, conn, false)
   135  }
   136  
   137  // timeout sends the responses added so far. For the remaining unanswered call
   138  // messages, it sends a timeout error response.
   139  func (b *batchCallBuffer) timeout(ctx context.Context, conn jsonWriter) {
   140  	b.mutex.Lock()
   141  	defer b.mutex.Unlock()
   142  
   143  	for _, msg := range b.calls {
   144  		if !msg.isNotification() {
   145  			resp := msg.errorResponse(&internalServerError{errcodeTimeout, errMsgTimeout})
   146  			b.resp = append(b.resp, resp)
   147  		}
   148  	}
   149  	b.doWrite(ctx, conn, true)
   150  }
   151  
   152  // doWrite actually writes the response.
   153  // This assumes b.mutex is held.
   154  func (b *batchCallBuffer) doWrite(ctx context.Context, conn jsonWriter, isErrorResponse bool) {
   155  	if b.wrote {
   156  		return
   157  	}
   158  	b.wrote = true // can only write once
   159  	if len(b.resp) > 0 {
   160  		conn.writeJSON(ctx, b.resp, isErrorResponse)
   161  	}
   162  }
   163  
   164  // handleBatch executes all messages in a batch and returns the responses.
   165  func (h *handler) handleBatch(msgs []*jsonrpcMessage) {
   166  	// Emit error response for empty batches:
   167  	if len(msgs) == 0 {
   168  		h.startCallProc(func(cp *callProc) {
   169  			resp := errorMessage(&invalidRequestError{"empty batch"})
   170  			h.conn.writeJSON(cp.ctx, resp, true)
   171  		})
   172  		return
   173  	}
   174  
   175  	// Handle non-call messages first:
   176  	calls := make([]*jsonrpcMessage, 0, len(msgs))
   177  	for _, msg := range msgs {
   178  		if handled := h.handleImmediate(msg); !handled {
   179  			calls = append(calls, msg)
   180  		}
   181  	}
   182  	if len(calls) == 0 {
   183  		return
   184  	}
   185  	// Process calls on a goroutine because they may block indefinitely:
   186  	h.startCallProc(func(cp *callProc) {
   187  		var (
   188  			timer      *time.Timer
   189  			cancel     context.CancelFunc
   190  			callBuffer = &batchCallBuffer{calls: calls, resp: make([]*jsonrpcMessage, 0, len(calls))}
   191  		)
   192  
   193  		cp.ctx, cancel = context.WithCancel(cp.ctx)
   194  		defer cancel()
   195  
   196  		// Cancel the request context after timeout and send an error response. Since the
   197  		// currently-running method might not return immediately on timeout, we must wait
   198  		// for the timeout concurrently with processing the request.
   199  		if timeout, ok := ContextRequestTimeout(cp.ctx); ok {
   200  			timer = time.AfterFunc(timeout, func() {
   201  				cancel()
   202  				callBuffer.timeout(cp.ctx, h.conn)
   203  			})
   204  		}
   205  
   206  		for {
   207  			// No need to handle rest of calls if timed out.
   208  			if cp.ctx.Err() != nil {
   209  				break
   210  			}
   211  			msg := callBuffer.nextCall()
   212  			if msg == nil {
   213  				break
   214  			}
   215  			resp := h.handleCallMsg(cp, msg)
   216  			callBuffer.pushResponse(resp)
   217  		}
   218  		if timer != nil {
   219  			timer.Stop()
   220  		}
   221  		callBuffer.write(cp.ctx, h.conn)
   222  		h.addSubscriptions(cp.notifiers)
   223  		for _, n := range cp.notifiers {
   224  			n.activate()
   225  		}
   226  	})
   227  }
   228  
   229  // handleMsg handles a single message.
   230  func (h *handler) handleMsg(msg *jsonrpcMessage) {
   231  	if ok := h.handleImmediate(msg); ok {
   232  		return
   233  	}
   234  	h.startCallProc(func(cp *callProc) {
   235  		var (
   236  			responded sync.Once
   237  			timer     *time.Timer
   238  			cancel    context.CancelFunc
   239  		)
   240  		cp.ctx, cancel = context.WithCancel(cp.ctx)
   241  		defer cancel()
   242  
   243  		// Cancel the request context after timeout and send an error response. Since the
   244  		// running method might not return immediately on timeout, we must wait for the
   245  		// timeout concurrently with processing the request.
   246  		if timeout, ok := ContextRequestTimeout(cp.ctx); ok {
   247  			timer = time.AfterFunc(timeout, func() {
   248  				cancel()
   249  				responded.Do(func() {
   250  					resp := msg.errorResponse(&internalServerError{errcodeTimeout, errMsgTimeout})
   251  					h.conn.writeJSON(cp.ctx, resp, true)
   252  				})
   253  			})
   254  		}
   255  
   256  		answer := h.handleCallMsg(cp, msg)
   257  		if timer != nil {
   258  			timer.Stop()
   259  		}
   260  		h.addSubscriptions(cp.notifiers)
   261  		if answer != nil {
   262  			responded.Do(func() {
   263  				h.conn.writeJSON(cp.ctx, answer, false)
   264  			})
   265  		}
   266  		for _, n := range cp.notifiers {
   267  			n.activate()
   268  		}
   269  	})
   270  }
   271  
   272  // close cancels all requests except for inflightReq and waits for
   273  // call goroutines to shut down.
   274  func (h *handler) close(err error, inflightReq *requestOp) {
   275  	h.cancelAllRequests(err, inflightReq)
   276  	h.callWG.Wait()
   277  	h.cancelRoot()
   278  	h.cancelServerSubscriptions(err)
   279  }
   280  
   281  // addRequestOp registers a request operation.
   282  func (h *handler) addRequestOp(op *requestOp) {
   283  	for _, id := range op.ids {
   284  		h.respWait[string(id)] = op
   285  	}
   286  }
   287  
   288  // removeRequestOps stops waiting for the given request IDs.
   289  func (h *handler) removeRequestOp(op *requestOp) {
   290  	for _, id := range op.ids {
   291  		delete(h.respWait, string(id))
   292  	}
   293  }
   294  
   295  // cancelAllRequests unblocks and removes pending requests and active subscriptions.
   296  func (h *handler) cancelAllRequests(err error, inflightReq *requestOp) {
   297  	didClose := make(map[*requestOp]bool)
   298  	if inflightReq != nil {
   299  		didClose[inflightReq] = true
   300  	}
   301  
   302  	for id, op := range h.respWait {
   303  		// Remove the op so that later calls will not close op.resp again.
   304  		delete(h.respWait, id)
   305  
   306  		if !didClose[op] {
   307  			op.err = err
   308  			close(op.resp)
   309  			didClose[op] = true
   310  		}
   311  	}
   312  	for id, sub := range h.clientSubs {
   313  		delete(h.clientSubs, id)
   314  		sub.close(err)
   315  	}
   316  }
   317  
   318  func (h *handler) addSubscriptions(nn []*Notifier) {
   319  	h.subLock.Lock()
   320  	defer h.subLock.Unlock()
   321  
   322  	for _, n := range nn {
   323  		if sub := n.takeSubscription(); sub != nil {
   324  			h.serverSubs[sub.ID] = sub
   325  		}
   326  	}
   327  }
   328  
   329  // cancelServerSubscriptions removes all subscriptions and closes their error channels.
   330  func (h *handler) cancelServerSubscriptions(err error) {
   331  	h.subLock.Lock()
   332  	defer h.subLock.Unlock()
   333  
   334  	for id, s := range h.serverSubs {
   335  		s.err <- err
   336  		close(s.err)
   337  		delete(h.serverSubs, id)
   338  	}
   339  }
   340  
   341  // startCallProc runs fn in a new goroutine and starts tracking it in the h.calls wait group.
   342  func (h *handler) startCallProc(fn func(*callProc)) {
   343  	h.callWG.Add(1)
   344  	go func() {
   345  		ctx, cancel := context.WithCancel(h.rootCtx)
   346  		defer h.callWG.Done()
   347  		defer cancel()
   348  		fn(&callProc{ctx: ctx})
   349  	}()
   350  }
   351  
   352  // handleImmediate executes non-call messages. It returns false if the message is a
   353  // call or requires a reply.
   354  func (h *handler) handleImmediate(msg *jsonrpcMessage) bool {
   355  	start := time.Now()
   356  	switch {
   357  	case msg.isNotification():
   358  		if strings.HasSuffix(msg.Method, notificationMethodSuffix) {
   359  			h.handleSubscriptionResult(msg)
   360  			return true
   361  		}
   362  		return false
   363  	case msg.isResponse():
   364  		h.handleResponse(msg)
   365  		h.log.Trace("Handled RPC response", "reqid", idForLog{msg.ID}, "duration", time.Since(start))
   366  		return true
   367  	default:
   368  		return false
   369  	}
   370  }
   371  
   372  // handleSubscriptionResult processes subscription notifications.
   373  func (h *handler) handleSubscriptionResult(msg *jsonrpcMessage) {
   374  	var result subscriptionResult
   375  	if err := json.Unmarshal(msg.Params, &result); err != nil {
   376  		h.log.Debug("Dropping invalid subscription message")
   377  		return
   378  	}
   379  	if h.clientSubs[result.ID] != nil {
   380  		h.clientSubs[result.ID].deliver(result.Result)
   381  	}
   382  }
   383  
   384  // handleResponse processes method call responses.
   385  func (h *handler) handleResponse(msg *jsonrpcMessage) {
   386  	op := h.respWait[string(msg.ID)]
   387  	if op == nil {
   388  		h.log.Debug("Unsolicited RPC response", "reqid", idForLog{msg.ID})
   389  		return
   390  	}
   391  	delete(h.respWait, string(msg.ID))
   392  	// For normal responses, just forward the reply to Call/BatchCall.
   393  	if op.sub == nil {
   394  		op.resp <- msg
   395  		return
   396  	}
   397  	// For subscription responses, start the subscription if the server
   398  	// indicates success. EthSubscribe gets unblocked in either case through
   399  	// the op.resp channel.
   400  	defer close(op.resp)
   401  	if msg.Error != nil {
   402  		op.err = msg.Error
   403  		return
   404  	}
   405  	if op.err = json.Unmarshal(msg.Result, &op.sub.subid); op.err == nil {
   406  		go op.sub.run()
   407  		h.clientSubs[op.sub.subid] = op.sub
   408  	}
   409  }
   410  
   411  // handleCallMsg executes a call message and returns the answer.
   412  func (h *handler) handleCallMsg(ctx *callProc, msg *jsonrpcMessage) *jsonrpcMessage {
   413  	start := time.Now()
   414  	switch {
   415  	case msg.isNotification():
   416  		h.handleCall(ctx, msg)
   417  		h.log.Debug("Served "+msg.Method, "duration", time.Since(start))
   418  		return nil
   419  	case msg.isCall():
   420  		resp := h.handleCall(ctx, msg)
   421  		var ctx []interface{}
   422  		ctx = append(ctx, "reqid", idForLog{msg.ID}, "duration", time.Since(start))
   423  		if resp.Error != nil {
   424  			ctx = append(ctx, "err", resp.Error.Message)
   425  			if resp.Error.Data != nil {
   426  				ctx = append(ctx, "errdata", resp.Error.Data)
   427  			}
   428  			h.log.Warn("Served "+msg.Method, ctx...)
   429  		} else {
   430  			h.log.Debug("Served "+msg.Method, ctx...)
   431  		}
   432  		return resp
   433  	case msg.hasValidID():
   434  		return msg.errorResponse(&invalidRequestError{"invalid request"})
   435  	default:
   436  		return errorMessage(&invalidRequestError{"invalid request"})
   437  	}
   438  }
   439  
   440  // handleCall processes method calls.
   441  func (h *handler) handleCall(cp *callProc, msg *jsonrpcMessage) *jsonrpcMessage {
   442  	if msg.isSubscribe() {
   443  		return h.handleSubscribe(cp, msg)
   444  	}
   445  	var callb *callback
   446  	if msg.isUnsubscribe() {
   447  		callb = h.unsubscribeCb
   448  	} else {
   449  		callb = h.reg.callback(msg.Method)
   450  	}
   451  	if callb == nil {
   452  		return msg.errorResponse(&methodNotFoundError{method: msg.Method})
   453  	}
   454  	args, err := parsePositionalArguments(msg.Params, callb.argTypes)
   455  	if err != nil {
   456  		return msg.errorResponse(&invalidParamsError{err.Error()})
   457  	}
   458  	start := time.Now()
   459  	answer := h.runMethod(cp.ctx, msg, callb, args)
   460  	// Collect the statistics for RPC calls if metrics is enabled.
   461  	// We only care about pure rpc call. Filter out subscription.
   462  	if callb != h.unsubscribeCb {
   463  		rpcRequestGauge.Inc(1)
   464  		if answer.Error != nil {
   465  			failedRequestGauge.Inc(1)
   466  		} else {
   467  			successfulRequestGauge.Inc(1)
   468  		}
   469  		rpcServingTimer.UpdateSince(start)
   470  		updateServeTimeHistogram(msg.Method, answer.Error == nil, time.Since(start))
   471  	}
   472  	return answer
   473  }
   474  
   475  // handleSubscribe processes *_subscribe method calls.
   476  func (h *handler) handleSubscribe(cp *callProc, msg *jsonrpcMessage) *jsonrpcMessage {
   477  	if !h.allowSubscribe {
   478  		return msg.errorResponse(&internalServerError{
   479  			code:    errcodeNotificationsUnsupported,
   480  			message: ErrNotificationsUnsupported.Error(),
   481  		})
   482  	}
   483  
   484  	// Subscription method name is first argument.
   485  	name, err := parseSubscriptionName(msg.Params)
   486  	if err != nil {
   487  		return msg.errorResponse(&invalidParamsError{err.Error()})
   488  	}
   489  	namespace := msg.namespace()
   490  	callb := h.reg.subscription(namespace, name)
   491  	if callb == nil {
   492  		return msg.errorResponse(&subscriptionNotFoundError{namespace, name})
   493  	}
   494  
   495  	// Parse subscription name arg too, but remove it before calling the callback.
   496  	argTypes := append([]reflect.Type{stringType}, callb.argTypes...)
   497  	args, err := parsePositionalArguments(msg.Params, argTypes)
   498  	if err != nil {
   499  		return msg.errorResponse(&invalidParamsError{err.Error()})
   500  	}
   501  	args = args[1:]
   502  
   503  	// Install notifier in context so the subscription handler can find it.
   504  	n := &Notifier{h: h, namespace: namespace}
   505  	cp.notifiers = append(cp.notifiers, n)
   506  	ctx := context.WithValue(cp.ctx, notifierKey{}, n)
   507  
   508  	return h.runMethod(ctx, msg, callb, args)
   509  }
   510  
   511  // runMethod runs the Go callback for an RPC method.
   512  func (h *handler) runMethod(ctx context.Context, msg *jsonrpcMessage, callb *callback, args []reflect.Value) *jsonrpcMessage {
   513  	result, err := callb.call(ctx, msg.Method, args)
   514  	if err != nil {
   515  		return msg.errorResponse(err)
   516  	}
   517  	return msg.response(result)
   518  }
   519  
   520  // unsubscribe is the callback function for all *_unsubscribe calls.
   521  func (h *handler) unsubscribe(ctx context.Context, id ID) (bool, error) {
   522  	h.subLock.Lock()
   523  	defer h.subLock.Unlock()
   524  
   525  	s := h.serverSubs[id]
   526  	if s == nil {
   527  		return false, ErrSubscriptionNotFound
   528  	}
   529  	close(s.err)
   530  	delete(h.serverSubs, id)
   531  	return true, nil
   532  }
   533  
   534  type idForLog struct{ json.RawMessage }
   535  
   536  func (id idForLog) String() string {
   537  	if s, err := strconv.Unquote(string(id.RawMessage)); err == nil {
   538  		return s
   539  	}
   540  	return string(id.RawMessage)
   541  }