WCF - IClientMessageInspector - Wrong principal in AfterReceiveReply - c#

Context
I've got .NET Framework 4.6.1 WebApi application. This application calls various web services. Therefore I've made an implementation of IClientMessageInspector in order to do some logging
of WCF (SOAP) requests/replies.
Issue
However after some time I've realized that some replies are logged under a different principal, i.e. principal A makes a request and receives a reply and yet the response is logged under principal B.
Below is a simplified minimalistic example of the implementation just to demonstrate the issue.
public class MyClientMessageInspector : IClientMessageInspector
{
public object BeforeSendRequest(ref Message request, IClientChannel channel)
{
var correlationState = new CorrelationState
{
Guid = Guid.NewGuid()
};
// Principal A
Debug.WriteLine(Thread.CurrentPrincipal);
// Guid: 2abf9a7b-dac2-4c0d-b38d-1cdfb95405a7
Debug.WriteLine(correlationState.Guid);
return correlationState;
}
public void AfterReceiveReply(ref Message reply, object correlationStateObject)
{
var correlationState = (CorrelationState) correlationStateObject;
// Principal B - How come???
Debug.WriteLine(Thread.CurrentPrincipal);
// Guid: 2abf9a7b-dac2-4c0d-b38d-1cdfb95405a7 (matches Guid from request)
Debug.WriteLine(correlationState.Guid);
}
}
Both Thread.CurrentPrincipal and HttpContext.CurrentUser are only set in the Global.asax.cs in the Application_PostAuthenticateRequest event.
The application heavily utilizes async/await paradigm and almost every async call (including calls of the web services) is followed by .ConfigureAwait(false). However my understanding is that Thread.CurrentPrincipal is flowed between threads reagardless of the usage of ConfigureAwait(false). That seems to be true since all my other logs are made under a correct principal, even those that happen after AfterReceiveReply.
Question
Is there an explanation why Thread.CurrentPrincipal in AfterReceiveReply contains a different principal? I know how to workaround this issue - I could send the principal between BeforeSendRequest and AfterReceiveReply within the CorrelationState object. But before doing so I need to understand why my code behaves differently than expected and also if there is possibly a better solution than the workaround.

When you use .ConfigureAwait(false) on an application with a threadpool any of the threads in the pool might be chosen to continue execution. Some of those threads might have the same context as your originating thread, others might have different contexts. You have no control over what thread takes over once you use .ConfigureAwait(false).
There is a more detailed description of what's going on here: https://medium.com/rubrikkgroup/understanding-async-avoiding-deadlocks-e41f8f2c6f5d
The above article talks about using .Result and Task.Run, not .ConfigureAwait(false) however .ConfigureAwait(false) effectively does this behind the scenes anyway if you ever call a .wait() or .result on the call anywhere in your call stack. Importantly this only happens if your code is actually waiting for something. If your async methods happen to have the data they need to continue they will happily stay on their original thread and the context variables will match.
Also when running Async with .ConfigureAwait(false) you can be sent from your first thread to another thread, then back to your first thread if it's free and the code needed to wait for something like a network read. The threadpool means you have a limited number of threads that can be called on and most operations that involve threads don't actually spin up a completely new one.
Some further reading that might help:
https://devblogs.microsoft.com/dotnet/configureawait-faq/

Related

REST-Call with async server implementation

In my application I have a server which provides a REST-Api where my UI can communicate with.
Now I have to start a long running Process on the Server but I want the client not to wait for the response of the server.
I know I could just fire and forget the Post-Call and not await the response but I need to know at the client that the Process on the server was startet correctly.
So I thought about the following:
[HttpPost]
[Route("startscan")]
public HttpResponseMessage StartScan()
{
Task.Factory.StartNew( () =>
{
//Do long running things here
});
return Request.CreateResponse(HttpStatusCode.OK);
}
So my question now is: Will the task I started be executed to it's end? Background of the question is, that as far as I knwo a controller-instance is created for each call to it. So will the instance of the controller be terminated when the requested finished or will the Task I started run to it's end. The task can take up to 10 minutes or longer sometimes.
A simple approach would be to just use an asynchronous method without awaiting the Task result like so:
[HttpPost]
[Route("startscan")]
public async HttpResponseMessage StartScan()
{
DoLongRunningThings();
return Request.CreateResponse(HttpStatusCode.OK);
}
public async Task DoLongRunningThings()
{
// Do Stuff here
}
However, if you Processing is more complex and requires more resilience you should look into how to use background jobs. Here is a good collection of what you can use: https://stackoverflow.com/a/36098218/16154479
Will the task I started be executed to it's end?
Probably yes, but also possibly no. Sometimes it won't be executed completely. This is the result of misusing an ASP.NET app (which handles HTTP requests) as a background service (which runs outside of a request context).
There is a best practice that avoids possibly-partial execution: a distributed architecture (as I describe on my blog). The idea is that your controller enqueues a message describing the work to be done into a durable queue and then returns. Then there's a separate background service that reads from the queue and does the actual work.

