如果服务器在PUT完成之前回答,那么Python-HTTP模块无法解析响应



我正在使用requests(它在后台使用urllib3和Python http模块(库从Python脚本上传文件。我的后端首先检查请求的标头,如果它不符合所需的先决条件,它会立即停止请求,并使用有效的400响应进行响应。

这种行为在Postman或Curl中效果良好;即客户端能够解析400响应,即使它还没有完成上传并且服务器过早地回答。然而,在使用requests/urllib3的Python中执行此操作时,库无法处理后端响应:

Traceback (most recent call last):
File "C:UsersNeumannAppDataLocalPackagesPythonSoftwareFoundation.Python.3.8_qbz5n2kfra8p0LocalCachelocal-packagesPython38site-packagesurllib3connectionpool.py", line 670, in urlopen
httplib_response = self._make_request(
File "C:UsersNeumannAppDataLocalPackagesPythonSoftwareFoundation.Python.3.8_qbz5n2kfra8p0LocalCachelocal-packagesPython38site-packagesurllib3connectionpool.py", line 392, in _make_request
conn.request(method, url, **httplib_request_kw)
File "C:Program FilesWindowsAppsPythonSoftwareFoundation.Python.3.8_3.8.1776.0_x64__qbz5n2kfra8p0libhttpclient.py", line 1255, in request
self._send_request(method, url, body, headers, encode_chunked)
File "C:Program FilesWindowsAppsPythonSoftwareFoundation.Python.3.8_3.8.1776.0_x64__qbz5n2kfra8p0libhttpclient.py", line 1301, in _send_request
self.endheaders(body, encode_chunked=encode_chunked)
File "C:Program FilesWindowsAppsPythonSoftwareFoundation.Python.3.8_3.8.1776.0_x64__qbz5n2kfra8p0libhttpclient.py", line 1250, in endheaders
self._send_output(message_body, encode_chunked=encode_chunked)
File "C:Program FilesWindowsAppsPythonSoftwareFoundation.Python.3.8_3.8.1776.0_x64__qbz5n2kfra8p0libhttpclient.py", line 1049, in _send_output
self.send(chunk)
File "C:Program FilesWindowsAppsPythonSoftwareFoundation.Python.3.8_3.8.1776.0_x64__qbz5n2kfra8p0libhttpclient.py", line 971, in send
self.sock.sendall(data)
ConnectionResetError: [WinError 10054] Une connexion existante a dû être fermée par l’hôte distant

由于服务器在传输完成之前进行了应答,因此它错误地认为连接已中止,即使服务器确实返回了有效的响应。

有没有一种方法可以避免这种情况并解析响应?

重现问题的步骤:

  • 下载minIO:https://min.io/download#/
  • 运行minIO:
export MINIO_ACCESS_KEY=<access_key>
export MINIO_SECRET_KEY=<secret_key>
.minio.exe server <data folder>
  • 运行以下脚本:
import os
import sys
import requests
from requests_toolbelt.multipart.encoder import MultipartEncoder
def fatal(msg):
print(msg)
sys.exit(1)
def upload_file():
mp_encoder = MultipartEncoder(fields={'file': (open('E:/Downloads/kek.mp3', 'rb'))})
headers = { "Authorization": "invalid" }
print('Uploading file with headers : ' + str(headers))
upload_endpoint = 'http://localhost:9000/mybucket/myobject'
try:
r = requests.put(upload_endpoint, headers=headers, data=mp_encoder, verify=False)
except requests.exceptions.ConnectionError as e:
print(e.status)
for property, value in vars(e).items():
print(property, ":", value)
fatal(str(e))

if r.status_code != 201:
for property, value in vars(r).items():
print(property, ":", value)
fatal('Error while uploading file. Status ' + str(r.status_code))
print('Upload successfully completed')
if __name__ == "__main__":
upload_file()

如果你用这个更改请求行,它就会工作(即服务器返回400,客户端能够解析它(:

r = requests.put(upload_endpoint, headers=headers, data='a string', verify=False)

EDIT:我更新了回溯并更改了问题标题,以反映这既不是requests也不是urllib3的错误,而是它们都使用的Python http模块。

这个问题应该在urllib3 v1.26.0中修复。您运行的是什么版本?

问题是,服务器在响应400后关闭了连接,因此当urllib3试图继续向其发送数据时,套接字就会关闭。因此,它并没有真正错误地认为连接已经关闭,只是对这种情况处理不当。

您的示例代码在我的urllib3==1.26.0机器上运行良好。但我注意到您的Windows机器上出现了不同的异常,所以修复程序可能不起作用。在这种情况下,我只需要捕获异常并向urllib3的维护人员提交一份错误报告。

您应该尝试requests.get而不是put,然后检查它是否有效。请在下面找到我的示例代码。

try:
output = requests.get('http://' + <ipaddress>)
status = output.status_code
print(status)
if status == 200:
print("PASS: HTTP is successful")
else:
raise RuntimeError("FAIL: HTTP is not successful")
except:
Flag = Flag + 1
print("FAIL: HTTP is not successful ")

我的这个代码适用于python3

最新更新