Skip to content

Commit e1290f5

Browse files
williambrodeAaron Gabriel Neyercojenco
authored
feat: add support for signed resumable upload URLs (#290)
Co-authored-by: Aaron Gabriel Neyer <[email protected]> Co-authored-by: Cathy Ouyang <[email protected]>
1 parent cee4164 commit e1290f5

File tree

2 files changed

+31
-6
lines changed

2 files changed

+31
-6
lines changed

google/resumable_media/_upload.py

+12-4
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import random
2828
import re
2929
import sys
30+
import urllib.parse
3031

3132
from google import resumable_media
3233
from google.resumable_media import _helpers
@@ -462,10 +463,17 @@ def _prepare_initiate_request(
462463

463464
self._stream = stream
464465
self._content_type = content_type
465-
headers = {
466-
_CONTENT_TYPE_HEADER: "application/json; charset=UTF-8",
467-
"x-upload-content-type": content_type,
468-
}
466+
467+
# Signed URL requires content type set directly - not through x-upload-content-type
468+
parse_result = urllib.parse.urlparse(self.upload_url)
469+
parsed_query = urllib.parse.parse_qs(parse_result.query)
470+
if "x-goog-signature" in parsed_query or "X-Goog-Signature" in parsed_query:
471+
headers = {_CONTENT_TYPE_HEADER: content_type}
472+
else:
473+
headers = {
474+
_CONTENT_TYPE_HEADER: "application/json; charset=UTF-8",
475+
"x-upload-content-type": content_type,
476+
}
469477
# Set the total bytes if possible.
470478
if total_bytes is not None:
471479
self._total_bytes = total_bytes

tests/unit/test__upload.py

+19-2
Original file line numberDiff line numberDiff line change
@@ -393,12 +393,14 @@ def test_total_bytes_property(self):
393393
upload._total_bytes = 8192
394394
assert upload.total_bytes == 8192
395395

396-
def _prepare_initiate_request_helper(self, upload_headers=None, **method_kwargs):
396+
def _prepare_initiate_request_helper(
397+
self, upload_url=RESUMABLE_URL, upload_headers=None, **method_kwargs
398+
):
397399
data = b"some really big big data."
398400
stream = io.BytesIO(data)
399401
metadata = {"name": "big-data-file.txt"}
400402

401-
upload = _upload.ResumableUpload(RESUMABLE_URL, ONE_MB, headers=upload_headers)
403+
upload = _upload.ResumableUpload(upload_url, ONE_MB, headers=upload_headers)
402404
orig_headers = upload._headers.copy()
403405
# Check ``upload``-s state before.
404406
assert upload._stream is None
@@ -435,6 +437,21 @@ def test__prepare_initiate_request(self):
435437
}
436438
assert headers == expected_headers
437439

440+
def test_prepare_initiate_request_with_signed_url(self):
441+
signed_urls = [
442+
"https://storage.googleapis.com/b/o?x-goog-signature=123abc",
443+
"https://storage.googleapis.com/b/o?X-Goog-Signature=123abc",
444+
]
445+
for signed_url in signed_urls:
446+
data, headers = self._prepare_initiate_request_helper(
447+
upload_url=signed_url,
448+
)
449+
expected_headers = {
450+
"content-type": BASIC_CONTENT,
451+
"x-upload-content-length": "{:d}".format(len(data)),
452+
}
453+
assert headers == expected_headers
454+
438455
def test__prepare_initiate_request_with_headers(self):
439456
headers = {"caviar": "beluga", "top": "quark"}
440457
data, new_headers = self._prepare_initiate_request_helper(

0 commit comments

Comments
 (0)