I get somehow a Deadlock/hanging after I purposely catch a WebServiceException to let my application continue. However, even though the application contiues. Doing a webservice hangs and it probably seems like it is still trying to do something from the previous call.
I tried using CancellationTokenSource but that did not seem to solve my problem.
RetryHandler:
public class RetryHandler : DelegatingHandler
{
private const int MaxRetries = 2;
public RetryHandler(HttpMessageHandler innerHandler)
: base(innerHandler)
{
}
protected override async Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request,
CancellationToken cancellationToken)
{
HttpResponseMessage response = null;
Exception lastException = null;
for (var i = 0; i < MaxRetries; i++)
{
try
{
response = await base.SendAsync(request, cancellationToken).ConfigureAwait(false);
}
catch (Exception ex)
{
lastException = ex;
}
if (IsSuccessful(response))
{
return response;
}
}
throw GetException(response, lastException);
}
Calling Post twice makes my program hang:
public async Task<T> Post<T>(
string path,
HttpContent content,
string username,
string token,
HttpMessageHandler handler)
{
var client = new HttpClient(new RetryHandler(handler));
var authString = GetAuthenticationString(username, token);
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", authString);
AddUsernameAndTokenToClientRequestHeader(client, username, token);
client.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", content.Headers.ContentType.MediaType);
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("*/*"));
var result = await client.PostAsync(new Uri(path), content, _cancelHttpRequests.Token).ConfigureAwait(false);
var resultContent = result.Content.ReadAsStringAsync().Result;
return JsonConvert.DeserializeObject<T>(resultContent);
}
What is happening here is, although you are catching the exception, and supposedly let your application continue, the service itself continues it's work, async, so that even when you are trying to force the service to continue, it will still go on attempting to complete all of the desired action.
In your case: causing deadlock. Cancellation token won't help here, as your service running async and you already stopped it by catching the exception, so, you are basically doing nothing with this token.
Two ways to solve this:
Either, disconnect the service when you are getting the exception, this way forcing the service to shut.
Or try to work with your service in a sync way so that you can stop the service when ever needed, this way insuring it won't do any additional work when you stop it.
Related
I'm trying to hit my simple server's endpoint using the following code, but I keep getting "A task was canceled." during the await call. The server's logs don't show any errors and cts.IsCancellationRequested == false, however e.CancellationToken.IsCancellationRequested == true. Any advice on how to track down the cause of this cancellation? At the very least, how can I tell if it's coming from the front end or the server endpoint?
private async Task<string> SendSingleRequestToDlis(
HttpClient client,
StringContent requestData)
{
int timeout = 600000; // in ms
string dlisEndpoint = "myendpointhere";
string response;
using (var cts = new CancellationTokenSource(timeout))
{
//send
HttpResponseMessage request;
try
{
request = await client.PostAsync(dlisEndpoint, requestData);
}
catch (Exception e)
{
throw new Exception("Could not establish conection to model hosted on DLIS.", e);
}
....
You are not using your CancellationToken so don't implement it. Just use this:
string dlisEndpoint = "myendpointhere";
string response;
HttpResponseMessage request;
try
{
request = await client.PostAsync(dlisEndpoint, requestData);
}
catch (Exception e)
{
throw new Exception("Could not establish conection to model hosted on DLIS.", e);
}
If you wanted to use the cancellation token you actually have to pass it down into the client.
request = await client.PostAsync(dlisEndpoint, requestData, cts.Source);
At the very least, how can I tell if it's coming from the front end or the server endpoint?
You can know it's at the client because you're seeing an OperationCanceledException. A server can just return a response code, which you would see as an HttpRequestException.
i timed it just now and the cancellation happens at exactly 100s
This is the default value of the HttpClient.Timeout property. Set this property higher (or to Timeout.InfiniteTimeSpan) at the point where the HttpClient is created (or configured, if the HttpClient is injected).
I am quite new to C#. I'm using the await keyword to invoke HttpClient's API.
static async Task<HttpResponseMessage> CreateChannel(string channelName)
{
try
{
HttpClient client = new HttpClient();
var req = new
{
id= channelName
};
StringContent content = new StringContent(JsonConvert.SerializeObject(req).ToString(), Encoding.UTF8, "application/json");
HttpResponseMessage response = await client.PostAsync("http://localhost:3000/channel", content);
response.EnsureSuccessStatusCode();
return response;
}
catch (Exception ex)
{
...
var view = new Dialog();
...
var result = await DialogHost.Show(view);
return null;
}
}
private void initSocketIo(string channel)
{
CreateChannel(channel).Wait();
...
// after this method we init UI etc.
}
I have 2 problems which I can't seem to be able to solve
After the client.PostAsync() method runs, my app just crashes. Simple as that. I understand it's because the main thread doesn't have anything else to process, but all the code above happens in the constructor code of MainWindow, so there is a lot more to do after the await
The exception is never triggered. How do I ensure that my exception catch clause is invoked in client.PostAsync() throws an exception.
Any code suggestion that just works would do :).
You are mixing blocking calls (.Result, .Wait()) with async calls which can lead to deadlocks.
Make initSocketTo async.
private async Task initSocketIo(string channel) {
var response = await CreateChannel(channel);
...
// after this method we init UI etc.
}
Also do not try to do async in the constructor. Move the heavier processes later in the life cycle. You could even raise an event and handle that on another thread so as not to block the flow.
I created a FileResult : IHttpActionResult webapi return type for my api calls. The FileResult downloads a file from another url and then returns the stream to the client.
Initially my code had a using statement like below:
public async Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
try
{
HttpResponseMessage response;
using (var httpClient = new HttpClient())
{
response = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new System.Net.Http.StreamContent(
await httpClient.GetStreamAsync(this.filePath))
};
}
return response;
}
catch (WebException exception)
{...}
}
However this would intermittently cause a TaskCanceledException. I know that if the HttpClient is disposed before the asychronous call is finished the Task's state will change to canceled. However since I use an await in: Content = new System.Net.Http.StreamContent(await httpClient.GetStreamAsync(this.filePath)) that should prevent the HttpClient from being disposed off in the middle of the task completion.
Why does that task get canceled? It is not because of a timeout since this has happened on the smallest requests and doesn't always occur on large requests.
When I removed the using statement the code worked properly:
public async Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
try
{
HttpResponseMessage response;
var httpClient = new HttpClient();
response = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new System.Net.Http.StreamContent(
await httpClient.GetStreamAsync(this.filePath))
};
return response;
}
catch (WebException exception)
{...}
}
Any idea why the using caused the issue?
I know that if the HttpClient is disposed before the asychronous call is finished the Task's state will change to canceled. However since I use an await in: Content = new System.Net.Http.StreamContent(await httpClient.GetStreamAsync(this.filePath)) that should prevent the HttpClient from being disposed off in the middle of the task completion.
But what does that task do? It gets the stream. So, your code ends up with a Stream that may or may not be completely read when it closes the HttpClient.
HttpClient is specifically designed for reuse (and simultaneous use), so I recommend removing the using completely and moving the HttpClient declaration to a static class member. But if you want to close and reopen the clients, you should be able to get it working by reading the stream entirely into memory before closing the HttpClient.
I had a similar issue with Task Canceled exceptions. If you try catching AggregateException or having a catch all Exception block underneath your WebException, you may well find that you catch it, with one exception with the entry stating "A task was canceled"
I did some investigation and found that the AggregateException is quite misleading as described in various threads;
Setting HttpClient to a too short timeout crashes process
How can I tell when HttpClient has timed out?
Bug in httpclientgetasync should throw webexception not taskcanceledexception
I ended up changing my code to set an explicit timeout (where asyncTimeoutInMins is read from the app.config file);
string jsonResponse = string.Empty;
try
{
using (HttpClient httpClient = new HttpClient())
{
httpClient.BaseAddress = new Uri(Properties.Settings.Default.MyWebService);
httpClient.DefaultRequestHeaders.Accept.Clear();
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
httpClient.Timeout = new TimeSpan(0, asyncTimeoutInMins, 0);
HttpResponseMessage response;
response = await httpClient.GetAsync("/myservice/resource");
// Check the response StatusCode
if (response.IsSuccessStatusCode)
{
// Read the content of the response into a string
jsonResponse = await response.Content.ReadAsStringAsync();
}
else if (response.StatusCode == HttpStatusCode.Forbidden)
{
jsonResponse = await response.Content.ReadAsStringAsync();
Logger.Instance.Warning(new HttpRequestException(string.Format("The response StatusCode was {0} - {1}", response.StatusCode.ToString(), jsonResponse)));
Environment.Exit((int)ExitCodes.Unauthorised);
}
else
{
jsonResponse = await response.Content.ReadAsStringAsync();
Logger.Instance.Warning(new HttpRequestException(string.Format("The response StatusCode was {0} - {1}", response.StatusCode.ToString(), jsonResponse)));
Environment.Exit((int)ExitCodes.ApplicationError);
}
}
}
catch (HttpRequestException reqEx)
{
Logger.Instance.Error(reqEx);
Console.WriteLine("HttpRequestException : {0}", reqEx.InnerException.Message);
Environment.Exit((int)ExitCodes.ApplicationError);
}
catch (Exception ex)
{
Logger.Instance.Error(ex);
throw;
}
return jsonResponse;
I want to iterate a batch of requests, sending each one of them to an external API using HttpClient class.
foreach (var MyRequest in RequestsBatch)
{
try
{
HttpClient httpClient = new HttpClient();
httpClient.Timeout = TimeSpan.FromMilliseconds(5);
HttpResponseMessage response = await httpClient.PostAsJsonAsync<string>(string.Format("{0}api/GetResponse", endpoint), myRequest);
JObject resultResponse = await response.Content.ReadAsAsync<JObject>();
}
catch (Exception ex)
{
continue;
}
}
The context here is I need to set a very small timeout value, so in case the response takes more than that time, we simply get the "Task was cancelled" exception and continue iterating.
Now, in the code above, comment these two lines:
HttpResponseMessage response = await httpClient.PostAsJsonAsync<string>(string.Format("{0}api/GetResponse", endpoint), myRequest);
resultResponse = await response.Content.ReadAsAsync<JObject>();
The iteration ends very fast. Uncomment them and try again. It takes a lot of time.
I wonder if calling PostAsJsonAsync/ReadAsAsync methods with await takes more time than the timeout value?
Based on the answer below, supposing it will create different threads, we have this method:
public Task<JObject> GetResponse(string endPoint, JObject request, TimeSpan timeout)
{
return Task.Run(async () =>
{
try
{
HttpClient httpClient = new HttpClient();
httpClient.Timeout = TimeSpan.FromMilliseconds(5);
HttpResponseMessage response = await httpClient.PostAsJsonAsync<string>(string.Format("{0}api/GetResponse", endPoint), request).WithTimeout<HttpResponseMessage>(timeout);
JObject resultResponse = await response.Content.ReadAsAsync<JObject>().WithTimeout<JObject>(timeout);
return resultResponse;
}
catch (Exception ex)
{
return new JObject() { new JProperty("ControlledException", "Invalid response. ")};
}
});
}
An exception is raised there and the JObject exception should be returned, very fast, however, if using httpClient methods, even if it raises the exception it takes a lot of time. Is there a behind the scenes processing affecting the Task even if the return value was a simple exception JObject?
If yes, which another approach could be used to send a batch of requests to an API in a very fast way?
I agree with the accepted answer in that the key to speeding things up is to run the requests in parallel. But any solution that forces additional threads into the mix by use of Task.Run or Parallel.ForEach is not gaining you any efficiency with I/O bound asynchronous operations. If anything it's hurting.
You can easily get all calls running concurrently while letting the underlying async subsystems decide how many threads are required to complete the tasks as efficiently as possible. Chances are that number is much smaller than the number of concurrent calls, because they don't require any thread at all while they're awaiting a response.
Further, the accepted answer creates a new instance of HttpClient for each call. Don't do that either - bad things can happen.
Here's a modified version of the accepted answer:
var httpClient = new HttpClient {
Timeout = TimeSpan.FromMilliseconds(5)
};
var taskList = new List<Task<JObject>>();
foreach (var myRequest in RequestsBatch)
{
// by virtue of not awaiting each call, you've already acheived parallelism
taskList.Add(GetResponseAsync(endPoint, myRequest));
}
try
{
// asynchronously wait until all tasks are complete
await Task.WhenAll(taskList.ToArray());
}
catch (Exception ex)
{
}
async Task<JObject> GetResponseAsync(string endPoint, string myRequest)
{
// no Task.Run here!
var response = await httpClient.PostAsJsonAsync<string>(
string.Format("{0}api/GetResponse", endpoint),
myRequest);
return await response.Content.ReadAsAsync<JObject>();
}
It doesn't look like you're actually running a seperate thread for each request. Try something like this:
var taskList = new List<Task<JObject>>();
foreach (var myRequest in RequestsBatch)
{
taskList.Add(GetResponse(endPoint, myRequest));
}
try
{
Task.WaitAll(taskList.ToArray());
}
catch (Exception ex)
{
}
public Task<JObject> GetResponse(string endPoint, string myRequest)
{
return Task.Run(() =>
{
HttpClient httpClient = new HttpClient();
HttpResponseMessage response = httpClient.PostAsJsonAsync<string>(
string.Format("{0}api/GetResponse", endpoint),
myRequest,
new CancellationTokenSource(TimeSpan.FromMilliseconds(5)).Token);
JObject resultResponse = response.Content.ReadAsAsync<JObject>();
});
}
I have code like this:
public async Task<string> getToken()
{
string uri = "http://localhost/api/getToken";
HttpClient client = new HttpClient();
//client.BaseAddress = new Uri(uri);
string token = "asfd";
string baseId = "asfasdf";
string appVersion = "afsadf";
string content = "";
HttpResponseMessage response = null;
try
{
string url = string.Format("{0}?token='{1}'&baseId='{2}'&appVersion='{3}'", uri, token, baseId, appVersion);
//client.BaseAddress = new Uri(url);
response = await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead);
response.EnsureSuccessStatusCode();
content = await response.Content.ReadAsStringAsync();
}
catch (Exception ex)
{
}
return content;
}
And when I get to the line when is GetAsync called VS turn off debug mode and does not throw any exception.
On the server I have breakpoint in the action in controller (mvc web api) and it is not reached.
But when I copy url and past it to the browser action in controller is invoked.
And when I change my url for some other incorrect url, GetAsync throw exception which is captured in catch.
My application is in .net framework 4.5, console application.
Maybe I must add any dll ?
You probably aren't waiting for the asynchronous operation to complete. That's why it gets to that aysnc call, the method returns the task that represents the operation and then the application ends.
You need to await the task getToken returns by using await getToken(). If you call getToken from the main method, which can't be async you need to use Task.Wait to wait synchronously:
static void Main()
{
getToken().Wait();
}