I've written a very basic game in .NET 7 that relies on server-sent events. I've noticed that my event content is occasionally malformed. From what I can tell this happens during small periods of time in which there are several updates in quick succession.
Below is an example - notice the Data for the highlighted record is malformed. In another instance that I did not screenshot, the data was fine, but the event type was lobbyevent:lobbyEvent instead of simply lobbyEvent.
I have to imagine that, somehow, the ResponseBody is not being properly flushed, and/or being "cross-contaminated" due to concurrent requests. The logging in the following code tells me that it's all working as expected up to the point where it sends response data. Furthermore, the problem seems worse out in the wild, as opposed to my localhost where it's much more difficult to replicate - this would seem to align with the idea that slower response times are causing concurrency issues.
My code is below, which was built following the patterns outlined here. Can anyone help me out? Am I correct in my assumption, and what can I do to remedy the issue?
[Produces("text/event-stream")]
[HttpGet("{code}/listen")]
public async Task ListenForNotifications(string code, CancellationToken cancellationToken)
{
_logger.LogCritical($"CONNECTED");
var lobby = await _dbContext.Lobbies.FirstOrDefaultAsync(l => l.JoinCode == code);
if (lobby == null) return;
SetServerSentEventHeaders();
await Response.WriteAsync($"event:connected\n", cancellationToken);
await Response.Body.FlushAsync(cancellationToken);
async void OnNotification(object? sender, LobbyEventArgs eventArgs)
{
try
{
if (eventArgs.LobbyEvent.LobbyId != lobby.Id) return;
var json = JsonSerializer.Serialize(eventArgs.LobbyEvent, _jsonSerializerOptions);
await Response.WriteAsync("retry:10000\n", cancellationToken);
await Response.WriteAsync($"event:lobbyEvent\n", cancellationToken);
await Response.WriteAsync($"data:{json}\n\n", cancellationToken);
await Response.Body.FlushAsync(cancellationToken);
_logger.LogCritical($"NOTIFICATION SENT: {eventArgs.LobbyEvent.Type}");
} catch (Exception ex)
{
_logger.LogCritical($"NOTIFICATION FAILED: {ex.Message}");
}
}
async void KeepAlive(object? sender, EventArgs eventArgs)
{
await Response.WriteAsync("event:keep-alive\n", cancellationToken);
await Response.WriteAsync("data:keep-alive\n\n", cancellationToken);
await Response.Body.FlushAsync(cancellationToken);
}
_eventService.LobbyEvent += OnNotification;
_eventService.KeepAlive += KeepAlive;
try
{
while (!cancellationToken.IsCancellationRequested)
await Task.Delay(1000, cancellationToken);
}
finally
{
_logger.LogCritical($"DISCONNECTED");
_eventService.LobbyEvent -= OnNotification;
_eventService.KeepAlive -= KeepAlive;
}
}
private void SetServerSentEventHeaders()
{
Response.StatusCode = 200;
Response.Headers.Add("Content-Type", "text/event-stream");
Response.Headers.Add("Cache-Control", "no-cache");
Response.Headers.Add("Connection", "keep-alive");
}
Related
I am trying to call a async method of ViewModel from another method in View but it is behaving as syncronous.
View Model:
public async Task<bool> GetReadyForUnlockWithQR()
{
try
{
SmilaHash = GetRandomeHashKey();
var data = JsonConvert.SerializeObject(GetSmilaInfo());
var res = await apiClient.PostAsync<String>("readyforunlockwithqr", data);
if (res != null)
{
JObject json = JObject.Parse(res);
if (json["doUnlock"] != null)
{
LoginStatus = json.SelectToken("doUnlock").Value<bool>();
}
}
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
CancelPendingRequests();
throw ex;
}
return false;
}
I have my api methods defined in a custome APIClient file. The above request may take a minute to complete. I don't want to stop the UI and perform my further operations in View. Following is my View:
private async void UnlockButton_Click(object sender, RoutedEventArgs e)
{
try
{
await ViewModel.GetReadyForUnlockWithQR();
DisplayQRCode();
}
catch(Exception ex)
{
if (ex is HttpRequestException)
{
Debug.WriteLine("Server not reachable");
MessageBox.Show("Server not reachable");
}
else if (ex is OperationCanceledException)
{
Debug.WriteLine("Timeout exception");
QRCodeImage.Source = null;
QRCodeCanvas.Visibility = Visibility.Hidden;
}
else
{
Debug.WriteLine(ex.Message);
}
}
}
I above code ideally the DisplayQRCode() function should work immediately after await ViewModel.GetReadyForUnlockWithQR(); but it is not happening. The DisplayQRCode() is waiting to receive response from ViewModel.GetReadyForUnlockWithQR() Why is this not behaving as logical asyn code.
The DisplayQRCode() is waiting to receive response from ViewModel.GetReadyForUnlockWithQR() Why is this not behaving as logical asyn code.
The asynchronous method is behaving serially (not "synchronously"), which is exactly what await is supposed to do.
You can think of await as "asynchronous wait": the method is paused and will not continue pass the await until the task completes, but it waits asynchronously, so the thread is freed (the method returns to its caller).
I above code ideally the DisplayQRCode() function should work immediately after await ViewModel.GetReadyForUnlockWithQR(); but it is not happening.
If you want to do that, then you can call GetReadyForUnlockWithQR but don't await the task until after DisplayQRCode completes:
var getReadyTask = ViewModel.GetReadyForUnlockWithQR();
DisplayQRCode();
await getReadyTask;
I'm trying to log a response to a database inside of a delegating handler. Since I want to return the response without waiting for the record to be saved to the database, I want to do that part with an async task.
My problem is, if anything goes wrong within that task, the error cannot be caught and the API crashes with the error
System.Exception: 'Exception of type 'System.Exception' was thrown.'
I've recreated a minimal case below:
public class LogDelegatingHandler : DelegatingHandler
{
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
try
{
//send request and get response
var response = await base.SendAsync(request, cancellationToken);
//return response while this runs
_ = Task.Run(() => Log());
//Task.Run(() => Log()); //also crashes
//await Task.Run(() => Log()); //also crashes
return response;
}
catch (Exception e)
{
//this is never reached
}
}
private async void Log()
{
try
{
//this crashes
throw new Exception("test");
}
catch (System.AggregateException e)
{
//this is never reached
}
}
}
After reading SO and finding a thread where the user did care about exceptions thrown, I wanted to ask this where in my case I don't care, but I do want the response to come through first. After reading this
How to safely call an async method in C# without await
I added the _ to dispose of the task and not await it.
That's because you are throwing an Exception but attempt to catch AggregateException.
Try this:
try
{
//this crashes
throw new Exception("test");
}
catch (System.AggregateException e)
{
// handle in case of aggregate exception
}
catch (Exception ex)
{
// in case of other exceptions
}
The catch in SendAsync() is never reached because Log() is async void instead async Task.
You should only ever use async void for fire-and-forget stuff like event. If you want anything back from it, even exceptions, you need to use a Task
I am running crazy with using the HttpClient in C#...
I simplified my project so my problem can be replicated easier. All i want to do is calling HttpClient.PostAsync in the Background without blocking my UI Window (I am using WPF btw).
Here is my code (slimed the code to the min.):
Bing is only used here to not show my private webservice it can be replaced with every other website of course.
private async void Window_Loaded(object sender, RoutedEventArgs e)
{
try {
MyTextBlock.Text = "Waiting...";
Uri webUri = new Uri("https://www.bing.com/");
using (HttpClient client = new HttpClient()) {
using (HttpResponseMessage response = await client.PostAsync(webUri, new MultipartFormDataContent())) {
MyTextBlock.Text = await response.Content.ReadAsStringAsync();
}
}
} catch (Exception exc) {
MessageBox.Show(exc.ToString(), "Unhandled Exception");
}
}
While my UI is waiting for the async post request it shows "Waiting" in a TextBox. And when the Async Request returns it shows the result. Nothing more should happen.
So here the Problem occurs, sometimes the PostAsync Method simply doesn't return... Even the Timeout is ignored. When I am debugging it always works but when I try the start the application it somettimes hangs. Not always which is not making find the error easier. I tried many ways with calling the request async but every time the same issue.
I also read following blog with the blocking issue in async methods but even with ConfigureAwait no differnce.
http://blog.stephencleary.com/2012/07/dont-block-on-async-code.html
I just can imagine that there is a problem within the HttpClient async method locking the main thread, so it cause this problem. Wenn i use the same code in a ConsoleApplication everything is fine. There is a proxy between my client and the destination but that shouldn't be a problem at all.
Can someone replicate this problem? I am using C#/WPF with .NET Framework 4.6.1.
You don't need to await client.PostAsync(webUri, i_formData) because you don't do anything with the result after the call returns, you can just return the Task. Change to this;
public static Task<HttpResponseMessage> BasicRequest(MultipartFormDataContent i_formData)
{
Uri webUri = new Uri("https://www.bing.com");
HttpClient client = new HttpClient {
Timeout = TimeSpan.FromSeconds(1)
};
return client.PostAsync(webUri, i_formData);
}
Your Window_Load is an event handler. You can make it async void, which is the only time you don't have to return Task. By making it async, you can remove all the over complicated code:
private async void Window_Loaded(object sender, RoutedEventArgs e)
{
MyTextBlock.Text = "Waiting";
HttpResponseMessage response = await BasicRequest(new
MultipartFormDataContent());
string test = await response.Content.ReadAsStringAsync();
this.Close();
}
First at all thanks for your help, the problem seems to be solved now.
I had to do 3 things to get this work:
Get a instance of HttpClient of use it the whole application life time, so no using anymore for HttpClient.
Don't call PostAsync in the Window_Loaded Event, it seems to be to early sometimes. (I still don't get why...)
Don't use ConfigureAwait(false)
The code now looks something like this:
HttpClient client = new HttpClient();
private async void MyButton_Click(object sender, RoutedEventArgs e)
{
try {
MyTextBlock.Text = "Waiting...";
Uri webUri = new Uri("https://www.bing.com/");
using (HttpResponseMessage response = await client.PostAsync(webUri, new ipartFormDataContent())) {
MyTextBlock.Text = await response.Content.ReadAsStringAsync();
}
} catch (Exception exc) {
MessageBox.Show(exc.ToString(), "Unhandled Exception");
}
}
And to get this at start up done i had to make a really bad piece of good. But it works finally:
private void Window_Loaded(object sender, RoutedEventArgs e)
{
DispatcherTimer startupTimer = new DispatcherTimer();
startupTimer.Tick += new EventHandler((o, a) => {
MyFunction();
startupTimer.Stop();
});
startupTimer.Interval = TimeSpan.FromSeconds(1);
startupTimer.Start();
}
When someone can replicate these behavior or can explain why these was happening, please comment it here :)
Update
Issue still occurs but it seems to be only there when the client is using some kind of proxy!
For few hours I am struggling with async code in C# and I can't really get why is my code deadlocked.
So far I've red many articles and anything ringed the bell for me.
Hope you can help me.
Here is the code I am trying to run.
Main
Task.Run(async () =>
{
Task<EventDetailed[]> events = getDetailedEvents();
await events;
}).Wait();
getDetailedEvents:
static async Task<EventDetailed[]> getDetailedEvents()
{
...
EventDetailed[] result = await LoadDetailedEventsDetailsAsync(evnts).ConfigureAwait(false);
return result;
}
And the core of my problem.
LoadDetailedEventsDetailsAsync
async static Task<EventDetailed[]> LoadDetailedEventsDetailsAsync(Event[] events)
{
List<EventDetailed> detailed = new List<EventDetailed>();
List<Task<WebResponse>> responses = new List<Task<WebResponse>>();
List<Event> tasksWithStream = new List<Event>();
foreach (Event e in events)
{
var httpWebRequest = (HttpWebRequest)WebRequest.Create("http://...");
... some headers etc ...
e.Stream = httpWebRequest.GetRequestStreamAsync();
e.WebRequest = httpWebRequest;
tasksWithStream.Add(e);
}
foreach (var tsk in tasksWithStream)
{
try {
await tsk.Stream.ConfigureAwait(false);
using (var streamWriter = new StreamWriter(tsk.Stream.Result))
{
streamWriter.Write("...");
streamWriter.Flush();
streamWriter.Close();
}
responses.Add(tsk.WebRequest.GetResponseAsync());
}
catch (Exception ex)
{
Logger.mes("Failed to get event data.");
}
}
foreach (var response in responses)
{
try
{
await response.ConfigureAwait(false);
}
catch (Exception ex)
{
Logger.mes("Failed to get event data.");
continue;
}
parseData.Add(ParseData(response));
}
A couple points:
First, it's important to note that you should almost never call .Wait (or .Result) on an async task - you should use await instead. One of the very few exceptions is in the Main method of a console app. The reason is that if you don't block the main thread, your program will simply exit prematurely.
Second, if you need to make multiple HTTP requests that do not depend on each other (i.e. request B does not need the results of request A), then there are huge performance gains to be had by executing them in parallel. Better still, you are not consuming a thread per request because the calls are asynchronous, i.e. they don't block a thread while waiting for a response, so the same thread can effectively fire off many concurrent requests.
I won't re-write your code, but I will suggest how I'd restructure it:
static void Main(string[] args)
{
// start all async tasks in parallel.
var tasks = GetEvents().Select(GetEventDetailsAsync);
// wait for them all to complete. normally you should use await instead of Wait,
// but you can't because you're in the main method of a console app.
Task.WhenAll(task).Wait();
}
static IEnumerable<Event> GetEvents()
{
// build a list of whatever metadata is needed to do your async work.
// do NOT do any actual async work here.
}
async static Task<EventDetailed> GetEventDetailsAsync(Event e)
{
// do all async work here, use await as needed,
// but only for one event (no loops).
}
I have a "rest client" that wraps HttpClient and whose methods are async.
Besides other reasons, I need to control signin/signout process with my rest client so that number of sessions is not exceeded.
The rest client implements IDisposable and upon disposing the client I need to check if the client is "still signed in" and sign out if it is.
Since doing any kind of external calls in Dispose method is considered bad practice, I have something as following
public class MappingsController : RestController
{
[HttpGet]
public async Task<HttpResponseMessage> GetYears()
{
return await ProcessRestCall(async rc => await rc.GetYearsAsync());
}
}
public class RestController : ApiController
{
protected async Task<HttpResponseMessage> ProcessRestCall<T>(Func<RestClient, Task<T>> restClientCallback)
{
RestClient restClient = null;
try
{
var credentials = GetCredentialsFromRequestHeader();
if (credentials == null)
{
return Request.CreateErrorResponse(HttpStatusCode.Unauthorized, "Missing credentials from header!");
}
var username = credentials["Username"];
var password = credentials["Password"];
restClient = new RestClient(username, password);
var authenticated = await restClient.SignInAsync();
if (!authenticated)
{
return CreateErrorResponseWithRestStatus(HttpStatusCode.Unauthorized, restClient);
}
var result = await restClientCallback(restClient);
// Following works, but since I need to do it in finally block in case exception happens, perhaps It should be done in finally anyways...
//await restClient.SignOutAsync();
var response = Request.CreateResponse(HttpStatusCode.OK, result);
return response;
}
catch (Exception e)
{
return CreateErrorResponseWithRestStatus(HttpStatusCode.BadRequest, restClient, e);
}
finally
{
if (restClient != null)
{
if (restClient.IsSignedIn)
{
//var signedOutOk = restClient.SignOutAsync();//.Result; //<-- problem - this blocks!!!
restClient.SignOutAsync().ConfigureAwait(false); // seems to work, but I am not sure if this is kosher + I can't get return var
//Logger.Warn(CultureInfo.InvariantCulture, m => m("Client was still signed in! Attempt to to sign out was {0}", signedOutOk ? "successful" : "unsuccessful"));
}
restClient.Dispose();
}
}
}
}
The use of .ConfigureAwait(false) is a non-issue. You aren't awaiting on the task at all. Since you don't await it, it doesn't matter what await is configured to do.
What you're doing is just basic fire and forget (which may or may not be acceptable for you).
You should remove the ConfigureAwait(false) no matter what, just because it does nothing and is confusing to the reader. If it's okay for you to send the request to sign out but not actually sign out, then this is okay.
If you need to ensure that restClient.Dispose(); isn't called until the sign out request returns, then you have a bit of a...problem. The problem stems from the fact that the sign out request might be unsuccessful, or much worse, it might not respond at all. You'd need some way of dealing with that.
You can't use await in a finally block, but you can more or less mimic its behavior through continuations. You may need to do something like this:
public static async Task DoStuff()
{
IDisposable disposable = null;
try { }
finally
{
var task = GenerateTask();
var continuation = Task.WhenAny(task, Task.Delay(5000))
.ContinueWith(t =>
{
if (task.IsCompleted) //if false we timed out or it threw an exception
{
var result = task.Result;
//TODO use result
}
disposable.Dispose();
});
}
}
Note that since you aren't using await the task returned from DoStuff will indicate that it is "done" as soon as it hits the finally block for the first time; not when the continuation fires and the object is disposed. That may or may not be acceptable.