我正在尝试运行一个简单的 boto3 客户端程序,将一个对象放入工作 S3 服务器上的存储桶中。我确信服务器已启动且配置正确,因为我可以通过curl 和AWS Java SDK 等完美上传。事实上,我可以从失败的boto3 脚本中获取调试日志行并将其转换为curl 请求而且它们工作得很好。我正在尝试上传一个非常小的文件,长度只有 136 字节。
但是,我的 S3 服务器在发送正文之前不会发送 100 个继续响应。但 boto3 和 botocore 期望在 put_object 请求中传递正文之前收到 100 响应。此外,boto3 和 botocore 似乎在初始标头集中指定了主体的内容长度,这意味着服务器无休止地等待 136 字节的内容到达。结果是服务器空闲,等待主体传输,而客户端则超时等待永远不会到来的 100 响应。该文件从未上传。该服务器是商业产品,因此我无法更改其 S3 或 HTTP 配置的任何内容。我坚持原来的设置。
是否有一种简单的方法可以绕过客户端代码中的 100 响应代码预期,或者即使未返回 100 也强制传输请求正文?
这是我的 boto3 代码。是的,我知道 sigV4 更好,但我在 V2 或 V4 上也有同样的问题,并且在使用其他程序进行调试时,V2 更容易生成签名。这只是草稿。
import boto3
import botocore
import logging
import warnings
from boto3.compat import PythonDeprecationWarning
from botocore.config import Config
import http.client as http_client
# Suppress deprecation warnings
warnings.filterwarnings("ignore", category=PythonDeprecationWarning)
# Configure logging
logging.basicConfig(level=logging.DEBUG)
# Configure to use Signature Version 2
config = Config(
signature_version='s3',
retries = {'max_attempts': 0},
s3 = {
'use_accelerate_endpoint': False,
'expect_100_continue': False
}
)
# Initialize the S3 client with the custom endpoint and configuration
s3 = boto3.client('s3', endpoint_url='http://<my-domain>:80', config=config)
# Upload the file
try:
with open('jeremy_test.txt', 'rb') as data:
s3.put_object(
Bucket='a-bucket',
Key='jeremy_upload.txt',
Body=data.read(),
ContentType='text/plain'
)
print("File uploaded successfully.")
except Exception as e:
print(f"An error occurred: {e}")
以下是显示所发生情况的相关日志文件:
DEBUG:botocore.hooks:Event request-created.s3.PutObject: calling handler <function add_retry_headers at 0x7f4c43cbb4d0>
DEBUG:botocore.endpoint:Sending http request: <AWSPreparedRequest stream_output=False, method=PUT, url=http://<my-domain>:80/a-bucket/jeremy_upload.txt, headers={'Content-Type': b'text/plain', 'User-Agent': b'Boto3/1.33.13 md/Botocore#1.33.13 ua/2.0 os/linux#4.18.19-100.fc27.x86_64 md/arch#x86_64 lang/python#3.7.11 md/pyimpl#CPython cfg/retry-mode#legacy Botocore/1.33.13', 'Content-MD5': b'p5noto3k/PSRC+Krl8ivcQ==', 'Expect': b'100-continue', 'Date': b'Fri, 27 Sep 2024 03:13:32 GMT', 'Authorization': b'AWS rUNA7sEVArrRIaEp3zlA:/SY4HtAuh4x7edFBU5u8krHMGz4=', 'amz-sdk-invocation-id': b'616bbf1b-f8fb-448e-9c33-340c6d7b614c', 'amz-sdk-request': b'attempt=1', 'Content-Length': '136'}>
DEBUG:urllib3.connectionpool:Starting new HTTP connection (1): <my-domain>:80
send: b'PUT /a-bucket/jeremy_upload.txt HTTP/1.1\r\nHost: <my-domain>\r\nAccept-Encoding: identity\r\nContent-Type: text/plain\r\nUser-Agent: Boto3/1.33.13 md/Botocore#1.33.13 ua/2.0 os/linux#4.18.19-100.fc27.x86_64 md/arch#x86_64 lang/python#3.7.11 md/pyimpl#CPython cfg/retry-mode#legacy Botocore/1.33.13\r\nContent-MD5: p5noto3k/PSRC+Krl8ivcQ==\r\nExpect: 100-continue\r\nDate: Fri, 27 Sep 2024 03:13:32 GMT\r\nAuthorization: AWS rUNA7sEVArrRIaEp3zlA:/SY4HtAuh4x7edFBU5u8krHMGz4=\r\namz-sdk-invocation-id: 616bbf1b-f8fb-448e-9c33-340c6d7b614c\r\namz-sdk-request: attempt=1\r\nContent-Length: 136\r\n\r\n'
DEBUG:botocore.awsrequest:Waiting for 100 Continue response.
reply: '\r\n'
DEBUG:botocore.hooks:Event needs-retry.s3.PutObject: calling handler <botocore.retryhandler.RetryHandler object at 0x7f4c46343e90>
An error occurred: Connection was closed before we received a valid response from endpoint URL: "http://<my-domain>:80/a-bucket/jeremy_upload.txt".
我尝试过事件处理程序,并将配置设置为不期望_100_继续。我尝试过事件处理程序,但我对 boto3/botocore 很陌生,所以我没有运气。
如果你想使用
boto3
,那你就不走运了,我猜其他 SDK 最终也会效仿它。
API 文档 没有提及接收 100 状态代码的可能性,尽管示例确实显示了 Expect: 100-continue
请求标头。但是,S3 用户指南 确实如此:100 状态旨在进行优化,以避免在重定向时多次发送请求正文。 不幸的是,
botocore
无条件地将
Expect
标头添加到请求中(在
handlers.py
add_expect_header()
中,这是基于请求操作表调用的。然后,如果设置了标头,则等待 100 回复.
因此,要么您需要更新您的服务器以使其表现得像 S3(包括 API 文档之外记录的部分),要么您需要分叉 botocore
以使该标头可选或完全删除它。如果您选择后者,也许可以通过配置设置来控制它,然后将补丁提交回 AWS。