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