使用HttpCompletionOption.ResponseHeadersRead时的HttpClient超时

5
我将为您翻译以下内容:

在Windows上创建.NET Core 3.1控制台应用程序时,我试图弄清楚为什么使用HttpCompletionOption.ResponseHeadersRead后获取内容时,httpClient.Timeout似乎不起作用。

static async Task Main(string[] args)
{
    var httpClient = new HttpClient();

    // if using HttpCompletionOption this timeout doesn't work
    httpClient.Timeout = TimeSpan.FromSeconds(5);

    var uri = new Uri("http://brokenlinkcheckerchecker.com/files/200MB.zip");

    // will not timeout
    //using var httpResponseMessage = await httpClient.GetAsync(uri, HttpCompletionOption.ResponseHeadersRead);

    // will timeout after 5s with a TaskCanceledException
    var httpResponseMessage = await httpClient.GetAsync(uri);

    Console.WriteLine($"Status code is {httpResponseMessage.StatusCode}. Press any key to get content");
    Console.ReadLine();
    Console.WriteLine("getting content");

    var html = await httpResponseMessage.Content.ReadAsStringAsync();
    Console.WriteLine($"finished and length is {html.Length}");
}

我也尝试过使用 CancellationToken

// will not timeout
var cts = new CancellationTokenSource(5000);
using var httpResponseMessage = await httpClient.GetAsync(uri, HttpCompletionOption.ResponseHeadersRead,
 cts.Token);

以及 ReadAsStreamAsync

// will not timeout
using (Stream streamToReadFrom = await httpResponseMessage.Content.ReadAsStreamAsync())
{
    string fileToWriteTo = Path.GetTempFileName();
    using (Stream streamToWriteTo = File.Open(fileToWriteTo, FileMode.Create))
    {
        await streamToReadFrom.CopyToAsync(streamToWriteTo);
    }
}

我从这篇优秀的文章中学到了关于HttpCompletionOption的内容: https://www.stevejgordon.co.uk/using-httpcompletionoption-responseheadersread-to-improve-httpclient-performance-dotnet

更新 通过使用@StephenCleary的答案,将cancellationToken传递给CopyToAsync方法,现在已按预期工作。

下面是更新后的代码,它展示了如何将数据复制到MemoryStream,然后再复制到字符串中。我发现这很棘手。对于我的用例来说,这很好用。

string html;
await using (var streamToReadFrom = await httpResponseMessage.Content.ReadAsStreamAsync())
await using (var streamToWriteTo = new MemoryStream())
{
    await streamToReadFrom.CopyToAsync(streamToWriteTo, cts.Token);
    // careful of what encoding - read from incoming MIME
    html = Encoding.UTF8.GetString(streamToWriteTo.ToArray());
}

你为什么在这里期望超时? - Pavel Anikhouski
@PavelAnikhouski - 好问题!我很感兴趣为什么在使用 HttpCompletionOption 时超时行为似乎会改变。我还在编写代码,在这里,即使客户端仍然可以成功下载内容,我也希望在 x 秒后抛出 Http 异常。 - Dave Mateer
1个回答

4
我认为HttpClient.Timeout仅适用于请求的GetAsync部分。HttpCompletionOption.ResponseHeadersRead的意思是“当读取响应头时,认为Get已完成”,所以它是完整的。因此问题在于它不适用于从流中读取。
我建议使用Polly的Timeout代替HttpClient.Timeout;Polly是一个通用库,可用于超时任何操作。
如果您现在不想使用Polly,可以将CancellationToken传递给Stream.CopyToAsync

1
非常感谢。我已经更新了问题,并提供了一个可行的答案。下一步是https://github.com/App-vNext/Polly,因为我忘记了这个优秀的库! - Dave Mateer

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接