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 }