Skip to content
This repository was archived by the owner on Dec 14, 2018. It is now read-only.

Fix 5594 - ExceptionHandled + Result is broken #5777

Closed
wants to merge 2 commits into from

Conversation

rynowak
Copy link
Member

@rynowak rynowak commented Feb 10, 2017

This change ensures that setting ExceptionContext.Result will always
execute if set. The problem with 1.1.0 is that when we had a real short
circuit the wrong set of conditions were checked. I suspect that when you
set ExceptionFilter.Result and didn't short circuit that result filters
were also running (which is a bug).

Added a few tests that verify that the result doesn't trigger result
filters.

I did some general cleanup on this code path to make the state transitions
more clear.

No exception was thrown -> BeginResult
Exception was handled -> ExceptionHandled
Exception was not handled -> gets rethrown

This change ensures that setting ExceptionContext.Result will always
execute if set. The problem with 1.1.0 is that when we had a real short
circuit the wrong set of conditions were checked. I suspect that when you
set ExceptionFilter.Result and didn't short circuit that result filters
were also running (which is a bug).

Added a few tests that verify that the result doesn't trigger result
filters.

I did some general cleanup on this code path to make the state transitions
more clear.

No exception was thrown -> BeginResult
Exception was handled -> ExceptionHandled
Exception was not handled -> gets rethrown
@@ -657,7 +675,7 @@ private Task Next(ref State next, ref Scope scope, ref object state, ref bool is
return task;
}

goto case State.ResourceEnd;
goto case State.InvokeEnd;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was a bug as well 😲

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we be fixing this in a patch?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Notice no tests changed to confirm a bug was fixed here. Suggest backing this out as it seems unrelated.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't think of a way that this will manifest in observable bad behavior, but it's part of the code we're fixing and it's just so obviously wrong.

"if the task is completed goto state1; else await the task and then goto state2"

If you still feel strongly then I'm at least going to leave a comment here that this is wrong but we decided not to fix it, and then I'll fix it in dev.

@rynowak
Copy link
Member Author

rynowak commented Feb 10, 2017

/cc @dougbu @javiercn @pranavkm

@@ -657,7 +675,7 @@ private Task Next(ref State next, ref Scope scope, ref object state, ref bool is
return task;
}

goto case State.ResourceEnd;
goto case State.InvokeEnd;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we be fixing this in a patch?

if (exceptionContext.Result != null && !exceptionContext.ExceptionHandled)
if (exceptionContext.Result != null ||
exceptionContext.Exception == null ||
exceptionContext.ExceptionHandled)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's possible the table we came up with still has a bug. In MVC 5.x, setting the Result alone changes nothing because the Exception will still be rethrown. I believe that was also the case in ASP.NET Core MVC 1.0.0. Should I double-check with 1.0.0?

If I'm wrong, this is fine for the patch release. We should revisit the exceptionContext.Result != null && exceptionContext.Exception != null && !exceptionContext.ExceptionHandled behaviour for 2.0. It doesn't make sense (and confused our customers) to have the Result used when they haven't indicated the Exception should be suppressed.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Follow up: I was incorrect and the ASP.NET Core MVC 1.0.0 behaviour when an IExceptionFilter sets Result but neither nulls out Exception nor sets ExceptionHandled is not the same as MVC 5.x: ASP.NET Core 1.0.0 and 1.1.0 both execute the Result in this case and neither short-circuits.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, no change needed here. I'd like to discuss suppressing the Exception in the "Result-only" case for 2.0.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Discussed in person and I will add a test that verifies that we don't short circuit when you set result. IE any subsequent exception filters still have a chance to run.

@rynowak
Copy link
Member Author

rynowak commented Feb 10, 2017

@dougbu updated

@@ -657,6 +675,9 @@ private Task Next(ref State next, ref Scope scope, ref object state, ref bool is
return task;
}

// Found this while investigating #5594. This is not right, but we think it's harmless
// so we're leaving it here for the patch release. This should go to InvokeEnd if the
// scope is not Resource because that can only happen when there are no resource filters.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 like it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And, of course, my requested change is to double the number of comments like this.

Debug.Assert(state != null);
Debug.Assert(_exceptionContext != null);

if (_exceptionContext.Result == null)
{
_exceptionContext.Result = new EmptyResult();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can remove the Debug.Assert on Line 655 since this ensures Result is never null.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nah, if I don't get to fix my obvious bug I'm not adding more code cleanup.

@rynowak
Copy link
Member Author

rynowak commented Feb 13, 2017

Fixed by:
531c11d (rel/1.1.2)
af5648c (dev)

@rynowak rynowak closed this Feb 13, 2017
@rynowak rynowak deleted the rynowak/exception-filter branch February 13, 2017 16:38
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants