Skip to content

Commit 3d860d5

Browse files
authored
Merge bc3f865 into b97fd4d
2 parents b97fd4d + bc3f865 commit 3d860d5

2 files changed

Lines changed: 138 additions & 0 deletions

File tree

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import threading
2+
from http.server import BaseHTTPRequestHandler, HTTPServer
3+
4+
from e2b.api.client.client import AuthenticatedClient
5+
from e2b.template_async.build_api import upload_file
6+
7+
8+
# Regression test for e2b-dev/e2b#1243 — upload_file must set Content-Length
9+
# and must not fall back to Transfer-Encoding: chunked. S3 presigned PUT URLs
10+
# reject chunked encoding with 501 NotImplemented. httpx sets Content-Length
11+
# automatically when we pass bytes (tar_buffer.getvalue()); this test guards
12+
# against someone swapping the bytes for a generator/stream later.
13+
#
14+
# The mock server runs in a daemon thread and doesn't need to be async — the
15+
# httpx.AsyncClient connects to it via asyncio sockets without blocking the
16+
# event loop.
17+
18+
19+
def _make_server():
20+
state = {"headers": None, "body_length": 0}
21+
22+
class Handler(BaseHTTPRequestHandler):
23+
def do_PUT(self):
24+
state["headers"] = dict(self.headers)
25+
length = int(self.headers.get("Content-Length", 0))
26+
body = self.rfile.read(length) if length else b""
27+
state["body_length"] = len(body)
28+
self.send_response(200)
29+
self.end_headers()
30+
31+
def log_message(self, *args, **kwargs):
32+
return
33+
34+
server = HTTPServer(("127.0.0.1", 0), Handler)
35+
thread = threading.Thread(target=server.serve_forever, daemon=True)
36+
thread.start()
37+
return server, thread, state
38+
39+
40+
async def test_upload_file_sets_content_length_and_no_chunked_encoding(tmp_path):
41+
(tmp_path / "hello.txt").write_text("hello world")
42+
43+
server, thread, state = _make_server()
44+
host, port = server.server_address
45+
url = f"http://{host}:{port}/upload"
46+
47+
try:
48+
client = AuthenticatedClient(base_url="http://test", token="test")
49+
await upload_file(
50+
api_client=client,
51+
file_name="*.txt",
52+
context_path=str(tmp_path),
53+
url=url,
54+
ignore_patterns=[],
55+
resolve_symlinks=False,
56+
stack_trace=None,
57+
)
58+
finally:
59+
server.shutdown()
60+
server.server_close()
61+
thread.join(timeout=5)
62+
63+
assert state["headers"] is not None
64+
content_length = state["headers"].get("Content-Length")
65+
assert content_length is not None
66+
assert int(content_length) > 0
67+
assert int(content_length) == state["body_length"]
68+
69+
transfer_encoding = state["headers"].get("Transfer-Encoding")
70+
if transfer_encoding is not None:
71+
assert "chunked" not in transfer_encoding.lower()
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import threading
2+
from http.server import BaseHTTPRequestHandler, HTTPServer
3+
4+
from e2b.api.client.client import AuthenticatedClient
5+
from e2b.template_sync.build_api import upload_file
6+
7+
8+
# Regression test for e2b-dev/e2b#1243 — upload_file must set Content-Length
9+
# and must not fall back to Transfer-Encoding: chunked. S3 presigned PUT URLs
10+
# reject chunked encoding with 501 NotImplemented. httpx sets Content-Length
11+
# automatically when we pass bytes (tar_buffer.getvalue()); this test guards
12+
# against someone swapping the bytes for a generator/stream later.
13+
14+
15+
def _make_server():
16+
state = {"headers": None, "body_length": 0}
17+
18+
class Handler(BaseHTTPRequestHandler):
19+
def do_PUT(self):
20+
state["headers"] = dict(self.headers)
21+
length = int(self.headers.get("Content-Length", 0))
22+
body = self.rfile.read(length) if length else b""
23+
state["body_length"] = len(body)
24+
self.send_response(200)
25+
self.end_headers()
26+
27+
def log_message(self, *args, **kwargs):
28+
return
29+
30+
server = HTTPServer(("127.0.0.1", 0), Handler)
31+
thread = threading.Thread(target=server.serve_forever, daemon=True)
32+
thread.start()
33+
return server, thread, state
34+
35+
36+
def test_upload_file_sets_content_length_and_no_chunked_encoding(tmp_path):
37+
(tmp_path / "hello.txt").write_text("hello world")
38+
39+
server, thread, state = _make_server()
40+
host, port = server.server_address
41+
url = f"http://{host}:{port}/upload"
42+
43+
try:
44+
client = AuthenticatedClient(base_url="http://test", token="test")
45+
upload_file(
46+
api_client=client,
47+
file_name="*.txt",
48+
context_path=str(tmp_path),
49+
url=url,
50+
ignore_patterns=[],
51+
resolve_symlinks=False,
52+
stack_trace=None,
53+
)
54+
finally:
55+
server.shutdown()
56+
server.server_close()
57+
thread.join(timeout=5)
58+
59+
assert state["headers"] is not None
60+
content_length = state["headers"].get("Content-Length")
61+
assert content_length is not None
62+
assert int(content_length) > 0
63+
assert int(content_length) == state["body_length"]
64+
65+
transfer_encoding = state["headers"].get("Transfer-Encoding")
66+
if transfer_encoding is not None:
67+
assert "chunked" not in transfer_encoding.lower()

0 commit comments

Comments
 (0)