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