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