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