Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Timeouts in Xamarin HTTP requests

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) - works
  • Timeout = Timeout = TimeSpan.FromSeconds(1) - doesn't work (?????)
  • Timeout = TimeSpan.FromMilliseconds(2000) (and above) - doesn't work

Any ideas? Thanks!

like image 965
Apidcloud Avatar asked May 23 '16 03:05

Apidcloud


1 Answers

I found that that changing the http client settings for Android resolved my issue.
To solve the issue:

  • from HttpClient implementation to AndroidClientHandler
  • from SSL/TLS implementation to Native TLS 1.2+

In Xamarin Android project options. "Default" values were using Mono's own client which caused the problem.

I posted more details here.

like image 103
PJeremyMalouf Avatar answered Nov 12 '22 02:11

PJeremyMalouf