未读取请求正文时的 ERR_INCOMPLETE_CHUNKED_ENCODING

Posted

技术标签:

【中文标题】未读取请求正文时的 ERR_INCOMPLETE_CHUNKED_ENCODING【英文标题】:ERR_INCOMPLETE_CHUNKED_ENCODING when the request body is not read 【发布时间】:2017-03-25 22:53:38 【问题描述】:

以下是我对一个有趣的错误的讨伐记录,虽然我已经解决了,但我不明白。我找到了一个解决方案,但我真的希望有人能提供一些关于该错误的实际原因的见解。

问题背景

这个问题首先出现在我们的 Django 应用的生产服务器上。堆栈如下

AngularJS nginx uWSGI 姜戈 PostgreSQL

我们的团队发现 Safari 上的单个 POST 请求存在问题。 Safari 会阻塞响应并吐出ERR_INCOMPLETE_CHUNKED_ENCODING。 post请求如下:

class ContractCloseOutSubmitView(APIView):
    def post(self, request, contract_id):
        contract = get_object_or_404(
            ContractCloseOut, pk=contract_id)
        if contract.submit():
            return Response("detail": "Closed successfully.", status=200)
        else:
            raise ParseError("Could not submit.")

请求是将对象标记为关闭的简单请求。我们在网站的很多地方都使用了这种模式,所以这个问题显然需要注意。

解决方案

我们的第一个线索是请求确实关闭了对象。也就是说,必须到达if contract.submit() 行。这将问题缩小到响应范围。我做了一些阅读,这个错误有很多原因。我们试过了:

在 Django 中显式设置内容长度 一些 hack 迫使 nginx 更可靠地判断内容长度 用几种不同的方式重写代码

没有任何效果,问题仍然没有在本地发生。所以我们决定在本地复制整个堆栈并进行测试。一次一个,我们删除了网络层的元素,并确定删除 nginx 并直接与 uWSGI 对话解决了这个问题。

所以现在我们确信问题出在 nginx 上,但仍然没有解决方案。好吧,在 Google 的第三页深处,我看到了一篇 *** 帖子,其中有一个简短的评论,提到 读取请求正文 对 nginx 的缓冲区以及它如何判断内容长度有某种影响(这是我不太熟悉的东西)。无论如何,显然解决此问题的方法是简单地读取缓冲区。也就是说,使用请求正文。所以我尝试通过将其值分配给变量来简单地触摸请求正文:

class ContractCloseOutSubmitView(APIView):
    def post(self, request, contract_id):

        data = request.data #touchie touchie

        contract = get_object_or_404(
            ContractCloseOut, pk=contract_id)
        if contract.submit():
            return Response("detail": "Closed successfuly.", status=200)
        else:
            raise ParseError("Could not submit.")

tl;博士

在收到对一个非常简单的呼叫的响应时遇到ERR_INCOMPLETE_CHUNKED_ENCODING 问题仅发生在生产环境中的 Safari 中 在调试时,我们一次删除了一部分堆栈,并确定删除 nginx 可以解决问题 请求是 POST,但请求正文中没有信息,因此正文被忽略 我发现一个 *** 问题的评论提到读取 POST 请求的正文对 nginx 的请求缓冲区有某种影响 只需将请求正文分配给一个变量(从而读取缓冲区)即可解决问题

在这一切之后,我只是想知道为什么会发生这种情况。不读取可能导致此错误的请求正文是什么?为什么只有nginx?为什么只有 Safari?

我希望 *** 社区可以帮助我解释这一点!解决起来很有趣。我还做了一个简短的presentation 与一些同事分享。

【问题讨论】:

【参考方案1】:

您没有指定 nginx 的版本。如果几个月没有更新,可能是这个已关闭的issue:

https://trac.nginx.org/nginx/ticket/959

以前,流的窗口保持为零,以防止 客户端在请求之前发送请求正文(请参阅 887cca40ba6a 了解详情)。直到这样的初始窗口被确认 所有带有数据的请求都被拒绝(有关详细信息,请参阅 0aa07850922f)。 这种方法揭示了许多问题:一些客户(尤其是 MS IE/Edge、Safari、ios 应用程序)显示错误甚至崩溃,如果 流被拒绝;这要求每个请求至少有一个 RTT 在客户端接收窗口更新并能够发送之前使用正文 数据。为了克服这些问题,新指令 介绍了“http2_body_preread_size”。它设置初始窗口 并配置一个特殊的每个流预读缓冲区,用于 在请求和处理正文之前保存所有传入数据。

我的猜测是创建引用会在设置确认来回后保持该缓冲区打开。

尝试升级 nginx,因为此补丁已合并。如果你升级后仍然有问题,这是一个回归,我会打开一个新的 nginx 票。

【讨论】:

有趣。它已经几个月没有更新了。这可能只是它!我正在为这个 bug 做一个概念证明,所以我会看看我是否可以将它隔离到 nginx 版本。

以上是关于未读取请求正文时的 ERR_INCOMPLETE_CHUNKED_ENCODING的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Golang 中间件中读取请求正文两次?

邮递员发布请求无法读取未定义的属性

读取请求正文两次

如何在 Spring 'HandlerMethodArgumentResolver' 中多次读取请求正文?

如何使用 spring webflux 读取请求正文

在 webflux 中读取请求正文