private Task Next(ref State next, ref Scope scope, ref object state, ref bool isCompleted) { switch (next) { case State.InvokeBegin: { goto case State.AuthorizationBegin; } case State.AuthorizationBegin: { _cursor.Reset(); goto case State.AuthorizationNext; } case State.AuthorizationNext: { var current = _cursor.GetNextFilter <IAuthorizationFilter, IAsyncAuthorizationFilter>(); if (current.FilterAsync != null) { if (_authorizationContext == null) { _authorizationContext = new AuthorizationFilterContext(_actionContext, _filters); } state = current.FilterAsync; goto case State.AuthorizationAsyncBegin; } else if (current.Filter != null) { if (_authorizationContext == null) { _authorizationContext = new AuthorizationFilterContext(_actionContext, _filters); } state = current.Filter; goto case State.AuthorizationSync; } else { goto case State.AuthorizationEnd; } } case State.AuthorizationAsyncBegin: { Debug.Assert(state != null); Debug.Assert(_authorizationContext != null); var filter = (IAsyncAuthorizationFilter)state; var authorizationContext = _authorizationContext; _diagnosticSource.BeforeOnAuthorizationAsync(authorizationContext, filter); var task = filter.OnAuthorizationAsync(authorizationContext); if (task.Status != TaskStatus.RanToCompletion) { next = State.AuthorizationAsyncEnd; return(task); } goto case State.AuthorizationAsyncEnd; } case State.AuthorizationAsyncEnd: { Debug.Assert(state != null); Debug.Assert(_authorizationContext != null); var filter = (IAsyncAuthorizationFilter)state; var authorizationContext = _authorizationContext; _diagnosticSource.AfterOnAuthorizationAsync(authorizationContext, filter); if (authorizationContext.Result != null) { goto case State.AuthorizationShortCircuit; } goto case State.AuthorizationNext; } case State.AuthorizationSync: { Debug.Assert(state != null); Debug.Assert(_authorizationContext != null); var filter = (IAuthorizationFilter)state; var authorizationContext = _authorizationContext; _diagnosticSource.BeforeOnAuthorization(authorizationContext, filter); filter.OnAuthorization(authorizationContext); _diagnosticSource.AfterOnAuthorization(authorizationContext, filter); if (authorizationContext.Result != null) { goto case State.AuthorizationShortCircuit; } goto case State.AuthorizationNext; } case State.AuthorizationShortCircuit: { Debug.Assert(state != null); Debug.Assert(_authorizationContext != null); _logger.AuthorizationFailure((IFilterMetadata)state); // If an authorization filter short circuits, the result is the last thing we execute // so just return that task instead of calling back into the state machine. isCompleted = true; return(InvokeResultAsync(_authorizationContext.Result)); } case State.AuthorizationEnd: { goto case State.ResourceBegin; } case State.ResourceBegin: { _cursor.Reset(); goto case State.ResourceNext; } case State.ResourceNext: { var current = _cursor.GetNextFilter <IResourceFilter, IAsyncResourceFilter>(); if (current.FilterAsync != null) { if (_resourceExecutingContext == null) { _resourceExecutingContext = new ResourceExecutingContext( _actionContext, _filters, _valueProviderFactories); } state = current.FilterAsync; goto case State.ResourceAsyncBegin; } else if (current.Filter != null) { if (_resourceExecutingContext == null) { _resourceExecutingContext = new ResourceExecutingContext( _actionContext, _filters, _valueProviderFactories); } state = current.Filter; goto case State.ResourceSyncBegin; } else { // All resource filters are currently on the stack - now execute the 'inside'. goto case State.ResourceInside; } } case State.ResourceAsyncBegin: { Debug.Assert(state != null); Debug.Assert(_resourceExecutingContext != null); var filter = (IAsyncResourceFilter)state; var resourceExecutingContext = _resourceExecutingContext; _diagnosticSource.BeforeOnResourceExecution(resourceExecutingContext, filter); var task = filter.OnResourceExecutionAsync(resourceExecutingContext, InvokeNextResourceFilterAwaitedAsync); if (task.Status != TaskStatus.RanToCompletion) { next = State.ResourceAsyncEnd; return(task); } goto case State.ResourceAsyncEnd; } case State.ResourceAsyncEnd: { Debug.Assert(state != null); Debug.Assert(_resourceExecutingContext != null); var filter = (IAsyncResourceFilter)state; if (_resourceExecutedContext == null) { // If we get here then the filter didn't call 'next' indicating a short circuit. _resourceExecutedContext = new ResourceExecutedContext(_resourceExecutingContext, _filters) { Canceled = true, Result = _resourceExecutingContext.Result, }; _diagnosticSource.AfterOnResourceExecution(_resourceExecutedContext, filter); // A filter could complete a Task without setting a result if (_resourceExecutingContext.Result != null) { goto case State.ResourceShortCircuit; } } goto case State.ResourceEnd; } case State.ResourceSyncBegin: { Debug.Assert(state != null); Debug.Assert(_resourceExecutingContext != null); var filter = (IResourceFilter)state; var resourceExecutingContext = _resourceExecutingContext; _diagnosticSource.BeforeOnResourceExecuting(resourceExecutingContext, filter); filter.OnResourceExecuting(resourceExecutingContext); _diagnosticSource.AfterOnResourceExecuting(resourceExecutingContext, filter); if (resourceExecutingContext.Result != null) { _resourceExecutedContext = new ResourceExecutedContext(resourceExecutingContext, _filters) { Canceled = true, Result = _resourceExecutingContext.Result, }; goto case State.ResourceShortCircuit; } var task = InvokeNextResourceFilter(); if (task.Status != TaskStatus.RanToCompletion) { next = State.ResourceSyncEnd; return(task); } goto case State.ResourceSyncEnd; } case State.ResourceSyncEnd: { Debug.Assert(state != null); Debug.Assert(_resourceExecutingContext != null); Debug.Assert(_resourceExecutedContext != null); var filter = (IResourceFilter)state; var resourceExecutedContext = _resourceExecutedContext; _diagnosticSource.BeforeOnResourceExecuted(resourceExecutedContext, filter); filter.OnResourceExecuted(resourceExecutedContext); _diagnosticSource.AfterOnResourceExecuted(resourceExecutedContext, filter); goto case State.ResourceEnd; } case State.ResourceShortCircuit: { Debug.Assert(state != null); Debug.Assert(_resourceExecutingContext != null); Debug.Assert(_resourceExecutedContext != null); _logger.ResourceFilterShortCircuited((IFilterMetadata)state); var task = InvokeResultAsync(_resourceExecutingContext.Result); if (task.Status != TaskStatus.RanToCompletion) { next = State.ResourceEnd; return(task); } goto case State.ResourceEnd; } case State.ResourceInside: { goto case State.ExceptionBegin; } case State.ExceptionBegin: { _cursor.Reset(); goto case State.ExceptionNext; } case State.ExceptionNext: { var current = _cursor.GetNextFilter <IExceptionFilter, IAsyncExceptionFilter>(); if (current.FilterAsync != null) { state = current.FilterAsync; goto case State.ExceptionAsyncBegin; } else if (current.Filter != null) { state = current.Filter; goto case State.ExceptionSyncBegin; } else if (scope == Scope.Exception) { // All exception filters are on the stack already - so execute the 'inside'. goto case State.ExceptionInside; } else { // There are no exception filters - so jump right to the action. Debug.Assert(scope == Scope.Invoker || scope == Scope.Resource); goto case State.ActionBegin; } } case State.ExceptionAsyncBegin: { var task = InvokeNextExceptionFilterAsync(); if (task.Status != TaskStatus.RanToCompletion) { next = State.ExceptionAsyncResume; return(task); } goto case State.ExceptionAsyncResume; } case State.ExceptionAsyncResume: { Debug.Assert(state != null); var filter = (IAsyncExceptionFilter)state; var exceptionContext = _exceptionContext; // When we get here we're 'unwinding' the stack of exception filters. If we have an unhandled exception, // we'll call the filter. Otherwise there's nothing to do. if (exceptionContext?.Exception != null && !exceptionContext.ExceptionHandled) { _diagnosticSource.BeforeOnExceptionAsync(exceptionContext, filter); var task = filter.OnExceptionAsync(exceptionContext); if (task.Status != TaskStatus.RanToCompletion) { next = State.ExceptionAsyncEnd; return(task); } goto case State.ExceptionAsyncEnd; } goto case State.ExceptionEnd; } case State.ExceptionAsyncEnd: { Debug.Assert(state != null); Debug.Assert(_exceptionContext != null); var filter = (IAsyncExceptionFilter)state; var exceptionContext = _exceptionContext; _diagnosticSource.AfterOnExceptionAsync(exceptionContext, filter); if (exceptionContext.Exception == null || exceptionContext.ExceptionHandled) { // We don't need to do anthing to trigger a short circuit. If there's another // exception filter on the stack it will check the same set of conditions // and then just skip itself. _logger.ExceptionFilterShortCircuited(filter); } goto case State.ExceptionEnd; } case State.ExceptionSyncBegin: { var task = InvokeNextExceptionFilterAsync(); if (task.Status != TaskStatus.RanToCompletion) { next = State.ExceptionSyncEnd; return(task); } goto case State.ExceptionSyncEnd; } case State.ExceptionSyncEnd: { Debug.Assert(state != null); var filter = (IExceptionFilter)state; var exceptionContext = _exceptionContext; // When we get here we're 'unwinding' the stack of exception filters. If we have an unhandled exception, // we'll call the filter. Otherwise there's nothing to do. if (exceptionContext?.Exception != null && !exceptionContext.ExceptionHandled) { _diagnosticSource.BeforeOnException(exceptionContext, filter); filter.OnException(exceptionContext); _diagnosticSource.AfterOnException(exceptionContext, filter); if (exceptionContext.Exception == null || exceptionContext.ExceptionHandled) { // We don't need to do anthing to trigger a short circuit. If there's another // exception filter on the stack it will check the same set of conditions // and then just skip itself. _logger.ExceptionFilterShortCircuited(filter); } } goto case State.ExceptionEnd; } case State.ExceptionInside: { goto case State.ActionBegin; } case State.ExceptionHandled: { // We arrive in this state when an exception happened, but was handled by exception filters // either by setting ExceptionHandled, or nulling out the Exception or setting a result // on the ExceptionContext. // // We need to execute the result (if any) and then exit gracefully which unwinding Resource // filters. Debug.Assert(state != null); Debug.Assert(_exceptionContext != null); if (_exceptionContext.Result == null) { _exceptionContext.Result = new EmptyResult(); } if (scope == Scope.Resource) { Debug.Assert(_exceptionContext.Result != null); _result = _exceptionContext.Result; } var task = InvokeResultAsync(_exceptionContext.Result); if (task.Status != TaskStatus.RanToCompletion) { next = State.ResourceInsideEnd; return(task); } goto case State.ResourceInsideEnd; } case State.ExceptionEnd: { var exceptionContext = _exceptionContext; if (scope == Scope.Exception) { isCompleted = true; return(Task.CompletedTask); } if (exceptionContext != null) { if (exceptionContext.Result != null || exceptionContext.Exception == null || exceptionContext.ExceptionHandled) { goto case State.ExceptionHandled; } Rethrow(exceptionContext); Debug.Fail("unreachable"); } goto case State.ResultBegin; } case State.ActionBegin: { var task = InvokeInnerFilterAsync(); if (task.Status != TaskStatus.RanToCompletion) { next = State.ActionEnd; return(task); } goto case State.ActionEnd; } case State.ActionEnd: { if (scope == Scope.Exception) { // If we're inside an exception filter, let's allow those filters to 'unwind' before // the result. isCompleted = true; return(Task.CompletedTask); } Debug.Assert(scope == Scope.Invoker || scope == Scope.Resource); goto case State.ResultBegin; } case State.ResultBegin: { _cursor.Reset(); goto case State.ResultNext; } case State.ResultNext: { var current = _cursor.GetNextFilter <IResultFilter, IAsyncResultFilter>(); if (current.FilterAsync != null) { if (_resultExecutingContext == null) { _resultExecutingContext = new ResultExecutingContext(_actionContext, _filters, _result, _instance); } state = current.FilterAsync; goto case State.ResultAsyncBegin; } else if (current.Filter != null) { if (_resultExecutingContext == null) { _resultExecutingContext = new ResultExecutingContext(_actionContext, _filters, _result, _instance); } state = current.Filter; goto case State.ResultSyncBegin; } else { goto case State.ResultInside; } } case State.ResultAsyncBegin: { Debug.Assert(state != null); Debug.Assert(_resultExecutingContext != null); var filter = (IAsyncResultFilter)state; var resultExecutingContext = _resultExecutingContext; _diagnosticSource.BeforeOnResultExecution(resultExecutingContext, filter); var task = filter.OnResultExecutionAsync(resultExecutingContext, InvokeNextResultFilterAwaitedAsync); if (task.Status != TaskStatus.RanToCompletion) { next = State.ResultAsyncEnd; return(task); } goto case State.ResultAsyncEnd; } case State.ResultAsyncEnd: { Debug.Assert(state != null); Debug.Assert(_resultExecutingContext != null); var filter = (IAsyncResultFilter)state; var resultExecutingContext = _resultExecutingContext; var resultExecutedContext = _resultExecutedContext; if (resultExecutedContext == null || resultExecutingContext.Cancel) { // Short-circuited by not calling next || Short-circuited by setting Cancel == true _logger.ResultFilterShortCircuited(filter); _resultExecutedContext = new ResultExecutedContext( _actionContext, _filters, resultExecutingContext.Result, _instance) { Canceled = true, }; } _diagnosticSource.AfterOnResultExecution(_resultExecutedContext, filter); goto case State.ResultEnd; } case State.ResultSyncBegin: { Debug.Assert(state != null); Debug.Assert(_resultExecutingContext != null); var filter = (IResultFilter)state; var resultExecutingContext = _resultExecutingContext; _diagnosticSource.BeforeOnResultExecuting(resultExecutingContext, filter); filter.OnResultExecuting(resultExecutingContext); _diagnosticSource.AfterOnResultExecuting(resultExecutingContext, filter); if (_resultExecutingContext.Cancel) { // Short-circuited by setting Cancel == true _logger.ResultFilterShortCircuited(filter); _resultExecutedContext = new ResultExecutedContext( resultExecutingContext, _filters, resultExecutingContext.Result, _instance) { Canceled = true, }; goto case State.ResultEnd; } var task = InvokeNextResultFilterAsync(); if (task.Status != TaskStatus.RanToCompletion) { next = State.ResultSyncEnd; return(task); } goto case State.ResultSyncEnd; } case State.ResultSyncEnd: { Debug.Assert(state != null); Debug.Assert(_resultExecutingContext != null); Debug.Assert(_resultExecutedContext != null); var filter = (IResultFilter)state; var resultExecutedContext = _resultExecutedContext; _diagnosticSource.BeforeOnResultExecuted(resultExecutedContext, filter); filter.OnResultExecuted(resultExecutedContext); _diagnosticSource.AfterOnResultExecuted(resultExecutedContext, filter); goto case State.ResultEnd; } case State.ResultInside: { // If we executed result filters then we need to grab the result from there. if (_resultExecutingContext != null) { _result = _resultExecutingContext.Result; } if (_result == null) { // The empty result is always flowed back as the 'executed' result if we don't have one. _result = new EmptyResult(); } var task = InvokeResultAsync(_result); if (task.Status != TaskStatus.RanToCompletion) { next = State.ResultEnd; return(task); } goto case State.ResultEnd; } case State.ResultEnd: { var result = _result; if (scope == Scope.Result) { if (_resultExecutedContext == null) { _resultExecutedContext = new ResultExecutedContext(_actionContext, _filters, result, _instance); } isCompleted = true; return(Task.CompletedTask); } Rethrow(_resultExecutedContext); goto case State.ResourceInsideEnd; } case State.ResourceInsideEnd: { if (scope == Scope.Resource) { _resourceExecutedContext = new ResourceExecutedContext(_actionContext, _filters) { Result = _result, }; goto case State.ResourceEnd; } goto case State.InvokeEnd; } case State.ResourceEnd: { if (scope == Scope.Resource) { isCompleted = true; return(Task.CompletedTask); } Debug.Assert(scope == Scope.Invoker); Rethrow(_resourceExecutedContext); goto case State.InvokeEnd; } case State.InvokeEnd: { isCompleted = true; return(Task.CompletedTask); } default: throw new InvalidOperationException(); } }