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