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