NancyFX error when doing per-route authentication in async route handler - c#

When I try to handle a route asynchronously and within that route I do the authentication, Nancy throws a RouteExecutionEarlyExitException and then the status code 500 (Internal Server Error) is returned instead of 401 (Unauthorized).
My route handler looks roughly like this:
Get["route/to/private/stuff", true] = async (args, ct) =>
{
this.RequiresAuthentication()
// process request
}
I followed the guidelines here to configure stateless authentication.
It works as expected (throws exception internally, but returns 401) when my route handler looks like this:
Get["route/to/private/stuff"] = args =>
{
this.RequiresAuthentication()
// process request
}
How do I get Nancy to return 401 in an async route handler with per-route authentication?
I'm using Nancy 1.0.0.

Ok, now I got it. Unfortunately I provided too little information.
I wasn't quite honest of how I process the request. Nancy fails with a 500 instead of a 401 when the RouteExecutionEarlyExitException is thrown synchronously, like here:
Get["/", true] = (args, ct) =>
{
this.RequiresAuthentication();
return Task.FromResult((object) "Hello World!");
};
AFAIK that wouldn't happen when Nancy used await when invoking the route handler (i.e. await handler(args, ct)) but it can happen when the handler is invoked synchronously (i.e. handler(args, ct).ContinueWith(...)).
And it seems as Nancy uses the latter approach.
So the solution is to either stick with the async modifier or ensure that exceptions are only thrown asynchronously (e.g. within Task.Run).

Related

Are exceptions an exception to the rule about not modifying the Response after next()?

While trying to find the best approach to implementing a catch-all for any uncaught exceptions I found this.
But, while implementing it, I remembered where I read:
WARNING Don't modify the Response object after calling next()..., as the response may have already started sending and you
could cause invalid data to be sent.
pg. 580
Is this an issue when the middleware is acting as a global exception handler before the MVC middleware where it seems reasonable that if the exception middleware were invoked, no response could have been started?
Invoke on Middleware:
public async Task Invoke(HttpContext context)
{
try
{
await _next(context);
}
// A catch-all for uncaught exceptions...
catch (Exception exception)
{
var response = context.Response;
response.ContentType = "application/json";
response.StatusCode = (int)HttpStatusCode.InternalServerError;
await response.WriteAsync(...);
}
}
Of course, I cannot comment on the exact intention the author had in mind when writing that. But the general idea is that you shouldn’t modify a HTTP response when it has already started. This is because the response can already be sent in parts before it is completely constructed. And when you then attempt to change the request then, you will get exceptions that you cannot modify the response.
That is why when you invoke some middleware, and that middleware is expected to produce a response, then you should not modify the response; simply because it will likely fail.
If you invoke some middleware, and that middleware does not produce a response, then of course you are free to still create a response.
For exceptions in particular, middlewares usually produce the response at the very last step, e.g. MVC works with action result objects internally and only at the end those will be executed to produce an actual result on the response. So exceptions will often be triggered before a response is produced. So it is fine to modify the response if you encounter an exception.
The built-in exception handler middleware works pretty much the same btw., so that should be a sign that what you are doing is fine. You should just be aware that modifying the response could potentially fail, so you should handle that case as well, e.g. by checking the HasStarted property.

Response bindings for Functions with ActivityTrigger

I want to create a Durable Function that calls an Activity Function, and then returns a value using dotnet core in a v2 function app. The function will of course validate its input, so may return a successful value or it may return an invalid error: a 200 or a 400 in HTTP terms. My activity function will be something like this:
[FunctionName("MyFunc")]
public static async Task<object> Run(
[ActivityTrigger] string input,
ILogger log)
{
// return something like
return new { Status = "OK", Content = content };
}
What return type should I use for this? Should make my own DTO that would be a valid response, or is there a way of returning HttpResponse objects to the orchestrator?
I think there may be a simpler solution here. Have you considered returning a simple value if your verification passes, and throwing an exception handled by the orchestrator if your verification fails?
The ActivityTrigger binding handles both input and output. Per the docs on error handling, an activity function can return any sort of JSON-serializable object to the orchestrator, and unhandled exceptions thrown by an activity function are marshalled back to the orchestrator, where they can be handled by catch blocks.
Activity-orchestrator communication doesn't use HTTP requests and responses; it uses Azure Storage tables to record history events and Azure Storage queues to trigger activities to perform async work or wake up the orchestrator once some activity's async work has completed, respectively. Unless you specifically need an HttpResponse object somewhere in your orchestration, there's no need to wrap your activity's return value in one.

Using Polly to retry after HttpStatusCode.Unauthorized

