HttpClient with infinite timeout excludes timeout

My HttpClient uses digest authentication to connect to the server and waits for search request responses. These searches can appear at any time, so the client is expected to always open a connection.

The connection is made using the following code:

public static async void ListenForSearchQueries(int resourceId)
{
    var url = $"xxx/yyy/{resourceId}/waitForSearchRequest?token=abc";

    var httpHandler = new HttpClientHandler { PreAuthenticate = true };

    using (var digestAuthMessageHandler = new DigestAuthMessageHandler(httpHandler, "user", "password"))
    using (var client = new HttpClient(digestAuthMessageHandler))
    {
        client.Timeout = TimeSpan.FromMilliseconds(Timeout.Infinite);

        var request = new HttpRequestMessage(HttpMethod.Get, url);

        var tokenSource = new CancellationTokenSource();
            tokenSource.CancelAfter(TimeSpan.FromMilliseconds(Timeout.Infinite));

        using (var response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, tokenSource.Token))
        {
            Console.WriteLine("\nResponse code: " + response.StatusCode);

            using (var body = await response.Content.ReadAsStreamAsync())
            using (var reader = new StreamReader(body))
                while (!reader.EndOfStream)
                    Console.WriteLine(reader.ReadLine());
         }
    }
}

      

This is the method used in the main method of the console application.

private static void Main(string[] args)
{
   const int serviceId = 128;
   .
   .
   .
   ListenForSearchQueries(resourceId);
   Console.ReadKey();
}

      

This is what the output looks like in the console window:

Response code: OK
--searchRequestBoundary

      

Even though the client timeout is set to infinity, the connection times out approximately five minutes (which is not the default HttpClient timeout) after the first output, causing the next exception.

System.IO.IOException occurred
  HResult=0x80131620
  Message=The read operation failed, see inner exception.
  Source=System.Net.Http
  StackTrace:
   at System.Net.Http.HttpClientHandler.WebExceptionWrapperStream.Read(Byte[] buffer, Int32 offset, Int32 count)
   at System.Net.Http.DelegatingStream.Read(Byte[] buffer, Int32 offset, Int32 count)
   at System.IO.StreamReader.ReadBuffer()
   at System.IO.StreamReader.get_EndOfStream()
   at ConsoleTester.Program.<ListenSearchQueriesDigestAuthMessageHandler>d__10.MoveNext() in C:\Users\xyz\ProjName\ConsoleTester\Program.cs:line 270

Inner Exception 1:
WebException: The operation has timed out.

      

The delegateHandler used for authentication is a crude adaptation of this code (see source section).

Why customer wait times and how can I prevent it?

My ultimate goal is to call and wait indefinitely for an answer. When a response comes in, I don't want the connection to close because there may be more responses in the future. Unfortunately I can't change anything on the server.

+3


source to share


2 answers


Although the default for Stream.CanTimeout

is incorrect , returning a stream thru response.Content.ReadAsStreamAsync()

yields a stream in which the CanTimeout property is returned true.

The default read and write time for this stream is 5 minutes . That is, after five minutes of inactivity, the thread will throw an exception. Very similar to the exception mentioned in the question.

To change this behavior, you can adjust the ReadTimeout and / or the WriteTimeout property of the stream.



Below is a modified version of the method ListenForSearchQueries

that changes the ReadTimeout value to Infinite.

public static async void ListenForSearchQueries(int resourceId)
{
    var url = $"xxx/yyy/{resourceId}/waitForSearchRequest?token=abc";

    var httpHandler = new HttpClientHandler { PreAuthenticate = true };

    using (var digestAuthMessageHandler = new DigestAuthMessageHandler(httpHandler, "user", "password"))
    using (var client = new HttpClient(digestAuthMessageHandler))
    {
        client.Timeout = TimeSpan.FromMilliseconds(Timeout.Infinite);

        var request = new HttpRequestMessage(HttpMethod.Get, url);

        var tokenSource = new CancellationTokenSource();
            tokenSource.CancelAfter(TimeSpan.FromMilliseconds(Timeout.Infinite));

        using (var response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, tokenSource.Token))
        {
            Console.WriteLine("\nResponse code: " + response.StatusCode);

            using (var body = await response.Content.ReadAsStreamAsync())
            {
                body.ReadTimeout = Timeout.Infinite;

                using (var reader = new StreamReader(body))
                    while (!reader.EndOfStream)
                        Console.WriteLine(reader.ReadLine());
            }
         }
    }
}

      

This was catching an exception that was actually thrown by the thread, but it looks like it was thrown by the HttpClient.

+1


source


Make a return a method Task

public static async Task ListenForSearchQueries(int resourceId) {
    //...code removed for brevity
}

      



Update the main console method to Wait

c Task

to complete.

public static void Main(string[] args) {
   const int serviceId = 128;
   .
   .
   .
   ListenForSearchQueries(resourceId).Wait();
   Console.ReadKey();
}

      

+2


source







All Articles