asyncio HTTP 服务器挂起并保持活动状态
asyncio HTTP server hangs up with keepalive
我正在尝试使用 asyncio 编写一个 HTTP/1 服务器,并且我正在尝试让它处理 HTTP keep-alives。我有以下代码。
import re
import socket
import asyncio
async def request_handler(reader, writer):
try:
keep_alive = True
while keep_alive:
keep_alive = False
while True:
print('Awaiting data')
line = await reader.readline()
print('Finished await got %s' % line)
if not line.rstrip(b'\r\n'):
break
if re.match(rb'connection:\s*keep-alive', line, re.I):
keep_alive = True
writer.write(b'HTTP/1.1 200 OK\r\n\r\n<h1>My web page</h1>\r\n')
await writer.drain()
finally:
writer.close()
if __name__ == '__main__':
loop = asyncio.get_event_loop()
coro = asyncio.start_server(request_handler, '', 8888, family=socket.AF_UNSPEC, loop=loop, limit=2048)
server = loop.run_until_complete(coro)
try:
loop.run_forever()
finally:
server.close()
loop.run_until_complete(server.wait_closed())
loop.close()
对于来自 curl 等工具的请求,可以正确处理请求,而不会导致服务器挂起。
但是,尝试在浏览器中加载 URL 会导致服务器永远不会终止连接。浏览器尝试请求两种资源,一种来自 /
,另一种来自 /favicon.ico
,请求使用 HTTP keep-alives。 (此信息可通过开发者工具查看。)
我尝试打印服务器接收到的数据。但是,服务器似乎从未收到第二个请求的数据:
Awaiting data
Finished await got b'GET / HTTP/1.1\r\n'
Awaiting data
Finished await got b'Host: localhost:8888\r\n'
Awaiting data
Finished await got b'Connection: keep-alive\r\n'
Awaiting data
Finished await got b'Upgrade-Insecure-Requests: 1\r\n'
Awaiting data
Finished await got b'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36\r\n'
Awaiting data
Finished await got b'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8\r\n'
Awaiting data
Finished await got b'Accept-Encoding: gzip, deflate, br\r\n'
Awaiting data
Finished await got b'Accept-Language: en-US,en;q=0.9\r\n'
Awaiting data
Finished await got b'\r\n'
Awaiting data
谁能告诉我问题出在哪里?
使用 Keep-Alive 时,响应必须包含 Content-Length
header(或使用更复杂的 chunked 传输编码)。没有它,客户端别无选择,只能等待连接关闭——这永远不会发生,因为 keep_alive
被设置为 true。例如,如果您将编写代码更改为:
body = b'<h1>My web page</h1>\r\n'
writer.write(b'HTTP/1.1 200 OK\r\nContent-Length: %d\r\n\r\n' % len(body))
writer.write(body)
...连接不再挂起。
curl
有效,因为它没有指定 Keep-Alive
(因为在命令行上只指定了一个 URL ),所以你的代码关闭了连接和内容长度不需要。
我正在尝试使用 asyncio 编写一个 HTTP/1 服务器,并且我正在尝试让它处理 HTTP keep-alives。我有以下代码。
import re
import socket
import asyncio
async def request_handler(reader, writer):
try:
keep_alive = True
while keep_alive:
keep_alive = False
while True:
print('Awaiting data')
line = await reader.readline()
print('Finished await got %s' % line)
if not line.rstrip(b'\r\n'):
break
if re.match(rb'connection:\s*keep-alive', line, re.I):
keep_alive = True
writer.write(b'HTTP/1.1 200 OK\r\n\r\n<h1>My web page</h1>\r\n')
await writer.drain()
finally:
writer.close()
if __name__ == '__main__':
loop = asyncio.get_event_loop()
coro = asyncio.start_server(request_handler, '', 8888, family=socket.AF_UNSPEC, loop=loop, limit=2048)
server = loop.run_until_complete(coro)
try:
loop.run_forever()
finally:
server.close()
loop.run_until_complete(server.wait_closed())
loop.close()
对于来自 curl 等工具的请求,可以正确处理请求,而不会导致服务器挂起。
但是,尝试在浏览器中加载 URL 会导致服务器永远不会终止连接。浏览器尝试请求两种资源,一种来自 /
,另一种来自 /favicon.ico
,请求使用 HTTP keep-alives。 (此信息可通过开发者工具查看。)
我尝试打印服务器接收到的数据。但是,服务器似乎从未收到第二个请求的数据:
Awaiting data
Finished await got b'GET / HTTP/1.1\r\n'
Awaiting data
Finished await got b'Host: localhost:8888\r\n'
Awaiting data
Finished await got b'Connection: keep-alive\r\n'
Awaiting data
Finished await got b'Upgrade-Insecure-Requests: 1\r\n'
Awaiting data
Finished await got b'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36\r\n'
Awaiting data
Finished await got b'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8\r\n'
Awaiting data
Finished await got b'Accept-Encoding: gzip, deflate, br\r\n'
Awaiting data
Finished await got b'Accept-Language: en-US,en;q=0.9\r\n'
Awaiting data
Finished await got b'\r\n'
Awaiting data
谁能告诉我问题出在哪里?
使用 Keep-Alive 时,响应必须包含 Content-Length
header(或使用更复杂的 chunked 传输编码)。没有它,客户端别无选择,只能等待连接关闭——这永远不会发生,因为 keep_alive
被设置为 true。例如,如果您将编写代码更改为:
body = b'<h1>My web page</h1>\r\n'
writer.write(b'HTTP/1.1 200 OK\r\nContent-Length: %d\r\n\r\n' % len(body))
writer.write(body)
...连接不再挂起。
curl
有效,因为它没有指定 Keep-Alive
(因为在命令行上只指定了一个 URL ),所以你的代码关闭了连接和内容长度不需要。