Reputation: 444
I'm trying to setup a timeout for each separate HttpClient.Send call in .net8.0.
So I used CancellationTokenSource
but It seems that it starts the timer as soon as I create it instead of starting the timer when calling HttpClient
Send method.
Here is my unit tests result:
[TestMethod]
[DataRow(1)]
[DataRow(10)]
[DataRow(100)]
[DataRow(1000)]
[DataRow(10000)]
[DataRow(100000)]
[DataRow(1000000)]
public void HttpClient_Send_CancellationTokenSource(int timeout)
{
var httpClient = new HttpClient();
var cts = new CancellationTokenSource(TimeSpan.FromMilliseconds(timeout));
var request = new HttpRequestMessage(HttpMethod.Get, "https://google.com");
Thread.Sleep(2 * timeout);
var response = httpClient.Send(request, cancellationToken: cts.Token);
}
So all unit tests failed with this exception: System.Threading.Tasks.TaskCanceledException: The operation was canceled. ---> System.OperationCanceledException: The operation was canceled.
Upvotes: -1
Views: 799
Reputation: 143393
That is how CancellationTokenSource
with delay works. From the remarks in the constructor docs:
The countdown for the delay starts during the call to the constructor. When the delay expires, the constructed
CancellationTokenSource
is canceled, if it has not been canceled already.
So you have basically two options - starting the countdown a little bit before the "sending", by creating the source right before the call to Send
:
Thread.Sleep(2 * timeout);
// move here
var cts = new CancellationTokenSource(TimeSpan.FromMilliseconds(timeout));
var response = httpClient.Send(request, cancellationToken: cts.Token);
or a bit "after" the "sending" has started if you switch to async flow + CancelAfter
:
var cts = new CancellationTokenSource();
var task = httpClient.SendAsync(request, cancellationToken: cts.Token);
cts.CancelAfter(timeout);
var response = await task;
P.S.
Note that this API highly likely is affected by the same resolution "problems" as Task.Delay
, to quote from it's docs:
This method depends on the system clock. This means that the time delay will approximately equal the resolution of the system clock if the
millisecondsDelay
argument is less than the resolution of the system clock, which is approximately 15 milliseconds on Windows systems.
Hence your test would potentially fail for small values of timeout
even if everything would work as you have expected.
Also note that you can specify timeout when creating the HttpClient
(though in modern .NET using IHttpClientFactory
to create HttpClient
usually is the preferred approach, then just manually creating one):
var httpClient = new HttpClient()
{
Timeout = TimeSpan.FromMilliseconds(15)
};
Upvotes: 3