【问题标题】:Django/apache handle incomplete/cancelled http requestsDjango/apache 处理不完整/取消的 http 请求
【发布时间】:2015-05-03 22:42:46
【问题描述】:

当 django 使用 mod_wsgi 部署在 apache 上时。它似乎以一种非常奇怪的方式处理不完整或取消的请求。

如果客户端取消请求,被取消的请求在django上并没有被取消,例如,如果你正在上传一个大文件,显然请求的body会被实际流式传输,所以django在读取body的同时,客户端取消请求,它仍在处理中(只是不完整)并且从未注意到实际的请求取消操作。

这是一个来自 apache 的日志示例,当请求被取消时。 [Fri May 01 22:05:51.055968 2015] [:error] [pid 31609] (70008) 部分结果有效但处理不完整:[client 172.31.43.91:3645] mod_wsgi (pid=31609): Unable to get bucket旅请求。

然后在 django 代码上,实际的 POST 字典永远不会构建(因为请求不完整,但它到达 django 并像有数据一样被处理),因此 django 在尝试获取数据时会失败(并且返回缺少的 XX 字段错误,或者处理它们的逻辑做什么)

最后,当 django 尝试写回响应时,显然会失败,并且客户端已经关闭了连接。

这种情况经常发生在用作移动应用程序的 REST 服务端点的请求上。移动应用上传大文件,因此请求在应用挂起/关闭时被取消,但服务器似乎总是收到部分请求。

发生这种情况时的完整日志如下所示:

[Fri May 01 22:05:51.055968 2015] [:error] [pid 31609] (70008)部分结果有效但处理不完整:[client 172.31.43.91:3645] mod_wsgi (pid=31609): Unable得到桶大队的请求。

[Fri May 01 22:05:51.062690 2015] [:error] [pid 10580] 一些与此处缺少数据相关的错误消息

[Fri May 01 22:05:51.068790 2015] [:error] [pid 10580] [remote 172.31.43.91:0] mod_wsgi (pid=10580): 处理 WSGI 脚本“some-path/wsgi.py”时出现异常'。

[Fri May 01 22:05:51.068827 2015] [:error] [pid 10580] [remote 172.31.43.91:0] IOError: failed to write data

现在最后一个问题是,有没有办法检测这种不完整的请求并相应地处理它,而不是稍后因为缺少所需的数据而失败?

【问题讨论】:

    标签: python django apache http


    【解决方案1】:

    在读取请求内容时,如果没有读取到所需长度的内容,则 WSGI 层会在调用 wsgi.input.read() 时引发 IOError 异常。这可以在 Django 中按原样传递,或者在更新的版本中更改为不同的派生 IOError 异常类型,称为 UnreadablePostError

    当您的应用程序代码没有专门检查损坏的请求内容并处理该异常类型时,它会向后传播并由 Django 作为未经处理的异常处理。 Django 将在此时尝试编写 500 错误响应。当由 WSGI 层写入时,它将失败,因为连接已关闭,这只能通过实际尝试写入响应来检测。

    所以 Django 不应该给你不完整的 POST 数据,而是应该引发异常。

    至于是否有更好的处理方式,答案是否定的。由于 WSGI 规范基于阻塞模型,因此实际上不可能以干净的方式检测和处理断开的连接。需要切换到 ASYNC Web 服务器和框架才能更好地处理它,这意味着无法使用 WSGI 或 Django。

    FWIW,过去在 mod_wsgi 邮件列表上讨论过连接断开的问题。因此,您可以访问 Google 网上论坛并使用“连接断开”或“连接失败”或“连接关闭”等搜索字词在档案中搜索列表,然后查看可以找到的内容。

    【讨论】:

    • 读取请求时在 django 中没有抛出异常,因为我的“视图(或控制器)”代码正常执行,原因是我能够在发生这种情况时记录很多事情。那么我应该在哪里查看这个异常?这将极大地帮助我检测不完整的请求...
    • 你使用的是什么版本的 Django?什么版本的 mod_wsgi?如果您使用的是旧的 mod_wsgi 版本,我建议您升级到最新版本,看看情况是否有所不同,尤其是在使用 mod_wsgi 守护程序模式时。
    • Django 1.7.7,mod_wsgi 3.5 python 2.7.8。我相信不会引发异常,因为实际上传的不是文件(也不是多部分表单),而是带有 base65 编码图像作为字符串的表单,因此它不会出现在实际 request.FILES 对象中,而是在 request.POST 中。我相信该异常仅针对文件引发?
    • 无论如何读取请求内容,如果没有找到 CONTENT_LENGTH 指定的那么多数据,则应该从 wsgi.input 上的 read() 调用引发 IOError。如果读取输入的代码捕获异常并将其转换为仅返回读取的内容,那是错误的。您在 git repo 中是否有正在阅读的特定代码的链接?另外,你使用的是 mod_wsgi 守护模式吗?
    • 那么不要捕获“异常”,而是在属性不存在时引发的更具体的异常类型。可能是“KeyError”或“AttributeError”,具体取决于 Django 代码的工作方式。
    猜你喜欢
    • 2014-06-21
    • 2011-04-24
    • 1970-01-01
    • 2014-11-15
    • 1970-01-01
    • 2012-02-09
    • 2018-08-07
    • 2020-09-06
    • 2014-03-25
    相关资源
    最近更新 更多