使用HTTP/1.1与SimpleHTTPRequestHandler进行通信

5

当我使用HTTP/1.1和SimpleHTTPRequestHandler时,加载一个拉取其他资源的页面在第二个资源之后会卡住。

这里有一个小的复现代码:

from SimpleHTTPServer import SimpleHTTPRequestHandler
from BaseHTTPServer import HTTPServer

class MyRequestHandler(SimpleHTTPRequestHandler):
    #protocol_version = "HTTP/1.0"   # works
    protocol_version = "HTTP/1.1"   # hangs

server = HTTPServer(("localhost", 7080), MyRequestHandler)
server.serve_forever()

使用上述服务器,当浏览器尝试加载b.png时,以下HTML会挂起:

<html>
    <body>
        <img src="a.png">
        <img src="b.png">
    </body>
</html>

HTTP/1.1能否与SimpleHTTPServer模块一起使用,如果可以,应该如何使用?请注意,将ForkingMixIn或ThreadingMixIn添加到服务器将允许事情继续进行,但似乎没有这些mixin也应该是可能的。


你写了“这是一个小的复现器”,但在这里没有“引入其他资源的页面”,所以我猜它不能算是一个复现器。请描述如何复现这个问题。 - Piotr Dobrogost
@PiotrDobrogost 这个问题非常简单,我都没打算发帖(任何包含多个资源的页面都会触发它),但我发布了一个展示问题的HTML示例(你需要为a.png和b.png提供PNG文件)。 - kanaka
Python的版本和操作系统是什么? - Piotr Dobrogost
@PiotrDobrogost Python 版本并不重要。问题是如何修改 SimpleHTTPRequestHandler 的行为。相同的行为发生在 Python2.4、Python2.7 和 Python3.2 上(只需稍微更改 Python3 的导入方式)。这是在 Linux 上(多个版本的 Ubuntu 和 Linux Mint)。我没有使用 Threading 或 Forking MixIn,所以我怀疑在 Windows 上会出现相同的行为。 - kanaka
1个回答

7
您看到的行为是由以下三个原因造成的:
  • BaseHTTPServer.HTTPServer 默认只能一次处理一个请求
  • 大多数用户代理(浏览器)会同时向同一主机打开多个连接
  • 大多数用户代理使用 HTTP 1.1 的 keep-alive 功能,在收到所请求的实体后不会立即关闭连接
你看到的是浏览器能够在打开到服务器的第一个连接中获取所有entities请求的内容。这包括页面本身和可能的一些资源。同时,浏览器会打开其他连接来获取其余的资源,但由于服务器与第一个连接绑定,这些连接无法进行。服务器与第一个连接绑定的原因是,尽管浏览器已经使用此连接接收到所请求的实体,但它不会立即关闭该连接,以防它在不久的将来可以重用以获取更多实体(服务器也不会在其端关闭连接,因为浏览器指定了HTTP的1.1版本并发送了Connection: keep-alive头)。只有在第一个连接超时后,服务器才开始处理下一个等待的连接,因此正在下载其他资源(所有这些资源都是使用此特定连接请求的)。如果您等待足够长的时间,浏览器就能够获取所有资源。您可以在Firefox中将network.http.max-persistent-connections-per-server首选项设置为1(而不是默认值6),或在其他浏览器中进行类似设置,以观察差异。然后,由于所有资源都是使用同一连接请求的,每个资源的检索都会在前一个资源检索完成后立即开始,没有任何延迟。
我想感谢freenode.net上#python IRC频道的在解决这个问题时提供的帮助。

谢谢你的解释!我被这个问题困扰了好几天。我添加了一个线程混合,似乎解决了问题,但在找到这篇文章之前,我仍然不明白问题出在哪里。 - Antimony

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