HttpClient long polling interrupted by seperate thread - c#

I'm running a messaging service bot, which utilities long polling to get user messages sent. This is all working fine, until I added a new component which sends a simple http get request to a heartbeat monitoring service every 30 seconds. After implementing this component, my bot code sends an additional poll request to the messaging service whenever the heartbeat component sends its request.
I'm assuming the request sent by the heartbeat component is interrupting the long polling, or something of that nature? If I comment out the heartbeat request, everything works fine.
I've tried using a shared HttpClient, and seperate HttpClients disposed after ever use.
Heartbeat monitor code:
using (var client = new HttpClient())
{
var response = await client.GetAsync(_heartbeatUrl);
var responseString = await response.Content.ReadAsStringAsync();
if (!response.IsSuccessStatusCode)
{
Log.LogMessage(response.ReasonPhrase, LogType.Error);
Log.LogMessage(responseString, LogType.Error);
}
Log.LogMessage(responseString, LogType.Verbose);
}
Message bot poll code:
using (var client = new HttpClient())
{
client.Timeout = TimeSpan.FromMilliseconds(Timeout.Infinite);
var jsonData = JsonConvert.SerializeObject(data);
var content = new StringContent(jsonData, Encoding.UTF8, "application/json");
var fullString = _url + "/" + methodName;
Log.LogMessage("Querying: " + fullString, LogType.Verbose);
Log.LogMessage("With Data: " + jsonData, LogType.Verbose);
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, fullString)
{
Content = content,
};
request.Headers.Authorization = new AuthenticationHeaderValue("mybot", _botToken);
var response = await client.SendAsync(request);
var responseString = await response.Content.ReadAsStringAsync();
returnedObject = JsonConvert.DeserializeObject<T>(responseString);
if (!response.IsSuccessStatusCode)
{
Log.LogMessage(response.ReasonPhrase, LogType.Error);
Log.LogMessage(responseString, LogType.Error);
}
Log.LogMessage(responseString, LogType.Verbose);
}
Both of these are called in simple while(true) loops, i.e
Thread th = new Thread(async a =>
{
while (true)
{
await SendHeartbeat();
Thread.Sleep(30000);
}
});
and
new Thread(async () =>
{
while (true)
{
try
{
var results = await Methods.getUpdates(_service, update_id);
} catch (Exception e) { Log.LogMessage("Error with HTML query: " + e.Message, LogType.Error, e.StackTrace); }
}
}).Start();
I'm a bit stumped as to what could be causing this. Any suggestions are much appreciated!

Figured this out in the end. My calls to my static Log class had a lock on the file write method. I believe when the heartbeat ran and logged it's result, the polling code would be blocked when trying to Log stuff, causing the thread to be released by the async await. A new thread would start polling before the previous one had finished executing.

Related

C# async - creating a task properly

I'm creating a Task in C# but I'm not sure what I do is correct. I'm using Restsharp and in Restsharp there are two methods: Execute and ExecuteAsync. I want to do an Async call but I also need to return data to the client without blocking the execution.
Therefore I created a task which will use Execute instead of ExecuteAsync. The reason why is because I have to wait until I get a response back and then return it in the right data structure. So I thought there is no use in using ExecuteAsync if I have to await it in a Task...
My code looks as follows:
public Task<Response> ExecuteAsync()
{
return new Task<Response>(() =>
{
var client = new RestClient(URL);
if (_useBasicAuth)
{
client.Authenticator = new HttpBasicAuthenticator(_username, _password);
}
var request = RequestBuilder(_method);
var response = client.Execute(request);
return new Response()
{
HttpStatusCode = response.StatusCode,
HttpStatusDescription = response.StatusDescription,
Content = response.Content,
Cookies = ExtractCookies(response.Cookies),
Headers = ExtractHeaders(response.Headers)
};
});
}
Is this correct? The client should be able to call ExecuteAsync without blocking the execution.
I strongly suspect you should really just use ExecuteAsync and write an async method:
public async Task<Response> ExecuteAsync()
{
var client = new RestClient(URL);
if (_useBasicAuth)
{
client.Authenticator = new HttpBasicAuthenticator(_username, _password);
}
var request = RequestBuilder(_method);
var response = await client.ExecuteAsync(request).ConfigureAwait(false);
return new Response
{
HttpStatusCode = response.StatusCode,
HttpStatusDescription = response.StatusDescription,
Content = response.Content,
Cookies = ExtractCookies(response.Cookies),
Headers = ExtractHeaders(response.Headers)
};
}

