如果使用 HTTP/1.1,模拟 S3 服务器会停止
Mock S3 server stalls if using HTTP/1.1
我正在编写一个测试,其中使用 http.server.HTTPServer
/http.server.BaseHTTPRequestHandler
在测试环境中加载了一个简单的模拟 S3,以测试涉及 Boto 的 S3Transfer 的多部分下载行为。
它工作正常,除非我指定服务器使用 HTTP/1.1
。在这种情况下,它会下载一个 100mb 文件的 2 个 8mb 部分,然后挂起。我希望模拟服务器使用 HTTP/1.1
因为那是真正的 S3 使用的(我相信)。
下面是测试的简化版本,可以通过...运行
pip3 install boto3
python3 test.py
# test.py
import http.server
import re
import threading
import boto3
from botocore import (
UNSIGNED,
)
from botocore.client import (
Config,
)
length = 100 * 2**20
class MockS3(http.server.BaseHTTPRequestHandler):
# If the below line is commented, the download completes
protocol_version = 'HTTP/1.1'
def do_GET(self):
range_header = self.headers['Range']
match = re.search(r'^bytes=(\d+)-(\d*)', range_header)
start_inclusive_str, end_inclusive_str = match.group(1), match.group(2)
start = int(start_inclusive_str)
end = int(end_inclusive_str) + 1 if end_inclusive_str else length
bytes_to_send = end - start
self.send_response(206)
self.send_header('Content-Length', str(bytes_to_send))
self.end_headers()
self.wfile.write(bytearray(bytes_to_send))
def do_HEAD(self):
self.send_response(200)
self.send_header('Content-Length', length)
self.end_headers()
server_address = ('localhost', 5678)
server = http.server.HTTPServer(server_address, MockS3)
thread = threading.Thread(target=server.serve_forever)
thread.daemon = True
thread.start()
class Writable():
def write(self, data):
pass
s3_client = boto3.client('s3',
endpoint_url='http://localhost:5678',
config=Config(signature_version=UNSIGNED),
)
s3_client.download_fileobj(
Bucket='some',
Key='key',
Fileobj=Writable(),
)
请注意 Writable
是故意 不可搜索的 :在我的真实代码中,我使用的是不可搜索的类文件对象。
是的,moto
可以 用于制作模拟 S3,我在其他测试中这样做,但对于这个特定的测试,我想 "real" 服务器。涉及自定义文件对象,并希望确保 S3Transfer 和与此问题无关的其他代码按照我的预期一起运行。
如何设置使用 HTTP/1.1
并且 S3Transfer 可以从中下载的模拟 S3 服务器?
您的线程逻辑中存在错误。您当前正在做的是在单独的线程上提供服务,但您真正想要做的是在多个线程上并发处理请求。
这可以通过创建一个非常愚蠢 HTTP 服务器来实现,它只是混合了线程功能:
class ThreadingServer(ThreadingMixIn, HTTPServer):
pass
并从此服务器而不是基础服务器提供服务 HTTPServer
。
至于为什么这适用于 HTTP/1.0
,连接在处理单个请求后关闭。
我正在编写一个测试,其中使用 http.server.HTTPServer
/http.server.BaseHTTPRequestHandler
在测试环境中加载了一个简单的模拟 S3,以测试涉及 Boto 的 S3Transfer 的多部分下载行为。
它工作正常,除非我指定服务器使用 HTTP/1.1
。在这种情况下,它会下载一个 100mb 文件的 2 个 8mb 部分,然后挂起。我希望模拟服务器使用 HTTP/1.1
因为那是真正的 S3 使用的(我相信)。
下面是测试的简化版本,可以通过...运行
pip3 install boto3
python3 test.py
# test.py
import http.server
import re
import threading
import boto3
from botocore import (
UNSIGNED,
)
from botocore.client import (
Config,
)
length = 100 * 2**20
class MockS3(http.server.BaseHTTPRequestHandler):
# If the below line is commented, the download completes
protocol_version = 'HTTP/1.1'
def do_GET(self):
range_header = self.headers['Range']
match = re.search(r'^bytes=(\d+)-(\d*)', range_header)
start_inclusive_str, end_inclusive_str = match.group(1), match.group(2)
start = int(start_inclusive_str)
end = int(end_inclusive_str) + 1 if end_inclusive_str else length
bytes_to_send = end - start
self.send_response(206)
self.send_header('Content-Length', str(bytes_to_send))
self.end_headers()
self.wfile.write(bytearray(bytes_to_send))
def do_HEAD(self):
self.send_response(200)
self.send_header('Content-Length', length)
self.end_headers()
server_address = ('localhost', 5678)
server = http.server.HTTPServer(server_address, MockS3)
thread = threading.Thread(target=server.serve_forever)
thread.daemon = True
thread.start()
class Writable():
def write(self, data):
pass
s3_client = boto3.client('s3',
endpoint_url='http://localhost:5678',
config=Config(signature_version=UNSIGNED),
)
s3_client.download_fileobj(
Bucket='some',
Key='key',
Fileobj=Writable(),
)
请注意 Writable
是故意 不可搜索的 :在我的真实代码中,我使用的是不可搜索的类文件对象。
是的,moto
可以 用于制作模拟 S3,我在其他测试中这样做,但对于这个特定的测试,我想 "real" 服务器。涉及自定义文件对象,并希望确保 S3Transfer 和与此问题无关的其他代码按照我的预期一起运行。
如何设置使用 HTTP/1.1
并且 S3Transfer 可以从中下载的模拟 S3 服务器?
您的线程逻辑中存在错误。您当前正在做的是在单独的线程上提供服务,但您真正想要做的是在多个线程上并发处理请求。
这可以通过创建一个非常愚蠢 HTTP 服务器来实现,它只是混合了线程功能:
class ThreadingServer(ThreadingMixIn, HTTPServer):
pass
并从此服务器而不是基础服务器提供服务 HTTPServer
。
至于为什么这适用于 HTTP/1.0
,连接在处理单个请求后关闭。