Skip to content

Commit 5980055

Browse files
authored
Do not quote embedded slashes for public / signed URLs (#4716)
1 parent fa0ef38 commit 5980055

3 files changed

Lines changed: 15 additions & 11 deletions

File tree

storage/google/cloud/storage/blob.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,7 @@ def public_url(self):
244244
return '{storage_base_url}/{bucket_name}/{quoted_name}'.format(
245245
storage_base_url=_API_ACCESS_ENDPOINT,
246246
bucket_name=self.bucket.name,
247-
quoted_name=_quote(self.name))
247+
quoted_name=quote(self.name.encode('utf-8')))
248248

249249
def generate_signed_url(self, expiration, method='GET',
250250
content_type=None,
@@ -315,7 +315,7 @@ def generate_signed_url(self, expiration, method='GET',
315315
"""
316316
resource = '/{bucket_name}/{quoted_name}'.format(
317317
bucket_name=self.bucket.name,
318-
quoted_name=_quote(self.name))
318+
quoted_name=quote(self.name))
319319

320320
if credentials is None:
321321
client = self._require_client(client)

storage/tests/system.py

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -554,20 +554,24 @@ def setUpClass(cls):
554554
# Make sure bucket empty before beginning.
555555
_empty_bucket(cls.bucket)
556556

557+
cls.suite_blobs_to_delete = []
557558
simple_path = cls.FILES['simple']['path']
558-
blob = storage.Blob(cls.FILENAMES[0], bucket=cls.bucket)
559-
blob.upload_from_filename(simple_path)
560-
cls.suite_blobs_to_delete = [blob]
561-
for filename in cls.FILENAMES[1:]:
562-
new_blob = retry_bad_copy(cls.bucket.copy_blob)(
563-
blob, cls.bucket, filename)
564-
cls.suite_blobs_to_delete.append(new_blob)
559+
for filename in cls.FILENAMES:
560+
blob = storage.Blob(filename, bucket=cls.bucket)
561+
blob.upload_from_filename(simple_path)
562+
cls.suite_blobs_to_delete.append(blob)
565563

566564
@classmethod
567565
def tearDownClass(cls):
568566
for blob in cls.suite_blobs_to_delete:
569567
blob.delete()
570568

569+
@RetryErrors(unittest.TestCase.failureException)
570+
def test_blob_get_w_delimiter(self):
571+
for filename in self.FILENAMES:
572+
blob = self.bucket.blob(filename)
573+
self.assertTrue(blob.exists(), filename)
574+
571575
@RetryErrors(unittest.TestCase.failureException)
572576
def test_root_level_w_delimiter(self):
573577
iterator = self.bucket.list_blobs(delimiter='/')

storage/tests/unit/test_blob.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ def test_public_url_w_slash_in_name(self):
171171
blob = self._make_one(BLOB_NAME, bucket=bucket)
172172
self.assertEqual(
173173
blob.public_url,
174-
'https://storage.googleapis.com/name/parent%2Fchild')
174+
'https://storage.googleapis.com/name/parent/child')
175175

176176
def test_public_url_with_non_ascii(self):
177177
blob_name = u'winter \N{snowman}'
@@ -274,7 +274,7 @@ def test_generate_signed_url_w_slash_in_name(self):
274274
'api_access_endpoint': 'https://storage.googleapis.com',
275275
'expiration': EXPIRATION,
276276
'method': 'GET',
277-
'resource': '/name/parent%2Fchild',
277+
'resource': '/name/parent/child',
278278
'content_type': None,
279279
'response_type': None,
280280
'response_disposition': None,

0 commit comments

Comments
 (0)