Passing a CancellationToken through a URI in C# - c#

I have a currently working controller method like the following in one project:
[HttpGet]
public async Task<IHttpActionResult> GetReport([FromUri] ReportParamiters ReportRequest, CancellationToken cancellationToken)
{…}
But this may be called from another project using the following to make a pass-through call, dependant on who/where the caller is:
[HttpGet]
public async Task<HttpResponseMessage> GetReport([FromUri] ReportParameters ReportParameters, CancellationToken cancellationToken)
{
using(HttpClient client = this.MessageTransferHelper.BuildJsonHttpClient(Helper.BearerToken(this.Url.Request.Headers.Authorization.ToString())))
{
HttpResponseMessage response =
await
client.GetAsync(this.ConfigurationService.ReportsUrl() + "report1/?DateFrom=" +
ReportParameters.DateFrom.ToString("MM-dd-yyyy") + "&DateTo=" + ReportParameters.DateTo.ToString("MM-dd-yyyy") +
"&valueList=" + String.Join("&valueList=", ReportParameters.Stores.ToArray()));
return response;
The second example simply creates the url and passes it to the actual controller held in the first example, and returns the results. I've searched Google for an answer to this, but can't find anything that matches.
Can I create a cancellation token passed into the GET of the second example, as shown, and then pass it on (along with the relevant search information) into the other GET ? (which is running on another, remote, server)

You can certainly pass a CancellationToken to GetAsync. It won't be the same cancellation token as the remote server will get, but they will be logically connected.
The CancellationToken in the remote GetReport may(1) be triggered if the calling process closes the HTTP socket. The local GetReport can pass a CancellationToken to HttpClient, which would cause it to cancel the request by closing its socket. If the local GetReport passes on the CancellationToken that it gets, then the following should(1) happen:
If the caller of the local GetReport (i.e., an end-user browser) cancels its request (i.e., user closes the browser tab), then the local GetReport's CancellationToken is(1) canceled.
This causes the HttpClient to cancel its request.
This causes(1) the remote GetReport's CancellationToken to cancel.
(1) It should be cancelled in theory. In reality, there's a rat's nest of bugs in ASP.NET cancellation, with the end result that the cancellation is not really guaranteed to happen. I expect this will be fixed in ASP.NET Core.

Related

How to handle CancellationToken in Azure function eventhub trigger

In an Azure function event hub trigger (v3) it takes in a cancellation token in the Run method. When cancellation is signaled it means the server it shutting down. If I send this token to for example a Get operation using httpClient it will throw a TaskCanceledException and the function will end.
Will the events that this function was processing be sent to another instance of the function on another server or are they lost? Should cancellation be handle in a different way?
[FunctionName(nameof(MyFunction)), FixedDelayRetry(10, "00:00:15")]
public async Task RunAsync(
[EventHubTrigger("%InEventHubName%",
Connection = "InEventHubConnectionString",
ConsumerGroup = "%ConsumerGroup%")]
EventData[] events,
PartitionContext partitionContext,
CancellationToken cancellationToken)
{
foreach (var ev in events)
{
var response = await _httpClient.GetAsync("http://example.com/fetch?key=" + ev.Properties["Key"],
cancellationToken);
await Process(response, cancellationToken);
}
}
Will the events that this function was processing be sent to another instance of the function on another server or are they lost?
They are lost:
Unhandled exceptions may cause you to lose messages. Executions that result in an exception will continue to progress the pointer.
Should cancellation be handle in a different way?
You could choose to ignore cancellation. That may be best for this kind of situation.

Is it possible for HTTP server to receive requests out of order even if they are sent sequentially?

(This discussion might not be specific to C#...)
I have a C# method SendMultipleRequests that sends HTTP POST request 10 times sequentially.
Is it possible for the server to receive requests out of order?
If my understanding is correct if the requests are sent concurrently (without await), the server could receive requests out of order, but in the example below it needs to wait for the response to be received at the client before sending next request, so the server will receive requests in order.
public async Task SendRequest(int i)
{
// definition of endpoint is omitted in this example
var content = new StringContent($"I am {i}-th request");
await HttpClient.PostAsync(endpoint, content);
}
public async Task SendMultipleRequests()
{
for (int i = 0; i < 10; i++)
{
await SendRequest(i);
}
}
with await your app will wait for the task returned by PostAsync to finish before it issues the next request - see the docs for postasync where it says “This operation will not block. The returned Task<TResult> object will complete after the whole response (including content) is read.” - using await will mean that you will only issue the next request after you I’ve read the content of the previous response
If you remove the await then your code will queue up ten tasks and start working on them all in some undefined order. The server will see requests in an unspecified order. This may be further exacerbated by the fact that the requests may take different routes through the internet, some slower. If you remove the await then you should capture the returned task into a list, then you can use something like await Task.WhenAll(list) to wait for them all to complete (unless you really want to fire and forget in which case you can assign them to the discard _ = client.PostAsync... but keeping the task allows you to discover and deal with exceptions that arose)

System.Net.HttpClient: SendAsync failed with OperationCanceledException without http request throught network

We use System.Net.Http.HttpClientfor calls between microservices inside k8s.
OS Linux(inside docker)
dotnet TargetFramework: netcoreapp2.1
server: Kestrel
protocol: http
Few days ago we noticed very strange behaviour of http calls:
some calls between microservice(near 2-3%) failed with error
System.OperationCanceledException: The operation was canceled.
at System.Net.Http.HttpClient.HandleFinishSendAsyncError(Exception e, CancellationTokenSource cts)
at System.Net.Http.HttpClient.FinishSendAsyncBuffered(Task`1 sendTask, HttpRequestMessage request, CancellationTokenSource cts, Boolean disposeCts)
at OurCode.HttpServiceClient.GetStringResponseAsync(String methodUri, HttpMethod httpMethod)
...another our code...
after our timeout for http calls(it is 3 sec). But there was no logs about call inside callee service.
We enabled packetbeat for tracing http requests and also noticed, that no any http requests from caller service to callee service was executed.
CPU, memory and network for this services was OK all the time.
Simplified version of our code for http calls looks like:
public async Task<string> GetStringResponseAsync(String methodUri, HttpMethod httpMethod)
{
int timeoutInMilliseconds = 3000;
var tokenSource = new CancellationTokenSource();
var rm = new HttpRequestMessage(httpMethod, methodUri);
Task<HttpResponseMessage> httpTask = HttpClient.SendAsync(rm, tokenSource.Token);
tokenSource.CancelAfter(timeoutInMilliseconds);
HttpResponseMessage response = await httpTask;
await EnsureSuccessStatusCode(response);
return await response.Content.ReadAsStringAsync();
}
Any ideas about what problem can cause this strange behaviour without http request through network, and what can I do for further investigation?
It just meant that the web service did not respond.
HttpClient throws a TaskCanceledException, (which inherits from OperationCanceledException) when the timeout elapses. It is not intuitive and doesn't make any sense to me (and others), but that's what it does unfortunately.
There is some discussion about it here (a few people mentioned some workarounds to distinguish timeout from a true cancel, if you care).
I don't know if this will help anyone in the future but I had the same issues. Turns out the server I was hosting on was not automatically using the proxy address by default. I ended up having to add the following to my code:
using (var handler = new WinHttpHandler()){
handler.WindowsProxyUsePolicy = WindowsProxyUsePolicy.UseCustomProxy;
handler.Proxy = new WebProxy(server, port);
//the rest of your code.....
}
This manually added the proxy server information and my console app began working as expected.

Cancel call to 'HttpClient.SendAsync()'

Is it possible to cancel a call to HttpClient.SendAsync()?
I'm sending some data like this:
var requestMessage = new HttpRequestMessage(HttpMethod.Post, "some url");
var multipartFormDataContent = new MultipartFormDataContent();
// ... construction of the MultipartFormDataContent. It contains form data + picture file
requestMessage.Content = multipartFormDataContent;
var response = await client.SendAsync(requestMessage).ConfigureAwait(false);
This code works perfectly, but I need to be able to cancel a request on user demand. Is this possible?
I see that there is an overload of SendAsync that accepts a CancellationToken but I don't know how to use it. I also know about a property called IsCancellationRequested that indicates if a request has been canceled. But how do I go about actually canceling a request?
The SendAsync method supports cancellation. You can use the overload which takes a CancellationToken, which can be canceled any time you like.
You need to use the CancellationTokenSource class for this purpose. The following code shows how to do that.
CancellationTokenSource tokenSource = new CancellationTokenSource();
...
var response = await client.SendAsync(requestMessage, tokenSource.Token)
.ConfigureAwait(false);
When you want to cancel the request, call tokenSource.Cancel(); and you're done.
Important: There is no guarantee that cancelling the CancellationTokenSource will cancel the underlying operation. It depends upon the implementation of the underlying operation (in this case the SendAsync method). The operation could be canceled immediately, after few seconds, or never.
It is worth noting that this is how you'd cancel any method which supports CancellationToken. It will work with any implementation, not just the SendAsync method that is the subject of your question.
For more info, refer to Cancellation in Managed Threads

WebAPI - Log binding information at MessageHandler?

I need to log (not Trace) all requests and responses from webAPI (v1) and store the information in DB.
I thought that the most appropriate place to do it is via a MessageHandler.
So :
public class LogRequestAndResponseHandler : DelegatingHandler
{
protected override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
return base.SendAsync(request, cancellationToken)
.ContinueWith(task =>
{
var MyRequest = request;
var MyResponse = task.Result;
var responseBody = task.Result.Content;
//log db....
return task.Result;
}, cancellationToken);
}
}
Please notice that I'm reading the request and response after the SendAsync ( in a continuation) because I want a single write to DB.
But:
I thought to myself : "it would be nice if I could extract the method binding information at the same place (in the MessageHandler)"
For example , consider this code :
[HttpGet]
[ActionName("GetGraph")]
public HttpResponseMessage BlaBla(string Member_ID, int DateDiff)
{
//...
}
And this request :
http://es.com/api/claims/GetGraph?Member_ID=123&DateDiff=5&NotExists=2
Question:
Is it possible(and how) to extract something like this :
User sent match-able Member_ID with value 123
User sent match-able DateDiff with value 5
User sent non-match-able NotExists with value 2
At this stage :
You are correct about using a message handler for logging the raw request and response but then were you able to log the request body using the code above? If you actually bind the body to an action method parameter, parameter binding would actually read and empty the request body stream. So, I don't think your idea of logging the request body in task continuation will work.
Logging data about binding in a message handler is complicated and depends a lot on your action methods. Message handler request processing runs before model binding and when the response processing part runs, the notion of models do not exist, since serialization is already done. BTW, take a look at my blog post for detecting extra fields in the request, when you are using JSON payload.
http://lbadri.wordpress.com/2014/01/28/detecting-extra-fields-in-asp-net-web-api-request/
You can possibly write an action filter to look at the extra fields from the model, as mentioned in that post and store that info in request dictionary (Request.Properties) and pick that up later in the message handler and write to database. It is not straight forward though.
Alternatively, look at the built-in tracer to see if it is helpful.

Categories