Skip to content

Commit

Permalink
url: resource and query encoding should support unicode
Browse files Browse the repository at this point in the history
python2.7 lacks proper unicode support, so we need
a proper wrapper function for url encoding. With
this the url and query encodings works for all
unicode characters.

Fixes #529
  • Loading branch information
harshavardhana authored and minio-trusted committed Jun 16, 2017
1 parent c90d65c commit c46bdc7
Show file tree
Hide file tree
Showing 5 changed files with 43 additions and 17 deletions.
5 changes: 3 additions & 2 deletions minio/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@

# Internal imports
from . import __title__, __version__
from .compat import urlsplit, range, urlencode, basestring
from .compat import (urlsplit, queryencode,
range, basestring)
from .error import (KnownResponseError, ResponseError, NoSuchBucket,
InvalidArgumentError, InvalidSizeError, NoSuchBucketPolicy)
from .definitions import Object, UploadPart
Expand Down Expand Up @@ -841,7 +842,7 @@ def copy_object(self, bucket_name, object_name, object_source,
if conditions:
headers = {k: v for k, v in conditions.items()}

headers['X-Amz-Copy-Source'] = urlencode(object_source)
headers['X-Amz-Copy-Source'] = queryencode(object_source)
response = self._url_open('PUT',
bucket_name=bucket_name,
object_name=object_name,
Expand Down
21 changes: 19 additions & 2 deletions minio/compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@

if _is_py2:
from urllib import quote
urlencode = quote
_urlencode = quote

from urllib import unquote
urldecode = unquote
Expand All @@ -57,7 +57,7 @@
basestring = basestring
elif _is_py3:
from urllib.request import quote
urlencode = quote
_urlencode = quote

from urllib.request import unquote
urldecode = unquote
Expand All @@ -80,3 +80,20 @@
str = str

numeric_types = (int, long, float)

def urlencode(resource):
"""
This implementation of urlencode supports all unicode characters
:param: resource: Resource value to be url encoded.
"""
resource_raw = '{0}'.format(resource)
return _urlencode(resource_raw)

def queryencode(query):
"""
This implementation of queryencode supports all unicode characters
:param: query: Query value to be url encoded.
"""
return urlencode(query).replace('/', '%2F')
17 changes: 10 additions & 7 deletions minio/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@
import errno
import math

from .compat import urlsplit, urlencode, str, bytes, basestring
from .compat import (urlsplit, urlencode, queryencode,
str, bytes, basestring)
from .error import (InvalidBucketError, InvalidEndpointError,
InvalidArgumentError)

Expand Down Expand Up @@ -226,13 +227,15 @@ def get_target_url(endpoint_url, bucket_name=None, object_name=None,
if ordered_query[component_key] is not None:
if isinstance(ordered_query[component_key], list):
for value in ordered_query[component_key]:
encoded_query = urlencode(
str(value)).replace('/', '%2F')
query_components.append(component_key+'='+encoded_query)
query_components.append(component_key+'='+
queryencode(value))
else:
encoded_query = urlencode(
str(ordered_query[component_key])).replace('/', '%2F')
query_components.append(component_key+'='+encoded_query)
query_components.append(
component_key+'='+
queryencode(
ordered_query[component_key]
)
)
else:
query_components.append(component_key)

Expand Down
8 changes: 3 additions & 5 deletions minio/signer.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@

from datetime import datetime
from .error import InvalidArgumentError
from .compat import urlsplit, urlencode
from .compat import urlsplit, queryencode
from .helpers import get_sha256_hexdigest
from .fold_case_dict import FoldCaseDict

Expand Down Expand Up @@ -117,10 +117,8 @@ def presign_v4(method, url, access_key, secret_key, region=None,
if ordered_query[component_key] is not None:
single_component.append('=')
single_component.append(
urlencode(
str(ordered_query[component_key])
).replace('/',
'%2F'))
queryencode(ordered_query[component_key])
)
query_components.append(''.join(single_component))

query_string = '&'.join(query_components)
Expand Down
9 changes: 8 additions & 1 deletion tests/unit/sign_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
generate_signing_key, generate_authorization_header,
presign_v4)
from minio.error import InvalidArgumentError
from minio.compat import urlsplit
from minio.compat import urlsplit, urlencode, queryencode
from minio.fold_case_dict import FoldCaseDict

empty_hash = 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855'
Expand Down Expand Up @@ -115,3 +115,10 @@ def test_presigned_no_access_key(self):
@raises(InvalidArgumentError)
def test_presigned_invalid_expires(self):
presign_v4('GET', 'http://localhost:9000/hello', None, None, region=None, headers={}, expires=0)

class UnicodeEncodeTest(TestCase):
def test_unicode_urlencode(self):
eq_(urlencode('/test/123/汉字'), '/test/123/%E6%B1%89%E5%AD%97')

def test_unicode_queryencode(self):
eq_(queryencode('/test/123/汉字'), '%2Ftest%2F123%2F%E6%B1%89%E5%AD%97')

0 comments on commit c46bdc7

Please sign in to comment.