Is task.Start() running in background

I have bulk of records to send push notifications by mobile device to other mobile devices.
For that I am using for loop inside task.Start(). I wonder whether task.Start() running in background or not? So that while sending remote notifications to mobile device at the same time I could do some other stuff too & it wont block mobile UI.
The code below I am using
var pushTask = new Task(() =>
{
if (myPushDataFilterd.Any())
{
var title = txtHomeworkTitle.Value.Trim();
for (int index = 0; index < myPushDataFilterd.Count; index++)
{
var row = myPushDataFilterd[index];
jData.Add("moduleName", "Homework");
jData.Add("organizationId", ddlOrganization.SelectedValue);
jData.Add("studentId", studentId);
jGcmData.Add("to", to);
jGcmData.Add("data", jData);
api = row.ServerKeyPush;
var url = new Uri("https://fcm.googleapis.com/fcm/send");
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.TryAddWithoutValidation("Authorization", "key=" + api);
var r = client.PostAsync(url, new StringContent(jGcmData.ToString(), Encoding.Default, "application/json")).Result;
}
}
}
});
pushTask.Start();
Actually this the web application part I am now using in mobile application. In mobile application do I have other better options too, to send notification?
There is no point in creating task at all. You are doing IO operations, so you can make use of already provided async api.
private async Task PostData()
{
if (myPushDataFilterd.Any())
{
var title = txtHomeworkTitle.Value.Trim();
using (var client = new HttpClient())
{
for (int index = 0; index < myPushDataFilterd.Count; index++)
{
var row = myPushDataFilterd[index];
jData.Add("moduleName", "Homework");
jData.Add("organizationId", ddlOrganization.SelectedValue);
jData.Add("studentId", studentId);
jGcmData.Add("to", to);
jGcmData.Add("data", jData);
api = row.ServerKeyPush;
var url = new Uri("https://fcm.googleapis.com/fcm/send");
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.TryAddWithoutValidation("Authorization", "key=" + api);
var r = await client.PostAsync(url, new StringContent(jGcmData.ToString(), Encoding.Default, "application/json"));
}
}
}
}
Task in general represents an asynchronous operation, so code inside a task block won't block unless you wanted to read its value by explicitly waiting, check the following for example:
Task myTask = new Task( () => Console.WriteLine("It is me myTask ^_^ "));
myTask.Start();
Console.WriteLine("Currently not waiting the output from myTask");
myTask.Wait();//Now I am waiting
//Output:
//Currently not waiting the output from myTask
//It is me myTask ^_^
Also you can create and start a task in one statement using Task.Run & TaskFactory.StartNew.
For more information about the differences in usage between them check the link.

Second call to HttpClient's PostAsJsonAsync stucks

