Python CGI 脚本在使用 uWSGI 时卡住,尝试读取 sys.stdin。

3

我试图简单地读取传递给Python CGI脚本的JSON数据,但是当我调用sys.stdin时,它会挂起。我使用--honour-stdin启动了uWSGI,但没有任何改变。我正在使用带有CGI插件的nginx-> uWSGI。

data = json.load(sys.stdin)
print "Status: 200 OK"
print "Content-Type: application/json"
print "Length:", len(data)
print ""
print data

编辑:如果我限制它所读字符的数量,它就不会挂起。因此,它在等待EOF。我是否遗漏了某些uWSGI设置?


为什么你使用CGI而不是WSGI?!有一些非常轻量级的WSGI库/框架,例如Werkzeug或Flask。 - ThiefMaster
因为我继承了一个旧的CGI脚本库,我需要让它们在新服务器上正常运行。如果可以选择的话,我会用Flask全部重写。但还是感谢你的见解。 - Collin Reynolds
你最终解决了这个问题吗? - James Irwin
2个回答

1
你可以使用 os.environ['CONTENT_LENGTH'] 环境变量来读取确切的字节数,作为 stdin 未被 CGI 库终止的解决方法。
import os

if not os.environ['CONTENT_LENGTH']:
    data = {}
else:
    post_length = int(os.environ['CONTENT_LENGTH'])
    # post_length stores byte count, but stdin.read, apparently, takes the character count
    post_string = sys.stdin.buffer.read(post_length).decode('utf-8')
    data = json.loads(post_string)

假设您的文本采用utf-8编码。

需要删除 .buffer 部分,除此之外,这个看起来对我来说运行得很正确。 - Sam Bull

1
让我从一点背景开始。这是一个CGI协议的description of the CGI protocol。页面中有两个亮点:
  1. 传统的CGI将为每个请求单独调用perl/python/...解释器。
  2. FastCGI使解释器保持活动状态:

FastCGI允许单个长时间运行的进程处理多个用户请求,同时保持接近CGI编程模型,保留简单性,同时消除为每个请求创建新进程的开销。

你的问题是:stdin在每个请求之后没有终止(EOF)。保持链接打开实际上对性能是一个好主意。此外,前面的HTTP连接也可能是keepalive。
我从this page中学到了一种通过fastCGI每个请求环境变量检查请求结束的方法。它们可以通过os.environ获得。
# read until EOF set in environment
while not os.environ.get("stdin_eof", False):
    buf += sys.stdin.read(1)

另一个想法可能是使用增量式JSON解析器,如ijson


1
我知道它不是以EOF结尾的,这就是为什么我更新了我的问题。那么这些请求是如何终止的呢?肯定不仅仅是超时条件。不知何故,Flask和Werkzeug设法工作。我知道Flask和Werkzeug,并且我已经广泛使用它们,但这不是我的问题的答案。 - Collin Reynolds
有趣的是,这段代码长期以来一直存在错误的逻辑。发现它被翻译成不同语言时仍保留着同样的错误 :) 应该将 os.environ.get("stdin_eof", True) 修改为 os.environ.get("stdin_eof", False)。我已经编辑了答案。 - Sergey Nudnov

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