Python - 如果服务器在 PUT 完成之前做出响应,则 HTTP 模块无法解析响应
Python - HTTP module cannot parse response if the server answers before the PUT is complete
我正在使用 requests
(它在后台使用 urllib3
和 Python http 模块)库从 Python 脚本上传文件。
我的后端首先检查请求的 headers,如果它不符合所需的先决条件,它会立即停止请求并以有效的 400 响应进行响应。
此行为在 Postman 或 Curl 中运行良好;即,客户端能够解析 400 响应,即使它尚未完成上传并且服务器过早响应。
但是,在 Python 和 requests
/urllib3
中这样做时,库无法处理后端响应:
Traceback (most recent call last):
File "C:\Users\Neumann\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.8_qbz5n2kfra8p0\LocalCache\local-packages\Python38\site-packages\urllib3\connectionpool.py", line 670, in urlopen
httplib_response = self._make_request(
File "C:\Users\Neumann\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.8_qbz5n2kfra8p0\LocalCache\local-packages\Python38\site-packages\urllib3\connectionpool.py", line 392, in _make_request
conn.request(method, url, **httplib_request_kw)
File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.8_3.8.1776.0_x64__qbz5n2kfra8p0\lib\http\client.py", line 1255, in request
self._send_request(method, url, body, headers, encode_chunked)
File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.8_3.8.1776.0_x64__qbz5n2kfra8p0\lib\http\client.py", line 1301, in _send_request
self.endheaders(body, encode_chunked=encode_chunked)
File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.8_3.8.1776.0_x64__qbz5n2kfra8p0\lib\http\client.py", line 1250, in endheaders
self._send_output(message_body, encode_chunked=encode_chunked)
File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.8_3.8.1776.0_x64__qbz5n2kfra8p0\lib\http\client.py", line 1049, in _send_output
self.send(chunk)
File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.8_3.8.1776.0_x64__qbz5n2kfra8p0\lib\http\client.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
因为服务器在传输完成之前做出响应,所以它错误地认为连接已经中止,即使服务器 return 是一个有效的响应。
有没有办法避免这种情况并仍然解析响应?
重现问题的步骤:
- 下载 minIO:https://min.io/download#/
- 运行最小IO:
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()
如果你用这个改变请求行,它将起作用(即服务器 returns 400 并且客户端能够解析它):
r = requests.put(upload_endpoint, headers=headers, data='a string', verify=False)
EDIT :我更新了回溯并更改了问题标题以反映它既不是 requests
也不是 urllib3
错误,但是 Python 他们都使用的 http 模块。
您应该尝试 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
此问题应在 urllib3 v1.26.0 中得到解决。你是什么版本运行?
问题是服务器在响应 400 后关闭了连接,所以当 urllib3 试图继续向它发送数据时套接字被关闭。所以它并不是真的错误地认为连接已关闭,它只是错误地处理了这种情况。
您的示例代码在我的机器上使用 urllib3==1.26.0 运行良好。但我注意到您在 Windows 机器上遇到了不同的异常,所以修复可能不起作用。在那种情况下,我会捕获异常并向 urllib3 的维护者提交错误报告。
我正在使用 requests
(它在后台使用 urllib3
和 Python http 模块)库从 Python 脚本上传文件。
我的后端首先检查请求的 headers,如果它不符合所需的先决条件,它会立即停止请求并以有效的 400 响应进行响应。
此行为在 Postman 或 Curl 中运行良好;即,客户端能够解析 400 响应,即使它尚未完成上传并且服务器过早响应。
但是,在 Python 和 requests
/urllib3
中这样做时,库无法处理后端响应:
Traceback (most recent call last):
File "C:\Users\Neumann\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.8_qbz5n2kfra8p0\LocalCache\local-packages\Python38\site-packages\urllib3\connectionpool.py", line 670, in urlopen
httplib_response = self._make_request(
File "C:\Users\Neumann\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.8_qbz5n2kfra8p0\LocalCache\local-packages\Python38\site-packages\urllib3\connectionpool.py", line 392, in _make_request
conn.request(method, url, **httplib_request_kw)
File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.8_3.8.1776.0_x64__qbz5n2kfra8p0\lib\http\client.py", line 1255, in request
self._send_request(method, url, body, headers, encode_chunked)
File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.8_3.8.1776.0_x64__qbz5n2kfra8p0\lib\http\client.py", line 1301, in _send_request
self.endheaders(body, encode_chunked=encode_chunked)
File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.8_3.8.1776.0_x64__qbz5n2kfra8p0\lib\http\client.py", line 1250, in endheaders
self._send_output(message_body, encode_chunked=encode_chunked)
File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.8_3.8.1776.0_x64__qbz5n2kfra8p0\lib\http\client.py", line 1049, in _send_output
self.send(chunk)
File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.8_3.8.1776.0_x64__qbz5n2kfra8p0\lib\http\client.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
因为服务器在传输完成之前做出响应,所以它错误地认为连接已经中止,即使服务器 return 是一个有效的响应。
有没有办法避免这种情况并仍然解析响应?
重现问题的步骤:
- 下载 minIO:https://min.io/download#/
- 运行最小IO:
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()
如果你用这个改变请求行,它将起作用(即服务器 returns 400 并且客户端能够解析它):
r = requests.put(upload_endpoint, headers=headers, data='a string', verify=False)
EDIT :我更新了回溯并更改了问题标题以反映它既不是 requests
也不是 urllib3
错误,但是 Python 他们都使用的 http 模块。
您应该尝试 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
此问题应在 urllib3 v1.26.0 中得到解决。你是什么版本运行?
问题是服务器在响应 400 后关闭了连接,所以当 urllib3 试图继续向它发送数据时套接字被关闭。所以它并不是真的错误地认为连接已关闭,它只是错误地处理了这种情况。
您的示例代码在我的机器上使用 urllib3==1.26.0 运行良好。但我注意到您在 Windows 机器上遇到了不同的异常,所以修复可能不起作用。在那种情况下,我会捕获异常并向 urllib3 的维护者提交错误报告。