Skip to content

Commit a93c07e

Browse files
author
Test User
committed
fix: propagate headers as sequence; add tests
1 parent 5ebb60c commit a93c07e

3 files changed

Lines changed: 56 additions & 11 deletions

File tree

packages/google-api-core/google/api_core/resumable_media/_upload.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
"""
1919

2020
import logging
21-
from typing import Dict, Optional, Tuple
21+
from typing import Dict, Optional, Sequence, Tuple
2222

2323
from google.api_core import exceptions
2424
from google.api_core.resumable_media import _common
@@ -83,14 +83,14 @@ def chunk_size(self) -> int:
8383

8484
def build_initiate_request(
8585
self,
86-
stream_metadata: Optional[Dict[str, str]] = None,
86+
stream_metadata: Optional[Sequence[Tuple[str, str]]] = None,
8787
content_type: Optional[str] = None,
8888
size: Optional[int] = None,
8989
) -> Tuple[str, str, Dict[str, str], bytes]:
9090
"""Constructs an upload initiation request.
9191
9292
Args:
93-
stream_metadata (Optional[Dict[str, str]]): Additional headers for
93+
stream_metadata (Optional[Sequence[Tuple[str, str]]]): Additional headers for
9494
the upload initiation request. These headers are ONLY applied to
9595
the initial request and will NOT be included in subsequent chunk
9696
upload requests. If not specified, no additional headers will be appended.
@@ -109,7 +109,7 @@ def build_initiate_request(
109109

110110
# Merge user metadata first
111111
if stream_metadata:
112-
for k, v in stream_metadata.items():
112+
for k, v in stream_metadata:
113113
headers[k] = str(v)
114114

115115
# Critical protocol headers overwrite user metadata

packages/google-api-core/tests/unit/resumable_media/test_requests_upload.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -307,3 +307,48 @@ def test_logging_success_path(caplog):
307307
# Check for final response logs
308308
assert "'final'" in caplog.text.lower() or "final" in caplog.text.lower()
309309
assert "Body: Final Success" in caplog.text
310+
311+
312+
@responses.activate
313+
def test_make_resumable_upload_with_custom_headers(caplog):
314+
caplog.set_level("DEBUG")
315+
initial_url = "http://example.com/start"
316+
session_url = "http://example.com/session/123"
317+
data = b"test data"
318+
stream = io.BytesIO(data)
319+
custom_headers = [("X-Custom-Header", "CustomValue")]
320+
321+
responses.add(
322+
responses.POST,
323+
initial_url,
324+
status=200,
325+
headers={"X-Goog-Upload-Status": "active", "X-Goog-Upload-URL": session_url},
326+
body="",
327+
)
328+
responses.add(
329+
responses.POST,
330+
session_url,
331+
status=200,
332+
headers={"X-Goog-Upload-Status": "final"},
333+
body="Final Success",
334+
)
335+
336+
session = requests.Session()
337+
requests_upload.make_resumable_upload(
338+
transport=session,
339+
request_body="metadata",
340+
stream=stream,
341+
upload_url=initial_url,
342+
size=len(data),
343+
chunk_size=1024,
344+
headers=custom_headers,
345+
)
346+
347+
# Check for initiation request logs containing the custom header
348+
assert f"HTTP Request: POST {initial_url}" in caplog.text
349+
assert "'X-Custom-Header': 'CustomValue'" in caplog.text
350+
351+
# Check for upload/finalize request logs NOT containing the custom header
352+
assert f"HTTP Request: POST {session_url}" in caplog.text
353+
assert caplog.text.count("'X-Custom-Header': 'CustomValue'") == 1
354+

packages/google-api-core/tests/unit/resumable_media/test_upload.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ def test_chunk_size_granularity(self):
3737
def test_initiate_request(self):
3838
upload = _upload.ResumableUpload("http://test.local/upload")
3939
method, url, headers, body = upload.build_initiate_request(
40-
stream_metadata={"x-custom-meta": "value"},
40+
stream_metadata=[("x-custom-meta", "value")],
4141
content_type="text/plain",
4242
size=100,
4343
)
@@ -53,12 +53,12 @@ def test_initiate_request(self):
5353
def test_initiate_request_conflicting_metadata(self):
5454
upload = _upload.ResumableUpload("http://test.local/upload")
5555
method, url, headers, body = upload.build_initiate_request(
56-
stream_metadata={
57-
"x-custom-meta": "value",
58-
_common.UPLOAD_PROTOCOL_HEADER: "malicious-protocol",
59-
_common.UPLOAD_COMMAND_HEADER: "malicious-command",
60-
_common.UPLOAD_CONTENT_TYPE_HEADER: "malicious/type",
61-
},
56+
stream_metadata=[
57+
("x-custom-meta", "value"),
58+
(_common.UPLOAD_PROTOCOL_HEADER, "malicious-protocol"),
59+
(_common.UPLOAD_COMMAND_HEADER, "malicious-command"),
60+
(_common.UPLOAD_CONTENT_TYPE_HEADER, "malicious/type"),
61+
],
6262
content_type="text/plain",
6363
size=100,
6464
)

0 commit comments

Comments
 (0)