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 是一个有效的响应。

有没有办法避免这种情况并仍然解析响应?

重现问题的步骤:

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 的维护者提交错误报告。