github.com/powerman/golang-tools@v0.1.11-0.20220410185822-5ad214d8d803/internal/jsonrpc2/handler.go (about) 1 // Copyright 2019 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package jsonrpc2 6 7 import ( 8 "context" 9 "fmt" 10 "sync" 11 12 "github.com/powerman/golang-tools/internal/event" 13 ) 14 15 // Handler is invoked to handle incoming requests. 16 // The Replier sends a reply to the request and must be called exactly once. 17 type Handler func(ctx context.Context, reply Replier, req Request) error 18 19 // Replier is passed to handlers to allow them to reply to the request. 20 // If err is set then result will be ignored. 21 type Replier func(ctx context.Context, result interface{}, err error) error 22 23 // MethodNotFound is a Handler that replies to all call requests with the 24 // standard method not found response. 25 // This should normally be the final handler in a chain. 26 func MethodNotFound(ctx context.Context, reply Replier, req Request) error { 27 return reply(ctx, nil, fmt.Errorf("%w: %q", ErrMethodNotFound, req.Method())) 28 } 29 30 // MustReplyHandler creates a Handler that panics if the wrapped handler does 31 // not call Reply for every request that it is passed. 32 func MustReplyHandler(handler Handler) Handler { 33 return func(ctx context.Context, reply Replier, req Request) error { 34 called := false 35 err := handler(ctx, func(ctx context.Context, result interface{}, err error) error { 36 if called { 37 panic(fmt.Errorf("request %q replied to more than once", req.Method())) 38 } 39 called = true 40 return reply(ctx, result, err) 41 }, req) 42 if !called { 43 panic(fmt.Errorf("request %q was never replied to", req.Method())) 44 } 45 return err 46 } 47 } 48 49 // CancelHandler returns a handler that supports cancellation, and a function 50 // that can be used to trigger canceling in progress requests. 51 func CancelHandler(handler Handler) (Handler, func(id ID)) { 52 var mu sync.Mutex 53 handling := make(map[ID]context.CancelFunc) 54 wrapped := func(ctx context.Context, reply Replier, req Request) error { 55 if call, ok := req.(*Call); ok { 56 cancelCtx, cancel := context.WithCancel(ctx) 57 ctx = cancelCtx 58 mu.Lock() 59 handling[call.ID()] = cancel 60 mu.Unlock() 61 innerReply := reply 62 reply = func(ctx context.Context, result interface{}, err error) error { 63 mu.Lock() 64 delete(handling, call.ID()) 65 mu.Unlock() 66 return innerReply(ctx, result, err) 67 } 68 } 69 return handler(ctx, reply, req) 70 } 71 return wrapped, func(id ID) { 72 mu.Lock() 73 cancel, found := handling[id] 74 mu.Unlock() 75 if found { 76 cancel() 77 } 78 } 79 } 80 81 // AsyncHandler returns a handler that processes each request goes in its own 82 // goroutine. 83 // The handler returns immediately, without the request being processed. 84 // Each request then waits for the previous request to finish before it starts. 85 // This allows the stream to unblock at the cost of unbounded goroutines 86 // all stalled on the previous one. 87 func AsyncHandler(handler Handler) Handler { 88 nextRequest := make(chan struct{}) 89 close(nextRequest) 90 return func(ctx context.Context, reply Replier, req Request) error { 91 waitForPrevious := nextRequest 92 nextRequest = make(chan struct{}) 93 unlockNext := nextRequest 94 innerReply := reply 95 reply = func(ctx context.Context, result interface{}, err error) error { 96 close(unlockNext) 97 return innerReply(ctx, result, err) 98 } 99 _, queueDone := event.Start(ctx, "queued") 100 go func() { 101 <-waitForPrevious 102 queueDone() 103 if err := handler(ctx, reply, req); err != nil { 104 event.Error(ctx, "jsonrpc2 async message delivery failed", err) 105 } 106 }() 107 return nil 108 } 109 }