TapHandle is called after receiving grpc request and headers, but before reading any request data yet. If we reject request here (by returning non-nil error), it won't be counted towards any metrics (eg. in middleware.grpcStatsHandler). If we accept request (no error), the request should be processe
(ctx context.Context, info *tap.Info)
| 61 | // If we accept request (no error), the request should be processed and eventually HandleRPC with stats.End notification will be called, |
| 62 | // unless the context is cancelled before we start processing the request. |
| 63 | func (g *grpcInflightLimitCheck) TapHandle(ctx context.Context, info *tap.Info) (context.Context, error) { |
| 64 | if !isMethodNameValid(info.FullMethodName) { |
| 65 | // If method name is not valid, we let the request continue, but not call method limiter. |
| 66 | // Otherwise, we would not be able to call method limiter again when the call finishes, because in this case grpc server will not call stat handler. |
| 67 | return ctx, nil |
| 68 | } |
| 69 | |
| 70 | ctx, err := g.methodLimiter.RPCCallStarting(ctx, info.FullMethodName, info.Header) |
| 71 | if err != nil { |
| 72 | return ctx, err |
| 73 | } |
| 74 | |
| 75 | // We called RPCCallStarting, so we need to ensure RPCCallFinished is called once the request is done. |
| 76 | // Because of a shortcut introduced in https://github.com/grpc/grpc-go/pull/8439 this may not happen. |
| 77 | // We could create a goroutine that would watch ctx.Done() and call RPCCallFinished if the context is done and we have not started processing the headers yet. |
| 78 | // However, that would mean paying the cost of an extra goroutine for every single gRPC request, just in case the request's context is cancelled before we start processing it. |
| 79 | // Instead of that we schedule a cheaper timer that we will cancel in the happy case, which will run after 10s and perform the cleanup only when needed. |
| 80 | state := &gprcInflightLimitCheckerState{ |
| 81 | fullMethod: info.FullMethodName, |
| 82 | timestamp: time.Now(), |
| 83 | headersProcessed: make(chan struct{}), |
| 84 | } |
| 85 | state.nonProcessedRequestTimer = g.timeAfterFunc(unprocessedRequestCheckTimeout, g.checkProbablyEarlyAbortedRequest(ctx, state)) |
| 86 | |
| 87 | return context.WithValue(ctx, gprcInflightLimitCheckerStateKey{}, state), nil |
| 88 | } |
| 89 | |
| 90 | func (g *grpcInflightLimitCheck) checkProbablyEarlyAbortedRequest(ctx context.Context, state *gprcInflightLimitCheckerState) func() { |
| 91 | return func() { |
nothing calls this directly
no test coverage detected