Http request from Chrome hangs python webserver

rba*_*ber 5 google-chrome pyopenssl python-2.7 python-3.x

I have a very basic python (2.7.12) web server (I've stripped it down as much as possible), code given below

import time
import ssl
from BaseHTTPServer import HTTPServer
from SimpleHTTPServer import SimpleHTTPRequestHandler

class Management(SimpleHTTPRequestHandler):
    def do_GET(self):
        self.send_response(200)
        SimpleHTTPRequestHandler.end_headers(self)
        self.wfile.write(time.asctime() + "\n")

httpd = HTTPServer(('', 4443), Management)
httpd.socket = ssl.wrap_socket (httpd.socket, certfile='./server.pem', server_side=True)
httpd.serve_forever()
Run Code Online (Sandbox Code Playgroud)

All it does is return the time. It works fine when I access it via https, but when I access it via http using (the latest version of) chrome on a different computer, it frequently (but not always) causes the entire server to hang in the python code, specifically ssl.py in the do_handshake function in the line

self._sslobj.do_handshake()
Run Code Online (Sandbox Code Playgroud)

I was expecting the connection to fail and get dropped because I am trying to access an https page via http, but I don't expect it to cause the entire process to hang. It only happens with chrome (not firefox or microsoft edge), and only when chrome is run on a different computer than the computer the server is running on.

I've also tried creating a python3 version of the code, and I see the same issue. I've tried running it in both Cygwin on windows 10, and the terminal in Ubuntu 14.04, and I get the same problem.

Jam*_*all 8

刚刚为此奋斗了两天:(

事实证明,用户 Ami Bar 的回答完全正确。Google Chrome 保持连接打开状态,并导致选择器库中的多路复用将连接注册为可读。从本质上讲,它会强制多路复用失败,因此您必须使服务器线程化以单独处理请求。

您可以通过实现 来线程化您的 HTTP 服务器socketserver.ThreadingMixIn。但是,如果更容易(这就是我所做的),您可以简单地将 python 升级到 3.7+ 并让您的服务器继承自http.server.ThreadingHTTPServer,它本机实现ThreadingMixIn

希望这能拯救一些可怜的灵魂两天......


小智 3

我没有解决办法,但我想我有理由。

Chrome 会导致 httpd.serve_forever() 挂起,因为它使请求流保持打开状态。

看起来httpd.serve_forever()像这样:

while not self.__shutdown_request:
    ready = selector.select(poll_interval)
    if ready:
        self._handle_request_noblock()
Run Code Online (Sandbox Code Playgroud)

Chrome 发送一个请求,获取响应,然后发送另一个请求,但保持流打开并且不发送任何内容。已selector.select()完成并将控制权传递给self._handle_request_noblock(),但由于流为空,请求处理程序无法完成它并且被阻止。

我猜 Chrome 这样做是为了加快请求速度。

对于 Firefox、Internet Explorer 和 wget,这种情况不会发生。