I'm trying to post some data to a web service and receive response with HttpClient in a console application. If I try to reuse a HttpClient instance, the first call to PostAsJsonAsync works as expected, but the second just waits forever. If i create a new HttpClient for every call then everything is OK.
public class DirectHandler
{
HttpClient httpClient;
public string SendToPlayer(object message)
{
if (httpClient == null)
{
httpClient = new HttpClient();
httpClient.BaseAddress = new Uri("..url...");
}
HttpResponseMessage response = httpClient.PostAsJsonAsync("", message).Result; // Blocking call!
if (response.IsSuccessStatusCode)
{
var result = response.Content.ReadAsByteArrayAsync().Result;
return System.Text.Encoding.Default.GetString(result);
}
else
{
throw new HttpRequestException("Error code " + response.StatusCode + ", reason: " + response.ReasonPhrase);
}
}
}
The code using this class:
DirectHandler dh = new DirectHandler();
var resp = dh.SendToPlayer(myObj);
Console.WriteLine("Received: " + resp);
Thread.Sleep(500);
resp = dh.SendToPlayer(myObj);
Console.WriteLine("Received: " + resp);
The "server" is a HttpListener:
public class HTTPServer
{
void StartListener()
{
HttpListener listener = new HttpListener();
listener.Prefixes.Add("my prefix");
listener.Start();
listener.BeginGetContext(new AsyncCallback(OnRequestReceive), listener);
}
private void OnRequestReceive(IAsyncResult result)
{
HttpListener listener = (HttpListener)result.AsyncState;
HttpListenerContext context = listener.EndGetContext(result);
HttpListenerResponse response = context.Response;
HttpListenerRequest request = context.Request;
using (var reader = new StreamReader(request.InputStream, request.ContentEncoding))
{
Console.WriteLine(reader.ReadToEnd());
}
string responseString = "{'a': \"b\"}";
byte[] buffer = Encoding.UTF8.GetBytes(responseString);
response.ContentLength64 = buffer.Length;
response.OutputStream.Write(buffer, 0, buffer.Length);
listener.BeginGetContext(new AsyncCallback(OnRequestReceive), listener);
}
}
How can i reuse HttpClient?
Update:
I changed .Result to async/await, now the SendToPlayer method looks like this:
public async Task<string> SendToPlayer(object message)
{
if (httpClient == null)
{
httpClient = new HttpClient();
httpClient.BaseAddress = new Uri("..url...");
}
HttpResponseMessage response = await httpClient.PostAsJsonAsync("", message).ConfigureAwait(false);
if (response.IsSuccessStatusCode)
{
var result = await response.Content.ReadAsByteArrayAsync();
return System.Text.Encoding.Default.GetString(result);
}
else
{
throw new HttpRequestException("Error code " + response.StatusCode + ", reason: " + response.ReasonPhrase);
}
}
It still waits forever in PostAsJsonAsync when called more than once.
Update2:
The test code, hangs on the second SendToPlayer line:
public static void Main(string[] args)
{
Task.Run(async () =>
{
DirectHandler dh = new DirectHandler();
var resp = await dh.SendToPlayer(myObj);
Console.WriteLine("Received: " + str);
Thread.Sleep(500);
resp = await dh.SendToPlayer(myObj);
Console.WriteLine("Received: " + str);
}).Wait();
}
I can't help but notice that the code you provided doesn't even compile. Like where does myObj in your main method come from? And how do you even start the http listener since the method StartListener is declared private?
These kind of issues make me suspect that the code you've posted is not the actual code that has the described problem.
Also, what do you put into myObj?
I copied and adjusted your code to make it work on my machine. Please take a look to see if it runs on your machine as well in a new console app (you'll need administrator permissions to run it)
public static void Main(string[] args)
{
var s = new HTTPServer();
s.StartListener();
Task.Run(async () =>
{
var myObj = 8;
DirectHandler dh = new DirectHandler();
var resp = await dh.SendToPlayer(myObj);
Console.WriteLine("Received: " + resp);
Thread.Sleep(500);
resp = await dh.SendToPlayer(myObj);
Console.WriteLine("Received: " + resp);
}).Wait();
}
public class HTTPServer
{
public void StartListener()
{
HttpListener listener = new HttpListener();
listener.Prefixes.Add("http://*:8080/");
listener.Start();
listener.BeginGetContext(new AsyncCallback(OnRequestReceive), listener);
}
private void OnRequestReceive(IAsyncResult result)
{
HttpListener listener = (HttpListener)result.AsyncState;
HttpListenerContext context = listener.EndGetContext(result);
HttpListenerResponse response = context.Response;
HttpListenerRequest request = context.Request;
using (var reader = new StreamReader(request.InputStream, request.ContentEncoding))
{
Console.WriteLine(reader.ReadToEnd());
}
string responseString = "{'a': \"b\"}";
byte[] buffer = Encoding.UTF8.GetBytes(responseString);
response.ContentLength64 = buffer.Length;
response.OutputStream.Write(buffer, 0, buffer.Length);
listener.BeginGetContext(new AsyncCallback(OnRequestReceive), listener);
}
}
public class DirectHandler
{
HttpClient httpClient;
public async Task<string> SendToPlayer(object message)
{
if (httpClient == null)
{
httpClient = new HttpClient();
httpClient.BaseAddress = new Uri("http://localhost:8080");
}
HttpResponseMessage response = await httpClient.PostAsJsonAsync("", message).ConfigureAwait(false);
if (response.IsSuccessStatusCode)
{
var result = await response.Content.ReadAsByteArrayAsync();
return System.Text.Encoding.Default.GetString(result);
}
else
{
throw new HttpRequestException("Error code " + response.StatusCode + ", reason: " + response.ReasonPhrase);
}
}
}
this outputs
8
Received: {'a': "b"}
8
Received: {'a': "b"}
Now, you might have simplified your code posted here. If there is any code you've not posted here it will be hard to solve.
Is your HttpListener running in a separate (console) app? Maybe the server process is terminated after the first call, due to an error for example.
If the above code works in a new console app try to find the differences with your code.
As I see it, there are two issues:
1) your server needs to wait around to process more than only one event, and
2) your client and server code should reside in separate execution threads.
Based on the way you've written it, the server will shut down shortly after starting up, which is why maybe the first request works but the second one hangs.
I've created an example:
https://github.com/paulsbruce/StackOverflowExamples/tree/master/SOF40934218
You have to kick the server project off first, which waits for incoming requests (StartListening doesn't hold the server thread open, so there is a while(IsRunning) { Thread.Sleep(100); } loop as an example.
https://github.com/paulsbruce/StackOverflowExamples/blob/master/SOF40934218/SOF40934218_Server/Server.cs
Then kick off the client as a separate process and watch it's console output. It does in fact get a second response (why there is a 5 second delay to allow you to see this).
https://github.com/paulsbruce/StackOverflowExamples/blob/master/SOF40934218/SOF40934218_Client/Client.cs
Hope these two suggestions help!

How to use HttpClient.PostAsync asynchronous when the URL responds after an artificial timeout?

How can HttpClient.PostAsync be used to post HTTP requests to URLs which have an artificial time for sending back a response.
Please note the URL and the parameter sleep=30 which is used to introduce an artificial delay of 30 seconds before sending back an HTTP response.
Console.WriteLine("Start time : " + DateTime.Now);
for (int i = 1; i <= 10; i++)
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri(#"http://fake-response.appspot.com/api/?data={Hello World}&sleep=30");
//client.BaseAddress = new Uri(#"http://fake-response.appspot.com/api/?data={Hello World}&status=200");
client.Timeout = new TimeSpan(0, 0, 60);
var parameters = new Dictionary<string, string>();
parameters["ContentType"] = "text/plain;charset=UTF-8";
//Create Task to POST to the URL
client.DefaultRequestHeaders.ExpectContinue = true;
var response = await client.PostAsync(client.BaseAddress, new FormUrlEncodedContent(parameters));
Task<bool> b1 = ProcessURLAsync(response, 1, 5, 2);
}
}
Console.WriteLine("End time : " + DateTime.Now);
What needs to be done is that async HTTP Posts need to be made in a loop and should not be dependent on the timeout specified in the URL.
However, the PostAsync times out before a response is received.
Please check the time needed for POSTing the 2 different URLs in a loop of 10 async POSTs
I checked HttpClient.DefaultRequestHeaders.ExpectContinue , but I do not think this might help in this use case.
That artificial delay is no different from network timeout, from client's perspective. So you should set client.Timeout to the maximum expected artificial delay + real network timeout time. If you don't want to block waiting for response - just not await Task returned from PostAsync. You can store all such tasks in some list and wait them all to complete with await Task.WhenAll(yourTaskList). Or you can use ContinueWith to perform specific actions when given task will be completed. However, if you care about response at all - you have to set large enough timeout anyway, otherwise request will be aborted prematurely.
Here is some sample code to help you out
static async void MakeRequests()
{
var requests = new List<Task<bool>>();
for (int i = 1; i <= 10; i++)
{
// no await here, so, not await MakeRequest(i);
requests.Add(MakeRequest(i));
}
// now all 10 requests are running in parallel
try {
await Task.WhenAll(requests);
}
catch {
// no need to handle it here - we handle all errors below
}
// if we are here, all requests are either completed or failed, inspect their results
foreach (var request in requests) {
if (request.IsCanceled) {
// failed by timeout
}
else if (request.IsFaulted) {
// failed
Log(request.Exception);
}
else {
// success
bool result = request.Result;
// handle your result here if needed
}
}
}
static async Task<bool> MakeRequest(int i) {
using (var client = new HttpClient()) {
client.BaseAddress = new Uri(#"http://fake-response.appspot.com/api/?data={Hello World}&sleep=30");
//client.BaseAddress = new Uri(#"http://fake-response.appspot.com/api/?data={Hello World}&status=200");
// no timeout here, or set to max expected delay
//client.Timeout = new TimeSpan(0, 0, 60);
var parameters = new Dictionary<string, string>();
parameters["ContentType"] = "text/plain;charset=UTF-8";
//Create Task to POST to the URL
client.DefaultRequestHeaders.ExpectContinue = true;
var response = await client.PostAsync(client.BaseAddress, new FormUrlEncodedContent(parameters));
Task<bool> b1 = ProcessURLAsync(response, 1, 5, 2);
return b1;
}
}
However, the PostAsync times out before a response is received.
This method times out because you set HttpClient.Timeout property to 10 seconds. Setting this property instructs the client to time out after a specified time if response is not received.

Endless wait for async Web Response

i have the following problem, i try to wait for for an Async Web Response.
But it never finished.
public string getTermine(string trmId)
{
System.Threading.Tasks.Task<string> lisi = LoadTermine((HttpWebRequest)WebRequest.Create("http://" + curent.usrCH + apiKey + curent.phrase + apiTrmIDIS + trmId));//Request get String result like http://ajax.googleapis.com/ajax/services/search/web?v=1.0&start="+i+"&q=
lisi.Wait();
return lisi.Result;
}
private async System.Threading.Tasks.Taskstring>LoadTermine(HttpWebRequest myRequest)
{
//List<Termine> terminListe = new List<Termine>();
List<Appointment> Resu = null;
using (WebResponse response = await myRequest.GetResponseAsync())
{
using (System.IO.StreamReader reader = new System.IO.StreamReader(response.GetResponseStream()))
{
Resu = reader.ReadToEnd();
}
}
return Resu;
}
P.S. I cant use and synchronous request because this methods are an part of the Base code which is used by iOS, WinPhone and Android and i dont know why i cant get an synchronous WebResponse.
You are creating a deadlock by calling .Result on the task.
You could do something like this where the remoteUrl variabled is the url of your web service
private async System.Threading.Tasks.Task<string> LoadTermineAsync(HttpWebRequest myRequest)
{
using (var client = new HttpClient()) {
using (var request = new HttpRequestMessage(HttpMethod.Get, myRemoteUrl)) {
var response = await client.SendAsync(request).ConfigureAwait(false);
var result = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
return result;
}
}
}
For more info on Async/Await
And this evolve video is a little bit more advanced.

Categories

Resources