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