github.com/diadata-org/diadata@v1.4.593/pkg/dia/helpers/substrate-helper/gsrpc/gethrpc/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 "fmt" 23 "reflect" 24 "strconv" 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 // 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 69 type callProc struct { 70 ctx context.Context 71 notifiers []*Notifier 72 } 73 74 func newHandler(connCtx context.Context, conn jsonWriter, idgen func() ID, reg *serviceRegistry) *handler { 75 rootCtx, cancelRoot := context.WithCancel(connCtx) 76 h := &handler{ 77 reg: reg, 78 idgen: idgen, 79 conn: conn, 80 respWait: make(map[string]*requestOp), 81 clientSubs: make(map[string]*ClientSubscription), 82 rootCtx: rootCtx, 83 cancelRoot: cancelRoot, 84 allowSubscribe: true, 85 serverSubs: make(map[ID]*Subscription), 86 log: log.Root(), 87 } 88 if conn.RemoteAddr() != "" { 89 h.log = h.log.New("conn", conn.RemoteAddr()) 90 } 91 h.unsubscribeCb = newCallback(reflect.Value{}, reflect.ValueOf(h.unsubscribe)) 92 return h 93 } 94 95 // handleBatch executes all messages in a batch and returns the responses. 96 func (h *handler) handleBatch(msgs []*jsonrpcMessage) { 97 // Emit error response for empty batches: 98 if len(msgs) == 0 { 99 h.startCallProc(func(cp *callProc) { 100 h.conn.Write(cp.ctx, errorMessage(&invalidRequestError{"empty batch"})) 101 }) 102 return 103 } 104 105 // Handle non-call messages first: 106 calls := make([]*jsonrpcMessage, 0, len(msgs)) 107 for _, msg := range msgs { 108 if handled := h.handleImmediate(msg); !handled { 109 calls = append(calls, msg) 110 } 111 } 112 if len(calls) == 0 { 113 return 114 } 115 // Process calls on a goroutine because they may block indefinitely: 116 h.startCallProc(func(cp *callProc) { 117 answers := make([]*jsonrpcMessage, 0, len(msgs)) 118 for _, msg := range calls { 119 if answer := h.handleCallMsg(cp, msg); answer != nil { 120 answers = append(answers, answer) 121 } 122 } 123 h.addSubscriptions(cp.notifiers) 124 if len(answers) > 0 { 125 h.conn.Write(cp.ctx, answers) 126 } 127 for _, n := range cp.notifiers { 128 n.activate() 129 } 130 }) 131 } 132 133 // handleMsg handles a single message. 134 func (h *handler) handleMsg(msg *jsonrpcMessage) { 135 if ok := h.handleImmediate(msg); ok { 136 return 137 } 138 h.startCallProc(func(cp *callProc) { 139 answer := h.handleCallMsg(cp, msg) 140 h.addSubscriptions(cp.notifiers) 141 if answer != nil { 142 h.conn.Write(cp.ctx, answer) 143 } 144 for _, n := range cp.notifiers { 145 n.activate() 146 } 147 }) 148 } 149 150 // close cancels all requests except for inflightReq and waits for 151 // call goroutines to shut down. 152 func (h *handler) close(err error, inflightReq *requestOp) { 153 h.cancelAllRequests(err, inflightReq) 154 h.callWG.Wait() 155 h.cancelRoot() 156 h.cancelServerSubscriptions(err) 157 } 158 159 // addRequestOp registers a request operation. 160 func (h *handler) addRequestOp(op *requestOp) { 161 for _, id := range op.ids { 162 h.respWait[string(id)] = op 163 } 164 } 165 166 // removeRequestOps stops waiting for the given request IDs. 167 func (h *handler) removeRequestOp(op *requestOp) { 168 for _, id := range op.ids { 169 delete(h.respWait, string(id)) 170 } 171 } 172 173 // cancelAllRequests unblocks and removes pending requests and active subscriptions. 174 func (h *handler) cancelAllRequests(err error, inflightReq *requestOp) { 175 didClose := make(map[*requestOp]bool) 176 if inflightReq != nil { 177 didClose[inflightReq] = true 178 } 179 180 for id, op := range h.respWait { 181 // Remove the op so that later calls will not close op.resp again. 182 delete(h.respWait, id) 183 184 if !didClose[op] { 185 op.err = err 186 close(op.resp) 187 didClose[op] = true 188 } 189 } 190 for id, sub := range h.clientSubs { 191 delete(h.clientSubs, id) 192 sub.quitWithError(err, false) 193 } 194 } 195 196 func (h *handler) addSubscriptions(nn []*Notifier) { 197 h.subLock.Lock() 198 defer h.subLock.Unlock() 199 200 for _, n := range nn { 201 if sub := n.takeSubscription(); sub != nil { 202 h.serverSubs[sub.ID] = sub 203 } 204 } 205 } 206 207 // cancelServerSubscriptions removes all subscriptions and closes their error channels. 208 func (h *handler) cancelServerSubscriptions(err error) { 209 h.subLock.Lock() 210 defer h.subLock.Unlock() 211 212 for id, s := range h.serverSubs { 213 s.err <- err 214 close(s.err) 215 delete(h.serverSubs, id) 216 } 217 } 218 219 // startCallProc runs fn in a new goroutine and starts tracking it in the h.calls wait group. 220 func (h *handler) startCallProc(fn func(*callProc)) { 221 h.callWG.Add(1) 222 go func() { 223 ctx, cancel := context.WithCancel(h.rootCtx) 224 defer h.callWG.Done() 225 defer cancel() 226 fn(&callProc{ctx: ctx}) 227 }() 228 } 229 230 // handleImmediate executes non-call messages. It returns false if the message is a 231 // call or requires a reply. 232 func (h *handler) handleImmediate(msg *jsonrpcMessage) bool { 233 start := time.Now() 234 switch { 235 case msg.isNotification(): 236 // TODO find better way to perform this check 237 // if strings.HasSuffix(msg.Method, notificationMethodSuffix) { 238 h.handleSubscriptionResult(msg) 239 return true 240 // } 241 // return false 242 case msg.isResponse(): 243 h.handleResponse(msg) 244 h.log.Trace("Handled RPC response", "reqid", idForLog{msg.ID}, "t", time.Since(start)) 245 return true 246 default: 247 return false 248 } 249 } 250 251 // handleSubscriptionResult processes subscription notifications. 252 func (h *handler) handleSubscriptionResult(msg *jsonrpcMessage) { 253 var result subscriptionResult 254 if err := json.Unmarshal(msg.Params, &result); err != nil { 255 h.log.Debug("Dropping invalid subscription message") 256 return 257 } 258 resultID := fmt.Sprint(result.ID) 259 if h.clientSubs[resultID] != nil { 260 h.clientSubs[resultID].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 though 279 // the op.resp channel. 280 defer close(op.resp) 281 if msg.Error != nil { 282 op.err = msg.Error 283 return 284 } 285 var subID string 286 if op.err = json.Unmarshal(msg.Result, &subID); op.err == nil { 287 op.sub.subid = subID 288 go op.sub.start() 289 h.clientSubs[op.sub.subid] = op.sub 290 } 291 } 292 293 // handleCallMsg executes a call message and returns the answer. 294 func (h *handler) handleCallMsg(ctx *callProc, msg *jsonrpcMessage) *jsonrpcMessage { 295 start := time.Now() 296 switch { 297 case msg.isNotification(): 298 h.handleCall(ctx, msg) 299 h.log.Debug("Served "+msg.Method, "t", time.Since(start)) 300 return nil 301 case msg.isCall(): 302 resp := h.handleCall(ctx, msg) 303 if resp.Error != nil { 304 h.log.Warn("Served "+msg.Method, "reqid", idForLog{msg.ID}, "t", time.Since(start), "err", resp.Error.Message) 305 } else { 306 h.log.Debug("Served "+msg.Method, "reqid", idForLog{msg.ID}, "t", time.Since(start)) 307 } 308 return resp 309 case msg.hasValidID(): 310 return msg.errorResponse(&invalidRequestError{"invalid request"}) 311 default: 312 return errorMessage(&invalidRequestError{"invalid request"}) 313 } 314 } 315 316 // handleCall processes method calls. 317 func (h *handler) handleCall(cp *callProc, msg *jsonrpcMessage) *jsonrpcMessage { 318 // if msg.isSubscribe() { 319 // return h.handleSubscribe(cp, msg) 320 // } 321 var callb *callback 322 // if msg.isUnsubscribe() { 323 // callb = h.unsubscribeCb 324 // } else { 325 callb = h.reg.callback(msg.Method) 326 // } 327 if callb == nil { 328 return msg.errorResponse(&methodNotFoundError{method: msg.Method}) 329 } 330 args, err := parsePositionalArguments(msg.Params, callb.argTypes) 331 if err != nil { 332 return msg.errorResponse(&invalidParamsError{err.Error()}) 333 } 334 335 return h.runMethod(cp.ctx, msg, callb, args) 336 } 337 338 // handleSubscribe processes *_subscribe method calls. 339 // func (h *handler) handleSubscribe(cp *callProc, msg *jsonrpcMessage) *jsonrpcMessage { 340 // if !h.allowSubscribe { 341 // return msg.errorResponse(ErrNotificationsUnsupported) 342 // } 343 344 // // Subscription method name is first argument. 345 // name, err := parseSubscriptionName(msg.Params) 346 // if err != nil { 347 // return msg.errorResponse(&invalidParamsError{err.Error()}) 348 // } 349 // namespace := msg.namespace() 350 // callb := h.reg.subscription(namespace, name) 351 // if callb == nil { 352 // return msg.errorResponse(&subscriptionNotFoundError{namespace, name}) 353 // } 354 355 // // Parse subscription name arg too, but remove it before calling the callback. 356 // argTypes := append([]reflect.Type{stringType}, callb.argTypes...) 357 // args, err := parsePositionalArguments(msg.Params, argTypes) 358 // if err != nil { 359 // return msg.errorResponse(&invalidParamsError{err.Error()}) 360 // } 361 // args = args[1:] 362 363 // // Install notifier in context so the subscription handler can find it. 364 // n := &Notifier{h: h, namespace: namespace} 365 // cp.notifiers = append(cp.notifiers, n) 366 // ctx := context.WithValue(cp.ctx, notifierKey{}, n) 367 368 // return h.runMethod(ctx, msg, callb, args) 369 // } 370 371 // runMethod runs the Go callback for an RPC method. 372 func (h *handler) runMethod(ctx context.Context, msg *jsonrpcMessage, callb *callback, args []reflect.Value) *jsonrpcMessage { 373 result, err := callb.call(ctx, msg.Method, args) 374 if err != nil { 375 return msg.errorResponse(err) 376 } 377 return msg.response(result) 378 } 379 380 // unsubscribe is the callback function for all *_unsubscribe calls. 381 func (h *handler) unsubscribe(ctx context.Context, id ID) (bool, error) { 382 h.subLock.Lock() 383 defer h.subLock.Unlock() 384 385 s := h.serverSubs[id] 386 if s == nil { 387 return false, ErrSubscriptionNotFound 388 } 389 close(s.err) 390 delete(h.serverSubs, id) 391 return true, nil 392 } 393 394 type idForLog struct{ json.RawMessage } 395 396 func (id idForLog) String() string { 397 if s, err := strconv.Unquote(string(id.RawMessage)); err == nil { 398 return s 399 } 400 return string(id.RawMessage) 401 }