Parallelizing synchronous tasks while retaining the HttpContext.Current in ASP.NET

I've scoured the SO for answers but found none that pertain to the problem at hand, although this one nails it on "why", but isn't solving it.
I have a REST endpoint that needs to gather data from other endpoints - in doing so, it accesses the HttpContext (setting authentication, headers, etc... all done with 3rd party lib I don't have access to).
Unfortunately, this library for service communication is made to be synchronous, and we want to parallelize its use.
In the following example (abstracted) code, the issue is that CallEndpointSynchronously unfortunately uses some built in authentication, which throws null exception when HttpContext isn't set:
public class MyController: ApiController
//...
[HttpPost]
public async Task<IHttpActionResult> DoIt(IEnumerable<int> inputs)
{
var tasks = inputs.Select(i =>
Task.Run(()=>
{
/* call some REST endpoints, pass some arguments, get the response from each.
The obvious answer (HttpContext.Current = parentContext) can't work because
there's some async code underneath (for whatever reasons), and that would cause it
to sometimes not return to the same thread, and basically abandon the Context,
again resulting in null */
var results = Some3rdPartyTool.CallEndpointSynchronously(MyRestEndpointConfig[i]);
return results;
});
var outcome = await Task.WhenAll(tasks);
// collect outcome, do something with it, render outputs...
}
Is there a cure for this?
We want to optimize for single requests, not interested in maximizing parallel users at this moment.
Unfortunately, this library for service communication is made to be synchronous, and we want to parallelize its use.
throws null exception when HttpContext isn't set:
The obvious answer (HttpContext.Current = parentContext) can't work because there's some async code underneath (for whatever reasons), and that would cause it to sometimes not return to the same thread, and basically abandon the Context, again resulting in null
There's an important part of your question in the example code comment. :)
Normally, HttpContext shouldn't be shared across threads. It's just not threadsafe at all. But you can set HttpContext.Current (for some reason), so you can choose to live dangerously.
The more insidious problem here is that the library has a synchronous API and is doing sync-over-async - but somehow without deadlocking (?). At this point, I must be honest and say the best approach is to fix the library: make the vendor fix it, or submit a PR, or just rewrite it if you have to.
However, there is a tiny chance that you can get this kinda sorta working by adding Even More Dangerous code.
So, here's the information you need to know:
ASP.NET (pre-Core) uses an AspNetSynchronizationContext. This context:
Ensures that only one thread runs in this context at a time.
Sets HttpContext.Current for any thread that is running in the context.
Now, you could capture the SynchronizationContext.Current and install it on the thread pool threads, but in addition to being Very Dangerous, it would not achieve your actual goal (parallelization), since the AspNetSynchronizationContext only allows one thread in at a time. The first portion of the 3rd-party code would be able to run in parallel, but anything queued to the AspNetSynchronizationContext would run one thread at a time.
So, the only way I can think of making this work is to use your own custom SynchronizationContext that resumes on the same thread, and set HttpContext.Current on that thread. I have an AsyncContext class that can be used for this:
[HttpPost]
public async Task<IHttpActionResult> DoIt(IEnumerable<int> inputs)
{
var context = HttpContext.Current;
var tasks = inputs.Select(i =>
Task.Run(() =>
AsyncContext.Run(() =>
{
HttpContext.Current = context;
var results = Some3rdPartyTool.CallEndpointSynchronously(MyRestEndpointConfig[i]);
return results;
})));
var outcome = await Task.WhenAll(tasks);
}
So for each input, a thread is grabbed from the thread pool (Task.Run), a custom single-threaded synchronization context is installed (AsyncContext.Run), HttpContext.Current is set, and then the code in question is run. This may or may not work; it depends on how exactly Some3rdPartyTool uses its SynchronizationContext and HttpContext.
Note that there are several bad practices in this solution:
Using Task.Run on ASP.NET.
Accessing the same HttpContext instance simultaneously from multiple threads.
Using AsyncContext.Run on ASP.NET.
Blocking on asynchronous code (done by AsyncContext.Run and also presumably Some3rdPartyTool.
In conclusion, I again recommend updating/rewriting/replacing Some3rdPartyTool. But this pile of hacks might work.

Best Practice for I/O Heavy Async Task with WebApi

I have async action responding to a HTTP POST via web api 1.0. I need to do 2 things when I receive this request:
Do a database insert and return the identity of that new entry to the WebApp that called the function.
Using that identity to do a whole bunch work that is I/O heavy, that they WebApp and the user don't immediately care about.
In a perfect world I would put data on a queue somewhere and have a little worker to handle the queue. Since I can't immediately do that, what is the best way to make sure this work gets done without impacting the user.
[HttpPost]
public async Task<int> Post([FromBody]Object myObject)
{
return await new ObjectLogic().InsertObject(myObject);
}
public async Task<int> InsertObject(Object myObject)
{
var id = await new ObjectData().InsertObjectRoot(myObject);
Task.Run(() => new ObjectData().ObjectWork(id, myObject));
return id;
}
This is the solution I came up but I think there has to be something better since I am bascially stealing of thread from the thread pool until my work is finished. Is there a better way? I think I could use ConfigureAwait(false) in my InsertObject method since I really dont' care about the context there.
// await async function but use ConfigureAwait
public async Task<int> InsertObject(Object myObject)
{
var id = await new ObjectData().InsertObjectRoot(myObject);
await new ObjectData().ObjectWork(id, myObject).ConfigureAwait(false);
return id;
}
One question is whether your Web API should do anything other than
receive the request
place it on a queue
response with an id to indicate that the request has been received.
It's going to depend to some degree on what sort of load you're expecting or might possibly see. But if you're concerned about the number of available threads from the outset then perhaps the answer is that your Web API does nothing but the above steps.
The queue could be a literal queue, like MSMQ (or whatever is popular now.) Or it could consist of a record inserted into a table. A separate Windows service could then process that queue and do the I/O heavy work. It doesn't even have to be on the same server. You can scale it separately.
If the user does want some eventual indication then they could poll for it at intervals using the id that you returned. But for me the key is in this statement:
Using that identity to do a whole bunch work that is I/O heavy, that the WebApp and the user don't immediately care about.
The job of a web application is to serve responses - IOW, to do what the user does care about. If it's long-running, I/O heavy work that the user doesn't care about then I'd consider offloading it.

C# IHttpHandler and the use of httpclient for a web service

I have been tasked with maintaining an C# web service that pretty much boils down to the following code that is used in conjunction with IIS 8:
public class Handler1 : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
// here we process the request and return it after fetching some data
}
}
Now I need to create another web service, which in turn will request another web service for fetching data. What I've gathered is that the HttpClient() in C# is the new shiny, however finding out how to implement it in my case is not so easy (or if that would be a good solution).
My solution would something like
public class Handler1 : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
// make sure that the incoming request is valid
HttpClient client = new HttpClient();
// do stuff with ^client and request the other service
[..]
// return the data from the response from ^client to the first request
}
}
Am I on the right track or would this be a catastrophe? I would gladly take tips or pointers to relevant documentation.
There are two issues with your approach.
First, as others have said, HttpClient must not be created for each request, as this would result in your server creating one new TCP connection for each request and not dispose it from your connection pool until they reach their MaxIdleTimeout.
This is because each instance of HttpClient (or rather, each instance of HttpClientHandler) creates a unique ConnectionGroup within the ConnectionPool, so two HttpClient (created in such a way) will not share the same connections.
You can quite easily exhaust your ports (because connections are kept alive for quite some time), and even without that, you'll have all the performance issues you might find when deactivating keepalive (typically when connecting on a HTTPs server).
As for a solution, you might add a private readonly HttpClient field in your handler that you would initialize as you've done in your code (I believe HttpHandlers are instanciated once by IIS, not per-request, as long as it says it's reusable). (Edit: note that HttpClient instances are thread-safe).
The other problem you'll find is that IHttpHandler are synchronous by design. This means you'll have to return task.Result in your code. This will block the current thread (let's call it A), and all subsequent processing of the outbound request (sending the body of the request, reading the header of the response and reading the body of the response) will spawn use a thread from the thread pool to process this (let's call them B, C, and D, though they can all be the same thread since no step overlap in time).
But in Asp.net, you cannot safely do that. When your main thread A is processing an Asp.net inbound request, it will, simply put, lock the current context. And when threads B, C and D will be spawn to process the outbound request, they too, will try to acquire and lock the current context. But because your main thread A is blocked, the context has not been released. This will effectively result in deadlocks (A locks the context and wait for B/C/D, B/C/D wait for the context).
To work around this, I would advise you to use instead a IHttpAsyncHandler. Async process are not made to be used synchronous, and HttpClient can only be used in an async way.
However, this might prove much more challenging for a first usage scenario. It would be much more easier to use a HttpClient from the context of a WebApi server. However, without any knowledge as to why you have to use a HttpHandler, I do not know if this would be in the realm of acceptable answers.
There are other way to run asynchronous tasks and wait synchronously for them (such as changing temporary the CurrentContext), but they're all very dangerous.
As a side note, HttpClient is the new shiny thing, and works quite well with other new shiny things such as "all-the-way async infrastructure". But trying to use it HttpClient in an older infrastructure is not as easy.
Better solution example:
public class YourApp
{
private static HttpClient Client = new HttpClient();
public static void Main(string[] args)
{
Console.WriteLine("Requests about to start!");
for(int i = 0; i < 5; i++)
{
var result = Client.GetAsync("http://www.stackoverflow.com").Result;
Console.WriteLine(result);
}
Console.WriteLine("Requests are finished!");
Console.ReadLine();
}
}

