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