since i googled for the last 2 days and still can't figure it out:
I'm working with Unity and c#. I have a http client which sends async get requests to my local server. This server is set to always wait a certain amount of time until it sends a response. This is to simulate some behavior where the real server just doesn't answer.
For this example, let's say my server waits 20 seconds before sending a response. My client is set to timeout in 3 seconds.
Now i assumed that an error would be thrown after 3 seconds because of the timeout and the request is canceled but that's not the case. The error is thrown after the client receives a response, which is after 20 seconds.
The problem is that the "real" server doesn't answer every request. Maybe because i send requests to fast? Anyway, this requests are never answered and live forever.
My code for the client is as follow (C# in Unity):
private HttpClient _client = new HttpClient(); // create the http client
public void Start()
{
_client.BaseAddress = new System.Uri("http://127.0.0.1:8000"); // set base url
_client.Timeout = new System.TimeSpan(0, 0, 0, 0, 3000); // set the timeout to 3 seconds, could be 500 ms
GetRequest();
}
private async Task GetRequest()
{
try
{
var httpResponse = await _client.GetAsync("/somepath_doesnt_matter"); // send request and await response
string result = await task.Content.ReadAsStringAsync(); // read the string
Debug.Log("Done"); // all is good
} catch (Exception e)
{
Debug.Log(e.Message); // canceled <--- prints after 20 seconds, not 3
}
}
ps: using a CancellationTokenSource, setting it to timeout after 3 seconds and passing a token to the GetAsync function results in the same behavior.
Is there a way to "immediately" throw the error after the timeout and cancel the task?
Edit: here ist he full file: https://pastebin.com/WJQjmngt
using System;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using UnityEngine;
public class Example1 : MonoBehaviour
{
private HttpClient _client = new HttpClient();
void Start()
{
_client.BaseAddress = new System.Uri("http://127.0.0.1:8000");
_client.Timeout = new System.TimeSpan(0, 0, 0, 0, 3000);
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
GetRequest();
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
}
private async Task GetRequest()
{
try
{
CancellationTokenSource cts = new CancellationTokenSource();
cts.CancelAfter(3000);
var task = await _client.GetAsync("/somepath", cts.Token);
string result = await task.Content.ReadAsStringAsync();
Debug.Log("GOT:\n" + result);
}
catch (Exception e){
Debug.Log("EXCEPTION ============================");
Debug.Log(e.Message);
}
}
}
Edit2: Seems like Unity do things different for whatever reason. I wrote the code in VS Console Application and it canceled as expected. In Unity however it waits for the response to throw the error. I guess i go to unity forums to ask whats wrong. Here the code and the results if someone is interested: pastebin.com/Tk4KHNmh
Be aware that once you've used your httpclient, you are no longer allowed to change the timeout on it. I suspect you used it for something before setting the timeout value. I would also change how you're setting your timeout so it's readable.
try
{
_client.Timeout = TimeSpan.FromSeconds(3);
HttpResponseMessage result = await client.GetAsync(uri);
string response = await result.Content.ReadAsStringAsync();
}
catch{ /*timeout*/}
This code absolutely works ( i took it from my own project) so if you're having problems, you're using the context before setting the timeout. It gets ignored if that happens.
You can also force a timeout exception by trickery. Run your own timeout code. The sample below will manually throw a timeout exception when the time expires. This should work in Unity and it forces the call to quit.
await client.GetAsync()
.WithTimeout(TimeSpan.FromSeconds(3))
.ConfigureAwait(true);
and the event:
public static async Task<TResult> WithTimeout<TResult>(this
Task<TResult> task, TimeSpan timeout)
{
if (task == await Task.WhenAny(task, Task.Delay(timeout)))
{
return await task;
}
throw new TimeoutException();
}