httpx POST上传文件加DigestAuth触发Errno32 Broken pipe错误原因咨询
问题原因
- Digest认证默认采用质询-响应的交互流程:客户端第一次发起请求时默认不会携带认证头,服务端返回
401 Unauthorized并携带WWW-Authenticate响应头告知认证算法、随机数等参数,客户端计算出正确摘要后再携带Authorization头重发请求。 - 上传大文件时,很多服务端不会等待完整请求体传输完成,收到未带认证头的POST请求后会直接返回401并断开连接,此时客户端还在上传70MB的文件内容,对端已经关闭连接,就会抛出
httpcore.WriteError: [Errno 32] Broken pipe错误。 - GET请求没有请求体,第一次请求只发送请求头即可收到服务端的认证质询,重发也不会有额外的传输开销,因此可以正常执行。
- 移除
auth参数后,客户端不会触发自动重发逻辑,服务端会等待完整文件传输完成后再返回401,因此不会出现中途断连的报错。
另外你原有代码存在逻辑缺陷:创建了httpx.Client实例但实际调用的是顶层httpx.post方法,无法复用客户端的认证缓存,也会加剧该问题。
解决方案
方案1:预完成认证质询(最优)
先向目标地址发起一次无请求体的HEAD请求,提前完成认证交互拿到有效凭证,再上传文件避免第一次大文件传输被中途断连:
import httpx headers = { 'action': 'upload' } file = 'filename.zip' auth = httpx.DigestAuth('user', 'password') upload_url = 'http://192.168.10.10/upload' with httpx.Client() as client: # 先发起HEAD请求完成认证握手,缓存有效凭证 client.head(upload_url, auth=auth) # 直接上传文件,首次请求就会携带正确的认证头 files = {'upload-file': open('files/' + file, 'rb')} r = client.post(upload_url, files=files, headers=headers)
方案2:使用可重读文件对象(仅适合测试)
如果服务端允许等待请求体传输完成再返回401,可以将文件指针重置逻辑加入,保证重发请求时可以重新读取文件内容:
import httpx headers = { 'action': 'upload' } file = 'filename.zip' auth = httpx.DigestAuth('user', 'password') upload_url = 'http://192.168.10.10/upload' with open('files/' + file, 'rb') as f: with httpx.Client(auth=auth) as client: f.seek(0) # 每次请求前重置文件指针到开头 files = {'upload-file': f} r = client.post(upload_url, files=files, headers=headers)
该方案会传输两次完整的70MB文件,带宽开销加倍,不建议生产环境使用。
内容的提问来源于stack exchange,提问作者hugo387
相关产品推荐
相关产品推荐

