简体   繁体   English

使用内容编码 gzip 和传输编码分块的 HttpClient 导致连接丢失时响应不完整

[英]HttpClient using content encoding gzip and transfer encoding chunked leads to incomplete response on connection loss

I have a .NET Core 3.1 WEB API with gzip compression enabled我有一个启用了 gzip 压缩的 .NET Core 3.1 WEB API

            services.AddResponseCompression(options =>
            {
                options.EnableForHttps = true;
                options.Providers.Add<GzipCompressionProvider>();
            });

            services.Configure<GzipCompressionProviderOptions>(options =>
            {
                options.Level = CompressionLevel.Fastest;
            });

And an endpoint that offers some JSON formatted entries以及一个提供一些 JSON 格式条目的端点

        public async Task<ActionResult> Get(int category)
        {
            var entries = await _service.getEntries(category);

            return Content(entries, MediaTypeNames.Application.Json);
        }

Response Headers响应头

HTTP/1.1 200 OK
Date: Wed, 04 Jan 2023 08:15:35 GMT
Content-Type: application/json; charset=utf-8
Server: Kestrel
Transfer-Encoding: chunked
Content-Encoding: gzip
Vary: Accept-Encoding

The client - Xamarin Forms App using .NET Standard 2.1 libs - fetches data like this客户端 - Xamarin Forms 应用程序使用 .NET 标准 2.1 库 - 像这样获取数据


            var handler = new HttpClientHandler();
            if (handler.SupportsAutomaticDecompression)
                handler.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate;
            
            var httpClient = new HttpClient(handler );
            httpClient.Timeout = TimeSpan.FromMinutes(7);
            httpClient.DefaultRequestHeaders.AcceptEncoding.Add(new StringWithQualityHeaderValue("gzip"));
            httpClient.DefaultRequestHeaders.AcceptEncoding.Add(new StringWithQualityHeaderValue("deflate"));

            var response = await httpClient.GetAsync(url,cancellationToken);
            if (response.IsSuccessStatusCode)
                return response;

// ...deserialize JSON and do stuff with it

The code works as expected if the connection to the api is not interrupted during the download of the data.如果在下载数据期间与 api 的连接没有中断,代码将按预期工作。 But if the connection gets lost (eg out of Wi-Fi range), the response still contains a HTTP 200 status code.但如果连接丢失(例如超出 Wi-Fi 范围),响应仍包含 HTTP 200 状态代码。 This behavior might be expected, as that is how chunked transmission works (Header first, then content by chunks).这种行为可能是预料之中的,因为这就是分块传输的工作方式(首先是标头,然后是分块的内容)。

Still, I didn't expect the client not to throw an error and let the code continue until it ultimately runs into a parsing error.尽管如此,我没想到客户端不会抛出错误并让代码继续执行,直到最终遇到解析错误。

Shouldn't the request fail because the last byte and the end message are missing?请求不应该因为最后一个字节和结束消息丢失而失败吗? What am I doing wrong or how should this problem be handled?我做错了什么或者应该如何处理这个问题?

I could disable chunking by setting a content length in the response header, or by some configuration?我可以通过在响应 header 中设置内容长度或通过某些配置来禁用分块?

But that doesn't seem to be the best approach.但这似乎不是最好的方法。

Update:更新:

Maybe I have a wrong understanding of how AutomaticDecompression works.也许我对 AutomaticDecompression 的工作原理有错误的理解。 If I omit this setting and use the default HttpClient without automatic decompression and the connection is interrupted, the following exception occurs:如果我省略这个设置,使用默认的HttpClient,不自动解压,连接中断,会出现如下异常:

System.Net.Http.HttpRequestException: Error while copying content to a stream. ---> System.IO.IOException: Unable to read data from the transport connection: Network subsystem is down.
      at System.Net.Http.HttpConnection.SendAsyncCore (System.Net.Http.HttpRequestMessage request, System.Threading.CancellationToken cancellationToken) [0x012bb] in /Users/builder/jenkins/workspace/archive-mono/2020-02/android/release/external/corefx/src/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnection.cs:745 
  at System.Net.Http.HttpConnectionPool.SendWithNtConnectionAuthAsync (System.Net.Http.HttpConnection connection, System.Net.Http.HttpRequestMessage request, System.Boolean doRequestAuth, System.Threading.CancellationToken cancellationToken) [0x000e6] in /Users/builder/jenkins/workspace/archive-mono/2020-02/android/release/external/corefx/src/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnectionPool.cs:330 
  at System.Net.Http.HttpConnectionPool.SendWithRetryAsync (System.Net.Http.HttpRequestMessage request, System.Boolean doRequestAuth, System.Threading.CancellationToken cancellationToken) [0x00101] in /Users/builder/jenkins/workspace/archive-mono/2020-02/android/release/external/corefx/src/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnectionPool.cs:296 
  at System.Net.Http.RedirectHandler.SendAsync (System.Net.Http.HttpRequestMessage request, System.Threading.CancellationToken cancellationToken) [0x00070] in /Users/builder/jenkins/workspace/archive-mono/2020-02/android/release/external/corefx/src/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/RedirectHandler.cs:32 
  at System.Net.Http.HttpClient.FinishSendAsyncBuffered (System.Threading.Tasks.Task`1[TResult] sendTask, System.Net.Http.HttpRequestMessage request, System.Threading.CancellationTokenSource cts, System.Boolean disposeCts) [0x0017e] in /Users/builder/jenkins/workspace/archive-mono/2020-02/android/release/external/corefx/src/System.Net.Http/src/System/Net/Http/HttpClient.cs:506

When using AutomaticDecompression, no exception is thrown, but the response is incomplete.使用 AutomaticDecompression 时,没有抛出异常,但响应不完整。

What is the reason for this?这是什么原因? I would expect the decompression handler to check whether the last (zero-length) chunk is present or not and behave appropriately.我希望解压缩处理程序检查最后一个(零长度)块是否存在并表现得适当。

At first, you can try to compare the content length between the begin and the end to check if the transfer is completed or not.首先,您可以尝试比较开始和结束之间的内容长度,以检查传输是否完成。

Actually, the connection lost must cause a error or a problem for the user unless you get the full response content before the connection interred.实际上,连接丢失肯定会给用户带来错误或问题,除非您在连接中断之前获得完整的响应内容。 So you can just use the try catch to deal with the exception and try to get the data from the web api again.所以你可以直接使用try catch来处理异常,再次尝试从web api中获取数据。

No matter the connection lost or the content wrong.无论连接丢失还是内容错误。 You can just add a try catch when you deserialize JSON. Or you can just use the HttpClient.GetAsync() to get the web api data.您可以在反序列化 JSON 时添加一个 try catch。或者您可以只使用HttpClient.GetAsync()来获取 web api 数据。

Generally speaking, the try catch is the best solution I can think about.一般来说, try catch是我能想到的最好的方案。 Maybe there are some better solution.也许有一些更好的解决方案。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM