HttpClientFactory TaskCanceledException: невозможно прочитать данные из транспортного соединения

Я создал простое консольное приложение, которое загружает один (PDF) файл с archive.org, используя новый ASP.NET Core 2.1 HttpClientFactory. Для конкретного URL, используемого в этой программе, я всегда получаю TaskCanceledException, Если вы попытаетесь запустить этот код, вы, вероятно, получите то же исключение. Это работает для других URL-адресов на archive.org, хотя. При загрузке файла с помощью wget с точно такого же URL

wget https://archive.org/download/1952-03_IF/1952-03_IF.pdf --output-document=IF.pdf

загрузка прошла успешно

Однако, когда я делаю это с HttpClient Я получаю следующее исключение.

Что я мог сделать неправильно?

Вот простой код:

using System;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using System.IO;
using System.Diagnostics;

namespace test2
{
    public class Program
    {
        public static async Task Main(string[] args)
        {
            var serviceCollection = new ServiceCollection();
            serviceCollection.AddHttpClient("archive", c =>
            {
                c.BaseAddress = new Uri("https://archive.org/download/");
                c.DefaultRequestHeaders.Add("Accept", "application/pdf");
            })
            .AddTypedClient<ArchiveClient>();

            var services = serviceCollection.BuildServiceProvider();
            var archive = services.GetRequiredService<ArchiveClient>();
            await archive.Get();
        }

        private class ArchiveClient
        {
            public ArchiveClient(HttpClient httpClient)
            {
                HttpClient = httpClient;
            }

            public HttpClient HttpClient { get; }

            public async Task Get()
            {
                var request = new HttpRequestMessage(HttpMethod.Get, "1952-03_IF/1952-03_IF.pdf");
                var response = await HttpClient.SendAsync(request).ConfigureAwait(false);
                response.EnsureSuccessStatusCode();
                using (Stream contentStream = await response.Content.ReadAsStreamAsync(), 
                    fileStream = new FileStream("Worlds of IF 1952-03.pdf", FileMode.Create, FileAccess.Write, FileShare.None, 8192, true))
                {
                    var totalRead = 0L;
                    var totalReads = 0L;
                    var buffer = new byte[8192];
                    var isMoreToRead = true;

                    do
                    {
                        var read = await contentStream.ReadAsync(buffer, 0, buffer.Length);
                        if (read == 0)
                        {
                            isMoreToRead = false;
                        }
                        else
                        {
                            await fileStream.WriteAsync(buffer, 0, read);

                            totalRead += read;
                            totalReads += 1;

                            if (totalReads % 2000 == 0)
                            {
                                Console.WriteLine(string.Format("bytes downloaded: {0:n0}", totalRead));
                            }
                        }
                    }
                    while (isMoreToRead);
                }
            }
        }
    }
}

Это полное исключение, которое я получаю:

Unhandled Exception: System.Threading.Tasks.TaskCanceledException: The operation was canceled. 
---> System.IO.IOException: Unable to read data from the transport connection: Operation canceled. 
---> System.Net.Sockets.SocketException: Operation canceled    
--- End of inner exception stack trace ---    
at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.ThrowException(SocketError error)    
at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.GetResult(Int16 token)    
at System.Net.Security.SslStreamInternal.<FillBufferAsync>g__InternalFillBufferAsync|38_0[TReadAdapter](TReadAdapter adap, ValueTask`1 task, Int32 min, Int32 initial)    
at System.Net.Security.SslStreamInternal.ReadAsyncInternal[TReadAdapter](TReadAdapter adapter, Memory`1 buffer)    
at System.Net.Http.HttpConnection.FillAsync()    
at System.Net.Http.HttpConnection.CopyToExactLengthAsync(Stream destination, UInt64 length, CancellationToken cancellationToken)    
at System.Net.Http.HttpConnection.ContentLengthReadStream.CompleteCopyToAsync(Task copyTask, CancellationToken cancellationToken)    
--- End of inner exception stack trace ---    
at System.Net.Http.HttpConnection.ContentLengthReadStream.CompleteCopyToAsync(Task copyTask, CancellationToken cancellationToken)    
at System.Net.Http.HttpConnection.HttpConnectionResponseContent.SerializeToStreamAsync(Stream stream, TransportContext context, CancellationToken cancellationToken) 
at System.Net.Http.HttpContent.LoadIntoBufferAsyncCore(Task serializeToStreamTask, MemoryStream tempBuffer)    at System.Net.Http.HttpClient.FinishSendAsyncBuffered(Task`1 sendTask, HttpRequestMessage request, CancellationTokenSource cts, Boolean disposeCts)    
at test2.Program.ArchiveClient.Get() in /Users/Foo/Temp/test3/Program.cs:line 42    
at test2.Program.Main(String[] args) in /Users/Foo/Temp/test3/Program.cs:line 27    
at test2.Program.<Main>(String[] args)

0 ответов

В вашем случае, похоже, проблема в размере. Еще я бы попробовал пройти

HttpCompletionOption.ResponseHeadersRead

в SendAsync() в качестве второго аргумента. Что происходит, так это то, что ваш метод возвращается, как только читаются заголовки. Ответ больше не буферизуется в буфере MemoryStream, а читается непосредственно из сокета. Это означает, что вы можете начать потоковую передачу до того, как будет воспроизведен весь объект. С точки зрения производительности он значительно быстрее, и в вашем случае скорость может иметь значение.

Просто не забудьте удалить ответное сообщение, иначе соединение не будет разорвано.

Другие вопросы по тегам