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 }