Good evening!
I was trying to clean/refine some code and ended up finding issues with timeouts in Xamarin HTTP requests (as described in my original thread: Async Download and Deserialize).
Issue found (only tested with Xamarin.Android; don't know about iOS):
When a host cannot be reached (e.g., an offline local server), GetAsync
throws a System.Net.WebException
after roughly 3 minutes with the message Error: ConnectFailure (Connection timed out). The Inner exception is System.Net.Sockets.SocketsException
(full log here: http://pastebin.com/MzHyp2FM).
Code:
internal static class WebUtilities
{
/// <summary>
/// Downloads the page of the given url
/// </summary>
/// <param name="url">url to download the page from</param>
/// <param name="cancellationToken">token to cancel the download</param>
/// <returns>the page content or null when impossible</returns>
internal static async Task<string> DownloadStringAsync(string url, CancellationToken cancellationToken)
{
try
{
// create Http Client and dispose of it even if exceptions are thrown (same as using finally statement)
using (var client = new HttpClient() { Timeout = TimeSpan.FromSeconds(5) })
{
// should I always do this?
client.CancelPendingRequests();
// Issue here; Timeout of roughly 3 minutes
using (var response = await client.GetAsync(url, cancellationToken).ConfigureAwait(false))
{
// if response was successful (otherwise return null)
if (response.IsSuccessStatusCode)
{
// return its content
return await response.Content.ReadAsStringAsync().ConfigureAwait(false);
}
}
}
}
// TODO: split exceptions?
catch (Exception ex) when (ex is System.Net.Sockets.SocketException ||
ex is InvalidOperationException ||
ex is OperationCanceledException ||
ex is System.Net.Http.HttpRequestException)
{
WriteLine("DownloadStringAsync task has been cancelled.");
WriteLine(ex.Message);
return null;
}
// return null if response was unsuccessful
return null;
}
}
Calling Method:
internal static async Task CallAsync(string url)
{
using (var cts = new CancellationTokenSource(TimeSpan.FromSeconds(10)))
{
var token = cts.Token;
token.ThrowIfCancellationRequested();
string result = await WebUtilities.DownloadStringAsync(url, token).ConfigureAwait(false);
}
}
Setting client.Timeout
doesn't seem to work.
Either way, shouldn't cancellationToken cancel automatically after 10 seconds?
This timeout issue happens when:
- An offline/unreachable IP address (in my case an offline local server
such as 192.168.1.101:8080) is requested (i.e., using
GetAsync
,SendAsync
,GetResponseAsync
)
The code works well when:
- The request is made from a desktop client (e.g., WPF). If the IP is offline/unreachable it throws 4 exceptions really fast (No connection could be made because the target machine actively refused it).
Conclusions
Xamarin seems to have some bugs in Http requests (with Timeouts at least?), as they do not give the expected results. And from what I have read, it could be something that has been around for few a years (since 2012 or 2013).
Xamarin unit testing doesn't really help either: https://github.com/xamarin/xamarin-android/blob/1b3a76c6874853049e89bbc113b22bc632ed5ca4/src/Mono.Android/Test/Xamarin.Android.Net/HttpClientIntegrationTests.cs
Edit
Timeout = TimeSpan.FromMilliseconds(1000)
- worksTimeout = Timeout = TimeSpan.FromSeconds(1)
- doesn't work (?????)Timeout = TimeSpan.FromMilliseconds(2000)
(and above) - doesn't work
Any ideas? Thanks!