I'm making calls to an external API and want to deal with the event that a call returns an Unauthorized HttpResponseMessage. When this happens I want to refresh the access token and make the call again.
I'm trying to use Polly with the following code:
public async Task<HttpResponseMessage> MakeGetRequestAsync()
{
var retryPolicy = Policy
.HandleResult<HttpResponseMessage>(r => r.StatusCode == HttpStatusCode.Unauthorized)
.Retry(1, (exception, retryCount) =>
{
RefreshAccessToken();
});
var result = await retryPolicy.ExecuteAsync(() => CallApiAsync());
return result;
}
private async Task<HttpResponseMessage> CallApiAsync()
{
var url = Options.ResourceSandboxUrl;
var httpClient = new HttpClient();
SetRequestHeaders(httpClient);
var response = await httpClient.GetAsync(url);
response.StatusCode = HttpStatusCode.Unauthorized;
return response;
}
I put breakpoints on the ExecuteAsync statement and in DoSomethingAsync - when I step over ExecuteAsync DoSomethingAsync is not called and control is returned to the function that called MakeGetRequestAsync
I don't understand why DoSomethingAsync is not called - can anyone help me with what I'm trying to achieve?
I've looked at the Polly documentation & Polly questions on Stack Overflow but I can't figure out what's going on..
To use ExecuteAsync() you must declare the policy as .RetryAsync(...), not .Retry(...).
If your actual code reads exactly as the code sample above, the .ExecuteAsync(...) will be throwing for the mismatch between .Retry(...) [a sync policy] and .ExecuteAsync(...) [an async execution]. Since this exception is thrown, CallApiAsync() is indeed never invoked. You should be able to see the thrown exception, when calling MakeGetRequestAsync()
Overall code approach looks good tho: this retry-refreshing-authentication is a proven pattern with Polly!
I'm replying to this old question just to point out the Polly wiki page where this pattern was official documented:
retry-to-refresh-authorization
In particular this is the code snippet suggested:
var authorisationEnsuringPolicy = Policy
.HandleResult<HttpResponseMessage>(r => r.StatusCode == HttpStatusCode.Unauthorized)
.RetryAsync(
retryCount: 1, // Consider how many retries. If auth lapses and you have valid credentials, one should be enough; too many tries can cause some auth systems to blacklist.
onRetryAsync: async (outcome, retryNumber, context) => FooRefreshAuthorizationAsync(context),
/* more configuration */);
var response = authorisationEnsuringPolicy.ExecuteAsync(context => DoSomethingThatRequiresAuthorization(context), cancellationToken);
The FooRefreshAuthorizationAsync(...) method can obtain a new authorization token and pass it to the delegate executed through the policy using Polly.Context.
I might be late to the game but I leave here a post for future readers. I have created two slightly different solutions for this refresh token with retry problem.
Retry policy, Delegating Handler and Custom exception
Here is sequence diagram which depicts the communication flow
Here is the full source code
Retry policy, Delegating Handler and Polly.Context
This version separates the responsibilities in a different way:
Here is sequence diagram which depicts the communication flow
Here is the full source code
this is how async await work in .net, when the execution of your code reaches an await, two things will happened
the current thread of your code should release to make your code async, that means, your method should return
when the task you await is complete, your method should continue from where it used to be

Web API call works but gives exception The view 'xxx' or its master was not found or no view engine supports

