Python CGI在curl中返回正确文本,但浏览器显示尾部零

1 投票
1 回答
943 浏览
提问于 2025-04-19 22:51

我写了一个简单的CGI应用程序,使用的是Python 3:

#! /usr/bin/env python3.4

print("Content-type: text/html\n")

print("AAAA")
  • 当我用任何浏览器访问这个网址时,页面上会显示“AAAA 0”。在Mac上的Firefox、Chrome和Safari,以及iPhone上的Safari上,显示的内容都是一样的。如果把“AAAA”换成其他任何东西,输出也会相应改变,但后面的“0”在所有浏览器中始终存在。

  • 浏览器的“查看源代码”功能总是显示我预期的字符串加换行(浏览器把换行当成空格处理),后面跟着一个意外的0:

    AAAA
    0
    
  • 直接从服务器的命令行执行这个程序(./foo.py | hex),然后用十六进制编辑器查看输出,得到了预期的结果:包括头部和内容行,内容行是41 41 41 0A(“AAAA\n”)。

  • 从我的Mac笔记本和Linux服务器的命令行使用curl,同样得到了我预期的十六进制输出:41 41 41 41 0A。

  • 在任何(测试过的)平台上使用curl都没有显示尾部的零。而所有平台上的浏览器都显示了尾部的零。

  • 如果我去掉第二个“print”语句,只保留一个用于头部的“print”语句,所有浏览器中尾部的“0”就消失了,只剩下一个空白页面。但如果在头部的print之后加上任何print语句,所有浏览器中都会出现尾部的零,而curl中则不会。即使是一个空的“print()”也会在“查看源代码”的第二行显示一个“0”。如果我添加更多的print行,插入正确的doctype和html,尾部的零会在源代码的结束html标签后出现。如果我打印任何头部以外的内容,浏览器源代码中都会显示尾部的零。

  • 把Python版本换成3.2也没有改变结果。

  • 所以,我试着让curl伪装成浏览器,添加了一些浏览器的“user-agent”。结果没有变化。curl仍然显示我预期的内容,而所有浏览器仍然显示尾部的零。

  • 重新输入简单的代码(而不是复制粘贴),确保没有隐形字符,结果还是一样(Python源代码的十六进制视图、在服务器上运行的输出和curl得到的输出都没有额外字符)。

  • 这是共享主机,使用的是Apache+CGI,我没有更改任何Apache文件或创建任何.htaccess文件。

  • 如果这是用于生产环境,我可以使用mod_wsgi,它运行得很好,但这只是为了学习。我想教孩子们如何使用低级CGI,让他们手动从环境变量中提取GET数据等,这样他们可以在使用更高级的功能(cgi模块、WSGI、Flask等)之前,理解底层的工作原理。但我自己并不明白。

那么,有谁能告诉我这个最基本的网页应用程序到底发生了什么?这个“0”是从哪里来的(成功状态码?),为什么它在浏览器源代码中出现而在curl中不显示?最重要的是,我该如何去掉它?

更新: 这个问题只发生在我的手机使用ATT的“LTE”数据时,或者任何浏览器通过ATT的“LTE”进行网络连接时。如果我把手机带进屋里,它就会切换到Comcast/Wifi,刷新浏览器页面后,“AAAA 0”就变成了“AAAA”。走出房子(超出wifi范围),用LTE刷新时,“AAAA”又变回“AAAA 0”。笔记本上的浏览器也是一样的情况。

所以,当笔记本通过热点连接并在所有浏览器中显示“AAAA 0”时,这里是telnet的输出(我没有看到任何问题,就像在curl中一样):

> telnet my.domain.com 80
Trying 100.99.98.97...
Connected to my.domain.com.
Escape character is '^]'.
GET /temp.py HTTP/1.0
Host: my.domain.com

HTTP/1.1 200 OK
Server: nginx
Date: Fri, 05 Sep 2014 23:28:10 GMT
Content-Type: text/html
Connection: close
Vary: Accept-Encoding

AAAA
Connection closed by foreign host.

1 个回答

1

结尾的'0'很可能是由于分块响应格式造成的,但浏览器不应该显示这个东西。

如果你添加一个正确大小的'Content-Length'头部,那个'0'应该就会消失,因为这样Apache就不会使用分块响应了,分块响应只在长度未知时才会用到。

如果你用'telnet'连接到服务器并手动发出请求,你会得到完整的输出是什么呢?

telnet server-hostname 80

然后输入:

GET /some/url HTTP/1.0
Host: virtual-hostname

在后面加一个空行。

撰写回答