asyncio HTTP服务器挂起keepalive

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了asyncio HTTP服务器挂起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'
'):
                    break

                if re.match(rb'connection:s*keep-alive', line, re.I):
                    keep_alive = True

            writer.write(b'HTTP/1.1 200 OK

<h1>My web page</h1>
')
            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
'
Awaiting data
Finished await got b'Host: localhost:8888
'
Awaiting data
Finished await got b'Connection: keep-alive
'
Awaiting data
Finished await got b'Upgrade-Insecure-Requests: 1
'
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
'
Awaiting data
Finished await got b'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
'
Awaiting data
Finished await got b'Accept-Encoding: gzip, deflate, br
'
Awaiting data
Finished await got b'Accept-Language: en-US,en;q=0.9
'
Awaiting data
Finished await got b'
'
Awaiting data

谁能告诉我这是什么问题?

答案

使用Keep-Alive时,响应必须包含Content-Length标头(或使用更复杂的chunked传输编码)。没有它,客户端别无选择,只能等待连接关闭 - 这绝不会因为keep_alive设置为true而发生。例如,如果您更改这样的编写代码:

            body = b'<h1>My web page</h1>
'
            writer.write(b'HTTP/1.1 200 OK
Content-Length: %d

' % len(body))
            writer.write(body)

......连接不再悬挂。

curl的工作原理是因为它没有指定Keep-Alive(因为在命令行中只指定了一个URL),因此您的代码会关闭连接并且不需要内容长度。

以上是关于asyncio HTTP服务器挂起keepalive的主要内容,如果未能解决你的问题,请参考以下文章

asyncio.new_event_loop 创建的事件循环挂起

ASYNCIO:[错误] 任务被破坏,但它处于挂起状态

线程和 asyncio:任务已销毁,但处于挂起状态

为啥在不同线程中调用 asyncio subprocess.communicate 会挂起?

取消 asyncio 任务导致“任务被破坏但它处于挂起状态”

9Python Asyncio异步编程-事件循环详解