Strange one here, code calls the method, and the method grabacat is executed on the server (I debug it and step through right to the end). The code returns to the client, but the response it received was 500 Internal Server Error with the above message. So it's saying it couldn't find the web API method that it just called successfully.
using (var response = await client.PostAsXmlAsync("cats/grabacat", mycatprefs))
{
if (response.IsSuccessStatusCode) // 500 cats/grabacat not found
Controller code:
[Route("~/api/cats/grabacat")]
[HttpPost]
public async Task GrabACat()
{
}
After debugging, if I change it to public async Task<SomeObject> GrabACat() then it works OK. This lets me return an object back to the client. However I don't want to return anything back; I want it to be equivelent to calling a void method. It will examine the status code to determine if it was successful.
I have got it working by changing GrabACat to Task and returning new object(); but I am not sure why this is required. It seems crude to return an empty object just to get it to work.
Any ideas?
The WebAPI method has a Route attribute like this:
[Route("~/api/cats/grabacat")]
Which means the URL is wrong in the POST request - you are missing the /api prefix:
using (var response = await client.PostAsXmlAsync("api/cats/grabacat", mycatprefs))
//snip

Web Api + HttpClient: An asynchronous module or handler completed while an asynchronous operation was still pending

I'm writing an application that proxies some HTTP requests using the ASP.NET Web API and I am struggling to identify the source of an intermittent error.
It seems like a race condition... but I'm not entirely sure.
Before I go into detail here is the general communication flow of the application:
Client makes a HTTP request to Proxy 1.
Proxy 1 relays the contents of the HTTP request to Proxy 2
Proxy 2 relays the contents of the HTTP request to the Target Web Application
Target Web App responds to the HTTP request and the response is streamed (chunked transfer) to Proxy 2
Proxy 2 returns the response to Proxy 1 which in turn responds to the original calling Client.
The Proxy applications are written in ASP.NET Web API RTM using .NET 4.5.
The code to perform the relay looks like so:
//Controller entry point.
public HttpResponseMessage Post()
{
using (var client = new HttpClient())
{
var request = BuildRelayHttpRequest(this.Request);
//HttpCompletionOption.ResponseHeadersRead - so that I can start streaming the response as soon
//As it begins to filter in.
var relayResult = client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead).Result;
var returnMessage = BuildResponse(relayResult);
return returnMessage;
}
}
private static HttpRequestMessage BuildRelayHttpRequest(HttpRequestMessage incomingRequest)
{
var requestUri = BuildRequestUri();
var relayRequest = new HttpRequestMessage(incomingRequest.Method, requestUri);
if (incomingRequest.Method != HttpMethod.Get && incomingRequest.Content != null)
{
relayRequest.Content = incomingRequest.Content;
}
//Copies all safe HTTP headers (mainly content) to the relay request
CopyHeaders(relayRequest, incomingRequest);
return relayRequest;
}
private static HttpRequestMessage BuildResponse(HttpResponseMessage responseMessage)
{
var returnMessage = Request.CreateResponse(responseMessage.StatusCode);
returnMessage.ReasonPhrase = responseMessage.ReasonPhrase;
returnMessage.Content = CopyContentStream(responseMessage);
//Copies all safe HTTP headers (mainly content) to the response
CopyHeaders(returnMessage, responseMessage);
}
private static PushStreamContent CopyContentStream(HttpResponseMessage sourceContent)
{
var content = new PushStreamContent(async (stream, context, transport) =>
await sourceContent.Content.ReadAsStreamAsync()
.ContinueWith(t1 => t1.Result.CopyToAsync(stream)
.ContinueWith(t2 => stream.Dispose())));
return content;
}
The error that occurs intermittently is:
An asynchronous module or handler completed while an asynchronous operation was still pending.
This error usually occurs on the first few requests to the proxy applications after which the error is not seen again.
Visual Studio never catches the Exception when thrown.
But the error can be caught in the Global.asax Application_Error event.
Unfortunately the Exception has no Stack Trace.
The proxy applications are hosted in Azure Web Roles.
Any help identifying the culprit would be appreciated.
Your problem is a subtle one: the async lambda you're passing to PushStreamContent is being interpreted as an async void (because the PushStreamContent constructor only takes Actions as parameters). So there's a race condition between your module/handler completing and the completion of that async void lambda.
PostStreamContent detects the stream closing and treats that as the end of its Task (completing the module/handler), so you just need to be sure there's no async void methods that could still run after the stream is closed. async Task methods are OK, so this should fix it:
private static PushStreamContent CopyContentStream(HttpResponseMessage sourceContent)
{
Func<Stream, Task> copyStreamAsync = async stream =>
{
using (stream)
using (var sourceStream = await sourceContent.Content.ReadAsStreamAsync())
{
await sourceStream.CopyToAsync(stream);
}
};
var content = new PushStreamContent(stream => { var _ = copyStreamAsync(stream); });
return content;
}
If you want your proxies to scale a bit better, I also recommend getting rid of all the Result calls:
//Controller entry point.
public async Task<HttpResponseMessage> PostAsync()
{
using (var client = new HttpClient())
{
var request = BuildRelayHttpRequest(this.Request);
//HttpCompletionOption.ResponseHeadersRead - so that I can start streaming the response as soon
//As it begins to filter in.
var relayResult = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
var returnMessage = BuildResponse(relayResult);
return returnMessage;
}
}
Your former code would block one thread for each request (until the headers are received); by using async all the way up to your controller level, you won't block a thread during that time.
I would like to add some wisdom for anyone else who landed here with the same error, but all of your code seems fine. Look for any lambda expressions passed into functions across the call-tree from where this occurs.
I was getting this error on a JavaScript JSON call to an MVC 5.x controller action. Everything I was doing up and down the stack was defined async Task and called using await.
However, using Visual Studio's "Set next statement" feature I systematically skipped over lines to determine which one caused it. I kept drilling down into local methods until I got to a call into an external NuGet package. The called method took an Action as a parameter and the lambda expression passed in for this Action was preceded by the async keyword. As Stephen Cleary points out above in his answer, this is treated as an async void, which MVC does not like. Luckily said package had *Async versions of the same methods. Switching to using those, along with some downstream calls to the same package fixed the problem.
I realize this is not a novel solution to the problem, but I passed over this thread a few times in my searches trying to resolve the issue because I thought I didn't have any async void or async <Action> calls, and I wanted to help someone else avoid that.
A slightly simpler model is that you can actually just use the HttpContents directly and pass them around inside the relay. I just uploaded a sample illustrating how you can rely both requests and responses asynchronously and without buffering the content in a relatively simple manner:
http://aspnet.codeplex.com/SourceControl/changeset/view/7ce67a547fd0#Samples/WebApi/RelaySample/ReadMe.txt
It is also beneficial to reuse the same HttpClient instance as this allows you to reuse connections where appropriate.

Categories