HttpContext.Current.Items after an Async operation

Consider the following ASP.NET Web API Delegating Handler:
public class MyHandler : DelegatingHandler
{
protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
{
var guid = Guid.NewGuid();
HttpContext.Current.Items["foo"] = guid;
// An Async operation
var result = await base.SendAsync(request, cancellationToken);
//All code from this point is not gauranteed to run on the same thread that started the handler
var restoredGuid = (Guid)HttpContext.Current.Items["foo"];
//Is this gauranteed to be true
var areTheSame = guid == restoredGuid;
return result;
}
}
The above example is in a delegating handler, the same problem I am trying to fix applies in Controllers, Business Objects, etc.
I am ultimately trying to provide some simple in-memory shared state between various objects per HTTP Request
As I understand it during Async operations the ASP.NET thread originally running the operation is returned to the thread pool and a different thread may be used to finish the request after the Async operation has completed.
Does this affect the HttpContext.Current.Items collection?
Is an item that was in the Items collection guaranteed to be there when the Request resumes?
I'm aware that using HttpContext.Current is often frowned upon by
the wider community these days for reasons I completely agree
with... I'm just helping someone out of a jam.
Storing this data in the Request.Items collection is not suitable to solve this problem as my colleague requires a static due to some poor design decisions.
Many Thanks
As I understand it during Async operations the ASP.NET thread originally running the operation is returned to the thread pool and a different thread may be used to finish the request after the Async operation has completed.
That is correct. But let's talk about async on ASP.NET for just a minute.
async requires .NET 4.5. Furthermore, ASP.NET 4.5 introduces a "quirks mode" on the server side, and you have to turn the SynchronizationContext quirk off. You can do this by either setting httpRuntime.targetFramework to 4.5 or using an appSettings with aspnet:UseTaskFriendlySynchronizationContext value of true.
If your web.config does not have one of those entries, then the behavior of async is undefined. See this post for more details. I recommend using the targetFramework setting and fixing any problems that come up.
Does this affect the HttpContext.Current.Items collection? Is an item that was in the Items collection guaranteed to be there when the Request resumes?
The AspNetSynchronizationContext preserves the current request context across await points. This includes HttpContext.Current (which includes Items, User, etc).
Another possibility is CallContext.Logical[Get|Set]Data, which also flows across await points. This is useful if you don't want a code dependency on HttpContext, but has slightly more overhead.
I gave a talk at ThatConference a couple weeks ago on async on the server side; you may find the slides helpful, particularly the ones dealing with Context and Thread-Local State.
Cutting a long story short, it normally should. Unless you are using ConfigureAwait(false) which can have a side effect with continuation not flowing the context.
Alternatively try adding this setting in your app.
<appSettings>
<add key="aspnet:UseTaskFriendlySynchronizationContext" value="true" />
</appSettings>
UPDATE
NOTE!!
Initially I put false. But it must be true so that context flows.

Categories