Python http.server处理POST请求体时卡顿问题排查
Python http.server读取POST请求体卡顿问题
问题现象
使用Python的http.server编写服务端时,遇到POST请求体读取卡顿的问题:执行curl请求后,服务端卡在self.rfile.read()操作,直到按下CTRL-C终止curl,读取才继续,但此时回复发送失败。同时使用CGIHTTPRequestHandler时,CGI程序里的sys.stdin.read()也会卡住(该CGI程序在nginx环境下运行正常)。运行环境为Linux,Python版本3.8和3.11。
最小复现代码
服务端代码
#!/usr/bin/env python3 import http.server from http import HTTPStatus import socketserver from datetime import datetime HOST="127.0.0.1" PORT=9001 class MyHandler(http.server.SimpleHTTPRequestHandler): def do_POST(self): print( self.rfile.read().decode("utf-8") ) self.send_response(HTTPStatus.OK,"ok") self.send_header( "Content-type", "text/plain" ) self.end_headers() self.wfile.write( "OK".encode("utf-8") ) self.wfile.flush() socketserver.TCPServer.allow_reuse_address = True with socketserver.TCPServer((HOST, PORT), MyHandler) as httpd: httpd.serve_forever()
请求命令
curl http://localhost:9001/list -d "hello server"
解决方案
修改服务端代码
将do_POST方法改为通过Content-Length头读取指定长度的请求体:
def do_POST(self): # 从请求头获取消息体长度,默认0 content_length = int(self.headers.get('Content-Length', 0)) # 读取对应长度的内容 body = self.rfile.read(content_length).decode("utf-8") print(body) self.send_response(HTTPStatus.OK,"ok") self.send_header("Content-type", "text/plain") self.end_headers() self.wfile.write("OK".encode("utf-8")) self.wfile.flush()
CGI场景解决方案
对于CGI程序,同样需要通过环境变量获取内容长度后读取:
import os import sys content_length = int(os.environ.get('CONTENT_LENGTH', 0)) body = sys.stdin.read(content_length)
问题原因
直接调用self.rfile.read()或sys.stdin.read()会尝试读取到流结束,但HTTP/1.1默认启用持久连接(Keep-Alive),客户端发送完请求体后不会立即关闭连接,导致服务端一直阻塞在读取操作上。只有当客户端终止连接(如按下CTRL-C),流才会触发EOF,此时服务端才会继续执行,但连接已经关闭,回复无法正常发送。
通过Content-Length头/环境变量获取请求体长度,读取对应字节数后即可结束读取操作,继续后续逻辑,避免不必要的阻塞。
内容的提问来源于stack exchange,提问作者Impatient Hippo
相关产品推荐
相关产品推荐

