LPQ
LPQ

Reputation: 738

HttpClient seems to be ignoring the Timeout property

I am trying to load a large amount of data from a server by returning MultiPartContent in the response. However, during the HttpGet method the timeout value I am setting is being ignored. I am trying to set a longer timeout as waiting in this situation is OK.

I have tried various Timeout values between 10 minutes to 2 hours. Realistically the user will only have to wait ~5 minutes the first time they use our app and all other times it will be less. I have also tried storing a reference of HttpClient to ensure that api.HttpClient isn't re-creating the HttpClient object each time.

var handler = new HttpClientHandler();
var progress = new ProgressMessageHandler(handler);
var api = APIHelpers.GetSession(progress);

progress.HttpReceiveProgress += (e, args) => ProgressChanged.Invoke(e, new SyncEventArgs(GlobalEnums.SyncStage.Downloading, args.ProgressPercentage * 0.01f));

api.HttpClient.Timeout = new TimeSpan(0, 10, 0);

var downloadUri = BuildURI();
var response = await api.HttpClient.GetAsync(downloadUri, HttpCompletionOption.ResponseHeadersRead);

I would expect the application to wait for 10 minutes before throwing a Timeout exception, however the exception is being thrown after 100 seconds (i believe that is the default value?). Checking the HttpClient.Timeout value after it is set does show that it was set correctly.

The APIHelpers.GetSession() method returns an object of our API with authorisation headers created. The HttpClient object is accessible via an inherited class. This is created by Swashbuckle and Swagger. I have used the api.HttpClient.Timeout = x this way before with success so I don't think this is the issue. It seems to be specific to this scenario in particular.

The exception thrown:

{System.Net.Http.HttpRequestException: An error occurred while sending the request ---> System.Net.WebException: The operation has timed out.
  at System.Net.HttpWebRequest.RunWithTimeoutWorker[T] (System.Threading.Tasks.Task`1[TResult] workerTask, System.Int32 timeout, System.Action abort, System.Func`1[TResult] aborted, System.Threading.CancellationTokenSource cts) [0x000f8] in <a1ab7fc4639d4d84af41d68234158b1c>:0 
  at System.Net.HttpWebRequest.EndGetResponse (System.IAsyncResult asyncResult) [0x00019] in /Library/Frameworks/Xamarin.iOS.framework/Versions/12.14.0.114/src/Xamarin.iOS/mcs/class/System/System.Net/HttpWebRequest.cs:1200 
  at System.Threading.Tasks.TaskFactory`1[TResult].FromAsyncCoreLogic (System.IAsyncResult iar, System.Func`2[T,TResult] endFunction, System.Action`1[T] endAction, System.Threading.Tasks.Task`1[TResult] promise, System.Boolean requiresSynchronization) [0x0000f] in <939d99b14d934342858948926287beba>:0 
--- End of stack trace from previous location where exception was thrown ---

  at System.Net.Http.MonoWebRequestHandler.SendAsync (System.Net.Http.HttpRequestMessage request, System.Threading.CancellationToken cancellationToken) [0x003d1] in /Library/Frameworks/Xamarin.iOS.framework/Versions/12.14.0.114/src/Xamarin.iOS/mcs/class/System.Net.Http/MonoWebRequestHandler.cs:499 
   --- End of inner exception stack trace ---
  at System.Net.Http.MonoWebRequestHandler.SendAsync (System.Net.Http.HttpRequestMessage request, System.Threading.CancellationToken cancellationToken) [0x0046a] in /Library/Frameworks/Xamarin.iOS.framework/Versions/12.14.0.114/src/Xamarin.iOS/mcs/class/System.Net.Http/MonoWebRequestHandler.cs:503 
  at Microsoft.Rest.RetryDelegatingHandler+<>c__DisplayClass11_0.<SendAsync>b__1 () [0x000ad] in <6a6c837cafbb4f1faffaba1ff30ca4e3>:0 
  at Microsoft.Rest.RetryDelegatingHandler.SendAsync (System.Net.Http.HttpRequestMessage request, System.Threading.CancellationToken cancellationToken) [0x00150] in <6a6c837cafbb4f1faffaba1ff30ca4e3>:0 
  at System.Net.Http.Handlers.ProgressMessageHandler.SendAsync (System.Net.Http.HttpRequestMessage request, System.Threading.CancellationToken cancellationToken) [0x00087] in <19af20e76ce04547b3fc150a0f8f2d47>:0 
  at System.Net.Http.HttpClient.SendAsyncWorker (System.Net.Http.HttpRequestMessage request, System.Net.Http.HttpCompletionOption completionOption, System.Threading.CancellationToken cancellationToken) [0x0009e] in /Library/Frameworks/Xamarin.iOS.framework/Versions/12.14.0.114/src/Xamarin.iOS/mcs/class/System.Net.Http/System.Net.Http/HttpClient.cs:281 
  at ...

EDIT: I'm just adding some additional things I have tried in an attempt to solve this.

  1. I have tried using the DependencyService in Xamarin.Forms to get native HttpClientHandlers and HttpClient objects for each platform where I can set the timeouts per platform instead of relying on Xamarin.Forms to translate it for me. This had the exact same result.
public HttpMessageHandler GetHttpHandler(double timeoutSeconds)
{
    var sessionConfig = NSUrlSessionConfiguration.DefaultSessionConfiguration;
    sessionConfig.TimeoutIntervalForRequest = timeoutSeconds;
    sessionConfig.TimeoutIntervalForResource = timeoutSeconds;
    sessionConfig.WaitsForConnectivity = true;

    var sessionHandler = new NSUrlSessionHandler(sessionConfig);
    return sessionHandler;
}
  1. I have tried creating a new HttpClient object just for this API call but with no luck here either. (This replaces Apihelpers.GetSession() in my code)

Upvotes: 5

Views: 3287

Answers (2)

Nilufar Makhmudova
Nilufar Makhmudova

Reputation: 211

Had the same issue in my Xamarin Android project. Resolved it by changing HttpClient implementation to Android in the Project properties as below: enter image description here

Upvotes: 0

jgoldberger - MSFT
jgoldberger - MSFT

Reputation: 6098

There was a known issue in both Xamarin.iOS and Xamarin.Android that HttpClient.Timeout values greater than 100 seconds are ignored. This is because the underlying native http clients have a timeout set to 100 seconds, so this times out before the .NET HttpClient times out when the HttpClient.Timeout value > 100 seconds. This should be fixed in all of the latest stable versions, so make sure you are updated. If you are using Visual Studio 2017, you won't have the fix, you will need to get VS 2019 for the fixes.

Upvotes: 1

Related Questions