github.com/april1989/origin-go-tools@v0.0.32/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/april1989/origin-go-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  }